mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
143 lines
6.8 KiB
C#
143 lines
6.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
|
using Penumbra.GameData.Enums;
|
|
using Penumbra.GameData.Structs;
|
|
using Penumbra.Interop.Structs;
|
|
using CustomizeData = FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData;
|
|
|
|
namespace Penumbra.Interop.ResourceTree;
|
|
|
|
public class ResourceTree
|
|
{
|
|
public readonly string Name;
|
|
public readonly nint GameObjectAddress;
|
|
public readonly nint DrawObjectAddress;
|
|
public readonly bool PlayerRelated;
|
|
public readonly string CollectionName;
|
|
public readonly List<ResourceNode> Nodes;
|
|
public readonly HashSet<ResourceNode> FlatNodes;
|
|
|
|
public int ModelId;
|
|
public CustomizeData CustomizeData;
|
|
public GenderRace RaceCode;
|
|
|
|
public ResourceTree(string name, nint gameObjectAddress, nint drawObjectAddress, bool playerRelated, string collectionName)
|
|
{
|
|
Name = name;
|
|
GameObjectAddress = gameObjectAddress;
|
|
DrawObjectAddress = drawObjectAddress;
|
|
PlayerRelated = playerRelated;
|
|
CollectionName = collectionName;
|
|
Nodes = new List<ResourceNode>();
|
|
FlatNodes = new HashSet<ResourceNode>();
|
|
}
|
|
|
|
internal unsafe void LoadResources(GlobalResolveContext globalContext)
|
|
{
|
|
var character = (Character*)GameObjectAddress;
|
|
var model = (CharacterBase*)DrawObjectAddress;
|
|
var equipment = new ReadOnlySpan<CharacterArmor>(&character->DrawData.Head, 10);
|
|
// var customize = new ReadOnlySpan<byte>( character->CustomizeData, 26 );
|
|
ModelId = character->CharacterData.ModelCharaId;
|
|
CustomizeData = character->DrawData.CustomizeData;
|
|
RaceCode = model->GetModelType() == CharacterBase.ModelType.Human ? (GenderRace) ((Human*)model)->RaceSexId : GenderRace.Unknown;
|
|
|
|
for (var i = 0; i < model->SlotCount; ++i)
|
|
{
|
|
var context = globalContext.CreateContext(
|
|
i < equipment.Length ? ((uint)i).ToEquipSlot() : EquipSlot.Unknown,
|
|
i < equipment.Length ? equipment[i] : default
|
|
);
|
|
|
|
var imc = (ResourceHandle*)model->IMCArray[i];
|
|
var imcNode = context.CreateNodeFromImc(imc);
|
|
if (imcNode != null)
|
|
Nodes.Add(globalContext.WithUIData ? imcNode.WithUIData(imcNode.Name ?? $"IMC #{i}", imcNode.Icon) : imcNode);
|
|
|
|
var mdl = (RenderModel*)model->Models[i];
|
|
var mdlNode = context.CreateNodeFromRenderModel(mdl);
|
|
if (mdlNode != null)
|
|
Nodes.Add(globalContext.WithUIData ? mdlNode.WithUIData(mdlNode.Name ?? $"Model #{i}", mdlNode.Icon) : mdlNode);
|
|
}
|
|
|
|
AddSkeleton(Nodes, globalContext.CreateContext(EquipSlot.Unknown, default), model->Skeleton);
|
|
|
|
if (character->GameObject.GetObjectKind() == (byte)ObjectKind.Pc)
|
|
AddHumanResources(globalContext, (HumanExt*)model);
|
|
}
|
|
|
|
private unsafe void AddHumanResources(GlobalResolveContext globalContext, HumanExt* human)
|
|
{
|
|
var firstSubObject = (CharacterBase*)human->Human.CharacterBase.DrawObject.Object.ChildObject;
|
|
if (firstSubObject != null)
|
|
{
|
|
var subObjectNodes = new List<ResourceNode>();
|
|
var subObject = firstSubObject;
|
|
var subObjectIndex = 0;
|
|
do
|
|
{
|
|
var weapon = subObject->GetModelType() == CharacterBase.ModelType.Weapon ? (Weapon*)subObject : null;
|
|
var subObjectNamePrefix = weapon != null ? "Weapon" : "Fashion Acc.";
|
|
var subObjectContext = globalContext.CreateContext(
|
|
weapon != null ? EquipSlot.MainHand : EquipSlot.Unknown,
|
|
weapon != null ? new CharacterArmor(weapon->ModelSetId, (byte)weapon->Variant, (byte)weapon->ModelUnknown) : default
|
|
);
|
|
|
|
for (var i = 0; i < subObject->SlotCount; ++i)
|
|
{
|
|
var imc = (ResourceHandle*)subObject->IMCArray[i];
|
|
var imcNode = subObjectContext.CreateNodeFromImc(imc);
|
|
if (imcNode != null)
|
|
subObjectNodes.Add(globalContext.WithUIData
|
|
? imcNode.WithUIData(imcNode.Name ?? $"{subObjectNamePrefix} #{subObjectIndex}, IMC #{i}", imcNode.Icon)
|
|
: imcNode);
|
|
|
|
var mdl = (RenderModel*)subObject->Models[i];
|
|
var mdlNode = subObjectContext.CreateNodeFromRenderModel(mdl);
|
|
if (mdlNode != null)
|
|
subObjectNodes.Add(globalContext.WithUIData
|
|
? mdlNode.WithUIData(mdlNode.Name ?? $"{subObjectNamePrefix} #{subObjectIndex}, Model #{i}", mdlNode.Icon)
|
|
: mdlNode);
|
|
}
|
|
|
|
AddSkeleton(subObjectNodes, subObjectContext, subObject->Skeleton, $"{subObjectNamePrefix} #{subObjectIndex}, ");
|
|
|
|
subObject = (CharacterBase*)subObject->DrawObject.Object.NextSiblingObject;
|
|
++subObjectIndex;
|
|
} while (subObject != null && subObject != firstSubObject);
|
|
|
|
Nodes.InsertRange(0, subObjectNodes);
|
|
}
|
|
|
|
var context = globalContext.CreateContext(EquipSlot.Unknown, default);
|
|
|
|
var decalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->Decal);
|
|
if (decalNode != null)
|
|
Nodes.Add(globalContext.WithUIData ? decalNode.WithUIData(decalNode.Name ?? "Face Decal", decalNode.Icon) : decalNode);
|
|
|
|
var legacyDecalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->LegacyBodyDecal);
|
|
if (legacyDecalNode != null)
|
|
Nodes.Add(globalContext.WithUIData ? legacyDecalNode.WithUIData(legacyDecalNode.Name ?? "Legacy Body Decal", legacyDecalNode.Icon) : legacyDecalNode);
|
|
}
|
|
|
|
private unsafe void AddSkeleton(List<ResourceNode> nodes, ResolveContext context, Skeleton* skeleton, string prefix = "")
|
|
{
|
|
if (skeleton == null)
|
|
return;
|
|
|
|
for (var i = 0; i < skeleton->PartialSkeletonCount; ++i)
|
|
{
|
|
var sklbNode = context.CreateNodeFromPartialSkeleton(&skeleton->PartialSkeletons[i]);
|
|
if (sklbNode != null)
|
|
nodes.Add(context.WithUIData ? sklbNode.WithUIData($"{prefix}Skeleton #{i}", sklbNode.Icon) : sklbNode);
|
|
|
|
var skpNode = context.CreateParameterNodeFromPartialSkeleton(&skeleton->PartialSkeletons[i]);
|
|
if (skpNode != null)
|
|
nodes.Add(context.WithUIData ? skpNode.WithUIData($"{prefix}Skeleton #{i} Parameters", skpNode.Icon) : skpNode);
|
|
}
|
|
}
|
|
}
|