diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ColorSet.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ColorSet.cs index daca1098..e1ba045d 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ColorSet.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ColorSet.cs @@ -17,65 +17,60 @@ public partial class ModEditWindow private static readonly float HalfMaxValue = (float)Half.MaxValue; private static readonly float HalfEpsilon = (float)Half.Epsilon; - private bool DrawMaterialColorSetChange( MtrlTab tab, bool disabled ) + private bool DrawMaterialColorSetChange(MtrlTab tab, bool disabled) { - if( !tab.SamplerIds.Contains( ShpkFile.TableSamplerId ) || !tab.Mtrl.ColorSets.Any( c => c.HasRows ) ) - { + if (!tab.SamplerIds.Contains(ShpkFile.TableSamplerId) || !tab.Mtrl.ColorSets.Any(c => c.HasRows)) return false; - } - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - if( !ImGui.CollapsingHeader( "Color Set", ImGuiTreeNodeFlags.DefaultOpen ) ) - { + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + if (!ImGui.CollapsingHeader("Color Set", ImGuiTreeNodeFlags.DefaultOpen)) return false; - } var hasAnyDye = tab.UseColorDyeSet; - ColorSetCopyAllClipboardButton( tab.Mtrl, 0 ); + ColorSetCopyAllClipboardButton(tab.Mtrl, 0); ImGui.SameLine(); - var ret = ColorSetPasteAllClipboardButton( tab, 0, disabled ); - if( !disabled ) + var ret = ColorSetPasteAllClipboardButton(tab, 0, disabled); + if (!disabled) { ImGui.SameLine(); - ImGui.Dummy( ImGuiHelpers.ScaledVector2( 20, 0 ) ); + ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0)); ImGui.SameLine(); - ret |= ColorSetDyeableCheckbox( tab, ref hasAnyDye ); - } - if( hasAnyDye ) - { - ImGui.SameLine(); - ImGui.Dummy( ImGuiHelpers.ScaledVector2( 20, 0 ) ); - ImGui.SameLine(); - ret |= DrawPreviewDye( tab, disabled ); + ret |= ColorSetDyeableCheckbox(tab, ref hasAnyDye); } - using var table = ImRaii.Table( "##ColorSets", hasAnyDye ? 11 : 9, - ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerV ); - if( !table ) + if (hasAnyDye) { + ImGui.SameLine(); + ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0)); + ImGui.SameLine(); + ret |= DrawPreviewDye(tab, disabled); + } + + using var table = ImRaii.Table("##ColorSets", hasAnyDye ? 11 : 9, + ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerV); + if (!table) return false; - } ImGui.TableNextColumn(); - ImGui.TableHeader( string.Empty ); + ImGui.TableHeader(string.Empty); ImGui.TableNextColumn(); - ImGui.TableHeader( "Row" ); + ImGui.TableHeader("Row"); ImGui.TableNextColumn(); - ImGui.TableHeader( "Diffuse" ); + ImGui.TableHeader("Diffuse"); ImGui.TableNextColumn(); - ImGui.TableHeader( "Specular" ); + ImGui.TableHeader("Specular"); ImGui.TableNextColumn(); - ImGui.TableHeader( "Emissive" ); + ImGui.TableHeader("Emissive"); ImGui.TableNextColumn(); - ImGui.TableHeader( "Gloss" ); + ImGui.TableHeader("Gloss"); ImGui.TableNextColumn(); - ImGui.TableHeader( "Tile" ); + ImGui.TableHeader("Tile"); ImGui.TableNextColumn(); - ImGui.TableHeader( "Repeat" ); + ImGui.TableHeader("Repeat"); ImGui.TableNextColumn(); - ImGui.TableHeader( "Skew" ); - if( hasAnyDye ) + ImGui.TableHeader("Skew"); + if (hasAnyDye) { ImGui.TableNextColumn(); ImGui.TableHeader("Dye"); @@ -83,12 +78,12 @@ public partial class ModEditWindow ImGui.TableHeader("Dye Preview"); } - for( var j = 0; j < tab.Mtrl.ColorSets.Length; ++j ) + for (var j = 0; j < tab.Mtrl.ColorSets.Length; ++j) { - using var _ = ImRaii.PushId( j ); - for( var i = 0; i < MtrlFile.ColorSet.RowArray.NumRows; ++i ) + using var _ = ImRaii.PushId(j); + for (var i = 0; i < MtrlFile.ColorSet.RowArray.NumRows; ++i) { - ret |= DrawColorSetRow( tab, j, i, disabled, hasAnyDye ); + ret |= DrawColorSetRow(tab, j, i, disabled, hasAnyDye); ImGui.TableNextRow(); } } @@ -97,22 +92,20 @@ public partial class ModEditWindow } - private static void ColorSetCopyAllClipboardButton( MtrlFile file, int colorSetIdx ) + private static void ColorSetCopyAllClipboardButton(MtrlFile file, int colorSetIdx) { - if( !ImGui.Button( "Export All Rows to Clipboard", ImGuiHelpers.ScaledVector2( 200, 0 ) ) ) - { + if (!ImGui.Button("Export All Rows to Clipboard", ImGuiHelpers.ScaledVector2(200, 0))) return; - } try { - var data1 = file.ColorSets[ colorSetIdx ].Rows.AsBytes(); - var data2 = file.ColorDyeSets.Length > colorSetIdx ? file.ColorDyeSets[ colorSetIdx ].Rows.AsBytes() : ReadOnlySpan< byte >.Empty; + var data1 = file.ColorSets[colorSetIdx].Rows.AsBytes(); + var data2 = file.ColorDyeSets.Length > colorSetIdx ? file.ColorDyeSets[colorSetIdx].Rows.AsBytes() : ReadOnlySpan.Empty; var array = new byte[data1.Length + data2.Length]; - data1.TryCopyTo( array ); - data2.TryCopyTo( array.AsSpan( data1.Length ) ); - var text = Convert.ToBase64String( array ); - ImGui.SetClipboardText( text ); + data1.TryCopyTo(array); + data2.TryCopyTo(array.AsSpan(data1.Length)); + var text = Convert.ToBase64String(array); + ImGui.SetClipboardText(text); } catch { @@ -120,19 +113,19 @@ public partial class ModEditWindow } } - private bool DrawPreviewDye( MtrlTab tab, bool disabled ) + private bool DrawPreviewDye(MtrlTab tab, bool disabled) { var (dyeId, (name, dyeColor, gloss)) = _stainService.StainCombo.CurrentSelection; - var tt = dyeId == 0 ? "Select a preview dye first." : "Apply all preview values corresponding to the dye template and chosen dye where dyeing is enabled."; - if( ImGuiUtil.DrawDisabledButton( "Apply Preview Dye", Vector2.Zero, tt, disabled || dyeId == 0 ) ) + var tt = dyeId == 0 + ? "Select a preview dye first." + : "Apply all preview values corresponding to the dye template and chosen dye where dyeing is enabled."; + if (ImGuiUtil.DrawDisabledButton("Apply Preview Dye", Vector2.Zero, tt, disabled || dyeId == 0)) { var ret = false; - for( var j = 0; j < tab.Mtrl.ColorDyeSets.Length; ++j ) + for (var j = 0; j < tab.Mtrl.ColorDyeSets.Length; ++j) { - for( var i = 0; i < MtrlFile.ColorSet.RowArray.NumRows; ++i ) - { - ret |= tab.Mtrl.ApplyDyeTemplate( _stainService.StmFile, j, i, dyeId ); - } + for (var i = 0; i < MtrlFile.ColorSet.RowArray.NumRows; ++i) + ret |= tab.Mtrl.ApplyDyeTemplate(_stainService.StmFile, j, i, dyeId); } tab.UpdateColorSetPreview(); @@ -147,33 +140,31 @@ public partial class ModEditWindow return false; } - private static unsafe bool ColorSetPasteAllClipboardButton( MtrlTab tab, int colorSetIdx, bool disabled ) + private static unsafe bool ColorSetPasteAllClipboardButton(MtrlTab tab, int colorSetIdx, bool disabled) { - if( !ImGuiUtil.DrawDisabledButton( "Import All Rows from Clipboard", ImGuiHelpers.ScaledVector2( 200, 0 ), string.Empty, disabled ) || tab.Mtrl.ColorSets.Length <= colorSetIdx ) - { + if (!ImGuiUtil.DrawDisabledButton("Import All Rows from Clipboard", ImGuiHelpers.ScaledVector2(200, 0), string.Empty, disabled) + || tab.Mtrl.ColorSets.Length <= colorSetIdx) return false; - } try { var text = ImGui.GetClipboardText(); - var data = Convert.FromBase64String( text ); - if( data.Length < Marshal.SizeOf< MtrlFile.ColorSet.RowArray >() ) - { + var data = Convert.FromBase64String(text); + if (data.Length < Marshal.SizeOf()) return false; - } - ref var rows = ref tab.Mtrl.ColorSets[ colorSetIdx ].Rows; - fixed( void* ptr = data, output = &rows ) + ref var rows = ref tab.Mtrl.ColorSets[colorSetIdx].Rows; + fixed (void* ptr = data, output = &rows) { - MemoryUtility.MemCpyUnchecked( output, ptr, Marshal.SizeOf< MtrlFile.ColorSet.RowArray >() ); - if( data.Length >= Marshal.SizeOf< MtrlFile.ColorSet.RowArray >() + Marshal.SizeOf< MtrlFile.ColorDyeSet.RowArray >() - && tab.Mtrl.ColorDyeSets.Length > colorSetIdx ) + MemoryUtility.MemCpyUnchecked(output, ptr, Marshal.SizeOf()); + if (data.Length >= Marshal.SizeOf() + Marshal.SizeOf() + && tab.Mtrl.ColorDyeSets.Length > colorSetIdx) { - ref var dyeRows = ref tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows; - fixed( void* output2 = &dyeRows ) + ref var dyeRows = ref tab.Mtrl.ColorDyeSets[colorSetIdx].Rows; + fixed (void* output2 = &dyeRows) { - MemoryUtility.MemCpyUnchecked( output2, ( byte* )ptr + Marshal.SizeOf< MtrlFile.ColorSet.RowArray >(), Marshal.SizeOf< MtrlFile.ColorDyeSet.RowArray >() ); + MemoryUtility.MemCpyUnchecked(output2, (byte*)ptr + Marshal.SizeOf(), + Marshal.SizeOf()); } } } @@ -188,38 +179,38 @@ public partial class ModEditWindow } } - private static unsafe void ColorSetCopyClipboardButton( MtrlFile.ColorSet.Row row, MtrlFile.ColorDyeSet.Row dye ) + private static unsafe void ColorSetCopyClipboardButton(MtrlFile.ColorSet.Row row, MtrlFile.ColorDyeSet.Row dye) { - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Clipboard.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, - "Export this row to your clipboard.", false, true ) ) - { - try - { - var data = new byte[MtrlFile.ColorSet.Row.Size + 2]; - fixed( byte* ptr = data ) - { - MemoryUtility.MemCpyUnchecked( ptr, &row, MtrlFile.ColorSet.Row.Size ); - MemoryUtility.MemCpyUnchecked( ptr + MtrlFile.ColorSet.Row.Size, &dye, 2 ); - } + if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + "Export this row to your clipboard.", false, true)) + return; - var text = Convert.ToBase64String( data ); - ImGui.SetClipboardText( text ); - } - catch + try + { + var data = new byte[MtrlFile.ColorSet.Row.Size + 2]; + fixed (byte* ptr = data) { - // ignored + MemoryUtility.MemCpyUnchecked(ptr, &row, MtrlFile.ColorSet.Row.Size); + MemoryUtility.MemCpyUnchecked(ptr + MtrlFile.ColorSet.Row.Size, &dye, 2); } + + var text = Convert.ToBase64String(data); + ImGui.SetClipboardText(text); + } + catch + { + // ignored } } - private static bool ColorSetDyeableCheckbox( MtrlTab tab, ref bool dyeable ) + private static bool ColorSetDyeableCheckbox(MtrlTab tab, ref bool dyeable) { - var ret = ImGui.Checkbox( "Dyeable", ref dyeable ); + var ret = ImGui.Checkbox("Dyeable", ref dyeable); - if( ret ) + if (ret) { tab.UseColorDyeSet = dyeable; - if( dyeable ) + if (dyeable) tab.Mtrl.FindOrAddColorDyeSet(); tab.UpdateColorSetPreview(); } @@ -227,215 +218,244 @@ public partial class ModEditWindow return ret; } - private static unsafe bool ColorSetPasteFromClipboardButton( MtrlTab tab, int colorSetIdx, int rowIdx, bool disabled ) + private static unsafe bool ColorSetPasteFromClipboardButton(MtrlTab tab, int colorSetIdx, int rowIdx, bool disabled) { - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Paste.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, - "Import an exported row from your clipboard onto this row.", disabled, true ) ) + if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Paste.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + "Import an exported row from your clipboard onto this row.", disabled, true)) + return false; + + try { - try + var text = ImGui.GetClipboardText(); + var data = Convert.FromBase64String(text); + if (data.Length != MtrlFile.ColorSet.Row.Size + 2 + || tab.Mtrl.ColorSets.Length <= colorSetIdx) + return false; + + fixed (byte* ptr = data) { - var text = ImGui.GetClipboardText(); - var data = Convert.FromBase64String( text ); - if( data.Length != MtrlFile.ColorSet.Row.Size + 2 - || tab.Mtrl.ColorSets.Length <= colorSetIdx ) - { - return false; - } - - fixed( byte* ptr = data ) - { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ] = *( MtrlFile.ColorSet.Row* )ptr; - if( colorSetIdx < tab.Mtrl.ColorDyeSets.Length ) - { - tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ] = *( MtrlFile.ColorDyeSet.Row* )( ptr + MtrlFile.ColorSet.Row.Size ); - } - } - - tab.UpdateColorSetRowPreview(rowIdx); - - return true; - } - catch - { - // ignored + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx] = *(MtrlFile.ColorSet.Row*)ptr; + if (colorSetIdx < tab.Mtrl.ColorDyeSets.Length) + tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx] = *(MtrlFile.ColorDyeSet.Row*)(ptr + MtrlFile.ColorSet.Row.Size); } + + tab.UpdateColorSetRowPreview(rowIdx); + + return true; + } + catch + { + return false; } - - return false; } - private static void ColorSetHighlightButton( MtrlTab tab, int rowIdx, bool disabled ) + private static void ColorSetHighlightButton(MtrlTab tab, int rowIdx, bool disabled) { - ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Crosshairs.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, - "Highlight this row on your character, if possible.", disabled || tab.ColorSetPreviewers.Count == 0, true ); + ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Crosshairs.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, + "Highlight this row on your character, if possible.", disabled || tab.ColorSetPreviewers.Count == 0, true); - if( ImGui.IsItemHovered() ) - tab.HighlightColorSetRow( rowIdx ); - else if( tab.HighlightedColorSetRow == rowIdx ) + if (ImGui.IsItemHovered()) + tab.HighlightColorSetRow(rowIdx); + else if (tab.HighlightedColorSetRow == rowIdx) tab.CancelColorSetHighlight(); } - private bool DrawColorSetRow( MtrlTab tab, int colorSetIdx, int rowIdx, bool disabled, bool hasAnyDye ) + private bool DrawColorSetRow(MtrlTab tab, int colorSetIdx, int rowIdx, bool disabled, bool hasAnyDye) { - static bool FixFloat( ref float val, float current ) + static bool FixFloat(ref float val, float current) { - val = ( float )( Half )val; + val = (float)(Half)val; return val != current; } - using var id = ImRaii.PushId( rowIdx ); - var row = tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ]; + using var id = ImRaii.PushId(rowIdx); + var row = tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx]; var hasDye = hasAnyDye && tab.Mtrl.ColorDyeSets.Length > colorSetIdx; - var dye = hasDye ? tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ] : new MtrlFile.ColorDyeSet.Row(); + var dye = hasDye ? tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx] : new MtrlFile.ColorDyeSet.Row(); var floatSize = 70 * UiHelpers.Scale; var intSize = 45 * UiHelpers.Scale; ImGui.TableNextColumn(); - ColorSetCopyClipboardButton( row, dye ); + ColorSetCopyClipboardButton(row, dye); ImGui.SameLine(); - var ret = ColorSetPasteFromClipboardButton( tab, colorSetIdx, rowIdx, disabled ); + var ret = ColorSetPasteFromClipboardButton(tab, colorSetIdx, rowIdx, disabled); ImGui.SameLine(); - ColorSetHighlightButton( tab, rowIdx, disabled ); + ColorSetHighlightButton(tab, rowIdx, disabled); ImGui.TableNextColumn(); - ImGui.TextUnformatted( $"#{rowIdx + 1:D2}" ); + ImGui.TextUnformatted($"#{rowIdx + 1:D2}"); ImGui.TableNextColumn(); - using var dis = ImRaii.Disabled( disabled ); - ret |= ColorPicker( "##Diffuse", "Diffuse Color", row.Diffuse, c => { tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].Diffuse = c; tab.UpdateColorSetRowPreview(rowIdx); } ); - if( hasDye ) + using var dis = ImRaii.Disabled(disabled); + ret |= ColorPicker("##Diffuse", "Diffuse Color", row.Diffuse, c => + { + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].Diffuse = c; + tab.UpdateColorSetRowPreview(rowIdx); + }); + if (hasDye) { ImGui.SameLine(); - ret |= ImGuiUtil.Checkbox( "##dyeDiffuse", "Apply Diffuse Color on Dye", dye.Diffuse, - b => { tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].Diffuse = b; tab.UpdateColorSetRowPreview(rowIdx); }, ImGuiHoveredFlags.AllowWhenDisabled ); + ret |= ImGuiUtil.Checkbox("##dyeDiffuse", "Apply Diffuse Color on Dye", dye.Diffuse, + b => + { + tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx].Diffuse = b; + tab.UpdateColorSetRowPreview(rowIdx); + }, ImGuiHoveredFlags.AllowWhenDisabled); } ImGui.TableNextColumn(); - ret |= ColorPicker( "##Specular", "Specular Color", row.Specular, c => { tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].Specular = c; tab.UpdateColorSetRowPreview(rowIdx); } ); + ret |= ColorPicker("##Specular", "Specular Color", row.Specular, c => + { + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].Specular = c; + tab.UpdateColorSetRowPreview(rowIdx); + }); ImGui.SameLine(); var tmpFloat = row.SpecularStrength; - ImGui.SetNextItemWidth( floatSize ); - if( ImGui.DragFloat( "##SpecularStrength", ref tmpFloat, 0.1f, 0f, HalfMaxValue, "%.2f" ) && FixFloat( ref tmpFloat, row.SpecularStrength ) ) + ImGui.SetNextItemWidth(floatSize); + if (ImGui.DragFloat("##SpecularStrength", ref tmpFloat, 0.1f, 0f, HalfMaxValue, "%.2f") && FixFloat(ref tmpFloat, row.SpecularStrength)) { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].SpecularStrength = tmpFloat; - ret = true; + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].SpecularStrength = tmpFloat; + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Specular Strength", ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip("Specular Strength", ImGuiHoveredFlags.AllowWhenDisabled); - if( hasDye ) + if (hasDye) { ImGui.SameLine(); - ret |= ImGuiUtil.Checkbox( "##dyeSpecular", "Apply Specular Color on Dye", dye.Specular, - b => { tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].Specular = b; tab.UpdateColorSetRowPreview(rowIdx); }, ImGuiHoveredFlags.AllowWhenDisabled ); + ret |= ImGuiUtil.Checkbox("##dyeSpecular", "Apply Specular Color on Dye", dye.Specular, + b => + { + tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx].Specular = b; + tab.UpdateColorSetRowPreview(rowIdx); + }, ImGuiHoveredFlags.AllowWhenDisabled); ImGui.SameLine(); - ret |= ImGuiUtil.Checkbox( "##dyeSpecularStrength", "Apply Specular Strength on Dye", dye.SpecularStrength, - b => { tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].SpecularStrength = b; tab.UpdateColorSetRowPreview(rowIdx); }, ImGuiHoveredFlags.AllowWhenDisabled ); + ret |= ImGuiUtil.Checkbox("##dyeSpecularStrength", "Apply Specular Strength on Dye", dye.SpecularStrength, + b => + { + tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx].SpecularStrength = b; + tab.UpdateColorSetRowPreview(rowIdx); + }, ImGuiHoveredFlags.AllowWhenDisabled); } ImGui.TableNextColumn(); - ret |= ColorPicker( "##Emissive", "Emissive Color", row.Emissive, c => { tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].Emissive = c; tab.UpdateColorSetRowPreview(rowIdx); } ); - if( hasDye ) + ret |= ColorPicker("##Emissive", "Emissive Color", row.Emissive, c => + { + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].Emissive = c; + tab.UpdateColorSetRowPreview(rowIdx); + }); + if (hasDye) { ImGui.SameLine(); - ret |= ImGuiUtil.Checkbox( "##dyeEmissive", "Apply Emissive Color on Dye", dye.Emissive, - b => { tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].Emissive = b; tab.UpdateColorSetRowPreview(rowIdx); }, ImGuiHoveredFlags.AllowWhenDisabled ); + ret |= ImGuiUtil.Checkbox("##dyeEmissive", "Apply Emissive Color on Dye", dye.Emissive, + b => + { + tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx].Emissive = b; + tab.UpdateColorSetRowPreview(rowIdx); + }, ImGuiHoveredFlags.AllowWhenDisabled); } ImGui.TableNextColumn(); tmpFloat = row.GlossStrength; - ImGui.SetNextItemWidth( floatSize ); - if( ImGui.DragFloat( "##GlossStrength", ref tmpFloat, Math.Max( 0.1f, tmpFloat * 0.025f ), HalfEpsilon, HalfMaxValue, "%.1f" ) && FixFloat( ref tmpFloat, row.GlossStrength ) ) + ImGui.SetNextItemWidth(floatSize); + if (ImGui.DragFloat("##GlossStrength", ref tmpFloat, Math.Max(0.1f, tmpFloat * 0.025f), HalfEpsilon, HalfMaxValue, "%.1f") + && FixFloat(ref tmpFloat, row.GlossStrength)) { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].GlossStrength = Math.Max(tmpFloat, HalfEpsilon); - ret = true; + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].GlossStrength = Math.Max(tmpFloat, HalfEpsilon); + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Gloss Strength", ImGuiHoveredFlags.AllowWhenDisabled ); - if( hasDye ) + ImGuiUtil.HoverTooltip("Gloss Strength", ImGuiHoveredFlags.AllowWhenDisabled); + if (hasDye) { ImGui.SameLine(); - ret |= ImGuiUtil.Checkbox( "##dyeGloss", "Apply Gloss Strength on Dye", dye.Gloss, - b => { tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].Gloss = b; tab.UpdateColorSetRowPreview(rowIdx); }, ImGuiHoveredFlags.AllowWhenDisabled ); + ret |= ImGuiUtil.Checkbox("##dyeGloss", "Apply Gloss Strength on Dye", dye.Gloss, + b => + { + tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx].Gloss = b; + tab.UpdateColorSetRowPreview(rowIdx); + }, ImGuiHoveredFlags.AllowWhenDisabled); } ImGui.TableNextColumn(); int tmpInt = row.TileSet; - ImGui.SetNextItemWidth( intSize ); - if( ImGui.DragInt( "##TileSet", ref tmpInt, 0.25f, 0, 63 ) && tmpInt != row.TileSet && tmpInt is >= 0 and <= ushort.MaxValue ) + ImGui.SetNextItemWidth(intSize); + if (ImGui.DragInt("##TileSet", ref tmpInt, 0.25f, 0, 63) && tmpInt != row.TileSet && tmpInt is >= 0 and <= ushort.MaxValue) { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].TileSet = ( ushort )Math.Clamp(tmpInt, 0, 63); - ret = true; + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].TileSet = (ushort)Math.Clamp(tmpInt, 0, 63); + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Tile Set", ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip("Tile Set", ImGuiHoveredFlags.AllowWhenDisabled); ImGui.TableNextColumn(); tmpFloat = row.MaterialRepeat.X; - ImGui.SetNextItemWidth( floatSize ); - if( ImGui.DragFloat( "##RepeatX", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f" ) && FixFloat( ref tmpFloat, row.MaterialRepeat.X ) ) + ImGui.SetNextItemWidth(floatSize); + if (ImGui.DragFloat("##RepeatX", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f") + && FixFloat(ref tmpFloat, row.MaterialRepeat.X)) { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].MaterialRepeat = row.MaterialRepeat with { X = tmpFloat }; - ret = true; + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].MaterialRepeat = row.MaterialRepeat with { X = tmpFloat }; + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Repeat X", ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip("Repeat X", ImGuiHoveredFlags.AllowWhenDisabled); ImGui.SameLine(); tmpFloat = row.MaterialRepeat.Y; - ImGui.SetNextItemWidth( floatSize ); - if( ImGui.DragFloat( "##RepeatY", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f" ) && FixFloat( ref tmpFloat, row.MaterialRepeat.Y ) ) + ImGui.SetNextItemWidth(floatSize); + if (ImGui.DragFloat("##RepeatY", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f") + && FixFloat(ref tmpFloat, row.MaterialRepeat.Y)) { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].MaterialRepeat = row.MaterialRepeat with { Y = tmpFloat }; - ret = true; + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].MaterialRepeat = row.MaterialRepeat with { Y = tmpFloat }; + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Repeat Y", ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip("Repeat Y", ImGuiHoveredFlags.AllowWhenDisabled); ImGui.TableNextColumn(); tmpFloat = row.MaterialSkew.X; - ImGui.SetNextItemWidth( floatSize ); - if( ImGui.DragFloat( "##SkewX", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f" ) && FixFloat( ref tmpFloat, row.MaterialSkew.X ) ) + ImGui.SetNextItemWidth(floatSize); + if (ImGui.DragFloat("##SkewX", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f") && FixFloat(ref tmpFloat, row.MaterialSkew.X)) { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].MaterialSkew = row.MaterialSkew with { X = tmpFloat }; - ret = true; + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].MaterialSkew = row.MaterialSkew with { X = tmpFloat }; + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Skew X", ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip("Skew X", ImGuiHoveredFlags.AllowWhenDisabled); ImGui.SameLine(); tmpFloat = row.MaterialSkew.Y; - ImGui.SetNextItemWidth( floatSize ); - if( ImGui.DragFloat( "##SkewY", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f" ) && FixFloat( ref tmpFloat, row.MaterialSkew.Y ) ) + ImGui.SetNextItemWidth(floatSize); + if (ImGui.DragFloat("##SkewY", ref tmpFloat, 0.1f, HalfMinValue, HalfMaxValue, "%.2f") && FixFloat(ref tmpFloat, row.MaterialSkew.Y)) { - tab.Mtrl.ColorSets[ colorSetIdx ].Rows[ rowIdx ].MaterialSkew = row.MaterialSkew with { Y = tmpFloat }; - ret = true; + tab.Mtrl.ColorSets[colorSetIdx].Rows[rowIdx].MaterialSkew = row.MaterialSkew with { Y = tmpFloat }; + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Skew Y", ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip("Skew Y", ImGuiHoveredFlags.AllowWhenDisabled); - if( hasDye ) + if (hasDye) { ImGui.TableNextColumn(); - if (_stainService.TemplateCombo.Draw( "##dyeTemplate", dye.Template.ToString(), string.Empty, intSize - + ImGui.GetStyle().ScrollbarSize / 2, ImGui.GetTextLineHeightWithSpacing(), ImGuiComboFlags.NoArrowButton ) ) + if (_stainService.TemplateCombo.Draw("##dyeTemplate", dye.Template.ToString(), string.Empty, intSize + + ImGui.GetStyle().ScrollbarSize / 2, ImGui.GetTextLineHeightWithSpacing(), ImGuiComboFlags.NoArrowButton)) { - tab.Mtrl.ColorDyeSets[ colorSetIdx ].Rows[ rowIdx ].Template = _stainService.TemplateCombo.CurrentSelection; - ret = true; + tab.Mtrl.ColorDyeSets[colorSetIdx].Rows[rowIdx].Template = _stainService.TemplateCombo.CurrentSelection; + ret = true; tab.UpdateColorSetRowPreview(rowIdx); } - ImGuiUtil.HoverTooltip( "Dye Template", ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip("Dye Template", ImGuiHoveredFlags.AllowWhenDisabled); ImGui.TableNextColumn(); - ret |= DrawDyePreview( tab, colorSetIdx, rowIdx, disabled, dye, floatSize ); + ret |= DrawDyePreview(tab, colorSetIdx, rowIdx, disabled, dye, floatSize); } - else if ( hasAnyDye ) + else if (hasAnyDye) { ImGui.TableNextColumn(); ImGui.TableNextColumn(); @@ -445,63 +465,65 @@ public partial class ModEditWindow return ret; } - private bool DrawDyePreview( MtrlTab tab, int colorSetIdx, int rowIdx, bool disabled, MtrlFile.ColorDyeSet.Row dye, float floatSize ) + private bool DrawDyePreview(MtrlTab tab, int colorSetIdx, int rowIdx, bool disabled, MtrlFile.ColorDyeSet.Row dye, float floatSize) { var stain = _stainService.StainCombo.CurrentSelection.Key; - if( stain == 0 || !_stainService.StmFile.Entries.TryGetValue( dye.Template, out var entry ) ) - { + if (stain == 0 || !_stainService.StmFile.Entries.TryGetValue(dye.Template, out var entry)) return false; - } - var values = entry[ ( int )stain ]; - using var style = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing / 2 ); + var values = entry[(int)stain]; + using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing / 2); - var ret = ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.PaintBrush.ToIconString(), new Vector2( ImGui.GetFrameHeight() ), - "Apply the selected dye to this row.", disabled, true ); + var ret = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.PaintBrush.ToIconString(), new Vector2(ImGui.GetFrameHeight()), + "Apply the selected dye to this row.", disabled, true); - ret = ret && tab.Mtrl.ApplyDyeTemplate(_stainService.StmFile, colorSetIdx, rowIdx, stain ); + ret = ret && tab.Mtrl.ApplyDyeTemplate(_stainService.StmFile, colorSetIdx, rowIdx, stain); if (ret) tab.UpdateColorSetRowPreview(rowIdx); ImGui.SameLine(); - ColorPicker( "##diffusePreview", string.Empty, values.Diffuse, _ => { }, "D" ); + ColorPicker("##diffusePreview", string.Empty, values.Diffuse, _ => { }, "D"); ImGui.SameLine(); - ColorPicker( "##specularPreview", string.Empty, values.Specular, _ => { }, "S" ); + ColorPicker("##specularPreview", string.Empty, values.Specular, _ => { }, "S"); ImGui.SameLine(); - ColorPicker( "##emissivePreview", string.Empty, values.Emissive, _ => { }, "E" ); + ColorPicker("##emissivePreview", string.Empty, values.Emissive, _ => { }, "E"); ImGui.SameLine(); using var dis = ImRaii.Disabled(); - ImGui.SetNextItemWidth( floatSize ); - ImGui.DragFloat( "##gloss", ref values.Gloss, 0, values.Gloss, values.Gloss, "%.1f G" ); + ImGui.SetNextItemWidth(floatSize); + ImGui.DragFloat("##gloss", ref values.Gloss, 0, values.Gloss, values.Gloss, "%.1f G"); ImGui.SameLine(); - ImGui.SetNextItemWidth( floatSize ); - ImGui.DragFloat( "##specularStrength", ref values.SpecularPower, 0, values.SpecularPower, values.SpecularPower, "%.2f S" ); + ImGui.SetNextItemWidth(floatSize); + ImGui.DragFloat("##specularStrength", ref values.SpecularPower, 0, values.SpecularPower, values.SpecularPower, "%.2f S"); return ret; } - private static bool ColorPicker( string label, string tooltip, Vector3 input, Action< Vector3 > setter, string letter = "" ) + private static bool ColorPicker(string label, string tooltip, Vector3 input, Action setter, string letter = "") { - var ret = false; - var inputSqrt = PseudoSqrtRgb( input ); - var tmp = inputSqrt; - if( ImGui.ColorEdit3( label, ref tmp, - ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.DisplayRGB | ImGuiColorEditFlags.InputRGB | ImGuiColorEditFlags.NoTooltip | ImGuiColorEditFlags.HDR ) - && tmp != inputSqrt ) + var ret = false; + var inputSqrt = PseudoSqrtRgb(input); + var tmp = inputSqrt; + if (ImGui.ColorEdit3(label, ref tmp, + ImGuiColorEditFlags.NoInputs + | ImGuiColorEditFlags.DisplayRGB + | ImGuiColorEditFlags.InputRGB + | ImGuiColorEditFlags.NoTooltip + | ImGuiColorEditFlags.HDR) + && tmp != inputSqrt) { - setter( PseudoSquareRgb( tmp ) ); + setter(PseudoSquareRgb(tmp)); ret = true; } - if( letter.Length > 0 && ImGui.IsItemVisible() ) + if (letter.Length > 0 && ImGui.IsItemVisible()) { - var textSize = ImGui.CalcTextSize( letter ); - var center = ImGui.GetItemRectMin() + ( ImGui.GetItemRectSize() - textSize ) / 2; + var textSize = ImGui.CalcTextSize(letter); + var center = ImGui.GetItemRectMin() + (ImGui.GetItemRectSize() - textSize) / 2; var textColor = input.LengthSquared() < 0.25f ? 0x80FFFFFFu : 0x80000000u; - ImGui.GetWindowDrawList().AddText( center, textColor, letter ); + ImGui.GetWindowDrawList().AddText(center, textColor, letter); } - ImGuiUtil.HoverTooltip( tooltip, ImGuiHoveredFlags.AllowWhenDisabled ); + ImGuiUtil.HoverTooltip(tooltip, ImGuiHoveredFlags.AllowWhenDisabled); return ret; } @@ -509,7 +531,7 @@ public partial class ModEditWindow // Functions to deal with squared RGB values without making negatives useless. private static float PseudoSquareRgb(float x) - => x < 0.0f ? -(x * x) : (x * x); + => x < 0.0f ? -(x * x) : x * x; private static Vector3 PseudoSquareRgb(Vector3 vec) => new(PseudoSquareRgb(vec.X), PseudoSquareRgb(vec.Y), PseudoSquareRgb(vec.Z)); @@ -525,4 +547,4 @@ public partial class ModEditWindow private static Vector4 PseudoSqrtRgb(Vector4 vec) => new(PseudoSqrtRgb(vec.X), PseudoSqrtRgb(vec.Y), PseudoSqrtRgb(vec.Z), vec.W); -} \ No newline at end of file +} diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ConstantEditor.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ConstantEditor.cs index 8a104145..e5b16a47 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ConstantEditor.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ConstantEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Numerics; using ImGuiNET; using OtterGui.Raii; @@ -27,7 +28,8 @@ public partial class ModEditWindow private readonly float _bias; private readonly string _format; - public FloatConstantEditor(float? minimum, float? maximum, float speed, float relativeSpeed, float factor, float bias, byte precision, string unit) + public FloatConstantEditor(float? minimum, float? maximum, float speed, float relativeSpeed, float factor, float bias, byte precision, + string unit) { _minimum = minimum; _maximum = maximum; @@ -55,10 +57,13 @@ public partial class ModEditWindow var value = (values[valueIdx] - _bias) / _factor; if (disabled) + { ImGui.DragFloat($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), value, value, _format); + } else { - if (ImGui.DragFloat($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), _minimum ?? 0.0f, _maximum ?? 0.0f, _format)) + if (ImGui.DragFloat($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), _minimum ?? 0.0f, + _maximum ?? 0.0f, _format)) { values[valueIdx] = Clamp(value) * _factor + _bias; ret = true; @@ -111,10 +116,13 @@ public partial class ModEditWindow var value = (int)Math.Clamp(MathF.Round((values[valueIdx] - _bias) / _factor), int.MinValue, int.MaxValue); if (disabled) + { ImGui.DragInt($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), value, value, _format); + } else { - if (ImGui.DragInt($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), _minimum ?? 0, _maximum ?? 0, _format)) + if (ImGui.DragInt($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), _minimum ?? 0, _maximum ?? 0, + _format)) { values[valueIdx] = Clamp(value) * _factor + _bias; ret = true; @@ -142,14 +150,17 @@ public partial class ModEditWindow public bool Draw(Span values, bool disabled, float editorWidth) { - if (values.Length == 3) + switch (values.Length) { - ImGui.SetNextItemWidth(editorWidth); - var value = new Vector3(values); - if (_squaredRgb) - value = PseudoSqrtRgb(value); - if (ImGui.ColorEdit3("##0", ref value, ImGuiColorEditFlags.Float | (_clamped ? 0 : ImGuiColorEditFlags.HDR)) && !disabled) + case 3: { + ImGui.SetNextItemWidth(editorWidth); + var value = new Vector3(values); + if (_squaredRgb) + value = PseudoSqrtRgb(value); + if (!ImGui.ColorEdit3("##0", ref value, ImGuiColorEditFlags.Float | (_clamped ? 0 : ImGuiColorEditFlags.HDR)) || disabled) + return false; + if (_squaredRgb) value = PseudoSquareRgb(value); if (_clamped) @@ -157,17 +168,17 @@ public partial class ModEditWindow value.CopyTo(values); return true; } - - return false; - } - else if (values.Length == 4) - { - ImGui.SetNextItemWidth(editorWidth); - var value = new Vector4(values); - if (_squaredRgb) - value = PseudoSqrtRgb(value); - if (ImGui.ColorEdit4("##0", ref value, ImGuiColorEditFlags.Float | ImGuiColorEditFlags.AlphaPreviewHalf | (_clamped ? 0 : ImGuiColorEditFlags.HDR)) && !disabled) + case 4: { + ImGui.SetNextItemWidth(editorWidth); + var value = new Vector4(values); + if (_squaredRgb) + value = PseudoSqrtRgb(value); + if (!ImGui.ColorEdit4("##0", ref value, + ImGuiColorEditFlags.Float | ImGuiColorEditFlags.AlphaPreviewHalf | (_clamped ? 0 : ImGuiColorEditFlags.HDR)) + || disabled) + return false; + if (_squaredRgb) value = PseudoSquareRgb(value); if (_clamped) @@ -175,11 +186,8 @@ public partial class ModEditWindow value.CopyTo(values); return true; } - - return false; + default: return FloatConstantEditor.Default.Draw(values, disabled, editorWidth); } - else - return FloatConstantEditor.Default.Draw(values, disabled, editorWidth); } } @@ -188,9 +196,7 @@ public partial class ModEditWindow private readonly IReadOnlyList<(string Label, float Value, string Description)> _values; public EnumConstantEditor(IReadOnlyList<(string Label, float Value, string Description)> values) - { - _values = values; - } + => _values = values; public bool Draw(Span values, bool disabled, float editorWidth) { @@ -200,33 +206,40 @@ public partial class ModEditWindow for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx) { + using var id = ImRaii.PushId(valueIdx); if (valueIdx > 0) ImGui.SameLine(); ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx)); var currentValue = values[valueIdx]; - var (currentLabel, _, currentDescription) = _values.FirstOrNull(v => v.Value == currentValue) ?? (currentValue.ToString(), currentValue, string.Empty); - if (disabled) - ImGui.InputText($"##{valueIdx}", ref currentLabel, (uint)currentLabel.Length, ImGuiInputTextFlags.ReadOnly); - else - { - using var c = ImRaii.Combo($"##{valueIdx}", currentLabel); - { - if (c) - foreach (var (valueLabel, value, valueDescription) in _values) - { - if (ImGui.Selectable(valueLabel, value == currentValue)) - { - values[valueIdx] = value; - ret = true; - } + var currentLabel = _values.FirstOrNull(v => v.Value == currentValue)?.Label + ?? currentValue.ToString(CultureInfo.CurrentCulture); + ret = disabled + ? ImGui.InputText(string.Empty, ref currentLabel, (uint)currentLabel.Length, ImGuiInputTextFlags.ReadOnly) + : DrawCombo(currentLabel, ref values[valueIdx]); + } - if (valueDescription.Length > 0) - ImGuiUtil.SelectableHelpMarker(valueDescription); - } - } + return ret; + } + + private bool DrawCombo(string label, ref float currentValue) + { + using var c = ImRaii.Combo(string.Empty, label); + if (!c) + return false; + + var ret = false; + foreach (var (valueLabel, value, valueDescription) in _values) + { + if (ImGui.Selectable(valueLabel, value == currentValue)) + { + currentValue = value; + ret = true; } + + if (valueDescription.Length > 0) + ImGuiUtil.SelectableHelpMarker(valueDescription); } return ret; diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs index 6677db5b..12f7acd7 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Numerics; using Dalamud.Interface; using Dalamud.Interface.Internal.Notifications; -using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using ImGuiNET; using Newtonsoft.Json.Linq; @@ -17,10 +16,8 @@ using Penumbra.GameData; using Penumbra.GameData.Data; using Penumbra.GameData.Files; using Penumbra.GameData.Structs; -using Penumbra.Services; using Penumbra.String; using Penumbra.String.Classes; -using Penumbra.Util; using static Penumbra.GameData.Files.ShpkFile; namespace Penumbra.UI.AdvancedWindow; @@ -48,28 +45,32 @@ public partial class ModEditWindow public ShpkFile? AssociatedShpk; public JObject? AssociatedShpkDevkit; - public readonly string LoadedBaseDevkitPathName = string.Empty; + public readonly string LoadedBaseDevkitPathName; public readonly JObject? AssociatedBaseDevkit; // Shader Key State - public readonly List< (string Label, int Index, string Description, bool MonoFont, IReadOnlyList< (string Label, uint Value, string Description) > Values) > ShaderKeys = new(16); + public readonly + List<(string Label, int Index, string Description, bool MonoFont, IReadOnlyList<(string Label, uint Value, string Description)> + Values)> ShaderKeys = new(16); - public readonly HashSet< int > VertexShaders = new(16); - public readonly HashSet< int > PixelShaders = new(16); - public bool ShadersKnown = false; - public string VertexShadersString = "Vertex Shaders: ???"; - public string PixelShadersString = "Pixel Shaders: ???"; + public readonly HashSet VertexShaders = new(16); + public readonly HashSet PixelShaders = new(16); + public bool ShadersKnown; + public string VertexShadersString = "Vertex Shaders: ???"; + public string PixelShadersString = "Pixel Shaders: ???"; // Textures & Samplers - 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< uint > SamplerIds = new(16); - public float TextureLabelWidth; - public bool UseColorDyeSet; + public readonly HashSet UnfoldedTextures = new(4); + public readonly HashSet SamplerIds = new(16); + public float TextureLabelWidth; + public bool UseColorDyeSet; // Material Constants - public readonly List< (string Header, List< (string Label, int ConstantIndex, Range Slice, string Description, bool MonoFont, IConstantEditor Editor) > Constants) > Constants = new(16); + public readonly + List<(string Header, List<(string Label, int ConstantIndex, Range Slice, string Description, bool MonoFont, IConstantEditor Editor)> + Constants)> Constants = new(16); // Live-Previewers public readonly List MaterialPreviewers = new(4); @@ -77,15 +78,13 @@ public partial class ModEditWindow public int HighlightedColorSetRow = -1; public readonly Stopwatch HighlightTime = new(); - public FullPath FindAssociatedShpk( out string defaultPath, out Utf8GamePath defaultGamePath ) + public FullPath FindAssociatedShpk(out string defaultPath, out Utf8GamePath defaultGamePath) { - defaultPath = GamePaths.Shader.ShpkPath( Mtrl.ShaderPackage.Name ); - if( !Utf8GamePath.FromString( defaultPath, out defaultGamePath, true ) ) - { + defaultPath = GamePaths.Shader.ShpkPath(Mtrl.ShaderPackage.Name); + if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath, true)) return FullPath.Empty; - } - return _edit.FindBestMatch( defaultGamePath ); + return _edit.FindBestMatch(defaultGamePath); } public string[] GetShpkNames() @@ -102,7 +101,7 @@ public partial class ModEditWindow return _shpkNames; } - public void LoadShpk( FullPath path ) + public void LoadShpk(FullPath path) { ShaderHeader = $"Shader ({Mtrl.ShaderPackage.Name})###Shader"; @@ -110,26 +109,30 @@ public partial class ModEditWindow { LoadedShpkPath = path; var data = LoadedShpkPath.IsRooted - ? File.ReadAllBytes( LoadedShpkPath.FullName ) - : _edit._dalamud.GameData.GetFile( LoadedShpkPath.InternalName.ToString() )?.Data; - AssociatedShpk = data?.Length > 0 ? new ShpkFile( data ) : throw new Exception( "Failure to load file data." ); + ? File.ReadAllBytes(LoadedShpkPath.FullName) + : _edit._dalamud.GameData.GetFile(LoadedShpkPath.InternalName.ToString())?.Data; + AssociatedShpk = data?.Length > 0 ? new ShpkFile(data) : throw new Exception("Failure to load file data."); LoadedShpkPathName = path.ToPath(); } - catch( Exception e ) + catch (Exception e) { LoadedShpkPath = FullPath.Empty; LoadedShpkPathName = string.Empty; AssociatedShpk = null; - Penumbra.Chat.NotificationMessage( $"Could not load {LoadedShpkPath.ToPath()}:\n{e}", "Penumbra Advanced Editing", NotificationType.Error ); + Penumbra.Chat.NotificationMessage($"Could not load {LoadedShpkPath.ToPath()}:\n{e}", "Penumbra Advanced Editing", + NotificationType.Error); } - if( LoadedShpkPath.InternalName.IsEmpty ) + if (LoadedShpkPath.InternalName.IsEmpty) { AssociatedShpkDevkit = null; LoadedShpkDevkitPathName = string.Empty; } else - AssociatedShpkDevkit = TryLoadShpkDevkit( Path.GetFileNameWithoutExtension( Mtrl.ShaderPackage.Name ), out LoadedShpkDevkitPathName ); + { + AssociatedShpkDevkit = + TryLoadShpkDevkit(Path.GetFileNameWithoutExtension(Mtrl.ShaderPackage.Name), out LoadedShpkDevkitPathName); + } UpdateShaderKeys(); Update(); @@ -157,10 +160,8 @@ public partial class ModEditWindow } private T? TryGetShpkDevkitData(string category, uint? id, bool mayVary) where T : class - { - return TryGetShpkDevkitData(AssociatedShpkDevkit, LoadedShpkDevkitPathName, category, id, mayVary) - ?? TryGetShpkDevkitData(AssociatedBaseDevkit, LoadedBaseDevkitPathName, category, id, mayVary); - } + => TryGetShpkDevkitData(AssociatedShpkDevkit, LoadedShpkDevkitPathName, category, id, mayVary) + ?? TryGetShpkDevkitData(AssociatedBaseDevkit, LoadedBaseDevkitPathName, category, id, mayVary); private T? TryGetShpkDevkitData(JObject? devkit, string devkitPathName, string category, uint? id, bool mayVary) where T : class { @@ -175,7 +176,7 @@ public partial class ModEditWindow if (mayVary && (data as JObject)?["Vary"] != null) { - var selector = BuildSelector(data!["Vary"]! + var selector = BuildSelector(data["Vary"]! .Select(key => (uint)key) .Select(key => Mtrl.GetShaderKey(key)?.Value ?? AssociatedShpk!.GetMaterialKeyById(key)!.Value.DefaultValue)); var index = (int)data["Selectors"]![selector.ToString()]!; @@ -192,14 +193,13 @@ public partial class ModEditWindow } } - public void UpdateShaderKeys() + private void UpdateShaderKeys() { ShaderKeys.Clear(); if (AssociatedShpk != null) - { foreach (var key in AssociatedShpk.MaterialKeys) { - var dkData = TryGetShpkDevkitData("ShaderKeys", key.Id, false); + var dkData = TryGetShpkDevkitData("ShaderKeys", key.Id, false); var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label); var valueSet = new HashSet(key.Values); @@ -211,8 +211,8 @@ public partial class ModEditWindow { if (dkData != null && dkData.Values.TryGetValue(value, out var dkValue)) return (dkValue.Label.Length > 0 ? dkValue.Label : $"0x{value:X8}", value, dkValue.Description); - else - return ($"0x{value:X8}", value, string.Empty); + + return ($"0x{value:X8}", value, string.Empty); }).ToArray(); Array.Sort(values, (x, y) => { @@ -220,31 +220,33 @@ public partial class ModEditWindow return -1; if (y.Value == key.DefaultValue) return 1; - return x.Label.CompareTo(y.Label); + + return string.Compare(x.Label, y.Label, StringComparison.Ordinal); }); - ShaderKeys.Add((hasDkLabel ? dkData!.Label : $"0x{key.Id:X8}", mtrlKeyIndex, dkData?.Description ?? string.Empty, !hasDkLabel, values)); + ShaderKeys.Add((hasDkLabel ? dkData!.Label : $"0x{key.Id:X8}", mtrlKeyIndex, dkData?.Description ?? string.Empty, + !hasDkLabel, values)); } - } else - { foreach (var (key, index) in Mtrl.ShaderPackage.ShaderKeys.WithIndex()) ShaderKeys.Add(($"0x{key.Category:X8}", index, string.Empty, true, Array.Empty<(string, uint, string)>())); - } } - public void UpdateShaders() + private void UpdateShaders() { VertexShaders.Clear(); PixelShaders.Clear(); if (AssociatedShpk == null) + { ShadersKnown = false; + } else { ShadersKnown = true; var systemKeySelectors = AllSelectors(AssociatedShpk.SystemKeys).ToArray(); var sceneKeySelectors = AllSelectors(AssociatedShpk.SceneKeys).ToArray(); var subViewKeySelectors = AllSelectors(AssociatedShpk.SubViewKeys).ToArray(); - var materialKeySelector = BuildSelector(AssociatedShpk.MaterialKeys.Select(key => Mtrl.GetOrAddShaderKey(key.Id, key.DefaultValue).Value)); + var materialKeySelector = + BuildSelector(AssociatedShpk.MaterialKeys.Select(key => Mtrl.GetOrAddShaderKey(key.Id, key.DefaultValue).Value)); foreach (var systemKeySelector in systemKeySelectors) { foreach (var sceneKeySelector in sceneKeySelectors) @@ -252,15 +254,13 @@ public partial class ModEditWindow foreach (var subViewKeySelector in subViewKeySelectors) { var selector = BuildSelector(systemKeySelector, sceneKeySelector, materialKeySelector, subViewKeySelector); - var node = AssociatedShpk.GetNodeBySelector(selector); + var node = AssociatedShpk.GetNodeBySelector(selector); if (node.HasValue) - { foreach (var pass in node.Value.Passes) { VertexShaders.Add((int)pass.VertexShader); PixelShaders.Add((int)pass.PixelShader); } - } else ShadersKnown = false; } @@ -272,12 +272,12 @@ public partial class ModEditWindow var pixelShaders = PixelShaders.OrderBy(i => i).Select(i => $"#{i}"); VertexShadersString = $"Vertex Shaders: {string.Join(", ", ShadersKnown ? vertexShaders : vertexShaders.Append("???"))}"; - PixelShadersString = $"Pixel Shaders: {string.Join(", ", ShadersKnown ? pixelShaders : pixelShaders.Append("???"))}"; + PixelShadersString = $"Pixel Shaders: {string.Join(", ", ShadersKnown ? pixelShaders : pixelShaders.Append("???"))}"; ShaderComment = TryGetShpkDevkitData("Comment", null, true) ?? string.Empty; } - public void UpdateTextures() + private void UpdateTextures() { Textures.Clear(); SamplerIds.Clear(); @@ -302,50 +302,63 @@ public partial class ModEditWindow if (Mtrl.ColorSets.Any(c => c.HasRows)) SamplerIds.Add(TableSamplerId); } + foreach (var samplerId in SamplerIds) { var shpkSampler = AssociatedShpk.GetSamplerById(samplerId); - if (!shpkSampler.HasValue || shpkSampler.Value.Slot != 2) + if (shpkSampler is not { Slot: 2 }) continue; - var dkData = TryGetShpkDevkitData("Samplers", samplerId, true); + var dkData = TryGetShpkDevkitData("Samplers", samplerId, 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, dkData?.Description ?? string.Empty, !hasDkLabel)); + Textures.Add((hasDkLabel ? dkData!.Label : shpkSampler.Value.Name, sampler.TextureIndex, samplerIndex, + dkData?.Description ?? string.Empty, !hasDkLabel)); } + if (SamplerIds.Contains(TableSamplerId)) Mtrl.FindOrAddColorSet(); } + Textures.Sort((x, y) => string.CompareOrdinal(x.Label, y.Label)); TextureLabelWidth = 50f * UiHelpers.Scale; float helpWidth; using (var _ = ImRaii.PushFont(UiBuilder.IconFont)) + { helpWidth = ImGui.GetStyle().ItemSpacing.X + ImGui.CalcTextSize(FontAwesomeIcon.InfoCircle.ToIconString()).X; + } foreach (var (label, _, _, description, monoFont) in Textures) + { if (!monoFont) TextureLabelWidth = Math.Max(TextureLabelWidth, ImGui.CalcTextSize(label).X + (description.Length > 0 ? helpWidth : 0.0f)); + } using (var _ = ImRaii.PushFont(UiBuilder.MonoFont)) { foreach (var (label, _, _, description, monoFont) in Textures) + { if (monoFont) - TextureLabelWidth = Math.Max(TextureLabelWidth, ImGui.CalcTextSize(label).X + (description.Length > 0 ? helpWidth : 0.0f)); + TextureLabelWidth = Math.Max(TextureLabelWidth, + ImGui.CalcTextSize(label).X + (description.Length > 0 ? helpWidth : 0.0f)); + } } TextureLabelWidth = TextureLabelWidth / UiHelpers.Scale + 4; } - public void UpdateConstants() + private void UpdateConstants() { static List FindOrAddGroup(List<(string, List)> groups, string name) { foreach (var (groupName, group) in groups) + { if (string.Equals(name, groupName, StringComparison.Ordinal)) return group; + } var newGroup = new List(16); groups.Add((name, newGroup)); @@ -360,7 +373,10 @@ public partial class ModEditWindow { var values = Mtrl.GetConstantValues(constant); for (var i = 0; i < values.Length; i += 4) - fcGroup.Add(($"0x{constant.Id:X8}", index, i..Math.Min(i + 4, values.Length), string.Empty, true, FloatConstantEditor.Default)); + { + fcGroup.Add(($"0x{constant.Id:X8}", index, i..Math.Min(i + 4, values.Length), string.Empty, true, + FloatConstantEditor.Default)); + } } } else @@ -371,13 +387,12 @@ public partial class ModEditWindow if ((shpkConstant.ByteSize & 0x3) != 0) continue; - var constant = Mtrl.GetOrAddConstant(shpkConstant.Id, shpkConstant.ByteSize >> 2, out var constantIndex); - var values = Mtrl.GetConstantValues(constant); + var constant = Mtrl.GetOrAddConstant(shpkConstant.Id, shpkConstant.ByteSize >> 2, out var constantIndex); + var values = Mtrl.GetConstantValues(constant); var handledElements = new IndexSet(values.Length, false); var dkData = TryGetShpkDevkitData("Constants", shpkConstant.Id, true); if (dkData != null) - { foreach (var dkConstant in dkData) { var offset = (int)dkConstant.Offset; @@ -386,13 +401,13 @@ public partial class ModEditWindow length = Math.Min(length, (int)dkConstant.Length.Value); if (length <= 0) continue; + var editor = dkConstant.CreateEditor(); if (editor != null) FindOrAddGroup(Constants, dkConstant.Group.Length > 0 ? dkConstant.Group : "Further Constants") .Add((dkConstant.Label, constantIndex, offset..(offset + length), dkConstant.Description, false, editor)); handledElements.AddRange(offset, length); } - } var fcGroup = FindOrAddGroup(Constants, "Further Constants"); foreach (var (start, end) in handledElements.Ranges(true)) @@ -403,15 +418,20 @@ public partial class ModEditWindow for (int i = (start & ~0x3) - (offset & 0x3), j = offset >> 2; i < end; i += 4, ++j) { var rangeStart = Math.Max(i, start); - var rangeEnd = Math.Min(i + 4, end); + var rangeEnd = Math.Min(i + 4, end); if (rangeEnd > rangeStart) - fcGroup.Add(($"{prefix}[{j:D2}]{VectorSwizzle((offset + rangeStart) & 0x3, (offset + rangeEnd - 1) & 0x3)} (0x{shpkConstant.Id:X8})", constantIndex, rangeStart..rangeEnd, string.Empty, true, FloatConstantEditor.Default)); + fcGroup.Add(( + $"{prefix}[{j:D2}]{VectorSwizzle((offset + rangeStart) & 0x3, (offset + rangeEnd - 1) & 0x3)} (0x{shpkConstant.Id:X8})", + constantIndex, rangeStart..rangeEnd, string.Empty, true, FloatConstantEditor.Default)); } } else { for (var i = start; i < end; i += 4) - fcGroup.Add(($"0x{shpkConstant.Id:X8}", constantIndex, i..Math.Min(i + 4, end), string.Empty, true, FloatConstantEditor.Default)); + { + fcGroup.Add(($"0x{shpkConstant.Id:X8}", constantIndex, i..Math.Min(i + 4, end), string.Empty, true, + FloatConstantEditor.Default)); + } } } } @@ -424,20 +444,23 @@ public partial class ModEditWindow return 1; if (string.Equals(y.Header, "Further Constants", StringComparison.Ordinal)) return -1; + return string.Compare(x.Header, y.Header, StringComparison.Ordinal); }); // HACK the Replace makes w appear after xyz, for the cbuffer-location-based naming scheme foreach (var (_, group) in Constants) + { group.Sort((x, y) => string.CompareOrdinal( x.MonoFont ? x.Label.Replace("].w", "].{") : x.Label, y.MonoFont ? y.Label.Replace("].w", "].{") : y.Label)); + } } public unsafe void BindToMaterialInstances() { UnbindFromMaterialInstances(); - var localPlayer = FindLocalPlayer(_edit._dalamud.Objects); + var localPlayer = LocalPlayer(_edit._dalamud.Objects); if (null == localPlayer) return; @@ -449,7 +472,9 @@ public partial class ModEditWindow var drawObjects = stackalloc CharacterBase*[4]; drawObjects[0] = drawObject; - + drawObjects[1] = *((CharacterBase**)&localPlayer->DrawData.MainHand + 1); + drawObjects[2] = *((CharacterBase**)&localPlayer->DrawData.OffHand + 1); + drawObjects[3] = *((CharacterBase**)&localPlayer->DrawData.UnkF0 + 1); for (var i = 0; i < 3; ++i) { var subActor = FindSubActor(localPlayer, i); @@ -470,9 +495,11 @@ public partial class ModEditWindow var material = GetDrawObjectMaterial(drawObjects[subActorType + 1], modelSlot, materialSlot); if (foundMaterials.Contains((nint)material)) continue; + try { - MaterialPreviewers.Add(new LiveMaterialPreviewer(_edit._dalamud.Objects, subActorType, childObjectIndex, modelSlot, materialSlot)); + MaterialPreviewers.Add(new LiveMaterialPreviewer(_edit._dalamud.Objects, subActorType, childObjectIndex, modelSlot, + materialSlot)); foundMaterials.Add((nint)material); } catch (InvalidOperationException) @@ -480,28 +507,31 @@ public partial class ModEditWindow // Carry on without that previewer. } } + UpdateMaterialPreview(); var colorSet = Mtrl.ColorSets.FirstOrNull(colorSet => colorSet.HasRows); - if (colorSet.HasValue) + if (!colorSet.HasValue) + return; + + foreach (var (subActorType, childObjectIndex, modelSlot, materialSlot) in instances) { - foreach (var (subActorType, childObjectIndex, modelSlot, materialSlot) in instances) + try { - try - { - ColorSetPreviewers.Add(new LiveColorSetPreviewer(_edit._dalamud.Objects, _edit._dalamud.Framework, subActorType, childObjectIndex, modelSlot, materialSlot)); - } - catch (InvalidOperationException) - { - // Carry on without that previewer. - } + ColorSetPreviewers.Add(new LiveColorSetPreviewer(_edit._dalamud.Objects, _edit._dalamud.Framework, subActorType, + childObjectIndex, modelSlot, materialSlot)); + } + catch (InvalidOperationException) + { + // Carry on without that previewer. } - UpdateColorSetPreview(); } + + UpdateColorSetPreview(); } - public void UnbindFromMaterialInstances() + private void UnbindFromMaterialInstances() { foreach (var previewer in MaterialPreviewers) previewer.Dispose(); @@ -512,9 +542,9 @@ public partial class ModEditWindow ColorSetPreviewers.Clear(); } - public unsafe void UnbindFromDrawObjectMaterialInstances(nint characterBase) + private unsafe void UnbindFromDrawObjectMaterialInstances(nint characterBase) { - for (var i = MaterialPreviewers.Count; i-- > 0; ) + for (var i = MaterialPreviewers.Count; i-- > 0;) { var previewer = MaterialPreviewers[i]; if ((nint)previewer.DrawObject != characterBase) @@ -553,7 +583,7 @@ public partial class ModEditWindow previewer.SetSamplerFlags(samplerCrc, samplerFlags); } - public void UpdateMaterialPreview() + private void UpdateMaterialPreview() { SetShaderPackageFlags(Mtrl.ShaderPackage.Flags); foreach (var constant in Mtrl.ShaderPackage.Constants) @@ -562,6 +592,7 @@ public partial class ModEditWindow if (values != null) SetMaterialParameter(constant.Id, 0, values); } + foreach (var sampler in Mtrl.ShaderPackage.Samplers) SetSamplerFlags(sampler.SamplerId, sampler.Flags); } @@ -602,7 +633,7 @@ public partial class ModEditWindow if (!maybeColorSet.HasValue) return; - var colorSet = maybeColorSet.Value; + var colorSet = maybeColorSet.Value; var maybeColorDyeSet = Mtrl.ColorDyeSets.FirstOrNull(colorDyeSet => colorDyeSet.Index == colorSet.Index); var row = colorSet.Rows[rowIdx]; @@ -610,7 +641,7 @@ public partial class ModEditWindow { var stm = _edit._stainService.StmFile; var dye = maybeColorDyeSet.Value.Rows[rowIdx]; - if (stm.TryGetValue(dye.Template, (StainId)_edit._stainService.StainCombo.CurrentSelection.Key, out var dyes)) + if (stm.TryGetValue(dye.Template, _edit._stainService.StainCombo.CurrentSelection.Key, out var dyes)) row.ApplyDyeTemplate(dye, dyes); } @@ -619,7 +650,8 @@ public partial class ModEditWindow foreach (var previewer in ColorSetPreviewers) { - row.AsHalves().CopyTo(previewer.ColorSet.AsSpan().Slice(LiveColorSetPreviewer.TextureWidth * 4 * rowIdx, LiveColorSetPreviewer.TextureWidth * 4)); + row.AsHalves().CopyTo(previewer.ColorSet.AsSpan() + .Slice(LiveColorSetPreviewer.TextureWidth * 4 * rowIdx, LiveColorSetPreviewer.TextureWidth * 4)); previewer.ScheduleUpdate(); } } @@ -633,19 +665,19 @@ public partial class ModEditWindow if (!maybeColorSet.HasValue) return; - var colorSet = maybeColorSet.Value; + var colorSet = maybeColorSet.Value; var maybeColorDyeSet = Mtrl.ColorDyeSets.FirstOrNull(colorDyeSet => colorDyeSet.Index == colorSet.Index); var rows = colorSet.Rows; if (maybeColorDyeSet.HasValue && UseColorDyeSet) { - var stm = _edit._stainService.StmFile; - var stainId = (StainId)_edit._stainService.StainCombo.CurrentSelection.Key; + var stm = _edit._stainService.StmFile; + var stainId = (StainId)_edit._stainService.StainCombo.CurrentSelection.Key; var colorDyeSet = maybeColorDyeSet.Value; for (var i = 0; i < MtrlFile.ColorSet.RowArray.NumRows; ++i) { ref var row = ref rows[i]; - var dye = colorDyeSet.Rows[i]; + var dye = colorDyeSet.Rows[i]; if (stm.TryGetValue(dye.Template, stainId, out var dyes)) row.ApplyDyeTemplate(dye, dyes); } @@ -663,10 +695,10 @@ public partial class ModEditWindow private static void ApplyHighlight(ref MtrlFile.ColorSet.Row row, float time) { - var level = Math.Sin(time * 2.0 * Math.PI) * 0.25 + 0.5; + var level = Math.Sin(time * 2.0 * Math.PI) * 0.25 + 0.5; var levelSq = (float)(level * level); - row.Diffuse = Vector3.Zero; + row.Diffuse = Vector3.Zero; row.Specular = Vector3.Zero; row.Emissive = new Vector3(levelSq); } @@ -678,15 +710,15 @@ public partial class ModEditWindow UpdateConstants(); } - public MtrlTab( ModEditWindow edit, MtrlFile file, string filePath, bool writable ) + public MtrlTab(ModEditWindow edit, MtrlFile file, string filePath, bool writable) { - _edit = edit; - Mtrl = file; - FilePath = filePath; - Writable = writable; - UseColorDyeSet = file.ColorDyeSets.Length > 0; - AssociatedBaseDevkit = TryLoadShpkDevkit( "_base", out LoadedBaseDevkitPathName ); - LoadShpk( FindAssociatedShpk( out _, out _ ) ); + _edit = edit; + Mtrl = file; + FilePath = filePath; + Writable = writable; + UseColorDyeSet = file.ColorDyeSets.Length > 0; + AssociatedBaseDevkit = TryLoadShpkDevkit("_base", out LoadedBaseDevkitPathName); + LoadShpk(FindAssociatedShpk(out _, out _)); if (writable) { _edit._gameEvents.CharacterBaseDestructor += UnbindFromDrawObjectMaterialInstances; @@ -694,18 +726,7 @@ public partial class ModEditWindow } } - ~MtrlTab() - { - DoDispose(); - } - public void Dispose() - { - DoDispose(); - GC.SuppressFinalize(this); - } - - private void DoDispose() { UnbindFromMaterialInstances(); if (Writable) @@ -723,11 +744,7 @@ public partial class ModEditWindow return output.Write(); } - private sealed class DevkitShaderKeyValue - { - public string Label = string.Empty; - public string Description = string.Empty; - } + private sealed record DevkitShaderKeyValue(string Label = "", string Description = ""); private sealed class DevkitShaderKey { @@ -736,12 +753,7 @@ public partial class ModEditWindow public Dictionary Values = new(); } - private sealed class DevkitSampler - { - public string Label = string.Empty; - public string Description = string.Empty; - public string DefaultTexture = string.Empty; - } + private sealed record DevkitSampler(string Label = "", string Description = "", string DefaultTexture = ""); private enum DevkitConstantType { @@ -752,12 +764,7 @@ public partial class ModEditWindow Enum = 3, } - private sealed class DevkitConstantValue - { - public string Label = string.Empty; - public string Description = string.Empty; - public float Value = 0.0f; - } + private sealed record DevkitConstantValue(string Label = "", string Description = "", float Value = 0); private sealed class DevkitConstant { @@ -783,25 +790,20 @@ public partial class ModEditWindow public DevkitConstantValue[] Values = Array.Empty(); public IConstantEditor? CreateEditor() - { - switch (Type) + => Type switch { - case DevkitConstantType.Hidden: - return null; - case DevkitConstantType.Float: - return new FloatConstantEditor(Minimum, Maximum, Speed ?? 0.1f, RelativeSpeed, Factor, Bias, Precision, Unit); - case DevkitConstantType.Integer: - return new IntConstantEditor(ToInteger(Minimum), ToInteger(Maximum), Speed ?? 0.25f, RelativeSpeed, Factor, Bias, Unit); - case DevkitConstantType.Color: - return new ColorConstantEditor(SquaredRgb, Clamped); - case DevkitConstantType.Enum: - return new EnumConstantEditor(Array.ConvertAll(Values, value => (value.Label, value.Value, value.Description))); - default: - return FloatConstantEditor.Default; - } - } + DevkitConstantType.Hidden => null, + DevkitConstantType.Float => new FloatConstantEditor(Minimum, Maximum, Speed ?? 0.1f, RelativeSpeed, Factor, Bias, Precision, + Unit), + DevkitConstantType.Integer => new IntConstantEditor(ToInteger(Minimum), ToInteger(Maximum), Speed ?? 0.25f, RelativeSpeed, + Factor, Bias, Unit), + DevkitConstantType.Color => new ColorConstantEditor(SquaredRgb, Clamped), + DevkitConstantType.Enum => new EnumConstantEditor(Array.ConvertAll(Values, + value => (value.Label, value.Value, value.Description))), + _ => FloatConstantEditor.Default, + }; - private int? ToInteger(float? value) + private static int? ToInteger(float? value) => value.HasValue ? (int)Math.Clamp(MathF.Round(value.Value), int.MinValue, int.MaxValue) : null; } } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.Shpk.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.Shpk.cs index 0f13f47e..8fca8aa6 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.Shpk.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.Shpk.cs @@ -1,18 +1,12 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Numerics; using System.Text; using Dalamud.Interface; -using Dalamud.Interface.ImGuiFileDialog; using ImGuiNET; -using Lumina.Data.Parsing; -using Lumina.Excel.GeneratedSheets; using OtterGui; using OtterGui.Raii; using Penumbra.GameData; -using Penumbra.GameData.Files; using Penumbra.String.Classes; namespace Penumbra.UI.AdvancedWindow; @@ -25,7 +19,7 @@ public partial class ModEditWindow // Apricot shader packages are unlisted because // 1. they cause performance/memory issues when calculating the effective shader set // 2. they probably aren't intended for use with materials anyway - private static readonly IReadOnlyList StandardShaderPackages = new string[] + private static readonly IReadOnlyList StandardShaderPackages = new[] { "3dui.shpk", // "apricot_decal_dummy.shpk", @@ -76,7 +70,7 @@ public partial class ModEditWindow Border = 3, } - private static readonly IReadOnlyList TextureAddressModeTooltips = new string[] + private static readonly IReadOnlyList TextureAddressModeTooltips = new[] { "Tile the texture at every UV integer junction.\n\nFor example, for U values between 0 and 3, the texture is repeated three times.", "Flip the texture at every UV integer junction.\n\nFor U values between 0 and 1, for example, the texture is addressed normally; between 1 and 2, the texture is mirrored; between 2 and 3, the texture is normal again; and so on.", @@ -113,18 +107,15 @@ public partial class ModEditWindow private static bool DrawShaderFlagsInput(MtrlTab tab, bool disabled) { - var ret = false; var shpkFlags = (int)tab.Mtrl.ShaderPackage.Flags; ImGui.SetNextItemWidth(UiHelpers.Scale * 250.0f); - if (ImGui.InputInt("Shader Flags", ref shpkFlags, 0, 0, + if (!ImGui.InputInt("Shader Flags", ref shpkFlags, 0, 0, ImGuiInputTextFlags.CharsHexadecimal | (disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None))) - { - tab.Mtrl.ShaderPackage.Flags = (uint)shpkFlags; - ret = true; - tab.SetShaderPackageFlags((uint)shpkFlags); - } + return false; - return ret; + tab.Mtrl.ShaderPackage.Flags = (uint)shpkFlags; + tab.SetShaderPackageFlags((uint)shpkFlags); + return true; } /// diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.cs index b89bab01..102a6778 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.cs @@ -6,34 +6,35 @@ using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Files; using Penumbra.String.Classes; +using Penumbra.UI.Classes; namespace Penumbra.UI.AdvancedWindow; public partial class ModEditWindow { - private readonly FileEditor< MtrlTab > _materialTab; + private readonly FileEditor _materialTab; - private bool DrawMaterialPanel( MtrlTab tab, bool disabled ) + private bool DrawMaterialPanel(MtrlTab tab, bool disabled) { - DrawMaterialLivePreviewRebind( tab, disabled ); + DrawMaterialLivePreviewRebind(tab, disabled); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - var ret = DrawBackFaceAndTransparency( tab, disabled ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + var ret = DrawBackFaceAndTransparency(tab, disabled); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - ret |= DrawMaterialShader( tab, disabled ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + ret |= DrawMaterialShader(tab, disabled); - ret |= DrawMaterialTextureChange( tab, disabled ); - ret |= DrawMaterialColorSetChange( tab, disabled ); - ret |= DrawMaterialConstants( tab, disabled ); + ret |= DrawMaterialTextureChange(tab, disabled); + ret |= DrawMaterialColorSetChange(tab, disabled); + ret |= DrawMaterialConstants(tab, disabled); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - DrawOtherMaterialDetails( tab.Mtrl, disabled ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + DrawOtherMaterialDetails(tab.Mtrl, disabled); return !disabled && ret; } - private static void DrawMaterialLivePreviewRebind( MtrlTab tab, bool disabled ) + private static void DrawMaterialLivePreviewRebind(MtrlTab tab, bool disabled) { if (disabled) return; @@ -41,82 +42,74 @@ public partial class ModEditWindow if (ImGui.Button("Reload live preview")) tab.BindToMaterialInstances(); - if (tab.MaterialPreviewers.Count == 0 && tab.ColorSetPreviewers.Count == 0) - { - ImGui.SameLine(); + if (tab.MaterialPreviewers.Count != 0 || tab.ColorSetPreviewers.Count != 0) + return; - var textColor = ImGui.GetColorU32(ImGuiCol.Text); - var textColorWarning = (textColor & 0xFF000000u) | ((textColor & 0x00FEFEFE) >> 1) | 0x80u; // Half red - - using var c = ImRaii.PushColor(ImGuiCol.Text, textColorWarning); - - ImGui.TextUnformatted("The current material has not been found on your character. Please check the Import from Screen tab for more information."); - } + ImGui.SameLine(); + using var c = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder); + ImGui.TextUnformatted( + "The current material has not been found on your character. Please check the Import from Screen tab for more information."); } - private static bool DrawMaterialTextureChange( MtrlTab tab, bool disabled ) + private static bool DrawMaterialTextureChange(MtrlTab tab, bool disabled) { - if( tab.Textures.Count == 0 ) - { + if (tab.Textures.Count == 0) return false; - } - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - if( !ImGui.CollapsingHeader( "Textures and Samplers", ImGuiTreeNodeFlags.DefaultOpen ) ) - { + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + if (!ImGui.CollapsingHeader("Textures and Samplers", ImGuiTreeNodeFlags.DefaultOpen)) return false; - } var frameHeight = ImGui.GetFrameHeight(); var ret = false; - using var table = ImRaii.Table( "##Textures", 3 ); + using var table = ImRaii.Table("##Textures", 3); - ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, frameHeight ); - ImGui.TableSetupColumn( "Path" , ImGuiTableColumnFlags.WidthStretch ); - ImGui.TableSetupColumn( "Name" , ImGuiTableColumnFlags.WidthFixed, tab.TextureLabelWidth * UiHelpers.Scale ); - for( var i = 0; i < tab.Textures.Count; ++i ) + ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthFixed, frameHeight); + ImGui.TableSetupColumn("Path", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed, tab.TextureLabelWidth * UiHelpers.Scale); + foreach (var (label, textureI, samplerI, description, monoFont) in tab.Textures) { - var (label, textureI, samplerI, description, monoFont) = tab.Textures[i]; - - using var _ = ImRaii.PushId( samplerI ); - var tmp = tab.Mtrl.Textures[ textureI ].Path; - var unfolded = tab.UnfoldedTextures.Contains( samplerI ); + using var _ = ImRaii.PushId(samplerI); + var tmp = tab.Mtrl.Textures[textureI].Path; + var unfolded = tab.UnfoldedTextures.Contains(samplerI); ImGui.TableNextColumn(); - if( ImGuiUtil.DrawDisabledButton( ( unfolded ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight ).ToIconString(), new Vector2( frameHeight ), - "Settings for this texture and the associated sampler", false, true ) ) + if (ImGuiUtil.DrawDisabledButton((unfolded ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight).ToIconString(), + new Vector2(frameHeight), + "Settings for this texture and the associated sampler", false, true)) { unfolded = !unfolded; - if( unfolded ) - tab.UnfoldedTextures.Add( samplerI ); + if (unfolded) + tab.UnfoldedTextures.Add(samplerI); else - tab.UnfoldedTextures.Remove( samplerI ); - } - ImGui.TableNextColumn(); - ImGui.SetNextItemWidth( ImGui.GetContentRegionAvail().X ); - if( ImGui.InputText( string.Empty, ref tmp, Utf8GamePath.MaxGamePathLength, - disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None ) - && tmp.Length > 0 - && tmp != tab.Mtrl.Textures[ textureI ].Path ) - { - ret = true; - tab.Mtrl.Textures[ textureI ].Path = tmp; + tab.UnfoldedTextures.Remove(samplerI); } ImGui.TableNextColumn(); - using( var font = ImRaii.PushFont( UiBuilder.MonoFont, monoFont ) ) + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.InputText(string.Empty, ref tmp, Utf8GamePath.MaxGamePathLength, + disabled ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None) + && tmp.Length > 0 + && tmp != tab.Mtrl.Textures[textureI].Path) + { + ret = true; + tab.Mtrl.Textures[textureI].Path = tmp; + } + + ImGui.TableNextColumn(); + using (var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont)) { ImGui.AlignTextToFramePadding(); - if( description.Length > 0 ) - ImGuiUtil.LabeledHelpMarker( label, description ); + if (description.Length > 0) + ImGuiUtil.LabeledHelpMarker(label, description); else - ImGui.TextUnformatted( label ); + ImGui.TextUnformatted(label); } - if( unfolded ) + if (unfolded) { ImGui.TableNextColumn(); ImGui.TableNextColumn(); - ret |= DrawMaterialSampler( tab, disabled, textureI, samplerI ); + ret |= DrawMaterialSampler(tab, disabled, textureI, samplerI); ImGui.TableNextColumn(); } } @@ -124,26 +117,27 @@ public partial class ModEditWindow return ret; } - private static bool DrawBackFaceAndTransparency( MtrlTab tab, bool disabled ) + private static bool DrawBackFaceAndTransparency(MtrlTab tab, bool disabled) { const uint transparencyBit = 0x10; const uint backfaceBit = 0x01; var ret = false; - using var dis = ImRaii.Disabled( disabled ); + using var dis = ImRaii.Disabled(disabled); - var tmp = ( tab.Mtrl.ShaderPackage.Flags & transparencyBit ) != 0; - if( ImGui.Checkbox( "Enable Transparency", ref tmp ) ) + var tmp = (tab.Mtrl.ShaderPackage.Flags & transparencyBit) != 0; + if (ImGui.Checkbox("Enable Transparency", ref tmp)) { - tab.Mtrl.ShaderPackage.Flags = tmp ? tab.Mtrl.ShaderPackage.Flags | transparencyBit : tab.Mtrl.ShaderPackage.Flags & ~transparencyBit; - ret = true; + tab.Mtrl.ShaderPackage.Flags = + tmp ? tab.Mtrl.ShaderPackage.Flags | transparencyBit : tab.Mtrl.ShaderPackage.Flags & ~transparencyBit; + ret = true; tab.SetShaderPackageFlags(tab.Mtrl.ShaderPackage.Flags); } - ImGui.SameLine( 200 * UiHelpers.Scale + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X ); - tmp = ( tab.Mtrl.ShaderPackage.Flags & backfaceBit ) != 0; - if( ImGui.Checkbox( "Hide Backfaces", ref tmp ) ) + ImGui.SameLine(200 * UiHelpers.Scale + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X); + tmp = (tab.Mtrl.ShaderPackage.Flags & backfaceBit) != 0; + if (ImGui.Checkbox("Hide Backfaces", ref tmp)) { tab.Mtrl.ShaderPackage.Flags = tmp ? tab.Mtrl.ShaderPackage.Flags | backfaceBit : tab.Mtrl.ShaderPackage.Flags & ~backfaceBit; ret = true; @@ -153,106 +147,80 @@ public partial class ModEditWindow return ret; } - private static void DrawOtherMaterialDetails( MtrlFile file, bool _ ) + private static void DrawOtherMaterialDetails(MtrlFile file, bool _) { - if( !ImGui.CollapsingHeader( "Further Content" ) ) - { + if (!ImGui.CollapsingHeader("Further Content")) return; + + using (var sets = ImRaii.TreeNode("UV Sets", ImGuiTreeNodeFlags.DefaultOpen)) + { + if (sets) + foreach (var set in file.UvSets) + ImRaii.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose(); } - using( var sets = ImRaii.TreeNode( "UV Sets", ImGuiTreeNodeFlags.DefaultOpen ) ) - { - if( sets ) - { - foreach( var set in file.UvSets ) - { - ImRaii.TreeNode( $"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf ).Dispose(); - } - } - } - - if( file.AdditionalData.Length <= 0 ) - { + if (file.AdditionalData.Length <= 0) return; - } - using var t = ImRaii.TreeNode( $"Additional Data (Size: {file.AdditionalData.Length})###AdditionalData" ); - if( t ) - { - ImGuiUtil.TextWrapped( string.Join( ' ', file.AdditionalData.Select( c => $"{c:X2}" ) ) ); - } + using var t = ImRaii.TreeNode($"Additional Data (Size: {file.AdditionalData.Length})###AdditionalData"); + if (t) + ImGuiUtil.TextWrapped(string.Join(' ', file.AdditionalData.Select(c => $"{c:X2}"))); } private void DrawMaterialReassignmentTab() { - if( _editor.Files.Mdl.Count == 0 ) - { + if (_editor.Files.Mdl.Count == 0) return; - } - using var tab = ImRaii.TabItem( "Material Reassignment" ); - if( !tab ) - { + using var tab = ImRaii.TabItem("Material Reassignment"); + if (!tab) return; - } ImGui.NewLine(); - MaterialSuffix.Draw( _editor, ImGuiHelpers.ScaledVector2( 175, 0 ) ); + MaterialSuffix.Draw(_editor, ImGuiHelpers.ScaledVector2(175, 0)); ImGui.NewLine(); - using var child = ImRaii.Child( "##mdlFiles", -Vector2.One, true ); - if( !child ) - { + using var child = ImRaii.Child("##mdlFiles", -Vector2.One, true); + if (!child) return; - } - using var table = ImRaii.Table( "##files", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.One ); - if( !table ) - { + using var table = ImRaii.Table("##files", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.One); + if (!table) return; - } var iconSize = ImGui.GetFrameHeight() * Vector2.One; - foreach( var (info, idx) in _editor.MdlMaterialEditor.ModelFiles.WithIndex() ) + foreach (var (info, idx) in _editor.MdlMaterialEditor.ModelFiles.WithIndex()) { - using var id = ImRaii.PushId( idx ); + using var id = ImRaii.PushId(idx); ImGui.TableNextColumn(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Save.ToIconString(), iconSize, - "Save the changed mdl file.\nUse at own risk!", !info.Changed, true ) ) - { + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Save.ToIconString(), iconSize, + "Save the changed mdl file.\nUse at own risk!", !info.Changed, true)) info.Save(); - } ImGui.TableNextColumn(); - if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Recycle.ToIconString(), iconSize, - "Restore current changes to default.", !info.Changed, true ) ) - { + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Recycle.ToIconString(), iconSize, + "Restore current changes to default.", !info.Changed, true)) info.Restore(); - } ImGui.TableNextColumn(); - ImGui.TextUnformatted( info.Path.FullName[ ( _mod!.ModPath.FullName.Length + 1 ).. ] ); + ImGui.TextUnformatted(info.Path.FullName[(_mod!.ModPath.FullName.Length + 1)..]); ImGui.TableNextColumn(); - ImGui.SetNextItemWidth( 400 * UiHelpers.Scale ); - var tmp = info.CurrentMaterials[ 0 ]; - if( ImGui.InputText( "##0", ref tmp, 64 ) ) - { - info.SetMaterial( tmp, 0 ); - } + ImGui.SetNextItemWidth(400 * UiHelpers.Scale); + var tmp = info.CurrentMaterials[0]; + if (ImGui.InputText("##0", ref tmp, 64)) + info.SetMaterial(tmp, 0); - for( var i = 1; i < info.Count; ++i ) + for (var i = 1; i < info.Count; ++i) { ImGui.TableNextColumn(); ImGui.TableNextColumn(); ImGui.TableNextColumn(); ImGui.TableNextColumn(); - ImGui.SetNextItemWidth( 400 * UiHelpers.Scale ); - tmp = info.CurrentMaterials[ i ]; - if( ImGui.InputText( $"##{i}", ref tmp, 64 ) ) - { - info.SetMaterial( tmp, i ); - } + ImGui.SetNextItemWidth(400 * UiHelpers.Scale); + tmp = info.CurrentMaterials[i]; + if (ImGui.InputText($"##{i}", ref tmp, 64)) + info.SetMaterial(tmp, i); } } } -} \ No newline at end of file +} diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index b212e791..518566f5 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -5,7 +5,6 @@ using Penumbra.GameData.Files; using Penumbra.String.Classes; using System.Globalization; using System.Linq; -using Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.AdvancedWindow; diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs index 1b159efc..c0868b71 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs @@ -20,32 +20,32 @@ namespace Penumbra.UI.AdvancedWindow; public partial class ModEditWindow { - private static readonly ByteString DisassemblyLabel = ByteString.FromSpanUnsafe( "##disassembly"u8, true, true, true ); + private static readonly ByteString DisassemblyLabel = ByteString.FromSpanUnsafe("##disassembly"u8, true, true, true); - private readonly FileEditor< ShpkTab > _shaderPackageTab; + private readonly FileEditor _shaderPackageTab; - private static bool DrawShaderPackagePanel( ShpkTab file, bool disabled ) + private static bool DrawShaderPackagePanel(ShpkTab file, bool disabled) { - DrawShaderPackageSummary( file ); + DrawShaderPackageSummary(file); var ret = false; - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - ret |= DrawShaderPackageShaderArray( file, "Vertex Shader", file.Shpk.VertexShaders, disabled ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + ret |= DrawShaderPackageShaderArray(file, "Vertex Shader", file.Shpk.VertexShaders, disabled); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - ret |= DrawShaderPackageShaderArray( file, "Pixel Shader", file.Shpk.PixelShaders, disabled ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + ret |= DrawShaderPackageShaderArray(file, "Pixel Shader", file.Shpk.PixelShaders, disabled); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - ret |= DrawShaderPackageMaterialParamLayout( file, disabled ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + ret |= DrawShaderPackageMaterialParamLayout(file, disabled); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - ret |= DrawShaderPackageResources( file, disabled ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + ret |= DrawShaderPackageResources(file, disabled); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - DrawShaderPackageSelection( file ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + DrawShaderPackageSelection(file); - ImGui.Dummy( new Vector2( ImGui.GetTextLineHeight() / 2 ) ); - DrawOtherShaderPackageDetails( file ); + ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); + DrawOtherShaderPackageDetails(file); file.FileDialog.Draw(); @@ -54,28 +54,26 @@ public partial class ModEditWindow return !disabled && ret; } - private static void DrawShaderPackageSummary( ShpkTab tab ) + private static void DrawShaderPackageSummary(ShpkTab tab) { - ImGui.TextUnformatted( tab.Header ); - if( !tab.Shpk.Disassembled ) + ImGui.TextUnformatted(tab.Header); + if (!tab.Shpk.Disassembled) { - var textColor = ImGui.GetColorU32( ImGuiCol.Text ); - var textColorWarning = ( textColor & 0xFF000000u ) | ( ( textColor & 0x00FEFEFE ) >> 1 ) | 0x80u; // Half red + var textColor = ImGui.GetColorU32(ImGuiCol.Text); + var textColorWarning = (textColor & 0xFF000000u) | ((textColor & 0x00FEFEFE) >> 1) | 0x80u; // Half red - using var c = ImRaii.PushColor( ImGuiCol.Text, textColorWarning ); + using var c = ImRaii.PushColor(ImGuiCol.Text, textColorWarning); - ImGui.TextUnformatted( "Your system doesn't support disassembling shaders. Some functionality will be missing." ); + ImGui.TextUnformatted("Your system doesn't support disassembling shaders. Some functionality will be missing."); } } - private static void DrawShaderExportButton( ShpkTab tab, string objectName, Shader shader, int idx ) + private static void DrawShaderExportButton(ShpkTab tab, string objectName, Shader shader, int idx) { - if( !ImGui.Button( $"Export Shader Program Blob ({shader.Blob.Length} bytes)" ) ) - { + if (!ImGui.Button($"Export Shader Program Blob ({shader.Blob.Length} bytes)")) return; - } - var defaultName = objectName[ 0 ] switch + var defaultName = objectName[0] switch { 'V' => $"vs{idx}", 'P' => $"ps{idx}", @@ -83,247 +81,225 @@ public partial class ModEditWindow }; var blob = shader.Blob; - tab.FileDialog.OpenSavePicker( $"Export {objectName} #{idx} Program Blob to...", tab.Extension, defaultName, tab.Extension, ( success, name ) => - { - if( !success ) + tab.FileDialog.OpenSavePicker($"Export {objectName} #{idx} Program Blob to...", tab.Extension, defaultName, tab.Extension, + (success, name) => { - return; - } + if (!success) + return; - try - { - File.WriteAllBytes( name, blob ); - } - catch( Exception e ) - { - Penumbra.Chat.NotificationMessage( $"Could not export {defaultName}{tab.Extension} to {name}:\n{e.Message}", "Penumbra Advanced Editing", - NotificationType.Error ); - return; - } + try + { + File.WriteAllBytes(name, blob); + } + catch (Exception e) + { + Penumbra.Chat.NotificationMessage($"Could not export {defaultName}{tab.Extension} to {name}:\n{e.Message}", + "Penumbra Advanced Editing", + NotificationType.Error); + return; + } - Penumbra.Chat.NotificationMessage( $"Shader Program Blob {defaultName}{tab.Extension} exported successfully to {Path.GetFileName( name )}", - "Penumbra Advanced Editing", NotificationType.Success ); - }, null, false ); + Penumbra.Chat.NotificationMessage( + $"Shader Program Blob {defaultName}{tab.Extension} exported successfully to {Path.GetFileName(name)}", + "Penumbra Advanced Editing", NotificationType.Success); + }, null, false); } - private static void DrawShaderImportButton( ShpkTab tab, string objectName, Shader[] shaders, int idx ) + private static void DrawShaderImportButton(ShpkTab tab, string objectName, Shader[] shaders, int idx) { - if( !ImGui.Button( "Replace Shader Program Blob" ) ) - { + if (!ImGui.Button("Replace Shader Program Blob")) return; - } - tab.FileDialog.OpenFilePicker( $"Replace {objectName} #{idx} Program Blob...", "Shader Program Blobs{.o,.cso,.dxbc,.dxil}", ( success, name ) => - { - if( !success ) + tab.FileDialog.OpenFilePicker($"Replace {objectName} #{idx} Program Blob...", "Shader Program Blobs{.o,.cso,.dxbc,.dxil}", + (success, name) => { - return; - } + if (!success) + return; - try - { - shaders[ idx ].Blob = File.ReadAllBytes(name[0] ); - } - catch( Exception e ) - { - Penumbra.Chat.NotificationMessage( $"Could not import {name}:\n{e.Message}", "Penumbra Advanced Editing", NotificationType.Error ); - return; - } + try + { + shaders[idx].Blob = File.ReadAllBytes(name[0]); + } + catch (Exception e) + { + Penumbra.Chat.NotificationMessage($"Could not import {name}:\n{e.Message}", "Penumbra Advanced Editing", + NotificationType.Error); + return; + } - try - { - shaders[ idx ].UpdateResources( tab.Shpk ); - tab.Shpk.UpdateResources(); - } - catch( Exception e ) - { - tab.Shpk.SetInvalid(); - Penumbra.Chat.NotificationMessage( $"Failed to update resources after importing {name}:\n{e.Message}", "Penumbra Advanced Editing", - NotificationType.Error ); - return; - } + try + { + shaders[idx].UpdateResources(tab.Shpk); + tab.Shpk.UpdateResources(); + } + catch (Exception e) + { + tab.Shpk.SetInvalid(); + Penumbra.Chat.NotificationMessage($"Failed to update resources after importing {name}:\n{e.Message}", + "Penumbra Advanced Editing", + NotificationType.Error); + return; + } - tab.Shpk.SetChanged(); - }, 1, null, false ); + tab.Shpk.SetChanged(); + }, 1, null, false); } - private static unsafe void DrawRawDisassembly( Shader shader ) + private static unsafe void DrawRawDisassembly(Shader shader) { - using var t2 = ImRaii.TreeNode( "Raw Program Disassembly" ); - if( !t2 ) - { + using var t2 = ImRaii.TreeNode("Raw Program Disassembly"); + if (!t2) return; - } - using var font = ImRaii.PushFont( UiBuilder.MonoFont ); - var size = new Vector2( ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight() * 20 ); - ImGuiNative.igInputTextMultiline( DisassemblyLabel.Path, shader.Disassembly!.RawDisassembly.Path, ( uint )shader.Disassembly!.RawDisassembly.Length + 1, size, - ImGuiInputTextFlags.ReadOnly, null, null ); + using var font = ImRaii.PushFont(UiBuilder.MonoFont); + var size = new Vector2(ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight() * 20); + ImGuiNative.igInputTextMultiline(DisassemblyLabel.Path, shader.Disassembly!.RawDisassembly.Path, + (uint)shader.Disassembly!.RawDisassembly.Length + 1, size, + ImGuiInputTextFlags.ReadOnly, null, null); } - private static bool DrawShaderPackageShaderArray( ShpkTab tab, string objectName, Shader[] shaders, bool disabled ) + private static bool DrawShaderPackageShaderArray(ShpkTab tab, string objectName, Shader[] shaders, bool disabled) { - if( shaders.Length == 0 || !ImGui.CollapsingHeader( $"{objectName}s" ) ) - { + if (shaders.Length == 0 || !ImGui.CollapsingHeader($"{objectName}s")) return false; - } var ret = false; - for( var idx = 0; idx < shaders.Length; ++idx ) + for (var idx = 0; idx < shaders.Length; ++idx) { - var shader = shaders[ idx ]; - using var t = ImRaii.TreeNode( $"{objectName} #{idx}" ); - if( !t ) - { + var shader = shaders[idx]; + using var t = ImRaii.TreeNode($"{objectName} #{idx}"); + if (!t) continue; - } - DrawShaderExportButton( tab, objectName, shader, idx ); - if( !disabled && tab.Shpk.Disassembled ) + DrawShaderExportButton(tab, objectName, shader, idx); + if (!disabled && tab.Shpk.Disassembled) { ImGui.SameLine(); - DrawShaderImportButton( tab, objectName, shaders, idx ); + DrawShaderImportButton(tab, objectName, shaders, idx); } - ret |= DrawShaderPackageResourceArray( "Constant Buffers", "slot", true, shader.Constants, true ); - ret |= DrawShaderPackageResourceArray( "Samplers", "slot", false, shader.Samplers, true ); - ret |= DrawShaderPackageResourceArray( "Unordered Access Views", "slot", true, shader.Uavs, true ); + ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, true); + ret |= DrawShaderPackageResourceArray("Samplers", "slot", false, shader.Samplers, true); + ret |= DrawShaderPackageResourceArray("Unordered Access Views", "slot", true, shader.Uavs, true); - if( shader.AdditionalHeader.Length > 0 ) + if (shader.AdditionalHeader.Length > 0) { - using var t2 = ImRaii.TreeNode( $"Additional Header (Size: {shader.AdditionalHeader.Length})###AdditionalHeader" ); - if( t2 ) - { - ImGuiUtil.TextWrapped( string.Join( ' ', shader.AdditionalHeader.Select( c => $"{c:X2}" ) ) ); - } + using var t2 = ImRaii.TreeNode($"Additional Header (Size: {shader.AdditionalHeader.Length})###AdditionalHeader"); + if (t2) + ImGuiUtil.TextWrapped(string.Join(' ', shader.AdditionalHeader.Select(c => $"{c:X2}"))); } - if( tab.Shpk.Disassembled ) - DrawRawDisassembly( shader ); + if (tab.Shpk.Disassembled) + DrawRawDisassembly(shader); } return ret; } - private static bool DrawShaderPackageResource( string slotLabel, bool withSize, ref Resource resource, bool disabled ) + private static bool DrawShaderPackageResource(string slotLabel, bool withSize, ref Resource resource, bool disabled) { var ret = false; - if( !disabled ) + if (!disabled) { - ImGui.SetNextItemWidth( UiHelpers.Scale * 150.0f ); - if( ImGuiUtil.InputUInt16( $"{char.ToUpper( slotLabel[ 0 ] )}{slotLabel[ 1.. ].ToLower()}", ref resource.Slot, ImGuiInputTextFlags.None ) ) - { + ImGui.SetNextItemWidth(UiHelpers.Scale * 150.0f); + if (ImGuiUtil.InputUInt16($"{char.ToUpper(slotLabel[0])}{slotLabel[1..].ToLower()}", ref resource.Slot, ImGuiInputTextFlags.None)) ret = true; - } } - if( resource.Used == null ) - { + if (resource.Used == null) return ret; - } - var usedString = UsedComponentString( withSize, resource ); - if( usedString.Length > 0 ) - { - ImRaii.TreeNode( $"Used: {usedString}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + var usedString = UsedComponentString(withSize, resource); + if (usedString.Length > 0) + ImRaii.TreeNode($"Used: {usedString}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); else - { - ImRaii.TreeNode( "Unused", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + ImRaii.TreeNode("Unused", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); return ret; } - private static bool DrawShaderPackageResourceArray( string arrayName, string slotLabel, bool withSize, Resource[] resources, bool disabled ) + private static bool DrawShaderPackageResourceArray(string arrayName, string slotLabel, bool withSize, Resource[] resources, bool disabled) { - if( resources.Length == 0 ) - { + if (resources.Length == 0) return false; - } - using var t = ImRaii.TreeNode( arrayName ); - if( !t ) - { + using var t = ImRaii.TreeNode(arrayName); + if (!t) return false; - } var ret = false; - for( var idx = 0; idx < resources.Length; ++idx ) + for (var idx = 0; idx < resources.Length; ++idx) { - ref var buf = ref resources[ idx ]; + ref var buf = ref resources[idx]; var name = $"#{idx}: {buf.Name} (ID: 0x{buf.Id:X8}), {slotLabel}: {buf.Slot}" - + ( withSize ? $", size: {buf.Size} registers###{idx}: {buf.Name} (ID: 0x{buf.Id:X8})" : string.Empty ); - using var font = ImRaii.PushFont( UiBuilder.MonoFont ); - using var t2 = ImRaii.TreeNode( name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ); + + (withSize ? $", size: {buf.Size} registers###{idx}: {buf.Name} (ID: 0x{buf.Id:X8})" : string.Empty); + using var font = ImRaii.PushFont(UiBuilder.MonoFont); + using var t2 = ImRaii.TreeNode(name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet); font.Dispose(); - if( t2 ) - { - ret |= DrawShaderPackageResource( slotLabel, withSize, ref buf, disabled ); - } + if (t2) + ret |= DrawShaderPackageResource(slotLabel, withSize, ref buf, disabled); } return ret; } - private static bool DrawMaterialParamLayoutHeader( string label ) + private static bool DrawMaterialParamLayoutHeader(string label) { - using var font = ImRaii.PushFont( UiBuilder.MonoFont ); + using var font = ImRaii.PushFont(UiBuilder.MonoFont); var pos = ImGui.GetCursorScreenPos() - + new Vector2( ImGui.CalcTextSize( label ).X + 3 * ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetFrameHeight(), ImGui.GetStyle().FramePadding.Y ); + + new Vector2(ImGui.CalcTextSize(label).X + 3 * ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetFrameHeight(), + ImGui.GetStyle().FramePadding.Y); - var ret = ImGui.CollapsingHeader( label ); - ImGui.GetWindowDrawList().AddText( UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGui.GetColorU32( ImGuiCol.Text ), "Layout" ); + var ret = ImGui.CollapsingHeader(label); + ImGui.GetWindowDrawList() + .AddText(UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGui.GetColorU32(ImGuiCol.Text), "Layout"); return ret; } - private static bool DrawMaterialParamLayoutBufferSize( ShpkFile file, Resource? materialParams ) + private static bool DrawMaterialParamLayoutBufferSize(ShpkFile file, Resource? materialParams) { - var isSizeWellDefined = ( file.MaterialParamsSize & 0xF ) == 0 && ( !materialParams.HasValue || file.MaterialParamsSize == materialParams.Value.Size << 4 ); - if( isSizeWellDefined ) - { + var isSizeWellDefined = (file.MaterialParamsSize & 0xF) == 0 + && (!materialParams.HasValue || file.MaterialParamsSize == materialParams.Value.Size << 4); + if (isSizeWellDefined) return true; - } - ImGui.TextUnformatted( materialParams.HasValue + ImGui.TextUnformatted(materialParams.HasValue ? $"Buffer size mismatch: {file.MaterialParamsSize} bytes ≠ {materialParams.Value.Size} registers ({materialParams.Value.Size << 4} bytes)" - : $"Buffer size mismatch: {file.MaterialParamsSize} bytes, not a multiple of 16" ); + : $"Buffer size mismatch: {file.MaterialParamsSize} bytes, not a multiple of 16"); return false; } - private static bool DrawShaderPackageMaterialMatrix( ShpkTab tab, bool disabled ) + private static bool DrawShaderPackageMaterialMatrix(ShpkTab tab, bool disabled) { - ImGui.TextUnformatted( tab.Shpk.Disassembled + ImGui.TextUnformatted(tab.Shpk.Disassembled ? "Parameter positions (continuations are grayed out, unused values are red):" - : "Parameter positions (continuations are grayed out):" ); + : "Parameter positions (continuations are grayed out):"); - using var table = ImRaii.Table( "##MaterialParamLayout", 5, - ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg ); - if( !table ) - { + using var table = ImRaii.Table("##MaterialParamLayout", 5, + ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); + if (!table) return false; - } - ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, 25 * UiHelpers.Scale ); - ImGui.TableSetupColumn( "x", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale ); - ImGui.TableSetupColumn( "y", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale ); - ImGui.TableSetupColumn( "z", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale ); - ImGui.TableSetupColumn( "w", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale ); + ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthFixed, 25 * UiHelpers.Scale); + ImGui.TableSetupColumn("x", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale); + ImGui.TableSetupColumn("y", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale); + ImGui.TableSetupColumn("z", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale); + ImGui.TableSetupColumn("w", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale); ImGui.TableHeadersRow(); - var textColorStart = ImGui.GetColorU32( ImGuiCol.Text ); - var textColorCont = ( textColorStart & 0x00FFFFFFu ) | ( ( textColorStart & 0xFE000000u ) >> 1 ); // Half opacity - var textColorUnusedStart = ( textColorStart & 0xFF000000u ) | ( ( textColorStart & 0x00FEFEFE ) >> 1 ) | 0x80u; // Half red - var textColorUnusedCont = ( textColorUnusedStart & 0x00FFFFFFu ) | ( ( textColorUnusedStart & 0xFE000000u ) >> 1 ); + var textColorStart = ImGui.GetColorU32(ImGuiCol.Text); + var textColorCont = (textColorStart & 0x00FFFFFFu) | ((textColorStart & 0xFE000000u) >> 1); // Half opacity + var textColorUnusedStart = (textColorStart & 0xFF000000u) | ((textColorStart & 0x00FEFEFE) >> 1) | 0x80u; // Half red + var textColorUnusedCont = (textColorUnusedStart & 0x00FFFFFFu) | ((textColorUnusedStart & 0xFE000000u) >> 1); var ret = false; - for( var i = 0; i < tab.Matrix.GetLength( 0 ); ++i ) + for (var i = 0; i < tab.Matrix.GetLength(0); ++i) { ImGui.TableNextColumn(); - ImGui.TableHeader( $" [{i}]" ); - for( var j = 0; j < 4; ++j ) + ImGui.TableHeader($" [{i}]"); + for (var j = 0; j < 4; ++j) { - var (name, tooltip, idx, colorType) = tab.Matrix[ i, j ]; + var (name, tooltip, idx, colorType) = tab.Matrix[i, j]; var color = colorType switch { ShpkTab.ColorType.Unused => textColorUnusedStart, @@ -332,367 +308,307 @@ public partial class ModEditWindow ShpkTab.ColorType.Continuation | ShpkTab.ColorType.Used => textColorCont, _ => textColorStart, }; - using var _ = ImRaii.PushId( i * 4 + j ); + using var _ = ImRaii.PushId(i * 4 + j); var deletable = !disabled && idx >= 0; - using( var font = ImRaii.PushFont( UiBuilder.MonoFont, tooltip.Length > 0 ) ) + using (var font = ImRaii.PushFont(UiBuilder.MonoFont, tooltip.Length > 0)) { - using( var c = ImRaii.PushColor( ImGuiCol.Text, color ) ) + using (var c = ImRaii.PushColor(ImGuiCol.Text, color)) { ImGui.TableNextColumn(); - ImGui.Selectable( name ); - if( deletable && ImGui.IsItemClicked( ImGuiMouseButton.Right ) && ImGui.GetIO().KeyCtrl ) + ImGui.Selectable(name); + if (deletable && ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl) { - tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.RemoveItems( idx ); + tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.RemoveItems(idx); ret = true; tab.Update(); } } - ImGuiUtil.HoverTooltip( tooltip ); + ImGuiUtil.HoverTooltip(tooltip); } - if( deletable ) - { - ImGuiUtil.HoverTooltip( "\nControl + Right-Click to remove." ); - } + if (deletable) + ImGuiUtil.HoverTooltip("\nControl + Right-Click to remove."); } } return ret; } - private static void DrawShaderPackageMisalignedParameters( ShpkTab tab ) + private static void DrawShaderPackageMisalignedParameters(ShpkTab tab) { - using var t = ImRaii.TreeNode( "Misaligned / Overflowing Parameters" ); - if( !t ) - { + using var t = ImRaii.TreeNode("Misaligned / Overflowing Parameters"); + if (!t) return; - } - using var _ = ImRaii.PushFont( UiBuilder.MonoFont ); - foreach( var name in tab.MalformedParameters ) - { - ImRaii.TreeNode( name, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + using var _ = ImRaii.PushFont(UiBuilder.MonoFont); + foreach (var name in tab.MalformedParameters) + ImRaii.TreeNode(name, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); } - private static void DrawShaderPackageStartCombo( ShpkTab tab ) + private static void DrawShaderPackageStartCombo(ShpkTab tab) { - using var s = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing ); - using( var _ = ImRaii.PushFont( UiBuilder.MonoFont ) ) + using var s = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing); + using (var _ = ImRaii.PushFont(UiBuilder.MonoFont)) { - ImGui.SetNextItemWidth( UiHelpers.Scale * 400 ); - using var c = ImRaii.Combo( "##Start", tab.Orphans[ tab.NewMaterialParamStart ].Name ); - if( c ) - { - foreach( var (start, idx) in tab.Orphans.WithIndex() ) + ImGui.SetNextItemWidth(UiHelpers.Scale * 400); + using var c = ImRaii.Combo("##Start", tab.Orphans[tab.NewMaterialParamStart].Name); + if (c) + foreach (var (start, idx) in tab.Orphans.WithIndex()) { - if( ImGui.Selectable( start.Name, idx == tab.NewMaterialParamStart ) ) - { - tab.UpdateOrphanStart( idx ); - } + if (ImGui.Selectable(start.Name, idx == tab.NewMaterialParamStart)) + tab.UpdateOrphanStart(idx); } - } } ImGui.SameLine(); - ImGui.TextUnformatted( "Start" ); + ImGui.TextUnformatted("Start"); } - private static void DrawShaderPackageEndCombo( ShpkTab tab ) + private static void DrawShaderPackageEndCombo(ShpkTab tab) { - using var s = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing ); - using( var _ = ImRaii.PushFont( UiBuilder.MonoFont ) ) + using var s = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing); + using (var _ = ImRaii.PushFont(UiBuilder.MonoFont)) { - ImGui.SetNextItemWidth( UiHelpers.Scale * 400 ); - using var c = ImRaii.Combo( "##End", tab.Orphans[ tab.NewMaterialParamEnd ].Name ); - if( c ) + ImGui.SetNextItemWidth(UiHelpers.Scale * 400); + using var c = ImRaii.Combo("##End", tab.Orphans[tab.NewMaterialParamEnd].Name); + if (c) { - var current = tab.Orphans[ tab.NewMaterialParamStart ].Index; - for( var i = tab.NewMaterialParamStart; i < tab.Orphans.Count; ++i ) + var current = tab.Orphans[tab.NewMaterialParamStart].Index; + for (var i = tab.NewMaterialParamStart; i < tab.Orphans.Count; ++i) { - var next = tab.Orphans[ i ]; - if( current++ != next.Index ) - { + var next = tab.Orphans[i]; + if (current++ != next.Index) break; - } - if( ImGui.Selectable( next.Name, i == tab.NewMaterialParamEnd ) ) - { + if (ImGui.Selectable(next.Name, i == tab.NewMaterialParamEnd)) tab.NewMaterialParamEnd = i; - } } } } ImGui.SameLine(); - ImGui.TextUnformatted( "End" ); + ImGui.TextUnformatted("End"); } - private static bool DrawShaderPackageNewParameter( ShpkTab tab ) + private static bool DrawShaderPackageNewParameter(ShpkTab tab) { - if( tab.Orphans.Count == 0 ) - { + if (tab.Orphans.Count == 0) return false; - } - DrawShaderPackageStartCombo( tab ); - DrawShaderPackageEndCombo( tab ); + DrawShaderPackageStartCombo(tab); + DrawShaderPackageEndCombo(tab); - ImGui.SetNextItemWidth( UiHelpers.Scale * 400 ); - if( ImGui.InputText( "Name", ref tab.NewMaterialParamName, 63 ) ) - { - tab.NewMaterialParamId = Crc32.Get( tab.NewMaterialParamName, 0xFFFFFFFFu ); - } + ImGui.SetNextItemWidth(UiHelpers.Scale * 400); + if (ImGui.InputText("Name", ref tab.NewMaterialParamName, 63)) + tab.NewMaterialParamId = Crc32.Get(tab.NewMaterialParamName, 0xFFFFFFFFu); - var tooltip = tab.UsedIds.Contains( tab.NewMaterialParamId ) + var tooltip = tab.UsedIds.Contains(tab.NewMaterialParamId) ? "The ID is already in use. Please choose a different name." : string.Empty; - if( !ImGuiUtil.DrawDisabledButton( $"Add ID 0x{tab.NewMaterialParamId:X8}", new Vector2( 400 * UiHelpers.Scale, ImGui.GetFrameHeight() ), tooltip, - tooltip.Length > 0 ) ) - { + if (!ImGuiUtil.DrawDisabledButton($"Add ID 0x{tab.NewMaterialParamId:X8}", new Vector2(400 * UiHelpers.Scale, ImGui.GetFrameHeight()), + tooltip, + tooltip.Length > 0)) return false; - } - tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.AddItem( new MaterialParam + tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.AddItem(new MaterialParam { Id = tab.NewMaterialParamId, - ByteOffset = ( ushort )( tab.Orphans[ tab.NewMaterialParamStart ].Index << 2 ), - ByteSize = ( ushort )( ( tab.NewMaterialParamEnd - tab.NewMaterialParamStart + 1 ) << 2 ), - } ); + ByteOffset = (ushort)(tab.Orphans[tab.NewMaterialParamStart].Index << 2), + ByteSize = (ushort)((tab.NewMaterialParamEnd - tab.NewMaterialParamStart + 1) << 2), + }); tab.Update(); return true; } - private static bool DrawShaderPackageMaterialParamLayout( ShpkTab tab, bool disabled ) + private static bool DrawShaderPackageMaterialParamLayout(ShpkTab tab, bool disabled) { var ret = false; - var materialParams = tab.Shpk.GetConstantById( MaterialParamsConstantId ); - if( !DrawMaterialParamLayoutHeader( materialParams?.Name ?? "Material Parameter" ) ) - { + var materialParams = tab.Shpk.GetConstantById(MaterialParamsConstantId); + if (!DrawMaterialParamLayoutHeader(materialParams?.Name ?? "Material Parameter")) return false; - } - var sizeWellDefined = DrawMaterialParamLayoutBufferSize( tab.Shpk, materialParams ); + var sizeWellDefined = DrawMaterialParamLayoutBufferSize(tab.Shpk, materialParams); - ret |= DrawShaderPackageMaterialMatrix( tab, disabled ); + ret |= DrawShaderPackageMaterialMatrix(tab, disabled); - if( tab.MalformedParameters.Count > 0 ) - { - DrawShaderPackageMisalignedParameters( tab ); - } - else if( !disabled && sizeWellDefined ) - { - ret |= DrawShaderPackageNewParameter( tab ); - } + if (tab.MalformedParameters.Count > 0) + DrawShaderPackageMisalignedParameters(tab); + else if (!disabled && sizeWellDefined) + ret |= DrawShaderPackageNewParameter(tab); return ret; } - private static bool DrawShaderPackageResources( ShpkTab tab, bool disabled ) + private static bool DrawShaderPackageResources(ShpkTab tab, bool disabled) { var ret = false; - if( !ImGui.CollapsingHeader( "Shader Resources" ) ) - { + if (!ImGui.CollapsingHeader("Shader Resources")) return false; - } - ret |= DrawShaderPackageResourceArray( "Constant Buffers", "type", true, tab.Shpk.Constants, disabled ); - ret |= DrawShaderPackageResourceArray( "Samplers", "type", false, tab.Shpk.Samplers, disabled ); - ret |= DrawShaderPackageResourceArray( "Unordered Access Views", "type", false, tab.Shpk.Uavs, disabled ); + ret |= DrawShaderPackageResourceArray("Constant Buffers", "type", true, tab.Shpk.Constants, disabled); + ret |= DrawShaderPackageResourceArray("Samplers", "type", false, tab.Shpk.Samplers, disabled); + ret |= DrawShaderPackageResourceArray("Unordered Access Views", "type", false, tab.Shpk.Uavs, disabled); return ret; } - private static void DrawKeyArray( string arrayName, bool withId, IReadOnlyCollection< Key > keys ) + private static void DrawKeyArray(string arrayName, bool withId, IReadOnlyCollection keys) { - if( keys.Count == 0 ) - { + if (keys.Count == 0) return; - } - using var t = ImRaii.TreeNode( arrayName ); - if( !t ) - { + using var t = ImRaii.TreeNode(arrayName); + if (!t) return; - } - using var font = ImRaii.PushFont( UiBuilder.MonoFont ); - foreach( var (key, idx) in keys.WithIndex() ) + using var font = ImRaii.PushFont(UiBuilder.MonoFont); + foreach (var (key, idx) in keys.WithIndex()) { - using var t2 = ImRaii.TreeNode( withId ? $"#{idx}: ID: 0x{key.Id:X8}" : $"#{idx}" ); - if( t2 ) + using var t2 = ImRaii.TreeNode(withId ? $"#{idx}: ID: 0x{key.Id:X8}" : $"#{idx}"); + if (t2) { - ImRaii.TreeNode( $"Default Value: 0x{key.DefaultValue:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - ImRaii.TreeNode( $"Known Values: {string.Join( ", ", Array.ConvertAll( key.Values, value => $"0x{value:X8}" ) )}", - ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); + ImRaii.TreeNode($"Default Value: 0x{key.DefaultValue:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); + ImRaii.TreeNode($"Known Values: {string.Join(", ", Array.ConvertAll(key.Values, value => $"0x{value:X8}"))}", + ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); } } } - private static void DrawShaderPackageNodes( ShpkTab tab ) + private static void DrawShaderPackageNodes(ShpkTab tab) { - if( tab.Shpk.Nodes.Length <= 0 ) - { + if (tab.Shpk.Nodes.Length <= 0) return; - } - using var t = ImRaii.TreeNode( $"Nodes ({tab.Shpk.Nodes.Length})###Nodes" ); - if( !t ) - { + using var t = ImRaii.TreeNode($"Nodes ({tab.Shpk.Nodes.Length})###Nodes"); + if (!t) return; - } - foreach( var (node, idx) in tab.Shpk.Nodes.WithIndex() ) + foreach (var (node, idx) in tab.Shpk.Nodes.WithIndex()) { - using var font = ImRaii.PushFont( UiBuilder.MonoFont ); - using var t2 = ImRaii.TreeNode( $"#{idx:D4}: Selector: 0x{node.Selector:X8}" ); - if( !t2 ) - { + using var font = ImRaii.PushFont(UiBuilder.MonoFont); + using var t2 = ImRaii.TreeNode($"#{idx:D4}: Selector: 0x{node.Selector:X8}"); + if (!t2) continue; - } - foreach( var (key, keyIdx) in node.SystemKeys.WithIndex() ) - { - ImRaii.TreeNode( $"System Key 0x{tab.Shpk.SystemKeys[ keyIdx ].Id:X8} = 0x{key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + foreach (var (key, keyIdx) in node.SystemKeys.WithIndex()) + ImRaii.TreeNode($"System Key 0x{tab.Shpk.SystemKeys[keyIdx].Id:X8} = 0x{key:X8}", + ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); - foreach( var (key, keyIdx) in node.SceneKeys.WithIndex() ) - { - ImRaii.TreeNode( $"Scene Key 0x{tab.Shpk.SceneKeys[ keyIdx ].Id:X8} = 0x{key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + foreach (var (key, keyIdx) in node.SceneKeys.WithIndex()) + ImRaii.TreeNode($"Scene Key 0x{tab.Shpk.SceneKeys[keyIdx].Id:X8} = 0x{key:X8}", + ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); - foreach( var (key, keyIdx) in node.MaterialKeys.WithIndex() ) - { - ImRaii.TreeNode( $"Material Key 0x{tab.Shpk.MaterialKeys[ keyIdx ].Id:X8} = 0x{key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + foreach (var (key, keyIdx) in node.MaterialKeys.WithIndex()) + ImRaii.TreeNode($"Material Key 0x{tab.Shpk.MaterialKeys[keyIdx].Id:X8} = 0x{key:X8}", + ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); - foreach( var (key, keyIdx) in node.SubViewKeys.WithIndex() ) - { - ImRaii.TreeNode( $"Sub-View Key #{keyIdx} = 0x{key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + foreach (var (key, keyIdx) in node.SubViewKeys.WithIndex()) + ImRaii.TreeNode($"Sub-View Key #{keyIdx} = 0x{key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); - ImRaii.TreeNode( $"Pass Indices: {string.Join( ' ', node.PassIndices.Select( c => $"{c:X2}" ) )}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - foreach( var (pass, passIdx) in node.Passes.WithIndex() ) + ImRaii.TreeNode($"Pass Indices: {string.Join(' ', node.PassIndices.Select(c => $"{c:X2}"))}", + ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); + foreach (var (pass, passIdx) in node.Passes.WithIndex()) { - ImRaii.TreeNode( $"Pass #{passIdx}: ID: 0x{pass.Id:X8}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}", - ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ) - .Dispose(); + ImRaii.TreeNode($"Pass #{passIdx}: ID: 0x{pass.Id:X8}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}", + ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet) + .Dispose(); } } } - private static void DrawShaderPackageSelection( ShpkTab tab ) + private static void DrawShaderPackageSelection(ShpkTab tab) { - if( !ImGui.CollapsingHeader( "Shader Selection" ) ) - { + if (!ImGui.CollapsingHeader("Shader Selection")) return; - } - DrawKeyArray( "System Keys", true, tab.Shpk.SystemKeys ); - DrawKeyArray( "Scene Keys", true, tab.Shpk.SceneKeys ); - DrawKeyArray( "Material Keys", true, tab.Shpk.MaterialKeys ); - DrawKeyArray( "Sub-View Keys", false, tab.Shpk.SubViewKeys ); + DrawKeyArray("System Keys", true, tab.Shpk.SystemKeys); + DrawKeyArray("Scene Keys", true, tab.Shpk.SceneKeys); + DrawKeyArray("Material Keys", true, tab.Shpk.MaterialKeys); + DrawKeyArray("Sub-View Keys", false, tab.Shpk.SubViewKeys); - DrawShaderPackageNodes( tab ); - using var t = ImRaii.TreeNode( $"Node Selectors ({tab.Shpk.NodeSelectors.Count})###NodeSelectors" ); - if( t ) + DrawShaderPackageNodes(tab); + using var t = ImRaii.TreeNode($"Node Selectors ({tab.Shpk.NodeSelectors.Count})###NodeSelectors"); + if (t) { - using var font = ImRaii.PushFont( UiBuilder.MonoFont ); - foreach( var selector in tab.Shpk.NodeSelectors ) - { - ImRaii.TreeNode( $"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); - } + using var font = ImRaii.PushFont(UiBuilder.MonoFont); + foreach (var selector in tab.Shpk.NodeSelectors) + ImRaii.TreeNode($"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet) + .Dispose(); } } - private static void DrawOtherShaderPackageDetails( ShpkTab tab ) + private static void DrawOtherShaderPackageDetails(ShpkTab tab) { - if( !ImGui.CollapsingHeader( "Further Content" ) ) - { + if (!ImGui.CollapsingHeader("Further Content")) return; - } - ImRaii.TreeNode( $"Version: 0x{tab.Shpk.Version:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet ).Dispose(); + ImRaii.TreeNode($"Version: 0x{tab.Shpk.Version:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); - if( tab.Shpk.AdditionalData.Length > 0 ) + if (tab.Shpk.AdditionalData.Length > 0) { - using var t = ImRaii.TreeNode( $"Additional Data (Size: {tab.Shpk.AdditionalData.Length})###AdditionalData" ); - if( t ) - { - ImGuiUtil.TextWrapped( string.Join( ' ', tab.Shpk.AdditionalData.Select( c => $"{c:X2}" ) ) ); - } + using var t = ImRaii.TreeNode($"Additional Data (Size: {tab.Shpk.AdditionalData.Length})###AdditionalData"); + if (t) + ImGuiUtil.TextWrapped(string.Join(' ', tab.Shpk.AdditionalData.Select(c => $"{c:X2}"))); } } - private static string UsedComponentString( bool withSize, in Resource resource ) + private static string UsedComponentString(bool withSize, in Resource resource) { - var sb = new StringBuilder( 256 ); - if( withSize ) + var sb = new StringBuilder(256); + if (withSize) { - foreach( var (components, i) in ( resource.Used ?? Array.Empty< DisassembledShader.VectorComponents >() ).WithIndex() ) + foreach (var (components, i) in (resource.Used ?? Array.Empty()).WithIndex()) { - switch( components ) + switch (components) { case 0: break; case DisassembledShader.VectorComponents.All: - sb.Append( $"[{i}], " ); + sb.Append($"[{i}], "); break; default: - sb.Append( $"[{i}]." ); - foreach( var c in components.ToString().Where( char.IsUpper ) ) - { - sb.Append( char.ToLower( c ) ); - } + sb.Append($"[{i}]."); + foreach (var c in components.ToString().Where(char.IsUpper)) + sb.Append(char.ToLower(c)); - sb.Append( ", " ); + sb.Append(", "); break; } } - switch( resource.UsedDynamically ?? 0 ) + switch (resource.UsedDynamically ?? 0) { case 0: break; case DisassembledShader.VectorComponents.All: - sb.Append( "[*], " ); + sb.Append("[*], "); break; default: - sb.Append( "[*]." ); - foreach( var c in resource.UsedDynamically!.Value.ToString().Where( char.IsUpper ) ) - { - sb.Append( char.ToLower( c ) ); - } + sb.Append("[*]."); + foreach (var c in resource.UsedDynamically!.Value.ToString().Where(char.IsUpper)) + sb.Append(char.ToLower(c)); - sb.Append( ", " ); + sb.Append(", "); break; } } else { - var components = ( resource.Used is { Length: > 0 } ? resource.Used[ 0 ] : 0 ) | ( resource.UsedDynamically ?? 0 ); - if( ( components & DisassembledShader.VectorComponents.X ) != 0 ) - { - sb.Append( "Red, " ); - } + var components = (resource.Used is { Length: > 0 } ? resource.Used[0] : 0) | (resource.UsedDynamically ?? 0); + if ((components & DisassembledShader.VectorComponents.X) != 0) + sb.Append("Red, "); - if( ( components & DisassembledShader.VectorComponents.Y ) != 0 ) - { - sb.Append( "Green, " ); - } + if ((components & DisassembledShader.VectorComponents.Y) != 0) + sb.Append("Green, "); - if( ( components & DisassembledShader.VectorComponents.Z ) != 0 ) - { - sb.Append( "Blue, " ); - } + if ((components & DisassembledShader.VectorComponents.Z) != 0) + sb.Append("Blue, "); - if( ( components & DisassembledShader.VectorComponents.W ) != 0 ) - { - sb.Append( "Alpha, " ); - } + if ((components & DisassembledShader.VectorComponents.W) != 0) + sb.Append("Alpha, "); } - return sb.Length == 0 ? string.Empty : sb.ToString( 0, sb.Length - 2 ); + return sb.Length == 0 ? string.Empty : sb.ToString(0, sb.Length - 2); } -} \ No newline at end of file +} diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index e40a7915..e90c148e 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -533,14 +533,20 @@ public partial class ModEditWindow : Window, IDisposable var ret = new HashSet(); foreach (var path in _activeCollections.Current.ResolvedFiles.Keys) + { if (path.Path.StartsWith(prefix)) ret.Add(path); + } if (_mod != null) foreach (var option in _mod.Groups.SelectMany(g => g).Append(_mod.Default)) + { foreach (var path in option.Files.Keys) + { if (path.Path.StartsWith(prefix)) ret.Add(path); + } + } return ret; }