compute all bones transform

This commit is contained in:
4sval 2023-01-06 16:48:39 +01:00
parent 0221405758
commit 51d334cb60
5 changed files with 72 additions and 50 deletions

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using CUE4Parse_Conversion.Animations;
using CUE4Parse.UE4.Objects.Core.Math;
@ -10,17 +11,24 @@ public class Animation : IDisposable
public float CurrentTime;
public float DeltaTime;
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;
CurrentAnimation = anim;
FinalBonesMatrix = new Matrix4x4[anim.TrackBoneNames.Length];
FinalBonesMatrix = new Transform[anim.TrackBoneNames.Length];
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;
if (CurrentAnimation != null)
{
CurrentTime = deltaTime;
// CurrentTime = deltaTime;
CalculateBoneTransform();
}
}
@ -43,17 +51,8 @@ public class Animation : IDisposable
var bonePosition = FVector.ZeroVector;
sequence.Tracks[boneIndex].GetBonePosition(CurrentTime, sequence.NumFrames, false, ref bonePosition, ref boneOrientation);
boneOrientation *= CurrentAnimation.BonePositions[boneIndex].Orientation;
bonePosition = boneOrientation.RotateVector(bonePosition);
bonePosition *= Constants.SCALE_DOWN_RATIO;
if (CurrentAnimation.TrackBoneNames[boneIndex].Text == "pelvis")
{
}
FinalBonesMatrix[boneIndex] =
Matrix4x4.CreateFromQuaternion(boneOrientation) *
Matrix4x4.CreateTranslation(bonePosition);
FinalBonesMatrix[boneIndex].Rotation = boneOrientation;
FinalBonesMatrix[boneIndex].Position = bonePosition * Constants.SCALE_DOWN_RATIO;
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using CUE4Parse_Conversion.Animations;
using CUE4Parse.UE4.Assets.Exports.Animation;
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Objects.UObject;
@ -9,59 +10,83 @@ namespace FModel.Views.Snooper.Models.Animations;
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 Socket[] Sockets;
public Animation Anim;
public Skeleton(FPackageIndex package)
{
RefSkel = package.Load<USkeleton>();
if (RefSkel == null) return;
UnrealSkeleton = package.Load<USkeleton>();
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;
Sockets = new Socket[RefSkel.Sockets.Length];
Sockets = new Socket[UnrealSkeleton.Sockets.Length];
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);
}
else
{
var transforms = new List<Transform>();
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]);
Sockets[i] = new Socket(socket, boneTransform);
}
}
}
public void SetAnimation(CAnimSet anim)
{
Anim = new Animation(anim, BonesIndexByName, BonesTransformByIndex);
}
public void SetUniform(Shader shader)
{
if (!IsLoaded) return;
for (var i = 0; i < Anim?.FinalBonesMatrix.Length; i++)
{
shader.SetUniform($"uFinalBonesMatrix[{i}]", Anim.FinalBonesMatrix[i]);
shader.SetUniform($"uFinalBonesMatrix[{i}]", Anim.FinalBonesMatrix[i].Matrix);
}
}

View File

@ -23,7 +23,7 @@ public class Socket : IDisposable
{
Name = socket.SocketName.Text;
Bone = socket.BoneName.Text;
Transform = transform;
Transform = Transform.Identity;
Transform.Relation = transform.Matrix;
Transform.Rotation = socket.RelativeRotation.Quaternion();
Transform.Position = socket.RelativeLocation * Constants.SCALE_DOWN_RATIO;

View File

@ -89,10 +89,10 @@ public class Renderer : IDisposable
public void Animate(UAnimSequence animSequence)
{
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;
model.Skeleton.Anim = new Animation(anim);
model.Skeleton.SetAnimation(anim);
Options.AnimateMesh(false);
}

View File

@ -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);
s.WindowShouldFreeze(false);
}
ImGui.BeginDisabled(true);
// ImGui.BeginDisabled(!model.HasSkeleton);
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.WindowShouldClose(true, false);
}
ImGui.EndDisabled();
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)}");
if (model.HasSkeleton)
{
Layout("Skeleton");ImGui.Text($" : {model.Skeleton.RefSkel.Name}");
Layout("Bones");ImGui.Text($" : x{model.Skeleton.RefSkel.BoneTree.Length}");
Layout("Skeleton");ImGui.Text($" : {model.Skeleton.UnrealSkeleton.Name}");
Layout("Bones");ImGui.Text($" : x{model.Skeleton.UnrealSkeleton.BoneTree.Length}");
Layout("Sockets");ImGui.Text($" : x{model.Skeleton.Sockets.Length}");
}
else