mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Update StainService for DT
This commit is contained in:
parent
59b3859f11
commit
e8182f285e
3 changed files with 124 additions and 39 deletions
|
|
@ -5,10 +5,15 @@ namespace Penumbra.Interop.Structs;
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct CharacterUtilityData
|
||||
{
|
||||
public const int IndexHumanPbd = 63;
|
||||
public const int IndexTransparentTex = 79;
|
||||
public const int IndexDecalTex = 80;
|
||||
public const int IndexSkinShpk = 83;
|
||||
public const int IndexHumanPbd = 63;
|
||||
public const int IndexTransparentTex = 79;
|
||||
public const int IndexDecalTex = 80;
|
||||
public const int IndexTileOrbArrayTex = 81;
|
||||
public const int IndexTileNormArrayTex = 82;
|
||||
public const int IndexSkinShpk = 83;
|
||||
public const int IndexGudStm = 94;
|
||||
public const int IndexLegacyStm = 95;
|
||||
public const int IndexSphereDArrayTex = 96;
|
||||
|
||||
public static readonly MetaIndex[] EqdpIndices = Enum.GetNames<MetaIndex>()
|
||||
.Zip(Enum.GetValues<MetaIndex>())
|
||||
|
|
@ -97,8 +102,23 @@ public unsafe struct CharacterUtilityData
|
|||
[FieldOffset(8 + IndexDecalTex * 8)]
|
||||
public TextureResourceHandle* DecalTexResource;
|
||||
|
||||
[FieldOffset(8 + IndexTileOrbArrayTex * 8)]
|
||||
public TextureResourceHandle* TileOrbArrayTexResource;
|
||||
|
||||
[FieldOffset(8 + IndexTileNormArrayTex * 8)]
|
||||
public TextureResourceHandle* TileNormArrayTexResource;
|
||||
|
||||
[FieldOffset(8 + IndexSkinShpk * 8)]
|
||||
public ResourceHandle* SkinShpkResource;
|
||||
|
||||
[FieldOffset(8 + IndexGudStm * 8)]
|
||||
public ResourceHandle* GudStmResource;
|
||||
|
||||
[FieldOffset(8 + IndexLegacyStm * 8)]
|
||||
public ResourceHandle* LegacyStmResource;
|
||||
|
||||
[FieldOffset(8 + IndexSphereDArrayTex * 8)]
|
||||
public TextureResourceHandle* SphereDArrayTexResource;
|
||||
|
||||
// not included resources have no known use case.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,25 @@ using OtterGui.Services;
|
|||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.UI.AdvancedWindow;
|
||||
using Penumbra.GameData.Files.StainMapStructs;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.UI.AdvancedWindow.Materials;
|
||||
|
||||
namespace Penumbra.Services;
|
||||
|
||||
public class StainService : IService
|
||||
{
|
||||
public sealed class StainTemplateCombo(FilterComboColors stainCombo, StmFile stmFile)
|
||||
: FilterComboCache<ushort>(stmFile.Entries.Keys.Prepend((ushort)0), MouseWheelType.None, Penumbra.Log)
|
||||
public sealed class StainTemplateCombo<TDyePack>(FilterComboColors[] stainCombos, StmFile<TDyePack> stmFile)
|
||||
: FilterComboCache<ushort>(stmFile.Entries.Keys.Prepend((ushort)0), MouseWheelType.None, Penumbra.Log) where TDyePack : unmanaged, IDyePack<TDyePack>
|
||||
{
|
||||
// FIXME There might be a better way to handle that.
|
||||
public int CurrentDyeChannel = 0;
|
||||
|
||||
protected override float GetFilterWidth()
|
||||
{
|
||||
var baseSize = ImGui.CalcTextSize("0000").X + ImGui.GetStyle().ScrollbarSize + ImGui.GetStyle().ItemInnerSpacing.X;
|
||||
if (stainCombo.CurrentSelection.Key == 0)
|
||||
if (stainCombos[CurrentDyeChannel].CurrentSelection.Key == 0)
|
||||
return baseSize;
|
||||
|
||||
return baseSize + ImGui.GetTextLineHeight() * 3 + ImGui.GetStyle().ItemInnerSpacing.X * 3;
|
||||
|
|
@ -47,33 +53,73 @@ public class StainService : IService
|
|||
protected override bool DrawSelectable(int globalIdx, bool selected)
|
||||
{
|
||||
var ret = base.DrawSelectable(globalIdx, selected);
|
||||
var selection = stainCombo.CurrentSelection.Key;
|
||||
var selection = stainCombos[CurrentDyeChannel].CurrentSelection.Key;
|
||||
if (selection == 0 || !stmFile.TryGetValue(Items[globalIdx], selection, out var colors))
|
||||
return ret;
|
||||
|
||||
ImGui.SameLine();
|
||||
var frame = new Vector2(ImGui.GetTextLineHeight());
|
||||
ImGui.ColorButton("D", new Vector4(ModEditWindow.PseudoSqrtRgb(colors.Diffuse), 1), 0, frame);
|
||||
ImGui.ColorButton("D", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.DiffuseColor), 1), 0, frame);
|
||||
ImGui.SameLine();
|
||||
ImGui.ColorButton("S", new Vector4(ModEditWindow.PseudoSqrtRgb(colors.Specular), 1), 0, frame);
|
||||
ImGui.ColorButton("S", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.SpecularColor), 1), 0, frame);
|
||||
ImGui.SameLine();
|
||||
ImGui.ColorButton("E", new Vector4(ModEditWindow.PseudoSqrtRgb(colors.Emissive), 1), 0, frame);
|
||||
ImGui.ColorButton("E", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)colors.EmissiveColor), 1), 0, frame);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly DictStain StainData;
|
||||
public readonly FilterComboColors StainCombo;
|
||||
public readonly StmFile StmFile;
|
||||
public readonly StainTemplateCombo TemplateCombo;
|
||||
public const int ChannelCount = 2;
|
||||
|
||||
public StainService(IDataManager dataManager, DictStain stainData)
|
||||
public readonly DictStain StainData;
|
||||
public readonly FilterComboColors StainCombo1;
|
||||
public readonly FilterComboColors StainCombo2; // FIXME is there a better way to handle this?
|
||||
public readonly StmFile<LegacyDyePack> LegacyStmFile;
|
||||
public readonly StmFile<DyePack> GudStmFile;
|
||||
public readonly StainTemplateCombo<LegacyDyePack> LegacyTemplateCombo;
|
||||
public readonly StainTemplateCombo<DyePack> GudTemplateCombo;
|
||||
|
||||
public unsafe StainService(IDataManager dataManager, CharacterUtility characterUtility, DictStain stainData)
|
||||
{
|
||||
StainData = stainData;
|
||||
StainCombo = new FilterComboColors(140, MouseWheelType.None,
|
||||
StainData = stainData;
|
||||
StainCombo1 = CreateStainCombo();
|
||||
StainCombo2 = CreateStainCombo();
|
||||
LegacyStmFile = LoadStmFile<LegacyDyePack>(characterUtility.Address->LegacyStmResource, dataManager);
|
||||
GudStmFile = LoadStmFile<DyePack>(characterUtility.Address->GudStmResource, dataManager);
|
||||
|
||||
FilterComboColors[] stainCombos = [StainCombo1, StainCombo2];
|
||||
|
||||
LegacyTemplateCombo = new StainTemplateCombo<LegacyDyePack>(stainCombos, LegacyStmFile);
|
||||
GudTemplateCombo = new StainTemplateCombo<DyePack>(stainCombos, GudStmFile);
|
||||
}
|
||||
|
||||
/// <summary> Retrieves the <see cref="FilterComboColors"/> instance for the given channel. Indexing is zero-based. </summary>
|
||||
public FilterComboColors GetStainCombo(int channel)
|
||||
=> channel switch
|
||||
{
|
||||
0 => StainCombo1,
|
||||
1 => StainCombo2,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(channel), channel, $"Unsupported dye channel {channel} (supported values are 0 and 1)")
|
||||
};
|
||||
|
||||
/// <summary> Loads a STM file. Opportunistically attempts to re-use the file already read by the game, with Lumina fallback. </summary>
|
||||
private static unsafe StmFile<TDyePack> LoadStmFile<TDyePack>(ResourceHandle* stmResourceHandle, IDataManager dataManager) where TDyePack : unmanaged, IDyePack<TDyePack>
|
||||
{
|
||||
if (stmResourceHandle != null)
|
||||
{
|
||||
var stmData = stmResourceHandle->CsHandle.GetDataSpan();
|
||||
if (stmData.Length > 0)
|
||||
{
|
||||
Penumbra.Log.Debug($"[StainService] Loading StmFile<{typeof(TDyePack)}> from ResourceHandle 0x{(nint)stmResourceHandle:X}");
|
||||
return new StmFile<TDyePack>(stmData);
|
||||
}
|
||||
}
|
||||
|
||||
Penumbra.Log.Debug($"[StainService] Loading StmFile<{typeof(TDyePack)}> from Lumina");
|
||||
return new StmFile<TDyePack>(dataManager);
|
||||
}
|
||||
|
||||
private FilterComboColors CreateStainCombo()
|
||||
=> new(140, MouseWheelType.None,
|
||||
() => StainData.Value.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))).ToList(),
|
||||
Penumbra.Log);
|
||||
StmFile = new StmFile(dataManager);
|
||||
TemplateCombo = new StainTemplateCombo(StainCombo, StmFile);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ using ImGuiClip = OtterGui.ImGuiClip;
|
|||
using Penumbra.Api.IpcTester;
|
||||
using Penumbra.Interop.Hooks.PostProcessing;
|
||||
using Penumbra.Interop.Hooks.ResourceLoading;
|
||||
using Penumbra.GameData.Files.StainMapStructs;
|
||||
using Penumbra.UI.AdvancedWindow;
|
||||
using Penumbra.UI.AdvancedWindow.Materials;
|
||||
|
||||
namespace Penumbra.UI.Tabs.Debug;
|
||||
|
||||
|
|
@ -697,32 +700,48 @@ public class DebugTab : Window, ITab, IUiService
|
|||
if (!mainTree)
|
||||
return;
|
||||
|
||||
foreach (var (key, data) in _stains.StmFile.Entries)
|
||||
using (var legacyTree = TreeNode("stainingtemplate.stm"))
|
||||
{
|
||||
if (legacyTree)
|
||||
DrawStainTemplatesFile(_stains.LegacyStmFile);
|
||||
}
|
||||
|
||||
using (var gudTree = TreeNode("stainingtemplate_gud.stm"))
|
||||
{
|
||||
if (gudTree)
|
||||
DrawStainTemplatesFile(_stains.GudStmFile);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawStainTemplatesFile<TDyePack>(StmFile<TDyePack> stmFile) where TDyePack : unmanaged, IDyePack<TDyePack>
|
||||
{
|
||||
foreach (var (key, data) in stmFile.Entries)
|
||||
{
|
||||
using var tree = TreeNode($"Template {key}");
|
||||
if (!tree)
|
||||
continue;
|
||||
|
||||
using var table = Table("##table", 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
|
||||
using var table = Table("##table", data.Colors.Length + data.Scalars.Length, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
|
||||
if (!table)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < StmFile.StainingTemplateEntry.NumElements; ++i)
|
||||
for (var i = 0; i < StmFile<TDyePack>.StainingTemplateEntry.NumElements; ++i)
|
||||
{
|
||||
var (r, g, b) = data.DiffuseEntries[i];
|
||||
ImGuiUtil.DrawTableColumn($"{r:F6} | {g:F6} | {b:F6}");
|
||||
foreach (var list in data.Colors)
|
||||
{
|
||||
var color = list[i];
|
||||
ImGui.TableNextColumn();
|
||||
var frame = new Vector2(ImGui.GetTextLineHeight());
|
||||
ImGui.ColorButton("###color", new Vector4(MtrlTab.PseudoSqrtRgb((Vector3)color), 1), 0, frame);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted($"{color.Red:F6} | {color.Green:F6} | {color.Blue:F6}");
|
||||
}
|
||||
|
||||
(r, g, b) = data.SpecularEntries[i];
|
||||
ImGuiUtil.DrawTableColumn($"{r:F6} | {g:F6} | {b:F6}");
|
||||
|
||||
(r, g, b) = data.EmissiveEntries[i];
|
||||
ImGuiUtil.DrawTableColumn($"{r:F6} | {g:F6} | {b:F6}");
|
||||
|
||||
var a = data.SpecularPowerEntries[i];
|
||||
ImGuiUtil.DrawTableColumn($"{a:F6}");
|
||||
|
||||
a = data.GlossEntries[i];
|
||||
ImGuiUtil.DrawTableColumn($"{a:F6}");
|
||||
foreach (var list in data.Scalars)
|
||||
{
|
||||
var scalar = list[i];
|
||||
ImGuiUtil.DrawTableColumn($"{scalar:F6}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue