mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Material Editor: Extend live preview.
This commit is contained in:
parent
5a24d9155b
commit
69388689ac
4 changed files with 44 additions and 56 deletions
|
|
@ -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(),
|
_ => nint.Zero,
|
||||||
DrawObjectType.MinionUnk1 => *((nint*)&gameObject->DrawData.MainHand + 1),
|
|
||||||
DrawObjectType.MinionUnk2 => *((nint*)&gameObject->DrawData.OffHand + 1),
|
|
||||||
DrawObjectType.MinionUnk3 => *((nint*)&gameObject->DrawData.UnkF0 + 1),
|
|
||||||
_ => 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,33 +60,42 @@ 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 type in Enum.GetValues<DrawObjectType>())
|
foreach (var objectPtr in gameObjects)
|
||||||
{
|
{
|
||||||
var drawObject = (CharacterBase*)GetDrawObject(type, objects);
|
var gameObject = (Character*)objectPtr;
|
||||||
if (drawObject == null)
|
if (gameObject == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (var i = 0; i < drawObject->SlotCount; ++i)
|
var index = gameObject->GameObject.ObjectIndex;
|
||||||
|
|
||||||
|
foreach (var type in Enum.GetValues<DrawObjectType>())
|
||||||
{
|
{
|
||||||
var model = drawObject->Models[i];
|
var drawObject = (CharacterBase*)GetDrawObject(type, objectPtr);
|
||||||
if (model == null)
|
if (drawObject == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (var j = 0; j < model->MaterialCount; ++j)
|
for (var i = 0; i < drawObject->SlotCount; ++i)
|
||||||
{
|
{
|
||||||
var material = model->Materials[j];
|
var model = drawObject->Models[i];
|
||||||
if (material == null)
|
if (model == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var mtrlHandle = material->MaterialResourceHandle;
|
for (var j = 0; j < model->MaterialCount; ++j)
|
||||||
var path = ResolveContext.GetResourceHandlePath((Structs.ResourceHandle*)mtrlHandle);
|
{
|
||||||
if (path == needle)
|
var material = model->Materials[j];
|
||||||
result.Add(new MaterialInfo(type, i, j));
|
if (material == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var mtrlHandle = material->MaterialResourceHandle;
|
||||||
|
var path = ResolveContext.GetResourceHandlePath((Structs.ResourceHandle*)mtrlHandle);
|
||||||
|
if (path == needle)
|
||||||
|
result.Add(new MaterialInfo(index, type, i, j));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -576,9 +576,10 @@ public partial class ModEditWindow : Window, IDisposable
|
||||||
_shaderPackageTab = new FileEditor<ShpkTab>(this, gameData, config, _editor.Compactor, _fileDialog, "Shaders", ".shpk",
|
_shaderPackageTab = new FileEditor<ShpkTab>(this, gameData, config, _editor.Compactor, _fileDialog, "Shaders", ".shpk",
|
||||||
() => _editor.Files.Shpk, DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty,
|
() => _editor.Files.Shpk, DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty,
|
||||||
(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);
|
||||||
_quickImportViewer =
|
_resourceTreeFactory = resourceTreeFactory;
|
||||||
|
_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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue