Some cleanup.

This commit is contained in:
Ottermandias 2023-12-22 15:18:43 +01:00
parent b22470ac79
commit a001fcf24f
2 changed files with 166 additions and 144 deletions

View file

@ -10,24 +10,33 @@ public partial class ModEditWindow
{ {
public readonly MdlFile Mdl; public readonly MdlFile Mdl;
private List<string>[] _attributes; private readonly List<string>[] _attributes;
public MdlTab(byte[] bytes) public MdlTab(byte[] bytes)
{ {
Mdl = new MdlFile(bytes); Mdl = new MdlFile(bytes);
_attributes = PopulateAttributes(); _attributes = CreateAttributes(Mdl);
} }
/// <inheritdoc/>
public bool Valid
=> Mdl.Valid;
/// <inheritdoc/>
public byte[] Write()
=> Mdl.Write();
/// <summary> Remove the material given by the index. </summary>
/// <remarks> Meshes using the removed material are redirected to material 0, and those after the index are corrected. </remarks>
public void RemoveMaterial(int materialIndex) public void RemoveMaterial(int materialIndex)
{ {
// Meshes using the removed material are redirected to material 0, and those after the index are corrected.
for (var meshIndex = 0; meshIndex < Mdl.Meshes.Length; meshIndex++) for (var meshIndex = 0; meshIndex < Mdl.Meshes.Length; meshIndex++)
{ {
var newIndex = Mdl.Meshes[meshIndex].MaterialIndex; var newIndex = Mdl.Meshes[meshIndex].MaterialIndex;
if (newIndex == materialIndex) if (newIndex == materialIndex)
newIndex = 0; newIndex = 0;
else if (newIndex > materialIndex) else if (newIndex > materialIndex)
newIndex -= 1; --newIndex;
Mdl.Meshes[meshIndex].MaterialIndex = newIndex; Mdl.Meshes[meshIndex].MaterialIndex = newIndex;
} }
@ -35,36 +44,41 @@ public partial class ModEditWindow
Mdl.Materials = Mdl.Materials.RemoveItems(materialIndex); Mdl.Materials = Mdl.Materials.RemoveItems(materialIndex);
} }
private List<string>[] PopulateAttributes() /// <summary> Create a list of attributes per sub mesh. </summary>
{ private static List<string>[] CreateAttributes(MdlFile mdl)
return Mdl.SubMeshes.Select(submesh => => mdl.SubMeshes.Select(s => Enumerable.Range(0, 32)
Enumerable.Range(0,32) .Where(idx => ((s.AttributeIndexMask >> idx) & 1) == 1)
.Where(index => ((submesh.AttributeIndexMask >> index) & 1) == 1) .Select(idx => mdl.Attributes[idx])
.Select(index => Mdl.Attributes[index]) .ToList()
.ToList()
).ToArray(); ).ToArray();
}
public IReadOnlyCollection<string> GetSubmeshAttributes(int submeshIndex) => _attributes[submeshIndex]; /// <summary> Obtain the attributes associated with a sub mesh by its index. </summary>
public IReadOnlyList<string> GetSubMeshAttributes(int subMeshIndex)
=> _attributes[subMeshIndex];
public void UpdateSubmeshAttribute(int submeshIndex, string? old, string? new_) /// <summary> Remove or add attributes from a sub mesh by its index. </summary>
/// <param name="subMeshIndex"> The index of the sub mesh to update. </param>
/// <param name="old"> If non-null, remove this attribute. </param>
/// <param name="new"> If non-null, add this attribute. </param>
public void UpdateSubMeshAttribute(int subMeshIndex, string? old, string? @new)
{ {
var attributes = _attributes[submeshIndex]; var attributes = _attributes[subMeshIndex];
if (old != null) if (old != null)
attributes.Remove(old); attributes.Remove(old);
if (new_ != null) if (@new != null)
attributes.Add(new_); attributes.Add(@new);
PersistAttributes(); PersistAttributes();
} }
/// <summary> Apply changes to attributes to the file in memory. </summary>
private void PersistAttributes() private void PersistAttributes()
{ {
var allAttributes = new List<string>(); var allAttributes = new List<string>();
foreach (var (attributes, submeshIndex) in _attributes.WithIndex()) foreach (var (attributes, subMeshIndex) in _attributes.WithIndex())
{ {
var mask = 0u; var mask = 0u;
@ -74,20 +88,16 @@ public partial class ModEditWindow
if (attributeIndex == -1) if (attributeIndex == -1)
{ {
allAttributes.Add(attribute); allAttributes.Add(attribute);
attributeIndex = allAttributes.Count() - 1; attributeIndex = allAttributes.Count - 1;
} }
mask |= 1u << attributeIndex; mask |= 1u << attributeIndex;
} }
Mdl.SubMeshes[submeshIndex].AttributeIndexMask = mask; Mdl.SubMeshes[subMeshIndex].AttributeIndexMask = mask;
} }
Mdl.Attributes = allAttributes.ToArray(); Mdl.Attributes = [.. allAttributes];
} }
public bool Valid => Mdl.Valid;
public byte[] Write() => Mdl.Write();
} }
} }

View file

@ -15,19 +15,19 @@ public partial class ModEditWindow
private readonly FileEditor<MdlTab> _modelTab; private readonly FileEditor<MdlTab> _modelTab;
private static string _modelNewMaterial = string.Empty; private string _modelNewMaterial = string.Empty;
private static List<TagButtons> _submeshAttributeTagWidgets = new(); private readonly List<TagButtons> _subMeshAttributeTagWidgets = [];
private static bool DrawModelPanel(MdlTab tab, bool disabled) private bool DrawModelPanel(MdlTab tab, bool disabled)
{ {
var file = tab.Mdl; var file = tab.Mdl;
var submeshTotal = file.Meshes.Aggregate(0, (count, mesh) => count + mesh.SubMeshCount); var subMeshTotal = file.Meshes.Aggregate(0, (count, mesh) => count + mesh.SubMeshCount);
if (_submeshAttributeTagWidgets.Count != submeshTotal) if (_subMeshAttributeTagWidgets.Count != subMeshTotal)
{ {
_submeshAttributeTagWidgets.Clear(); _subMeshAttributeTagWidgets.Clear();
_submeshAttributeTagWidgets.AddRange( _subMeshAttributeTagWidgets.AddRange(
Enumerable.Range(0, submeshTotal).Select(_ => new TagButtons()) Enumerable.Range(0, subMeshTotal).Select(_ => new TagButtons())
); );
} }
@ -44,93 +44,92 @@ public partial class ModEditWindow
return !disabled && ret; return !disabled && ret;
} }
private static bool DrawModelMaterialDetails(MdlTab tab, bool disabled) private bool DrawModelMaterialDetails(MdlTab tab, bool disabled)
{ {
if (!ImGui.CollapsingHeader("Materials")) if (!ImGui.CollapsingHeader("Materials"))
return false; return false;
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); using var table = ImRaii.Table(string.Empty, disabled ? 2 : 3, ImGuiTableFlags.SizingFixedFit);
if (!table) if (!table)
return false; return false;
var ret = false; var ret = false;
var materials = tab.Mdl.Materials; var materials = tab.Mdl.Materials;
ImGui.TableSetupColumn("index", ImGuiTableColumnFlags.WidthFixed, 80 * UiHelpers.Scale); ImGui.TableSetupColumn("index", ImGuiTableColumnFlags.WidthFixed, 80 * UiHelpers.Scale);
ImGui.TableSetupColumn("path", ImGuiTableColumnFlags.WidthStretch, 1); ImGui.TableSetupColumn("path", ImGuiTableColumnFlags.WidthStretch, 1);
ImGui.TableSetupColumn("actions", ImGuiTableColumnFlags.WidthFixed, UiHelpers.IconButtonSize.X); if (!disabled)
ImGui.TableSetupColumn("actions", ImGuiTableColumnFlags.WidthFixed, UiHelpers.IconButtonSize.X);
var inputFlags = ImGuiInputTextFlags.None;
if (disabled)
inputFlags |= ImGuiInputTextFlags.ReadOnly;
var inputFlags = disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None;
for (var materialIndex = 0; materialIndex < materials.Length; materialIndex++) for (var materialIndex = 0; materialIndex < materials.Length; materialIndex++)
{ ret |= DrawMaterialRow(tab, disabled, materials, materialIndex, inputFlags);
using var id = ImRaii.PushId(materialIndex);
ImGui.TableNextColumn(); if (materials.Length >= MdlMaterialMaximum || disabled)
ImGui.AlignTextToFramePadding(); return ret;
ImGui.Text($"Material #{materialIndex + 1}");
var temp = materials[materialIndex]; ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
if (
ImGui.InputText($"##material{materialIndex}", ref temp, Utf8GamePath.MaxGamePathLength, inputFlags)
&& temp.Length > 0
&& temp != materials[materialIndex]
) {
materials[materialIndex] = temp;
ret = true;
}
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint("##newMaterial", "Add new material...", ref _modelNewMaterial, Utf8GamePath.MaxGamePathLength, inputFlags);
var validName = _modelNewMaterial.Length > 0 && _modelNewMaterial[0] == '/';
ImGui.TableNextColumn();
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), UiHelpers.IconButtonSize, string.Empty, !validName, true))
return ret;
// Need to have at least one material. tab.Mdl.Materials = materials.AddItem(_modelNewMaterial);
if (materials.Length <= 1) _modelNewMaterial = string.Empty;
continue; return true;
if (ImGuiUtil.DrawDisabledButton(
FontAwesomeIcon.Trash.ToIconString(),
UiHelpers.IconButtonSize,
"Delete this material.\nAny meshes targeting this material will be updated to use material #1.\nHold Control while clicking to delete.",
disabled || !ImGui.GetIO().KeyCtrl,
true
)) {
tab.RemoveMaterial(materialIndex);
ret = true;
}
}
if (materials.Length < MdlMaterialMaximum)
{
ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint($"##newMaterial", "Add new material...", ref _modelNewMaterial, Utf8GamePath.MaxGamePathLength, inputFlags);
var validName = _modelNewMaterial != "";
ImGui.TableNextColumn();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), UiHelpers.IconButtonSize, "description", disabled || !validName, true))
{
tab.Mdl.Materials = materials.AddItem(_modelNewMaterial);
_modelNewMaterial = string.Empty;
ret = true;
}
}
return ret;
} }
private static bool DrawModelLodDetails(MdlTab tab, int lodIndex, bool disabled) private bool DrawMaterialRow(MdlTab tab, bool disabled, string[] materials, int materialIndex, ImGuiInputTextFlags inputFlags)
{ {
using var lodNode = ImRaii.TreeNode($"Level of Detail #{lodIndex}", ImGuiTreeNodeFlags.DefaultOpen); using var id = ImRaii.PushId(materialIndex);
var ret = false;
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted($"Material #{materialIndex + 1}");
var temp = materials[materialIndex];
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
if (ImGui.InputText($"##material{materialIndex}", ref temp, Utf8GamePath.MaxGamePathLength, inputFlags)
&& temp.Length > 0
&& temp != materials[materialIndex]
)
{
materials[materialIndex] = temp;
ret = true;
}
if (disabled)
return ret;
ImGui.TableNextColumn();
// Need to have at least one material.
if (materials.Length <= 1)
return ret;
var tt = "Delete this material.\nAny meshes targeting this material will be updated to use material #1.";
var modifierActive = _config.DeleteModModifier.IsActive();
if (!modifierActive)
tt += $"\nHold {_config.DeleteModModifier} to delete.";
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), UiHelpers.IconButtonSize, tt, !modifierActive, true))
return ret;
tab.RemoveMaterial(materialIndex);
return true;
}
private bool DrawModelLodDetails(MdlTab tab, int lodIndex, bool disabled)
{
using var lodNode = ImRaii.TreeNode($"Level of Detail #{lodIndex + 1}", ImGuiTreeNodeFlags.DefaultOpen);
if (!lodNode) if (!lodNode)
return false; return false;
var lod = tab.Mdl.Lods[lodIndex]; var lod = tab.Mdl.Lods[lodIndex];
var ret = false; var ret = false;
for (var meshOffset = 0; meshOffset < lod.MeshCount; meshOffset++) for (var meshOffset = 0; meshOffset < lod.MeshCount; meshOffset++)
@ -139,76 +138,89 @@ public partial class ModEditWindow
return ret; return ret;
} }
private static bool DrawModelMeshDetails(MdlTab tab, int meshIndex, bool disabled) private bool DrawModelMeshDetails(MdlTab tab, int meshIndex, bool disabled)
{ {
using var meshNode = ImRaii.TreeNode($"Mesh #{meshIndex}", ImGuiTreeNodeFlags.DefaultOpen); using var meshNode = ImRaii.TreeNode($"Mesh #{meshIndex + 1}", ImGuiTreeNodeFlags.DefaultOpen);
if (!meshNode) if (!meshNode)
return false; return false;
using var id = ImRaii.PushId(meshIndex); using var id = ImRaii.PushId(meshIndex);
using var table = ImRaii.Table(string.Empty, 2, ImGuiTableFlags.SizingFixedFit); using var table = ImRaii.Table(string.Empty, 2, ImGuiTableFlags.SizingFixedFit);
if (!table) if (!table)
return false; return false;
ImGui.TableSetupColumn("name", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale); ImGui.TableSetupColumn("name", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale);
ImGui.TableSetupColumn("field", ImGuiTableColumnFlags.WidthStretch, 1); ImGui.TableSetupColumn("field", ImGuiTableColumnFlags.WidthStretch, 1);
var file = tab.Mdl; var file = tab.Mdl;
var mesh = file.Meshes[meshIndex]; var mesh = file.Meshes[meshIndex];
var ret = false;
// Mesh material // Mesh material
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.Text("Material"); ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Material");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
var ret = DrawMaterialCombo(tab, meshIndex, disabled);
// Sub meshes
for (var subMeshOffset = 0; subMeshOffset < mesh.SubMeshCount; subMeshOffset++)
ret |= DrawSubMeshAttributes(tab, meshIndex, disabled, subMeshOffset);
return ret;
}
private bool DrawMaterialCombo(MdlTab tab, int meshIndex, bool disabled)
{
var mesh = tab.Mdl.Meshes[meshIndex];
using var _ = ImRaii.Disabled(disabled);
ImGui.SetNextItemWidth(-1); ImGui.SetNextItemWidth(-1);
using (var materialCombo = ImRaii.Combo("##material", tab.Mdl.Materials[mesh.MaterialIndex])) using var materialCombo = ImRaii.Combo("##material", tab.Mdl.Materials[mesh.MaterialIndex]);
if (!materialCombo)
return false;
var ret = false;
foreach (var (material, materialIndex) in tab.Mdl.Materials.WithIndex())
{ {
if (materialCombo) if (!ImGui.Selectable(material, mesh.MaterialIndex == materialIndex))
{ continue;
foreach (var (material, materialIndex) in tab.Mdl.Materials.WithIndex())
{
if (ImGui.Selectable(material, mesh.MaterialIndex == materialIndex))
{
file.Meshes[meshIndex].MaterialIndex = (ushort)materialIndex;
ret = true;
}
}
}
}
// Submeshes tab.Mdl.Meshes[meshIndex].MaterialIndex = (ushort)materialIndex;
for (var submeshOffset = 0; submeshOffset < mesh.SubMeshCount; submeshOffset++) ret = true;
{
using var submeshId = ImRaii.PushId(submeshOffset);
var submeshIndex = mesh.SubMeshIndex + submeshOffset;
ImGui.TableNextColumn();
ImGui.Text($"Attributes #{submeshOffset}");
ImGui.TableNextColumn();
var widget = _submeshAttributeTagWidgets[submeshIndex];
var attributes = tab.GetSubmeshAttributes(submeshIndex);
var tagIndex = widget.Draw("", "", attributes, out var editedAttribute, !disabled);
if (tagIndex >= 0)
{
tab.UpdateSubmeshAttribute(
submeshIndex,
tagIndex < attributes.Count() ? attributes.ElementAt(tagIndex) : null,
editedAttribute != "" ? editedAttribute : null
);
ret = true;
}
} }
return ret; return ret;
} }
private bool DrawSubMeshAttributes(MdlTab tab, int meshIndex, bool disabled, int subMeshOffset)
{
using var _ = ImRaii.PushId(subMeshOffset);
var mesh = tab.Mdl.Meshes[meshIndex];
var subMeshIndex = mesh.SubMeshIndex + subMeshOffset;
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted($"Attributes #{subMeshOffset + 1}");
ImGui.TableNextColumn();
var widget = _subMeshAttributeTagWidgets[subMeshIndex];
var attributes = tab.GetSubMeshAttributes(subMeshIndex);
var tagIndex = widget.Draw(string.Empty, string.Empty, attributes,
out var editedAttribute, !disabled);
if (tagIndex < 0)
return false;
var oldName = tagIndex < attributes.Count ? attributes[tagIndex] : null;
var newName = editedAttribute.Length > 0 ? editedAttribute : null;
tab.UpdateSubMeshAttribute(subMeshIndex, oldName, newName);
return true;
}
private static bool DrawOtherModelDetails(MdlFile file, bool _) private static bool DrawOtherModelDetails(MdlFile file, bool _)
{ {
if (!ImGui.CollapsingHeader("Further Content")) if (!ImGui.CollapsingHeader("Further Content"))