Auto-formatting, generous application of ImUtf8, minor cleanups.

This commit is contained in:
Ottermandias 2024-08-04 22:48:15 +02:00
parent c8e859ae05
commit f8b034c42d
13 changed files with 647 additions and 517 deletions

View file

@ -13,7 +13,7 @@ public partial class MtrlTab
{ {
private const float ColorTableScalarSize = 65.0f; private const float ColorTableScalarSize = 65.0f;
private int _colorTableSelectedPair = 0; private int _colorTableSelectedPair;
private bool DrawColorTable(ColorTable table, ColorDyeTable? dyeTable, bool disabled) private bool DrawColorTable(ColorTable table, ColorDyeTable? dyeTable, bool disabled)
{ {
@ -23,25 +23,27 @@ public partial class MtrlTab
private void DrawColorTablePairSelector(ColorTable table, bool disabled) private void DrawColorTablePairSelector(ColorTable table, bool disabled)
{ {
var style = ImGui.GetStyle();
var itemSpacing = style.ItemSpacing.X;
var itemInnerSpacing = style.ItemInnerSpacing.X;
var framePadding = style.FramePadding;
var buttonWidth = (ImGui.GetContentRegionAvail().X - itemSpacing * 7.0f) * 0.125f;
var frameHeight = ImGui.GetFrameHeight();
var highlighterSize = ImUtf8.CalcIconSize(FontAwesomeIcon.Crosshairs) + framePadding * 2.0f;
var spaceWidth = ImUtf8.CalcTextSize(" "u8).X;
var spacePadding = (int)MathF.Ceiling((highlighterSize.X + framePadding.X + itemInnerSpacing) / spaceWidth);
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var alignment = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f)); using var alignment = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
var style = ImGui.GetStyle();
var itemSpacing = style.ItemSpacing.X;
var itemInnerSpacing = style.ItemInnerSpacing.X;
var framePadding = style.FramePadding;
var buttonWidth = (ImGui.GetContentRegionAvail().X - itemSpacing * 7.0f) * 0.125f;
var frameHeight = ImGui.GetFrameHeight();
var highlighterSize = ImUtf8.CalcIconSize(FontAwesomeIcon.Crosshairs) + framePadding * 2.0f;
var spaceWidth = ImUtf8.CalcTextSize(" "u8).X;
var spacePadding = (int)MathF.Ceiling((highlighterSize.X + framePadding.X + itemInnerSpacing) / spaceWidth);
for (var i = 0; i < ColorTable.NumRows >> 1; i += 8) for (var i = 0; i < ColorTable.NumRows >> 1; i += 8)
{ {
for (var j = 0; j < 8; ++j) for (var j = 0; j < 8; ++j)
{ {
var pairIndex = i + j; var pairIndex = i + j;
using (var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), pairIndex == _colorTableSelectedPair)) using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), pairIndex == _colorTableSelectedPair))
{ {
if (ImUtf8.Button($"#{pairIndex + 1}".PadLeft(3 + spacePadding), new Vector2(buttonWidth, ImGui.GetFrameHeightWithSpacing() + frameHeight))) if (ImUtf8.Button($"#{pairIndex + 1}".PadLeft(3 + spacePadding),
new Vector2(buttonWidth, ImGui.GetFrameHeightWithSpacing() + frameHeight)))
_colorTableSelectedPair = pairIndex; _colorTableSelectedPair = pairIndex;
} }
@ -79,12 +81,10 @@ public partial class MtrlTab
private bool DrawColorTablePairEditor(ColorTable table, ColorDyeTable? dyeTable, bool disabled) private bool DrawColorTablePairEditor(ColorTable table, ColorDyeTable? dyeTable, bool disabled)
{ {
var retA = false; var retA = false;
var retB = false; var retB = false;
ref var rowA = ref table[_colorTableSelectedPair << 1]; var dyeA = dyeTable?[_colorTableSelectedPair << 1] ?? default;
ref var rowB = ref table[(_colorTableSelectedPair << 1) | 1]; var dyeB = dyeTable?[(_colorTableSelectedPair << 1) | 1] ?? default;
var dyeA = dyeTable != null ? dyeTable[_colorTableSelectedPair << 1] : default;
var dyeB = dyeTable != null ? dyeTable[(_colorTableSelectedPair << 1) | 1] : default;
var previewDyeA = _stainService.GetStainCombo(dyeA.Channel).CurrentSelection.Key; var previewDyeA = _stainService.GetStainCombo(dyeA.Channel).CurrentSelection.Key;
var previewDyeB = _stainService.GetStainCombo(dyeB.Channel).CurrentSelection.Key; var previewDyeB = _stainService.GetStainCombo(dyeB.Channel).CurrentSelection.Key;
var dyePackA = _stainService.GudStmFile.GetValueOrNull(dyeA.Template, previewDyeA); var dyePackA = _stainService.GudStmFile.GetValueOrNull(dyeA.Template, previewDyeA);
@ -108,68 +108,96 @@ public partial class MtrlTab
using (var columns = ImUtf8.Columns(2, "ColorTable"u8)) using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{ {
using var dis = ImRaii.Disabled(disabled); using var dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("ColorsA"u8)) using (ImUtf8.PushId("ColorsA"u8))
{
retA |= DrawColors(table, dyeTable, dyePackA, _colorTableSelectedPair << 1); retA |= DrawColors(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next(); columns.Next();
using (var id = ImUtf8.PushId("ColorsB"u8)) using (ImUtf8.PushId("ColorsB"u8))
{
retB |= DrawColors(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1); retB |= DrawColors(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
} }
DrawHeader(" Physical Parameters"u8); DrawHeader(" Physical Parameters"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8)) using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{ {
using var dis = ImRaii.Disabled(disabled); using var dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("PbrA"u8)) using (ImUtf8.PushId("PbrA"u8))
{
retA |= DrawPbr(table, dyeTable, dyePackA, _colorTableSelectedPair << 1); retA |= DrawPbr(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next(); columns.Next();
using (var id = ImUtf8.PushId("PbrB"u8)) using (ImUtf8.PushId("PbrB"u8))
{
retB |= DrawPbr(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1); retB |= DrawPbr(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
} }
DrawHeader(" Sheen Layer Parameters"u8); DrawHeader(" Sheen Layer Parameters"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8)) using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{ {
using var dis = ImRaii.Disabled(disabled); using var dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("SheenA"u8)) using (ImUtf8.PushId("SheenA"u8))
{
retA |= DrawSheen(table, dyeTable, dyePackA, _colorTableSelectedPair << 1); retA |= DrawSheen(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next(); columns.Next();
using (var id = ImUtf8.PushId("SheenB"u8)) using (ImUtf8.PushId("SheenB"u8))
{
retB |= DrawSheen(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1); retB |= DrawSheen(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
} }
DrawHeader(" Pair Blending"u8); DrawHeader(" Pair Blending"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8)) using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{ {
using var dis = ImRaii.Disabled(disabled); using var dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("BlendingA"u8)) using (ImUtf8.PushId("BlendingA"u8))
{
retA |= DrawBlending(table, dyeTable, dyePackA, _colorTableSelectedPair << 1); retA |= DrawBlending(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next(); columns.Next();
using (var id = ImUtf8.PushId("BlendingB"u8)) using (ImUtf8.PushId("BlendingB"u8))
{
retB |= DrawBlending(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1); retB |= DrawBlending(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
} }
DrawHeader(" Material Template"u8); DrawHeader(" Material Template"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8)) using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{ {
using var dis = ImRaii.Disabled(disabled); using var dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("TemplateA"u8)) using (ImUtf8.PushId("TemplateA"u8))
{
retA |= DrawTemplate(table, dyeTable, dyePackA, _colorTableSelectedPair << 1); retA |= DrawTemplate(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next(); columns.Next();
using (var id = ImUtf8.PushId("TemplateB"u8)) using (ImUtf8.PushId("TemplateB"u8))
{
retB |= DrawTemplate(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1); retB |= DrawTemplate(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
} }
if (dyeTable != null) if (dyeTable != null)
{ {
DrawHeader(" Dye Properties"u8); DrawHeader(" Dye Properties"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8)) using var columns = ImUtf8.Columns(2, "ColorTable"u8);
using var dis = ImRaii.Disabled(disabled);
using (ImUtf8.PushId("DyeA"u8))
{ {
using var dis = ImRaii.Disabled(disabled); retA |= DrawDye(dyeTable, dyePackA, _colorTableSelectedPair << 1);
using (var id = ImUtf8.PushId("DyeA"u8)) }
retA |= DrawDye(dyeTable, dyePackA, _colorTableSelectedPair << 1);
columns.Next(); columns.Next();
using (var id = ImUtf8.PushId("DyeB"u8)) using (ImUtf8.PushId("DyeB"u8))
retB |= DrawDye(dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1); {
retB |= DrawDye(dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
} }
} }
@ -177,11 +205,16 @@ public partial class MtrlTab
using (var columns = ImUtf8.Columns(2, "ColorTable"u8)) using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{ {
using var dis = ImRaii.Disabled(disabled); using var dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("FurtherA"u8)) using (ImUtf8.PushId("FurtherA"u8))
{
retA |= DrawFurther(table, dyeTable, dyePackA, _colorTableSelectedPair << 1); retA |= DrawFurther(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next(); columns.Next();
using (var id = ImUtf8.PushId("FurtherB"u8)) using (ImUtf8.PushId("FurtherB"u8))
{
retB |= DrawFurther(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1); retB |= DrawFurther(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
} }
if (retA) if (retA)
@ -195,18 +228,21 @@ public partial class MtrlTab
/// <remarks> Padding styles do not seem to apply to this component. It is recommended to prepend two spaces. </remarks> /// <remarks> Padding styles do not seem to apply to this component. It is recommended to prepend two spaces. </remarks>
private static void DrawHeader(ReadOnlySpan<byte> label) private static void DrawHeader(ReadOnlySpan<byte> label)
{ {
var headerColor = ImGui.GetColorU32(ImGuiCol.Header); var headerColor = ImGui.GetColorU32(ImGuiCol.Header);
using var _ = ImRaii.PushColor(ImGuiCol.HeaderHovered, headerColor).Push(ImGuiCol.HeaderActive, headerColor); using var _ = ImRaii.PushColor(ImGuiCol.HeaderHovered, headerColor).Push(ImGuiCol.HeaderActive, headerColor);
ImUtf8.CollapsingHeader(label, ImGuiTreeNodeFlags.Leaf); ImUtf8.CollapsingHeader(label, ImGuiTreeNodeFlags.Leaf);
} }
private static bool DrawColors(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx) private static bool DrawColors(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{ {
var dyeOffset = ImGui.GetContentRegionAvail().X + ImGui.GetStyle().ItemSpacing.X - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight() * 2.0f; var dyeOffset = ImGui.GetContentRegionAvail().X
+ ImGui.GetStyle().ItemSpacing.X
- ImGui.GetStyle().ItemInnerSpacing.X
- ImGui.GetFrameHeight() * 2.0f;
var ret = false; var ret = false;
ref var row = ref table[rowIdx]; ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default; var dye = dyeTable?[rowIdx] ?? default;
ret |= CtColorPicker("Diffuse Color"u8, default, row.DiffuseColor, ret |= CtColorPicker("Diffuse Color"u8, default, row.DiffuseColor,
c => table[rowIdx].DiffuseColor = c); c => table[rowIdx].DiffuseColor = c);
@ -247,13 +283,17 @@ public partial class MtrlTab
private static bool DrawBlending(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx) private static bool DrawBlending(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{ {
var scalarSize = ColorTableScalarSize * UiHelpers.Scale; var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var dyeOffset = ImGui.GetContentRegionAvail().X + ImGui.GetStyle().ItemSpacing.X - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight() - scalarSize; var dyeOffset = ImGui.GetContentRegionAvail().X
+ ImGui.GetStyle().ItemSpacing.X
- ImGui.GetStyle().ItemInnerSpacing.X
- ImGui.GetFrameHeight()
- scalarSize;
var isRowB = (rowIdx & 1) != 0; var isRowB = (rowIdx & 1) != 0;
var ret = false; var ret = false;
ref var row = ref table[rowIdx]; ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default; var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf(isRowB ? "Field #19"u8 : "Anisotropy Degree"u8, default, row.Anisotropy, "%.2f"u8, 0.0f, HalfMaxValue, 0.1f, ret |= CtDragHalf(isRowB ? "Field #19"u8 : "Anisotropy Degree"u8, default, row.Anisotropy, "%.2f"u8, 0.0f, HalfMaxValue, 0.1f,
@ -261,11 +301,13 @@ public partial class MtrlTab
if (dyeTable != null) if (dyeTable != null)
{ {
ImGui.SameLine(dyeOffset); ImGui.SameLine(dyeOffset);
ret |= CtApplyStainCheckbox("##dyeAnisotropy"u8, isRowB ? "Apply Field #19 on Dye"u8 : "Apply Anisotropy Degree on Dye"u8, dye.Anisotropy, ret |= CtApplyStainCheckbox("##dyeAnisotropy"u8, isRowB ? "Apply Field #19 on Dye"u8 : "Apply Anisotropy Degree on Dye"u8,
dye.Anisotropy,
b => dyeTable[rowIdx].Anisotropy = b); b => dyeTable[rowIdx].Anisotropy = b);
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
CtDragHalf("##dyePreviewAnisotropy"u8, isRowB ? "Dye Preview for Field #19"u8 : "Dye Preview for Anisotropy Degree"u8, dyePack?.Anisotropy, "%.2f"u8); CtDragHalf("##dyePreviewAnisotropy"u8, isRowB ? "Dye Preview for Field #19"u8 : "Dye Preview for Anisotropy Degree"u8,
dyePack?.Anisotropy, "%.2f"u8);
} }
return ret; return ret;
@ -276,11 +318,11 @@ public partial class MtrlTab
var scalarSize = ColorTableScalarSize * UiHelpers.Scale; var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var itemSpacing = ImGui.GetStyle().ItemSpacing.X; var itemSpacing = ImGui.GetStyle().ItemSpacing.X;
var dyeOffset = ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight() - scalarSize - 64.0f; var dyeOffset = ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight() - scalarSize - 64.0f;
var subcolWidth = CalculateSubcolumnWidth(2); var subColWidth = CalculateSubColumnWidth(2);
var ret = false; var ret = false;
ref var row = ref table[rowIdx]; ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default; var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Shader ID"u8, default, row.ShaderId, "%d"u8, (ushort)0, (ushort)255, 0.25f, ret |= CtDragScalar("Shader ID"u8, default, row.ShaderId, "%d"u8, (ushort)0, (ushort)255, 0.25f,
@ -306,13 +348,15 @@ public partial class MtrlTab
ImGui.SetCursorScreenPos(ImGui.GetCursorScreenPos() with { Y = cursor.Y }); ImGui.SetCursorScreenPos(ImGui.GetCursorScreenPos() with { Y = cursor.Y });
ImGui.SetNextItemWidth(scalarSize + itemSpacing + 64.0f); ImGui.SetNextItemWidth(scalarSize + itemSpacing + 64.0f);
using var dis = ImRaii.Disabled(); using var dis = ImRaii.Disabled();
CtSphereMapIndexPicker("###SphereMapIndexDye"u8, "Dye Preview for Sphere Map"u8, dyePack?.SphereMapIndex ?? ushort.MaxValue, false, Nop); CtSphereMapIndexPicker("###SphereMapIndexDye"u8, "Dye Preview for Sphere Map"u8, dyePack?.SphereMapIndex ?? ushort.MaxValue, false,
Nop);
} }
ImGui.Dummy(new Vector2(64.0f, 0.0f)); ImGui.Dummy(new Vector2(64.0f, 0.0f));
ImGui.SameLine(); ImGui.SameLine();
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Sphere Map Intensity"u8, default, (float)row.SphereMapMask * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f, 1.0f, ret |= CtDragScalar("Sphere Map Intensity"u8, default, (float)row.SphereMapMask * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f,
HalfMaxValue * 100.0f, 1.0f,
v => table[rowIdx].SphereMapMask = (Half)(v * 0.01f)); v => table[rowIdx].SphereMapMask = (Half)(v * 0.01f));
if (dyeTable != null) if (dyeTable != null)
{ {
@ -326,10 +370,10 @@ public partial class MtrlTab
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
var leftLineHeight = 64.0f + ImGui.GetStyle().FramePadding.Y * 2.0f; var leftLineHeight = 64.0f + ImGui.GetStyle().FramePadding.Y * 2.0f;
var rightLineHeight = 3.0f * ImGui.GetFrameHeight() + 2.0f * ImGui.GetStyle().ItemSpacing.Y; var rightLineHeight = 3.0f * ImGui.GetFrameHeight() + 2.0f * ImGui.GetStyle().ItemSpacing.Y;
var lineHeight = Math.Max(leftLineHeight, rightLineHeight); var lineHeight = Math.Max(leftLineHeight, rightLineHeight);
var cursorPos = ImGui.GetCursorScreenPos(); var cursorPos = ImGui.GetCursorScreenPos();
ImGui.SetCursorScreenPos(cursorPos + new Vector2(0.0f, (lineHeight - leftLineHeight) * 0.5f)); ImGui.SetCursorScreenPos(cursorPos + new Vector2(0.0f, (lineHeight - leftLineHeight) * 0.5f));
ImGui.SetNextItemWidth(scalarSize + (itemSpacing + 64.0f) * 2.0f); ImGui.SetNextItemWidth(scalarSize + (itemSpacing + 64.0f) * 2.0f);
ret |= CtTileIndexPicker("###TileIndex"u8, default, row.TileIndex, false, ret |= CtTileIndexPicker("###TileIndex"u8, default, row.TileIndex, false,
@ -337,9 +381,10 @@ public partial class MtrlTab
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImUtf8.Text("Tile"u8); ImUtf8.Text("Tile"u8);
ImGui.SameLine(subcolWidth); ImGui.SameLine(subColWidth);
ImGui.SetCursorScreenPos(ImGui.GetCursorScreenPos() with { Y = cursorPos.Y + (lineHeight - rightLineHeight) * 0.5f, }); ImGui.SetCursorScreenPos(ImGui.GetCursorScreenPos() with { Y = cursorPos.Y + (lineHeight - rightLineHeight) * 0.5f });
using (var cld = ImUtf8.Child("###TileProperties"u8, new(ImGui.GetContentRegionAvail().X, float.Lerp(rightLineHeight, lineHeight, 0.5f)), false)) using (ImUtf8.Child("###TileProperties"u8,
new Vector2(ImGui.GetContentRegionAvail().X, float.Lerp(rightLineHeight, lineHeight, 0.5f))))
{ {
ImGui.Dummy(new Vector2(scalarSize, 0.0f)); ImGui.Dummy(new Vector2(scalarSize, 0.0f));
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
@ -350,7 +395,8 @@ public partial class MtrlTab
ret |= CtTileTransformMatrix(row.TileTransform, scalarSize, true, ret |= CtTileTransformMatrix(row.TileTransform, scalarSize, true,
m => table[rowIdx].TileTransform = m); m => table[rowIdx].TileTransform = m);
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImGui.SetCursorScreenPos(ImGui.GetCursorScreenPos() - new Vector2(0.0f, (ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y) * 0.5f)); ImGui.SetCursorScreenPos(ImGui.GetCursorScreenPos()
- new Vector2(0.0f, (ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y) * 0.5f));
ImUtf8.Text("Tile Transform"u8); ImUtf8.Text("Tile Transform"u8);
} }
@ -360,15 +406,20 @@ public partial class MtrlTab
private static bool DrawPbr(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx) private static bool DrawPbr(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{ {
var scalarSize = ColorTableScalarSize * UiHelpers.Scale; var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var subcolWidth = CalculateSubcolumnWidth(2) + ImGui.GetStyle().ItemSpacing.X; var subColWidth = CalculateSubColumnWidth(2) + ImGui.GetStyle().ItemSpacing.X;
var dyeOffset = subcolWidth - ImGui.GetStyle().ItemSpacing.X * 2.0f - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight() - scalarSize; var dyeOffset = subColWidth
- ImGui.GetStyle().ItemSpacing.X * 2.0f
- ImGui.GetStyle().ItemInnerSpacing.X
- ImGui.GetFrameHeight()
- scalarSize;
var ret = false; var ret = false;
ref var row = ref table[rowIdx]; ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default; var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Roughness"u8, default, (float)row.Roughness * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f, 1.0f, ret |= CtDragScalar("Roughness"u8, default, (float)row.Roughness * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f,
1.0f,
v => table[rowIdx].Roughness = (Half)(v * 0.01f)); v => table[rowIdx].Roughness = (Half)(v * 0.01f));
if (dyeTable != null) if (dyeTable != null)
{ {
@ -380,13 +431,14 @@ public partial class MtrlTab
CtDragScalar("##dyePreviewRoughness"u8, "Dye Preview for Roughness"u8, (float?)dyePack?.Roughness * 100.0f, "%.0f%%"u8); CtDragScalar("##dyePreviewRoughness"u8, "Dye Preview for Roughness"u8, (float?)dyePack?.Roughness * 100.0f, "%.0f%%"u8);
} }
ImGui.SameLine(subcolWidth); ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Metalness"u8, default, (float)row.Metalness * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f, 1.0f, ret |= CtDragScalar("Metalness"u8, default, (float)row.Metalness * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f,
1.0f,
v => table[rowIdx].Metalness = (Half)(v * 0.01f)); v => table[rowIdx].Metalness = (Half)(v * 0.01f));
if (dyeTable != null) if (dyeTable != null)
{ {
ImGui.SameLine(subcolWidth + dyeOffset); ImGui.SameLine(subColWidth + dyeOffset);
ret |= CtApplyStainCheckbox("##dyeMetalness"u8, "Apply Metalness on Dye"u8, dye.Metalness, ret |= CtApplyStainCheckbox("##dyeMetalness"u8, "Apply Metalness on Dye"u8, dye.Metalness,
b => dyeTable[rowIdx].Metalness = b); b => dyeTable[rowIdx].Metalness = b);
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
@ -400,12 +452,16 @@ public partial class MtrlTab
private static bool DrawSheen(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx) private static bool DrawSheen(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{ {
var scalarSize = ColorTableScalarSize * UiHelpers.Scale; var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var subcolWidth = CalculateSubcolumnWidth(2) + ImGui.GetStyle().ItemSpacing.X; var subColWidth = CalculateSubColumnWidth(2) + ImGui.GetStyle().ItemSpacing.X;
var dyeOffset = subcolWidth - ImGui.GetStyle().ItemSpacing.X * 2.0f - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight() - scalarSize; var dyeOffset = subColWidth
- ImGui.GetStyle().ItemSpacing.X * 2.0f
- ImGui.GetStyle().ItemInnerSpacing.X
- ImGui.GetFrameHeight()
- scalarSize;
var ret = false; var ret = false;
ref var row = ref table[rowIdx]; ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default; var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Sheen"u8, default, (float)row.SheenRate * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f, 1.0f, ret |= CtDragScalar("Sheen"u8, default, (float)row.SheenRate * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f, 1.0f,
@ -420,13 +476,14 @@ public partial class MtrlTab
CtDragScalar("##dyePreviewSheenRate"u8, "Dye Preview for Sheen"u8, (float?)dyePack?.SheenRate * 100.0f, "%.0f%%"u8); CtDragScalar("##dyePreviewSheenRate"u8, "Dye Preview for Sheen"u8, (float?)dyePack?.SheenRate * 100.0f, "%.0f%%"u8);
} }
ImGui.SameLine(subcolWidth); ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Sheen Tint"u8, default, (float)row.SheenTintRate * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f, HalfMaxValue * 100.0f, 1.0f, ret |= CtDragScalar("Sheen Tint"u8, default, (float)row.SheenTintRate * 100.0f, "%.0f%%"u8, HalfMinValue * 100.0f,
HalfMaxValue * 100.0f, 1.0f,
v => table[rowIdx].SheenTintRate = (Half)(v * 0.01f)); v => table[rowIdx].SheenTintRate = (Half)(v * 0.01f));
if (dyeTable != null) if (dyeTable != null)
{ {
ImGui.SameLine(subcolWidth + dyeOffset); ImGui.SameLine(subColWidth + dyeOffset);
ret |= CtApplyStainCheckbox("##dyeSheenTintRate"u8, "Apply Sheen Tint on Dye"u8, dye.SheenTintRate, ret |= CtApplyStainCheckbox("##dyeSheenTintRate"u8, "Apply Sheen Tint on Dye"u8, dye.SheenTintRate,
b => dyeTable[rowIdx].SheenTintRate = b); b => dyeTable[rowIdx].SheenTintRate = b);
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
@ -435,7 +492,8 @@ public partial class MtrlTab
} }
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Sheen Roughness"u8, default, 100.0f / (float)row.SheenAperture, "%.0f%%"u8, 100.0f / HalfMaxValue, 100.0f / HalfEpsilon, 1.0f, ret |= CtDragScalar("Sheen Roughness"u8, default, 100.0f / (float)row.SheenAperture, "%.0f%%"u8, 100.0f / HalfMaxValue,
100.0f / HalfEpsilon, 1.0f,
v => table[rowIdx].SheenAperture = (Half)(100.0f / v)); v => table[rowIdx].SheenAperture = (Half)(100.0f / v));
if (dyeTable != null) if (dyeTable != null)
{ {
@ -444,7 +502,8 @@ public partial class MtrlTab
b => dyeTable[rowIdx].SheenAperture = b); b => dyeTable[rowIdx].SheenAperture = b);
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
CtDragScalar("##dyePreviewSheenRoughness"u8, "Dye Preview for Sheen Roughness"u8, 100.0f / (float?)dyePack?.SheenAperture, "%.0f%%"u8); CtDragScalar("##dyePreviewSheenRoughness"u8, "Dye Preview for Sheen Roughness"u8, 100.0f / (float?)dyePack?.SheenAperture,
"%.0f%%"u8);
} }
return ret; return ret;
@ -453,12 +512,16 @@ public partial class MtrlTab
private static bool DrawFurther(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx) private static bool DrawFurther(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{ {
var scalarSize = ColorTableScalarSize * UiHelpers.Scale; var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var subcolWidth = CalculateSubcolumnWidth(2) + ImGui.GetStyle().ItemSpacing.X; var subColWidth = CalculateSubColumnWidth(2) + ImGui.GetStyle().ItemSpacing.X;
var dyeOffset = subcolWidth - ImGui.GetStyle().ItemSpacing.X * 2.0f - ImGui.GetStyle().ItemInnerSpacing.X - ImGui.GetFrameHeight() - scalarSize; var dyeOffset = subColWidth
- ImGui.GetStyle().ItemSpacing.X * 2.0f
- ImGui.GetStyle().ItemInnerSpacing.X
- ImGui.GetFrameHeight()
- scalarSize;
var ret = false; var ret = false;
ref var row = ref table[rowIdx]; ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default; var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf("Field #11"u8, default, row.Scalar11, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f, ret |= CtDragHalf("Field #11"u8, default, row.Scalar11, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
@ -479,7 +542,7 @@ public partial class MtrlTab
ret |= CtDragHalf("Field #3"u8, default, row.Scalar3, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f, ret |= CtDragHalf("Field #3"u8, default, row.Scalar3, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
v => table[rowIdx].Scalar3 = v); v => table[rowIdx].Scalar3 = v);
ImGui.SameLine(subcolWidth); ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf("Field #7"u8, default, row.Scalar7, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f, ret |= CtDragHalf("Field #7"u8, default, row.Scalar7, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
v => table[rowIdx].Scalar7 = v); v => table[rowIdx].Scalar7 = v);
@ -488,7 +551,7 @@ public partial class MtrlTab
ret |= CtDragHalf("Field #15"u8, default, row.Scalar15, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f, ret |= CtDragHalf("Field #15"u8, default, row.Scalar15, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
v => table[rowIdx].Scalar15 = v); v => table[rowIdx].Scalar15 = v);
ImGui.SameLine(subcolWidth); ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf("Field #17"u8, default, row.Scalar17, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f, ret |= CtDragHalf("Field #17"u8, default, row.Scalar17, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
v => table[rowIdx].Scalar17 = v); v => table[rowIdx].Scalar17 = v);
@ -497,7 +560,7 @@ public partial class MtrlTab
ret |= CtDragHalf("Field #20"u8, default, row.Scalar20, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f, ret |= CtDragHalf("Field #20"u8, default, row.Scalar20, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
v => table[rowIdx].Scalar20 = v); v => table[rowIdx].Scalar20 = v);
ImGui.SameLine(subcolWidth); ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf("Field #22"u8, default, row.Scalar22, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f, ret |= CtDragHalf("Field #22"u8, default, row.Scalar22, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
v => table[rowIdx].Scalar22 = v); v => table[rowIdx].Scalar22 = v);
@ -513,33 +576,32 @@ public partial class MtrlTab
{ {
var scalarSize = ColorTableScalarSize * UiHelpers.Scale; var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var applyButtonWidth = ImUtf8.CalcTextSize("Apply Preview Dye"u8).X + ImGui.GetStyle().FramePadding.X * 2.0f; var applyButtonWidth = ImUtf8.CalcTextSize("Apply Preview Dye"u8).X + ImGui.GetStyle().FramePadding.X * 2.0f;
var subcolWidth = CalculateSubcolumnWidth(2, applyButtonWidth); var subColWidth = CalculateSubColumnWidth(2, applyButtonWidth);
var ret = false; var ret = false;
ref var dye = ref dyeTable[rowIdx]; ref var dye = ref dyeTable[rowIdx];
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragScalar("Dye Channel"u8, default, dye.Channel + 1, "%d"u8, 1, StainService.ChannelCount, 0.1f, ret |= CtDragScalar("Dye Channel"u8, default, dye.Channel + 1, "%d"u8, 1, StainService.ChannelCount, 0.1f,
value => dyeTable[rowIdx].Channel = (byte)(Math.Clamp(value, 1, StainService.ChannelCount) - 1)); value => dyeTable[rowIdx].Channel = (byte)(Math.Clamp(value, 1, StainService.ChannelCount) - 1));
ImGui.SameLine(subcolWidth); ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize); ImGui.SetNextItemWidth(scalarSize);
if (_stainService.GudTemplateCombo.Draw("##dyeTemplate", dye.Template.ToString(), string.Empty, if (_stainService.GudTemplateCombo.Draw("##dyeTemplate", dye.Template.ToString(), string.Empty,
scalarSize + ImGui.GetStyle().ScrollbarSize / 2, ImGui.GetTextLineHeightWithSpacing(), ImGuiComboFlags.NoArrowButton)) scalarSize + ImGui.GetStyle().ScrollbarSize / 2, ImGui.GetTextLineHeightWithSpacing(), ImGuiComboFlags.NoArrowButton))
{ {
dye.Template = _stainService.LegacyTemplateCombo.CurrentSelection; dye.Template = _stainService.LegacyTemplateCombo.CurrentSelection;
ret = true; ret = true;
} }
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImUtf8.Text("Dye Template"u8); ImUtf8.Text("Dye Template"u8);
ImGui.SameLine(ImGui.GetContentRegionAvail().X - applyButtonWidth + ImGui.GetStyle().ItemSpacing.X); ImGui.SameLine(ImGui.GetContentRegionAvail().X - applyButtonWidth + ImGui.GetStyle().ItemSpacing.X);
using var dis = ImRaii.Disabled(!dyePack.HasValue); using var dis = ImRaii.Disabled(!dyePack.HasValue);
if (ImUtf8.Button("Apply Preview Dye"u8)) if (ImUtf8.Button("Apply Preview Dye"u8))
{
ret |= Mtrl.ApplyDyeToRow(_stainService.GudStmFile, [ ret |= Mtrl.ApplyDyeToRow(_stainService.GudStmFile, [
_stainService.StainCombo1.CurrentSelection.Key, _stainService.StainCombo1.CurrentSelection.Key,
_stainService.StainCombo2.CurrentSelection.Key, _stainService.StainCombo2.CurrentSelection.Key,
], rowIdx); ], rowIdx);
}
return ret; return ret;
} }
@ -554,9 +616,9 @@ public partial class MtrlTab
ImGui.TextUnformatted(text); ImGui.TextUnformatted(text);
} }
private static float CalculateSubcolumnWidth(int numSubcolumns, float reservedSpace = 0.0f) private static float CalculateSubColumnWidth(int numSubColumns, float reservedSpace = 0.0f)
{ {
var itemSpacing = ImGui.GetStyle().ItemSpacing.X; var itemSpacing = ImGui.GetStyle().ItemSpacing.X;
return (ImGui.GetContentRegionAvail().X - reservedSpace - itemSpacing * (numSubcolumns - 1)) / numSubcolumns + itemSpacing; return (ImGui.GetContentRegionAvail().X - reservedSpace - itemSpacing * (numSubColumns - 1)) / numSubColumns + itemSpacing;
} }
} }

View file

@ -12,9 +12,9 @@ namespace Penumbra.UI.AdvancedWindow.Materials;
public partial class MtrlTab public partial class MtrlTab
{ {
private static readonly float HalfMinValue = (float)Half.MinValue; private static readonly float HalfMinValue = (float)Half.MinValue;
private static readonly float HalfMaxValue = (float)Half.MaxValue; private static readonly float HalfMaxValue = (float)Half.MaxValue;
private static readonly float HalfEpsilon = (float)Half.Epsilon; private static readonly float HalfEpsilon = (float)Half.Epsilon;
private static readonly FontAwesomeCheckbox ApplyStainCheckbox = new(FontAwesomeIcon.FillDrip); private static readonly FontAwesomeCheckbox ApplyStainCheckbox = new(FontAwesomeIcon.FillDrip);
@ -22,11 +22,11 @@ public partial class MtrlTab
private bool DrawColorTableSection(bool disabled) private bool DrawColorTableSection(bool disabled)
{ {
if ((!ShpkLoading && !SamplerIds.Contains(ShpkFile.TableSamplerId)) || Mtrl.Table == null) if (!_shpkLoading && !SamplerIds.Contains(ShpkFile.TableSamplerId) || Mtrl.Table == null)
return false; return false;
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
if (!ImGui.CollapsingHeader("Color Table", ImGuiTreeNodeFlags.DefaultOpen)) if (!ImUtf8.CollapsingHeader("Color Table"u8, ImGuiTreeNodeFlags.DefaultOpen))
return false; return false;
ColorTableCopyAllClipboardButton(); ColorTableCopyAllClipboardButton();
@ -35,7 +35,7 @@ public partial class MtrlTab
if (!disabled) if (!disabled)
{ {
ImGui.SameLine(); ImGui.SameLine();
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0)); ImUtf8.IconDummy();
ImGui.SameLine(); ImGui.SameLine();
ret |= ColorTableDyeableCheckbox(); ret |= ColorTableDyeableCheckbox();
} }
@ -43,17 +43,18 @@ public partial class MtrlTab
if (Mtrl.DyeTable != null) if (Mtrl.DyeTable != null)
{ {
ImGui.SameLine(); ImGui.SameLine();
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0)); ImUtf8.IconDummy();
ImGui.SameLine(); ImGui.SameLine();
ret |= DrawPreviewDye(disabled); ret |= DrawPreviewDye(disabled);
} }
ret |= Mtrl.Table switch ret |= Mtrl.Table switch
{ {
LegacyColorTable legacyTable => DrawLegacyColorTable(legacyTable, Mtrl.DyeTable as LegacyColorDyeTable, disabled), LegacyColorTable legacyTable => DrawLegacyColorTable(legacyTable, Mtrl.DyeTable as LegacyColorDyeTable, disabled),
ColorTable table when Mtrl.ShaderPackage.Name is "characterlegacy.shpk" => DrawLegacyColorTable(table, Mtrl.DyeTable as ColorDyeTable, disabled), ColorTable table when Mtrl.ShaderPackage.Name is "characterlegacy.shpk" => DrawLegacyColorTable(table,
ColorTable table => DrawColorTable(table, Mtrl.DyeTable as ColorDyeTable, disabled), Mtrl.DyeTable as ColorDyeTable, disabled),
_ => false, ColorTable table => DrawColorTable(table, Mtrl.DyeTable as ColorDyeTable, disabled),
_ => false,
}; };
return ret; return ret;
@ -64,7 +65,7 @@ public partial class MtrlTab
if (Mtrl.Table == null) if (Mtrl.Table == null)
return; return;
if (!ImGui.Button("Export All Rows to Clipboard", ImGuiHelpers.ScaledVector2(200, 0))) if (!ImUtf8.Button("Export All Rows to Clipboard"u8, ImGuiHelpers.ScaledVector2(200, 0)))
return; return;
try try
@ -178,16 +179,18 @@ public partial class MtrlTab
private bool ColorTableDyeableCheckbox() private bool ColorTableDyeableCheckbox()
{ {
var dyeable = Mtrl.DyeTable != null; var dyeable = Mtrl.DyeTable != null;
var ret = ImGui.Checkbox("Dyeable", ref dyeable); var ret = ImUtf8.Checkbox("Dyeable"u8, ref dyeable);
if (ret) if (ret)
{ {
Mtrl.DyeTable = dyeable ? Mtrl.Table switch Mtrl.DyeTable = dyeable
{ ? Mtrl.Table switch
ColorTable => new ColorDyeTable(), {
LegacyColorTable => new LegacyColorDyeTable(), ColorTable => new ColorDyeTable(),
_ => null, LegacyColorTable => new LegacyColorDyeTable(),
} : null; _ => null,
}
: null;
UpdateColorTablePreview(); UpdateColorTablePreview();
} }
@ -227,24 +230,27 @@ public partial class MtrlTab
private void ColorTableHighlightButton(int pairIdx, bool disabled) private void ColorTableHighlightButton(int pairIdx, bool disabled)
{ {
ImUtf8.IconButton(FontAwesomeIcon.Crosshairs, "Highlight this pair of rows on your character, if possible.\n\nHighlight colors can be configured in Penumbra's settings."u8, ImUtf8.IconButton(FontAwesomeIcon.Crosshairs,
ImGui.GetFrameHeight() * Vector2.One, disabled || ColorTablePreviewers.Count == 0); "Highlight this pair of rows on your character, if possible.\n\nHighlight colors can be configured in Penumbra's settings."u8,
ImGui.GetFrameHeight() * Vector2.One, disabled || _colorTablePreviewers.Count == 0);
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
HighlightColorTablePair(pairIdx); HighlightColorTablePair(pairIdx);
else if (HighlightedColorTablePair == pairIdx) else if (_highlightedColorTablePair == pairIdx)
CancelColorTableHighlight(); CancelColorTableHighlight();
} }
private static void CtBlendRect(Vector2 rcMin, Vector2 rcMax, uint topColor, uint bottomColor) private static void CtBlendRect(Vector2 rcMin, Vector2 rcMax, uint topColor, uint bottomColor)
{ {
var style = ImGui.GetStyle(); var style = ImGui.GetStyle();
var frameRounding = style.FrameRounding; var frameRounding = style.FrameRounding;
var frameThickness = style.FrameBorderSize; var frameThickness = style.FrameBorderSize;
var borderColor = ImGui.GetColorU32(ImGuiCol.Border); var borderColor = ImGui.GetColorU32(ImGuiCol.Border);
var drawList = ImGui.GetWindowDrawList(); var drawList = ImGui.GetWindowDrawList();
if (topColor == bottomColor) if (topColor == bottomColor)
{
drawList.AddRectFilled(rcMin, rcMax, topColor, frameRounding, ImDrawFlags.RoundCornersDefault); drawList.AddRectFilled(rcMin, rcMax, topColor, frameRounding, ImDrawFlags.RoundCornersDefault);
}
else else
{ {
drawList.AddRectFilled( drawList.AddRectFilled(
@ -258,10 +264,12 @@ public partial class MtrlTab
rcMin with { Y = float.Lerp(rcMin.Y, rcMax.Y, 2.0f / 3) }, rcMax, rcMin with { Y = float.Lerp(rcMin.Y, rcMax.Y, 2.0f / 3) }, rcMax,
bottomColor, frameRounding, ImDrawFlags.RoundCornersBottomLeft | ImDrawFlags.RoundCornersBottomRight); bottomColor, frameRounding, ImDrawFlags.RoundCornersBottomLeft | ImDrawFlags.RoundCornersBottomRight);
} }
drawList.AddRect(rcMin, rcMax, borderColor, frameRounding, ImDrawFlags.RoundCornersDefault, frameThickness); drawList.AddRect(rcMin, rcMax, borderColor, frameRounding, ImDrawFlags.RoundCornersDefault, frameThickness);
} }
private static bool CtColorPicker(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, HalfColor current, Action<HalfColor> setter, ReadOnlySpan<byte> letter = default) private static bool CtColorPicker(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, HalfColor current, Action<HalfColor> setter,
ReadOnlySpan<byte> letter = default)
{ {
var ret = false; var ret = false;
var inputSqrt = PseudoSqrtRgb((Vector3)current); var inputSqrt = PseudoSqrtRgb((Vector3)current);
@ -291,10 +299,13 @@ public partial class MtrlTab
return ret; return ret;
} }
private static void CtColorPicker(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, HalfColor? current, ReadOnlySpan<byte> letter = default) private static void CtColorPicker(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, HalfColor? current,
ReadOnlySpan<byte> letter = default)
{ {
if (current.HasValue) if (current.HasValue)
{
CtColorPicker(label, description, current.Value, Nop, letter); CtColorPicker(label, description, current.Value, Nop, letter);
}
else else
{ {
var tmp = Vector4.Zero; var tmp = Vector4.Zero;
@ -308,8 +319,8 @@ public partial class MtrlTab
if (letter.Length > 0 && ImGui.IsItemVisible()) if (letter.Length > 0 && ImGui.IsItemVisible())
{ {
var textSize = ImUtf8.CalcTextSize(letter); var textSize = ImUtf8.CalcTextSize(letter);
var center = ImGui.GetItemRectMin() + (ImGui.GetItemRectSize() - textSize) / 2; var center = ImGui.GetItemRectMin() + (ImGui.GetItemRectSize() - textSize) / 2;
ImGui.GetWindowDrawList().AddText(letter, center, 0x80000000u); ImGui.GetWindowDrawList().AddText(letter, center, 0x80000000u);
} }
@ -319,7 +330,7 @@ public partial class MtrlTab
private static bool CtApplyStainCheckbox(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, bool current, Action<bool> setter) private static bool CtApplyStainCheckbox(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, bool current, Action<bool> setter)
{ {
var tmp = current; var tmp = current;
var result = ApplyStainCheckbox.Draw(label, ref tmp); var result = ApplyStainCheckbox.Draw(label, ref tmp);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description); ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == current) if (!result || tmp == current)
@ -329,68 +340,79 @@ public partial class MtrlTab
return true; return true;
} }
private static bool CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, Half value, ReadOnlySpan<byte> format, float min, float max, float speed, Action<Half> setter) private static bool CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, Half value, ReadOnlySpan<byte> format, float min,
float max, float speed, Action<Half> setter)
{ {
var tmp = (float)value; var tmp = (float)value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed); var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description); ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result) if (!result)
return false; return false;
var newValue = (Half)tmp; var newValue = (Half)tmp;
if (newValue == value) if (newValue == value)
return false; return false;
setter(newValue); setter(newValue);
return true; return true;
} }
private static bool CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, ref Half value, ReadOnlySpan<byte> format, float min, float max, float speed) private static bool CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, ref Half value, ReadOnlySpan<byte> format,
float min, float max, float speed)
{ {
var tmp = (float)value; var tmp = (float)value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed); var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description); ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result) if (!result)
return false; return false;
var newValue = (Half)tmp; var newValue = (Half)tmp;
if (newValue == value) if (newValue == value)
return false; return false;
value = newValue; value = newValue;
return true; return true;
} }
private static void CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, Half? value, ReadOnlySpan<byte> format) private static void CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, Half? value, ReadOnlySpan<byte> format)
{ {
using var _ = ImRaii.Disabled(); using var _ = ImRaii.Disabled();
var valueOrDefault = value ?? Half.Zero; var valueOrDefault = value ?? Half.Zero;
var floatValue = (float)valueOrDefault; var floatValue = (float)valueOrDefault;
CtDragHalf(label, description, valueOrDefault, value.HasValue ? format : "-"u8, floatValue, floatValue, 0.0f, Nop); CtDragHalf(label, description, valueOrDefault, value.HasValue ? format : "-"u8, floatValue, floatValue, 0.0f, Nop);
} }
private static bool CtDragScalar<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, T value, ReadOnlySpan<byte> format, T min, T max, float speed, Action<T> setter) where T : unmanaged, INumber<T> private static bool CtDragScalar<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, T value, ReadOnlySpan<byte> format, T min,
T max, float speed, Action<T> setter) where T : unmanaged, INumber<T>
{ {
var tmp = value; var tmp = value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed); var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description); ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == value) if (!result || tmp == value)
return false; return false;
setter(tmp); setter(tmp);
return true; return true;
} }
private static bool CtDragScalar<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, ref T value, ReadOnlySpan<byte> format, T min, T max, float speed) where T : unmanaged, INumber<T> private static bool CtDragScalar<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, ref T value, ReadOnlySpan<byte> format, T min,
T max, float speed) where T : unmanaged, INumber<T>
{ {
var tmp = value; var tmp = value;
var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed); var result = ImUtf8.DragScalar(label, ref tmp, format, min, max, speed);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description); ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == value) if (!result || tmp == value)
return false; return false;
value = tmp; value = tmp;
return true; return true;
} }
private static void CtDragScalar<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, T? value, ReadOnlySpan<byte> format) where T : unmanaged, INumber<T> private static void CtDragScalar<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, T? value, ReadOnlySpan<byte> format)
where T : unmanaged, INumber<T>
{ {
using var _ = ImRaii.Disabled(); using var _ = ImRaii.Disabled();
var valueOrDefault = value ?? T.Zero; var valueOrDefault = value ?? T.Zero;
CtDragScalar(label, description, valueOrDefault, value.HasValue ? format : "-"u8, valueOrDefault, valueOrDefault, 0.0f, Nop); CtDragScalar(label, description, valueOrDefault, value.HasValue ? format : "-"u8, valueOrDefault, valueOrDefault, 0.0f, Nop);
} }
@ -398,14 +420,17 @@ public partial class MtrlTab
{ {
if (!_materialTemplatePickers.DrawTileIndexPicker(label, description, ref value, compact)) if (!_materialTemplatePickers.DrawTileIndexPicker(label, description, ref value, compact))
return false; return false;
setter(value); setter(value);
return true; return true;
} }
private bool CtSphereMapIndexPicker(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, ushort value, bool compact, Action<ushort> setter) private bool CtSphereMapIndexPicker(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, ushort value, bool compact,
Action<ushort> setter)
{ {
if (!_materialTemplatePickers.DrawSphereMapIndexPicker(label, description, ref value, compact)) if (!_materialTemplatePickers.DrawSphereMapIndexPicker(label, description, ref value, compact))
return false; return false;
setter(value); setter(value);
return true; return true;
} }
@ -430,33 +455,34 @@ public partial class MtrlTab
ret |= CtDragHalf("##TileTransformVU"u8, "Tile Skew V"u8, ref tmp.VU, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f); ret |= CtDragHalf("##TileTransformVU"u8, "Tile Skew V"u8, ref tmp.VU, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
if (!ret || tmp == value) if (!ret || tmp == value)
return false; return false;
setter(tmp); setter(tmp);
} }
else else
{ {
value.Decompose(out var scale, out var rotation, out var shear); value.Decompose(out var scale, out var rotation, out var shear);
rotation *= 180.0f / MathF.PI; rotation *= 180.0f / MathF.PI;
shear *= 180.0f / MathF.PI; shear *= 180.0f / MathF.PI;
ImGui.SetNextItemWidth(floatSize); ImGui.SetNextItemWidth(floatSize);
var scaleXChanged = CtDragScalar("##TileScaleU"u8, "Tile Scale U"u8, ref scale.X, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f); var scaleXChanged = CtDragScalar("##TileScaleU"u8, "Tile Scale U"u8, ref scale.X, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
var activated = ImGui.IsItemActivated(); var activated = ImGui.IsItemActivated();
var deactivated = ImGui.IsItemDeactivated(); var deactivated = ImGui.IsItemDeactivated();
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(floatSize); ImGui.SetNextItemWidth(floatSize);
var scaleYChanged = CtDragScalar("##TileScaleV"u8, "Tile Scale V"u8, ref scale.Y, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f); var scaleYChanged = CtDragScalar("##TileScaleV"u8, "Tile Scale V"u8, ref scale.Y, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
activated |= ImGui.IsItemActivated(); activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated(); deactivated |= ImGui.IsItemDeactivated();
if (!twoRowLayout) if (!twoRowLayout)
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(floatSize); ImGui.SetNextItemWidth(floatSize);
var rotationChanged = CtDragScalar("##TileRotation"u8, "Tile Rotation"u8, ref rotation, "%.0f°"u8, -180.0f, 180.0f, 1.0f); var rotationChanged = CtDragScalar("##TileRotation"u8, "Tile Rotation"u8, ref rotation, "%.0f°"u8, -180.0f, 180.0f, 1.0f);
activated |= ImGui.IsItemActivated(); activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated(); deactivated |= ImGui.IsItemDeactivated();
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(floatSize); ImGui.SetNextItemWidth(floatSize);
var shearChanged = CtDragScalar("##TileShear"u8, "Tile Shear"u8, ref shear, "%.0f°"u8, -90.0f, 90.0f, 1.0f); var shearChanged = CtDragScalar("##TileShear"u8, "Tile Shear"u8, ref shear, "%.0f°"u8, -90.0f, 90.0f, 1.0f);
activated |= ImGui.IsItemActivated(); activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated(); deactivated |= ImGui.IsItemDeactivated();
if (deactivated) if (deactivated)
_pinnedTileTransform = null; _pinnedTileTransform = null;
else if (activated) else if (activated)
@ -464,6 +490,7 @@ public partial class MtrlTab
ret = scaleXChanged | scaleYChanged | rotationChanged | shearChanged; ret = scaleXChanged | scaleYChanged | rotationChanged | shearChanged;
if (!ret) if (!ret)
return false; return false;
if (_pinnedTileTransform.HasValue) if (_pinnedTileTransform.HasValue)
{ {
var (pinScale, pinRotation, pinShear) = _pinnedTileTransform.Value; var (pinScale, pinRotation, pinShear) = _pinnedTileTransform.Value;
@ -476,11 +503,14 @@ public partial class MtrlTab
if (!shearChanged) if (!shearChanged)
shear = pinShear; shear = pinShear;
} }
var newValue = HalfMatrix2x2.Compose(scale, rotation * MathF.PI / 180.0f, shear * MathF.PI / 180.0f); var newValue = HalfMatrix2x2.Compose(scale, rotation * MathF.PI / 180.0f, shear * MathF.PI / 180.0f);
if (newValue == value) if (newValue == value)
return false; return false;
setter(newValue); setter(newValue);
} }
return true; return true;
} }

View file

@ -35,7 +35,7 @@ public partial class MtrlTab
Constants.Clear(); Constants.Clear();
string mpPrefix; string mpPrefix;
if (AssociatedShpk == null) if (_associatedShpk == null)
{ {
mpPrefix = MaterialParamsConstantName.Value!; mpPrefix = MaterialParamsConstantName.Value!;
var fcGroup = FindOrAddGroup(Constants, "Further Constants"); var fcGroup = FindOrAddGroup(Constants, "Further Constants");
@ -51,12 +51,12 @@ public partial class MtrlTab
} }
else else
{ {
mpPrefix = AssociatedShpk.GetConstantById(MaterialParamsConstantId)?.Name ?? MaterialParamsConstantName.Value!; mpPrefix = _associatedShpk.GetConstantById(MaterialParamsConstantId)?.Name ?? MaterialParamsConstantName.Value!;
var autoNameMaxLength = Math.Max(Names.LongestKnownNameLength, mpPrefix.Length + 8); var autoNameMaxLength = Math.Max(Names.LongestKnownNameLength, mpPrefix.Length + 8);
foreach (var shpkConstant in AssociatedShpk.MaterialParams) foreach (var shpkConstant in _associatedShpk.MaterialParams)
{ {
var name = Names.KnownNames.TryResolve(shpkConstant.Id); var name = Names.KnownNames.TryResolve(shpkConstant.Id);
var constant = Mtrl.GetOrAddConstant(shpkConstant.Id, AssociatedShpk, out var constantIndex); var constant = Mtrl.GetOrAddConstant(shpkConstant.Id, _associatedShpk, out var constantIndex);
var values = Mtrl.GetConstantValue<byte>(constant); var values = Mtrl.GetConstantValue<byte>(constant);
var handledElements = new IndexSet(values.Length, false); var handledElements = new IndexSet(values.Length, false);
@ -64,8 +64,8 @@ public partial class MtrlTab
if (dkData != null) if (dkData != null)
foreach (var dkConstant in dkData) foreach (var dkConstant in dkData)
{ {
var offset = (int)dkConstant.EffectiveByteOffset; var offset = (int)dkConstant.EffectiveByteOffset;
var length = values.Length - offset; var length = values.Length - offset;
var constantSize = dkConstant.EffectiveByteSize; var constantSize = dkConstant.EffectiveByteSize;
if (constantSize.HasValue) if (constantSize.HasValue)
length = Math.Min(length, (int)constantSize.Value); length = Math.Min(length, (int)constantSize.Value);
@ -86,7 +86,6 @@ public partial class MtrlTab
foreach (var (start, end) in handledElements.Ranges(complement: true)) foreach (var (start, end) in handledElements.Ranges(complement: true))
{ {
if (start == 0 && end == values.Length && end - start <= 16) if (start == 0 && end == values.Length && end - start <= 16)
{
if (name.Value != null) if (name.Value != null)
{ {
fcGroup.Add(( fcGroup.Add((
@ -94,7 +93,6 @@ public partial class MtrlTab
constantIndex, 0..values.Length, string.Empty, true, DefaultConstantEditorFor(name))); constantIndex, 0..values.Length, string.Empty, true, DefaultConstantEditorFor(name)));
continue; continue;
} }
}
if ((shpkConstant.ByteOffset & 0x3) == 0 && (shpkConstant.ByteSize & 0x3) == 0) if ((shpkConstant.ByteOffset & 0x3) == 0 && (shpkConstant.ByteSize & 0x3) == 0)
{ {
@ -105,7 +103,8 @@ public partial class MtrlTab
var rangeEnd = Math.Min(i + 16, end); var rangeEnd = Math.Min(i + 16, end);
if (rangeEnd > rangeStart) if (rangeEnd > rangeStart)
{ {
var autoName = $"{mpPrefix}[{j,2:D}]{VectorSwizzle(((offset + rangeStart) & 0xF) >> 2, ((offset + rangeEnd - 1) & 0xF) >> 2)}"; var autoName =
$"{mpPrefix}[{j,2:D}]{VectorSwizzle(((offset + rangeStart) & 0xF) >> 2, ((offset + rangeEnd - 1) & 0xF) >> 2)}";
fcGroup.Add(( fcGroup.Add((
$"{autoName.PadRight(autoNameMaxLength)} (0x{shpkConstant.Id:X8})", $"{autoName.PadRight(autoNameMaxLength)} (0x{shpkConstant.Id:X8})",
constantIndex, rangeStart..rangeEnd, string.Empty, true, DefaultConstantEditorFor(name))); constantIndex, rangeStart..rangeEnd, string.Empty, true, DefaultConstantEditorFor(name)));
@ -116,7 +115,8 @@ public partial class MtrlTab
{ {
for (var i = start; i < end; i += 16) for (var i = start; i < end; i += 16)
{ {
fcGroup.Add(($"{"???".PadRight(autoNameMaxLength)} (0x{shpkConstant.Id:X8})", constantIndex, i..Math.Min(i + 16, end), string.Empty, true, fcGroup.Add(($"{"???".PadRight(autoNameMaxLength)} (0x{shpkConstant.Id:X8})", constantIndex,
i..Math.Min(i + 16, end), string.Empty, true,
DefaultConstantEditorFor(name))); DefaultConstantEditorFor(name)));
} }
} }
@ -178,15 +178,16 @@ public partial class MtrlTab
ret = true; ret = true;
SetMaterialParameter(constant.Id, slice.Start, buffer[slice]); SetMaterialParameter(constant.Id, slice.Start, buffer[slice]);
} }
var shpkConstant = AssociatedShpk?.GetMaterialParamById(constant.Id);
var defaultConstantValue = shpkConstant.HasValue ? AssociatedShpk!.GetMaterialParamDefault<byte>(shpkConstant.Value) : []; var shpkConstant = _associatedShpk?.GetMaterialParamById(constant.Id);
var defaultConstantValue = shpkConstant.HasValue ? _associatedShpk!.GetMaterialParamDefault<byte>(shpkConstant.Value) : [];
var defaultValue = IsValid(slice, defaultConstantValue.Length) ? defaultConstantValue[slice] : []; var defaultValue = IsValid(slice, defaultConstantValue.Length) ? defaultConstantValue[slice] : [];
var canReset = AssociatedShpk?.MaterialParamsDefaults != null var canReset = _associatedShpk?.MaterialParamsDefaults != null
? defaultValue.Length > 0 && !defaultValue.SequenceEqual(buffer[slice]) ? defaultValue.Length > 0 && !defaultValue.SequenceEqual(buffer[slice])
: buffer[slice].ContainsAnyExcept((byte)0); : buffer[slice].ContainsAnyExcept((byte)0);
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Backspace.ToIconString(), ImGui.GetFrameHeight() * Vector2.One, if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Backspace.ToIconString(), ImGui.GetFrameHeight() * Vector2.One,
"Reset this constant to its default value.\n\nHold Ctrl to unlock.", !ImGui.GetIO().KeyCtrl || !canReset, true)) "Reset this constant to its default value.\n\nHold Ctrl to unlock.", !ImGui.GetIO().KeyCtrl || !canReset, true))
{ {
ret = true; ret = true;
if (defaultValue.Length > 0) if (defaultValue.Length > 0)

View file

@ -1,3 +1,4 @@
using JetBrains.Annotations;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Text.Widget.Editors; using OtterGui.Text.Widget.Editors;
using Penumbra.String.Classes; using Penumbra.String.Classes;
@ -29,8 +30,8 @@ public partial class MtrlTab
} }
private T? TryGetShpkDevkitData<T>(string category, uint? id, bool mayVary) where T : class private T? TryGetShpkDevkitData<T>(string category, uint? id, bool mayVary) where T : class
=> TryGetShpkDevkitData<T>(AssociatedShpkDevkit, LoadedShpkDevkitPathName, category, id, mayVary) => TryGetShpkDevkitData<T>(_associatedShpkDevkit, _loadedShpkDevkitPathName, category, id, mayVary)
?? TryGetShpkDevkitData<T>(AssociatedBaseDevkit, LoadedBaseDevkitPathName, 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 private T? TryGetShpkDevkitData<T>(JObject? devkit, string devkitPathName, string category, uint? id, bool mayVary) where T : class
{ {
@ -47,7 +48,7 @@ public partial class MtrlTab
{ {
var selector = BuildSelector(data!["Vary"]! var selector = BuildSelector(data!["Vary"]!
.Select(key => (uint)key) .Select(key => (uint)key)
.Select(key => Mtrl.GetShaderKey(key)?.Value ?? AssociatedShpk!.GetMaterialKeyById(key)!.Value.DefaultValue)); .Select(key => Mtrl.GetShaderKey(key)?.Value ?? _associatedShpk!.GetMaterialKeyById(key)!.Value.DefaultValue));
var index = (int)data["Selectors"]![selector.ToString()]!; var index = (int)data["Selectors"]![selector.ToString()]!;
data = data["Items"]![index]; data = data["Items"]![index];
} }
@ -62,12 +63,14 @@ public partial class MtrlTab
} }
} }
[UsedImplicitly]
private sealed class DevkitShaderKeyValue private sealed class DevkitShaderKeyValue
{ {
public string Label = string.Empty; public string Label = string.Empty;
public string Description = string.Empty; public string Description = string.Empty;
} }
[UsedImplicitly]
private sealed class DevkitShaderKey private sealed class DevkitShaderKey
{ {
public string Label = string.Empty; public string Label = string.Empty;
@ -75,6 +78,7 @@ public partial class MtrlTab
public Dictionary<uint, DevkitShaderKeyValue> Values = []; public Dictionary<uint, DevkitShaderKeyValue> Values = [];
} }
[UsedImplicitly]
private sealed class DevkitSampler private sealed class DevkitSampler
{ {
public string Label = string.Empty; public string Label = string.Empty;
@ -84,14 +88,16 @@ public partial class MtrlTab
private enum DevkitConstantType private enum DevkitConstantType
{ {
Hidden = -1, Hidden = -1,
Float = 0, Float = 0,
/// <summary> Integer encoded as a float. </summary> /// <summary> Integer encoded as a float. </summary>
Integer = 1, Integer = 1,
Color = 2, Color = 2,
Enum = 3, Enum = 3,
/// <summary> Native integer. </summary> /// <summary> Native integer. </summary>
Int32 = 4, Int32 = 4,
Int32Enum = 5, Int32Enum = 5,
Int8 = 6, Int8 = 6,
Int8Enum = 7, Int8Enum = 7,
@ -105,6 +111,7 @@ public partial class MtrlTab
SphereMapIndex = 15, SphereMapIndex = 15,
} }
[UsedImplicitly]
private sealed class DevkitConstantValue private sealed class DevkitConstantValue
{ {
public string Label = string.Empty; public string Label = string.Empty;
@ -112,6 +119,7 @@ public partial class MtrlTab
public double Value = 0; public double Value = 0;
} }
[UsedImplicitly]
private sealed class DevkitConstant private sealed class DevkitConstant
{ {
public uint Offset = 0; public uint Offset = 0;
@ -147,7 +155,7 @@ public partial class MtrlTab
=> ByteOffset ?? Offset * ValueSize; => ByteOffset ?? Offset * ValueSize;
public uint? EffectiveByteSize public uint? EffectiveByteSize
=> ByteSize ?? (Length * ValueSize); => ByteSize ?? Length * ValueSize;
public unsafe uint ValueSize public unsafe uint ValueSize
=> Type switch => Type switch
@ -198,19 +206,23 @@ public partial class MtrlTab
private IEditor<T> CreateIntegerEditor<T>() private IEditor<T> CreateIntegerEditor<T>()
where T : unmanaged, INumber<T> where T : unmanaged, INumber<T>
=> ((Drag || Slider) && !Hex => ((Drag || Slider) && !Hex
? (Drag ? Drag
? (IEditor<T>)DragEditor<T>.CreateInteger(ToInteger<T>(Minimum), ToInteger<T>(Maximum), Speed ?? 0.25f, RelativeSpeed, Unit, 0) ? (IEditor<T>)DragEditor<T>.CreateInteger(ToInteger<T>(Minimum), ToInteger<T>(Maximum), Speed ?? 0.25f, RelativeSpeed,
: SliderEditor<T>.CreateInteger(ToInteger<T>(Minimum) ?? default, ToInteger<T>(Maximum) ?? default, Unit, 0)) Unit, 0)
: InputEditor<T>.CreateInteger(ToInteger<T>(Minimum), ToInteger<T>(Maximum), ToInteger<T>(Step), ToInteger<T>(StepFast), Hex, Unit, 0)) : SliderEditor<T>.CreateInteger(ToInteger<T>(Minimum) ?? default, ToInteger<T>(Maximum) ?? default, Unit, 0)
: InputEditor<T>.CreateInteger(ToInteger<T>(Minimum), ToInteger<T>(Maximum), ToInteger<T>(Step), ToInteger<T>(StepFast),
Hex, Unit, 0))
.WithFactorAndBias(ToInteger<T>(Factor), ToInteger<T>(Bias)); .WithFactorAndBias(ToInteger<T>(Factor), ToInteger<T>(Bias));
private IEditor<T> CreateFloatEditor<T>() private IEditor<T> CreateFloatEditor<T>()
where T : unmanaged, INumber<T>, IPowerFunctions<T> where T : unmanaged, INumber<T>, IPowerFunctions<T>
=> ((Drag || Slider) => (Drag || Slider
? (Drag ? Drag
? (IEditor<T>)DragEditor<T>.CreateFloat(ToFloat<T>(Minimum), ToFloat<T>(Maximum), Speed ?? 0.1f, RelativeSpeed, Precision, Unit, 0) ? (IEditor<T>)DragEditor<T>.CreateFloat(ToFloat<T>(Minimum), ToFloat<T>(Maximum), Speed ?? 0.1f, RelativeSpeed,
: SliderEditor<T>.CreateFloat(ToFloat<T>(Minimum) ?? default, ToFloat<T>(Maximum) ?? default, Precision, Unit, 0)) Precision, Unit, 0)
: InputEditor<T>.CreateFloat(ToFloat<T>(Minimum), ToFloat<T>(Maximum), T.CreateSaturating(Step), T.CreateSaturating(StepFast), Precision, Unit, 0)) : SliderEditor<T>.CreateFloat(ToFloat<T>(Minimum) ?? default, ToFloat<T>(Maximum) ?? default, Precision, Unit, 0)
: InputEditor<T>.CreateFloat(ToFloat<T>(Minimum), ToFloat<T>(Maximum), T.CreateSaturating(Step),
T.CreateSaturating(StepFast), Precision, Unit, 0))
.WithExponent(T.CreateSaturating(Exponent)) .WithExponent(T.CreateSaturating(Exponent))
.WithFactorAndBias(T.CreateSaturating(Factor), T.CreateSaturating(Bias)); .WithFactorAndBias(T.CreateSaturating(Factor), T.CreateSaturating(Bias));

View file

@ -110,9 +110,9 @@ public partial class MtrlTab
} }
ImGui.TableNextColumn(); ImGui.TableNextColumn();
using (ImRaii.PushFont(UiBuilder.MonoFont)) using (ImRaii.PushFont(UiBuilder.MonoFont))
{ {
ImUtf8.Text($"{(rowIdx >> 1) + 1,2:D}{"AB"[rowIdx & 1]}"); ImUtf8.Text($"{(rowIdx >> 1) + 1,2:D}{"AB"[rowIdx & 1]}");
} }
ImGui.TableNextColumn(); ImGui.TableNextColumn();

View file

@ -12,20 +12,20 @@ namespace Penumbra.UI.AdvancedWindow.Materials;
public partial class MtrlTab public partial class MtrlTab
{ {
public readonly List<LiveMaterialPreviewer> MaterialPreviewers = new(4); private readonly List<LiveMaterialPreviewer> _materialPreviewers = new(4);
public readonly List<LiveColorTablePreviewer> ColorTablePreviewers = new(4); private readonly List<LiveColorTablePreviewer> _colorTablePreviewers = new(4);
public int HighlightedColorTablePair = -1; private int _highlightedColorTablePair = -1;
public readonly Stopwatch HighlightTime = new(); private readonly Stopwatch _highlightTime = new();
private void DrawMaterialLivePreviewRebind(bool disabled) private void DrawMaterialLivePreviewRebind(bool disabled)
{ {
if (disabled) if (disabled)
return; return;
if (ImGui.Button("Reload live preview")) if (ImUtf8.Button("Reload live preview"u8))
BindToMaterialInstances(); BindToMaterialInstances();
if (MaterialPreviewers.Count != 0 || ColorTablePreviewers.Count != 0) if (_materialPreviewers.Count != 0 || _colorTablePreviewers.Count != 0)
return; return;
ImGui.SameLine(); ImGui.SameLine();
@ -34,7 +34,7 @@ public partial class MtrlTab
"The current material has not been found on your character. Please check the Import from Screen tab for more information."u8); "The current material has not been found on your character. Please check the Import from Screen tab for more information."u8);
} }
public unsafe void BindToMaterialInstances() private unsafe void BindToMaterialInstances()
{ {
UnbindFromMaterialInstances(); UnbindFromMaterialInstances();
@ -50,7 +50,7 @@ public partial class MtrlTab
try try
{ {
MaterialPreviewers.Add(new LiveMaterialPreviewer(_objects, materialInfo)); _materialPreviewers.Add(new LiveMaterialPreviewer(_objects, materialInfo));
foundMaterials.Add((nint)material); foundMaterials.Add((nint)material);
} }
catch (InvalidOperationException) catch (InvalidOperationException)
@ -68,7 +68,7 @@ public partial class MtrlTab
{ {
try try
{ {
ColorTablePreviewers.Add(new LiveColorTablePreviewer(_objects, _framework, materialInfo)); _colorTablePreviewers.Add(new LiveColorTablePreviewer(_objects, _framework, materialInfo));
} }
catch (InvalidOperationException) catch (InvalidOperationException)
{ {
@ -81,53 +81,53 @@ public partial class MtrlTab
private void UnbindFromMaterialInstances() private void UnbindFromMaterialInstances()
{ {
foreach (var previewer in MaterialPreviewers) foreach (var previewer in _materialPreviewers)
previewer.Dispose(); previewer.Dispose();
MaterialPreviewers.Clear(); _materialPreviewers.Clear();
foreach (var previewer in ColorTablePreviewers) foreach (var previewer in _colorTablePreviewers)
previewer.Dispose(); previewer.Dispose();
ColorTablePreviewers.Clear(); _colorTablePreviewers.Clear();
} }
private unsafe void UnbindFromDrawObjectMaterialInstances(CharacterBase* characterBase) private unsafe void UnbindFromDrawObjectMaterialInstances(CharacterBase* characterBase)
{ {
for (var i = MaterialPreviewers.Count; i-- > 0;) for (var i = _materialPreviewers.Count; i-- > 0;)
{ {
var previewer = MaterialPreviewers[i]; var previewer = _materialPreviewers[i];
if (previewer.DrawObject != characterBase) if (previewer.DrawObject != characterBase)
continue; continue;
previewer.Dispose(); previewer.Dispose();
MaterialPreviewers.RemoveAt(i); _materialPreviewers.RemoveAt(i);
} }
for (var i = ColorTablePreviewers.Count; i-- > 0;) for (var i = _colorTablePreviewers.Count; i-- > 0;)
{ {
var previewer = ColorTablePreviewers[i]; var previewer = _colorTablePreviewers[i];
if (previewer.DrawObject != characterBase) if (previewer.DrawObject != characterBase)
continue; continue;
previewer.Dispose(); previewer.Dispose();
ColorTablePreviewers.RemoveAt(i); _colorTablePreviewers.RemoveAt(i);
} }
} }
public void SetShaderPackageFlags(uint shPkFlags) private void SetShaderPackageFlags(uint shPkFlags)
{ {
foreach (var previewer in MaterialPreviewers) foreach (var previewer in _materialPreviewers)
previewer.SetShaderPackageFlags(shPkFlags); previewer.SetShaderPackageFlags(shPkFlags);
} }
public void SetMaterialParameter(uint parameterCrc, Index offset, Span<byte> value) private void SetMaterialParameter(uint parameterCrc, Index offset, Span<byte> value)
{ {
foreach (var previewer in MaterialPreviewers) foreach (var previewer in _materialPreviewers)
previewer.SetMaterialParameter(parameterCrc, offset, value); previewer.SetMaterialParameter(parameterCrc, offset, value);
} }
public void SetSamplerFlags(uint samplerCrc, uint samplerFlags) private void SetSamplerFlags(uint samplerCrc, uint samplerFlags)
{ {
foreach (var previewer in MaterialPreviewers) foreach (var previewer in _materialPreviewers)
previewer.SetSamplerFlags(samplerCrc, samplerFlags); previewer.SetSamplerFlags(samplerCrc, samplerFlags);
} }
@ -145,14 +145,14 @@ public partial class MtrlTab
SetSamplerFlags(sampler.SamplerId, sampler.Flags); SetSamplerFlags(sampler.SamplerId, sampler.Flags);
} }
public void HighlightColorTablePair(int pairIdx) private void HighlightColorTablePair(int pairIdx)
{ {
var oldPairIdx = HighlightedColorTablePair; var oldPairIdx = _highlightedColorTablePair;
if (HighlightedColorTablePair != pairIdx) if (_highlightedColorTablePair != pairIdx)
{ {
HighlightedColorTablePair = pairIdx; _highlightedColorTablePair = pairIdx;
HighlightTime.Restart(); _highlightTime.Restart();
} }
if (oldPairIdx >= 0) if (oldPairIdx >= 0)
@ -160,19 +160,6 @@ public partial class MtrlTab
UpdateColorTableRowPreview(oldPairIdx << 1); UpdateColorTableRowPreview(oldPairIdx << 1);
UpdateColorTableRowPreview((oldPairIdx << 1) | 1); UpdateColorTableRowPreview((oldPairIdx << 1) | 1);
} }
if (pairIdx >= 0)
{
UpdateColorTableRowPreview(pairIdx << 1);
UpdateColorTableRowPreview((pairIdx << 1) | 1);
}
}
public void CancelColorTableHighlight()
{
var pairIdx = HighlightedColorTablePair;
HighlightedColorTablePair = -1;
HighlightTime.Reset();
if (pairIdx >= 0) if (pairIdx >= 0)
{ {
@ -181,9 +168,23 @@ public partial class MtrlTab
} }
} }
public void UpdateColorTableRowPreview(int rowIdx) private void CancelColorTableHighlight()
{ {
if (ColorTablePreviewers.Count == 0) var pairIdx = _highlightedColorTablePair;
_highlightedColorTablePair = -1;
_highlightTime.Reset();
if (pairIdx >= 0)
{
UpdateColorTableRowPreview(pairIdx << 1);
UpdateColorTableRowPreview((pairIdx << 1) | 1);
}
}
private void UpdateColorTableRowPreview(int rowIdx)
{
if (_colorTablePreviewers.Count == 0)
return; return;
if (Mtrl.Table == null) if (Mtrl.Table == null)
@ -192,7 +193,7 @@ public partial class MtrlTab
var row = Mtrl.Table switch var row = Mtrl.Table switch
{ {
LegacyColorTable legacyTable => new ColorTableRow(legacyTable[rowIdx]), LegacyColorTable legacyTable => new ColorTableRow(legacyTable[rowIdx]),
ColorTable table => table[rowIdx], ColorTable table => table[rowIdx],
_ => throw new InvalidOperationException($"Unsupported color table type {Mtrl.Table.GetType()}"), _ => throw new InvalidOperationException($"Unsupported color table type {Mtrl.Table.GetType()}"),
}; };
if (Mtrl.DyeTable != null) if (Mtrl.DyeTable != null)
@ -200,8 +201,8 @@ public partial class MtrlTab
var dyeRow = Mtrl.DyeTable switch var dyeRow = Mtrl.DyeTable switch
{ {
LegacyColorDyeTable legacyDyeTable => new ColorDyeTableRow(legacyDyeTable[rowIdx]), LegacyColorDyeTable legacyDyeTable => new ColorDyeTableRow(legacyDyeTable[rowIdx]),
ColorDyeTable dyeTable => dyeTable[rowIdx], ColorDyeTable dyeTable => dyeTable[rowIdx],
_ => throw new InvalidOperationException($"Unsupported color dye table type {Mtrl.DyeTable.GetType()}"), _ => throw new InvalidOperationException($"Unsupported color dye table type {Mtrl.DyeTable.GetType()}"),
}; };
if (dyeRow.Channel < StainService.ChannelCount) if (dyeRow.Channel < StainService.ChannelCount)
{ {
@ -213,21 +214,21 @@ public partial class MtrlTab
} }
} }
if (HighlightedColorTablePair << 1 == rowIdx) if (_highlightedColorTablePair << 1 == rowIdx)
ApplyHighlight(ref row, ColorId.InGameHighlight, (float)HighlightTime.Elapsed.TotalSeconds); ApplyHighlight(ref row, ColorId.InGameHighlight, (float)_highlightTime.Elapsed.TotalSeconds);
else if (((HighlightedColorTablePair << 1) | 1) == rowIdx) else if (((_highlightedColorTablePair << 1) | 1) == rowIdx)
ApplyHighlight(ref row, ColorId.InGameHighlight2, (float)HighlightTime.Elapsed.TotalSeconds); ApplyHighlight(ref row, ColorId.InGameHighlight2, (float)_highlightTime.Elapsed.TotalSeconds);
foreach (var previewer in ColorTablePreviewers) foreach (var previewer in _colorTablePreviewers)
{ {
row[..].CopyTo(previewer.GetColorRow(rowIdx)); row[..].CopyTo(previewer.GetColorRow(rowIdx));
previewer.ScheduleUpdate(); previewer.ScheduleUpdate();
} }
} }
public void UpdateColorTablePreview() private void UpdateColorTablePreview()
{ {
if (ColorTablePreviewers.Count == 0) if (_colorTablePreviewers.Count == 0)
return; return;
if (Mtrl.Table == null) if (Mtrl.Table == null)
@ -237,7 +238,8 @@ public partial class MtrlTab
var dyeRows = Mtrl.DyeTable != null ? ColorDyeTable.CastOrConvert(Mtrl.DyeTable) : null; var dyeRows = Mtrl.DyeTable != null ? ColorDyeTable.CastOrConvert(Mtrl.DyeTable) : null;
if (dyeRows != null) if (dyeRows != null)
{ {
ReadOnlySpan<StainId> stainIds = [ ReadOnlySpan<StainId> stainIds =
[
_stainService.StainCombo1.CurrentSelection.Key, _stainService.StainCombo1.CurrentSelection.Key,
_stainService.StainCombo2.CurrentSelection.Key, _stainService.StainCombo2.CurrentSelection.Key,
]; ];
@ -245,13 +247,14 @@ public partial class MtrlTab
rows.ApplyDye(_stainService.GudStmFile, stainIds, dyeRows); rows.ApplyDye(_stainService.GudStmFile, stainIds, dyeRows);
} }
if (HighlightedColorTablePair >= 0) if (_highlightedColorTablePair >= 0)
{ {
ApplyHighlight(ref rows[HighlightedColorTablePair << 1], ColorId.InGameHighlight, (float)HighlightTime.Elapsed.TotalSeconds); ApplyHighlight(ref rows[_highlightedColorTablePair << 1], ColorId.InGameHighlight, (float)_highlightTime.Elapsed.TotalSeconds);
ApplyHighlight(ref rows[(HighlightedColorTablePair << 1) | 1], ColorId.InGameHighlight2, (float)HighlightTime.Elapsed.TotalSeconds); ApplyHighlight(ref rows[(_highlightedColorTablePair << 1) | 1], ColorId.InGameHighlight2,
(float)_highlightTime.Elapsed.TotalSeconds);
} }
foreach (var previewer in ColorTablePreviewers) foreach (var previewer in _colorTablePreviewers)
{ {
rows.AsHalves().CopyTo(previewer.ColorTable); rows.AsHalves().CopyTo(previewer.ColorTable);
previewer.ScheduleUpdate(); previewer.ScheduleUpdate();
@ -260,11 +263,11 @@ public partial class MtrlTab
private static void ApplyHighlight(ref ColorTableRow row, ColorId colorId, float time) private static void ApplyHighlight(ref ColorTableRow row, ColorId colorId, float time)
{ {
var level = (MathF.Sin(time * 2.0f * MathF.PI) + 2.0f) / 3.0f / 255.0f; var level = (MathF.Sin(time * 2.0f * MathF.PI) + 2.0f) / 3.0f / 255.0f;
var baseColor = colorId.Value(); var baseColor = colorId.Value();
var color = level * new Vector3(baseColor & 0xFF, (baseColor >> 8) & 0xFF, (baseColor >> 16) & 0xFF); var color = level * new Vector3(baseColor & 0xFF, (baseColor >> 8) & 0xFF, (baseColor >> 16) & 0xFF);
var halfColor = (HalfColor)(color * color); var halfColor = (HalfColor)(color * color);
row.DiffuseColor = halfColor; row.DiffuseColor = halfColor;
row.SpecularColor = halfColor; row.SpecularColor = halfColor;
row.EmissiveColor = halfColor; row.EmissiveColor = halfColor;

View file

@ -21,8 +21,8 @@ public partial class MtrlTab
// Apricot shader packages are unlisted because // Apricot shader packages are unlisted because
// 1. they cause severe performance/memory issues when calculating the effective shader set // 1. they cause severe performance/memory issues when calculating the effective shader set
// 2. they probably aren't intended for use with materials anyway // 2. they probably aren't intended for use with materials anyway
internal static readonly IReadOnlyList<string> StandardShaderPackages = new[] private static readonly IReadOnlyList<string> StandardShaderPackages =
{ [
"3dui.shpk", "3dui.shpk",
// "apricot_decal_dummy.shpk", // "apricot_decal_dummy.shpk",
// "apricot_decal_ring.shpk", // "apricot_decal_ring.shpk",
@ -80,35 +80,35 @@ public partial class MtrlTab
"verticalfog.shpk", "verticalfog.shpk",
"water.shpk", "water.shpk",
"weather.shpk", "weather.shpk",
}; ];
private static readonly byte[] UnknownShadersString = Encoding.UTF8.GetBytes("Vertex Shaders: ???\nPixel Shaders: ???"); private static readonly byte[] UnknownShadersString = "Vertex Shaders: ???\nPixel Shaders: ???"u8.ToArray();
private string[]? _shpkNames; private string[]? _shpkNames;
public string ShaderHeader = "Shader###Shader"; private string _shaderHeader = "Shader###Shader";
public FullPath LoadedShpkPath = FullPath.Empty; private FullPath _loadedShpkPath = FullPath.Empty;
public string LoadedShpkPathName = string.Empty; private string _loadedShpkPathName = string.Empty;
public string LoadedShpkDevkitPathName = string.Empty; private string _loadedShpkDevkitPathName = string.Empty;
public string ShaderComment = string.Empty; private string _shaderComment = string.Empty;
public ShpkFile? AssociatedShpk; private ShpkFile? _associatedShpk;
public bool ShpkLoading; private bool _shpkLoading;
public JObject? AssociatedShpkDevkit; private JObject? _associatedShpkDevkit;
public readonly string LoadedBaseDevkitPathName; private readonly string _loadedBaseDevkitPathName;
public readonly JObject? AssociatedBaseDevkit; private readonly JObject? _associatedBaseDevkit;
// Shader Key State // Shader Key State
public readonly private readonly
List<(string Label, int Index, string Description, bool MonoFont, IReadOnlyList<(string Label, uint Value, string Description)> List<(string Label, int Index, string Description, bool MonoFont, IReadOnlyList<(string Label, uint Value, string Description)>
Values)> ShaderKeys = new(16); Values)> _shaderKeys = new(16);
public readonly HashSet<int> VertexShaders = new(16); private readonly HashSet<int> _vertexShaders = new(16);
public readonly HashSet<int> PixelShaders = new(16); private readonly HashSet<int> _pixelShaders = new(16);
public bool ShadersKnown; private bool _shadersKnown;
public ReadOnlyMemory<byte> ShadersString = UnknownShadersString; private ReadOnlyMemory<byte> _shadersString = UnknownShadersString;
public string[] GetShpkNames() private string[] GetShpkNames()
{ {
if (null != _shpkNames) if (null != _shpkNames)
return _shpkNames; return _shpkNames;
@ -122,7 +122,7 @@ public partial class MtrlTab
return _shpkNames; return _shpkNames;
} }
public FullPath FindAssociatedShpk(out string defaultPath, out Utf8GamePath defaultGamePath) private FullPath FindAssociatedShpk(out string defaultPath, out Utf8GamePath defaultGamePath)
{ {
defaultPath = GamePaths.Shader.ShpkPath(Mtrl.ShaderPackage.Name); defaultPath = GamePaths.Shader.ShpkPath(Mtrl.ShaderPackage.Name);
if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath)) if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath))
@ -131,45 +131,45 @@ public partial class MtrlTab
return _edit.FindBestMatch(defaultGamePath); return _edit.FindBestMatch(defaultGamePath);
} }
public void LoadShpk(FullPath path) private void LoadShpk(FullPath path)
=> Task.Run(() => DoLoadShpk(path)); => Task.Run(() => DoLoadShpk(path));
private async Task DoLoadShpk(FullPath path) private async Task DoLoadShpk(FullPath path)
{ {
ShadersKnown = false; _shadersKnown = false;
ShaderHeader = $"Shader ({Mtrl.ShaderPackage.Name})###Shader"; _shaderHeader = $"Shader ({Mtrl.ShaderPackage.Name})###Shader";
ShpkLoading = true; _shpkLoading = true;
try try
{ {
var data = path.IsRooted var data = path.IsRooted
? await File.ReadAllBytesAsync(path.FullName) ? await File.ReadAllBytesAsync(path.FullName)
: _gameData.GetFile(path.InternalName.ToString())?.Data; : _gameData.GetFile(path.InternalName.ToString())?.Data;
LoadedShpkPath = path; _loadedShpkPath = path;
AssociatedShpk = data?.Length > 0 ? new ShpkFile(data) : throw new Exception("Failure to load file data."); _associatedShpk = data?.Length > 0 ? new ShpkFile(data) : throw new Exception("Failure to load file data.");
LoadedShpkPathName = path.ToPath(); _loadedShpkPathName = path.ToPath();
} }
catch (Exception e) catch (Exception e)
{ {
LoadedShpkPath = FullPath.Empty; _loadedShpkPath = FullPath.Empty;
LoadedShpkPathName = string.Empty; _loadedShpkPathName = string.Empty;
AssociatedShpk = null; _associatedShpk = null;
Penumbra.Messager.NotificationMessage(e, $"Could not load {LoadedShpkPath.ToPath()}.", NotificationType.Error, false); Penumbra.Messager.NotificationMessage(e, $"Could not load {_loadedShpkPath.ToPath()}.", NotificationType.Error, false);
} }
finally finally
{ {
ShpkLoading = false; _shpkLoading = false;
} }
if (LoadedShpkPath.InternalName.IsEmpty) if (_loadedShpkPath.InternalName.IsEmpty)
{ {
AssociatedShpkDevkit = null; _associatedShpkDevkit = null;
LoadedShpkDevkitPathName = string.Empty; _loadedShpkDevkitPathName = string.Empty;
} }
else else
{ {
AssociatedShpkDevkit = _associatedShpkDevkit =
TryLoadShpkDevkit(Path.GetFileNameWithoutExtension(Mtrl.ShaderPackage.Name), out LoadedShpkDevkitPathName); TryLoadShpkDevkit(Path.GetFileNameWithoutExtension(Mtrl.ShaderPackage.Name), out _loadedShpkDevkitPathName);
} }
UpdateShaderKeys(); UpdateShaderKeys();
@ -178,9 +178,9 @@ public partial class MtrlTab
private void UpdateShaderKeys() private void UpdateShaderKeys()
{ {
ShaderKeys.Clear(); _shaderKeys.Clear();
if (AssociatedShpk != null) if (_associatedShpk != null)
foreach (var key in AssociatedShpk.MaterialKeys) foreach (var key in _associatedShpk.MaterialKeys)
{ {
var keyName = Names.KnownNames.TryResolve(key.Id); var keyName = Names.KnownNames.TryResolve(key.Id);
var dkData = TryGetShpkDevkitData<DevkitShaderKey>("ShaderKeys", key.Id, false); var dkData = TryGetShpkDevkitData<DevkitShaderKey>("ShaderKeys", key.Id, false);
@ -210,7 +210,7 @@ public partial class MtrlTab
return string.Compare(x.Label, y.Label, StringComparison.Ordinal); return string.Compare(x.Label, y.Label, StringComparison.Ordinal);
}); });
ShaderKeys.Add((hasDkLabel ? dkData!.Label : keyName.ToString(), mtrlKeyIndex, dkData?.Description ?? string.Empty, _shaderKeys.Add((hasDkLabel ? dkData!.Label : keyName.ToString(), mtrlKeyIndex, dkData?.Description ?? string.Empty,
!hasDkLabel, values)); !hasDkLabel, values));
} }
else else
@ -218,7 +218,7 @@ public partial class MtrlTab
{ {
var keyName = Names.KnownNames.TryResolve(key.Category); var keyName = Names.KnownNames.TryResolve(key.Category);
var valueName = keyName.WithKnownSuffixes().TryResolve(Names.KnownNames, key.Value); var valueName = keyName.WithKnownSuffixes().TryResolve(Names.KnownNames, key.Value);
ShaderKeys.Add((keyName.ToString(), index, string.Empty, true, [(valueName.ToString(), key.Value, string.Empty)])); _shaderKeys.Add((keyName.ToString(), index, string.Empty, true, [(valueName.ToString(), key.Value, string.Empty)]));
} }
} }
@ -232,27 +232,28 @@ public partial class MtrlTab
passSet = []; passSet = [];
byPassSets.Add(passId, passSet); byPassSets.Add(passId, passSet);
} }
passSet.Add(shaderIndex); passSet.Add(shaderIndex);
} }
VertexShaders.Clear(); _vertexShaders.Clear();
PixelShaders.Clear(); _pixelShaders.Clear();
var vertexShadersByPass = new Dictionary<uint, HashSet<int>>(); var vertexShadersByPass = new Dictionary<uint, HashSet<int>>();
var pixelShadersByPass = new Dictionary<uint, HashSet<int>>(); var pixelShadersByPass = new Dictionary<uint, HashSet<int>>();
if (AssociatedShpk == null || !AssociatedShpk.IsExhaustiveNodeAnalysisFeasible()) if (_associatedShpk == null || !_associatedShpk.IsExhaustiveNodeAnalysisFeasible())
{ {
ShadersKnown = false; _shadersKnown = false;
} }
else else
{ {
ShadersKnown = true; _shadersKnown = true;
var systemKeySelectors = AllSelectors(AssociatedShpk.SystemKeys).ToArray(); var systemKeySelectors = AllSelectors(_associatedShpk.SystemKeys).ToArray();
var sceneKeySelectors = AllSelectors(AssociatedShpk.SceneKeys).ToArray(); var sceneKeySelectors = AllSelectors(_associatedShpk.SceneKeys).ToArray();
var subViewKeySelectors = AllSelectors(AssociatedShpk.SubViewKeys).ToArray(); var subViewKeySelectors = AllSelectors(_associatedShpk.SubViewKeys).ToArray();
var materialKeySelector = var materialKeySelector =
BuildSelector(AssociatedShpk.MaterialKeys.Select(key => Mtrl.GetOrAddShaderKey(key.Id, key.DefaultValue).Value)); BuildSelector(_associatedShpk.MaterialKeys.Select(key => Mtrl.GetOrAddShaderKey(key.Id, key.DefaultValue).Value));
foreach (var systemKeySelector in systemKeySelectors) foreach (var systemKeySelector in systemKeySelectors)
{ {
@ -261,38 +262,39 @@ public partial class MtrlTab
foreach (var subViewKeySelector in subViewKeySelectors) foreach (var subViewKeySelector in subViewKeySelectors)
{ {
var selector = BuildSelector(systemKeySelector, sceneKeySelector, materialKeySelector, subViewKeySelector); var selector = BuildSelector(systemKeySelector, sceneKeySelector, materialKeySelector, subViewKeySelector);
var node = AssociatedShpk.GetNodeBySelector(selector); var node = _associatedShpk.GetNodeBySelector(selector);
if (node.HasValue) if (node.HasValue)
foreach (var pass in node.Value.Passes) foreach (var pass in node.Value.Passes)
{ {
AddShader(VertexShaders, vertexShadersByPass, pass.Id, (int)pass.VertexShader); AddShader(_vertexShaders, vertexShadersByPass, pass.Id, (int)pass.VertexShader);
AddShader(PixelShaders, pixelShadersByPass, pass.Id, (int)pass.PixelShader); AddShader(_pixelShaders, pixelShadersByPass, pass.Id, (int)pass.PixelShader);
} }
else else
ShadersKnown = false; _shadersKnown = false;
} }
} }
} }
} }
if (ShadersKnown) if (_shadersKnown)
{ {
var builder = new StringBuilder(); var builder = new StringBuilder();
foreach (var (passId, passVS) in vertexShadersByPass) foreach (var (passId, passVertexShader) in vertexShadersByPass)
{ {
if (builder.Length > 0) if (builder.Length > 0)
builder.Append("\n\n"); builder.Append("\n\n");
var passName = Names.KnownNames.TryResolve(passId); var passName = Names.KnownNames.TryResolve(passId);
var shaders = passVS.OrderBy(i => i).Select(i => $"#{i}"); var shaders = passVertexShader.OrderBy(i => i).Select(i => $"#{i}");
builder.Append($"Vertex Shaders ({passName}): {string.Join(", ", shaders)}"); builder.Append($"Vertex Shaders ({passName}): {string.Join(", ", shaders)}");
if (pixelShadersByPass.TryGetValue(passId, out var passPS)) if (pixelShadersByPass.TryGetValue(passId, out var passPixelShader))
{ {
shaders = passPS.OrderBy(i => i).Select(i => $"#{i}"); shaders = passPixelShader.OrderBy(i => i).Select(i => $"#{i}");
builder.Append($"\nPixel Shaders ({passName}): {string.Join(", ", shaders)}"); builder.Append($"\nPixel Shaders ({passName}): {string.Join(", ", shaders)}");
} }
} }
foreach (var (passId, passPS) in pixelShadersByPass)
foreach (var (passId, passPixelShader) in pixelShadersByPass)
{ {
if (vertexShadersByPass.ContainsKey(passId)) if (vertexShadersByPass.ContainsKey(passId))
continue; continue;
@ -301,22 +303,24 @@ public partial class MtrlTab
builder.Append("\n\n"); builder.Append("\n\n");
var passName = Names.KnownNames.TryResolve(passId); var passName = Names.KnownNames.TryResolve(passId);
var shaders = passPS.OrderBy(i => i).Select(i => $"#{i}"); var shaders = passPixelShader.OrderBy(i => i).Select(i => $"#{i}");
builder.Append($"Pixel Shaders ({passName}): {string.Join(", ", shaders)}"); builder.Append($"Pixel Shaders ({passName}): {string.Join(", ", shaders)}");
} }
ShadersString = Encoding.UTF8.GetBytes(builder.ToString()); _shadersString = Encoding.UTF8.GetBytes(builder.ToString());
} }
else else
ShadersString = UnknownShadersString; {
_shadersString = UnknownShadersString;
}
ShaderComment = TryGetShpkDevkitData<string>("Comment", null, true) ?? string.Empty; _shaderComment = TryGetShpkDevkitData<string>("Comment", null, true) ?? string.Empty;
} }
private bool DrawShaderSection(bool disabled) private bool DrawShaderSection(bool disabled)
{ {
var ret = false; var ret = false;
if (ImGui.CollapsingHeader(ShaderHeader)) if (ImGui.CollapsingHeader(_shaderHeader))
{ {
ret |= DrawPackageNameInput(disabled); ret |= DrawPackageNameInput(disabled);
ret |= DrawShaderFlagsInput(disabled); ret |= DrawShaderFlagsInput(disabled);
@ -325,20 +329,17 @@ public partial class MtrlTab
DrawMaterialShaders(); DrawMaterialShaders();
} }
if (!ShpkLoading && (AssociatedShpk == null || AssociatedShpkDevkit == null)) if (!_shpkLoading && (_associatedShpk == null || _associatedShpkDevkit == null))
{ {
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
if (AssociatedShpk == null) if (_associatedShpk == null)
{
ImUtf8.Text("Unable to find a suitable shader (.shpk) file for cross-references. Some functionality will be missing."u8, ImUtf8.Text("Unable to find a suitable shader (.shpk) file for cross-references. Some functionality will be missing."u8,
ImGuiUtil.HalfBlendText(0x80u)); // Half red ImGuiUtil.HalfBlendText(0x80u)); // Half red
}
else else
{ ImUtf8.Text(
ImUtf8.Text("No dev-kit file found for this material's shaders. Please install one for optimal editing experience, such as actual constant names instead of hexadecimal identifiers."u8, "No dev-kit file found for this material's shaders. Please install one for optimal editing experience, such as actual constant names instead of hexadecimal identifiers."u8,
ImGuiUtil.HalfBlendText(0x8080u)); // Half yellow ImGuiUtil.HalfBlendText(0x8080u)); // Half yellow
}
} }
return ret; return ret;
@ -358,14 +359,14 @@ public partial class MtrlTab
if (c) if (c)
foreach (var value in GetShpkNames()) foreach (var value in GetShpkNames())
{ {
if (ImGui.Selectable(value, value == Mtrl.ShaderPackage.Name)) if (!ImGui.Selectable(value, value == Mtrl.ShaderPackage.Name))
{ continue;
Mtrl.ShaderPackage.Name = value;
ret = true; Mtrl.ShaderPackage.Name = value;
AssociatedShpk = null; ret = true;
LoadedShpkPath = FullPath.Empty; _associatedShpk = null;
LoadShpk(FindAssociatedShpk(out _, out _)); _loadedShpkPath = FullPath.Empty;
} LoadShpk(FindAssociatedShpk(out _, out _));
} }
return ret; return ret;
@ -391,23 +392,23 @@ public partial class MtrlTab
private void DrawCustomAssociations() private void DrawCustomAssociations()
{ {
const string tooltip = "Click to copy file path to clipboard."; const string tooltip = "Click to copy file path to clipboard.";
var text = AssociatedShpk == null var text = _associatedShpk == null
? "Associated .shpk file: None" ? "Associated .shpk file: None"
: $"Associated .shpk file: {LoadedShpkPathName}"; : $"Associated .shpk file: {_loadedShpkPathName}";
var devkitText = AssociatedShpkDevkit == null var devkitText = _associatedShpkDevkit == null
? "Associated dev-kit file: None" ? "Associated dev-kit file: None"
: $"Associated dev-kit file: {LoadedShpkDevkitPathName}"; : $"Associated dev-kit file: {_loadedShpkDevkitPathName}";
var baseDevkitText = AssociatedBaseDevkit == null var baseDevkitText = _associatedBaseDevkit == null
? "Base dev-kit file: None" ? "Base dev-kit file: None"
: $"Base dev-kit file: {LoadedBaseDevkitPathName}"; : $"Base dev-kit file: {_loadedBaseDevkitPathName}";
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
ImGuiUtil.CopyOnClickSelectable(text, LoadedShpkPathName, tooltip); ImUtf8.CopyOnClickSelectable(text, _loadedShpkPathName, tooltip);
ImGuiUtil.CopyOnClickSelectable(devkitText, LoadedShpkDevkitPathName, tooltip); ImUtf8.CopyOnClickSelectable(devkitText, _loadedShpkDevkitPathName, tooltip);
ImGuiUtil.CopyOnClickSelectable(baseDevkitText, LoadedBaseDevkitPathName, tooltip); ImUtf8.CopyOnClickSelectable(baseDevkitText, _loadedBaseDevkitPathName, tooltip);
if (ImGui.Button("Associate Custom .shpk File")) if (ImUtf8.Button("Associate Custom .shpk File"u8))
_fileDialog.OpenFilePicker("Associate Custom .shpk File...", ".shpk", (success, name) => _fileDialog.OpenFilePicker("Associate Custom .shpk File...", ".shpk", (success, name) =>
{ {
if (success) if (success)
@ -416,15 +417,15 @@ public partial class MtrlTab
var moddedPath = FindAssociatedShpk(out var defaultPath, out var gamePath); var moddedPath = FindAssociatedShpk(out var defaultPath, out var gamePath);
ImGui.SameLine(); ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Associate Default .shpk File", Vector2.Zero, moddedPath.ToPath(), if (ImUtf8.ButtonEx("Associate Default .shpk File"u8, moddedPath.ToPath(), Vector2.Zero,
moddedPath.Equals(LoadedShpkPath))) moddedPath.Equals(_loadedShpkPath)))
LoadShpk(moddedPath); LoadShpk(moddedPath);
if (!gamePath.Path.Equals(moddedPath.InternalName)) if (!gamePath.Path.Equals(moddedPath.InternalName))
{ {
ImGui.SameLine(); ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Associate Unmodded .shpk File", Vector2.Zero, defaultPath, if (ImUtf8.ButtonEx("Associate Unmodded .shpk File", defaultPath, Vector2.Zero,
gamePath.Path.Equals(LoadedShpkPath.InternalName))) gamePath.Path.Equals(_loadedShpkPath.InternalName)))
LoadShpk(new FullPath(gamePath)); LoadShpk(new FullPath(gamePath));
} }
@ -433,22 +434,23 @@ public partial class MtrlTab
private bool DrawMaterialShaderKeys(bool disabled) private bool DrawMaterialShaderKeys(bool disabled)
{ {
if (ShaderKeys.Count == 0) if (_shaderKeys.Count == 0)
return false; return false;
var ret = false; var ret = false;
foreach (var (label, index, description, monoFont, values) in ShaderKeys) foreach (var (label, index, description, monoFont, values) in _shaderKeys)
{ {
using var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont, monoFont);
ref var key = ref Mtrl.ShaderPackage.ShaderKeys[index]; ref var key = ref Mtrl.ShaderPackage.ShaderKeys[index];
var shpkKey = AssociatedShpk?.GetMaterialKeyById(key.Category); using var id = ImUtf8.PushId((int)key.Category);
var shpkKey = _associatedShpk?.GetMaterialKeyById(key.Category);
var currentValue = key.Value; var currentValue = key.Value;
var (currentLabel, _, currentDescription) = var (currentLabel, _, currentDescription) =
values.FirstOrNull(v => v.Value == currentValue) ?? ($"0x{currentValue:X8}", currentValue, string.Empty); values.FirstOrNull(v => v.Value == currentValue) ?? ($"0x{currentValue:X8}", currentValue, string.Empty);
if (!disabled && shpkKey.HasValue) if (!disabled && shpkKey.HasValue)
{ {
ImGui.SetNextItemWidth(UiHelpers.Scale * 250.0f); ImGui.SetNextItemWidth(UiHelpers.Scale * 250.0f);
using (var c = ImRaii.Combo($"##{key.Category:X8}", currentLabel)) using (var c = ImUtf8.Combo(""u8, currentLabel))
{ {
if (c) if (c)
foreach (var (valueLabel, value, valueDescription) in values) foreach (var (valueLabel, value, valueDescription) in values)
@ -469,16 +471,16 @@ public partial class MtrlTab
if (description.Length > 0) if (description.Length > 0)
ImGuiUtil.LabeledHelpMarker(label, description); ImGuiUtil.LabeledHelpMarker(label, description);
else else
ImGui.TextUnformatted(label); ImUtf8.Text(label);
} }
else if (description.Length > 0 || currentDescription.Length > 0) else if (description.Length > 0 || currentDescription.Length > 0)
{ {
ImGuiUtil.LabeledHelpMarker($"{label}: {currentLabel}", ImUtf8.LabeledHelpMarker($"{label}: {currentLabel}",
description + (description.Length > 0 && currentDescription.Length > 0 ? "\n\n" : string.Empty) + currentDescription); description + (description.Length > 0 && currentDescription.Length > 0 ? "\n\n" : string.Empty) + currentDescription);
} }
else else
{ {
ImGui.TextUnformatted($"{label}: {currentLabel}"); ImUtf8.Text($"{label}: {currentLabel}");
} }
} }
@ -487,19 +489,19 @@ public partial class MtrlTab
private void DrawMaterialShaders() private void DrawMaterialShaders()
{ {
if (AssociatedShpk == null) if (_associatedShpk == null)
return; return;
using (var node = ImUtf8.TreeNode("Candidate Shaders"u8)) using (var node = ImUtf8.TreeNode("Candidate Shaders"u8))
{ {
if (node) if (node)
ImUtf8.Text(ShadersString.Span); ImUtf8.Text(_shadersString.Span);
} }
if (ShaderComment.Length > 0) if (_shaderComment.Length > 0)
{ {
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
ImGui.TextUnformatted(ShaderComment); ImUtf8.Text(_shaderComment);
} }
} }
} }

View file

@ -23,7 +23,7 @@ public partial class MtrlTab
{ {
Textures.Clear(); Textures.Clear();
SamplerIds.Clear(); SamplerIds.Clear();
if (AssociatedShpk == null) if (_associatedShpk == null)
{ {
SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId)); SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId));
if (Mtrl.Table != null) if (Mtrl.Table != null)
@ -34,11 +34,11 @@ public partial class MtrlTab
} }
else else
{ {
foreach (var index in VertexShaders) foreach (var index in _vertexShaders)
SamplerIds.UnionWith(AssociatedShpk.VertexShaders[index].Samplers.Select(sampler => sampler.Id)); SamplerIds.UnionWith(_associatedShpk.VertexShaders[index].Samplers.Select(sampler => sampler.Id));
foreach (var index in PixelShaders) foreach (var index in _pixelShaders)
SamplerIds.UnionWith(AssociatedShpk.PixelShaders[index].Samplers.Select(sampler => sampler.Id)); SamplerIds.UnionWith(_associatedShpk.PixelShaders[index].Samplers.Select(sampler => sampler.Id));
if (!ShadersKnown) if (!_shadersKnown)
{ {
SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId)); SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId));
if (Mtrl.Table != null) if (Mtrl.Table != null)
@ -47,11 +47,11 @@ public partial class MtrlTab
foreach (var samplerId in SamplerIds) foreach (var samplerId in SamplerIds)
{ {
var shpkSampler = AssociatedShpk.GetSamplerById(samplerId); var shpkSampler = _associatedShpk.GetSamplerById(samplerId);
if (shpkSampler is not { Slot: 2 }) if (shpkSampler is not { Slot: 2 })
continue; continue;
var dkData = TryGetShpkDevkitData<DevkitSampler>("Samplers", samplerId, true); var dkData = TryGetShpkDevkitData<DevkitSampler>("Samplers", samplerId, true);
var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label); var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label);
var sampler = Mtrl.GetOrAddSampler(samplerId, dkData?.DefaultTexture ?? string.Empty, out var samplerIndex); var sampler = Mtrl.GetOrAddSampler(samplerId, dkData?.DefaultTexture ?? string.Empty, out var samplerIndex);
@ -95,9 +95,12 @@ public partial class MtrlTab
private static ReadOnlySpan<byte> TextureAddressModeTooltip(TextureAddressMode addressMode) private static ReadOnlySpan<byte> TextureAddressModeTooltip(TextureAddressMode addressMode)
=> addressMode switch => addressMode switch
{ {
TextureAddressMode.Wrap => "Tile the texture at every UV integer junction.\n\nFor example, for U values between 0 and 3, the texture is repeated three times."u8, TextureAddressMode.Wrap =>
TextureAddressMode.Mirror => "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."u8, "Tile the texture at every UV integer junction.\n\nFor example, for U values between 0 and 3, the texture is repeated three times."u8,
TextureAddressMode.Clamp => "Texture coordinates outside the range [0.0, 1.0] are set to the texture color at 0.0 or 1.0, respectively."u8, TextureAddressMode.Mirror =>
"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."u8,
TextureAddressMode.Clamp =>
"Texture coordinates outside the range [0.0, 1.0] are set to the texture color at 0.0 or 1.0, respectively."u8,
TextureAddressMode.Border => "Texture coordinates outside the range [0.0, 1.0] are set to the border color (generally black)."u8, TextureAddressMode.Border => "Texture coordinates outside the range [0.0, 1.0] are set to the border color (generally black)."u8,
_ => ""u8, _ => ""u8,
}; };
@ -167,7 +170,7 @@ public partial class MtrlTab
return ret; return ret;
} }
private static bool ComboTextureAddressMode(ReadOnlySpan<byte> label, ref TextureAddressMode value) private static bool ComboTextureAddressMode(ReadOnlySpan<byte> label, ref TextureAddressMode value)
{ {
using var c = ImUtf8.Combo(label, value.ToString()); using var c = ImUtf8.Combo(label, value.ToString());
@ -202,7 +205,7 @@ public partial class MtrlTab
ret = true; ret = true;
} }
ref var samplerFlags = ref SamplerFlags.Wrap(ref sampler.Flags); ref var samplerFlags = ref Wrap(ref sampler.Flags);
ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f); ImGui.SetNextItemWidth(UiHelpers.Scale * 100.0f);
var addressMode = samplerFlags.UAddressMode; var addressMode = samplerFlags.UAddressMode;

View file

@ -4,7 +4,6 @@ using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.GameData;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
using Penumbra.GameData.Files.MaterialStructs; using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
@ -21,7 +20,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
private const int ShpkPrefixLength = 16; private const int ShpkPrefixLength = 16;
private static readonly CiByteString ShpkPrefix = CiByteString.FromSpanUnsafe("shader/sm5/shpk/"u8, true, true, true); private static readonly CiByteString ShpkPrefix = CiByteString.FromSpanUnsafe("shader/sm5/shpk/"u8, true, true, true);
private readonly IDataManager _gameData; private readonly IDataManager _gameData;
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly ObjectManager _objects; private readonly ObjectManager _objects;
@ -40,7 +39,8 @@ public sealed partial class MtrlTab : IWritable, IDisposable
private bool _updateOnNextFrame; private bool _updateOnNextFrame;
public unsafe MtrlTab(IDataManager gameData, IFramework framework, ObjectManager objects, CharacterBaseDestructor characterBaseDestructor, public unsafe MtrlTab(IDataManager gameData, IFramework framework, ObjectManager objects, CharacterBaseDestructor characterBaseDestructor,
StainService stainService, ResourceTreeFactory resourceTreeFactory, FileDialogService fileDialog, MaterialTemplatePickers materialTemplatePickers, StainService stainService, ResourceTreeFactory resourceTreeFactory, FileDialogService fileDialog,
MaterialTemplatePickers materialTemplatePickers,
Configuration config, ModEditWindow edit, MtrlFile file, string filePath, bool writable) Configuration config, ModEditWindow edit, MtrlFile file, string filePath, bool writable)
{ {
_gameData = gameData; _gameData = gameData;
@ -53,11 +53,11 @@ public sealed partial class MtrlTab : IWritable, IDisposable
_materialTemplatePickers = materialTemplatePickers; _materialTemplatePickers = materialTemplatePickers;
_config = config; _config = config;
_edit = edit; _edit = edit;
Mtrl = file; Mtrl = file;
FilePath = filePath; FilePath = filePath;
Writable = writable; Writable = writable;
AssociatedBaseDevkit = TryLoadShpkDevkit("_base", out LoadedBaseDevkitPathName); _associatedBaseDevkit = TryLoadShpkDevkit("_base", out _loadedBaseDevkitPathName);
Update(); Update();
LoadShpk(FindAssociatedShpk(out _, out _)); LoadShpk(FindAssociatedShpk(out _, out _));
if (writable) if (writable)
@ -118,7 +118,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
using var dis = ImRaii.Disabled(disabled); using var dis = ImRaii.Disabled(disabled);
var tmp = shaderFlags.EnableTransparency; var tmp = shaderFlags.EnableTransparency;
if (ImGui.Checkbox("Enable Transparency", ref tmp)) if (ImUtf8.Checkbox("Enable Transparency"u8, ref tmp))
{ {
shaderFlags.EnableTransparency = tmp; shaderFlags.EnableTransparency = tmp;
ret = true; ret = true;
@ -127,14 +127,14 @@ public sealed partial class MtrlTab : IWritable, IDisposable
ImGui.SameLine(200 * UiHelpers.Scale + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X); ImGui.SameLine(200 * UiHelpers.Scale + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X);
tmp = shaderFlags.HideBackfaces; tmp = shaderFlags.HideBackfaces;
if (ImGui.Checkbox("Hide Backfaces", ref tmp)) if (ImUtf8.Checkbox("Hide Backfaces"u8, ref tmp))
{ {
shaderFlags.HideBackfaces = tmp; shaderFlags.HideBackfaces = tmp;
ret = true; ret = true;
SetShaderPackageFlags(Mtrl.ShaderPackage.Flags); SetShaderPackageFlags(Mtrl.ShaderPackage.Flags);
} }
if (ShpkLoading) if (_shpkLoading)
{ {
ImGui.SameLine(400 * UiHelpers.Scale + 2 * ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X); ImGui.SameLine(400 * UiHelpers.Scale + 2 * ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X);
@ -147,32 +147,32 @@ public sealed partial class MtrlTab : IWritable, IDisposable
private void DrawOtherMaterialDetails(bool _) private void DrawOtherMaterialDetails(bool _)
{ {
if (!ImGui.CollapsingHeader("Further Content")) if (!ImUtf8.CollapsingHeader("Further Content"u8))
return; return;
using (var sets = ImRaii.TreeNode("UV Sets", ImGuiTreeNodeFlags.DefaultOpen)) using (var sets = ImUtf8.TreeNode("UV Sets"u8, ImGuiTreeNodeFlags.DefaultOpen))
{ {
if (sets) if (sets)
foreach (var set in Mtrl.UvSets) foreach (var set in Mtrl.UvSets)
ImRaii.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose(); ImUtf8.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose();
} }
using (var sets = ImRaii.TreeNode("Color Sets", ImGuiTreeNodeFlags.DefaultOpen)) using (var sets = ImUtf8.TreeNode("Color Sets"u8, ImGuiTreeNodeFlags.DefaultOpen))
{ {
if (sets) if (sets)
foreach (var set in Mtrl.ColorSets) foreach (var set in Mtrl.ColorSets)
ImRaii.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose(); ImUtf8.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose();
} }
if (Mtrl.AdditionalData.Length <= 0) if (Mtrl.AdditionalData.Length <= 0)
return; return;
using var t = ImRaii.TreeNode($"Additional Data (Size: {Mtrl.AdditionalData.Length})###AdditionalData"); using var t = ImUtf8.TreeNode($"Additional Data (Size: {Mtrl.AdditionalData.Length})###AdditionalData");
if (t) if (t)
Widget.DrawHexViewer(Mtrl.AdditionalData); Widget.DrawHexViewer(Mtrl.AdditionalData);
} }
public void Update() private void Update()
{ {
UpdateShaders(); UpdateShaders();
UpdateTextures(); UpdateTextures();
@ -187,12 +187,12 @@ public sealed partial class MtrlTab : IWritable, IDisposable
} }
public bool Valid public bool Valid
=> ShadersKnown && Mtrl.Valid; => _shadersKnown && Mtrl.Valid;
public byte[] Write() public byte[] Write()
{ {
var output = Mtrl.Clone(); var output = Mtrl.Clone();
output.GarbageCollect(AssociatedShpk, SamplerIds); output.GarbageCollect(_associatedShpk, SamplerIds);
return output.Write(); return output.Write();
} }

View file

@ -8,9 +8,16 @@ using Penumbra.Services;
namespace Penumbra.UI.AdvancedWindow.Materials; namespace Penumbra.UI.AdvancedWindow.Materials;
public sealed class MtrlTabFactory(IDataManager gameData, IFramework framework, ObjectManager objects, public sealed class MtrlTabFactory(
CharacterBaseDestructor characterBaseDestructor, StainService stainService, ResourceTreeFactory resourceTreeFactory, IDataManager gameData,
FileDialogService fileDialog, MaterialTemplatePickers materialTemplatePickers, Configuration config) : IUiService IFramework framework,
ObjectManager objects,
CharacterBaseDestructor characterBaseDestructor,
StainService stainService,
ResourceTreeFactory resourceTreeFactory,
FileDialogService fileDialog,
MaterialTemplatePickers materialTemplatePickers,
Configuration config) : IUiService
{ {
public MtrlTab Create(ModEditWindow edit, MtrlFile file, string filePath, bool writable) public MtrlTab Create(ModEditWindow edit, MtrlFile file, string filePath, bool writable)
=> new(gameData, framework, objects, characterBaseDestructor, stainService, resourceTreeFactory, fileDialog, => new(gameData, framework, objects, characterBaseDestructor, stainService, resourceTreeFactory, fileDialog,

View file

@ -3,6 +3,7 @@ using Dalamud.Interface.Utility;
using ImGuiNET; using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.UI.AdvancedWindow.Materials; using Penumbra.UI.AdvancedWindow.Materials;
namespace Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.AdvancedWindow;
@ -24,7 +25,7 @@ public partial class ModEditWindow
if (_editor.Files.Mdl.Count == 0) if (_editor.Files.Mdl.Count == 0)
return; return;
using var tab = ImRaii.TabItem("Material Reassignment"); using var tab = ImUtf8.TabItem("Material Reassignment"u8);
if (!tab) if (!tab)
return; return;
@ -32,45 +33,43 @@ public partial class ModEditWindow
MaterialSuffix.Draw(_editor, ImGuiHelpers.ScaledVector2(175, 0)); MaterialSuffix.Draw(_editor, ImGuiHelpers.ScaledVector2(175, 0));
ImGui.NewLine(); ImGui.NewLine();
using var child = ImRaii.Child("##mdlFiles", -Vector2.One, true); using var child = ImUtf8.Child("##mdlFiles"u8, -Vector2.One, true);
if (!child) if (!child)
return; return;
using var table = ImRaii.Table("##files", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.One); using var table = ImUtf8.Table("##files"u8, 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.One);
if (!table) if (!table)
return; 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(); ImGui.TableNextColumn();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Save.ToIconString(), iconSize, if (ImUtf8.IconButton(FontAwesomeIcon.Save, "Save the changed mdl file.\nUse at own risk!"u8, disabled: !info.Changed))
"Save the changed mdl file.\nUse at own risk!", !info.Changed, true))
info.Save(_editor.Compactor); info.Save(_editor.Compactor);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Recycle.ToIconString(), iconSize, if (ImUtf8.IconButton(FontAwesomeIcon.Recycle, "Restore current changes to default."u8, disabled: !info.Changed))
"Restore current changes to default.", !info.Changed, true))
info.Restore(); info.Restore();
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TextUnformatted(info.Path.FullName[(Mod!.ModPath.FullName.Length + 1)..]); ImUtf8.Text(info.Path.InternalName.Span[(Mod!.ModPath.FullName.Length + 1)..]);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.SetNextItemWidth(400 * UiHelpers.Scale); ImGui.SetNextItemWidth(400 * UiHelpers.Scale);
var tmp = info.CurrentMaterials[0]; var tmp = info.CurrentMaterials[0];
if (ImGui.InputText("##0", ref tmp, 64)) if (ImUtf8.InputText("##0"u8, ref tmp))
info.SetMaterial(tmp, 0); info.SetMaterial(tmp, 0);
for (var i = 1; i < info.Count; ++i) for (var i = 1; i < info.Count; ++i)
{ {
using var id2 = ImUtf8.PushId(i);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.SetNextItemWidth(400 * UiHelpers.Scale); ImGui.SetNextItemWidth(400 * UiHelpers.Scale);
tmp = info.CurrentMaterials[i]; tmp = info.CurrentMaterials[i];
if (ImGui.InputText($"##{i}", ref tmp, 64)) if (ImUtf8.InputText(""u8, ref tmp))
info.SetMaterial(tmp, i); info.SetMaterial(tmp, i);
} }
} }

View file

@ -10,7 +10,6 @@ using Penumbra.GameData.Interop;
using Penumbra.String; using Penumbra.String;
using static Penumbra.GameData.Files.ShpkFile; using static Penumbra.GameData.Files.ShpkFile;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.GameData.Files.ShaderStructs;
using OtterGui.Text; using OtterGui.Text;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -56,21 +55,17 @@ public partial class ModEditWindow
private static void DrawShaderPackageSummary(ShpkTab tab) private static void DrawShaderPackageSummary(ShpkTab tab)
{ {
if (tab.Shpk.IsLegacy) if (tab.Shpk.IsLegacy)
{
ImUtf8.Text("This legacy shader package will not work in the current version of the game. Do not attempt to load it.", ImUtf8.Text("This legacy shader package will not work in the current version of the game. Do not attempt to load it.",
ImGuiUtil.HalfBlendText(0x80u)); // Half red ImGuiUtil.HalfBlendText(0x80u)); // Half red
} ImUtf8.Text(tab.Header);
ImGui.TextUnformatted(tab.Header);
if (!tab.Shpk.Disassembled) if (!tab.Shpk.Disassembled)
{
ImUtf8.Text("Your system doesn't support disassembling shaders. Some functionality will be missing.", ImUtf8.Text("Your system doesn't support disassembling shaders. Some functionality will be missing.",
ImGuiUtil.HalfBlendText(0x80u)); // Half red ImGuiUtil.HalfBlendText(0x80u)); // Half red
}
} }
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 (!ImUtf8.Button($"Export Shader Program Blob ({shader.Blob.Length} bytes)"))
return; return;
var defaultName = objectName[0] switch var defaultName = objectName[0] switch
@ -106,7 +101,7 @@ public partial class ModEditWindow
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 (!ImUtf8.Button("Replace Shader Program Blob"u8))
return; return;
tab.FileDialog.OpenFilePicker($"Replace {objectName} #{idx} Program Blob...", "Shader Program Blobs{.o,.cso,.dxbc,.dxil}", tab.FileDialog.OpenFilePicker($"Replace {objectName} #{idx} Program Blob...", "Shader Program Blobs{.o,.cso,.dxbc,.dxil}",
@ -145,8 +140,8 @@ public partial class ModEditWindow
private static unsafe void DrawRawDisassembly(Shader shader) private static unsafe void DrawRawDisassembly(Shader shader)
{ {
using var t2 = ImRaii.TreeNode("Raw Program Disassembly"); using var tree = ImUtf8.TreeNode("Raw Program Disassembly"u8);
if (!t2) if (!tree)
return; return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
@ -164,31 +159,34 @@ public partial class ModEditWindow
{ {
foreach (var (key, keyIdx) in shader.SystemValues!.WithIndex()) foreach (var (key, keyIdx) in shader.SystemValues!.WithIndex())
{ {
ImRaii.TreeNode($"Used with System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}", ImUtf8.TreeNode(
$"Used with System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
foreach (var (key, keyIdx) in shader.SceneValues!.WithIndex()) foreach (var (key, keyIdx) in shader.SceneValues!.WithIndex())
{ {
ImRaii.TreeNode($"Used with Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}", ImUtf8.TreeNode(
$"Used with Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
foreach (var (key, keyIdx) in shader.MaterialValues!.WithIndex()) foreach (var (key, keyIdx) in shader.MaterialValues!.WithIndex())
{ {
ImRaii.TreeNode($"Used with Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}", ImUtf8.TreeNode(
$"Used with Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
foreach (var (key, keyIdx) in shader.SubViewValues!.WithIndex()) foreach (var (key, keyIdx) in shader.SubViewValues!.WithIndex())
{ {
ImRaii.TreeNode($"Used with Sub-View Key #{keyIdx} \u2208 {{ {tab.NameSetToString(key)} }}", ImUtf8.TreeNode($"Used with Sub-View Key #{keyIdx} \u2208 {{ {tab.NameSetToString(key)} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
} }
} }
ImRaii.TreeNode($"Used in Passes: {tab.NameSetToString(shader.Passes)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode($"Used in Passes: {tab.NameSetToString(shader.Passes)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
private static void DrawShaderPackageFilterSection(ShpkTab tab) private static void DrawShaderPackageFilterSection(ShpkTab tab)
@ -215,19 +213,20 @@ public partial class ModEditWindow
{ {
if (values.PossibleValues == null) if (values.PossibleValues == null)
{ {
ImRaii.TreeNode(label, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode(label, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
return; return;
} }
using var node = ImRaii.TreeNode(label); using var node = ImUtf8.TreeNode(label);
if (!node) if (!node)
return; return;
foreach (var value in values.PossibleValues) foreach (var value in values.PossibleValues)
{ {
var contains = values.Contains(value); var contains = values.Contains(value);
if (!ImGui.Checkbox($"{tab.TryResolveName(value)}", ref contains)) if (!ImUtf8.Checkbox($"{tab.TryResolveName(value)}", ref contains))
continue; continue;
if (contains) if (contains)
{ {
if (values.AddExisting(value)) if (values.AddExisting(value))
@ -249,7 +248,7 @@ public partial class ModEditWindow
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 || !ImUtf8.CollapsingHeader($"{objectName}s"))
return false; return false;
var ret = false; var ret = false;
@ -259,7 +258,7 @@ public partial class ModEditWindow
if (!tab.IsFilterMatch(shader)) if (!tab.IsFilterMatch(shader))
continue; continue;
using var t = ImRaii.TreeNode($"{objectName} #{idx}"); using var t = ImUtf8.TreeNode($"{objectName} #{idx}");
if (!t) if (!t)
continue; continue;
@ -270,20 +269,20 @@ public partial class ModEditWindow
DrawShaderImportButton(tab, objectName, shaders, idx); DrawShaderImportButton(tab, objectName, shaders, idx);
} }
ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, false, true); ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, false, true);
ret |= DrawShaderPackageResourceArray("Samplers", "slot", false, shader.Samplers, false, true); ret |= DrawShaderPackageResourceArray("Samplers", "slot", false, shader.Samplers, false, true);
if (!tab.Shpk.IsLegacy) if (!tab.Shpk.IsLegacy)
ret |= DrawShaderPackageResourceArray("Textures", "slot", false, shader.Textures, false, true); ret |= DrawShaderPackageResourceArray("Textures", "slot", false, shader.Textures, false, true);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "slot", true, shader.Uavs, false, true); ret |= DrawShaderPackageResourceArray("Unordered Access Views", "slot", true, shader.Uavs, false, true);
if (shader.DeclaredInputs != 0) if (shader.DeclaredInputs != 0)
ImRaii.TreeNode($"Declared Inputs: {shader.DeclaredInputs}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode($"Declared Inputs: {shader.DeclaredInputs}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
if (shader.UsedInputs != 0) if (shader.UsedInputs != 0)
ImRaii.TreeNode($"Used Inputs: {shader.UsedInputs}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode($"Used Inputs: {shader.UsedInputs}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
if (shader.AdditionalHeader.Length > 8) if (shader.AdditionalHeader.Length > 8)
{ {
using var t2 = ImRaii.TreeNode($"Additional Header (Size: {shader.AdditionalHeader.Length})###AdditionalHeader"); using var t2 = ImUtf8.TreeNode($"Additional Header (Size: {shader.AdditionalHeader.Length})###AdditionalHeader");
if (t2) if (t2)
Widget.DrawHexViewer(shader.AdditionalHeader); Widget.DrawHexViewer(shader.AdditionalHeader);
} }
@ -313,23 +312,28 @@ public partial class ModEditWindow
var usedString = UsedComponentString(withSize, false, resource); var usedString = UsedComponentString(withSize, false, resource);
if (usedString.Length > 0) if (usedString.Length > 0)
{ {
ImRaii.TreeNode(hasFilter ? $"Globally Used: {usedString}" : $"Used: {usedString}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode(hasFilter ? $"Globally Used: {usedString}" : $"Used: {usedString}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
if (hasFilter) if (hasFilter)
{ {
var filteredUsedString = UsedComponentString(withSize, true, resource); var filteredUsedString = UsedComponentString(withSize, true, resource);
if (filteredUsedString.Length > 0) if (filteredUsedString.Length > 0)
ImRaii.TreeNode($"Used within Filters: {filteredUsedString}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode($"Used within Filters: {filteredUsedString}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose();
else else
ImRaii.TreeNode("Unused within Filters", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode("Unused within Filters"u8, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
} }
else else
ImRaii.TreeNode(hasFilter ? "Globally Unused" : "Unused", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); {
ImUtf8.TreeNode(hasFilter ? "Globally Unused"u8 : "Unused"u8, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
}
return ret; return ret;
} }
private static bool DrawShaderPackageResourceArray(string arrayName, string slotLabel, bool withSize, Resource[] resources, bool hasFilter, bool disabled) private static bool DrawShaderPackageResourceArray(string arrayName, string slotLabel, bool withSize, Resource[] resources, bool hasFilter,
bool disabled)
{ {
if (resources.Length == 0) if (resources.Length == 0)
return false; return false;
@ -345,8 +349,8 @@ public partial class ModEditWindow
var name = $"#{idx}: {buf.Name} (ID: 0x{buf.Id:X8}), {slotLabel}: {buf.Slot}" 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); + (withSize ? $", size: {buf.Size} registers###{idx}: {buf.Name} (ID: 0x{buf.Id:X8})" : string.Empty);
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var t2 = ImRaii.TreeNode(name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet); using var t2 = ImUtf8.TreeNode(name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
font.Dispose(); font.Pop();
if (t2) if (t2)
ret |= DrawShaderPackageResource(slotLabel, withSize, ref buf, hasFilter, disabled); ret |= DrawShaderPackageResource(slotLabel, withSize, ref buf, hasFilter, disabled);
} }
@ -361,7 +365,7 @@ public partial class ModEditWindow
+ new Vector2(ImGui.CalcTextSize(label).X + 3 * ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetFrameHeight(), + new Vector2(ImGui.CalcTextSize(label).X + 3 * ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetFrameHeight(),
ImGui.GetStyle().FramePadding.Y); ImGui.GetStyle().FramePadding.Y);
var ret = ImGui.CollapsingHeader(label); var ret = ImUtf8.CollapsingHeader(label);
ImGui.GetWindowDrawList() ImGui.GetWindowDrawList()
.AddText(UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGui.GetColorU32(ImGuiCol.Text), "Layout"); .AddText(UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGui.GetColorU32(ImGuiCol.Text), "Layout");
return ret; return ret;
@ -374,7 +378,7 @@ public partial class ModEditWindow
if (isSizeWellDefined) if (isSizeWellDefined)
return true; return true;
ImGui.TextUnformatted(materialParams.HasValue ImUtf8.Text(materialParams.HasValue
? $"Buffer size mismatch: {file.MaterialParamsSize} bytes ≠ {materialParams.Value.Size} registers ({materialParams.Value.Size << 4} bytes)" ? $"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; return false;
@ -382,7 +386,7 @@ public partial class ModEditWindow
private static bool DrawShaderPackageMaterialMatrix(ShpkTab tab, bool disabled) private static bool DrawShaderPackageMaterialMatrix(ShpkTab tab, bool disabled)
{ {
ImGui.TextUnformatted(tab.Shpk.Disassembled ImUtf8.Text(tab.Shpk.Disassembled
? "Parameter positions (continuations are grayed out, globally unused values are red, unused values within filters are yellow):" ? "Parameter positions (continuations are grayed out, globally unused values are red, unused values within filters are yellow):"
: "Parameter positions (continuations are grayed out):"); : "Parameter positions (continuations are grayed out):");
@ -398,10 +402,7 @@ public partial class ModEditWindow
ImGui.TableSetupColumn("w", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale); ImGui.TableSetupColumn("w", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale);
ImGui.TableHeadersRow(); ImGui.TableHeadersRow();
var textColorStart = ImGui.GetColorU32(ImGuiCol.Text); var textColorStart = ImGui.GetColorU32(ImGuiCol.Text);
var textColorCont = ImGuiUtil.HalfTransparent(textColorStart); // Half opacity
var textColorUnusedStart = ImGuiUtil.HalfBlend(textColorStart, 0x80u); // Half red
var textColorUnusedCont = ImGuiUtil.HalfTransparent(textColorUnusedStart);
var ret = false; var ret = false;
for (var i = 0; i < tab.Matrix.GetLength(0); ++i) for (var i = 0; i < tab.Matrix.GetLength(0); ++i)
@ -420,12 +421,12 @@ public partial class ModEditWindow
color = ImGuiUtil.HalfTransparent(color); // Half opacity color = ImGuiUtil.HalfTransparent(color); // Half opacity
using var _ = ImRaii.PushId(i * 4 + j); using var _ = ImRaii.PushId(i * 4 + j);
var deletable = !disabled && idx >= 0; var deletable = !disabled && idx >= 0;
using (var font = ImRaii.PushFont(UiBuilder.MonoFont, tooltip.Length > 0)) using (ImRaii.PushFont(UiBuilder.MonoFont, tooltip.Length > 0))
{ {
using (var c = ImRaii.PushColor(ImGuiCol.Text, color)) using (ImRaii.PushColor(ImGuiCol.Text, color))
{ {
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.Selectable(name); ImUtf8.Selectable(name);
if (deletable && ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl) 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);
@ -434,11 +435,11 @@ public partial class ModEditWindow
} }
} }
ImGuiUtil.HoverTooltip(tooltip); ImUtf8.HoverTooltip(tooltip);
} }
if (deletable) if (deletable)
ImGuiUtil.HoverTooltip("\nControl + Right-Click to remove."); ImUtf8.HoverTooltip("\nControl + Right-Click to remove."u8);
} }
} }
@ -450,7 +451,9 @@ public partial class ModEditWindow
if (!ImUtf8.Button("Export globally unused parameters as material dev-kit file"u8)) if (!ImUtf8.Button("Export globally unused parameters as material dev-kit file"u8))
return; return;
tab.FileDialog.OpenSavePicker("Export material dev-kit file", ".json", $"{Path.GetFileNameWithoutExtension(tab.FilePath)}.json", ".json", DoSave, null, false); tab.FileDialog.OpenSavePicker("Export material dev-kit file", ".json", $"{Path.GetFileNameWithoutExtension(tab.FilePath)}.json",
".json", DoSave, null, false);
return;
void DoSave(bool success, string path) void DoSave(bool success, string path)
{ {
@ -476,22 +479,22 @@ public partial class ModEditWindow
private static void DrawShaderPackageMisalignedParameters(ShpkTab tab) private static void DrawShaderPackageMisalignedParameters(ShpkTab tab)
{ {
using var t = ImRaii.TreeNode("Misaligned / Overflowing Parameters"); using var t = ImUtf8.TreeNode("Misaligned / Overflowing Parameters"u8);
if (!t) if (!t)
return; return;
using var _ = ImRaii.PushFont(UiBuilder.MonoFont); using var _ = ImRaii.PushFont(UiBuilder.MonoFont);
foreach (var name in tab.MalformedParameters) foreach (var name in tab.MalformedParameters)
ImRaii.TreeNode(name, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.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 s = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing);
using (var _ = ImRaii.PushFont(UiBuilder.MonoFont)) using (ImRaii.PushFont(UiBuilder.MonoFont))
{ {
ImGui.SetNextItemWidth(UiHelpers.Scale * 400); ImGui.SetNextItemWidth(UiHelpers.Scale * 400);
using var c = ImRaii.Combo("##Start", tab.Orphans[tab.NewMaterialParamStart].Name); using var c = ImUtf8.Combo("##Start", tab.Orphans[tab.NewMaterialParamStart].Name);
if (c) if (c)
foreach (var (start, idx) in tab.Orphans.WithIndex()) foreach (var (start, idx) in tab.Orphans.WithIndex())
{ {
@ -501,7 +504,7 @@ public partial class ModEditWindow
} }
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted("Start"); ImUtf8.Text("Start"u8);
} }
private static void DrawShaderPackageEndCombo(ShpkTab tab) private static void DrawShaderPackageEndCombo(ShpkTab tab)
@ -510,7 +513,7 @@ public partial class ModEditWindow
using (var _ = ImRaii.PushFont(UiBuilder.MonoFont)) using (var _ = ImRaii.PushFont(UiBuilder.MonoFont))
{ {
ImGui.SetNextItemWidth(UiHelpers.Scale * 400); ImGui.SetNextItemWidth(UiHelpers.Scale * 400);
using var c = ImRaii.Combo("##End", tab.Orphans[tab.NewMaterialParamEnd].Name); using var c = ImUtf8.Combo("##End", tab.Orphans[tab.NewMaterialParamEnd].Name);
if (c) if (c)
{ {
var current = tab.Orphans[tab.NewMaterialParamStart].Index; var current = tab.Orphans[tab.NewMaterialParamStart].Index;
@ -527,7 +530,7 @@ public partial class ModEditWindow
} }
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted("End"); ImUtf8.Text("End"u8);
} }
private static bool DrawShaderPackageNewParameter(ShpkTab tab) private static bool DrawShaderPackageNewParameter(ShpkTab tab)
@ -540,15 +543,14 @@ public partial class ModEditWindow
ImGui.SetNextItemWidth(UiHelpers.Scale * 400); ImGui.SetNextItemWidth(UiHelpers.Scale * 400);
var newName = tab.NewMaterialParamName.Value!; var newName = tab.NewMaterialParamName.Value!;
if (ImGui.InputText("Name", ref newName, 63)) if (ImUtf8.InputText("Name", ref newName))
tab.NewMaterialParamName = newName; tab.NewMaterialParamName = newName;
var tooltip = tab.UsedIds.Contains(tab.NewMaterialParamName.Crc32) var tooltip = tab.UsedIds.Contains(tab.NewMaterialParamName.Crc32)
? "The ID is already in use. Please choose a different name." ? "The ID is already in use. Please choose a different name."u8
: string.Empty; : ""u8;
if (!ImGuiUtil.DrawDisabledButton($"Add {tab.NewMaterialParamName} (0x{tab.NewMaterialParamName.Crc32:X8})", new Vector2(400 * UiHelpers.Scale, ImGui.GetFrameHeight()), if (!ImUtf8.ButtonEx($"Add {tab.NewMaterialParamName} (0x{tab.NewMaterialParamName.Crc32:X8})", tooltip,
tooltip, new Vector2(400 * UiHelpers.Scale, ImGui.GetFrameHeight()), tooltip.Length > 0))
tooltip.Length > 0))
return false; return false;
tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.AddItem(new MaterialParam tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.AddItem(new MaterialParam
@ -589,15 +591,15 @@ public partial class ModEditWindow
{ {
var ret = false; var ret = false;
if (!ImGui.CollapsingHeader("Shader Resources")) if (!ImUtf8.CollapsingHeader("Shader Resources"u8))
return false; return false;
var hasFilters = tab.FilterPopCount != tab.FilterMaximumPopCount; var hasFilters = tab.FilterPopCount != tab.FilterMaximumPopCount;
ret |= DrawShaderPackageResourceArray("Constant Buffers", "type", true, tab.Shpk.Constants, hasFilters, disabled); ret |= DrawShaderPackageResourceArray("Constant Buffers", "type", true, tab.Shpk.Constants, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Samplers", "type", false, tab.Shpk.Samplers, hasFilters, disabled); ret |= DrawShaderPackageResourceArray("Samplers", "type", false, tab.Shpk.Samplers, hasFilters, disabled);
if (!tab.Shpk.IsLegacy) if (!tab.Shpk.IsLegacy)
ret |= DrawShaderPackageResourceArray("Textures", "type", false, tab.Shpk.Textures, hasFilters, disabled); ret |= DrawShaderPackageResourceArray("Textures", "type", false, tab.Shpk.Textures, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "type", false, tab.Shpk.Uavs, hasFilters, disabled); ret |= DrawShaderPackageResourceArray("Unordered Access Views", "type", false, tab.Shpk.Uavs, hasFilters, disabled);
return ret; return ret;
} }
@ -607,18 +609,20 @@ public partial class ModEditWindow
if (keys.Count == 0) if (keys.Count == 0)
return; return;
using var t = ImRaii.TreeNode(arrayName); using var t = ImUtf8.TreeNode(arrayName);
if (!t) if (!t)
return; return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
foreach (var (key, idx) in keys.WithIndex()) foreach (var (key, idx) in keys.WithIndex())
{ {
using var t2 = ImRaii.TreeNode(withId ? $"#{idx}: {tab.TryResolveName(key.Id)} (0x{key.Id:X8})" : $"#{idx}"); using var t2 = ImUtf8.TreeNode(withId ? $"#{idx}: {tab.TryResolveName(key.Id)} (0x{key.Id:X8})" : $"#{idx}");
if (t2) if (t2)
{ {
ImRaii.TreeNode($"Default Value: {tab.TryResolveName(key.DefaultValue)} (0x{key.DefaultValue:X8})", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.TreeNode($"Default Value: {tab.TryResolveName(key.DefaultValue)} (0x{key.DefaultValue:X8})",
ImRaii.TreeNode($"Known Values: {tab.NameSetToString(key.Values, true)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
ImUtf8.TreeNode($"Known Values: {tab.NameSetToString(key.Values, true)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose();
} }
} }
} }
@ -628,7 +632,7 @@ public partial class ModEditWindow
if (tab.Shpk.Nodes.Length <= 0) if (tab.Shpk.Nodes.Length <= 0)
return; return;
using var t = ImRaii.TreeNode($"Nodes ({tab.Shpk.Nodes.Length})###Nodes"); using var t = ImUtf8.TreeNode($"Nodes ({tab.Shpk.Nodes.Length})###Nodes");
if (!t) if (!t)
return; return;
@ -639,39 +643,44 @@ public partial class ModEditWindow
if (!tab.IsFilterMatch(node)) if (!tab.IsFilterMatch(node))
continue; continue;
using var t2 = ImRaii.TreeNode($"#{idx:D4}: Selector: 0x{node.Selector:X8}"); using var t2 = ImUtf8.TreeNode($"#{idx:D4}: Selector: 0x{node.Selector:X8}");
if (!t2) if (!t2)
continue; continue;
foreach (var (key, keyIdx) in node.SystemKeys.WithIndex()) foreach (var (key, keyIdx) in node.SystemKeys.WithIndex())
{ {
ImRaii.TreeNode($"System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SystemValues![keyIdx])} }}", ImUtf8.TreeNode(
$"System Key {tab.TryResolveName(tab.Shpk.SystemKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SystemValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
foreach (var (key, keyIdx) in node.SceneKeys.WithIndex()) foreach (var (key, keyIdx) in node.SceneKeys.WithIndex())
{ {
ImRaii.TreeNode($"Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SceneValues![keyIdx])} }}", ImUtf8.TreeNode(
$"Scene Key {tab.TryResolveName(tab.Shpk.SceneKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SceneValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
foreach (var (key, keyIdx) in node.MaterialKeys.WithIndex()) foreach (var (key, keyIdx) in node.MaterialKeys.WithIndex())
{ {
ImRaii.TreeNode($"Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.MaterialValues![keyIdx])} }}", ImUtf8.TreeNode(
$"Material Key {tab.TryResolveName(tab.Shpk.MaterialKeys[keyIdx].Id)} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.MaterialValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
foreach (var (key, keyIdx) in node.SubViewKeys.WithIndex()) foreach (var (key, keyIdx) in node.SubViewKeys.WithIndex())
{ {
ImRaii.TreeNode($"Sub-View Key #{keyIdx} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SubViewValues![keyIdx])} }}", ImUtf8.TreeNode(
$"Sub-View Key #{keyIdx} = {tab.TryResolveName(key)} / \u2208 {{ {tab.NameSetToString(node.SubViewValues![keyIdx])} }}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
} }
ImRaii.TreeNode($"Pass Indices: {string.Join(' ', node.PassIndices.Select(c => $"{c:X2}"))}", ImUtf8.TreeNode($"Pass Indices: {string.Join(' ', node.PassIndices.Select(c => $"{c:X2}"))}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
foreach (var (pass, passIdx) in node.Passes.WithIndex()) foreach (var (pass, passIdx) in node.Passes.WithIndex())
{ {
ImRaii.TreeNode($"Pass #{passIdx}: ID: {tab.TryResolveName(pass.Id)}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}", ImUtf8.TreeNode(
$"Pass #{passIdx}: ID: {tab.TryResolveName(pass.Id)}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}",
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet) ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose(); .Dispose();
} }
@ -680,7 +689,7 @@ public partial class ModEditWindow
private static void DrawShaderPackageSelection(ShpkTab tab) private static void DrawShaderPackageSelection(ShpkTab tab)
{ {
if (!ImGui.CollapsingHeader("Shader Selection")) if (!ImUtf8.CollapsingHeader("Shader Selection"u8))
return; return;
DrawKeyArray(tab, "System Keys", true, tab.Shpk.SystemKeys); DrawKeyArray(tab, "System Keys", true, tab.Shpk.SystemKeys);
@ -689,13 +698,13 @@ public partial class ModEditWindow
DrawKeyArray(tab, "Sub-View Keys", false, tab.Shpk.SubViewKeys); DrawKeyArray(tab, "Sub-View Keys", false, tab.Shpk.SubViewKeys);
DrawShaderPackageNodes(tab); DrawShaderPackageNodes(tab);
using var t = ImRaii.TreeNode($"Node Selectors ({tab.Shpk.NodeSelectors.Count})###NodeSelectors"); using var t = ImUtf8.TreeNode($"Node Selectors ({tab.Shpk.NodeSelectors.Count})###NodeSelectors");
if (t) if (t)
{ {
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
foreach (var selector in tab.Shpk.NodeSelectors) foreach (var selector in tab.Shpk.NodeSelectors)
{ {
ImRaii.TreeNode($"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet) ImUtf8.TreeNode($"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose(); .Dispose();
} }
} }
@ -703,14 +712,14 @@ public partial class ModEditWindow
private static void DrawOtherShaderPackageDetails(ShpkTab tab) private static void DrawOtherShaderPackageDetails(ShpkTab tab)
{ {
if (!ImGui.CollapsingHeader("Further Content")) if (!ImUtf8.CollapsingHeader("Further Content"u8))
return; return;
ImRaii.TreeNode($"Version: 0x{tab.Shpk.Version:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose(); ImUtf8.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"); using var t = ImUtf8.TreeNode($"Additional Data (Size: {tab.Shpk.AdditionalData.Length})###AdditionalData");
if (t) if (t)
Widget.DrawHexViewer(tab.Shpk.AdditionalData); Widget.DrawHexViewer(tab.Shpk.AdditionalData);
} }
@ -718,9 +727,9 @@ public partial class ModEditWindow
private static string UsedComponentString(bool withSize, bool filtered, in Resource resource) private static string UsedComponentString(bool withSize, bool filtered, in Resource resource)
{ {
var used = filtered ? resource.FilteredUsed : resource.Used; var used = filtered ? resource.FilteredUsed : resource.Used;
var usedDynamically = filtered ? resource.FilteredUsedDynamically : resource.UsedDynamically; var usedDynamically = filtered ? resource.FilteredUsedDynamically : resource.UsedDynamically;
var sb = new StringBuilder(256); var sb = new StringBuilder(256);
if (withSize) if (withSize)
{ {
foreach (var (components, i) in (used ?? Array.Empty<DisassembledShader.VectorComponents>()).WithIndex()) foreach (var (components, i) in (used ?? Array.Empty<DisassembledShader.VectorComponents>()).WithIndex())

View file

@ -21,11 +21,11 @@ public partial class ModEditWindow
public short NewMaterialParamStart; public short NewMaterialParamStart;
public short NewMaterialParamEnd; public short NewMaterialParamEnd;
public SharedSet<uint, uint>[] FilterSystemValues; public readonly SharedSet<uint, uint>[] FilterSystemValues;
public SharedSet<uint, uint>[] FilterSceneValues; public readonly SharedSet<uint, uint>[] FilterSceneValues;
public SharedSet<uint, uint>[] FilterMaterialValues; public readonly SharedSet<uint, uint>[] FilterMaterialValues;
public SharedSet<uint, uint>[] FilterSubViewValues; public readonly SharedSet<uint, uint>[] FilterSubViewValues;
public SharedSet<uint, uint> FilterPasses; public SharedSet<uint, uint> FilterPasses;
public readonly int FilterMaximumPopCount; public readonly int FilterMaximumPopCount;
public int FilterPopCount; public int FilterPopCount;
@ -46,6 +46,7 @@ public partial class ModEditWindow
{ {
Shpk = new ShpkFile(bytes, false); Shpk = new ShpkFile(bytes, false);
} }
FilePath = filePath; FilePath = filePath;
Header = $"Shader Package for DirectX {(int)Shpk.DirectXVersion}"; Header = $"Shader Package for DirectX {(int)Shpk.DirectXVersion}";
@ -105,13 +106,21 @@ public partial class ModEditWindow
_nameSetWithIdsCache.Clear(); _nameSetWithIdsCache.Clear();
} }
public void UpdateNameCache() private void UpdateNameCache()
{ {
static void CollectResourceNames(Dictionary<uint, Name> nameCache, ShpkFile.Resource[] resources) CollectResourceNames(_nameCache, Shpk.Constants);
{ CollectResourceNames(_nameCache, Shpk.Samplers);
foreach (var resource in resources) CollectResourceNames(_nameCache, Shpk.Textures);
nameCache.TryAdd(resource.Id, resource.Name); CollectResourceNames(_nameCache, Shpk.Uavs);
}
CollectKeyNames(_nameCache, Shpk.SystemKeys);
CollectKeyNames(_nameCache, Shpk.SceneKeys);
CollectKeyNames(_nameCache, Shpk.MaterialKeys);
CollectKeyNames(_nameCache, Shpk.SubViewKeys);
_nameSetCache.Clear();
_nameSetWithIdsCache.Clear();
return;
static void CollectKeyNames(Dictionary<uint, Name> nameCache, ShpkFile.Key[] keys) static void CollectKeyNames(Dictionary<uint, Name> nameCache, ShpkFile.Key[] keys)
{ {
@ -128,18 +137,11 @@ public partial class ModEditWindow
} }
} }
CollectResourceNames(_nameCache, Shpk.Constants); static void CollectResourceNames(Dictionary<uint, Name> nameCache, ShpkFile.Resource[] resources)
CollectResourceNames(_nameCache, Shpk.Samplers); {
CollectResourceNames(_nameCache, Shpk.Textures); foreach (var resource in resources)
CollectResourceNames(_nameCache, Shpk.Uavs); nameCache.TryAdd(resource.Id, resource.Name);
}
CollectKeyNames(_nameCache, Shpk.SystemKeys);
CollectKeyNames(_nameCache, Shpk.SceneKeys);
CollectKeyNames(_nameCache, Shpk.MaterialKeys);
CollectKeyNames(_nameCache, Shpk.SubViewKeys);
_nameSetCache.Clear();
_nameSetWithIdsCache.Clear();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -151,6 +153,7 @@ public partial class ModEditWindow
var cache = withIds ? _nameSetWithIdsCache : _nameSetCache; var cache = withIds ? _nameSetWithIdsCache : _nameSetCache;
if (cache.TryGetValue(nameSet, out var nameSetStr)) if (cache.TryGetValue(nameSet, out var nameSetStr))
return nameSetStr; return nameSetStr;
if (withIds) if (withIds)
nameSetStr = string.Join(", ", nameSet.Select(id => $"{TryResolveName(id)} (0x{id:X8})")); nameSetStr = string.Join(", ", nameSet.Select(id => $"{TryResolveName(id)} (0x{id:X8})"));
else else
@ -186,7 +189,8 @@ public partial class ModEditWindow
var jEnd = ((param.ByteOffset + param.ByteSize - 1) >> 2) & 3; var jEnd = ((param.ByteOffset + param.ByteSize - 1) >> 2) & 3;
if ((param.ByteOffset & 0x3) != 0 || (param.ByteSize & 0x3) != 0) if ((param.ByteOffset & 0x3) != 0 || (param.ByteSize & 0x3) != 0)
{ {
MalformedParameters.Add($"ID: {TryResolveName(param.Id)} (0x{param.Id:X8}), offset: 0x{param.ByteOffset:X4}, size: 0x{param.ByteSize:X4}"); MalformedParameters.Add(
$"ID: {TryResolveName(param.Id)} (0x{param.Id:X8}), offset: 0x{param.ByteOffset:X4}, size: 0x{param.ByteSize:X4}");
continue; continue;
} }
@ -206,7 +210,8 @@ public partial class ModEditWindow
var tt = var tt =
$"{MtrlTab.MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2).Item1} ({TryResolveName(param.Id)}, 0x{param.Id:X8})"; $"{MtrlTab.MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2).Item1} ({TryResolveName(param.Id)}, 0x{param.Id:X8})";
if (component < defaultFloats.Length) if (component < defaultFloats.Length)
tt += $"\n\nDefault value: {defaultFloats[component]} ({defaults[component << 2]:X2} {defaults[(component << 2) | 1]:X2} {defaults[(component << 2) | 2]:X2} {defaults[(component << 2) | 3]:X2})"; tt +=
$"\n\nDefault value: {defaultFloats[component]} ({defaults[component << 2]:X2} {defaults[(component << 2) | 1]:X2} {defaults[(component << 2) | 2]:X2} {defaults[(component << 2) | 3]:X2})";
Matrix[i, j] = (TryResolveName(param.Id).ToString(), tt, (short)idx, 0); Matrix[i, j] = (TryResolveName(param.Id).ToString(), tt, (short)idx, 0);
} }
} }
@ -265,7 +270,8 @@ public partial class ModEditWindow
if (oldStart == linear) if (oldStart == linear)
newMaterialParamStart = (short)Orphans.Count; newMaterialParamStart = (short)Orphans.Count;
Orphans.Add(($"{materialParams?.Name ?? ShpkFile.MaterialParamsConstantName}{MtrlTab.MaterialParamName(false, linear)}", linear)); Orphans.Add(($"{materialParams?.Name ?? ShpkFile.MaterialParamsConstantName}{MtrlTab.MaterialParamName(false, linear)}",
linear));
} }
} }
@ -407,7 +413,6 @@ public partial class ModEditWindow
var unusedSlices = new JArray(); var unusedSlices = new JArray();
if (materialParameterUsage.Indices(start, length).Any()) if (materialParameterUsage.Indices(start, length).Any())
{
foreach (var (rgStart, rgEnd) in materialParameterUsage.Ranges(start, length, true)) foreach (var (rgStart, rgEnd) in materialParameterUsage.Ranges(start, length, true))
{ {
unusedSlices.Add(new JObject unusedSlices.Add(new JObject
@ -417,14 +422,11 @@ public partial class ModEditWindow
["Length"] = rgEnd - rgStart, ["Length"] = rgEnd - rgStart,
}); });
} }
}
else else
{
unusedSlices.Add(new JObject unusedSlices.Add(new JObject
{ {
["Type"] = "Hidden", ["Type"] = "Hidden",
}); });
}
dkConstants[param.Id.ToString()] = unusedSlices; dkConstants[param.Id.ToString()] = unusedSlices;
} }