Fix material editor and improve pinning logic

This commit is contained in:
Exter-N 2025-03-29 16:52:08 +01:00 committed by Ottermandias
parent 5a5a1487a3
commit cb0214ca2f
6 changed files with 110 additions and 71 deletions

@ -1 +1 @@
Subproject commit e717a66f33b0656a7c5c971ffa2f63fd96477d94
Subproject commit b6b91f846096d15276b728ba2078f27b95317d15

View file

@ -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?

View file

@ -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));

View file

@ -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();
}

View file

@ -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<int> UnfoldedTextures = new(4);
public readonly HashSet<uint> TextureIds = new(16);
public readonly HashSet<uint> 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<DevkitSampler>("Samplers", samplerId, true);
var dkData = TryGetShpkDevkitData<DevkitSampler>("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,6 +216,8 @@ public partial class MtrlTab
ret = true;
}
if (SamplerIds.Contains(sampler.SamplerId))
{
ref var samplerFlags = ref Wrap(ref sampler.Flags);
ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f);
@ -217,7 +230,8 @@ public partial class MtrlTab
}
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.");
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;
@ -229,7 +243,8 @@ public partial class MtrlTab
}
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.");
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);
@ -256,6 +271,11 @@ public partial class MtrlTab
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.");
}
else
{
ImUtf8.Text("This texture does not have a dedicated sampler."u8);
}
using var t = ImUtf8.TreeNode("Advanced Settings"u8);
if (!t)

View file

@ -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();
}