Merge remote-tracking branch 'Exter-N/human-skin-materials'

This commit is contained in:
Ottermandias 2025-07-05 22:04:48 +02:00
commit 692beacc2e
4 changed files with 81 additions and 54 deletions

View file

@ -35,6 +35,7 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
private readonly Hook<MPapResolveDelegate> _resolveMPapPathHook;
private readonly Hook<PerSlotResolveDelegate> _resolveMdlPathHook;
private readonly Hook<NamedResolveDelegate> _resolveMtrlPathHook;
private readonly Hook<PerSlotResolveDelegate> _resolveSkinMtrlPathHook;
private readonly Hook<NamedResolveDelegate> _resolvePapPathHook;
private readonly Hook<PerSlotResolveDelegate> _resolveKdbPathHook;
private readonly Hook<PerSlotResolveDelegate> _resolvePhybPathHook;
@ -52,22 +53,23 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
{
_parent = parent;
// @formatter:off
_resolveSklbPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSklb)}", hooks, vTable[76], type, ResolveSklb, ResolveSklbHuman);
_resolveMdlPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveMdl)}", hooks, vTable[77], type, ResolveMdl, ResolveMdlHuman);
_resolveSkpPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSkp)}", hooks, vTable[78], type, ResolveSkp, ResolveSkpHuman);
_resolvePhybPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolvePhyb)}", hooks, vTable[79], type, ResolvePhyb, ResolvePhybHuman);
_resolveKdbPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveKdb)}", hooks, vTable[80], type, ResolveKdb, ResolveKdbHuman);
_vFunc81Hook = Create<SkeletonVFuncDelegate>( $"{name}.{nameof(VFunc81)}", hooks, vTable[81], type, null, VFunc81);
_resolveBnmbPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveBnmb)}", hooks, vTable[82], type, ResolveBnmb, ResolveBnmbHuman);
_vFunc83Hook = Create<SkeletonVFuncDelegate>( $"{name}.{nameof(VFunc83)}", hooks, vTable[83], type, null, VFunc83);
_resolvePapPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolvePap)}", hooks, vTable[84], type, ResolvePap, ResolvePapHuman);
_resolveTmbPathHook = Create<TmbResolveDelegate>( $"{name}.{nameof(ResolveTmb)}", hooks, vTable[85], ResolveTmb);
_resolveMPapPathHook = Create<MPapResolveDelegate>( $"{name}.{nameof(ResolveMPap)}", hooks, vTable[87], ResolveMPap);
_resolveImcPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveImc)}", hooks, vTable[89], ResolveImc);
_resolveMtrlPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolveMtrl)}", hooks, vTable[90], ResolveMtrl);
_resolveDecalPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveDecal)}", hooks, vTable[92], ResolveDecal);
_resolveVfxPathHook = Create<VfxResolveDelegate>( $"{name}.{nameof(ResolveVfx)}", hooks, vTable[93], type, ResolveVfx, ResolveVfxHuman);
_resolveEidPathHook = Create<SingleResolveDelegate>( $"{name}.{nameof(ResolveEid)}", hooks, vTable[94], ResolveEid);
_resolveSklbPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSklb)}", hooks, vTable[76], type, ResolveSklb, ResolveSklbHuman);
_resolveMdlPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveMdl)}", hooks, vTable[77], type, ResolveMdl, ResolveMdlHuman);
_resolveSkpPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSkp)}", hooks, vTable[78], type, ResolveSkp, ResolveSkpHuman);
_resolvePhybPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolvePhyb)}", hooks, vTable[79], type, ResolvePhyb, ResolvePhybHuman);
_resolveKdbPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveKdb)}", hooks, vTable[80], type, ResolveKdb, ResolveKdbHuman);
_vFunc81Hook = Create<SkeletonVFuncDelegate>( $"{name}.{nameof(VFunc81)}", hooks, vTable[81], type, null, VFunc81);
_resolveBnmbPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveBnmb)}", hooks, vTable[82], type, ResolveBnmb, ResolveBnmbHuman);
_vFunc83Hook = Create<SkeletonVFuncDelegate>( $"{name}.{nameof(VFunc83)}", hooks, vTable[83], type, null, VFunc83);
_resolvePapPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolvePap)}", hooks, vTable[84], type, ResolvePap, ResolvePapHuman);
_resolveTmbPathHook = Create<TmbResolveDelegate>( $"{name}.{nameof(ResolveTmb)}", hooks, vTable[85], ResolveTmb);
_resolveMPapPathHook = Create<MPapResolveDelegate>( $"{name}.{nameof(ResolveMPap)}", hooks, vTable[87], ResolveMPap);
_resolveImcPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveImc)}", hooks, vTable[89], ResolveImc);
_resolveMtrlPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolveMtrl)}", hooks, vTable[90], ResolveMtrl);
_resolveSkinMtrlPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSkinMtrl)}", hooks, vTable[91], ResolveSkinMtrl);
_resolveDecalPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveDecal)}", hooks, vTable[92], ResolveDecal);
_resolveVfxPathHook = Create<VfxResolveDelegate>( $"{name}.{nameof(ResolveVfx)}", hooks, vTable[93], type, ResolveVfx, ResolveVfxHuman);
_resolveEidPathHook = Create<SingleResolveDelegate>( $"{name}.{nameof(ResolveEid)}", hooks, vTable[94], ResolveEid);
// @formatter:on
@ -83,6 +85,7 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveMPapPathHook.Enable();
_resolveMdlPathHook.Enable();
_resolveMtrlPathHook.Enable();
_resolveSkinMtrlPathHook.Enable();
_resolvePapPathHook.Enable();
_resolveKdbPathHook.Enable();
_resolvePhybPathHook.Enable();
@ -103,6 +106,7 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveMPapPathHook.Disable();
_resolveMdlPathHook.Disable();
_resolveMtrlPathHook.Disable();
_resolveSkinMtrlPathHook.Disable();
_resolvePapPathHook.Disable();
_resolveKdbPathHook.Disable();
_resolvePhybPathHook.Disable();
@ -123,6 +127,7 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveMPapPathHook.Dispose();
_resolveMdlPathHook.Dispose();
_resolveMtrlPathHook.Dispose();
_resolveSkinMtrlPathHook.Dispose();
_resolvePapPathHook.Dispose();
_resolveKdbPathHook.Dispose();
_resolvePhybPathHook.Dispose();
@ -153,6 +158,9 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
private nint ResolveMtrl(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex, nint mtrlFileName)
=> ResolvePath(drawObject, _resolveMtrlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex, mtrlFileName));
private nint ResolveSkinMtrl(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex)
=> ResolvePath(drawObject, _resolveSkinMtrlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex));
private nint ResolvePap(nint drawObject, nint pathBuffer, nint pathBufferSize, uint unkAnimationIndex, nint animationName)
=> ResolvePath(drawObject, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));

View file

@ -188,7 +188,8 @@ internal unsafe partial record ResolveContext(
return GetOrCreateNode(ResourceType.Tex, (nint)tex->Texture, &tex->ResourceHandle, gamePath);
}
public ResourceNode? CreateNodeFromModel(Model* mdl, ResourceHandle* imc, TextureResourceHandle* decalHandle, ResourceHandle* mpapHandle)
public ResourceNode? CreateNodeFromModel(Model* mdl, ResourceHandle* imc, TextureResourceHandle* decalHandle,
MaterialResourceHandle* skinMtrlHandle, ResourceHandle* mpapHandle)
{
if (mdl is null || mdl->ModelResourceHandle is null)
return null;
@ -218,6 +219,12 @@ internal unsafe partial record ResolveContext(
}
}
if (skinMtrlHandle is not null
&& Utf8GamePath.FromByteString(CharacterBase->ResolveSkinMtrlPathAsByteString(SlotIndex), out var skinMtrlPath)
&& CreateNodeFromMaterial(skinMtrlHandle->Material, skinMtrlPath) is
{ } skinMaaterialNode)
node.Children.Add(skinMaaterialNode);
if (CreateNodeFromDecal(decalHandle, imc) is { } decalNode)
node.Children.Add(decalNode);

View file

@ -70,9 +70,14 @@ public class ResourceTree(
var genericContext = globalContext.CreateContext(model);
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1312)
var mpapArrayPtr = *(ResourceHandle***)((nint)model + 0x948);
var mpapArrayPtr = model->MaterialAnimationPacks;
var mpapArray = mpapArrayPtr is not null ? new ReadOnlySpan<Pointer<ResourceHandle>>(mpapArrayPtr, model->SlotCount) : [];
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1474)
var skinMtrlArray = modelType switch
{
ModelType.Human => new ReadOnlySpan<Pointer<MaterialResourceHandle>>((MaterialResourceHandle**)((nint)model + 0xB48), 5),
_ => [],
};
var decalArray = modelType switch
{
ModelType.Human => human->SlotDecalsSpan,
@ -108,7 +113,8 @@ public class ResourceTree(
var mdl = model->Models[i];
if (slotContext.CreateNodeFromModel(mdl, imc, i < decalArray.Length ? decalArray[(int)i].Value : null,
i < mpapArray.Length ? mpapArray[(int)i].Value : null) is { } mdlNode)
i < skinMtrlArray.Length ? skinMtrlArray[(int)i].Value : null, i < mpapArray.Length ? mpapArray[(int)i].Value : null) is
{ } mdlNode)
{
if (globalContext.WithUiData)
mdlNode.FallbackName = $"Model #{i}";
@ -117,8 +123,7 @@ public class ResourceTree(
}
AddSkeleton(Nodes, genericContext, model->EID, model->Skeleton, model->BonePhysicsModule);
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1312)
AddMaterialAnimationSkeleton(Nodes, genericContext, *(SkeletonResourceHandle**)((nint)model + 0x940));
AddMaterialAnimationSkeleton(Nodes, genericContext, model->MaterialAnimationSkeleton);
AddWeapons(globalContext, model);
@ -149,8 +154,7 @@ public class ResourceTree(
var genericContext = globalContext.CreateContext(subObject, 0xFFFFFFFFu, slot, equipment, weaponType);
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1312)
var mpapArrayPtr = *(ResourceHandle***)((nint)subObject + 0x948);
var mpapArrayPtr = subObject->MaterialAnimationPacks;
var mpapArray = mpapArrayPtr is not null ? new ReadOnlySpan<Pointer<ResourceHandle>>(mpapArrayPtr, subObject->SlotCount) : [];
for (var i = 0; i < subObject->SlotCount; ++i)
@ -166,7 +170,8 @@ public class ResourceTree(
}
var mdl = subObject->Models[i];
if (slotContext.CreateNodeFromModel(mdl, imc, weapon->Decal, i < mpapArray.Length ? mpapArray[i].Value : null) is { } mdlNode)
if (slotContext.CreateNodeFromModel(mdl, imc, weapon->Decal, null, i < mpapArray.Length ? mpapArray[i].Value : null) is
{ } mdlNode)
{
if (globalContext.WithUiData)
mdlNode.FallbackName = $"Weapon #{weaponIndex}, Model #{i}";
@ -176,8 +181,7 @@ public class ResourceTree(
AddSkeleton(weaponNodes, genericContext, subObject->EID, subObject->Skeleton, subObject->BonePhysicsModule,
$"Weapon #{weaponIndex}, ");
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1312)
AddMaterialAnimationSkeleton(weaponNodes, genericContext, *(SkeletonResourceHandle**)((nint)subObject + 0x940),
AddMaterialAnimationSkeleton(weaponNodes, genericContext, subObject->MaterialAnimationSkeleton,
$"Weapon #{weaponIndex}, ");
++weaponIndex;
@ -255,7 +259,7 @@ public class ResourceTree(
for (var i = 0; i < skeleton->PartialSkeletonCount; ++i)
{
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1312)
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1475)
var phybHandle = physics != null ? ((ResourceHandle**)((nint)physics + 0x190))[i] : null;
if (context.CreateNodeFromPartialSkeleton(&skeleton->PartialSkeletons[i], phybHandle, (uint)i) is { } sklbNode)
{

View file

@ -34,6 +34,14 @@ internal static class StructExtensions
return ToOwnedByteString(character.ResolveMtrlPath(pathBuffer, CharacterBase.PathBufferSize, slotIndex, mtrlFileName));
}
public static unsafe CiByteString ResolveSkinMtrlPathAsByteString(ref this CharacterBase character, uint slotIndex)
{
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1474)
var vf91 = (delegate* unmanaged<CharacterBase*, byte*, nuint, uint, byte*>)((nint*)character.VirtualTable)[91];
var pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
return ToOwnedByteString(vf91((CharacterBase*)Unsafe.AsPointer(ref character), pathBuffer, CharacterBase.PathBufferSize, slotIndex));
}
public static CiByteString ResolveMaterialPapPathAsByteString(ref this CharacterBase character, uint slotIndex, uint unkSId)
{
Span<byte> pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];