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 int _colorTableSelectedPair = 0;
private int _colorTableSelectedPair;
private bool DrawColorTable(ColorTable table, ColorDyeTable? dyeTable, bool disabled)
{
@ -23,25 +23,27 @@ public partial class MtrlTab
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 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 j = 0; j < 8; ++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;
}
@ -79,12 +81,10 @@ public partial class MtrlTab
private bool DrawColorTablePairEditor(ColorTable table, ColorDyeTable? dyeTable, bool disabled)
{
var retA = false;
var retB = false;
ref var rowA = ref table[_colorTableSelectedPair << 1];
ref var rowB = ref table[(_colorTableSelectedPair << 1) | 1];
var dyeA = dyeTable != null ? dyeTable[_colorTableSelectedPair << 1] : default;
var dyeB = dyeTable != null ? dyeTable[(_colorTableSelectedPair << 1) | 1] : default;
var retA = false;
var retB = false;
var dyeA = dyeTable?[_colorTableSelectedPair << 1] ?? default;
var dyeB = dyeTable?[(_colorTableSelectedPair << 1) | 1] ?? default;
var previewDyeA = _stainService.GetStainCombo(dyeA.Channel).CurrentSelection.Key;
var previewDyeB = _stainService.GetStainCombo(dyeB.Channel).CurrentSelection.Key;
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 dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("ColorsA"u8))
using (ImUtf8.PushId("ColorsA"u8))
{
retA |= DrawColors(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next();
using (var id = ImUtf8.PushId("ColorsB"u8))
using (ImUtf8.PushId("ColorsB"u8))
{
retB |= DrawColors(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
}
DrawHeader(" Physical Parameters"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{
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);
}
columns.Next();
using (var id = ImUtf8.PushId("PbrB"u8))
using (ImUtf8.PushId("PbrB"u8))
{
retB |= DrawPbr(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
}
DrawHeader(" Sheen Layer Parameters"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{
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);
}
columns.Next();
using (var id = ImUtf8.PushId("SheenB"u8))
using (ImUtf8.PushId("SheenB"u8))
{
retB |= DrawSheen(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
}
DrawHeader(" Pair Blending"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{
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);
}
columns.Next();
using (var id = ImUtf8.PushId("BlendingB"u8))
using (ImUtf8.PushId("BlendingB"u8))
{
retB |= DrawBlending(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
}
DrawHeader(" Material Template"u8);
using (var columns = ImUtf8.Columns(2, "ColorTable"u8))
{
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);
}
columns.Next();
using (var id = ImUtf8.PushId("TemplateB"u8))
using (ImUtf8.PushId("TemplateB"u8))
{
retB |= DrawTemplate(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
}
if (dyeTable != null)
{
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);
using (var id = ImUtf8.PushId("DyeA"u8))
retA |= DrawDye(dyeTable, dyePackA, _colorTableSelectedPair << 1);
columns.Next();
using (var id = ImUtf8.PushId("DyeB"u8))
retB |= DrawDye(dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
retA |= DrawDye(dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next();
using (ImUtf8.PushId("DyeB"u8))
{
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 dis = ImRaii.Disabled(disabled);
using (var id = ImUtf8.PushId("FurtherA"u8))
using (ImUtf8.PushId("FurtherA"u8))
{
retA |= DrawFurther(table, dyeTable, dyePackA, _colorTableSelectedPair << 1);
}
columns.Next();
using (var id = ImUtf8.PushId("FurtherB"u8))
using (ImUtf8.PushId("FurtherB"u8))
{
retB |= DrawFurther(table, dyeTable, dyePackB, (_colorTableSelectedPair << 1) | 1);
}
}
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>
private static void DrawHeader(ReadOnlySpan<byte> label)
{
var headerColor = ImGui.GetColorU32(ImGuiCol.Header);
using var _ = ImRaii.PushColor(ImGuiCol.HeaderHovered, headerColor).Push(ImGuiCol.HeaderActive, headerColor);
var headerColor = ImGui.GetColorU32(ImGuiCol.Header);
using var _ = ImRaii.PushColor(ImGuiCol.HeaderHovered, headerColor).Push(ImGuiCol.HeaderActive, headerColor);
ImUtf8.CollapsingHeader(label, ImGuiTreeNodeFlags.Leaf);
}
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];
var dye = dyeTable != null ? dyeTable[rowIdx] : default;
var dye = dyeTable?[rowIdx] ?? default;
ret |= CtColorPicker("Diffuse Color"u8, default, row.DiffuseColor,
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)
{
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 ret = false;
var ret = false;
ref var row = ref table[rowIdx];
var dye = dyeTable != null ? dyeTable[rowIdx] : default;
var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize);
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)
{
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);
ImUtf8.SameLineInner();
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;
@ -276,11 +318,11 @@ public partial class MtrlTab
var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var itemSpacing = ImGui.GetStyle().ItemSpacing.X;
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];
var dye = dyeTable != null ? dyeTable[rowIdx] : default;
var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize);
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.SetNextItemWidth(scalarSize + itemSpacing + 64.0f);
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.SameLine();
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));
if (dyeTable != null)
{
@ -326,10 +370,10 @@ public partial class MtrlTab
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 lineHeight = Math.Max(leftLineHeight, rightLineHeight);
var cursorPos = ImGui.GetCursorScreenPos();
var lineHeight = Math.Max(leftLineHeight, rightLineHeight);
var cursorPos = ImGui.GetCursorScreenPos();
ImGui.SetCursorScreenPos(cursorPos + new Vector2(0.0f, (lineHeight - leftLineHeight) * 0.5f));
ImGui.SetNextItemWidth(scalarSize + (itemSpacing + 64.0f) * 2.0f);
ret |= CtTileIndexPicker("###TileIndex"u8, default, row.TileIndex, false,
@ -337,9 +381,10 @@ public partial class MtrlTab
ImUtf8.SameLineInner();
ImUtf8.Text("Tile"u8);
ImGui.SameLine(subcolWidth);
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))
ImGui.SameLine(subColWidth);
ImGui.SetCursorScreenPos(ImGui.GetCursorScreenPos() with { Y = cursorPos.Y + (lineHeight - rightLineHeight) * 0.5f });
using (ImUtf8.Child("###TileProperties"u8,
new Vector2(ImGui.GetContentRegionAvail().X, float.Lerp(rightLineHeight, lineHeight, 0.5f))))
{
ImGui.Dummy(new Vector2(scalarSize, 0.0f));
ImUtf8.SameLineInner();
@ -350,7 +395,8 @@ public partial class MtrlTab
ret |= CtTileTransformMatrix(row.TileTransform, scalarSize, true,
m => table[rowIdx].TileTransform = m);
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);
}
@ -360,15 +406,20 @@ public partial class MtrlTab
private static bool DrawPbr(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{
var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
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 subColWidth = CalculateSubColumnWidth(2) + ImGui.GetStyle().ItemSpacing.X;
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];
var dye = dyeTable != null ? dyeTable[rowIdx] : default;
var dye = dyeTable?[rowIdx] ?? default;
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));
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);
}
ImGui.SameLine(subcolWidth);
ImGui.SameLine(subColWidth);
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));
if (dyeTable != null)
{
ImGui.SameLine(subcolWidth + dyeOffset);
ImGui.SameLine(subColWidth + dyeOffset);
ret |= CtApplyStainCheckbox("##dyeMetalness"u8, "Apply Metalness on Dye"u8, dye.Metalness,
b => dyeTable[rowIdx].Metalness = b);
ImUtf8.SameLineInner();
@ -400,12 +452,16 @@ public partial class MtrlTab
private static bool DrawSheen(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{
var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
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 subColWidth = CalculateSubColumnWidth(2) + ImGui.GetStyle().ItemSpacing.X;
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];
var dye = dyeTable != null ? dyeTable[rowIdx] : default;
var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize);
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);
}
ImGui.SameLine(subcolWidth);
ImGui.SameLine(subColWidth);
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));
if (dyeTable != null)
{
ImGui.SameLine(subcolWidth + dyeOffset);
ImGui.SameLine(subColWidth + dyeOffset);
ret |= CtApplyStainCheckbox("##dyeSheenTintRate"u8, "Apply Sheen Tint on Dye"u8, dye.SheenTintRate,
b => dyeTable[rowIdx].SheenTintRate = b);
ImUtf8.SameLineInner();
@ -435,7 +492,8 @@ public partial class MtrlTab
}
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));
if (dyeTable != null)
{
@ -444,7 +502,8 @@ public partial class MtrlTab
b => dyeTable[rowIdx].SheenAperture = b);
ImUtf8.SameLineInner();
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;
@ -453,12 +512,16 @@ public partial class MtrlTab
private static bool DrawFurther(ColorTable table, ColorDyeTable? dyeTable, DyePack? dyePack, int rowIdx)
{
var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
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 subColWidth = CalculateSubColumnWidth(2) + ImGui.GetStyle().ItemSpacing.X;
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];
var dye = dyeTable != null ? dyeTable[rowIdx] : default;
var dye = dyeTable?[rowIdx] ?? default;
ImGui.SetNextItemWidth(scalarSize);
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,
v => table[rowIdx].Scalar3 = v);
ImGui.SameLine(subcolWidth);
ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf("Field #7"u8, default, row.Scalar7, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
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,
v => table[rowIdx].Scalar15 = v);
ImGui.SameLine(subcolWidth);
ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf("Field #17"u8, default, row.Scalar17, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
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,
v => table[rowIdx].Scalar20 = v);
ImGui.SameLine(subcolWidth);
ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize);
ret |= CtDragHalf("Field #22"u8, default, row.Scalar22, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f,
v => table[rowIdx].Scalar22 = v);
@ -513,33 +576,32 @@ public partial class MtrlTab
{
var scalarSize = ColorTableScalarSize * UiHelpers.Scale;
var applyButtonWidth = ImUtf8.CalcTextSize("Apply Preview Dye"u8).X + ImGui.GetStyle().FramePadding.X * 2.0f;
var subcolWidth = CalculateSubcolumnWidth(2, applyButtonWidth);
var ret = false;
var subColWidth = CalculateSubColumnWidth(2, applyButtonWidth);
var ret = false;
ref var dye = ref dyeTable[rowIdx];
ImGui.SetNextItemWidth(scalarSize);
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));
ImGui.SameLine(subcolWidth);
ImGui.SameLine(subColWidth);
ImGui.SetNextItemWidth(scalarSize);
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;
ret = true;
}
ImUtf8.SameLineInner();
ImUtf8.Text("Dye Template"u8);
ImGui.SameLine(ImGui.GetContentRegionAvail().X - applyButtonWidth + ImGui.GetStyle().ItemSpacing.X);
using var dis = ImRaii.Disabled(!dyePack.HasValue);
if (ImUtf8.Button("Apply Preview Dye"u8))
{
ret |= Mtrl.ApplyDyeToRow(_stainService.GudStmFile, [
_stainService.StainCombo1.CurrentSelection.Key,
_stainService.StainCombo2.CurrentSelection.Key,
], rowIdx);
}
return ret;
}
@ -554,9 +616,9 @@ public partial class MtrlTab
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;
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
{
private static readonly float HalfMinValue = (float)Half.MinValue;
private static readonly float HalfMaxValue = (float)Half.MaxValue;
private static readonly float HalfEpsilon = (float)Half.Epsilon;
private static readonly float HalfMinValue = (float)Half.MinValue;
private static readonly float HalfMaxValue = (float)Half.MaxValue;
private static readonly float HalfEpsilon = (float)Half.Epsilon;
private static readonly FontAwesomeCheckbox ApplyStainCheckbox = new(FontAwesomeIcon.FillDrip);
@ -22,11 +22,11 @@ public partial class MtrlTab
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;
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
if (!ImGui.CollapsingHeader("Color Table", ImGuiTreeNodeFlags.DefaultOpen))
if (!ImUtf8.CollapsingHeader("Color Table"u8, ImGuiTreeNodeFlags.DefaultOpen))
return false;
ColorTableCopyAllClipboardButton();
@ -35,7 +35,7 @@ public partial class MtrlTab
if (!disabled)
{
ImGui.SameLine();
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0));
ImUtf8.IconDummy();
ImGui.SameLine();
ret |= ColorTableDyeableCheckbox();
}
@ -43,17 +43,18 @@ public partial class MtrlTab
if (Mtrl.DyeTable != null)
{
ImGui.SameLine();
ImGui.Dummy(ImGuiHelpers.ScaledVector2(20, 0));
ImUtf8.IconDummy();
ImGui.SameLine();
ret |= DrawPreviewDye(disabled);
}
ret |= Mtrl.Table switch
{
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 => DrawColorTable(table, Mtrl.DyeTable as ColorDyeTable, disabled),
_ => false,
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 => DrawColorTable(table, Mtrl.DyeTable as ColorDyeTable, disabled),
_ => false,
};
return ret;
@ -64,7 +65,7 @@ public partial class MtrlTab
if (Mtrl.Table == null)
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;
try
@ -178,16 +179,18 @@ public partial class MtrlTab
private bool ColorTableDyeableCheckbox()
{
var dyeable = Mtrl.DyeTable != null;
var ret = ImGui.Checkbox("Dyeable", ref dyeable);
var ret = ImUtf8.Checkbox("Dyeable"u8, ref dyeable);
if (ret)
{
Mtrl.DyeTable = dyeable ? Mtrl.Table switch
{
ColorTable => new ColorDyeTable(),
LegacyColorTable => new LegacyColorDyeTable(),
_ => null,
} : null;
Mtrl.DyeTable = dyeable
? Mtrl.Table switch
{
ColorTable => new ColorDyeTable(),
LegacyColorTable => new LegacyColorDyeTable(),
_ => null,
}
: null;
UpdateColorTablePreview();
}
@ -227,24 +230,27 @@ public partial class MtrlTab
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,
ImGui.GetFrameHeight() * Vector2.One, disabled || ColorTablePreviewers.Count == 0);
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,
ImGui.GetFrameHeight() * Vector2.One, disabled || _colorTablePreviewers.Count == 0);
if (ImGui.IsItemHovered())
HighlightColorTablePair(pairIdx);
else if (HighlightedColorTablePair == pairIdx)
else if (_highlightedColorTablePair == pairIdx)
CancelColorTableHighlight();
}
private static void CtBlendRect(Vector2 rcMin, Vector2 rcMax, uint topColor, uint bottomColor)
{
var style = ImGui.GetStyle();
var frameRounding = style.FrameRounding;
var style = ImGui.GetStyle();
var frameRounding = style.FrameRounding;
var frameThickness = style.FrameBorderSize;
var borderColor = ImGui.GetColorU32(ImGuiCol.Border);
var drawList = ImGui.GetWindowDrawList();
var borderColor = ImGui.GetColorU32(ImGuiCol.Border);
var drawList = ImGui.GetWindowDrawList();
if (topColor == bottomColor)
{
drawList.AddRectFilled(rcMin, rcMax, topColor, frameRounding, ImDrawFlags.RoundCornersDefault);
}
else
{
drawList.AddRectFilled(
@ -258,10 +264,12 @@ public partial class MtrlTab
rcMin with { Y = float.Lerp(rcMin.Y, rcMax.Y, 2.0f / 3) }, rcMax,
bottomColor, frameRounding, ImDrawFlags.RoundCornersBottomLeft | ImDrawFlags.RoundCornersBottomRight);
}
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 inputSqrt = PseudoSqrtRgb((Vector3)current);
@ -291,10 +299,13 @@ public partial class MtrlTab
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)
{
CtColorPicker(label, description, current.Value, Nop, letter);
}
else
{
var tmp = Vector4.Zero;
@ -308,8 +319,8 @@ public partial class MtrlTab
if (letter.Length > 0 && ImGui.IsItemVisible())
{
var textSize = ImUtf8.CalcTextSize(letter);
var center = ImGui.GetItemRectMin() + (ImGui.GetItemRectSize() - textSize) / 2;
var textSize = ImUtf8.CalcTextSize(letter);
var center = ImGui.GetItemRectMin() + (ImGui.GetItemRectSize() - textSize) / 2;
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)
{
var tmp = current;
var tmp = current;
var result = ApplyStainCheckbox.Draw(label, ref tmp);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == current)
@ -329,68 +340,79 @@ public partial class MtrlTab
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);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result)
return false;
var newValue = (Half)tmp;
if (newValue == value)
return false;
setter(newValue);
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);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result)
return false;
var newValue = (Half)tmp;
if (newValue == value)
return false;
value = newValue;
return true;
}
private static void CtDragHalf(ReadOnlySpan<byte> label, ReadOnlySpan<byte> description, Half? value, ReadOnlySpan<byte> format)
{
using var _ = ImRaii.Disabled();
var valueOrDefault = value ?? Half.Zero;
var floatValue = (float)valueOrDefault;
using var _ = ImRaii.Disabled();
var valueOrDefault = value ?? Half.Zero;
var floatValue = (float)valueOrDefault;
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);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == value)
return false;
setter(tmp);
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);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, description);
if (!result || tmp == value)
return false;
value = tmp;
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();
var valueOrDefault = value ?? T.Zero;
using var _ = ImRaii.Disabled();
var valueOrDefault = value ?? T.Zero;
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))
return false;
setter(value);
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))
return false;
setter(value);
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);
if (!ret || tmp == value)
return false;
setter(tmp);
}
else
{
value.Decompose(out var scale, out var rotation, out var shear);
rotation *= 180.0f / MathF.PI;
shear *= 180.0f / MathF.PI;
shear *= 180.0f / MathF.PI;
ImGui.SetNextItemWidth(floatSize);
var scaleXChanged = CtDragScalar("##TileScaleU"u8, "Tile Scale U"u8, ref scale.X, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
var activated = ImGui.IsItemActivated();
var deactivated = ImGui.IsItemDeactivated();
ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(floatSize);
var scaleYChanged = CtDragScalar("##TileScaleV"u8, "Tile Scale V"u8, ref scale.Y, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
var scaleYChanged = CtDragScalar("##TileScaleV"u8, "Tile Scale V"u8, ref scale.Y, "%.2f"u8, HalfMinValue, HalfMaxValue, 0.1f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
if (!twoRowLayout)
ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(floatSize);
var rotationChanged = CtDragScalar("##TileRotation"u8, "Tile Rotation"u8, ref rotation, "%.0f°"u8, -180.0f, 180.0f, 1.0f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
var rotationChanged = CtDragScalar("##TileRotation"u8, "Tile Rotation"u8, ref rotation, "%.0f°"u8, -180.0f, 180.0f, 1.0f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(floatSize);
var shearChanged = CtDragScalar("##TileShear"u8, "Tile Shear"u8, ref shear, "%.0f°"u8, -90.0f, 90.0f, 1.0f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
var shearChanged = CtDragScalar("##TileShear"u8, "Tile Shear"u8, ref shear, "%.0f°"u8, -90.0f, 90.0f, 1.0f);
activated |= ImGui.IsItemActivated();
deactivated |= ImGui.IsItemDeactivated();
if (deactivated)
_pinnedTileTransform = null;
else if (activated)
@ -464,6 +490,7 @@ public partial class MtrlTab
ret = scaleXChanged | scaleYChanged | rotationChanged | shearChanged;
if (!ret)
return false;
if (_pinnedTileTransform.HasValue)
{
var (pinScale, pinRotation, pinShear) = _pinnedTileTransform.Value;
@ -476,11 +503,14 @@ public partial class MtrlTab
if (!shearChanged)
shear = pinShear;
}
var newValue = HalfMatrix2x2.Compose(scale, rotation * MathF.PI / 180.0f, shear * MathF.PI / 180.0f);
if (newValue == value)
return false;
setter(newValue);
}
return true;
}

View file

@ -35,7 +35,7 @@ public partial class MtrlTab
Constants.Clear();
string mpPrefix;
if (AssociatedShpk == null)
if (_associatedShpk == null)
{
mpPrefix = MaterialParamsConstantName.Value!;
var fcGroup = FindOrAddGroup(Constants, "Further Constants");
@ -51,12 +51,12 @@ public partial class MtrlTab
}
else
{
mpPrefix = AssociatedShpk.GetConstantById(MaterialParamsConstantId)?.Name ?? MaterialParamsConstantName.Value!;
mpPrefix = _associatedShpk.GetConstantById(MaterialParamsConstantId)?.Name ?? MaterialParamsConstantName.Value!;
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 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 handledElements = new IndexSet(values.Length, false);
@ -64,8 +64,8 @@ public partial class MtrlTab
if (dkData != null)
foreach (var dkConstant in dkData)
{
var offset = (int)dkConstant.EffectiveByteOffset;
var length = values.Length - offset;
var offset = (int)dkConstant.EffectiveByteOffset;
var length = values.Length - offset;
var constantSize = dkConstant.EffectiveByteSize;
if (constantSize.HasValue)
length = Math.Min(length, (int)constantSize.Value);
@ -86,7 +86,6 @@ public partial class MtrlTab
foreach (var (start, end) in handledElements.Ranges(complement: true))
{
if (start == 0 && end == values.Length && end - start <= 16)
{
if (name.Value != null)
{
fcGroup.Add((
@ -94,7 +93,6 @@ public partial class MtrlTab
constantIndex, 0..values.Length, string.Empty, true, DefaultConstantEditorFor(name)));
continue;
}
}
if ((shpkConstant.ByteOffset & 0x3) == 0 && (shpkConstant.ByteSize & 0x3) == 0)
{
@ -105,7 +103,8 @@ public partial class MtrlTab
var rangeEnd = Math.Min(i + 16, end);
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((
$"{autoName.PadRight(autoNameMaxLength)} (0x{shpkConstant.Id:X8})",
constantIndex, rangeStart..rangeEnd, string.Empty, true, DefaultConstantEditorFor(name)));
@ -116,7 +115,8 @@ public partial class MtrlTab
{
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)));
}
}
@ -178,15 +178,16 @@ public partial class MtrlTab
ret = true;
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 canReset = AssociatedShpk?.MaterialParamsDefaults != null
var canReset = _associatedShpk?.MaterialParamsDefaults != null
? defaultValue.Length > 0 && !defaultValue.SequenceEqual(buffer[slice])
: buffer[slice].ContainsAnyExcept((byte)0);
ImUtf8.SameLineInner();
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;
if (defaultValue.Length > 0)

View file

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

View file

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

View file

@ -12,20 +12,20 @@ namespace Penumbra.UI.AdvancedWindow.Materials;
public partial class MtrlTab
{
public readonly List<LiveMaterialPreviewer> MaterialPreviewers = new(4);
public readonly List<LiveColorTablePreviewer> ColorTablePreviewers = new(4);
public int HighlightedColorTablePair = -1;
public readonly Stopwatch HighlightTime = new();
private readonly List<LiveMaterialPreviewer> _materialPreviewers = new(4);
private readonly List<LiveColorTablePreviewer> _colorTablePreviewers = new(4);
private int _highlightedColorTablePair = -1;
private readonly Stopwatch _highlightTime = new();
private void DrawMaterialLivePreviewRebind(bool disabled)
{
if (disabled)
return;
if (ImGui.Button("Reload live preview"))
if (ImUtf8.Button("Reload live preview"u8))
BindToMaterialInstances();
if (MaterialPreviewers.Count != 0 || ColorTablePreviewers.Count != 0)
if (_materialPreviewers.Count != 0 || _colorTablePreviewers.Count != 0)
return;
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);
}
public unsafe void BindToMaterialInstances()
private unsafe void BindToMaterialInstances()
{
UnbindFromMaterialInstances();
@ -50,7 +50,7 @@ public partial class MtrlTab
try
{
MaterialPreviewers.Add(new LiveMaterialPreviewer(_objects, materialInfo));
_materialPreviewers.Add(new LiveMaterialPreviewer(_objects, materialInfo));
foundMaterials.Add((nint)material);
}
catch (InvalidOperationException)
@ -68,7 +68,7 @@ public partial class MtrlTab
{
try
{
ColorTablePreviewers.Add(new LiveColorTablePreviewer(_objects, _framework, materialInfo));
_colorTablePreviewers.Add(new LiveColorTablePreviewer(_objects, _framework, materialInfo));
}
catch (InvalidOperationException)
{
@ -81,53 +81,53 @@ public partial class MtrlTab
private void UnbindFromMaterialInstances()
{
foreach (var previewer in MaterialPreviewers)
foreach (var previewer in _materialPreviewers)
previewer.Dispose();
MaterialPreviewers.Clear();
_materialPreviewers.Clear();
foreach (var previewer in ColorTablePreviewers)
foreach (var previewer in _colorTablePreviewers)
previewer.Dispose();
ColorTablePreviewers.Clear();
_colorTablePreviewers.Clear();
}
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)
continue;
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)
continue;
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);
}
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);
}
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);
}
@ -145,14 +145,14 @@ public partial class MtrlTab
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;
HighlightTime.Restart();
_highlightedColorTablePair = pairIdx;
_highlightTime.Restart();
}
if (oldPairIdx >= 0)
@ -160,19 +160,6 @@ public partial class MtrlTab
UpdateColorTableRowPreview(oldPairIdx << 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)
{
@ -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;
if (Mtrl.Table == null)
@ -192,7 +193,7 @@ public partial class MtrlTab
var row = Mtrl.Table switch
{
LegacyColorTable legacyTable => new ColorTableRow(legacyTable[rowIdx]),
ColorTable table => table[rowIdx],
ColorTable table => table[rowIdx],
_ => throw new InvalidOperationException($"Unsupported color table type {Mtrl.Table.GetType()}"),
};
if (Mtrl.DyeTable != null)
@ -200,8 +201,8 @@ public partial class MtrlTab
var dyeRow = Mtrl.DyeTable switch
{
LegacyColorDyeTable legacyDyeTable => new ColorDyeTableRow(legacyDyeTable[rowIdx]),
ColorDyeTable dyeTable => dyeTable[rowIdx],
_ => throw new InvalidOperationException($"Unsupported color dye table type {Mtrl.DyeTable.GetType()}"),
ColorDyeTable dyeTable => dyeTable[rowIdx],
_ => throw new InvalidOperationException($"Unsupported color dye table type {Mtrl.DyeTable.GetType()}"),
};
if (dyeRow.Channel < StainService.ChannelCount)
{
@ -213,21 +214,21 @@ public partial class MtrlTab
}
}
if (HighlightedColorTablePair << 1 == rowIdx)
ApplyHighlight(ref row, ColorId.InGameHighlight, (float)HighlightTime.Elapsed.TotalSeconds);
else if (((HighlightedColorTablePair << 1) | 1) == rowIdx)
ApplyHighlight(ref row, ColorId.InGameHighlight2, (float)HighlightTime.Elapsed.TotalSeconds);
if (_highlightedColorTablePair << 1 == rowIdx)
ApplyHighlight(ref row, ColorId.InGameHighlight, (float)_highlightTime.Elapsed.TotalSeconds);
else if (((_highlightedColorTablePair << 1) | 1) == rowIdx)
ApplyHighlight(ref row, ColorId.InGameHighlight2, (float)_highlightTime.Elapsed.TotalSeconds);
foreach (var previewer in ColorTablePreviewers)
foreach (var previewer in _colorTablePreviewers)
{
row[..].CopyTo(previewer.GetColorRow(rowIdx));
previewer.ScheduleUpdate();
}
}
public void UpdateColorTablePreview()
private void UpdateColorTablePreview()
{
if (ColorTablePreviewers.Count == 0)
if (_colorTablePreviewers.Count == 0)
return;
if (Mtrl.Table == null)
@ -237,7 +238,8 @@ public partial class MtrlTab
var dyeRows = Mtrl.DyeTable != null ? ColorDyeTable.CastOrConvert(Mtrl.DyeTable) : null;
if (dyeRows != null)
{
ReadOnlySpan<StainId> stainIds = [
ReadOnlySpan<StainId> stainIds =
[
_stainService.StainCombo1.CurrentSelection.Key,
_stainService.StainCombo2.CurrentSelection.Key,
];
@ -245,13 +247,14 @@ public partial class MtrlTab
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) | 1], ColorId.InGameHighlight2, (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);
}
foreach (var previewer in ColorTablePreviewers)
foreach (var previewer in _colorTablePreviewers)
{
rows.AsHalves().CopyTo(previewer.ColorTable);
previewer.ScheduleUpdate();
@ -260,11 +263,11 @@ public partial class MtrlTab
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 baseColor = colorId.Value();
var level = (MathF.Sin(time * 2.0f * MathF.PI) + 2.0f) / 3.0f / 255.0f;
var baseColor = colorId.Value();
var color = level * new Vector3(baseColor & 0xFF, (baseColor >> 8) & 0xFF, (baseColor >> 16) & 0xFF);
var halfColor = (HalfColor)(color * color);
row.DiffuseColor = halfColor;
row.SpecularColor = halfColor;
row.EmissiveColor = halfColor;

View file

@ -21,8 +21,8 @@ public partial class MtrlTab
// Apricot shader packages are unlisted because
// 1. they cause severe performance/memory issues when calculating the effective shader set
// 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",
// "apricot_decal_dummy.shpk",
// "apricot_decal_ring.shpk",
@ -80,35 +80,35 @@ public partial class MtrlTab
"verticalfog.shpk",
"water.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;
public string ShaderHeader = "Shader###Shader";
public FullPath LoadedShpkPath = FullPath.Empty;
public string LoadedShpkPathName = string.Empty;
public string LoadedShpkDevkitPathName = string.Empty;
public string ShaderComment = string.Empty;
public ShpkFile? AssociatedShpk;
public bool ShpkLoading;
public JObject? AssociatedShpkDevkit;
private string _shaderHeader = "Shader###Shader";
private FullPath _loadedShpkPath = FullPath.Empty;
private string _loadedShpkPathName = string.Empty;
private string _loadedShpkDevkitPathName = string.Empty;
private string _shaderComment = string.Empty;
private ShpkFile? _associatedShpk;
private bool _shpkLoading;
private JObject? _associatedShpkDevkit;
public readonly string LoadedBaseDevkitPathName;
public readonly JObject? AssociatedBaseDevkit;
private readonly string _loadedBaseDevkitPathName;
private readonly JObject? _associatedBaseDevkit;
// Shader Key State
public readonly
private readonly
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);
public readonly HashSet<int> PixelShaders = new(16);
public bool ShadersKnown;
public ReadOnlyMemory<byte> ShadersString = UnknownShadersString;
private readonly HashSet<int> _vertexShaders = new(16);
private readonly HashSet<int> _pixelShaders = new(16);
private bool _shadersKnown;
private ReadOnlyMemory<byte> _shadersString = UnknownShadersString;
public string[] GetShpkNames()
private string[] GetShpkNames()
{
if (null != _shpkNames)
return _shpkNames;
@ -122,7 +122,7 @@ public partial class MtrlTab
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);
if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath))
@ -131,45 +131,45 @@ public partial class MtrlTab
return _edit.FindBestMatch(defaultGamePath);
}
public void LoadShpk(FullPath path)
private void LoadShpk(FullPath path)
=> Task.Run(() => DoLoadShpk(path));
private async Task DoLoadShpk(FullPath path)
{
ShadersKnown = false;
ShaderHeader = $"Shader ({Mtrl.ShaderPackage.Name})###Shader";
ShpkLoading = true;
_shadersKnown = false;
_shaderHeader = $"Shader ({Mtrl.ShaderPackage.Name})###Shader";
_shpkLoading = true;
try
{
var data = path.IsRooted
? await File.ReadAllBytesAsync(path.FullName)
: _gameData.GetFile(path.InternalName.ToString())?.Data;
LoadedShpkPath = path;
AssociatedShpk = data?.Length > 0 ? new ShpkFile(data) : throw new Exception("Failure to load file data.");
LoadedShpkPathName = path.ToPath();
_loadedShpkPath = path;
_associatedShpk = data?.Length > 0 ? new ShpkFile(data) : throw new Exception("Failure to load file data.");
_loadedShpkPathName = path.ToPath();
}
catch (Exception e)
{
LoadedShpkPath = FullPath.Empty;
LoadedShpkPathName = string.Empty;
AssociatedShpk = null;
Penumbra.Messager.NotificationMessage(e, $"Could not load {LoadedShpkPath.ToPath()}.", NotificationType.Error, false);
_loadedShpkPath = FullPath.Empty;
_loadedShpkPathName = string.Empty;
_associatedShpk = null;
Penumbra.Messager.NotificationMessage(e, $"Could not load {_loadedShpkPath.ToPath()}.", NotificationType.Error, false);
}
finally
{
ShpkLoading = false;
_shpkLoading = false;
}
if (LoadedShpkPath.InternalName.IsEmpty)
if (_loadedShpkPath.InternalName.IsEmpty)
{
AssociatedShpkDevkit = null;
LoadedShpkDevkitPathName = string.Empty;
_associatedShpkDevkit = null;
_loadedShpkDevkitPathName = string.Empty;
}
else
{
AssociatedShpkDevkit =
TryLoadShpkDevkit(Path.GetFileNameWithoutExtension(Mtrl.ShaderPackage.Name), out LoadedShpkDevkitPathName);
_associatedShpkDevkit =
TryLoadShpkDevkit(Path.GetFileNameWithoutExtension(Mtrl.ShaderPackage.Name), out _loadedShpkDevkitPathName);
}
UpdateShaderKeys();
@ -178,9 +178,9 @@ public partial class MtrlTab
private void UpdateShaderKeys()
{
ShaderKeys.Clear();
if (AssociatedShpk != null)
foreach (var key in AssociatedShpk.MaterialKeys)
_shaderKeys.Clear();
if (_associatedShpk != null)
foreach (var key in _associatedShpk.MaterialKeys)
{
var keyName = Names.KnownNames.TryResolve(key.Id);
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);
});
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));
}
else
@ -218,7 +218,7 @@ public partial class MtrlTab
{
var keyName = Names.KnownNames.TryResolve(key.Category);
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 = [];
byPassSets.Add(passId, passSet);
}
passSet.Add(shaderIndex);
}
VertexShaders.Clear();
PixelShaders.Clear();
_vertexShaders.Clear();
_pixelShaders.Clear();
var vertexShadersByPass = 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
{
ShadersKnown = true;
var systemKeySelectors = AllSelectors(AssociatedShpk.SystemKeys).ToArray();
var sceneKeySelectors = AllSelectors(AssociatedShpk.SceneKeys).ToArray();
var subViewKeySelectors = AllSelectors(AssociatedShpk.SubViewKeys).ToArray();
_shadersKnown = true;
var systemKeySelectors = AllSelectors(_associatedShpk.SystemKeys).ToArray();
var sceneKeySelectors = AllSelectors(_associatedShpk.SceneKeys).ToArray();
var subViewKeySelectors = AllSelectors(_associatedShpk.SubViewKeys).ToArray();
var materialKeySelector =
BuildSelector(AssociatedShpk.MaterialKeys.Select(key => Mtrl.GetOrAddShaderKey(key.Id, key.DefaultValue).Value));
BuildSelector(_associatedShpk.MaterialKeys.Select(key => Mtrl.GetOrAddShaderKey(key.Id, key.DefaultValue).Value));
foreach (var systemKeySelector in systemKeySelectors)
{
@ -261,38 +262,39 @@ public partial class MtrlTab
foreach (var subViewKeySelector in subViewKeySelectors)
{
var selector = BuildSelector(systemKeySelector, sceneKeySelector, materialKeySelector, subViewKeySelector);
var node = AssociatedShpk.GetNodeBySelector(selector);
var node = _associatedShpk.GetNodeBySelector(selector);
if (node.HasValue)
foreach (var pass in node.Value.Passes)
{
AddShader(VertexShaders, vertexShadersByPass, pass.Id, (int)pass.VertexShader);
AddShader(PixelShaders, pixelShadersByPass, pass.Id, (int)pass.PixelShader);
AddShader(_vertexShaders, vertexShadersByPass, pass.Id, (int)pass.VertexShader);
AddShader(_pixelShaders, pixelShadersByPass, pass.Id, (int)pass.PixelShader);
}
else
ShadersKnown = false;
_shadersKnown = false;
}
}
}
}
if (ShadersKnown)
if (_shadersKnown)
{
var builder = new StringBuilder();
foreach (var (passId, passVS) in vertexShadersByPass)
foreach (var (passId, passVertexShader) in vertexShadersByPass)
{
if (builder.Length > 0)
builder.Append("\n\n");
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)}");
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)}");
}
}
foreach (var (passId, passPS) in pixelShadersByPass)
foreach (var (passId, passPixelShader) in pixelShadersByPass)
{
if (vertexShadersByPass.ContainsKey(passId))
continue;
@ -301,22 +303,24 @@ public partial class MtrlTab
builder.Append("\n\n");
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)}");
}
ShadersString = Encoding.UTF8.GetBytes(builder.ToString());
_shadersString = Encoding.UTF8.GetBytes(builder.ToString());
}
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)
{
var ret = false;
if (ImGui.CollapsingHeader(ShaderHeader))
if (ImGui.CollapsingHeader(_shaderHeader))
{
ret |= DrawPackageNameInput(disabled);
ret |= DrawShaderFlagsInput(disabled);
@ -325,20 +329,17 @@ public partial class MtrlTab
DrawMaterialShaders();
}
if (!ShpkLoading && (AssociatedShpk == null || AssociatedShpkDevkit == null))
if (!_shpkLoading && (_associatedShpk == null || _associatedShpkDevkit == null))
{
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,
ImGuiUtil.HalfBlendText(0x80u)); // Half red
}
else
{
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,
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,
ImGuiUtil.HalfBlendText(0x8080u)); // Half yellow
}
}
return ret;
@ -358,14 +359,14 @@ public partial class MtrlTab
if (c)
foreach (var value in GetShpkNames())
{
if (ImGui.Selectable(value, value == Mtrl.ShaderPackage.Name))
{
Mtrl.ShaderPackage.Name = value;
ret = true;
AssociatedShpk = null;
LoadedShpkPath = FullPath.Empty;
LoadShpk(FindAssociatedShpk(out _, out _));
}
if (!ImGui.Selectable(value, value == Mtrl.ShaderPackage.Name))
continue;
Mtrl.ShaderPackage.Name = value;
ret = true;
_associatedShpk = null;
_loadedShpkPath = FullPath.Empty;
LoadShpk(FindAssociatedShpk(out _, out _));
}
return ret;
@ -391,23 +392,23 @@ public partial class MtrlTab
private void DrawCustomAssociations()
{
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: {LoadedShpkPathName}";
var devkitText = AssociatedShpkDevkit == null
: $"Associated .shpk file: {_loadedShpkPathName}";
var devkitText = _associatedShpkDevkit == null
? "Associated dev-kit file: None"
: $"Associated dev-kit file: {LoadedShpkDevkitPathName}";
var baseDevkitText = AssociatedBaseDevkit == null
: $"Associated dev-kit file: {_loadedShpkDevkitPathName}";
var baseDevkitText = _associatedBaseDevkit == null
? "Base dev-kit file: None"
: $"Base dev-kit file: {LoadedBaseDevkitPathName}";
: $"Base dev-kit file: {_loadedBaseDevkitPathName}";
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
ImGuiUtil.CopyOnClickSelectable(text, LoadedShpkPathName, tooltip);
ImGuiUtil.CopyOnClickSelectable(devkitText, LoadedShpkDevkitPathName, tooltip);
ImGuiUtil.CopyOnClickSelectable(baseDevkitText, LoadedBaseDevkitPathName, tooltip);
ImUtf8.CopyOnClickSelectable(text, _loadedShpkPathName, tooltip);
ImUtf8.CopyOnClickSelectable(devkitText, _loadedShpkDevkitPathName, 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) =>
{
if (success)
@ -416,15 +417,15 @@ public partial class MtrlTab
var moddedPath = FindAssociatedShpk(out var defaultPath, out var gamePath);
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Associate Default .shpk File", Vector2.Zero, moddedPath.ToPath(),
moddedPath.Equals(LoadedShpkPath)))
if (ImUtf8.ButtonEx("Associate Default .shpk File"u8, moddedPath.ToPath(), Vector2.Zero,
moddedPath.Equals(_loadedShpkPath)))
LoadShpk(moddedPath);
if (!gamePath.Path.Equals(moddedPath.InternalName))
{
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Associate Unmodded .shpk File", Vector2.Zero, defaultPath,
gamePath.Path.Equals(LoadedShpkPath.InternalName)))
if (ImUtf8.ButtonEx("Associate Unmodded .shpk File", defaultPath, Vector2.Zero,
gamePath.Path.Equals(_loadedShpkPath.InternalName)))
LoadShpk(new FullPath(gamePath));
}
@ -433,22 +434,23 @@ public partial class MtrlTab
private bool DrawMaterialShaderKeys(bool disabled)
{
if (ShaderKeys.Count == 0)
if (_shaderKeys.Count == 0)
return 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);
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 (currentLabel, _, currentDescription) =
values.FirstOrNull(v => v.Value == currentValue) ?? ($"0x{currentValue:X8}", currentValue, string.Empty);
if (!disabled && shpkKey.HasValue)
{
ImGui.SetNextItemWidth(UiHelpers.Scale * 250.0f);
using (var c = ImRaii.Combo($"##{key.Category:X8}", currentLabel))
using (var c = ImUtf8.Combo(""u8, currentLabel))
{
if (c)
foreach (var (valueLabel, value, valueDescription) in values)
@ -469,16 +471,16 @@ public partial class MtrlTab
if (description.Length > 0)
ImGuiUtil.LabeledHelpMarker(label, description);
else
ImGui.TextUnformatted(label);
ImUtf8.Text(label);
}
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);
}
else
{
ImGui.TextUnformatted($"{label}: {currentLabel}");
ImUtf8.Text($"{label}: {currentLabel}");
}
}
@ -487,19 +489,19 @@ public partial class MtrlTab
private void DrawMaterialShaders()
{
if (AssociatedShpk == null)
if (_associatedShpk == null)
return;
using (var node = ImUtf8.TreeNode("Candidate Shaders"u8))
{
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.TextUnformatted(ShaderComment);
ImUtf8.Text(_shaderComment);
}
}
}

View file

@ -23,7 +23,7 @@ public partial class MtrlTab
{
Textures.Clear();
SamplerIds.Clear();
if (AssociatedShpk == null)
if (_associatedShpk == null)
{
SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId));
if (Mtrl.Table != null)
@ -34,11 +34,11 @@ public partial class MtrlTab
}
else
{
foreach (var index in VertexShaders)
SamplerIds.UnionWith(AssociatedShpk.VertexShaders[index].Samplers.Select(sampler => sampler.Id));
foreach (var index in PixelShaders)
SamplerIds.UnionWith(AssociatedShpk.PixelShaders[index].Samplers.Select(sampler => sampler.Id));
if (!ShadersKnown)
foreach (var index in _vertexShaders)
SamplerIds.UnionWith(_associatedShpk.VertexShaders[index].Samplers.Select(sampler => sampler.Id));
foreach (var index in _pixelShaders)
SamplerIds.UnionWith(_associatedShpk.PixelShaders[index].Samplers.Select(sampler => sampler.Id));
if (!_shadersKnown)
{
SamplerIds.UnionWith(Mtrl.ShaderPackage.Samplers.Select(sampler => sampler.SamplerId));
if (Mtrl.Table != null)
@ -47,11 +47,11 @@ public partial class MtrlTab
foreach (var samplerId in SamplerIds)
{
var shpkSampler = AssociatedShpk.GetSamplerById(samplerId);
var shpkSampler = _associatedShpk.GetSamplerById(samplerId);
if (shpkSampler is not { Slot: 2 })
continue;
var dkData = TryGetShpkDevkitData<DevkitSampler>("Samplers", samplerId, true);
var dkData = TryGetShpkDevkitData<DevkitSampler>("Samplers", samplerId, true);
var hasDkLabel = !string.IsNullOrEmpty(dkData?.Label);
var sampler = Mtrl.GetOrAddSampler(samplerId, dkData?.DefaultTexture ?? string.Empty, out var samplerIndex);
@ -95,9 +95,12 @@ public partial class MtrlTab
private static ReadOnlySpan<byte> TextureAddressModeTooltip(TextureAddressMode addressMode)
=> 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.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.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.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,
_ => ""u8,
};
@ -167,7 +170,7 @@ public partial class MtrlTab
return ret;
}
private static bool ComboTextureAddressMode(ReadOnlySpan<byte> label, ref TextureAddressMode value)
{
using var c = ImUtf8.Combo(label, value.ToString());
@ -202,7 +205,7 @@ public partial class MtrlTab
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);
var addressMode = samplerFlags.UAddressMode;

View file

@ -4,7 +4,6 @@ using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.GameData;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Interop;
@ -21,7 +20,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
private const int ShpkPrefixLength = 16;
private static readonly CiByteString ShpkPrefix = CiByteString.FromSpanUnsafe("shader/sm5/shpk/"u8, true, true, true);
private readonly IDataManager _gameData;
private readonly IFramework _framework;
private readonly ObjectManager _objects;
@ -40,7 +39,8 @@ public sealed partial class MtrlTab : IWritable, IDisposable
private bool _updateOnNextFrame;
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)
{
_gameData = gameData;
@ -53,11 +53,11 @@ public sealed partial class MtrlTab : IWritable, IDisposable
_materialTemplatePickers = materialTemplatePickers;
_config = config;
_edit = edit;
Mtrl = file;
FilePath = filePath;
Writable = writable;
AssociatedBaseDevkit = TryLoadShpkDevkit("_base", out LoadedBaseDevkitPathName);
_edit = edit;
Mtrl = file;
FilePath = filePath;
Writable = writable;
_associatedBaseDevkit = TryLoadShpkDevkit("_base", out _loadedBaseDevkitPathName);
Update();
LoadShpk(FindAssociatedShpk(out _, out _));
if (writable)
@ -118,7 +118,7 @@ public sealed partial class MtrlTab : IWritable, IDisposable
using var dis = ImRaii.Disabled(disabled);
var tmp = shaderFlags.EnableTransparency;
if (ImGui.Checkbox("Enable Transparency", ref tmp))
if (ImUtf8.Checkbox("Enable Transparency"u8, ref tmp))
{
shaderFlags.EnableTransparency = tmp;
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);
tmp = shaderFlags.HideBackfaces;
if (ImGui.Checkbox("Hide Backfaces", ref tmp))
if (ImUtf8.Checkbox("Hide Backfaces"u8, ref tmp))
{
shaderFlags.HideBackfaces = tmp;
ret = true;
SetShaderPackageFlags(Mtrl.ShaderPackage.Flags);
}
if (ShpkLoading)
if (_shpkLoading)
{
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 _)
{
if (!ImGui.CollapsingHeader("Further Content"))
if (!ImUtf8.CollapsingHeader("Further Content"u8))
return;
using (var sets = ImRaii.TreeNode("UV Sets", ImGuiTreeNodeFlags.DefaultOpen))
using (var sets = ImUtf8.TreeNode("UV Sets"u8, ImGuiTreeNodeFlags.DefaultOpen))
{
if (sets)
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)
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)
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)
Widget.DrawHexViewer(Mtrl.AdditionalData);
}
public void Update()
private void Update()
{
UpdateShaders();
UpdateTextures();
@ -187,12 +187,12 @@ public sealed partial class MtrlTab : IWritable, IDisposable
}
public bool Valid
=> ShadersKnown && Mtrl.Valid;
=> _shadersKnown && Mtrl.Valid;
public byte[] Write()
{
var output = Mtrl.Clone();
output.GarbageCollect(AssociatedShpk, SamplerIds);
output.GarbageCollect(_associatedShpk, SamplerIds);
return output.Write();
}

View file

@ -8,9 +8,16 @@ using Penumbra.Services;
namespace Penumbra.UI.AdvancedWindow.Materials;
public sealed class MtrlTabFactory(IDataManager gameData, IFramework framework, ObjectManager objects,
CharacterBaseDestructor characterBaseDestructor, StainService stainService, ResourceTreeFactory resourceTreeFactory,
FileDialogService fileDialog, MaterialTemplatePickers materialTemplatePickers, Configuration config) : IUiService
public sealed class MtrlTabFactory(
IDataManager gameData,
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)
=> new(gameData, framework, objects, characterBaseDestructor, stainService, resourceTreeFactory, fileDialog,

View file

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

View file

@ -10,7 +10,6 @@ using Penumbra.GameData.Interop;
using Penumbra.String;
using static Penumbra.GameData.Files.ShpkFile;
using OtterGui.Widgets;
using Penumbra.GameData.Files.ShaderStructs;
using OtterGui.Text;
using Penumbra.GameData.Structs;
@ -56,21 +55,17 @@ public partial class ModEditWindow
private static void DrawShaderPackageSummary(ShpkTab tab)
{
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.",
ImGuiUtil.HalfBlendText(0x80u)); // Half red
}
ImGui.TextUnformatted(tab.Header);
ImUtf8.Text(tab.Header);
if (!tab.Shpk.Disassembled)
{
ImUtf8.Text("Your system doesn't support disassembling shaders. Some functionality will be missing.",
ImGuiUtil.HalfBlendText(0x80u)); // Half red
}
}
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;
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)
{
if (!ImGui.Button("Replace Shader Program Blob"))
if (!ImUtf8.Button("Replace Shader Program Blob"u8))
return;
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)
{
using var t2 = ImRaii.TreeNode("Raw Program Disassembly");
if (!t2)
using var tree = ImUtf8.TreeNode("Raw Program Disassembly"u8);
if (!tree)
return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
@ -164,31 +159,34 @@ public partial class ModEditWindow
{
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();
}
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();
}
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();
}
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();
}
}
}
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)
@ -215,19 +213,20 @@ public partial class ModEditWindow
{
if (values.PossibleValues == null)
{
ImRaii.TreeNode(label, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
ImUtf8.TreeNode(label, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
return;
}
using var node = ImRaii.TreeNode(label);
using var node = ImUtf8.TreeNode(label);
if (!node)
return;
foreach (var value in values.PossibleValues)
{
var contains = values.Contains(value);
if (!ImGui.Checkbox($"{tab.TryResolveName(value)}", ref contains))
if (!ImUtf8.Checkbox($"{tab.TryResolveName(value)}", ref contains))
continue;
if (contains)
{
if (values.AddExisting(value))
@ -249,7 +248,7 @@ public partial class ModEditWindow
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;
var ret = false;
@ -259,7 +258,7 @@ public partial class ModEditWindow
if (!tab.IsFilterMatch(shader))
continue;
using var t = ImRaii.TreeNode($"{objectName} #{idx}");
using var t = ImUtf8.TreeNode($"{objectName} #{idx}");
if (!t)
continue;
@ -270,20 +269,20 @@ public partial class ModEditWindow
DrawShaderImportButton(tab, objectName, shaders, idx);
}
ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, false, true);
ret |= DrawShaderPackageResourceArray("Samplers", "slot", false, shader.Samplers, false, true);
ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, false, true);
ret |= DrawShaderPackageResourceArray("Samplers", "slot", false, shader.Samplers, false, true);
if (!tab.Shpk.IsLegacy)
ret |= DrawShaderPackageResourceArray("Textures", "slot", false, shader.Textures, false, true);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "slot", true, shader.Uavs, false, true);
ret |= DrawShaderPackageResourceArray("Textures", "slot", false, shader.Textures, false, true);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "slot", true, shader.Uavs, false, true);
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)
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)
{
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)
Widget.DrawHexViewer(shader.AdditionalHeader);
}
@ -313,23 +312,28 @@ public partial class ModEditWindow
var usedString = UsedComponentString(withSize, false, resource);
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)
{
var filteredUsedString = UsedComponentString(withSize, true, resource);
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
ImRaii.TreeNode("Unused within Filters", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
ImUtf8.TreeNode("Unused within Filters"u8, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
}
}
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;
}
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)
return false;
@ -345,8 +349,8 @@ public partial class ModEditWindow
var name = $"#{idx}: {buf.Name} (ID: 0x{buf.Id:X8}), {slotLabel}: {buf.Slot}"
+ (withSize ? $", size: {buf.Size} registers###{idx}: {buf.Name} (ID: 0x{buf.Id:X8})" : string.Empty);
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
using var t2 = ImRaii.TreeNode(name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
font.Dispose();
using var t2 = ImUtf8.TreeNode(name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
font.Pop();
if (t2)
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(),
ImGui.GetStyle().FramePadding.Y);
var ret = ImGui.CollapsingHeader(label);
var ret = ImUtf8.CollapsingHeader(label);
ImGui.GetWindowDrawList()
.AddText(UiBuilder.DefaultFont, UiBuilder.DefaultFont.FontSize, pos, ImGui.GetColorU32(ImGuiCol.Text), "Layout");
return ret;
@ -374,7 +378,7 @@ public partial class ModEditWindow
if (isSizeWellDefined)
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, not a multiple of 16");
return false;
@ -382,7 +386,7 @@ public partial class ModEditWindow
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):");
@ -398,10 +402,7 @@ public partial class ModEditWindow
ImGui.TableSetupColumn("w", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale);
ImGui.TableHeadersRow();
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 textColorStart = ImGui.GetColorU32(ImGuiCol.Text);
var ret = false;
for (var i = 0; i < tab.Matrix.GetLength(0); ++i)
@ -420,12 +421,12 @@ public partial class ModEditWindow
color = ImGuiUtil.HalfTransparent(color); // Half opacity
using var _ = ImRaii.PushId(i * 4 + j);
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.Selectable(name);
ImUtf8.Selectable(name);
if (deletable && ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
{
tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.RemoveItems(idx);
@ -434,11 +435,11 @@ public partial class ModEditWindow
}
}
ImGuiUtil.HoverTooltip(tooltip);
ImUtf8.HoverTooltip(tooltip);
}
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))
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)
{
@ -476,22 +479,22 @@ public partial class ModEditWindow
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)
return;
using var _ = ImRaii.PushFont(UiBuilder.MonoFont);
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)
{
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);
using var c = ImRaii.Combo("##Start", tab.Orphans[tab.NewMaterialParamStart].Name);
using var c = ImUtf8.Combo("##Start", tab.Orphans[tab.NewMaterialParamStart].Name);
if (c)
foreach (var (start, idx) in tab.Orphans.WithIndex())
{
@ -501,7 +504,7 @@ public partial class ModEditWindow
}
ImGui.SameLine();
ImGui.TextUnformatted("Start");
ImUtf8.Text("Start"u8);
}
private static void DrawShaderPackageEndCombo(ShpkTab tab)
@ -510,7 +513,7 @@ public partial class ModEditWindow
using (var _ = ImRaii.PushFont(UiBuilder.MonoFont))
{
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)
{
var current = tab.Orphans[tab.NewMaterialParamStart].Index;
@ -527,7 +530,7 @@ public partial class ModEditWindow
}
ImGui.SameLine();
ImGui.TextUnformatted("End");
ImUtf8.Text("End"u8);
}
private static bool DrawShaderPackageNewParameter(ShpkTab tab)
@ -540,15 +543,14 @@ public partial class ModEditWindow
ImGui.SetNextItemWidth(UiHelpers.Scale * 400);
var newName = tab.NewMaterialParamName.Value!;
if (ImGui.InputText("Name", ref newName, 63))
if (ImUtf8.InputText("Name", ref newName))
tab.NewMaterialParamName = newName;
var tooltip = tab.UsedIds.Contains(tab.NewMaterialParamName.Crc32)
? "The ID is already in use. Please choose a different name."
: string.Empty;
if (!ImGuiUtil.DrawDisabledButton($"Add {tab.NewMaterialParamName} (0x{tab.NewMaterialParamName.Crc32:X8})", new Vector2(400 * UiHelpers.Scale, ImGui.GetFrameHeight()),
tooltip,
tooltip.Length > 0))
? "The ID is already in use. Please choose a different name."u8
: ""u8;
if (!ImUtf8.ButtonEx($"Add {tab.NewMaterialParamName} (0x{tab.NewMaterialParamName.Crc32:X8})", tooltip,
new Vector2(400 * UiHelpers.Scale, ImGui.GetFrameHeight()), tooltip.Length > 0))
return false;
tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.AddItem(new MaterialParam
@ -589,15 +591,15 @@ public partial class ModEditWindow
{
var ret = false;
if (!ImGui.CollapsingHeader("Shader Resources"))
if (!ImUtf8.CollapsingHeader("Shader Resources"u8))
return false;
var hasFilters = tab.FilterPopCount != tab.FilterMaximumPopCount;
ret |= DrawShaderPackageResourceArray("Constant Buffers", "type", true, tab.Shpk.Constants, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Samplers", "type", false, tab.Shpk.Samplers, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Constant Buffers", "type", true, tab.Shpk.Constants, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Samplers", "type", false, tab.Shpk.Samplers, hasFilters, disabled);
if (!tab.Shpk.IsLegacy)
ret |= DrawShaderPackageResourceArray("Textures", "type", false, tab.Shpk.Textures, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "type", false, tab.Shpk.Uavs, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Textures", "type", false, tab.Shpk.Textures, hasFilters, disabled);
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "type", false, tab.Shpk.Uavs, hasFilters, disabled);
return ret;
}
@ -607,18 +609,20 @@ public partial class ModEditWindow
if (keys.Count == 0)
return;
using var t = ImRaii.TreeNode(arrayName);
using var t = ImUtf8.TreeNode(arrayName);
if (!t)
return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
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)
{
ImRaii.TreeNode($"Default Value: {tab.TryResolveName(key.DefaultValue)} (0x{key.DefaultValue:X8})", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
ImRaii.TreeNode($"Known Values: {tab.NameSetToString(key.Values, true)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
ImUtf8.TreeNode($"Default Value: {tab.TryResolveName(key.DefaultValue)} (0x{key.DefaultValue:X8})",
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)
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)
return;
@ -639,39 +643,44 @@ public partial class ModEditWindow
if (!tab.IsFilterMatch(node))
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)
continue;
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();
}
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();
}
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();
}
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();
}
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();
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)
.Dispose();
}
@ -680,7 +689,7 @@ public partial class ModEditWindow
private static void DrawShaderPackageSelection(ShpkTab tab)
{
if (!ImGui.CollapsingHeader("Shader Selection"))
if (!ImUtf8.CollapsingHeader("Shader Selection"u8))
return;
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);
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)
{
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
foreach (var selector in tab.Shpk.NodeSelectors)
{
ImRaii.TreeNode($"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
ImUtf8.TreeNode($"#{selector.Value:D4}: Selector: 0x{selector.Key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
.Dispose();
}
}
@ -703,14 +712,14 @@ public partial class ModEditWindow
private static void DrawOtherShaderPackageDetails(ShpkTab tab)
{
if (!ImGui.CollapsingHeader("Further Content"))
if (!ImUtf8.CollapsingHeader("Further Content"u8))
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)
{
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)
Widget.DrawHexViewer(tab.Shpk.AdditionalData);
}
@ -718,9 +727,9 @@ public partial class ModEditWindow
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 sb = new StringBuilder(256);
var sb = new StringBuilder(256);
if (withSize)
{
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 NewMaterialParamEnd;
public SharedSet<uint, uint>[] FilterSystemValues;
public SharedSet<uint, uint>[] FilterSceneValues;
public SharedSet<uint, uint>[] FilterMaterialValues;
public SharedSet<uint, uint>[] FilterSubViewValues;
public SharedSet<uint, uint> FilterPasses;
public readonly SharedSet<uint, uint>[] FilterSystemValues;
public readonly SharedSet<uint, uint>[] FilterSceneValues;
public readonly SharedSet<uint, uint>[] FilterMaterialValues;
public readonly SharedSet<uint, uint>[] FilterSubViewValues;
public SharedSet<uint, uint> FilterPasses;
public readonly int FilterMaximumPopCount;
public int FilterPopCount;
@ -46,6 +46,7 @@ public partial class ModEditWindow
{
Shpk = new ShpkFile(bytes, false);
}
FilePath = filePath;
Header = $"Shader Package for DirectX {(int)Shpk.DirectXVersion}";
@ -105,13 +106,21 @@ public partial class ModEditWindow
_nameSetWithIdsCache.Clear();
}
public void UpdateNameCache()
private void UpdateNameCache()
{
static void CollectResourceNames(Dictionary<uint, Name> nameCache, ShpkFile.Resource[] resources)
{
foreach (var resource in resources)
nameCache.TryAdd(resource.Id, resource.Name);
}
CollectResourceNames(_nameCache, Shpk.Constants);
CollectResourceNames(_nameCache, Shpk.Samplers);
CollectResourceNames(_nameCache, Shpk.Textures);
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)
{
@ -128,18 +137,11 @@ public partial class ModEditWindow
}
}
CollectResourceNames(_nameCache, Shpk.Constants);
CollectResourceNames(_nameCache, Shpk.Samplers);
CollectResourceNames(_nameCache, Shpk.Textures);
CollectResourceNames(_nameCache, Shpk.Uavs);
CollectKeyNames(_nameCache, Shpk.SystemKeys);
CollectKeyNames(_nameCache, Shpk.SceneKeys);
CollectKeyNames(_nameCache, Shpk.MaterialKeys);
CollectKeyNames(_nameCache, Shpk.SubViewKeys);
_nameSetCache.Clear();
_nameSetWithIdsCache.Clear();
static void CollectResourceNames(Dictionary<uint, Name> nameCache, ShpkFile.Resource[] resources)
{
foreach (var resource in resources)
nameCache.TryAdd(resource.Id, resource.Name);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -151,6 +153,7 @@ public partial class ModEditWindow
var cache = withIds ? _nameSetWithIdsCache : _nameSetCache;
if (cache.TryGetValue(nameSet, out var nameSetStr))
return nameSetStr;
if (withIds)
nameSetStr = string.Join(", ", nameSet.Select(id => $"{TryResolveName(id)} (0x{id:X8})"));
else
@ -186,7 +189,8 @@ public partial class ModEditWindow
var jEnd = ((param.ByteOffset + param.ByteSize - 1) >> 2) & 3;
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;
}
@ -206,7 +210,8 @@ public partial class ModEditWindow
var tt =
$"{MtrlTab.MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2).Item1} ({TryResolveName(param.Id)}, 0x{param.Id:X8})";
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);
}
}
@ -265,7 +270,8 @@ public partial class ModEditWindow
if (oldStart == linear)
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();
if (materialParameterUsage.Indices(start, length).Any())
{
foreach (var (rgStart, rgEnd) in materialParameterUsage.Ranges(start, length, true))
{
unusedSlices.Add(new JObject
@ -417,14 +422,11 @@ public partial class ModEditWindow
["Length"] = rgEnd - rgStart,
});
}
}
else
{
unusedSlices.Add(new JObject
{
["Type"] = "Hidden",
});
}
dkConstants[param.Id.ToString()] = unusedSlices;
}