Material Editor: Extend live preview.

This commit is contained in:
Exter-N 2023-09-20 01:53:10 +02:00
parent 5a24d9155b
commit 69388689ac
4 changed files with 44 additions and 56 deletions

View file

@ -9,41 +9,20 @@ namespace Penumbra.Interop.MaterialPreview;
public enum DrawObjectType public enum DrawObjectType
{ {
PlayerCharacter, Character,
PlayerMainhand, Mainhand,
PlayerOffhand, Offhand,
PlayerVfx, Vfx,
MinionCharacter,
MinionUnk1,
MinionUnk2,
MinionUnk3,
}; };
public readonly record struct MaterialInfo(DrawObjectType Type, int ModelSlot, int MaterialSlot) public readonly record struct MaterialInfo(ushort ObjectIndex, DrawObjectType Type, int ModelSlot, int MaterialSlot)
{ {
public nint GetCharacter(IObjectTable objects) public nint GetCharacter(IObjectTable objects)
=> GetCharacter(Type, objects); => objects.GetObjectAddress(ObjectIndex);
public static nint GetCharacter(DrawObjectType type, IObjectTable objects)
=> type switch
{
DrawObjectType.PlayerCharacter => objects.GetObjectAddress(0),
DrawObjectType.PlayerMainhand => objects.GetObjectAddress(0),
DrawObjectType.PlayerOffhand => objects.GetObjectAddress(0),
DrawObjectType.PlayerVfx => objects.GetObjectAddress(0),
DrawObjectType.MinionCharacter => objects.GetObjectAddress(1),
DrawObjectType.MinionUnk1 => objects.GetObjectAddress(1),
DrawObjectType.MinionUnk2 => objects.GetObjectAddress(1),
DrawObjectType.MinionUnk3 => objects.GetObjectAddress(1),
_ => nint.Zero,
};
public nint GetDrawObject(nint address) public nint GetDrawObject(nint address)
=> GetDrawObject(Type, address); => GetDrawObject(Type, address);
public static nint GetDrawObject(DrawObjectType type, IObjectTable objects)
=> GetDrawObject(type, GetCharacter(type, objects));
public static unsafe nint GetDrawObject(DrawObjectType type, nint address) public static unsafe nint GetDrawObject(DrawObjectType type, nint address)
{ {
var gameObject = (Character*)address; var gameObject = (Character*)address;
@ -52,18 +31,17 @@ public readonly record struct MaterialInfo(DrawObjectType Type, int ModelSlot, i
return type switch return type switch
{ {
DrawObjectType.PlayerCharacter => (nint)gameObject->GameObject.GetDrawObject(), DrawObjectType.Character => (nint)gameObject->GameObject.GetDrawObject(),
DrawObjectType.PlayerMainhand => *((nint*)&gameObject->DrawData.MainHand + 1), DrawObjectType.Mainhand => *((nint*)&gameObject->DrawData.MainHand + 1),
DrawObjectType.PlayerOffhand => *((nint*)&gameObject->DrawData.OffHand + 1), DrawObjectType.Offhand => *((nint*)&gameObject->DrawData.OffHand + 1),
DrawObjectType.PlayerVfx => *((nint*)&gameObject->DrawData.UnkF0 + 1), DrawObjectType.Vfx => *((nint*)&gameObject->DrawData.UnkF0 + 1),
DrawObjectType.MinionCharacter => (nint)gameObject->GameObject.GetDrawObject(),
DrawObjectType.MinionUnk1 => *((nint*)&gameObject->DrawData.MainHand + 1),
DrawObjectType.MinionUnk2 => *((nint*)&gameObject->DrawData.OffHand + 1),
DrawObjectType.MinionUnk3 => *((nint*)&gameObject->DrawData.UnkF0 + 1),
_ => nint.Zero, _ => nint.Zero,
}; };
} }
public unsafe Material* GetDrawObjectMaterial(IObjectTable objects)
=> GetDrawObjectMaterial((CharacterBase*)GetDrawObject(GetCharacter(objects)));
public unsafe Material* GetDrawObjectMaterial(CharacterBase* drawObject) public unsafe Material* GetDrawObjectMaterial(CharacterBase* drawObject)
{ {
if (drawObject == null) if (drawObject == null)
@ -82,14 +60,22 @@ public readonly record struct MaterialInfo(DrawObjectType Type, int ModelSlot, i
return model->Materials[MaterialSlot]; return model->Materials[MaterialSlot];
} }
public static unsafe List<MaterialInfo> FindMaterials(IObjectTable objects, string materialPath) public static unsafe List<MaterialInfo> FindMaterials(IEnumerable<nint> gameObjects, string materialPath)
{ {
var needle = ByteString.FromString(materialPath.Replace('\\', '/'), out var m, true) ? m : ByteString.Empty; var needle = ByteString.FromString(materialPath.Replace('\\', '/'), out var m, true) ? m : ByteString.Empty;
var result = new List<MaterialInfo>(Enum.GetValues<DrawObjectType>().Length); var result = new List<MaterialInfo>(Enum.GetValues<DrawObjectType>().Length);
foreach (var objectPtr in gameObjects)
{
var gameObject = (Character*)objectPtr;
if (gameObject == null)
continue;
var index = gameObject->GameObject.ObjectIndex;
foreach (var type in Enum.GetValues<DrawObjectType>()) foreach (var type in Enum.GetValues<DrawObjectType>())
{ {
var drawObject = (CharacterBase*)GetDrawObject(type, objects); var drawObject = (CharacterBase*)GetDrawObject(type, objectPtr);
if (drawObject == null) if (drawObject == null)
continue; continue;
@ -108,7 +94,8 @@ public readonly record struct MaterialInfo(DrawObjectType Type, int ModelSlot, i
var mtrlHandle = material->MaterialResourceHandle; var mtrlHandle = material->MaterialResourceHandle;
var path = ResolveContext.GetResourceHandlePath((Structs.ResourceHandle*)mtrlHandle); var path = ResolveContext.GetResourceHandlePath((Structs.ResourceHandle*)mtrlHandle);
if (path == needle) if (path == needle)
result.Add(new MaterialInfo(type, i, j)); result.Add(new MaterialInfo(index, type, i, j));
}
} }
} }
} }

View file

@ -454,13 +454,12 @@ public partial class ModEditWindow
{ {
UnbindFromMaterialInstances(); UnbindFromMaterialInstances();
var instances = MaterialInfo.FindMaterials(_edit._dalamud.Objects, FilePath); var instances = MaterialInfo.FindMaterials(_edit._resourceTreeFactory.GetLocalPlayerRelatedCharacters().Select(ch => ch.Address), FilePath);
var foundMaterials = new HashSet<nint>(); var foundMaterials = new HashSet<nint>();
foreach (var materialInfo in instances) foreach (var materialInfo in instances)
{ {
var drawObject = (CharacterBase*)MaterialInfo.GetDrawObject(materialInfo.Type, _edit._dalamud.Objects); var material = materialInfo.GetDrawObjectMaterial(_edit._dalamud.Objects);
var material = materialInfo.GetDrawObjectMaterial(drawObject);
if (foundMaterials.Contains((nint)material)) if (foundMaterials.Contains((nint)material))
continue; continue;

View file

@ -14,6 +14,7 @@ namespace Penumbra.UI.AdvancedWindow;
public partial class ModEditWindow public partial class ModEditWindow
{ {
private readonly ResourceTreeFactory _resourceTreeFactory;
private readonly ResourceTreeViewer _quickImportViewer; private readonly ResourceTreeViewer _quickImportViewer;
private readonly Dictionary<FullPath, IWritable?> _quickImportWritables = new(); private readonly Dictionary<FullPath, IWritable?> _quickImportWritables = new();
private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new(); private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new();

View file

@ -578,6 +578,7 @@ public partial class ModEditWindow : Window, IDisposable
(bytes, _, _) => new ShpkTab(_fileDialog, bytes)); (bytes, _, _) => new ShpkTab(_fileDialog, bytes));
_center = new CombinedTexture(_left, _right); _center = new CombinedTexture(_left, _right);
_textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor); _textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor);
_resourceTreeFactory = resourceTreeFactory;
_quickImportViewer = _quickImportViewer =
new ResourceTreeViewer(_config, resourceTreeFactory, changedItemDrawer, 2, OnQuickImportRefresh, DrawQuickImportActions); new ResourceTreeViewer(_config, resourceTreeFactory, changedItemDrawer, 2, OnQuickImportRefresh, DrawQuickImportActions);
_communicator.ModPathChanged.Subscribe(OnModPathChanged, ModPathChanged.Priority.ModEditWindow); _communicator.ModPathChanged.Subscribe(OnModPathChanged, ModPathChanged.Priority.ModEditWindow);