mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Fix material editor and improve pinning logic
This commit is contained in:
parent
5a5a1487a3
commit
cb0214ca2f
6 changed files with 110 additions and 71 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit e717a66f33b0656a7c5c971ffa2f63fd96477d94
|
Subproject commit b6b91f846096d15276b728ba2078f27b95317d15
|
||||||
|
|
@ -288,7 +288,7 @@ public class MaterialExporter
|
||||||
const uint valueFace = 0x6E5B8F10;
|
const uint valueFace = 0x6E5B8F10;
|
||||||
|
|
||||||
var isFace = material.Mtrl.ShaderPackage.ShaderKeys
|
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 normal = material.Textures[TextureUsage.SamplerNormal];
|
||||||
var mask = material.Textures[TextureUsage.SamplerMask];
|
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.
|
// Face is the default for the skin shader, so a lack of skin type category is also correct.
|
||||||
var isFace = !material.Mtrl.ShaderPackage.ShaderKeys
|
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: There's more nuance to skin than this, but this should be enough for a baseline reference.
|
||||||
// TODO: Specular?
|
// TODO: Specular?
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ public partial class MtrlTab
|
||||||
|
|
||||||
private bool DrawColorTableSection(bool disabled)
|
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;
|
return false;
|
||||||
|
|
||||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ public partial class MtrlTab
|
||||||
else
|
else
|
||||||
foreach (var (key, index) in Mtrl.ShaderPackage.ShaderKeys.WithIndex())
|
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);
|
var valueName = keyName.WithKnownSuffixes().TryResolve(Names.KnownNames, key.Value);
|
||||||
_shaderKeys.Add((keyName.ToString(), index, string.Empty, true, [(valueName.ToString(), key.Value, string.Empty)]));
|
_shaderKeys.Add((keyName.ToString(), index, string.Empty, true, [(valueName.ToString(), key.Value, string.Empty)]));
|
||||||
}
|
}
|
||||||
|
|
@ -366,6 +366,7 @@ public partial class MtrlTab
|
||||||
ret = true;
|
ret = true;
|
||||||
_associatedShpk = null;
|
_associatedShpk = null;
|
||||||
_loadedShpkPath = FullPath.Empty;
|
_loadedShpkPath = FullPath.Empty;
|
||||||
|
UnpinResources(true);
|
||||||
LoadShpk(FindAssociatedShpk(out _, out _));
|
LoadShpk(FindAssociatedShpk(out _, out _));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -442,8 +443,8 @@ public partial class MtrlTab
|
||||||
{
|
{
|
||||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont);
|
using var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont);
|
||||||
ref var key = ref Mtrl.ShaderPackage.ShaderKeys[index];
|
ref var key = ref Mtrl.ShaderPackage.ShaderKeys[index];
|
||||||
using var id = ImUtf8.PushId((int)key.Category);
|
using var id = ImUtf8.PushId((int)key.Key);
|
||||||
var shpkKey = _associatedShpk?.GetMaterialKeyById(key.Category);
|
var shpkKey = _associatedShpk?.GetMaterialKeyById(key.Key);
|
||||||
var currentValue = key.Value;
|
var currentValue = key.Value;
|
||||||
var (currentLabel, _, currentDescription) =
|
var (currentLabel, _, currentDescription) =
|
||||||
values.FirstOrNull(v => v.Value == currentValue) ?? ($"0x{currentValue:X8}", currentValue, string.Empty);
|
values.FirstOrNull(v => v.Value == currentValue) ?? ($"0x{currentValue:X8}", currentValue, string.Empty);
|
||||||
|
|
@ -459,6 +460,7 @@ public partial class MtrlTab
|
||||||
{
|
{
|
||||||
key.Value = value;
|
key.Value = value;
|
||||||
ret = true;
|
ret = true;
|
||||||
|
UnpinResources(false);
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using OtterGui.Text;
|
using OtterGui.Text;
|
||||||
using Penumbra.GameData;
|
|
||||||
using Penumbra.GameData.Files.MaterialStructs;
|
using Penumbra.GameData.Files.MaterialStructs;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using static Penumbra.GameData.Files.MaterialStructs.SamplerFlags;
|
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 List<(string Label, int TextureIndex, int SamplerIndex, string Description, bool MonoFont)> Textures = new(4);
|
||||||
|
|
||||||
public readonly HashSet<int> UnfoldedTextures = new(4);
|
public readonly HashSet<int> UnfoldedTextures = new(4);
|
||||||
|
public readonly HashSet<uint> TextureIds = new(16);
|
||||||
public readonly HashSet<uint> SamplerIds = new(16);
|
public readonly HashSet<uint> SamplerIds = new(16);
|
||||||
public float TextureLabelWidth;
|
public float TextureLabelWidth;
|
||||||
|
private bool _samplersPinned;
|
||||||
|
|
||||||
private void UpdateTextures()
|
private void UpdateTextures()
|
||||||
{
|
{
|
||||||
Textures.Clear();
|
Textures.Clear();
|
||||||
|
TextureIds.Clear();
|
||||||
SamplerIds.Clear();
|
SamplerIds.Clear();
|
||||||
if (_associatedShpk == null)
|
if (_associatedShpk == null)
|
||||||
{
|
{
|
||||||
|
TextureIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId));
|
||||||
SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId));
|
SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId));
|
||||||
if (Mtrl.Table != null)
|
if (Mtrl.Table != null)
|
||||||
SamplerIds.Add(TableSamplerId);
|
TextureIds.Add(TableSamplerId);
|
||||||
|
|
||||||
foreach (var (sampler, index) in Mtrl.ShaderPackage.Samplers.WithIndex())
|
foreach (var (sampler, index) in Mtrl.ShaderPackage.Samplers.WithIndex())
|
||||||
Textures.Add(($"0x{sampler.SamplerId:X8}", sampler.TextureIndex, index, string.Empty, true));
|
Textures.Add(($"0x{sampler.SamplerId:X8}", sampler.TextureIndex, index, string.Empty, true));
|
||||||
|
|
@ -35,31 +38,39 @@ public partial class MtrlTab
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var index in _vertexShaders)
|
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));
|
TextureIds.UnionWith(_associatedShpk.VertexShaders[index].Textures.Select(texture => texture.Id));
|
||||||
if (Mtrl.Table != null)
|
SamplerIds.UnionWith(_associatedShpk.VertexShaders[index].Samplers.Select(sampler => sampler.Id));
|
||||||
SamplerIds.Add(TableSamplerId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var samplerId in SamplerIds)
|
foreach (var index in _pixelShaders)
|
||||||
{
|
{
|
||||||
var shpkSampler = _associatedShpk.GetSamplerById(samplerId);
|
TextureIds.UnionWith(_associatedShpk.PixelShaders[index].Textures.Select(texture => texture.Id));
|
||||||
if (shpkSampler is not { Slot: 2 })
|
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;
|
continue;
|
||||||
|
|
||||||
var dkData = TryGetShpkDevkitData<DevkitSampler>("Samplers", samplerId, true);
|
var dkData = TryGetShpkDevkitData<DevkitSampler>("Samplers", textureId, true);
|
||||||
var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label);
|
var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label);
|
||||||
|
|
||||||
var sampler = Mtrl.GetOrAddSampler(samplerId, dkData?.DefaultTexture ?? string.Empty, out var samplerIndex);
|
var sampler = Mtrl.GetOrAddSampler(textureId, dkData?.DefaultTexture ?? string.Empty, out var samplerIndex);
|
||||||
Textures.Add((hasDkLabel ? dkData!.Label : shpkSampler.Value.Name, sampler.TextureIndex, samplerIndex,
|
Textures.Add((hasDkLabel ? dkData!.Label : shpkTexture.Value.Name, sampler.TextureIndex, samplerIndex,
|
||||||
dkData?.Description ?? string.Empty, !hasDkLabel));
|
dkData?.Description ?? string.Empty, !hasDkLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SamplerIds.Contains(TableSamplerId))
|
if (TextureIds.Contains(TableSamplerId))
|
||||||
Mtrl.Table ??= new ColorTable();
|
Mtrl.Table ??= new ColorTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,58 +216,67 @@ public partial class MtrlTab
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var samplerFlags = ref Wrap(ref sampler.Flags);
|
if (SamplerIds.Contains(sampler.SamplerId))
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f);
|
|
||||||
var addressMode = samplerFlags.UAddressMode;
|
|
||||||
if (ComboTextureAddressMode("##UAddressMode"u8, ref addressMode))
|
|
||||||
{
|
{
|
||||||
samplerFlags.UAddressMode = addressMode;
|
ref var samplerFlags = ref Wrap(ref sampler.Flags);
|
||||||
ret = true;
|
|
||||||
SetSamplerFlags(sampler.SamplerId, 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.");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
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;
|
ImUtf8.Text("This texture does not have a dedicated sampler."u8);
|
||||||
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.");
|
|
||||||
|
|
||||||
using var t = ImUtf8.TreeNode("Advanced Settings"u8);
|
using var t = ImUtf8.TreeNode("Advanced Settings"u8);
|
||||||
if (!t)
|
if (!t)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
|
||||||
Mtrl = file;
|
Mtrl = file;
|
||||||
FilePath = filePath;
|
FilePath = filePath;
|
||||||
Writable = writable;
|
Writable = writable;
|
||||||
|
_samplersPinned = true;
|
||||||
_associatedBaseDevkit = TryLoadShpkDevkit("_base", out _loadedBaseDevkitPathName);
|
_associatedBaseDevkit = TryLoadShpkDevkit("_base", out _loadedBaseDevkitPathName);
|
||||||
Update();
|
Update();
|
||||||
LoadShpk(FindAssociatedShpk(out _, out _));
|
LoadShpk(FindAssociatedShpk(out _, out _));
|
||||||
|
|
@ -172,6 +173,22 @@ public sealed partial class MtrlTab : IWritable, IDisposable
|
||||||
Widget.DrawHexViewer(Mtrl.AdditionalData);
|
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()
|
private void Update()
|
||||||
{
|
{
|
||||||
UpdateShaders();
|
UpdateShaders();
|
||||||
|
|
@ -192,7 +209,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
|
||||||
public byte[] Write()
|
public byte[] Write()
|
||||||
{
|
{
|
||||||
var output = Mtrl.Clone();
|
var output = Mtrl.Clone();
|
||||||
output.GarbageCollect(_associatedShpk, SamplerIds);
|
output.GarbageCollect(_associatedShpk, TextureIds);
|
||||||
|
|
||||||
return output.Write();
|
return output.Write();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue