mirror of
https://github.com/4sval/FModel.git
synced 2026-04-26 00:04:53 -05:00
compute all bones transform
This commit is contained in:
parent
0221405758
commit
51d334cb60
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using CUE4Parse_Conversion.Animations;
|
using CUE4Parse_Conversion.Animations;
|
||||||
using CUE4Parse.UE4.Objects.Core.Math;
|
using CUE4Parse.UE4.Objects.Core.Math;
|
||||||
|
|
@ -10,17 +11,24 @@ public class Animation : IDisposable
|
||||||
public float CurrentTime;
|
public float CurrentTime;
|
||||||
public float DeltaTime;
|
public float DeltaTime;
|
||||||
public CAnimSet CurrentAnimation;
|
public CAnimSet CurrentAnimation;
|
||||||
public Matrix4x4[] FinalBonesMatrix;
|
public Transform[] FinalBonesMatrix;
|
||||||
|
|
||||||
public Animation(CAnimSet anim)
|
public Animation(CAnimSet anim, Dictionary<string, int> nameToIndex, Dictionary<int, Transform> indexToTransform)
|
||||||
{
|
{
|
||||||
CurrentTime = 0f;
|
CurrentTime = 0f;
|
||||||
CurrentAnimation = anim;
|
CurrentAnimation = anim;
|
||||||
|
|
||||||
FinalBonesMatrix = new Matrix4x4[anim.TrackBoneNames.Length];
|
FinalBonesMatrix = new Transform[anim.TrackBoneNames.Length];
|
||||||
for (int i = 0; i < FinalBonesMatrix.Length; i++)
|
for (int i = 0; i < FinalBonesMatrix.Length; i++)
|
||||||
{
|
{
|
||||||
FinalBonesMatrix[i] = Matrix4x4.Identity;
|
if (!nameToIndex.TryGetValue(anim.TrackBoneNames[i].Text, out var boneIndex) ||
|
||||||
|
!indexToTransform.TryGetValue(boneIndex, out var boneTransform))
|
||||||
|
{
|
||||||
|
boneTransform = Transform.Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
FinalBonesMatrix[i] = Transform.Identity;
|
||||||
|
FinalBonesMatrix[i].Relation = boneTransform.Matrix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +37,7 @@ public class Animation : IDisposable
|
||||||
DeltaTime = deltaTime;
|
DeltaTime = deltaTime;
|
||||||
if (CurrentAnimation != null)
|
if (CurrentAnimation != null)
|
||||||
{
|
{
|
||||||
CurrentTime = deltaTime;
|
// CurrentTime = deltaTime;
|
||||||
CalculateBoneTransform();
|
CalculateBoneTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,17 +51,8 @@ public class Animation : IDisposable
|
||||||
var bonePosition = FVector.ZeroVector;
|
var bonePosition = FVector.ZeroVector;
|
||||||
sequence.Tracks[boneIndex].GetBonePosition(CurrentTime, sequence.NumFrames, false, ref bonePosition, ref boneOrientation);
|
sequence.Tracks[boneIndex].GetBonePosition(CurrentTime, sequence.NumFrames, false, ref bonePosition, ref boneOrientation);
|
||||||
|
|
||||||
boneOrientation *= CurrentAnimation.BonePositions[boneIndex].Orientation;
|
FinalBonesMatrix[boneIndex].Rotation = boneOrientation;
|
||||||
bonePosition = boneOrientation.RotateVector(bonePosition);
|
FinalBonesMatrix[boneIndex].Position = bonePosition * Constants.SCALE_DOWN_RATIO;
|
||||||
bonePosition *= Constants.SCALE_DOWN_RATIO;
|
|
||||||
if (CurrentAnimation.TrackBoneNames[boneIndex].Text == "pelvis")
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FinalBonesMatrix[boneIndex] =
|
|
||||||
Matrix4x4.CreateFromQuaternion(boneOrientation) *
|
|
||||||
Matrix4x4.CreateTranslation(bonePosition);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using CUE4Parse_Conversion.Animations;
|
||||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||||
using CUE4Parse.UE4.Objects.UObject;
|
using CUE4Parse.UE4.Objects.UObject;
|
||||||
|
|
@ -9,59 +10,83 @@ namespace FModel.Views.Snooper.Models.Animations;
|
||||||
|
|
||||||
public class Skeleton : IDisposable
|
public class Skeleton : IDisposable
|
||||||
{
|
{
|
||||||
public readonly USkeleton RefSkel;
|
public readonly USkeleton UnrealSkeleton;
|
||||||
|
public readonly Dictionary<string, int> BonesIndexByName;
|
||||||
|
public readonly Dictionary<int, Transform> BonesTransformByIndex;
|
||||||
public readonly bool IsLoaded;
|
public readonly bool IsLoaded;
|
||||||
|
|
||||||
public readonly Socket[] Sockets;
|
public readonly Socket[] Sockets;
|
||||||
|
|
||||||
public Animation Anim;
|
public Animation Anim;
|
||||||
|
|
||||||
public Skeleton(FPackageIndex package)
|
public Skeleton(FPackageIndex package)
|
||||||
{
|
{
|
||||||
RefSkel = package.Load<USkeleton>();
|
UnrealSkeleton = package.Load<USkeleton>();
|
||||||
if (RefSkel == null) return;
|
if (UnrealSkeleton == null) return;
|
||||||
|
|
||||||
|
BonesIndexByName = UnrealSkeleton.ReferenceSkeleton.FinalNameToIndexMap;
|
||||||
|
BonesTransformByIndex = new Dictionary<int, Transform>();
|
||||||
|
foreach ((_, int boneIndex) in BonesIndexByName)
|
||||||
|
{
|
||||||
|
var transforms = new List<Transform>();
|
||||||
|
var parentBoneIndex = boneIndex;
|
||||||
|
while (parentBoneIndex > -1)
|
||||||
|
{
|
||||||
|
var parentFound = BonesTransformByIndex.TryGetValue(parentBoneIndex, out var boneTransform);
|
||||||
|
if (!parentFound)
|
||||||
|
{
|
||||||
|
var bone = UnrealSkeleton.ReferenceSkeleton.FinalRefBonePose[parentBoneIndex];
|
||||||
|
boneTransform = new Transform
|
||||||
|
{
|
||||||
|
Rotation = bone.Rotation,
|
||||||
|
Position = bone.Translation * Constants.SCALE_DOWN_RATIO,
|
||||||
|
Scale = bone.Scale3D
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parentBoneIndex = UnrealSkeleton.ReferenceSkeleton.FinalRefBoneInfo[parentBoneIndex].ParentIndex;
|
||||||
|
transforms.Add(boneTransform);
|
||||||
|
if (parentFound) parentBoneIndex = -1; // the parent transform is already relative to all its parent so we can just skip
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = transforms.Count - 2; j > -1; j--)
|
||||||
|
{
|
||||||
|
transforms[j].Relation *= transforms[j + 1].Matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
BonesTransformByIndex[boneIndex] = transforms[0];
|
||||||
|
transforms.Clear();
|
||||||
|
}
|
||||||
IsLoaded = true;
|
IsLoaded = true;
|
||||||
Sockets = new Socket[RefSkel.Sockets.Length];
|
|
||||||
|
Sockets = new Socket[UnrealSkeleton.Sockets.Length];
|
||||||
for (int i = 0; i < Sockets.Length; i++)
|
for (int i = 0; i < Sockets.Length; i++)
|
||||||
{
|
{
|
||||||
if (RefSkel.Sockets[i].Load<USkeletalMeshSocket>() is not { } socket) continue;
|
if (UnrealSkeleton.Sockets[i].Load<USkeletalMeshSocket>() is not { } socket) continue;
|
||||||
|
|
||||||
if (!RefSkel.ReferenceSkeleton.FinalNameToIndexMap.TryGetValue(socket.BoneName.Text, out var boneIndex))
|
if (!BonesIndexByName.TryGetValue(socket.BoneName.Text, out var boneIndex) ||
|
||||||
|
!BonesTransformByIndex.TryGetValue(boneIndex, out var boneTransform))
|
||||||
{
|
{
|
||||||
Sockets[i] = new Socket(socket);
|
Sockets[i] = new Socket(socket);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var transforms = new List<Transform>();
|
Sockets[i] = new Socket(socket, boneTransform);
|
||||||
while (boneIndex > -1)
|
|
||||||
{
|
|
||||||
var bone = RefSkel.ReferenceSkeleton.FinalRefBonePose[boneIndex];
|
|
||||||
boneIndex = RefSkel.ReferenceSkeleton.FinalRefBoneInfo[boneIndex].ParentIndex;
|
|
||||||
|
|
||||||
transforms.Add(new Transform
|
|
||||||
{
|
|
||||||
Rotation = bone.Rotation,
|
|
||||||
Position = bone.Translation * Constants.SCALE_DOWN_RATIO,
|
|
||||||
Scale = bone.Scale3D
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = transforms.Count - 2; j > -1; j--)
|
|
||||||
{
|
|
||||||
transforms[j].Relation *= transforms[j + 1].Matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sockets[i] = new Socket(socket, transforms[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAnimation(CAnimSet anim)
|
||||||
|
{
|
||||||
|
Anim = new Animation(anim, BonesIndexByName, BonesTransformByIndex);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetUniform(Shader shader)
|
public void SetUniform(Shader shader)
|
||||||
{
|
{
|
||||||
if (!IsLoaded) return;
|
if (!IsLoaded) return;
|
||||||
for (var i = 0; i < Anim?.FinalBonesMatrix.Length; i++)
|
for (var i = 0; i < Anim?.FinalBonesMatrix.Length; i++)
|
||||||
{
|
{
|
||||||
shader.SetUniform($"uFinalBonesMatrix[{i}]", Anim.FinalBonesMatrix[i]);
|
shader.SetUniform($"uFinalBonesMatrix[{i}]", Anim.FinalBonesMatrix[i].Matrix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public class Socket : IDisposable
|
||||||
{
|
{
|
||||||
Name = socket.SocketName.Text;
|
Name = socket.SocketName.Text;
|
||||||
Bone = socket.BoneName.Text;
|
Bone = socket.BoneName.Text;
|
||||||
Transform = transform;
|
Transform = Transform.Identity;
|
||||||
Transform.Relation = transform.Matrix;
|
Transform.Relation = transform.Matrix;
|
||||||
Transform.Rotation = socket.RelativeRotation.Quaternion();
|
Transform.Rotation = socket.RelativeRotation.Quaternion();
|
||||||
Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO;
|
Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO;
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,10 @@ public class Renderer : IDisposable
|
||||||
public void Animate(UAnimSequence animSequence)
|
public void Animate(UAnimSequence animSequence)
|
||||||
{
|
{
|
||||||
if (!Options.TryGetModel(out var model) || !model.Skeleton.IsLoaded ||
|
if (!Options.TryGetModel(out var model) || !model.Skeleton.IsLoaded ||
|
||||||
model.Skeleton?.RefSkel.ConvertAnims(animSequence) is not { } anim || anim.Sequences.Count == 0)
|
model.Skeleton?.UnrealSkeleton.ConvertAnims(animSequence) is not { } anim || anim.Sequences.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
model.Skeleton.Anim = new Animation(anim);
|
model.Skeleton.SetAnimation(anim);
|
||||||
Options.AnimateMesh(false);
|
Options.AnimateMesh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -338,7 +338,6 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
||||||
_saver.Value = model.TrySave(out _saver.Label, out _saver.Path);
|
_saver.Value = model.TrySave(out _saver.Label, out _saver.Path);
|
||||||
s.WindowShouldFreeze(false);
|
s.WindowShouldFreeze(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.BeginDisabled(true);
|
ImGui.BeginDisabled(true);
|
||||||
// ImGui.BeginDisabled(!model.HasSkeleton);
|
// ImGui.BeginDisabled(!model.HasSkeleton);
|
||||||
if (ImGui.Selectable("Animate"))
|
if (ImGui.Selectable("Animate"))
|
||||||
|
|
@ -346,7 +345,6 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
||||||
s.Renderer.Options.AnimateMesh(true);
|
s.Renderer.Options.AnimateMesh(true);
|
||||||
s.WindowShouldClose(true, false);
|
s.WindowShouldClose(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
if (ImGui.Selectable("Teleport To"))
|
if (ImGui.Selectable("Teleport To"))
|
||||||
{
|
{
|
||||||
|
|
@ -432,8 +430,8 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
||||||
Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}");
|
Layout("Guid");ImGui.Text($" : {s.Renderer.Options.SelectedModel.ToString(EGuidFormats.UniqueObjectGuid)}");
|
||||||
if (model.HasSkeleton)
|
if (model.HasSkeleton)
|
||||||
{
|
{
|
||||||
Layout("Skeleton");ImGui.Text($" : {model.Skeleton.RefSkel.Name}");
|
Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}");
|
||||||
Layout("Bones");ImGui.Text($" : x{model.Skeleton.RefSkel.BoneTree.Length}");
|
Layout("Bones");ImGui.Text($" : x{model.Skeleton.UnrealSkeleton.BoneTree.Length}");
|
||||||
Layout("Sockets");ImGui.Text($" : x{model.Skeleton.Sockets.Length}");
|
Layout("Sockets");ImGui.Text($" : x{model.Skeleton.Sockets.Length}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user