Merge branch 'master' into mdl-io-warnings

# Conflicts:
#	Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs
This commit is contained in:
Ottermandias 2024-01-20 15:39:25 +01:00
commit fff9fb00f8
8 changed files with 73 additions and 21 deletions

@ -1 +1 @@
Subproject commit 9259090121b26f097948e7bbd83b32708ea0410d Subproject commit c6f101bbef976b74eb651523445563dd81fafbaf

View file

@ -156,9 +156,14 @@ public class CollectionEditor
// Either copy the unused source settings directly if they are not inheriting, // Either copy the unused source settings directly if they are not inheriting,
// or remove any unused settings for the target if they are inheriting. // or remove any unused settings for the target if they are inheriting.
if (savedSettings != null) if (savedSettings != null)
{
((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings)[targetName] = savedSettings.Value; ((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings)[targetName] = savedSettings.Value;
else _saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings).Remove(targetName); }
else if (((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings).Remove(targetName))
{
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
}
} }
return true; return true;

View file

@ -135,7 +135,6 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
var subMeshOffset = _subMeshes.Count; var subMeshOffset = _subMeshes.Count;
var vertexOffset = _vertexBuffer.Count; var vertexOffset = _vertexBuffer.Count;
var indexOffset = _indices.Count; var indexOffset = _indices.Count;
var shapeValueOffset = _shapeValues.Count;
var mesh = MeshImporter.Import(subMeshNodes, notifier.WithContext($"Mesh {index}")); var mesh = MeshImporter.Import(subMeshNodes, notifier.WithContext($"Mesh {index}"));
var meshStartIndex = (uint)(mesh.MeshStruct.StartIndex + indexOffset); var meshStartIndex = (uint)(mesh.MeshStruct.StartIndex + indexOffset);
@ -185,7 +184,7 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
shapeMeshes.Add(meshShapeKey.ShapeMesh with shapeMeshes.Add(meshShapeKey.ShapeMesh with
{ {
MeshIndexOffset = meshStartIndex, MeshIndexOffset = meshStartIndex,
ShapeValueOffset = (uint)shapeValueOffset, ShapeValueOffset = (uint)_shapeValues.Count,
}); });
_shapeValues.AddRange(meshShapeKey.ShapeValues); _shapeValues.AddRange(meshShapeKey.ShapeValues);

View file

@ -235,7 +235,7 @@ public class SubMeshImporter
foreach (var (modifiedVertices, morphIndex) in morphModifiedVertices.WithIndex()) foreach (var (modifiedVertices, morphIndex) in morphModifiedVertices.WithIndex())
{ {
// Each for a given mesh, each shape key contains a list of shape value mappings. // For a given mesh, each shape key contains a list of shape value mappings.
var shapeValues = new List<MdlStructs.ShapeValueStruct>(); var shapeValues = new List<MdlStructs.ShapeValueStruct>();
foreach (var vertexIndex in modifiedVertices) foreach (var vertexIndex in modifiedVertices)

View file

@ -10,10 +10,10 @@ public sealed unsafe class CharacterBaseDestructor : EventWrapperPtr<CharacterBa
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="PathResolving.DrawObjectState"/> /// <seealso cref="PathResolving.DrawObjectState.OnCharacterBaseDestructor"/>
DrawObjectState = 0, DrawObjectState = 0,
/// <seealso cref="ModEditWindow.MtrlTab"/> /// <seealso cref="ModEditWindow.MtrlTab.UnbindFromDrawObjectMaterialInstances"/>
MtrlTab = -1000, MtrlTab = -1000,
} }

View file

@ -3,7 +3,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.Interop.Hooks;
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object; using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.Interop.Hooks.Objects; using Penumbra.Interop.Hooks.Objects;

View file

@ -16,7 +16,7 @@ public partial class ModEditWindow
private readonly ModEditWindow _edit; private readonly ModEditWindow _edit;
public MdlFile Mdl { get; private set; } public MdlFile Mdl { get; private set; }
private List<string>[] _attributes; private List<string>?[] _attributes;
public bool ImportKeepMaterials; public bool ImportKeepMaterials;
public bool ImportKeepAttributes; public bool ImportKeepAttributes;
@ -296,15 +296,21 @@ public partial class ModEditWindow
} }
/// <summary> Create a list of attributes per sub mesh. </summary> /// <summary> Create a list of attributes per sub mesh. </summary>
private static List<string>[] CreateAttributes(MdlFile mdl) private static List<string>?[] CreateAttributes(MdlFile mdl)
=> mdl.SubMeshes.Select(s => Enumerable.Range(0, 32) => mdl.SubMeshes.Select(s =>
.Where(idx => ((s.AttributeIndexMask >> idx) & 1) == 1) {
.Select(idx => mdl.Attributes[idx]) var maxAttribute = 31 - BitOperations.LeadingZeroCount(s.AttributeIndexMask);
.ToList() // TODO: Research what results in this - it seems to primarily be reproducible on bgparts, is it garbage data, or an alternative usage of the value?
).ToArray(); return maxAttribute < mdl.Attributes.Length
? Enumerable.Range(0, 32)
.Where(idx => ((s.AttributeIndexMask >> idx) & 1) == 1)
.Select(idx => mdl.Attributes[idx])
.ToList()
: null;
}).ToArray();
/// <summary> Obtain the attributes associated with a sub mesh by its index. </summary> /// <summary> Obtain the attributes associated with a sub mesh by its index. </summary>
public IReadOnlyList<string> GetSubMeshAttributes(int subMeshIndex) public IReadOnlyList<string>? GetSubMeshAttributes(int subMeshIndex)
=> _attributes[subMeshIndex]; => _attributes[subMeshIndex];
/// <summary> Remove or add attributes from a sub mesh by its index. </summary> /// <summary> Remove or add attributes from a sub mesh by its index. </summary>
@ -314,6 +320,8 @@ public partial class ModEditWindow
public void UpdateSubMeshAttribute(int subMeshIndex, string? old, string? @new) public void UpdateSubMeshAttribute(int subMeshIndex, string? old, string? @new)
{ {
var attributes = _attributes[subMeshIndex]; var attributes = _attributes[subMeshIndex];
if (attributes == null)
return;
if (old != null) if (old != null)
attributes.Remove(old); attributes.Remove(old);
@ -331,6 +339,9 @@ public partial class ModEditWindow
foreach (var (attributes, subMeshIndex) in _attributes.WithIndex()) foreach (var (attributes, subMeshIndex) in _attributes.WithIndex())
{ {
if (attributes == null)
continue;
var mask = 0u; var mask = 0u;
foreach (var attribute in attributes) foreach (var attribute in attributes)

View file

@ -1,6 +1,7 @@
using Dalamud.Interface; using Dalamud.Interface;
using ImGuiNET; using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Custom;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.GameData; using Penumbra.GameData;
@ -13,7 +14,9 @@ namespace Penumbra.UI.AdvancedWindow;
public partial class ModEditWindow public partial class ModEditWindow
{ {
private const int MdlMaterialMaximum = 4; private const int MdlMaterialMaximum = 4;
private const string MdlImportDocumentation = @"https://github.com/xivdev/Penumbra/wiki/Model-IO#user-content-9b49d296-23ab-410a-845b-a3be769b71ea";
private const string MdlExportDocumentation = @"https://github.com/xivdev/Penumbra/wiki/Model-IO#user-content-25968400-ebe5-4861-b610-cb1556db7ec4";
private readonly FileEditor<MdlTab> _modelTab; private readonly FileEditor<MdlTab> _modelTab;
private readonly ModelManager _models; private readonly ModelManager _models;
@ -68,6 +71,8 @@ public partial class ModEditWindow
private void DrawImport(MdlTab tab, Vector2 size, bool _1) private void DrawImport(MdlTab tab, Vector2 size, bool _1)
{ {
using var id = ImRaii.PushId("import");
_dragDropManager.CreateImGuiSource("ModelDragDrop", _dragDropManager.CreateImGuiSource("ModelDragDrop",
m => m.Extensions.Any(e => ValidModelExtensions.Contains(e.ToLowerInvariant())), m => m => m.Extensions.Any(e => ValidModelExtensions.Contains(e.ToLowerInvariant())), m =>
{ {
@ -90,6 +95,9 @@ public partial class ModEditWindow
if (success && paths.Count > 0) if (success && paths.Count > 0)
tab.Import(paths[0]); tab.Import(paths[0]);
}, 1, _mod!.ModPath.FullName, false); }, 1, _mod!.ModPath.FullName, false);
ImGui.SameLine();
DrawDocumentationLink(MdlImportDocumentation);
} }
if (_dragDropManager.CreateImGuiTarget("ModelDragDrop", out var files, out _) && GetFirstModel(files, out var importFile)) if (_dragDropManager.CreateImGuiTarget("ModelDragDrop", out var files, out _) && GetFirstModel(files, out var importFile))
@ -98,6 +106,7 @@ public partial class ModEditWindow
private void DrawExport(MdlTab tab, Vector2 size, bool _) private void DrawExport(MdlTab tab, Vector2 size, bool _)
{ {
using var id = ImRaii.PushId("export");
using var frame = ImRaii.FramedGroup("Export", size, headerPreIcon: FontAwesomeIcon.FileExport); using var frame = ImRaii.FramedGroup("Export", size, headerPreIcon: FontAwesomeIcon.FileExport);
if (tab.GamePaths == null) if (tab.GamePaths == null)
@ -122,6 +131,7 @@ public partial class ModEditWindow
var gamePath = tab.GamePathIndex >= 0 && tab.GamePathIndex < tab.GamePaths.Count var gamePath = tab.GamePathIndex >= 0 && tab.GamePathIndex < tab.GamePaths.Count
? tab.GamePaths[tab.GamePathIndex] ? tab.GamePaths[tab.GamePathIndex]
: _customGamePath; : _customGamePath;
if (ImGuiUtil.DrawDisabledButton("Export to glTF", Vector2.Zero, "Exports this mdl file to glTF, for use in 3D authoring applications.", if (ImGuiUtil.DrawDisabledButton("Export to glTF", Vector2.Zero, "Exports this mdl file to glTF, for use in 3D authoring applications.",
tab.PendingIo || gamePath.IsEmpty)) tab.PendingIo || gamePath.IsEmpty))
_fileDialog.OpenSavePicker("Save model as glTF.", ".gltf", Path.GetFileNameWithoutExtension(gamePath.Filename().ToString()), _fileDialog.OpenSavePicker("Save model as glTF.", ".gltf", Path.GetFileNameWithoutExtension(gamePath.Filename().ToString()),
@ -135,6 +145,9 @@ public partial class ModEditWindow
_mod!.ModPath.FullName, _mod!.ModPath.FullName,
false false
); );
ImGui.SameLine();
DrawDocumentationLink(MdlExportDocumentation);
} }
private static void DrawIoExceptions(MdlTab tab) private static void DrawIoExceptions(MdlTab tab)
@ -249,6 +262,24 @@ public partial class ModEditWindow
ImGuiUtil.HoverTooltip("Right-Click to copy to clipboard.", ImGuiHoveredFlags.AllowWhenDisabled); ImGuiUtil.HoverTooltip("Right-Click to copy to clipboard.", ImGuiHoveredFlags.AllowWhenDisabled);
} }
private void DrawDocumentationLink(string address)
{
const string text = "Documentation →";
var framePadding = ImGui.GetStyle().FramePadding;
var width = ImGui.CalcTextSize(text).X + framePadding.X * 2;
// Draw the link button. We set the background colour to transparent to mimic the look of a link.
using var color = ImRaii.PushColor(ImGuiCol.Button, 0x00000000);
CustomGui.DrawLinkButton(Penumbra.Messager, text, address, width);
// Draw an underline for the text.
var lineStart = ImGui.GetItemRectMax();
lineStart -= framePadding;
var lineEnd = lineStart with { X = ImGui.GetItemRectMin().X + framePadding.X };
ImGui.GetWindowDrawList().AddLine(lineStart, lineEnd, 0xFFFFFFFF);
}
private bool DrawModelMaterialDetails(MdlTab tab, bool disabled) private bool DrawModelMaterialDetails(MdlTab tab, bool disabled)
{ {
if (!ImGui.CollapsingHeader("Materials")) if (!ImGui.CollapsingHeader("Materials"))
@ -370,7 +401,7 @@ public partial class ModEditWindow
// Sub meshes // Sub meshes
for (var subMeshOffset = 0; subMeshOffset < mesh.SubMeshCount; subMeshOffset++) for (var subMeshOffset = 0; subMeshOffset < mesh.SubMeshCount; subMeshOffset++)
ret |= DrawSubMeshAttributes(tab, meshIndex, disabled, subMeshOffset); ret |= DrawSubMeshAttributes(tab, meshIndex, subMeshOffset, disabled);
return ret; return ret;
} }
@ -398,7 +429,7 @@ public partial class ModEditWindow
return ret; return ret;
} }
private bool DrawSubMeshAttributes(MdlTab tab, int meshIndex, bool disabled, int subMeshOffset) private bool DrawSubMeshAttributes(MdlTab tab, int meshIndex, int subMeshOffset, bool disabled)
{ {
using var _ = ImRaii.PushId(subMeshOffset); using var _ = ImRaii.PushId(subMeshOffset);
@ -413,6 +444,12 @@ public partial class ModEditWindow
var widget = _subMeshAttributeTagWidgets[subMeshIndex]; var widget = _subMeshAttributeTagWidgets[subMeshIndex];
var attributes = tab.GetSubMeshAttributes(subMeshIndex); var attributes = tab.GetSubMeshAttributes(subMeshIndex);
if (attributes == null)
{
attributes = ["invalid attribute data"];
disabled = true;
}
var tagIndex = widget.Draw(string.Empty, string.Empty, attributes, var tagIndex = widget.Draw(string.Empty, string.Empty, attributes,
out var editedAttribute, !disabled); out var editedAttribute, !disabled);
if (tagIndex < 0) if (tagIndex < 0)
@ -427,7 +464,8 @@ public partial class ModEditWindow
private static bool DrawOtherModelDetails(MdlFile file, bool _) private static bool DrawOtherModelDetails(MdlFile file, bool _)
{ {
if (!ImGui.CollapsingHeader("Further Content")) using var header = ImRaii.CollapsingHeader("Further Content");
if (!header)
return false; return false;
using (var table = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit)) using (var table = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit))