diff --git a/CUE4Parse b/CUE4Parse
index b18da130..d0c44876 160000
--- a/CUE4Parse
+++ b/CUE4Parse
@@ -1 +1 @@
-Subproject commit b18da13023101f3d050420cf4878f4a1175dc344
+Subproject commit d0c44876fc718e0893ed699551bc354b8d2a5422
diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs
index 71508d8e..449f04a0 100644
--- a/FModel/MainWindow.xaml.cs
+++ b/FModel/MainWindow.xaml.cs
@@ -85,10 +85,10 @@ public partial class MainWindow
#if DEBUG
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
- "ShooterGame/Content/Characters/Guide/S0/3P/Models/TP_Guide_S0_Skelmesh.uasset"));
+ "fortnitegame/Content/Characters/Player/Female/Medium/Bodies/F_Med_Soldier_01/Meshes/F_Med_Soldier_01.uasset"));
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
- "ShooterGame/Content/Characters/Guide/S0/Ability_Q/3P/Anims/TP_Guide_S0_Q_WolfTotem_Cast_Outro_Montage.uasset"));
+ "fortnitegame/Content/Animation/Game/MainPlayer/Emotes/Cowbell/Cowbell_CMM_Loop_M.uasset"));
#endif
}
diff --git a/FModel/Resources/default.vert b/FModel/Resources/default.vert
index 1cae7fda..3d530a5b 100644
--- a/FModel/Resources/default.vert
+++ b/FModel/Resources/default.vert
@@ -16,10 +16,15 @@ layout(std430, binding = 1) buffer BoneMatrices
{
mat4 uFinalBonesMatrix[];
};
+layout(std430, binding = 2) buffer RestBoneMatrices
+{
+ mat4 uRestBonesMatrix[];
+};
uniform mat4 uView;
uniform mat4 uProjection;
uniform float uMorphTime;
+uniform bool uIsAnimated;
out vec3 fPos;
out vec3 fNormal;
@@ -37,14 +42,14 @@ void main()
vec4 finalPos = vec4(0.0);
vec4 finalNormal = vec4(0.0);
vec4 finalTangent = vec4(0.0);
- if (vBoneIds != vBoneWeights)
+ if (uIsAnimated)
{
for(int i = 0 ; i < 4; i++)
{
int boneIndex = int(vBoneIds[i]);
if(boneIndex < 0) break;
- mat4 boneMatrix = uFinalBonesMatrix[boneIndex];
+ mat4 boneMatrix = uFinalBonesMatrix[boneIndex] * inverse(uRestBonesMatrix[boneIndex]);
mat4 inverseBoneMatrix = transpose(inverse(boneMatrix));
float weight = vBoneWeights[i];
diff --git a/FModel/Resources/outline.vert b/FModel/Resources/outline.vert
index 56de336d..ffb85c6c 100644
--- a/FModel/Resources/outline.vert
+++ b/FModel/Resources/outline.vert
@@ -11,11 +11,16 @@ layout(std430, binding = 1) buffer BoneMatrices
{
mat4 uFinalBonesMatrix[];
};
+layout(std430, binding = 2) buffer RestBoneMatrices
+{
+ mat4 uRestBonesMatrix[];
+};
uniform mat4 uView;
uniform vec3 uViewPos;
uniform mat4 uProjection;
uniform float uMorphTime;
+uniform bool uIsAnimated;
void main()
{
@@ -24,14 +29,14 @@ void main()
vec4 finalPos = vec4(0.0);
vec4 finalNormal = vec4(0.0);
- if (vBoneIds != vBoneWeights)
+ if (uIsAnimated)
{
for(int i = 0 ; i < 4; i++)
{
int boneIndex = int(vBoneIds[i]);
if(boneIndex < 0) break;
- mat4 boneMatrix = uFinalBonesMatrix[boneIndex];
+ mat4 boneMatrix = uFinalBonesMatrix[boneIndex] * inverse(uRestBonesMatrix[boneIndex]);
float weight = vBoneWeights[i];
finalPos += boneMatrix * bindPos * weight;
diff --git a/FModel/Resources/picking.vert b/FModel/Resources/picking.vert
index 9f354e9f..7d267c0c 100644
--- a/FModel/Resources/picking.vert
+++ b/FModel/Resources/picking.vert
@@ -10,24 +10,29 @@ layout(std430, binding = 1) buffer BoneMatrices
{
mat4 uFinalBonesMatrix[];
};
+layout(std430, binding = 2) buffer RestBoneMatrices
+{
+ mat4 uRestBonesMatrix[];
+};
uniform mat4 uView;
uniform mat4 uProjection;
uniform float uMorphTime;
+uniform bool uIsAnimated;
void main()
{
vec4 bindPos = vec4(mix(vPos, vMorphTargetPos, uMorphTime), 1.0);
vec4 finalPos = vec4(0.0);
- if (vBoneIds != vBoneWeights)
+ if (uIsAnimated)
{
for(int i = 0 ; i < 4; i++)
{
int boneIndex = int(vBoneIds[i]);
if(boneIndex < 0) break;
- finalPos += uFinalBonesMatrix[boneIndex] * bindPos * vBoneWeights[i];
+ finalPos += uFinalBonesMatrix[boneIndex] * inverse(uRestBonesMatrix[boneIndex]) * bindPos * vBoneWeights[i];
}
}
else finalPos = bindPos;
diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml
index 162ffa1f..61f071b9 100644
--- a/FModel/Views/Resources/Resources.xaml
+++ b/FModel/Views/Resources/Resources.xaml
@@ -655,47 +655,50 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FModel/Views/Snooper/Animations/Animation.cs b/FModel/Views/Snooper/Animations/Animation.cs
index 028676c5..14b51c71 100644
--- a/FModel/Views/Snooper/Animations/Animation.cs
+++ b/FModel/Views/Snooper/Animations/Animation.cs
@@ -11,8 +11,8 @@ namespace FModel.Views.Snooper.Animations;
public class Animation : IDisposable
{
private readonly UObject _export;
- private readonly CAnimSet _animSet;
+ public readonly CAnimSet UnrealAnim;
public readonly string Path;
public readonly string Name;
public readonly Sequence[] Sequences;
@@ -38,12 +38,12 @@ public class Animation : IDisposable
public Animation(UObject export, CAnimSet animSet) : this(export)
{
- _animSet = animSet;
+ UnrealAnim = animSet;
- Sequences = new Sequence[_animSet.Sequences.Count];
+ Sequences = new Sequence[UnrealAnim.Sequences.Count];
for (int i = 0; i < Sequences.Length; i++)
{
- Sequences[i] = new Sequence(_animSet.Sequences[i]);
+ Sequences[i] = new Sequence(UnrealAnim.Sequences[i]);
EndTime = Sequences[i].EndTime;
}
@@ -62,7 +62,7 @@ public class Animation : IDisposable
for (int i = 0; i < Sequences.Length; i++)
{
var sequence = Sequences[i];
- if (elapsedTime < sequence.EndTime && elapsedTime >= sequence.StartTime)
+ if (elapsedTime <= sequence.EndTime && elapsedTime >= sequence.StartTime)
{
Framing[i] = (elapsedTime - sequence.StartTime) / sequence.TimePerFrame;
}
@@ -122,7 +122,7 @@ public class Animation : IDisposable
{
if (selected) AttachedModels.Remove(guid); else AttachedModels.Add(guid);
model.Skeleton.ResetAnimatedData(true);
- if (!selected) model.Skeleton.Animate(_animSet, s.Renderer.AnimateWithRotationOnly);
+ if (!selected) model.Skeleton.Animate(UnrealAnim);
}
}
ImGui.EndMenu();
diff --git a/FModel/Views/Snooper/Animations/Skeleton.cs b/FModel/Views/Snooper/Animations/Skeleton.cs
index 94f33938..a2cf922f 100644
--- a/FModel/Views/Snooper/Animations/Skeleton.cs
+++ b/FModel/Views/Snooper/Animations/Skeleton.cs
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Numerics;
using CUE4Parse_Conversion.Animations.PSA;
using CUE4Parse.UE4.Assets.Exports.Animation;
-using CUE4Parse.UE4.Objects.Core.Math;
-using CUE4Parse.Utils;
using FModel.Views.Snooper.Buffers;
+using FModel.Views.Snooper.Shading;
using OpenTK.Graphics.OpenGL4;
using Serilog;
@@ -14,27 +14,24 @@ namespace FModel.Views.Snooper.Animations;
public class Skeleton : IDisposable
{
private int _handle;
+ private BufferObject _rest;
private BufferObject _ssbo;
public string Name;
- public readonly Dictionary BonesByLoweredName;
+ public bool IsAnimated { get; private set; }
- private Transform[][][] _animatedBonesTransform; // [sequence][bone][frame]
- private readonly Matrix4x4[] _invertedBonesMatrix;
- public int BoneCount => _invertedBonesMatrix.Length;
- public bool IsAnimated => _animatedBonesTransform.Length > 0;
+ public readonly int BoneCount;
+ public readonly Dictionary BonesByLoweredName;
public Skeleton()
{
BonesByLoweredName = new Dictionary();
- _animatedBonesTransform = Array.Empty();
- _invertedBonesMatrix = Array.Empty();
}
public Skeleton(FReferenceSkeleton referenceSkeleton) : this()
{
- _invertedBonesMatrix = new Matrix4x4[referenceSkeleton.FinalRefBoneInfo.Length];
- for (int boneIndex = 0; boneIndex < _invertedBonesMatrix.Length; boneIndex++)
+ BoneCount = referenceSkeleton.FinalRefBoneInfo.Length;
+ for (int boneIndex = 0; boneIndex < BoneCount; boneIndex++)
{
var info = referenceSkeleton.FinalRefBoneInfo[boneIndex];
var boneTransform = new Transform
@@ -53,128 +50,35 @@ public class Skeleton : IDisposable
}
BonesByLoweredName[info.Name.Text.ToLower()] = bone;
-
- Matrix4x4.Invert(boneTransform.Matrix, out var inverted);
- _invertedBonesMatrix[bone.Index] = inverted;
}
}
- public void Animate(CAnimSet anim, bool rotationOnly)
- {
- MapSkeleton(anim);
-
- _animatedBonesTransform = new Transform[anim.Sequences.Count][][];
- for (int s = 0; s < _animatedBonesTransform.Length; s++)
- {
- var sequence = anim.Sequences[s];
- _animatedBonesTransform[s] = new Transform[BoneCount][];
- foreach (var bone in BonesByLoweredName.Values)
- {
- _animatedBonesTransform[s][bone.Index] = new Transform[sequence.NumFrames];
-
- var skeletonBoneIndex = bone.SkeletonIndex;
- if (sequence.OriginalSequence.FindTrackForBoneIndex(skeletonBoneIndex) < 0)
- {
- for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++)
- {
- _animatedBonesTransform[s][bone.Index][frame] = new Transform
- {
- Relation = bone.IsRoot ? bone.Rest.Relation :
- bone.Rest.LocalMatrix * _animatedBonesTransform[s][bone.ParentIndex][frame].Matrix
- };
- }
- }
- else
- {
- bone.AnimatedBySequences.Add(s);
- for (int frame = 0; frame < _animatedBonesTransform[s][bone.Index].Length; frame++)
- {
- var boneOrientation = bone.Rest.Rotation;
- var bonePosition = bone.Rest.Position;
- var boneScale = bone.Rest.Scale;
-
- sequence.Tracks[skeletonBoneIndex].GetBoneTransform(frame, sequence.NumFrames, ref boneOrientation, ref bonePosition, ref boneScale);
-
- switch (anim.Skeleton.BoneTree[skeletonBoneIndex])
- {
- case EBoneTranslationRetargetingMode.Skeleton when !rotationOnly:
- {
- var targetTransform = sequence.RetargetBasePose?[skeletonBoneIndex] ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex];
- bonePosition = targetTransform.Translation;
- break;
- }
- case EBoneTranslationRetargetingMode.AnimationScaled when !rotationOnly:
- {
- var sourceTranslationLength = (bone.Rest.Position / Constants.SCALE_DOWN_RATIO).Size();
- if (sourceTranslationLength > UnrealMath.KindaSmallNumber)
- {
- var targetTranslationLength = sequence.RetargetBasePose?[skeletonBoneIndex].Translation.Size() ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex].Translation.Size();
- bonePosition.Scale(targetTranslationLength / sourceTranslationLength);
- }
- break;
- }
- case EBoneTranslationRetargetingMode.AnimationRelative when !rotationOnly:
- {
- // can't tell if it's working or not
- var sourceSkelTrans = bone.Rest.Position / Constants.SCALE_DOWN_RATIO;
- var refPoseTransform = sequence.RetargetBasePose?[skeletonBoneIndex] ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex];
-
- boneOrientation = boneOrientation * FQuat.Conjugate(bone.Rest.Rotation) * refPoseTransform.Rotation;
- bonePosition += refPoseTransform.Translation - sourceSkelTrans;
- boneScale *= refPoseTransform.Scale3D * bone.Rest.Scale;
- boneOrientation.Normalize();
- break;
- }
- case EBoneTranslationRetargetingMode.OrientAndScale when !rotationOnly:
- {
- var sourceSkelTrans = bone.Rest.Position / Constants.SCALE_DOWN_RATIO;
- var targetSkelTrans = sequence.RetargetBasePose?[skeletonBoneIndex].Translation ?? anim.Skeleton.ReferenceSkeleton.FinalRefBonePose[skeletonBoneIndex].Translation;
-
- if (!sourceSkelTrans.Equals(targetSkelTrans))
- {
- var sourceSkelTransLength = sourceSkelTrans.Size();
- var targetSkelTransLength = targetSkelTrans.Size();
- if (!UnrealMath.IsNearlyZero(sourceSkelTransLength * targetSkelTransLength))
- {
- var sourceSkelTransDir = sourceSkelTrans / sourceSkelTransLength;
- var targetSkelTransDir = targetSkelTrans / targetSkelTransLength;
-
- var deltaRotation = FQuat.FindBetweenNormals(sourceSkelTransDir, targetSkelTransDir);
- var scale = targetSkelTransLength / sourceSkelTransLength;
- bonePosition = deltaRotation.RotateVector(bonePosition) * scale;
- }
- }
- break;
- }
- }
-
- _animatedBonesTransform[s][bone.Index][frame] = new Transform
- {
- Relation = bone.IsRoot ? bone.Rest.Relation : _animatedBonesTransform[s][bone.ParentIndex][frame].Matrix,
- Rotation = boneOrientation,
- Position = rotationOnly ? bone.Rest.Position : bonePosition * Constants.SCALE_DOWN_RATIO,
- Scale = boneScale
- };
- }
- }
- }
- }
- }
-
- private void MapSkeleton(CAnimSet anim)
+ public void Animate(CAnimSet animation)
{
+ IsAnimated = true;
ResetAnimatedData();
// map bones
- for (int boneIndex = 0; boneIndex < anim.Skeleton.BoneCount; boneIndex++)
+ for (int boneIndex = 0; boneIndex < animation.Skeleton.BoneCount; boneIndex++)
{
- var info = anim.Skeleton.ReferenceSkeleton.FinalRefBoneInfo[boneIndex];
+ var info = animation.Skeleton.ReferenceSkeleton.FinalRefBoneInfo[boneIndex];
if (!BonesByLoweredName.TryGetValue(info.Name.Text.ToLower(), out var bone))
continue;
bone.SkeletonIndex = boneIndex;
}
+ // find playable sequences
+ for (int s = 0; s < animation.Sequences.Count; s++)
+ {
+ var sequence = animation.Sequences[s];
+ foreach (var bone in BonesByLoweredName.Values.Where(bone => sequence.OriginalSequence.FindTrackForBoneIndex(bone.SkeletonIndex) >= 0))
+ {
+ bone.AnimatedBySequences.Add(s);
+ }
+ sequence.Retarget(animation.Skeleton);
+ }
+
#if DEBUG
foreach ((var boneName, var bone) in BonesByLoweredName)
{
@@ -195,7 +99,7 @@ public class Skeleton : IDisposable
}
if (!full) return;
- _animatedBonesTransform = Array.Empty();
+ IsAnimated = false;
_ssbo.UpdateRange(BoneCount, Matrix4x4.Identity);
}
@@ -203,11 +107,18 @@ public class Skeleton : IDisposable
{
_handle = GL.CreateProgram();
+ _rest = new BufferObject(BoneCount, BufferTarget.ShaderStorageBuffer);
+ foreach (var bone in BonesByLoweredName.Values)
+ {
+ _rest.Update(bone.Index, bone.Rest.Matrix);
+ }
+ _rest.Unbind();
+
_ssbo = new BufferObject(BoneCount, BufferTarget.ShaderStorageBuffer);
_ssbo.UpdateRange(BoneCount, Matrix4x4.Identity);
}
- public void UpdateAnimationMatrices(Animation animation)
+ public void UpdateAnimationMatrices(Animation animation, bool rotationOnly)
{
if (!IsAnimated) return;
@@ -215,17 +126,28 @@ public class Skeleton : IDisposable
foreach (var bone in BonesByLoweredName.Values)
{
- var (s, f) = GetBoneFrameData(bone, animation);
- var frameInSequence = Math.Min(f.FloorToInt(), animation.Sequences[s].EndFrame);
- var nextFrameInSequence = Math.Min(frameInSequence + 1, animation.Sequences[s].EndFrame);
- var lerpAmount = Math.Clamp(f - frameInSequence, 0, 1);
+ var boneRelation = bone.IsRoot ? bone.Rest.Relation : bone.Rest.LocalMatrix * _ssbo.Get(bone.ParentIndex);
+ if (bone.IsAnimated)
+ {
+ var (s, f) = GetBoneFrameData(bone, animation);
+ var sequence = animation.UnrealAnim.Sequences[s];
+ var boneOrientation = bone.Rest.Rotation;
+ var bonePosition = bone.Rest.Position;
+ var boneScale = bone.Rest.Scale;
- var boneIndex = bone.Index;
- var matrix = Matrix4x4.Lerp(
- _animatedBonesTransform[s][boneIndex][frameInSequence].Matrix,
- _animatedBonesTransform[s][boneIndex][nextFrameInSequence].Matrix,
- lerpAmount);
- _ssbo.Update(boneIndex, _invertedBonesMatrix[boneIndex] * matrix);
+ sequence.Tracks[bone.SkeletonIndex].GetBoneTransform(f, sequence.NumFrames, ref boneOrientation, ref bonePosition, ref boneScale);
+ if (!bone.IsRoot) boneRelation = _ssbo.Get(bone.ParentIndex);
+ bonePosition = rotationOnly ? bone.Rest.Position : bonePosition * Constants.SCALE_DOWN_RATIO;
+
+ _ssbo.Update(bone.Index, new Transform
+ {
+ Relation = boneRelation,
+ Rotation = boneOrientation,
+ Position = bonePosition,
+ Scale = boneScale
+ }.Matrix);
+ }
+ else _ssbo.Update(bone.Index, boneRelation);
}
_ssbo.Unbind();
@@ -260,17 +182,28 @@ public class Skeleton : IDisposable
return (s, f);
}
- public Matrix4x4 GetBoneMatrix(Bone bone) => IsAnimated ? bone.Rest.Matrix * _ssbo.Get(bone.Index) : bone.Rest.Matrix;
-
- public void Render()
+ public Matrix4x4 GetBoneMatrix(Bone bone)
{
+ _ssbo.Bind();
+ var anim = _ssbo.Get(bone.Index);
+ _ssbo.Unbind();
+
+ return IsAnimated ? anim : bone.Rest.Matrix;
+ }
+
+ public void Render(Shader shader)
+ {
+ shader.SetUniform("uIsAnimated", IsAnimated);
+
_ssbo.BindBufferBase(1);
+ _rest.BindBufferBase(2);
}
public void Dispose()
{
BonesByLoweredName.Clear();
+ _rest?.Dispose();
_ssbo?.Dispose();
GL.DeleteProgram(_handle);
}
diff --git a/FModel/Views/Snooper/Buffers/BufferObject.cs b/FModel/Views/Snooper/Buffers/BufferObject.cs
index db8810b5..16ad16dc 100644
--- a/FModel/Views/Snooper/Buffers/BufferObject.cs
+++ b/FModel/Views/Snooper/Buffers/BufferObject.cs
@@ -40,17 +40,15 @@ public class BufferObject : IDisposable where TDataType : unmanaged
public unsafe void Update(TDataType[] data)
{
+ Bind();
GL.BufferSubData(_bufferTarget, IntPtr.Zero, data.Length * sizeof(TDataType), data);
+ Unbind();
}
public unsafe TDataType Get(int offset)
{
TDataType data = default;
-
- Bind();
GL.GetBufferSubData(_bufferTarget, (IntPtr) (offset * sizeof(TDataType)), sizeof(TDataType), ref data);
- Unbind();
-
return data;
}
diff --git a/FModel/Views/Snooper/Models/Model.cs b/FModel/Views/Snooper/Models/Model.cs
index a931ccfc..05739dd0 100644
--- a/FModel/Views/Snooper/Models/Model.cs
+++ b/FModel/Views/Snooper/Models/Model.cs
@@ -285,9 +285,7 @@ public class Model : IDisposable
public void UpdateMorph(int index)
{
- _morphVbo.Bind();
_morphVbo.Update(Morphs[index].Vertices);
- _morphVbo.Unbind();
}
public void AttachModel(Model attachedTo, Socket socket, SocketAttachementInfo info)
@@ -398,7 +396,7 @@ public class Model : IDisposable
_vao.Bind();
shader.SetUniform("uMorphTime", MorphTime);
- if (HasSkeleton) Skeleton.Render();
+ if (HasSkeleton) Skeleton.Render(shader);
if (!outline)
{
shader.SetUniform("uUvCount", UvCount);
@@ -434,7 +432,7 @@ public class Model : IDisposable
_vao.Bind();
shader.SetUniform("uMorphTime", MorphTime);
- if (HasSkeleton) Skeleton.Render();
+ if (HasSkeleton) Skeleton.Render(shader);
foreach (var section in Sections)
{
diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs
index b25391f2..0b2ee808 100644
--- a/FModel/Views/Snooper/Renderer.cs
+++ b/FModel/Views/Snooper/Renderer.cs
@@ -115,7 +115,7 @@ public class Renderer : IDisposable
var animSet = skeleton.ConvertAnims(animSequence);
var animation = new Animation(animSequence, animSet, guid);
maxElapsedTime = animation.TotalElapsedTime;
- model.Skeleton.Animate(animSet, AnimateWithRotationOnly);
+ model.Skeleton.Animate(animSet);
Options.AddAnimation(animation);
break;
}
@@ -124,7 +124,7 @@ public class Renderer : IDisposable
var animSet = skeleton.ConvertAnims(animMontage);
var animation = new Animation(animMontage, animSet, guid);
maxElapsedTime = animation.TotalElapsedTime;
- model.Skeleton.Animate(animSet, AnimateWithRotationOnly);
+ model.Skeleton.Animate(animSet);
Options.AddAnimation(animation);
foreach (var notifyEvent in animMontage.Notifies)
@@ -191,7 +191,7 @@ public class Renderer : IDisposable
var animSet = skeleton.ConvertAnims(animComposite);
var animation = new Animation(animComposite, animSet, guid);
maxElapsedTime = animation.TotalElapsedTime;
- model.Skeleton.Animate(animSet, AnimateWithRotationOnly);
+ model.Skeleton.Animate(animSet);
Options.AddAnimation(animation);
break;
}
@@ -267,7 +267,7 @@ public class Renderer : IDisposable
animation.TimeCalculation(Options.Tracker.ElapsedTime);
foreach (var guid in animation.AttachedModels.Where(guid => Options.Models[guid].HasSkeleton))
{
- Options.Models[guid].Skeleton.UpdateAnimationMatrices(animation);
+ Options.Models[guid].Skeleton.UpdateAnimationMatrices(animation, AnimateWithRotationOnly);
}
}