mirror of
https://github.com/4sval/FModel.git
synced 2026-04-05 00:26:17 -05:00
compute all bones transform
This commit is contained in:
parent
0221405758
commit
51d334cb60
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user