From ae409c2cd1732f8889949d5f10865703285441b9 Mon Sep 17 00:00:00 2001 From: ackwell Date: Sat, 20 Jan 2024 15:31:27 +1100 Subject: [PATCH 1/8] Fix shape value offset handling --- Penumbra/Import/Models/Import/ModelImporter.cs | 3 +-- Penumbra/Import/Models/Import/SubMeshImporter.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Penumbra/Import/Models/Import/ModelImporter.cs b/Penumbra/Import/Models/Import/ModelImporter.cs index 1b7fdfa5..3c7e97c7 100644 --- a/Penumbra/Import/Models/Import/ModelImporter.cs +++ b/Penumbra/Import/Models/Import/ModelImporter.cs @@ -134,7 +134,6 @@ public partial class ModelImporter(ModelRoot model) var subMeshOffset = _subMeshes.Count; var vertexOffset = _vertexBuffer.Count; var indexOffset = _indices.Count; - var shapeValueOffset = _shapeValues.Count; var mesh = MeshImporter.Import(subMeshNodes); var meshStartIndex = (uint)(mesh.MeshStruct.StartIndex + indexOffset); @@ -184,7 +183,7 @@ public partial class ModelImporter(ModelRoot model) shapeMeshes.Add(meshShapeKey.ShapeMesh with { MeshIndexOffset = meshStartIndex, - ShapeValueOffset = (uint)shapeValueOffset, + ShapeValueOffset = (uint)_shapeValues.Count, }); _shapeValues.AddRange(meshShapeKey.ShapeValues); diff --git a/Penumbra/Import/Models/Import/SubMeshImporter.cs b/Penumbra/Import/Models/Import/SubMeshImporter.cs index 6a5d0d52..023e5c2f 100644 --- a/Penumbra/Import/Models/Import/SubMeshImporter.cs +++ b/Penumbra/Import/Models/Import/SubMeshImporter.cs @@ -234,7 +234,7 @@ public class SubMeshImporter 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(); foreach (var vertexIndex in modifiedVertices) From c11519c95ef223bd4964bfd94739dfecc470012a Mon Sep 17 00:00:00 2001 From: ackwell Date: Sat, 20 Jan 2024 15:32:44 +1100 Subject: [PATCH 2/8] Fix further content expanding other sections --- Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index ad609285..cc6493b6 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -386,6 +386,8 @@ public partial class ModEditWindow if (!ImGui.CollapsingHeader("Further Content")) return false; + using var furtherContentId = ImRaii.PushId("furtherContent"); + using (var table = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit)) { if (table) From 655d1722c18fbd24b5a569129c6005c439d7102d Mon Sep 17 00:00:00 2001 From: ackwell Date: Sat, 20 Jan 2024 17:49:39 +1100 Subject: [PATCH 3/8] Fail soft on invalid attribute masks --- .../ModEditWindow.Models.MdlTab.cs | 27 +++++++++++++------ .../UI/AdvancedWindow/ModEditWindow.Models.cs | 10 +++++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs index 26fcd1ee..98bd66d6 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs @@ -14,7 +14,7 @@ public partial class ModEditWindow private readonly ModEditWindow _edit; public MdlFile Mdl { get; private set; } - private List[] _attributes; + private List?[] _attributes; public bool ImportKeepMaterials; public bool ImportKeepAttributes; @@ -290,15 +290,21 @@ public partial class ModEditWindow } /// Create a list of attributes per sub mesh. - private static List[] CreateAttributes(MdlFile mdl) - => mdl.SubMeshes.Select(s => Enumerable.Range(0, 32) - .Where(idx => ((s.AttributeIndexMask >> idx) & 1) == 1) - .Select(idx => mdl.Attributes[idx]) - .ToList() - ).ToArray(); + private static List?[] CreateAttributes(MdlFile mdl) + => mdl.SubMeshes.Select(s => + { + var maxAttribute = 31 - BitOperations.LeadingZeroCount(s.AttributeIndexMask); + // 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? + return maxAttribute < mdl.Attributes.Length + ? Enumerable.Range(0, 32) + .Where(idx => ((s.AttributeIndexMask >> idx) & 1) == 1) + .Select(idx => mdl.Attributes[idx]) + .ToList() + : null; + }).ToArray(); /// Obtain the attributes associated with a sub mesh by its index. - public IReadOnlyList GetSubMeshAttributes(int subMeshIndex) + public IReadOnlyList? GetSubMeshAttributes(int subMeshIndex) => _attributes[subMeshIndex]; /// Remove or add attributes from a sub mesh by its index. @@ -308,6 +314,8 @@ public partial class ModEditWindow public void UpdateSubMeshAttribute(int subMeshIndex, string? old, string? @new) { var attributes = _attributes[subMeshIndex]; + if (attributes == null) + return; if (old != null) attributes.Remove(old); @@ -325,6 +333,9 @@ public partial class ModEditWindow foreach (var (attributes, subMeshIndex) in _attributes.WithIndex()) { + if (attributes == null) + continue; + var mask = 0u; foreach (var attribute in attributes) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index cc6493b6..bc763d7d 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -326,7 +326,7 @@ public partial class ModEditWindow // Sub meshes for (var subMeshOffset = 0; subMeshOffset < mesh.SubMeshCount; subMeshOffset++) - ret |= DrawSubMeshAttributes(tab, meshIndex, disabled, subMeshOffset); + ret |= DrawSubMeshAttributes(tab, meshIndex, subMeshOffset, disabled); return ret; } @@ -354,7 +354,7 @@ public partial class ModEditWindow 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); @@ -369,6 +369,12 @@ public partial class ModEditWindow var widget = _subMeshAttributeTagWidgets[subMeshIndex]; var attributes = tab.GetSubMeshAttributes(subMeshIndex); + if (attributes == null) + { + attributes = ["invalid attribute data"]; + disabled = true; + } + var tagIndex = widget.Draw(string.Empty, string.Empty, attributes, out var editedAttribute, !disabled); if (tagIndex < 0) From de08862a88c19dba406db90b7e3770386be68225 Mon Sep 17 00:00:00 2001 From: ackwell Date: Sat, 20 Jan 2024 21:13:53 +1100 Subject: [PATCH 4/8] Add documentation links --- OtterGui | 2 +- .../UI/AdvancedWindow/ModEditWindow.Models.cs | 34 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/OtterGui b/OtterGui index 92590901..5d0aed2b 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 9259090121b26f097948e7bbd83b32708ea0410d +Subproject commit 5d0aed2b32a61654321a6616689932635cb35dde diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index bc763d7d..1fb744ae 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -1,6 +1,7 @@ using Dalamud.Interface; using ImGuiNET; using OtterGui; +using OtterGui.Custom; using OtterGui.Raii; using OtterGui.Widgets; using Penumbra.GameData; @@ -13,7 +14,9 @@ namespace Penumbra.UI.AdvancedWindow; 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#import"; + private const string MdlExportDocumentation = @"https://github.com/xivdev/Penumbra/wiki/Model-IO#export"; private readonly FileEditor _modelTab; private readonly ModelManager _models; @@ -67,6 +70,8 @@ public partial class ModEditWindow private void DrawImport(MdlTab tab, Vector2 size, bool _1) { + using var id = ImRaii.PushId("import"); + _dragDropManager.CreateImGuiSource("ModelDragDrop", m => m.Extensions.Any(e => ValidModelExtensions.Contains(e.ToLowerInvariant())), m => { @@ -89,6 +94,9 @@ public partial class ModEditWindow if (success && paths.Count > 0) tab.Import(paths[0]); }, 1, _mod!.ModPath.FullName, false); + + ImGui.SameLine(); + DrawDocumentationLink(MdlImportDocumentation); } if (_dragDropManager.CreateImGuiTarget("ModelDragDrop", out var files, out _) && GetFirstModel(files, out var importFile)) @@ -97,6 +105,7 @@ public partial class ModEditWindow private void DrawExport(MdlTab tab, Vector2 size, bool _) { + using var id = ImRaii.PushId("export"); using var frame = ImRaii.FramedGroup("Export", size, headerPreIcon: FontAwesomeIcon.FileExport); if (tab.GamePaths == null) @@ -110,10 +119,10 @@ public partial class ModEditWindow } DrawGamePathCombo(tab); - var gamePath = tab.GamePathIndex >= 0 && tab.GamePathIndex < tab.GamePaths.Count ? tab.GamePaths[tab.GamePathIndex] : _customGamePath; + if (ImGuiUtil.DrawDisabledButton("Export to glTF", Vector2.Zero, "Exports this mdl file to glTF, for use in 3D authoring applications.", tab.PendingIo || gamePath.IsEmpty)) _fileDialog.OpenSavePicker("Save model as glTF.", ".gltf", Path.GetFileNameWithoutExtension(gamePath.Filename().ToString()), @@ -127,6 +136,9 @@ public partial class ModEditWindow _mod!.ModPath.FullName, false ); + + ImGui.SameLine(); + DrawDocumentationLink(MdlExportDocumentation); } private static void DrawIoExceptions(MdlTab tab) @@ -205,6 +217,24 @@ public partial class ModEditWindow 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) { if (!ImGui.CollapsingHeader("Materials")) From c752835d2c3d59692815b3f565c61b1a17bd3d71 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 20 Jan 2024 13:12:08 +0100 Subject: [PATCH 5/8] Make CopySettings save even for unused settings. --- Penumbra/Collections/Manager/CollectionEditor.cs | 9 +++++++-- .../Interop/Hooks/Objects/CharacterBaseDestructor.cs | 4 ++-- Penumbra/Interop/PathResolving/DrawObjectState.cs | 1 - 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Penumbra/Collections/Manager/CollectionEditor.cs b/Penumbra/Collections/Manager/CollectionEditor.cs index f0b4d509..73950942 100644 --- a/Penumbra/Collections/Manager/CollectionEditor.cs +++ b/Penumbra/Collections/Manager/CollectionEditor.cs @@ -156,9 +156,14 @@ public class CollectionEditor // Either copy the unused source settings directly if they are not inheriting, // or remove any unused settings for the target if they are inheriting. if (savedSettings != null) + { ((Dictionary)collection.UnusedSettings)[targetName] = savedSettings.Value; - else - ((Dictionary)collection.UnusedSettings).Remove(targetName); + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); + } + else if (((Dictionary)collection.UnusedSettings).Remove(targetName)) + { + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); + } } return true; diff --git a/Penumbra/Interop/Hooks/Objects/CharacterBaseDestructor.cs b/Penumbra/Interop/Hooks/Objects/CharacterBaseDestructor.cs index fc6dbfe6..e01a6550 100644 --- a/Penumbra/Interop/Hooks/Objects/CharacterBaseDestructor.cs +++ b/Penumbra/Interop/Hooks/Objects/CharacterBaseDestructor.cs @@ -10,10 +10,10 @@ public sealed unsafe class CharacterBaseDestructor : EventWrapperPtr + /// DrawObjectState = 0, - /// + /// MtrlTab = -1000, } diff --git a/Penumbra/Interop/PathResolving/DrawObjectState.cs b/Penumbra/Interop/PathResolving/DrawObjectState.cs index dd4b03f2..b3ae108b 100644 --- a/Penumbra/Interop/PathResolving/DrawObjectState.cs +++ b/Penumbra/Interop/PathResolving/DrawObjectState.cs @@ -3,7 +3,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using OtterGui.Services; -using Penumbra.Interop.Hooks; using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object; using Penumbra.GameData.Structs; using Penumbra.Interop.Hooks.Objects; From 153b1e0d83170547ce77aa8e0ff2611a1b7d8f64 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 20 Jan 2024 13:17:12 +0100 Subject: [PATCH 6/8] Meep --- OtterGui | 2 +- Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/OtterGui b/OtterGui index 5d0aed2b..c6f101bb 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 5d0aed2b32a61654321a6616689932635cb35dde +Subproject commit c6f101bbef976b74eb651523445563dd81fafbaf diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index 1fb744ae..022f48f1 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -419,11 +419,9 @@ public partial class ModEditWindow private static bool DrawOtherModelDetails(MdlFile file, bool _) { - if (!ImGui.CollapsingHeader("Further Content")) + if (!ImRaii.CollapsingHeader("Further Content")) return false; - using var furtherContentId = ImRaii.PushId("furtherContent"); - using (var table = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit)) { if (table) From 3debd470643ccee23cdfc109ebb39c7012d7a020 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 20 Jan 2024 13:33:51 +0100 Subject: [PATCH 7/8] stupid. --- Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index 022f48f1..d799834c 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -419,7 +419,8 @@ public partial class ModEditWindow private static bool DrawOtherModelDetails(MdlFile file, bool _) { - if (!ImRaii.CollapsingHeader("Further Content")) + using var header = ImRaii.CollapsingHeader("Further Content"); + if (!header) return false; using (var table = ImRaii.Table("##data", 2, ImGuiTableFlags.SizingFixedFit)) From edad7d9ec97863ff066da3588698d7342397074e Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 20 Jan 2024 13:47:03 +0100 Subject: [PATCH 8/8] Update persistent links. --- Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index d799834c..a12be0f6 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -15,8 +15,8 @@ namespace Penumbra.UI.AdvancedWindow; public partial class ModEditWindow { private const int MdlMaterialMaximum = 4; - private const string MdlImportDocumentation = @"https://github.com/xivdev/Penumbra/wiki/Model-IO#import"; - private const string MdlExportDocumentation = @"https://github.com/xivdev/Penumbra/wiki/Model-IO#export"; + 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 _modelTab; private readonly ModelManager _models;