mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-15 12:27:46 +01:00
Improve ShPk tab
This commit is contained in:
parent
f4fe3605f0
commit
5323add662
3 changed files with 494 additions and 82 deletions
|
|
@ -1,7 +1,6 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using ImGuiNET;
|
||||
using Lumina.Misc;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
|
|
@ -11,6 +10,9 @@ 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;
|
||||
|
||||
namespace Penumbra.UI.AdvancedWindow;
|
||||
|
||||
|
|
@ -24,6 +26,9 @@ public partial class ModEditWindow
|
|||
{
|
||||
DrawShaderPackageSummary(file);
|
||||
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
DrawShaderPackageFilterSection(file);
|
||||
|
||||
var ret = false;
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
ret |= DrawShaderPackageShaderArray(file, "Vertex Shader", file.Shpk.VertexShaders, disabled);
|
||||
|
|
@ -50,15 +55,16 @@ 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);
|
||||
if (!tab.Shpk.Disassembled)
|
||||
{
|
||||
var textColor = ImGui.GetColorU32(ImGuiCol.Text);
|
||||
var textColorWarning = (textColor & 0xFF000000u) | ((textColor & 0x00FEFEFE) >> 1) | 0x80u; // Half red
|
||||
|
||||
using var c = ImRaii.PushColor(ImGuiCol.Text, textColorWarning);
|
||||
|
||||
ImGui.TextUnformatted("Your system doesn't support disassembling shaders. Some functionality will be missing.");
|
||||
ImUtf8.Text("Your system doesn't support disassembling shaders. Some functionality will be missing.",
|
||||
ImGuiUtil.HalfBlendText(0x80u)); // Half red
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +129,7 @@ public partial class ModEditWindow
|
|||
{
|
||||
shaders[idx].UpdateResources(tab.Shpk);
|
||||
tab.Shpk.UpdateResources();
|
||||
tab.UpdateFilteredUsed();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -149,6 +156,97 @@ public partial class ModEditWindow
|
|||
ImGuiInputTextFlags.ReadOnly, null, null);
|
||||
}
|
||||
|
||||
private static void DrawShaderUsage(ShpkTab tab, Shader shader)
|
||||
{
|
||||
using (var node = ImUtf8.TreeNode("Used with Shader Keys"u8))
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
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)} }}",
|
||||
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)} }}",
|
||||
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)} }}",
|
||||
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)} }}",
|
||||
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImRaii.TreeNode($"Used in Passes: {tab.NameSetToString(shader.Passes)}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
}
|
||||
|
||||
private static void DrawShaderPackageFilterSection(ShpkTab tab)
|
||||
{
|
||||
if (!ImUtf8.CollapsingHeader(tab.FilterPopCount == tab.FilterMaximumPopCount ? "Filters###Filters"u8 : "Filters (ACTIVE)###Filters"u8))
|
||||
return;
|
||||
|
||||
foreach (var (key, keyIdx) in tab.Shpk.SystemKeys.WithIndex())
|
||||
DrawShaderPackageFilterSet(tab, $"System Key {tab.TryResolveName(key.Id)}", ref tab.FilterSystemValues[keyIdx]);
|
||||
|
||||
foreach (var (key, keyIdx) in tab.Shpk.SceneKeys.WithIndex())
|
||||
DrawShaderPackageFilterSet(tab, $"Scene Key {tab.TryResolveName(key.Id)}", ref tab.FilterSceneValues[keyIdx]);
|
||||
|
||||
foreach (var (key, keyIdx) in tab.Shpk.MaterialKeys.WithIndex())
|
||||
DrawShaderPackageFilterSet(tab, $"Material Key {tab.TryResolveName(key.Id)}", ref tab.FilterMaterialValues[keyIdx]);
|
||||
|
||||
foreach (var (_, keyIdx) in tab.Shpk.SubViewKeys.WithIndex())
|
||||
DrawShaderPackageFilterSet(tab, $"Sub-View Key #{keyIdx}", ref tab.FilterSubViewValues[keyIdx]);
|
||||
|
||||
DrawShaderPackageFilterSet(tab, "Passes", ref tab.FilterPasses);
|
||||
}
|
||||
|
||||
private static void DrawShaderPackageFilterSet(ShpkTab tab, string label, ref SharedSet<uint, uint> values)
|
||||
{
|
||||
if (values.PossibleValues == null)
|
||||
{
|
||||
ImRaii.TreeNode(label, ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
using var node = ImRaii.TreeNode(label);
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
foreach (var value in values.PossibleValues)
|
||||
{
|
||||
var contains = values.Contains(value);
|
||||
if (!ImGui.Checkbox($"{tab.TryResolveName(value)}", ref contains))
|
||||
continue;
|
||||
if (contains)
|
||||
{
|
||||
if (values.AddExisting(value))
|
||||
{
|
||||
++tab.FilterPopCount;
|
||||
tab.UpdateFilteredUsed();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (values.Remove(value))
|
||||
{
|
||||
--tab.FilterPopCount;
|
||||
tab.UpdateFilteredUsed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DrawShaderPackageShaderArray(ShpkTab tab, string objectName, Shader[] shaders, bool disabled)
|
||||
{
|
||||
if (shaders.Length == 0 || !ImGui.CollapsingHeader($"{objectName}s"))
|
||||
|
|
@ -157,8 +255,11 @@ public partial class ModEditWindow
|
|||
var ret = false;
|
||||
for (var idx = 0; idx < shaders.Length; ++idx)
|
||||
{
|
||||
var shader = shaders[idx];
|
||||
using var t = ImRaii.TreeNode($"{objectName} #{idx}");
|
||||
var shader = shaders[idx];
|
||||
if (!tab.IsFilterMatch(shader))
|
||||
continue;
|
||||
|
||||
using var t = ImRaii.TreeNode($"{objectName} #{idx}");
|
||||
if (!t)
|
||||
continue;
|
||||
|
||||
|
|
@ -169,9 +270,11 @@ public partial class ModEditWindow
|
|||
DrawShaderImportButton(tab, objectName, shaders, idx);
|
||||
}
|
||||
|
||||
ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, true);
|
||||
ret |= DrawShaderPackageResourceArray("Samplers", "slot", false, shader.Samplers, true);
|
||||
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "slot", true, shader.Uavs, true);
|
||||
ret |= DrawShaderPackageResourceArray("Constant Buffers", "slot", true, shader.Constants, 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);
|
||||
|
||||
if (shader.DeclaredInputs != 0)
|
||||
ImRaii.TreeNode($"Declared Inputs: {shader.DeclaredInputs}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
|
|
@ -187,12 +290,14 @@ public partial class ModEditWindow
|
|||
|
||||
if (tab.Shpk.Disassembled)
|
||||
DrawRawDisassembly(shader);
|
||||
|
||||
DrawShaderUsage(tab, shader);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawShaderPackageResource(string slotLabel, bool withSize, ref Resource resource, bool disabled)
|
||||
private static bool DrawShaderPackageResource(string slotLabel, bool withSize, ref Resource resource, bool hasFilter, bool disabled)
|
||||
{
|
||||
var ret = false;
|
||||
if (!disabled)
|
||||
|
|
@ -205,16 +310,26 @@ public partial class ModEditWindow
|
|||
if (resource.Used == null)
|
||||
return ret;
|
||||
|
||||
var usedString = UsedComponentString(withSize, resource);
|
||||
var usedString = UsedComponentString(withSize, false, resource);
|
||||
if (usedString.Length > 0)
|
||||
ImRaii.TreeNode($"Used: {usedString}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
{
|
||||
ImRaii.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();
|
||||
else
|
||||
ImRaii.TreeNode("Unused within Filters", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
ImRaii.TreeNode("Unused", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
ImRaii.TreeNode(hasFilter ? "Globally Unused" : "Unused", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static bool DrawShaderPackageResourceArray(string arrayName, string slotLabel, bool withSize, Resource[] resources, bool disabled)
|
||||
private static bool DrawShaderPackageResourceArray(string arrayName, string slotLabel, bool withSize, Resource[] resources, bool hasFilter, bool disabled)
|
||||
{
|
||||
if (resources.Length == 0)
|
||||
return false;
|
||||
|
|
@ -233,7 +348,7 @@ public partial class ModEditWindow
|
|||
using var t2 = ImRaii.TreeNode(name, !disabled || buf.Used != null ? 0 : ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
|
||||
font.Dispose();
|
||||
if (t2)
|
||||
ret |= DrawShaderPackageResource(slotLabel, withSize, ref buf, disabled);
|
||||
ret |= DrawShaderPackageResource(slotLabel, withSize, ref buf, hasFilter, disabled);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -268,7 +383,7 @@ public partial class ModEditWindow
|
|||
private static bool DrawShaderPackageMaterialMatrix(ShpkTab tab, bool disabled)
|
||||
{
|
||||
ImGui.TextUnformatted(tab.Shpk.Disassembled
|
||||
? "Parameter positions (continuations are grayed out, unused values are red):"
|
||||
? "Parameter positions (continuations are grayed out, globally unused values are red, unused values within filters are yellow):"
|
||||
: "Parameter positions (continuations are grayed out):");
|
||||
|
||||
using var table = ImRaii.Table("##MaterialParamLayout", 5,
|
||||
|
|
@ -276,17 +391,17 @@ public partial class ModEditWindow
|
|||
if (!table)
|
||||
return false;
|
||||
|
||||
ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthFixed, 25 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("x", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("y", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("z", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("w", ImGuiTableColumnFlags.WidthFixed, 100 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthFixed, 40 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("x", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("y", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("z", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("w", ImGuiTableColumnFlags.WidthFixed, 250 * UiHelpers.Scale);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
var textColorStart = ImGui.GetColorU32(ImGuiCol.Text);
|
||||
var textColorCont = (textColorStart & 0x00FFFFFFu) | ((textColorStart & 0xFE000000u) >> 1); // Half opacity
|
||||
var textColorUnusedStart = (textColorStart & 0xFF000000u) | ((textColorStart & 0x00FEFEFE) >> 1) | 0x80u; // Half red
|
||||
var textColorUnusedCont = (textColorUnusedStart & 0x00FFFFFFu) | ((textColorUnusedStart & 0xFE000000u) >> 1);
|
||||
var textColorCont = ImGuiUtil.HalfTransparent(textColorStart); // Half opacity
|
||||
var textColorUnusedStart = ImGuiUtil.HalfBlend(textColorStart, 0x80u); // Half red
|
||||
var textColorUnusedCont = ImGuiUtil.HalfTransparent(textColorUnusedStart);
|
||||
|
||||
var ret = false;
|
||||
for (var i = 0; i < tab.Matrix.GetLength(0); ++i)
|
||||
|
|
@ -296,14 +411,13 @@ public partial class ModEditWindow
|
|||
for (var j = 0; j < 4; ++j)
|
||||
{
|
||||
var (name, tooltip, idx, colorType) = tab.Matrix[i, j];
|
||||
var color = colorType switch
|
||||
{
|
||||
ShpkTab.ColorType.Unused => textColorUnusedStart,
|
||||
ShpkTab.ColorType.Used => textColorStart,
|
||||
ShpkTab.ColorType.Continuation => textColorUnusedCont,
|
||||
ShpkTab.ColorType.Continuation | ShpkTab.ColorType.Used => textColorCont,
|
||||
_ => textColorStart,
|
||||
};
|
||||
var color = textColorStart;
|
||||
if (!colorType.HasFlag(ShpkTab.ColorType.Used))
|
||||
color = ImGuiUtil.HalfBlend(color, 0x80u); // Half red
|
||||
else if (!colorType.HasFlag(ShpkTab.ColorType.FilteredUsed))
|
||||
color = ImGuiUtil.HalfBlend(color, 0x8080u); // Half yellow
|
||||
if (colorType.HasFlag(ShpkTab.ColorType.Continuation))
|
||||
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))
|
||||
|
|
@ -331,6 +445,35 @@ public partial class ModEditWindow
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static void DrawShaderPackageMaterialDevkitExport(ShpkTab tab)
|
||||
{
|
||||
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);
|
||||
|
||||
void DoSave(bool success, string path)
|
||||
{
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(path, tab.ExportDevkit().ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(e, $"Could not export dev-kit for {Path.GetFileName(tab.FilePath)} to {path}.",
|
||||
NotificationType.Error, false);
|
||||
return;
|
||||
}
|
||||
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Material dev-kit file for {Path.GetFileName(tab.FilePath)} exported successfully to {Path.GetFileName(path)}.",
|
||||
NotificationType.Success, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawShaderPackageMisalignedParameters(ShpkTab tab)
|
||||
{
|
||||
using var t = ImRaii.TreeNode("Misaligned / Overflowing Parameters");
|
||||
|
|
@ -396,23 +539,25 @@ public partial class ModEditWindow
|
|||
DrawShaderPackageEndCombo(tab);
|
||||
|
||||
ImGui.SetNextItemWidth(UiHelpers.Scale * 400);
|
||||
if (ImGui.InputText("Name", ref tab.NewMaterialParamName, 63))
|
||||
tab.NewMaterialParamId = Crc32.Get(tab.NewMaterialParamName, 0xFFFFFFFFu);
|
||||
var newName = tab.NewMaterialParamName.Value!;
|
||||
if (ImGui.InputText("Name", ref newName, 63))
|
||||
tab.NewMaterialParamName = newName;
|
||||
|
||||
var tooltip = tab.UsedIds.Contains(tab.NewMaterialParamId)
|
||||
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 ID 0x{tab.NewMaterialParamId:X8}", new Vector2(400 * UiHelpers.Scale, ImGui.GetFrameHeight()),
|
||||
if (!ImGuiUtil.DrawDisabledButton($"Add {tab.NewMaterialParamName} (0x{tab.NewMaterialParamName.Crc32:X8})", new Vector2(400 * UiHelpers.Scale, ImGui.GetFrameHeight()),
|
||||
tooltip,
|
||||
tooltip.Length > 0))
|
||||
return false;
|
||||
|
||||
tab.Shpk.MaterialParams = tab.Shpk.MaterialParams.AddItem(new MaterialParam
|
||||
{
|
||||
Id = tab.NewMaterialParamId,
|
||||
Id = tab.NewMaterialParamName.Crc32,
|
||||
ByteOffset = (ushort)(tab.Orphans[tab.NewMaterialParamStart].Index << 2),
|
||||
ByteSize = (ushort)((tab.NewMaterialParamEnd - tab.NewMaterialParamStart + 1) << 2),
|
||||
});
|
||||
tab.AddNameToCache(tab.NewMaterialParamName);
|
||||
tab.Update();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -434,6 +579,9 @@ public partial class ModEditWindow
|
|||
else if (!disabled && sizeWellDefined)
|
||||
ret |= DrawShaderPackageNewParameter(tab);
|
||||
|
||||
if (tab.Shpk.Disassembled)
|
||||
DrawShaderPackageMaterialDevkitExport(tab);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -444,14 +592,17 @@ public partial class ModEditWindow
|
|||
if (!ImGui.CollapsingHeader("Shader Resources"))
|
||||
return false;
|
||||
|
||||
ret |= DrawShaderPackageResourceArray("Constant Buffers", "type", true, tab.Shpk.Constants, disabled);
|
||||
ret |= DrawShaderPackageResourceArray("Samplers", "type", false, tab.Shpk.Samplers, disabled);
|
||||
ret |= DrawShaderPackageResourceArray("Unordered Access Views", "type", false, tab.Shpk.Uavs, disabled);
|
||||
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);
|
||||
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);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void DrawKeyArray(string arrayName, bool withId, IReadOnlyCollection<Key> keys)
|
||||
private static void DrawKeyArray(ShpkTab tab, string arrayName, bool withId, IReadOnlyCollection<Key> keys)
|
||||
{
|
||||
if (keys.Count == 0)
|
||||
return;
|
||||
|
|
@ -463,12 +614,11 @@ public partial class ModEditWindow
|
|||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
foreach (var (key, idx) in keys.WithIndex())
|
||||
{
|
||||
using var t2 = ImRaii.TreeNode(withId ? $"#{idx}: ID: 0x{key.Id:X8}" : $"#{idx}");
|
||||
using var t2 = ImRaii.TreeNode(withId ? $"#{idx}: {tab.TryResolveName(key.Id)} (0x{key.Id:X8})" : $"#{idx}");
|
||||
if (t2)
|
||||
{
|
||||
ImRaii.TreeNode($"Default Value: 0x{key.DefaultValue:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
ImRaii.TreeNode($"Known Values: {string.Join(", ", Array.ConvertAll(key.Values, value => $"0x{value:X8}"))}",
|
||||
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
ImRaii.TreeNode($"Default Value: {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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -482,39 +632,46 @@ public partial class ModEditWindow
|
|||
if (!t)
|
||||
return;
|
||||
|
||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
|
||||
foreach (var (node, idx) in tab.Shpk.Nodes.WithIndex())
|
||||
{
|
||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
using var t2 = ImRaii.TreeNode($"#{idx:D4}: Selector: 0x{node.Selector:X8}");
|
||||
if (!tab.IsFilterMatch(node))
|
||||
continue;
|
||||
|
||||
using var t2 = ImRaii.TreeNode($"#{idx:D4}: Selector: 0x{node.Selector:X8}");
|
||||
if (!t2)
|
||||
continue;
|
||||
|
||||
foreach (var (key, keyIdx) in node.SystemKeys.WithIndex())
|
||||
{
|
||||
ImRaii.TreeNode($"System Key 0x{tab.Shpk.SystemKeys[keyIdx].Id:X8} = 0x{key:X8}",
|
||||
ImRaii.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 0x{tab.Shpk.SceneKeys[keyIdx].Id:X8} = 0x{key:X8}",
|
||||
ImRaii.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 0x{tab.Shpk.MaterialKeys[keyIdx].Id:X8} = 0x{key:X8}",
|
||||
ImRaii.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} = 0x{key:X8}", ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
{
|
||||
ImRaii.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}"))}",
|
||||
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet).Dispose();
|
||||
foreach (var (pass, passIdx) in node.Passes.WithIndex())
|
||||
{
|
||||
ImRaii.TreeNode($"Pass #{passIdx}: ID: 0x{pass.Id:X8}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}",
|
||||
ImRaii.TreeNode($"Pass #{passIdx}: ID: {tab.TryResolveName(pass.Id)}, Vertex Shader #{pass.VertexShader}, Pixel Shader #{pass.PixelShader}",
|
||||
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet)
|
||||
.Dispose();
|
||||
}
|
||||
|
|
@ -526,10 +683,10 @@ public partial class ModEditWindow
|
|||
if (!ImGui.CollapsingHeader("Shader Selection"))
|
||||
return;
|
||||
|
||||
DrawKeyArray("System Keys", true, tab.Shpk.SystemKeys);
|
||||
DrawKeyArray("Scene Keys", true, tab.Shpk.SceneKeys);
|
||||
DrawKeyArray("Material Keys", true, tab.Shpk.MaterialKeys);
|
||||
DrawKeyArray("Sub-View Keys", false, tab.Shpk.SubViewKeys);
|
||||
DrawKeyArray(tab, "System Keys", true, tab.Shpk.SystemKeys);
|
||||
DrawKeyArray(tab, "Scene Keys", true, tab.Shpk.SceneKeys);
|
||||
DrawKeyArray(tab, "Material Keys", true, tab.Shpk.MaterialKeys);
|
||||
DrawKeyArray(tab, "Sub-View Keys", false, tab.Shpk.SubViewKeys);
|
||||
|
||||
DrawShaderPackageNodes(tab);
|
||||
using var t = ImRaii.TreeNode($"Node Selectors ({tab.Shpk.NodeSelectors.Count})###NodeSelectors");
|
||||
|
|
@ -559,12 +716,14 @@ public partial class ModEditWindow
|
|||
}
|
||||
}
|
||||
|
||||
private static string UsedComponentString(bool withSize, in Resource resource)
|
||||
private static string UsedComponentString(bool withSize, bool filtered, in Resource resource)
|
||||
{
|
||||
var used = filtered ? resource.FilteredUsed : resource.Used;
|
||||
var usedDynamically = filtered ? resource.FilteredUsedDynamically : resource.UsedDynamically;
|
||||
var sb = new StringBuilder(256);
|
||||
if (withSize)
|
||||
{
|
||||
foreach (var (components, i) in (resource.Used ?? Array.Empty<DisassembledShader.VectorComponents>()).WithIndex())
|
||||
foreach (var (components, i) in (used ?? Array.Empty<DisassembledShader.VectorComponents>()).WithIndex())
|
||||
{
|
||||
switch (components)
|
||||
{
|
||||
|
|
@ -582,7 +741,7 @@ public partial class ModEditWindow
|
|||
}
|
||||
}
|
||||
|
||||
switch (resource.UsedDynamically ?? 0)
|
||||
switch (usedDynamically ?? 0)
|
||||
{
|
||||
case 0: break;
|
||||
case DisassembledShader.VectorComponents.All:
|
||||
|
|
@ -590,7 +749,7 @@ public partial class ModEditWindow
|
|||
break;
|
||||
default:
|
||||
sb.Append("[*].");
|
||||
foreach (var c in resource.UsedDynamically!.Value.ToString().Where(char.IsUpper))
|
||||
foreach (var c in usedDynamically!.Value.ToString().Where(char.IsUpper))
|
||||
sb.Append(char.ToLower(c));
|
||||
|
||||
sb.Append(", ");
|
||||
|
|
@ -599,7 +758,7 @@ public partial class ModEditWindow
|
|||
}
|
||||
else
|
||||
{
|
||||
var components = (resource.Used is { Length: > 0 } ? resource.Used[0] : 0) | (resource.UsedDynamically ?? 0);
|
||||
var components = (used is { Length: > 0 } ? used[0] : 0) | (usedDynamically ?? 0);
|
||||
if ((components & DisassembledShader.VectorComponents.X) != 0)
|
||||
sb.Append("Red, ");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
using Dalamud.Utility;
|
||||
using Lumina.Misc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Data;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Files.ShaderStructs;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.UI.AdvancedWindow.Materials;
|
||||
|
||||
namespace Penumbra.UI.AdvancedWindow;
|
||||
|
||||
|
|
@ -12,18 +15,27 @@ public partial class ModEditWindow
|
|||
private class ShpkTab : IWritable
|
||||
{
|
||||
public readonly ShpkFile Shpk;
|
||||
public readonly string FilePath;
|
||||
|
||||
public string NewMaterialParamName = string.Empty;
|
||||
public uint NewMaterialParamId = Crc32.Get(string.Empty, 0xFFFFFFFFu);
|
||||
public short NewMaterialParamStart;
|
||||
public short NewMaterialParamEnd;
|
||||
public Name NewMaterialParamName = string.Empty;
|
||||
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 int FilterMaximumPopCount;
|
||||
public int FilterPopCount;
|
||||
|
||||
public readonly FileDialogService FileDialog;
|
||||
|
||||
public readonly string Header;
|
||||
public readonly string Extension;
|
||||
|
||||
public ShpkTab(FileDialogService fileDialog, byte[] bytes)
|
||||
public ShpkTab(FileDialogService fileDialog, byte[] bytes, string filePath)
|
||||
{
|
||||
FileDialog = fileDialog;
|
||||
try
|
||||
|
|
@ -34,6 +46,7 @@ public partial class ModEditWindow
|
|||
{
|
||||
Shpk = new ShpkFile(bytes, false);
|
||||
}
|
||||
FilePath = filePath;
|
||||
|
||||
Header = $"Shader Package for DirectX {(int)Shpk.DirectXVersion}";
|
||||
Extension = Shpk.DirectXVersion switch
|
||||
|
|
@ -42,15 +55,36 @@ public partial class ModEditWindow
|
|||
ShpkFile.DxVersion.DirectX11 => ".dxbc",
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
FilterSystemValues = Array.ConvertAll(Shpk.SystemKeys, key => key.Values.FullSet());
|
||||
FilterSceneValues = Array.ConvertAll(Shpk.SceneKeys, key => key.Values.FullSet());
|
||||
FilterMaterialValues = Array.ConvertAll(Shpk.MaterialKeys, key => key.Values.FullSet());
|
||||
FilterSubViewValues = Array.ConvertAll(Shpk.SubViewKeys, key => key.Values.FullSet());
|
||||
FilterPasses = Shpk.Passes.FullSet();
|
||||
|
||||
FilterMaximumPopCount = FilterPasses.Count;
|
||||
foreach (var key in Shpk.SystemKeys)
|
||||
FilterMaximumPopCount += key.Values.Count;
|
||||
foreach (var key in Shpk.SceneKeys)
|
||||
FilterMaximumPopCount += key.Values.Count;
|
||||
foreach (var key in Shpk.MaterialKeys)
|
||||
FilterMaximumPopCount += key.Values.Count;
|
||||
foreach (var key in Shpk.SubViewKeys)
|
||||
FilterMaximumPopCount += key.Values.Count;
|
||||
|
||||
FilterPopCount = FilterMaximumPopCount;
|
||||
|
||||
UpdateNameCache();
|
||||
Shpk.UpdateFilteredUsed(IsFilterMatch);
|
||||
Update();
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ColorType : byte
|
||||
{
|
||||
Unused = 0,
|
||||
Used = 1,
|
||||
Continuation = 2,
|
||||
FilteredUsed = 2,
|
||||
Continuation = 4,
|
||||
}
|
||||
|
||||
public (string Name, string Tooltip, short Index, ColorType Color)[,] Matrix = null!;
|
||||
|
|
@ -58,10 +92,87 @@ public partial class ModEditWindow
|
|||
public readonly HashSet<uint> UsedIds = new(16);
|
||||
public readonly List<(string Name, short Index)> Orphans = new(16);
|
||||
|
||||
private readonly Dictionary<uint, Name> _nameCache = [];
|
||||
private readonly Dictionary<SharedSet<uint, uint>, string> _nameSetCache = [];
|
||||
private readonly Dictionary<SharedSet<uint, uint>, string> _nameSetWithIdsCache = [];
|
||||
|
||||
public void AddNameToCache(Name name)
|
||||
{
|
||||
if (name.Value != null)
|
||||
_nameCache.TryAdd(name.Crc32, name);
|
||||
|
||||
_nameSetCache.Clear();
|
||||
_nameSetWithIdsCache.Clear();
|
||||
}
|
||||
|
||||
public void UpdateNameCache()
|
||||
{
|
||||
static void CollectResourceNames(Dictionary<uint, Name> nameCache, ShpkFile.Resource[] resources)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
nameCache.TryAdd(resource.Id, resource.Name);
|
||||
}
|
||||
|
||||
static void CollectKeyNames(Dictionary<uint, Name> nameCache, ShpkFile.Key[] keys)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var keyName = nameCache.TryResolve(Names.KnownNames, key.Id);
|
||||
var valueNames = keyName.WithKnownSuffixes();
|
||||
foreach (var value in key.Values)
|
||||
{
|
||||
var valueName = valueNames.TryResolve(value);
|
||||
if (valueName.Value != null)
|
||||
nameCache.TryAdd(value, valueName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Name TryResolveName(uint crc32)
|
||||
=> _nameCache.TryResolve(Names.KnownNames, crc32);
|
||||
|
||||
public string NameSetToString(SharedSet<uint, uint> nameSet, bool withIds = false)
|
||||
{
|
||||
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
|
||||
nameSetStr = string.Join(", ", nameSet.Select(TryResolveName));
|
||||
cache.Add(nameSet, nameSetStr);
|
||||
return nameSetStr;
|
||||
}
|
||||
|
||||
public void UpdateFilteredUsed()
|
||||
{
|
||||
Shpk.UpdateFilteredUsed(IsFilterMatch);
|
||||
|
||||
var materialParams = Shpk.GetConstantById(ShpkFile.MaterialParamsConstantId);
|
||||
UpdateColors(materialParams);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var materialParams = Shpk.GetConstantById(ShpkFile.MaterialParamsConstantId);
|
||||
var numParameters = ((Shpk.MaterialParamsSize + 0xFu) & ~0xFu) >> 4;
|
||||
var defaults = Shpk.MaterialParamsDefaults != null ? (ReadOnlySpan<byte>)Shpk.MaterialParamsDefaults : [];
|
||||
var defaultFloats = MemoryMarshal.Cast<byte, float>(defaults);
|
||||
Matrix = new (string Name, string Tooltip, short Index, ColorType Color)[numParameters, 4];
|
||||
|
||||
MalformedParameters.Clear();
|
||||
|
|
@ -75,14 +186,14 @@ 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: 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;
|
||||
}
|
||||
|
||||
if (iEnd >= numParameters)
|
||||
{
|
||||
MalformedParameters.Add(
|
||||
$"{MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2)} (ID: 0x{param.Id:X8})");
|
||||
$"{MtrlTab.MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2)} ({TryResolveName(param.Id)}, 0x{param.Id:X8})");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -91,9 +202,12 @@ public partial class ModEditWindow
|
|||
var end = i == iEnd ? jEnd : 3;
|
||||
for (var j = i == iStart ? jStart : 0; j <= end; ++j)
|
||||
{
|
||||
var component = (i << 2) | j;
|
||||
var tt =
|
||||
$"{MaterialParamRangeName(materialParams?.Name ?? string.Empty, param.ByteOffset >> 2, param.ByteSize >> 2).Item1} (ID: 0x{param.Id:X8})";
|
||||
Matrix[i, j] = ($"0x{param.Id:X8}", tt, (short)idx, 0);
|
||||
$"{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})";
|
||||
Matrix[i, j] = (TryResolveName(param.Id).ToString(), tt, (short)idx, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -151,7 +265,7 @@ public partial class ModEditWindow
|
|||
if (oldStart == linear)
|
||||
newMaterialParamStart = (short)Orphans.Count;
|
||||
|
||||
Orphans.Add(($"{materialParams?.Name ?? string.Empty}{MaterialParamName(false, linear)}", linear));
|
||||
Orphans.Add(($"{materialParams?.Name ?? ShpkFile.MaterialParamsConstantName}{MtrlTab.MaterialParamName(false, linear)}", linear));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,11 +282,15 @@ public partial class ModEditWindow
|
|||
{
|
||||
var usedComponents = (materialParams?.Used?[i] ?? DisassembledShader.VectorComponents.All)
|
||||
| (materialParams?.UsedDynamically ?? 0);
|
||||
var filteredUsedComponents = (materialParams?.FilteredUsed?[i] ?? DisassembledShader.VectorComponents.All)
|
||||
| (materialParams?.FilteredUsedDynamically ?? 0);
|
||||
for (var j = 0; j < 4; ++j)
|
||||
{
|
||||
var color = ((byte)usedComponents & (1 << j)) != 0
|
||||
? ColorType.Used
|
||||
: 0;
|
||||
ColorType color = 0;
|
||||
if (((byte)usedComponents & (1 << j)) != 0)
|
||||
color |= ColorType.Used;
|
||||
if (((byte)filteredUsedComponents & (1 << j)) != 0)
|
||||
color |= ColorType.FilteredUsed;
|
||||
if (Matrix[i, j].Index == lastIndex || Matrix[i, j].Index < 0)
|
||||
color |= ColorType.Continuation;
|
||||
|
||||
|
|
@ -182,6 +300,141 @@ public partial class ModEditWindow
|
|||
}
|
||||
}
|
||||
|
||||
public bool IsFilterMatch(ShpkFile.Shader shader)
|
||||
{
|
||||
if (!FilterPasses.Overlaps(shader.Passes))
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < shader.SystemValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterSystemValues[i].Overlaps(shader.SystemValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < shader.SceneValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterSceneValues[i].Overlaps(shader.SceneValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < shader.MaterialValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterMaterialValues[i].Overlaps(shader.MaterialValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < shader.SubViewValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterSubViewValues[i].Overlaps(shader.SubViewValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsFilterMatch(ShpkFile.Node node)
|
||||
{
|
||||
if (!node.Passes.Any(pass => FilterPasses.Contains(pass.Id)))
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < node.SystemValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterSystemValues[i].Overlaps(node.SystemValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < node.SceneValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterSceneValues[i].Overlaps(node.SceneValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < node.MaterialValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterMaterialValues[i].Overlaps(node.MaterialValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < node.SubViewValues!.Length; ++i)
|
||||
{
|
||||
if (!FilterSubViewValues[i].Overlaps(node.SubViewValues[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a minimal material dev-kit file for the given shader package.
|
||||
///
|
||||
/// This file currently only hides globally unused material constants.
|
||||
/// </summary>
|
||||
public JObject ExportDevkit()
|
||||
{
|
||||
var devkit = new JObject();
|
||||
|
||||
var maybeMaterialParameter = Shpk.GetConstantById(ShpkFile.MaterialParamsConstantId);
|
||||
if (maybeMaterialParameter.HasValue)
|
||||
{
|
||||
var materialParameter = maybeMaterialParameter.Value;
|
||||
var materialParameterUsage = new IndexSet(materialParameter.Size << 2, true);
|
||||
|
||||
var used = materialParameter.Used ?? [];
|
||||
var usedDynamically = materialParameter.UsedDynamically ?? 0;
|
||||
for (var i = 0; i < used.Length; ++i)
|
||||
{
|
||||
for (var j = 0; j < 4; ++j)
|
||||
{
|
||||
if (!(used[i] | usedDynamically).HasFlag((DisassembledShader.VectorComponents)(1 << j)))
|
||||
materialParameterUsage[(i << 2) | j] = false;
|
||||
}
|
||||
}
|
||||
|
||||
var dkConstants = new JObject();
|
||||
foreach (var param in Shpk.MaterialParams)
|
||||
{
|
||||
// Don't handle misaligned parameters.
|
||||
if ((param.ByteOffset & 0x3) != 0 || (param.ByteSize & 0x3) != 0)
|
||||
continue;
|
||||
|
||||
var start = param.ByteOffset >> 2;
|
||||
var length = param.ByteSize >> 2;
|
||||
|
||||
// If the parameter is fully used, don't include it.
|
||||
if (!materialParameterUsage.Indices(start, length, true).Any())
|
||||
continue;
|
||||
|
||||
var unusedSlices = new JArray();
|
||||
|
||||
if (materialParameterUsage.Indices(start, length).Any())
|
||||
{
|
||||
foreach (var (rgStart, rgEnd) in materialParameterUsage.Ranges(start, length, true))
|
||||
{
|
||||
unusedSlices.Add(new JObject
|
||||
{
|
||||
["Type"] = "Hidden",
|
||||
["Offset"] = rgStart,
|
||||
["Length"] = rgEnd - rgStart,
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unusedSlices.Add(new JObject
|
||||
{
|
||||
["Type"] = "Hidden",
|
||||
});
|
||||
}
|
||||
|
||||
dkConstants[param.Id.ToString()] = unusedSlices;
|
||||
}
|
||||
|
||||
devkit["Constants"] = dkConstants;
|
||||
}
|
||||
|
||||
return devkit;
|
||||
}
|
||||
|
||||
public bool Valid
|
||||
=> Shpk.Valid;
|
||||
|
||||
|
|
|
|||
|
|
@ -615,7 +615,7 @@ public partial class ModEditWindow : Window, IDisposable, IUiService
|
|||
_shaderPackageTab = new FileEditor<ShpkTab>(this, _communicator, gameData, config, _editor.Compactor, _fileDialog, "Shaders", ".shpk",
|
||||
() => PopulateIsOnPlayer(_editor.Files.Shpk, ResourceType.Shpk), DrawShaderPackagePanel,
|
||||
() => Mod?.ModPath.FullName ?? string.Empty,
|
||||
(bytes, _, _) => new ShpkTab(_fileDialog, bytes));
|
||||
(bytes, path, _) => new ShpkTab(_fileDialog, bytes, path));
|
||||
_center = new CombinedTexture(_left, _right);
|
||||
_textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor, () => GetPlayerResourcesOfType(ResourceType.Tex));
|
||||
_resourceTreeFactory = resourceTreeFactory;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue