From 2e6cc736669a043ebe8868e1dd1901ca2ef97660 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 25 Feb 2023 18:11:21 +0100 Subject: [PATCH] Rework Samplers. --- .../ModEditWindow.Materials.MtrlTab.cs | 100 +++++-- .../Classes/ModEditWindow.Materials.Shpk.cs | 277 +++++++++--------- 2 files changed, 214 insertions(+), 163 deletions(-) diff --git a/Penumbra/UI/Classes/ModEditWindow.Materials.MtrlTab.cs b/Penumbra/UI/Classes/ModEditWindow.Materials.MtrlTab.cs index 5f0c50f7..8c8cc508 100644 --- a/Penumbra/UI/Classes/ModEditWindow.Materials.MtrlTab.cs +++ b/Penumbra/UI/Classes/ModEditWindow.Materials.MtrlTab.cs @@ -23,18 +23,19 @@ public partial class ModEditWindow private readonly ModEditWindow _edit; public readonly MtrlFile Mtrl; - public uint MaterialNewKeyId = 0; - public uint MaterialNewKeyDefault = 0; - public uint MaterialNewConstantId = 0; - public int MaterialNewConstantIdx = 0; - public uint MaterialNewSamplerId = 0; + public uint NewKeyId; + public uint NewKeyDefault; + public uint NewConstantId; + public int NewConstantIdx; + public uint NewSamplerId; + public int NewSamplerIdx; public ShpkFile? AssociatedShpk; public readonly List< string > TextureLabels = new(4); public FullPath LoadedShpkPath = FullPath.Empty; public string LoadedShpkPathName = string.Empty; - public float TextureLabelWidth = 0f; + public float TextureLabelWidth; // Shader Key State public readonly List< string > ShaderKeyLabels = new(16); @@ -45,14 +46,21 @@ public partial class ModEditWindow public string PixelShaders = "Pixel Shaders: ???"; // Material Constants - public List< (string Name, bool ComponentOnly, int ParamValueOffset) > MaterialConstants = new(16); - public List< (string Name, uint Id, ushort ByteSize) > MissingMaterialConstants = new(16); + public readonly List< (string Name, bool ComponentOnly, int ParamValueOffset) > MaterialConstants = new(16); + public readonly List< (string Name, uint Id, ushort ByteSize) > MissingMaterialConstants = new(16); + public readonly HashSet< uint > DefinedMaterialConstants = new(16); - public string MaterialConstantLabel = "Constants###Constants"; - public bool HasMalformedMaterialConstants = false; - public IndexSet OrphanedMaterialValues = new(0, false); - public HashSet< uint > DefinedMaterialConstants = new(16); - public int AliasedMaterialValueCount = 0; + public string MaterialConstantLabel = "Constants###Constants"; + public IndexSet OrphanedMaterialValues = new(0, false); + public int AliasedMaterialValueCount; + public bool HasMalformedMaterialConstants; + + // Samplers + public readonly List< (string Label, string FileName) > Samplers = new(4); + public readonly List< (string Name, uint Id) > MissingSamplers = new(4); + public readonly HashSet< uint > DefinedSamplers = new(4); + public IndexSet OrphanedSamplers = new(0, false); + public int AliasedSamplerCount; public FullPath FindAssociatedShpk( out string defaultPath, out Utf8GamePath defaultGamePath ) { @@ -124,11 +132,11 @@ public partial class ModEditWindow { MissingShaderKeyIndices.AddRange( AssociatedShpk.MaterialKeys.WithIndex().Where( k => !DefinedShaderKeys.ContainsKey( k.Value.Id ) ).WithoutValue() ); - if( MissingShaderKeyIndices.Count > 0 && MissingShaderKeyIndices.All( i => AssociatedShpk.MaterialKeys[ i ].Id != MaterialNewKeyId ) ) + if( MissingShaderKeyIndices.Count > 0 && MissingShaderKeyIndices.All( i => AssociatedShpk.MaterialKeys[ i ].Id != NewKeyId ) ) { var key = AssociatedShpk.MaterialKeys[ MissingShaderKeyIndices[ 0 ] ]; - MaterialNewKeyId = key.Id; - MaterialNewKeyDefault = key.DefaultValue; + NewKeyId = key.Id; + NewKeyDefault = key.DefaultValue; } AvailableKeyValues.AddRange( AssociatedShpk.MaterialKeys.Select( k => DefinedShaderKeys.TryGetValue( k.Id, out var value ) ? value : k.DefaultValue ) ); @@ -193,24 +201,69 @@ public partial class ModEditWindow if( AssociatedShpk != null ) { var setIdx = false; - foreach( var param in AssociatedShpk.MaterialParams.Where( m => !DefinedMaterialConstants.Contains(m.Id)) ) + foreach( var param in AssociatedShpk.MaterialParams.Where( m => !DefinedMaterialConstants.Contains( m.Id ) ) ) { var (name, _) = MaterialParamRangeName( prefix, param.ByteOffset >> 2, param.ByteSize >> 2 ); var label = name == null ? $"(ID: 0x{param.Id:X8})" : $"{name} (ID: 0x{param.Id:X8})"; - if( MaterialNewConstantId == param.Id ) + if( NewConstantId == param.Id ) { - setIdx = true; - MaterialNewConstantIdx = MissingMaterialConstants.Count; + setIdx = true; + NewConstantIdx = MissingMaterialConstants.Count; } + MissingMaterialConstants.Add( ( label, param.Id, param.ByteSize ) ); } - if (!setIdx && MissingMaterialConstants.Count > 0) + if( !setIdx && MissingMaterialConstants.Count > 0 ) { - MaterialNewConstantIdx = 0; - MaterialNewConstantId = MissingMaterialConstants[ 0 ].Id; + NewConstantIdx = 0; + NewConstantId = MissingMaterialConstants[ 0 ].Id; + } + } + } + + public void UpdateSamplers() + { + Samplers.Clear(); + DefinedSamplers.Clear(); + OrphanedSamplers = new IndexSet( Mtrl.Textures.Length, true ); + foreach( var (sampler, idx) in Mtrl.ShaderPackage.Samplers.WithIndex() ) + { + DefinedSamplers.Add( sampler.SamplerId ); + if( !OrphanedSamplers.Remove( sampler.TextureIndex ) ) + { + ++AliasedSamplerCount; + } + + var shpk = AssociatedShpk?.GetSamplerById( sampler.SamplerId ); + var label = shpk.HasValue + ? $"#{idx}: {shpk.Value.Name} (ID: 0x{sampler.SamplerId:X8})##{sampler.SamplerId}" + : $"#{idx} (ID: 0x{sampler.SamplerId:X8})##{sampler.SamplerId}"; + var fileName = $"Texture #{sampler.TextureIndex} - {Path.GetFileName( Mtrl.Textures[ sampler.TextureIndex ].Path )}"; + Samplers.Add( ( label, fileName ) ); + } + + MissingSamplers.Clear(); + if( AssociatedShpk != null ) + { + var setSampler = false; + foreach( var sampler in AssociatedShpk.Samplers.Where( s => s.Slot == 2 && !DefinedSamplers.Contains( s.Id ) ) ) + { + if( sampler.Id == NewSamplerId ) + { + setSampler = true; + NewSamplerIdx = MissingSamplers.Count; + } + + MissingSamplers.Add( ( sampler.Name, sampler.Id ) ); + } + + if( !setSampler && MissingSamplers.Count > 0 ) + { + NewSamplerIdx = 0; + NewSamplerId = MissingSamplers[ 0 ].Id; } } } @@ -220,6 +273,7 @@ public partial class ModEditWindow UpdateTextureLabels(); UpdateShaderKeyLabels(); UpdateConstantLabels(); + UpdateSamplers(); } public MtrlTab( ModEditWindow edit, MtrlFile file ) diff --git a/Penumbra/UI/Classes/ModEditWindow.Materials.Shpk.cs b/Penumbra/UI/Classes/ModEditWindow.Materials.Shpk.cs index 12780a09..14719f88 100644 --- a/Penumbra/UI/Classes/ModEditWindow.Materials.Shpk.cs +++ b/Penumbra/UI/Classes/ModEditWindow.Materials.Shpk.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; @@ -9,7 +8,6 @@ using Dalamud.Interface.ImGuiFileDialog; using ImGuiNET; using Lumina.Data.Parsing; using OtterGui; -using OtterGui.Classes; using OtterGui.Raii; using Penumbra.GameData; using Penumbra.GameData.Files; @@ -150,7 +148,7 @@ public partial class ModEditWindow { ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f ); var ret = false; - using( var c = ImRaii.Combo( "##NewConstantId", $"ID: 0x{tab.MaterialNewKeyId:X8}" ) ) + using( var c = ImRaii.Combo( "##NewConstantId", $"ID: 0x{tab.NewKeyId:X8}" ) ) { if( c ) { @@ -158,10 +156,10 @@ public partial class ModEditWindow { var key = tab.AssociatedShpk!.MaterialKeys[ idx ]; - if( ImGui.Selectable( $"ID: 0x{key.Id:X8}", key.Id == tab.MaterialNewKeyId ) ) + if( ImGui.Selectable( $"ID: 0x{key.Id:X8}", key.Id == tab.NewKeyId ) ) { - tab.MaterialNewKeyDefault = key.DefaultValue; - tab.MaterialNewKeyId = key.Id; + tab.NewKeyDefault = key.DefaultValue; + tab.NewKeyId = key.Id; ret = true; tab.UpdateShaderKeyLabels(); } @@ -174,8 +172,8 @@ public partial class ModEditWindow { tab.Mtrl.ShaderPackage.ShaderKeys = tab.Mtrl.ShaderPackage.ShaderKeys.AddItem( new ShaderKey { - Category = tab.MaterialNewKeyId, - Value = tab.MaterialNewKeyDefault, + Category = tab.NewKeyId, + Value = tab.NewKeyDefault, } ); ret = true; tab.UpdateShaderKeyLabels(); @@ -312,15 +310,15 @@ public partial class ModEditWindow ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 450.0f ); using( var font = ImRaii.PushFont( UiBuilder.MonoFont ) ) { - using var c = ImRaii.Combo( "##NewConstantId", tab.MissingMaterialConstants[ tab.MaterialNewConstantIdx ].Name ); + using var c = ImRaii.Combo( "##NewConstantId", tab.MissingMaterialConstants[ tab.NewConstantIdx ].Name ); if( c ) { foreach( var (constant, idx) in tab.MissingMaterialConstants.WithIndex() ) { - if( ImGui.Selectable( constant.Name, constant.Id == tab.MaterialNewConstantId ) ) + if( ImGui.Selectable( constant.Name, constant.Id == tab.NewConstantId ) ) { - tab.MaterialNewConstantIdx = idx; - tab.MaterialNewConstantId = constant.Id; + tab.NewConstantIdx = idx; + tab.NewConstantId = constant.Id; } } } @@ -329,10 +327,10 @@ public partial class ModEditWindow ImGui.SameLine(); if( ImGui.Button( "Add Constant" ) ) { - var (_, _, byteSize) = tab.MissingMaterialConstants[ tab.MaterialNewConstantIdx ]; + var (_, _, byteSize) = tab.MissingMaterialConstants[ tab.NewConstantIdx ]; tab.Mtrl.ShaderPackage.Constants = tab.Mtrl.ShaderPackage.Constants.AddItem( new MtrlFile.Constant { - Id = tab.MaterialNewConstantId, + Id = tab.NewConstantId, ByteOffset = ( ushort )( tab.Mtrl.ShaderPackage.ShaderValues.Length << 2 ), ByteSize = byteSize, } ); @@ -379,146 +377,145 @@ public partial class ModEditWindow return ret; } - - private bool DrawMaterialSamplers( MtrlTab tab, bool disabled ) + private static bool DrawMaterialSampler( MtrlTab tab, bool disabled, ref int idx ) { - var ret = false; - if( tab.Mtrl.ShaderPackage.Samplers.Length > 0 - || tab.Mtrl.Textures.Length > 0 - || !disabled && tab.AssociatedShpk != null && tab.AssociatedShpk.Samplers.Any( sampler => sampler.Slot == 2 ) ) + var (label, filename) = tab.Samplers[ idx ]; + using var tree = ImRaii.TreeNode( label ); + if( !tree ) + return false; + + ImRaii.TreeNode( filename, ImGuiTreeNodeFlags.Leaf ).Dispose(); + var ret = false; + var sampler = tab.Mtrl.ShaderPackage.Samplers[ idx ]; + + // FIXME this probably doesn't belong here + static unsafe bool InputHexUInt16( string label, ref ushort v, ImGuiInputTextFlags flags ) { - using var t = ImRaii.TreeNode( "Samplers" ); - if( t ) + fixed( ushort* v2 = &v ) { - var orphanTextures = new IndexSet( tab.Mtrl.Textures.Length, true ); - var aliasedTextureCount = 0; - var definedSamplers = new HashSet< uint >(); + return ImGui.InputScalar( label, ImGuiDataType.U16, ( nint )v2, IntPtr.Zero, IntPtr.Zero, "%04X", flags ); + } + } - foreach( var sampler in tab.Mtrl.ShaderPackage.Samplers ) + ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f ); + if( InputHexUInt16( "Texture Flags", ref tab.Mtrl.Textures[sampler.TextureIndex].Flags, + disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) ) + { + ret = true; + } + + var samplerFlags = ( int )sampler.Flags; + ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f ); + if( ImGui.InputInt( "Sampler Flags", ref samplerFlags, 0, 0, + ImGuiInputTextFlags.CharsHexadecimal | ( disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) ) ) + { + tab.Mtrl.ShaderPackage.Samplers[idx].Flags = ( uint )samplerFlags; + ret = true; + } + + if( !disabled + && tab.OrphanedSamplers.Count == 0 + && tab.AliasedSamplerCount == 0 + && ImGui.Button( "Remove Sampler" ) ) + { + tab.Mtrl.Textures = tab.Mtrl.Textures.RemoveItems( sampler.TextureIndex ); + tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.RemoveItems( idx-- ); + for( var i = 0; i < tab.Mtrl.ShaderPackage.Samplers.Length; ++i ) + { + if( tab.Mtrl.ShaderPackage.Samplers[i].TextureIndex >= sampler.TextureIndex ) { - if( !orphanTextures.Remove( sampler.TextureIndex ) ) - { - ++aliasedTextureCount; - } - - definedSamplers.Add( sampler.SamplerId ); + --tab.Mtrl.ShaderPackage.Samplers[i].TextureIndex; } + } - foreach( var (sampler, idx) in tab.Mtrl.ShaderPackage.Samplers.WithIndex() ) + ret = true; + tab.UpdateSamplers(); + tab.UpdateTextureLabels(); + } + + return ret; + } + + private static bool DrawMaterialNewSampler( MtrlTab tab ) + { + var (name, id) = tab.MissingSamplers[tab.NewSamplerIdx]; + ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 450.0f ); + using( var c = ImRaii.Combo( "##NewSamplerId", $"{name} (ID: 0x{id:X8})" ) ) + { + if( c ) + { + foreach( var (sampler, idx) in tab.MissingSamplers.WithIndex() ) { - var shpkSampler = tab.AssociatedShpk?.GetSamplerById( sampler.SamplerId ); - using var t2 = ImRaii.TreeNode( $"#{idx}{( shpkSampler.HasValue ? ": " + shpkSampler.Value.Name : "" )} (ID: 0x{sampler.SamplerId:X8})" ); - if( t2 ) + if( ImGui.Selectable( $"{sampler.Name} (ID: 0x{sampler.Id:X8})", sampler.Id == tab.NewSamplerId ) ) { - ImRaii.TreeNode( $"Texture: #{sampler.TextureIndex} - {Path.GetFileName( tab.Mtrl.Textures[ sampler.TextureIndex ].Path )}", ImGuiTreeNodeFlags.Leaf ) - .Dispose(); - - // FIXME this probably doesn't belong here - static unsafe bool InputHexUInt16( string label, ref ushort v, ImGuiInputTextFlags flags ) - { - fixed( ushort* v2 = &v ) - { - return ImGui.InputScalar( label, ImGuiDataType.U16, ( nint )v2, nint.Zero, nint.Zero, "%04X", flags ); - } - } - - ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f ); - if( InputHexUInt16( "Texture Flags", ref tab.Mtrl.Textures[ sampler.TextureIndex ].Flags, - disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) ) - { - ret = true; - } - - var sampFlags = ( int )sampler.Flags; - ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 150.0f ); - if( ImGui.InputInt( "Sampler Flags", ref sampFlags, 0, 0, - ImGuiInputTextFlags.CharsHexadecimal | ( disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) ) ) - { - tab.Mtrl.ShaderPackage.Samplers[ idx ].Flags = ( uint )sampFlags; - ret = true; - } - - if( !disabled - && orphanTextures.Count == 0 - && aliasedTextureCount == 0 - && ImGui.Button( "Remove Sampler" ) ) - { - tab.Mtrl.Textures = tab.Mtrl.Textures.RemoveItems( sampler.TextureIndex ); - tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.RemoveItems( idx ); - for( var i = 0; i < tab.Mtrl.ShaderPackage.Samplers.Length; ++i ) - { - if( tab.Mtrl.ShaderPackage.Samplers[ i ].TextureIndex >= sampler.TextureIndex ) - { - --tab.Mtrl.ShaderPackage.Samplers[ i ].TextureIndex; - } - } - - ret = true; - } - } - } - - if( orphanTextures.Count > 0 ) - { - using var t2 = ImRaii.TreeNode( $"Orphan Textures ({orphanTextures.Count})" ); - if( t2 ) - { - foreach( var idx in orphanTextures ) - { - ImRaii.TreeNode( $"#{idx}: {Path.GetFileName( tab.Mtrl.Textures[ idx ].Path )} - {tab.Mtrl.Textures[ idx ].Flags:X4}", ImGuiTreeNodeFlags.Leaf ) - .Dispose(); - } - } - } - else if( !disabled && tab.AssociatedShpk != null && aliasedTextureCount == 0 && tab.Mtrl.Textures.Length < 255 ) - { - var missingSamplers = tab.AssociatedShpk.Samplers.Where( sampler => sampler.Slot == 2 && !definedSamplers.Contains( sampler.Id ) ).ToArray(); - if( missingSamplers.Length > 0 ) - { - var selectedSampler = Array.Find( missingSamplers, sampler => sampler.Id == tab.MaterialNewSamplerId ); - if( selectedSampler.Name == null ) - { - selectedSampler = missingSamplers[ 0 ]; - tab.MaterialNewSamplerId = selectedSampler.Id; - } - - ImGui.SetNextItemWidth( ImGuiHelpers.GlobalScale * 450.0f ); - using( var c = ImRaii.Combo( "##NewSamplerId", $"{selectedSampler.Name} (ID: 0x{selectedSampler.Id:X8})" ) ) - { - if( c ) - { - foreach( var sampler in missingSamplers ) - { - if( ImGui.Selectable( $"{sampler.Name} (ID: 0x{sampler.Id:X8})", sampler.Id == tab.MaterialNewSamplerId ) ) - { - selectedSampler = sampler; - tab.MaterialNewSamplerId = sampler.Id; - } - } - } - } - - ImGui.SameLine(); - if( ImGui.Button( "Add Sampler" ) ) - { - tab.Mtrl.Textures = tab.Mtrl.Textures.AddItem( new MtrlFile.Texture - { - Path = string.Empty, - Flags = 0, - } ); - tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.AddItem( new Sampler - { - SamplerId = tab.MaterialNewSamplerId, - TextureIndex = ( byte )tab.Mtrl.Textures.Length, - Flags = 0, - } ); - ret = true; - } + tab.NewSamplerIdx = idx; + tab.NewSamplerId = sampler.Id; } } } } + ImGui.SameLine(); + if( !ImGui.Button( "Add Sampler" ) ) + { + return false; + } + + tab.Mtrl.ShaderPackage.Samplers = tab.Mtrl.ShaderPackage.Samplers.AddItem( new Sampler + { + SamplerId = tab.NewSamplerId, + TextureIndex = ( byte )tab.Mtrl.Textures.Length, + Flags = 0, + } ); + tab.Mtrl.Textures = tab.Mtrl.Textures.AddItem( new MtrlFile.Texture + { + Path = string.Empty, + Flags = 0, + } ); + tab.UpdateSamplers(); + tab.UpdateTextureLabels(); + return true; + + } + + private static bool DrawMaterialSamplers( MtrlTab tab, bool disabled ) + { + if( tab.Mtrl.ShaderPackage.Samplers.Length == 0 + && tab.Mtrl.Textures.Length == 0 + && ( disabled || (tab.AssociatedShpk?.Samplers.All( sampler => sampler.Slot != 2 ) ?? false ) )) + { + return false; + } + + using var t = ImRaii.TreeNode( "Samplers" ); + if( !t ) + { + return false; + } + + var ret = false; + for( var idx = 0; idx < tab.Mtrl.ShaderPackage.Samplers.Length; ++idx ) + { + ret |= DrawMaterialSampler( tab, disabled, ref idx ); + } + + if( tab.OrphanedSamplers.Count > 0 ) + { + using var t2 = ImRaii.TreeNode( $"Orphan Textures ({tab.OrphanedSamplers.Count})" ); + if( t2 ) + { + foreach( var idx in tab.OrphanedSamplers ) + { + ImRaii.TreeNode( $"#{idx}: {Path.GetFileName( tab.Mtrl.Textures[ idx ].Path )} - {tab.Mtrl.Textures[ idx ].Flags:X4}", ImGuiTreeNodeFlags.Leaf ) + .Dispose(); + } + } + } + else if( !disabled && tab.MissingSamplers.Count > 0 && tab.AliasedSamplerCount == 0 && tab.Mtrl.Textures.Length < 255 ) + { + ret |= DrawMaterialNewSampler( tab ); + } + return ret; }