Auto-formatting and some cleanup.

This commit is contained in:
Ottermandias 2023-08-31 17:12:39 +02:00
parent ff01276869
commit e5e555b981
8 changed files with 876 additions and 959 deletions

View file

@ -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<byte>.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<MtrlFile.ColorSet.RowArray>())
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<MtrlFile.ColorSet.RowArray>());
if (data.Length >= Marshal.SizeOf<MtrlFile.ColorSet.RowArray>() + Marshal.SizeOf<MtrlFile.ColorDyeSet.RowArray>()
&& 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<MtrlFile.ColorSet.RowArray>(),
Marshal.SizeOf<MtrlFile.ColorDyeSet.RowArray>());
}
}
}
@ -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<Vector3> 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);
}
}

View file

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

View file

@ -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<int> VertexShaders = new(16);
public readonly HashSet<int> 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<int> UnfoldedTextures = new(4);
public readonly HashSet<uint> 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<LiveMaterialPreviewer> 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<T>(string category, uint? id, bool mayVary) where T : class
{
return TryGetShpkDevkitData<T>(AssociatedShpkDevkit, LoadedShpkDevkitPathName, category, id, mayVary)
?? TryGetShpkDevkitData<T>(AssociatedBaseDevkit, LoadedBaseDevkitPathName, category, id, mayVary);
}
=> TryGetShpkDevkitData<T>(AssociatedShpkDevkit, LoadedShpkDevkitPathName, category, id, mayVary)
?? TryGetShpkDevkitData<T>(AssociatedBaseDevkit, LoadedBaseDevkitPathName, category, id, mayVary);
private T? TryGetShpkDevkitData<T>(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<DevkitShaderKey>("ShaderKeys", key.Id, false);
var dkData = TryGetShpkDevkitData<DevkitShaderKey>("ShaderKeys", key.Id, false);
var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label);
var valueSet = new HashSet<uint>(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<string>("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<DevkitSampler>("Samplers", samplerId, true);
var dkData = TryGetShpkDevkitData<DevkitSampler>("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<T> FindOrAddGroup<T>(List<(string, List<T>)> groups, string name)
{
foreach (var (groupName, group) in groups)
{
if (string.Equals(name, groupName, StringComparison.Ordinal))
return group;
}
var newGroup = new List<T>(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<DevkitConstant[]>("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<uint, DevkitShaderKeyValue> 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<DevkitConstantValue>();
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;
}
}

View file

@ -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<string> StandardShaderPackages = new string[]
private static readonly IReadOnlyList<string> StandardShaderPackages = new[]
{
"3dui.shpk",
// "apricot_decal_dummy.shpk",
@ -76,7 +70,7 @@ public partial class ModEditWindow
Border = 3,
}
private static readonly IReadOnlyList<string> TextureAddressModeTooltips = new string[]
private static readonly IReadOnlyList<string> 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;
}
/// <summary>

View file

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

View file

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

View file

@ -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<ShpkTab> _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<Key> 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<DisassembledShader.VectorComponents>()).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);
}
}
}

View file

@ -533,14 +533,20 @@ public partial class ModEditWindow : Window, IDisposable
var ret = new HashSet<Utf8GamePath>();
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;
}