From f13fa3e589b44c2877b4cb01598ab5336ee46d6f Mon Sep 17 00:00:00 2001 From: Exter-N Date: Sat, 29 Mar 2025 16:52:08 +0100 Subject: [PATCH] Fix material editor and improve pinning logic --- Penumbra.GameData | 2 +- .../Import/Models/Export/MaterialExporter.cs | 4 +- .../Materials/MtrlTab.CommonColorTable.cs | 2 +- .../Materials/MtrlTab.ShaderPackage.cs | 8 +- .../Materials/MtrlTab.Textures.cs | 146 ++++++++++-------- .../UI/AdvancedWindow/Materials/MtrlTab.cs | 19 ++- 6 files changed, 110 insertions(+), 71 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index e717a66f..b6b91f84 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit e717a66f33b0656a7c5c971ffa2f63fd96477d94 +Subproject commit b6b91f846096d15276b728ba2078f27b95317d15 diff --git a/Penumbra/Import/Models/Export/MaterialExporter.cs b/Penumbra/Import/Models/Export/MaterialExporter.cs index 121e6eed..6be2ccbd 100644 --- a/Penumbra/Import/Models/Export/MaterialExporter.cs +++ b/Penumbra/Import/Models/Export/MaterialExporter.cs @@ -288,7 +288,7 @@ public class MaterialExporter const uint valueFace = 0x6E5B8F10; var isFace = material.Mtrl.ShaderPackage.ShaderKeys - .Any(key => key is { Category: categoryHairType, Value: valueFace }); + .Any(key => key is { Key: categoryHairType, Value: valueFace }); var normal = material.Textures[TextureUsage.SamplerNormal]; var mask = material.Textures[TextureUsage.SamplerMask]; @@ -363,7 +363,7 @@ public class MaterialExporter // Face is the default for the skin shader, so a lack of skin type category is also correct. var isFace = !material.Mtrl.ShaderPackage.ShaderKeys - .Any(key => key.Category == categorySkinType && key.Value != valueFace); + .Any(key => key.Key == categorySkinType && key.Value != valueFace); // TODO: There's more nuance to skin than this, but this should be enough for a baseline reference. // TODO: Specular? diff --git a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.CommonColorTable.cs b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.CommonColorTable.cs index 236a66c3..d70a4b50 100644 --- a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.CommonColorTable.cs +++ b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.CommonColorTable.cs @@ -22,7 +22,7 @@ public partial class MtrlTab private bool DrawColorTableSection(bool disabled) { - if (!_shpkLoading && !SamplerIds.Contains(ShpkFile.TableSamplerId) || Mtrl.Table == null) + if (!_shpkLoading && !TextureIds.Contains(ShpkFile.TableSamplerId) || Mtrl.Table == null) return false; ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); diff --git a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.ShaderPackage.cs b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.ShaderPackage.cs index a13dd96b..202047e4 100644 --- a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.ShaderPackage.cs +++ b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.ShaderPackage.cs @@ -216,7 +216,7 @@ public partial class MtrlTab else foreach (var (key, index) in Mtrl.ShaderPackage.ShaderKeys.WithIndex()) { - var keyName = Names.KnownNames.TryResolve(key.Category); + var keyName = Names.KnownNames.TryResolve(key.Key); var valueName = keyName.WithKnownSuffixes().TryResolve(Names.KnownNames, key.Value); _shaderKeys.Add((keyName.ToString(), index, string.Empty, true, [(valueName.ToString(), key.Value, string.Empty)])); } @@ -366,6 +366,7 @@ public partial class MtrlTab ret = true; _associatedShpk = null; _loadedShpkPath = FullPath.Empty; + UnpinResources(true); LoadShpk(FindAssociatedShpk(out _, out _)); } @@ -442,8 +443,8 @@ public partial class MtrlTab { using var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont); ref var key = ref Mtrl.ShaderPackage.ShaderKeys[index]; - using var id = ImUtf8.PushId((int)key.Category); - var shpkKey = _associatedShpk?.GetMaterialKeyById(key.Category); + using var id = ImUtf8.PushId((int)key.Key); + var shpkKey = _associatedShpk?.GetMaterialKeyById(key.Key); var currentValue = key.Value; var (currentLabel, _, currentDescription) = values.FirstOrNull(v => v.Value == currentValue) ?? ($"0x{currentValue:X8}", currentValue, string.Empty); @@ -459,6 +460,7 @@ public partial class MtrlTab { key.Value = value; ret = true; + UnpinResources(false); Update(); } diff --git a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.Textures.cs b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.Textures.cs index 7ab2900d..dfa07d52 100644 --- a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.Textures.cs +++ b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.Textures.cs @@ -3,7 +3,6 @@ using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Text; -using Penumbra.GameData; using Penumbra.GameData.Files.MaterialStructs; using Penumbra.String.Classes; using static Penumbra.GameData.Files.MaterialStructs.SamplerFlags; @@ -16,18 +15,22 @@ public partial class MtrlTab public readonly List<(string Label, int TextureIndex, int SamplerIndex, string Description, bool MonoFont)> Textures = new(4); public readonly HashSet UnfoldedTextures = new(4); + public readonly HashSet TextureIds = new(16); public readonly HashSet SamplerIds = new(16); public float TextureLabelWidth; + private bool _samplersPinned; private void UpdateTextures() { Textures.Clear(); + TextureIds.Clear(); SamplerIds.Clear(); if (_associatedShpk == null) { + TextureIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId)); SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId)); if (Mtrl.Table != null) - SamplerIds.Add(TableSamplerId); + TextureIds.Add(TableSamplerId); foreach (var (sampler, index) in Mtrl.ShaderPackage.Samplers.WithIndex()) Textures.Add(($"0x{sampler.SamplerId:X8}", sampler.TextureIndex, index, string.Empty, true)); @@ -35,31 +38,39 @@ public partial class MtrlTab else { foreach (var index in _vertexShaders) - SamplerIds.UnionWith(_associatedShpk.VertexShaders[index].Samplers.Select(sampler => sampler.Id)); - foreach (var index in _pixelShaders) - SamplerIds.UnionWith(_associatedShpk.PixelShaders[index].Samplers.Select(sampler => sampler.Id)); - if (!_shadersKnown) { - SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId)); - if (Mtrl.Table != null) - SamplerIds.Add(TableSamplerId); + TextureIds.UnionWith(_associatedShpk.VertexShaders[index].Textures.Select(texture => texture.Id)); + SamplerIds.UnionWith(_associatedShpk.VertexShaders[index].Samplers.Select(sampler => sampler.Id)); } - foreach (var samplerId in SamplerIds) + foreach (var index in _pixelShaders) { - var shpkSampler = _associatedShpk.GetSamplerById(samplerId); - if (shpkSampler is not { Slot: 2 }) + TextureIds.UnionWith(_associatedShpk.PixelShaders[index].Textures.Select(texture => texture.Id)); + SamplerIds.UnionWith(_associatedShpk.PixelShaders[index].Samplers.Select(sampler => sampler.Id)); + } + + if (_samplersPinned || !_shadersKnown) + { + TextureIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId)); + if (Mtrl.Table != null) + TextureIds.Add(TableSamplerId); + } + + foreach (var textureId in TextureIds) + { + var shpkTexture = _associatedShpk.GetTextureById(textureId); + if (shpkTexture is not { Slot: 2 }) continue; - var dkData = TryGetShpkDevkitData("Samplers", samplerId, true); + var dkData = TryGetShpkDevkitData("Samplers", textureId, true); var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label); - var sampler = Mtrl.GetOrAddSampler(samplerId, dkData?.DefaultTexture ?? string.Empty, out var samplerIndex); - Textures.Add((hasDkLabel ? dkData!.Label : shpkSampler.Value.Name, sampler.TextureIndex, samplerIndex, + var sampler = Mtrl.GetOrAddSampler(textureId, dkData?.DefaultTexture ?? string.Empty, out var samplerIndex); + Textures.Add((hasDkLabel ? dkData!.Label : shpkTexture.Value.Name, sampler.TextureIndex, samplerIndex, dkData?.Description ?? string.Empty, !hasDkLabel)); } - if (SamplerIds.Contains(TableSamplerId)) + if (TextureIds.Contains(TableSamplerId)) Mtrl.Table ??= new ColorTable(); } @@ -205,58 +216,67 @@ public partial class MtrlTab ret = true; } - ref var samplerFlags = ref Wrap(ref sampler.Flags); - - ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); - var addressMode = samplerFlags.UAddressMode; - if (ComboTextureAddressMode("##UAddressMode"u8, ref addressMode)) + if (SamplerIds.Contains(sampler.SamplerId)) { - samplerFlags.UAddressMode = addressMode; - ret = true; - SetSamplerFlags(sampler.SamplerId, sampler.Flags); + ref var samplerFlags = ref Wrap(ref sampler.Flags); + + ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); + var addressMode = samplerFlags.UAddressMode; + if (ComboTextureAddressMode("##UAddressMode"u8, ref addressMode)) + { + samplerFlags.UAddressMode = addressMode; + ret = true; + SetSamplerFlags(sampler.SamplerId, sampler.Flags); + } + + ImGui.SameLine(); + ImUtf8.LabeledHelpMarker("U Address Mode"u8, + "Method to use for resolving a U texture coordinate that is outside the 0 to 1 range."); + + ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); + addressMode = samplerFlags.VAddressMode; + if (ComboTextureAddressMode("##VAddressMode"u8, ref addressMode)) + { + samplerFlags.VAddressMode = addressMode; + ret = true; + SetSamplerFlags(sampler.SamplerId, sampler.Flags); + } + + ImGui.SameLine(); + ImUtf8.LabeledHelpMarker("V Address Mode"u8, + "Method to use for resolving a V texture coordinate that is outside the 0 to 1 range."); + + var lodBias = samplerFlags.LodBias; + ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); + if (ImUtf8.DragScalar("##LoDBias"u8, ref lodBias, -8.0f, 7.984375f, 0.1f)) + { + samplerFlags.LodBias = lodBias; + ret = true; + SetSamplerFlags(sampler.SamplerId, sampler.Flags); + } + + ImGui.SameLine(); + ImUtf8.LabeledHelpMarker("Level of Detail Bias"u8, + "Offset from the calculated mipmap level.\n\nHigher means that the texture will start to lose detail nearer.\nLower means that the texture will keep its detail until farther."); + + var minLod = samplerFlags.MinLod; + ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); + if (ImUtf8.DragScalar("##MinLoD"u8, ref minLod, 0, 15, 0.1f)) + { + samplerFlags.MinLod = minLod; + ret = true; + SetSamplerFlags(sampler.SamplerId, sampler.Flags); + } + + ImGui.SameLine(); + ImUtf8.LabeledHelpMarker("Minimum Level of Detail"u8, + "Most detailed mipmap level to use.\n\n0 is the full-sized texture, 1 is the half-sized texture, 2 is the quarter-sized texture, and so on.\n15 will forcibly reduce the texture to its smallest mipmap."); } - - ImGui.SameLine(); - ImUtf8.LabeledHelpMarker("U Address Mode"u8, "Method to use for resolving a U texture coordinate that is outside the 0 to 1 range."); - - ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); - addressMode = samplerFlags.VAddressMode; - if (ComboTextureAddressMode("##VAddressMode"u8, ref addressMode)) + else { - samplerFlags.VAddressMode = addressMode; - ret = true; - SetSamplerFlags(sampler.SamplerId, sampler.Flags); + ImUtf8.Text("This texture does not have a dedicated sampler."u8); } - ImGui.SameLine(); - ImUtf8.LabeledHelpMarker("V Address Mode"u8, "Method to use for resolving a V texture coordinate that is outside the 0 to 1 range."); - - var lodBias = samplerFlags.LodBias; - ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); - if (ImUtf8.DragScalar("##LoDBias"u8, ref lodBias, -8.0f, 7.984375f, 0.1f)) - { - samplerFlags.LodBias = lodBias; - ret = true; - SetSamplerFlags(sampler.SamplerId, sampler.Flags); - } - - ImGui.SameLine(); - ImUtf8.LabeledHelpMarker("Level of Detail Bias"u8, - "Offset from the calculated mipmap level.\n\nHigher means that the texture will start to lose detail nearer.\nLower means that the texture will keep its detail until farther."); - - var minLod = samplerFlags.MinLod; - ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); - if (ImUtf8.DragScalar("##MinLoD"u8, ref minLod, 0, 15, 0.1f)) - { - samplerFlags.MinLod = minLod; - ret = true; - SetSamplerFlags(sampler.SamplerId, sampler.Flags); - } - - ImGui.SameLine(); - ImUtf8.LabeledHelpMarker("Minimum Level of Detail"u8, - "Most detailed mipmap level to use.\n\n0 is the full-sized texture, 1 is the half-sized texture, 2 is the quarter-sized texture, and so on.\n15 will forcibly reduce the texture to its smallest mipmap."); - using var t = ImUtf8.TreeNode("Advanced Settings"u8); if (!t) return ret; diff --git a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs index 6e16de99..97acf130 100644 --- a/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs +++ b/Penumbra/UI/AdvancedWindow/Materials/MtrlTab.cs @@ -57,6 +57,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable Mtrl = file; FilePath = filePath; Writable = writable; + _samplersPinned = true; _associatedBaseDevkit = TryLoadShpkDevkit("_base", out _loadedBaseDevkitPathName); Update(); LoadShpk(FindAssociatedShpk(out _, out _)); @@ -172,6 +173,22 @@ public sealed partial class MtrlTab : IWritable, IDisposable Widget.DrawHexViewer(Mtrl.AdditionalData); } + private void UnpinResources(bool all) + { + _samplersPinned = false; + + if (!all) + return; + + var keys = Mtrl.ShaderPackage.ShaderKeys; + for (var i = 0; i < keys.Length; i++) + keys[i].Pinned = false; + + var constants = Mtrl.ShaderPackage.Constants; + for (var i = 0; i < constants.Length; i++) + constants[i].Pinned = false; + } + private void Update() { UpdateShaders(); @@ -192,7 +209,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable public byte[] Write() { var output = Mtrl.Clone(); - output.GarbageCollect(_associatedShpk, SamplerIds); + output.GarbageCollect(_associatedShpk, TextureIds); return output.Write(); }