Skip to content

Commit 9c89fdc

Browse files
Add mtrl preview for character tab
1 parent 636d2a6 commit 9c89fdc

File tree

6 files changed

+287
-27
lines changed

6 files changed

+287
-27
lines changed

Meddle/Meddle.Plugin/Plugin.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ public Plugin(IDalamudPluginInterface pluginInterface)
6262
log = app.Services.GetRequiredService<ILogger<Plugin>>();
6363
Logger = log;
6464
Meddle.Utils.Global.Logger = log;
65-
NativeDll.Initialize(app.Services.GetRequiredService<IDalamudPluginInterface>().AssemblyLocation
66-
.DirectoryName);
65+
NativeDll.Initialize(app.Services.GetRequiredService<IDalamudPluginInterface>().AssemblyLocation.DirectoryName);
6766

6867
app.Start();
6968
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// using Dalamud.Game;
2+
// using Dalamud.Hooking;
3+
// using Dalamud.Plugin.Services;
4+
// using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
5+
// using Meddle.Plugin.Utils;
6+
// using Microsoft.Extensions.Logging;
7+
//
8+
// namespace Meddle.Plugin.Services;
9+
//
10+
// public class DtorHooks : IDisposable, IService
11+
// {
12+
// private readonly string materialResourceHandleDtorSig =
13+
// "48 89 5C 24 ?? 57 48 83 EC 20 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 89 01 8B FA 48 8B 89 ?? ?? ?? ?? " +
14+
// "48 85 C9 74 22 48 8B 01 FF 50 18 48 8B 93 ?? ?? ?? ?? 48 8B C8 4C 8B 00 41 FF 50 18 48 C7 83 ?? " +
15+
// "?? ?? ?? ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 40 F6 C7 01 74 0D BA ?? ?? ?? ?? 48 8B CB E8 ?? ?? " +
16+
// "?? ?? 48 8B C3 48 8B 5C 24 ?? 48 83 C4 20 5F C3 40 53 48 83 EC 20 48 8D 05 ?? ?? ?? ?? 48 8B D9 " +
17+
// "48 89 01 F6 C2 01 74 0A BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B C3 48 83 C4 20 5B C3 CC CC CC CC CC " +
18+
// "48 89 5C 24 ?? 57 48 83 EC 20 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 89 01 8B FA 48 8B 89 ?? ?? ?? ?? " +
19+
// "48 85 C9 74 05 E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9 74 05 E8 ?? ?? ?? ?? 48 8D 8B ?? ?? ?? ??";
20+
//
21+
// private readonly string modelResourceHandleDtorSig =
22+
// "48 89 5C 24 ?? 57 48 83 EC 20 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 89 01 8B FA 48 8B 89 ?? ?? ?? ?? " +
23+
// "48 85 C9 74 05 E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9 74 05 E8 ?? ?? ?? ?? 48 8D 8B ?? ?? ?? ??";
24+
//
25+
// private readonly ILogger<DtorHooks> logger;
26+
// private readonly Hook<MaterialResourceHandleDtorDelegate>? materialResourceHandleDtorHook;
27+
// private readonly Hook<ModelResourceHandleDtorDelegate>? modelResourceHandleDtorHook;
28+
// public event EventHandler<MaterialResourceHandleDtorEventArgs>? OnMaterialResourceHandleDtor;
29+
// public event EventHandler<ModelResourceHandleDtorEventArgs>? OnModelResourceHandleDtor;
30+
//
31+
// public DtorHooks(ISigScanner sigScanner, IGameInteropProvider gameInterop, ILogger<DtorHooks> logger)
32+
// {
33+
// this.logger = logger;
34+
// if (sigScanner.TryScanText(materialResourceHandleDtorSig, out var mtrlResourceHandleDtorPtr))
35+
// {
36+
// logger.LogDebug("Found MaterialResourceHandle::Dtor at {ptr:X}", mtrlResourceHandleDtorPtr);
37+
// materialResourceHandleDtorHook = gameInterop.HookFromAddress<MaterialResourceHandleDtorDelegate>(mtrlResourceHandleDtorPtr, MaterialResourceHandle_Dtor_Detour);
38+
// materialResourceHandleDtorHook.Enable();
39+
// }
40+
// else
41+
// {
42+
// logger.LogError("Failed to find MaterialResourceHandle::Dtor");
43+
// }
44+
//
45+
// if (sigScanner.TryScanText(modelResourceHandleDtorSig, out var mdlResourceHandleDtorPtr))
46+
// {
47+
// logger.LogDebug("Found ModelResourceHandle::Dtor at {ptr:X}", mdlResourceHandleDtorPtr);
48+
// modelResourceHandleDtorHook = gameInterop.HookFromAddress<ModelResourceHandleDtorDelegate>(mdlResourceHandleDtorPtr, ModelResourceHandle_Dtor_Detour);
49+
// modelResourceHandleDtorHook.Enable();
50+
// }
51+
// else
52+
// {
53+
// logger.LogError("Failed to find ModelResourceHandle::Dtor");
54+
// }
55+
// }
56+
//
57+
// private delegate nint MaterialResourceHandleDtorDelegate(nint materialResourceHandle, char a2);
58+
// private delegate nint ModelResourceHandleDtorDelegate(nint modelResourceHandle, char a2);
59+
//
60+
// public class MaterialResourceHandleDtorEventArgs : EventArgs
61+
// {
62+
// public nint MaterialResourceHandle { get; }
63+
// public char A2 { get; }
64+
// public MaterialResourceHandleDtorEventArgs(nint materialResourceHandle, char a2)
65+
// {
66+
// MaterialResourceHandle = materialResourceHandle;
67+
// A2 = a2;
68+
// }
69+
// }
70+
//
71+
// public class ModelResourceHandleDtorEventArgs : EventArgs
72+
// {
73+
// public nint ModelResourceHandle { get; }
74+
// public char A2 { get; }
75+
// public ModelResourceHandleDtorEventArgs(nint modelResourceHandle, char a2)
76+
// {
77+
// ModelResourceHandle = modelResourceHandle;
78+
// A2 = a2;
79+
// }
80+
// }
81+
//
82+
// private unsafe nint MaterialResourceHandle_Dtor_Detour(nint materialResourceHandle, char a2)
83+
// {
84+
// var mtrlResourceHandle = (MaterialResourceHandle*)materialResourceHandle;
85+
// var path = mtrlResourceHandle->ResourceHandle.FileName.ParseString();
86+
// logger.LogDebug("MaterialResourceHandle_Dtor_Detour: {path}", path);
87+
// var result = materialResourceHandleDtorHook!.Original(materialResourceHandle, a2);
88+
// OnMaterialResourceHandleDtor?.Invoke(this, new MaterialResourceHandleDtorEventArgs(materialResourceHandle, a2));
89+
// return result;
90+
// }
91+
//
92+
// private unsafe nint ModelResourceHandle_Dtor_Detour(nint modelResourceHandle, char a2)
93+
// {
94+
// var mdlResourceHandle = (ModelResourceHandle*)modelResourceHandle;
95+
// var path = mdlResourceHandle->ResourceHandle.FileName.ParseString();
96+
// logger.LogDebug("ModelResourceHandle_Dtor_Detour: {path}", path);
97+
// var result = modelResourceHandleDtorHook!.Original(modelResourceHandle, a2);
98+
// OnModelResourceHandleDtor?.Invoke(this, new ModelResourceHandleDtorEventArgs(modelResourceHandle, a2));
99+
// return result;
100+
// }
101+
//
102+
// public void Dispose()
103+
// {
104+
// logger.LogDebug("Disposing DtorHooks");
105+
// materialResourceHandleDtorHook?.Dispose();
106+
// modelResourceHandleDtorHook?.Dispose();
107+
// }
108+
// }

Meddle/Meddle.Plugin/UI/Layout/Instance.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ private unsafe void DrawBgObject(ParsedBgPartsInstance bg)
170170
{
171171
BgObject* drawObject = (BgObject*)bgPartLayout->GraphicsObject;
172172
UiUtil.Text($"Graphics Object {(nint)drawObject:X8}", $"{(nint)drawObject:X8}");
173-
if (ImGui.Button($"Preview Materials##{(nint)drawObject:X8}"))
173+
using var disabled = ImRaii.Disabled( mdlMaterialWindowManager.HasWindow(drawObject->ModelResourceHandle));
174+
if (ImGui.Button("Open Material Window"))
174175
{
175176
mdlMaterialWindowManager.AddMaterialWindow(drawObject->ModelResourceHandle);
176177
}

Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Meddle.Plugin.Services;
1919
using Meddle.Plugin.Services.UI;
2020
using Meddle.Plugin.UI.Layout;
21+
using Meddle.Plugin.UI.Windows;
2122
using Meddle.Plugin.Utils;
2223
using Meddle.Utils;
2324
using Meddle.Utils.Constants;
@@ -44,6 +45,7 @@ public unsafe class LiveCharacterTab : ITab
4445
{
4546
private readonly CommonUi commonUi;
4647
private readonly Configuration config;
48+
private readonly MdlMaterialWindowManager mdlMaterialWindowManager;
4749
private readonly ComposerFactory composerFactory;
4850
public MenuType MenuType => MenuType.Default;
4951

@@ -80,6 +82,7 @@ public LiveCharacterTab(
8082
PbdHooks pbd,
8183
CommonUi commonUi,
8284
Configuration config,
85+
MdlMaterialWindowManager mdlMaterialWindowManager,
8386
ComposerFactory composerFactory)
8487
{
8588
this.log = log;
@@ -91,6 +94,7 @@ public LiveCharacterTab(
9194
this.pbd = pbd;
9295
this.commonUi = commonUi;
9396
this.config = config;
97+
this.mdlMaterialWindowManager = mdlMaterialWindowManager;
9498
this.composerFactory = composerFactory;
9599
}
96100

@@ -522,6 +526,15 @@ private void DrawModel(Pointer<CharacterBase> cPtr, Pointer<CSModel> mPtr, Custo
522526
{
523527
ImGui.Text("No deformer info found");
524528
}
529+
530+
using (var disableMatParam = ImRaii.Disabled(mdlMaterialWindowManager.HasWindow(mPtr.Value->ModelResourceHandle)))
531+
{
532+
// Note; since character materials are stored in model->Materials instead of model->ModelResourceHandle->Materials,
533+
if (ImGui.Button("Open Material Window"))
534+
{
535+
mdlMaterialWindowManager.AddMaterialWindow(model);
536+
}
537+
}
525538

526539
var modelShapeAttributes = StructExtensions.ParseModelShapeAttributes(model);
527540
DrawShapeAttributeTable(modelShapeAttributes);

Meddle/Meddle.Plugin/UI/Windows/MdlMaterialWindow.cs

Lines changed: 125 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,109 @@
11
using System.Numerics;
22
using Dalamud.Interface.Utility.Raii;
33
using Dalamud.Interface.Windowing;
4+
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
45
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
56
using FFXIVClientStructs.Interop;
67
using ImGuiNET;
78
using Meddle.Plugin.Models;
89
using Meddle.Plugin.Utils;
910
using Meddle.Utils.Constants;
1011
using Meddle.Utils.Files;
11-
using Meddle.Utils.Files.SqPack;
12+
using Microsoft.Extensions.Logging;
1213

1314
namespace Meddle.Plugin.UI.Windows;
1415

1516
public class MdlMaterialWindow : Window
1617
{
17-
private readonly SqPack pack;
1818
private readonly MdlMaterialWindowManager windowManager;
1919
private readonly Pointer<ModelResourceHandle> model;
20-
private readonly Dictionary<string, ShpkFile> shpkCache = new();
20+
private readonly Pointer<Model> model1;
2121
public readonly string Id;
2222

23-
public unsafe MdlMaterialWindow(SqPack pack, MdlMaterialWindowManager windowManager, Pointer<ModelResourceHandle> model) : base("Material Editor", ImGuiWindowFlags.None)
23+
public unsafe MdlMaterialWindow(MdlMaterialWindowManager windowManager, Pointer<ModelResourceHandle> model) :
24+
base("Material Editor")
2425
{
25-
this.pack = pack;
2626
this.windowManager = windowManager;
2727
this.model = model;
28-
WindowName = $"Material Editor {model.Value->FileName.ParseString()}";
2928
Id = $"{(nint)this.model.Value:X8}";
29+
WindowName = $"Material Editor {model.Value->FileName.ParseString()}###{Id}";
30+
SizeConstraints = new WindowSizeConstraints
31+
{
32+
MinimumSize = new Vector2(375, 350),
33+
MaximumSize = new Vector2(1200, 1000)
34+
};
35+
}
36+
37+
public unsafe MdlMaterialWindow(MdlMaterialWindowManager windowManager, Pointer<Model> model) :
38+
base("Material Editor")
39+
{
40+
this.windowManager = windowManager;
41+
this.model1 = model;
42+
Id = $"{(nint)this.model.Value:X8}";
43+
WindowName = $"Material Editor {model.Value->ModelResourceHandle->FileName.ParseString()}###{Id}";
44+
SizeConstraints = new WindowSizeConstraints
45+
{
46+
MinimumSize = new Vector2(375, 350),
47+
MaximumSize = new Vector2(1200, 1000)
48+
};
3049
}
3150

32-
public override void OnClose()
51+
52+
public override unsafe void OnClose()
3353
{
3454
windowManager.RemoveMaterialWindow(this);
3555
base.OnClose();
3656
}
3757

3858
public override void Draw()
3959
{
40-
DrawModel(model);
60+
if (model1 != null)
61+
{
62+
DrawModel(model1);
63+
}
64+
else
65+
{
66+
DrawModel(model);
67+
}
68+
}
69+
private unsafe void DrawModel(Pointer<Model> modelPtr)
70+
{
71+
if (modelPtr == null || modelPtr.Value == null)
72+
{
73+
ImGui.Text("Model data is null.");
74+
return;
75+
}
76+
77+
var modelName = modelPtr.Value->ModelResourceHandle->FileName.ParseString();
78+
using var modelId = ImRaii.PushId(modelName);
79+
80+
ImGui.Text($"Model: {modelName}");
81+
82+
var materials = modelPtr.Value->MaterialsSpan;
83+
ImGui.Text($"Material Count: {materials.Length}");
84+
85+
for (var i = 0; i < materials.Length; i++)
86+
{
87+
var material = materials[i];
88+
if (material == null || material.Value == null || material.Value->MaterialResourceHandle == null) continue;
89+
using var materialId = ImRaii.PushId(i);
90+
var shpkName = material.Value->MaterialResourceHandle->ShpkNameString;
91+
using var materialNode = ImRaii.TreeNode($"[{shpkName}]Material {i}: {material.Value->MaterialResourceHandle->FileName.ParseString()}");
92+
if (!materialNode.Success) continue;
93+
DrawMtrl(material.Value->MaterialResourceHandle);
94+
// DrawMtrlTextures(material.Value->MaterialResourceHandle);
95+
// using var materialParamNode = ImRaii.TreeNode("Material Parameters");
96+
// if (materialParamNode.Success)
97+
// {
98+
// DrawMtrl(material.Value->MaterialResourceHandle);
99+
// }
100+
//
101+
// using var materialTexturesNode = ImRaii.TreeNode("Material Textures");
102+
// if (materialTexturesNode.Success)
103+
// {
104+
// DrawMtrlTextures(material.Value->MaterialResourceHandle);
105+
// }
106+
}
41107
}
42108

43109
private unsafe void DrawModel(Pointer<ModelResourceHandle> modelPtr)
@@ -65,10 +131,59 @@ private unsafe void DrawModel(Pointer<ModelResourceHandle> modelPtr)
65131
using var materialNode = ImRaii.TreeNode($"[{shpkName}]Material {i}: {material.Value->FileName.ParseString()}");
66132
if (!materialNode.Success) continue;
67133
DrawMtrl(material);
134+
// using var materialParamNode = ImRaii.TreeNode("Material Parameters");
135+
// if (materialParamNode.Success)
136+
// {
137+
// DrawMtrl(material);
138+
// }
139+
//
140+
// using var materialTexturesNode = ImRaii.TreeNode("Material Textures");
141+
// if (materialTexturesNode.Success)
142+
// {
143+
// DrawMtrlTextures(material);
144+
// }
68145
}
69146
}
70147

71-
private Dictionary<nint, float[]> materialParamsCache = new();
148+
private readonly Dictionary<nint, float[]> materialParamsCache = new();
149+
150+
public unsafe void DrawMtrlTextures(Pointer<MaterialResourceHandle> mtrlPtr)
151+
{
152+
if (mtrlPtr == null || mtrlPtr.Value == null)
153+
{
154+
return;
155+
}
156+
157+
var textures = mtrlPtr.Value->TexturesSpan;
158+
if (textures.Length == 0)
159+
{
160+
ImGui.Text("No textures.");
161+
return;
162+
}
163+
164+
var availWidth = ImGui.GetContentRegionAvail().X;
165+
using var table = ImRaii.Table("MaterialTextures", 3, ImGuiTableFlags.Borders |
166+
ImGuiTableFlags.RowBg |
167+
ImGuiTableFlags.Hideable |
168+
ImGuiTableFlags.Resizable);
169+
170+
ImGui.TableSetupColumn("ID", ImGuiTableColumnFlags.WidthFixed, availWidth * 0.05f);
171+
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed, availWidth * 0.2f);
172+
ImGui.TableSetupColumn("Path", ImGuiTableColumnFlags.WidthStretch);
173+
ImGui.TableHeadersRow();
174+
175+
for (var i = 0; i < textures.Length; i++)
176+
{
177+
var texture = textures[i];
178+
if (texture.TextureResourceHandle == null) continue;
179+
using var textureId = ImRaii.PushId(i);
180+
ImGui.TableNextRow();
181+
ImGui.TableNextColumn();
182+
ImGui.Text(i.ToString());
183+
ImGui.TableNextColumn();
184+
ImGui.Text(texture.TextureResourceHandle->FileName.ParseString());
185+
}
186+
}
72187

73188
public unsafe void DrawMtrl(Pointer<MaterialResourceHandle> mtrlPtr)
74189
{
@@ -89,19 +204,7 @@ public unsafe void DrawMtrl(Pointer<MaterialResourceHandle> mtrlPtr)
89204

90205
var shpkName = mtrlPtr.Value->ShpkNameString;
91206
var shpkPath = $"shader/sm5/shpk/{shpkName}";
92-
if (!shpkCache.TryGetValue(shpkPath, out var shpk))
93-
{
94-
var shpkData = pack.GetFileOrReadFromDisk(shpkPath);
95-
if (shpkData != null)
96-
{
97-
shpk = new ShpkFile(shpkData);
98-
shpkCache[shpkPath] = shpk;
99-
}
100-
else
101-
{
102-
throw new Exception($"Failed to load {shpkPath}");
103-
}
104-
}
207+
var shpk = windowManager.GetShpkFile(shpkPath);
105208

106209
ImGui.Text($"Shader Package: {shpkName}");
107210
var orderedMaterialParams = shpk.MaterialParams.Select((x, idx) => (x, idx))

0 commit comments

Comments
 (0)