Files
glitchy-engine-beef/GlitchyEngine/src/Renderer/Animation/AnimationClip.bf
T
Simon Lübeß 9e1f648aa0 Transitioned from VectorX to floatX
And some trying to update Rigidbody when transform changes
2023-06-25 02:47:33 +02:00

280 lines
6.2 KiB
Beef

using System;
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer.Animation
{
class AnimationPlayer
{
public Skeleton Skeleton ~ _.ReleaseRef();
public AnimationClip CurrentClip ~ _.ReleaseRef();
public float TimeStamp;
public SkeletonPose Pose ~ delete _;
public Matrix[] SkinningMatricies ~ delete _;
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
public this(Skeleton skeleton, AnimationClip clip)
{
Skeleton = skeleton;
CurrentClip = clip;
Pose = new SkeletonPose(Skeleton);
SkinningMatricies = new Matrix[Skeleton.Joints.Count];
InvTransSkinningMatricies = new Matrix3x3[Skeleton.Joints.Count];
}
public void Update(GameTime gameTime)
{
TimeStamp += (float)gameTime.FrameTime.TotalSeconds;
if(CurrentClip.IsLooping && CurrentClip.Duration != 0)
{
TimeStamp %= CurrentClip.Duration;
}
for(int i < Skeleton.Joints.Count)
{
ref JointPose localPose = ref Pose.LocalPose[i];
localPose = CurrentClip.JointAnimations[i].GetCurrentPose(TimeStamp);
Matrix jointToParent =
Matrix.Translation(localPose.Translation) *
Matrix.RotationQuaternion(localPose.Rotation) *
Matrix.Scaling(localPose.Scale);
uint8 parentIndex = Skeleton.Joints[i].ParentID;
ref Matrix globalPose = ref Pose.GlobalPose[i];
if(parentIndex == uint8.MaxValue)
{
globalPose = jointToParent;
}
else
{
globalPose = Pose.GlobalPose[parentIndex] * jointToParent;
}
SkinningMatricies[i] = globalPose * Skeleton.Joints[i].InverseBindPose;
InvTransSkinningMatricies[i] = ((Matrix3x3)SkinningMatricies[i]).Inverse().Transpose();
}
}
}
class AnimationClip : RefCounter
{
private Skeleton _skeleton ~ _.ReleaseRef();
public JointAnimation[] JointAnimations ~ delete _;
public bool IsLooping;
public float Duration;
public Skeleton Skeleton => _skeleton;
[AllowAppend]
public this(Skeleton skeleton)
{
var jointAnimations = new JointAnimation[skeleton.Joints.Count];
Log.EngineLogger.AssertDebug(skeleton != null);
_skeleton = skeleton..AddRef();
JointAnimations = jointAnimations;
}
// TODO: check
public ~this()
{
for(var jointAnimation in JointAnimations)
{
//jointAnimation.Dispose();
delete jointAnimation;
}
}
}
public enum InterpolationMode
{
/**
* No keyframe interpolation.
* The keyframe with the greates timestamp that is smaller than the current time will be used.
*/
Step,
/**
* Linear interpolation between the two keyframes that are closest to the current time.
*/
Linear,
/**
* Cubic spline interpolation between the two keyframes that are closest to the current time.
*/
CubicSpline
}
class JointAnimationChannel<T> where T : struct // : IDisposable
{
public float[] TimeStamps;
public T[] Values;
public InterpolationMode InterpolationMode = .Step;
public float Duration => TimeStamps[TimeStamps.Count - 1];
public this(int samples, InterpolationMode interpolationMode)
{
TimeStamps = new float[samples];
Values = new T[samples];
InterpolationMode = interpolationMode;
}
public ~this()
{
delete TimeStamps;
delete Values;
}
public (T, T, float) GetSample(float currentTime)
{
let (previousIndex, previousTime) = FindPreviousTimeStamp(currentTime);
T previousSample = Values[previousIndex];
if(InterpolationMode == .Step)
{
return (previousSample, default(T), 0f);
}
else
{
int nextIndex = previousIndex + 1;
if(nextIndex >= Values.Count)
nextIndex = Values.Count - 1;
float nextTime = TimeStamps[nextIndex];
T nextSample = Values[nextIndex];
float interpolationValue = (currentTime - previousTime) / (nextTime - previousTime);
return (previousSample, nextSample, interpolationValue);
}
}
public (int Index, float timeStamp) FindPreviousTimeStamp(float currentTime)
{
// TODO use binary search to speed things up?
int index = TimeStamps.Count - 1;
for(int i < TimeStamps.Count)
{
if(TimeStamps[i] > currentTime)
{
index = i - 1;
break;
}
}
if(index == -1)
index = 0;
// Don't return -1 but return first frame
return (index, TimeStamps[index]);
//return array.Count - 1;
}
}
class JointAnimation// : IDisposable
{
public JointAnimationChannel<float3> TranslationChannel ~ delete _;
public JointAnimationChannel<Quaternion> RotationChannel ~ delete _;
public JointAnimationChannel<float3> ScaleChannel ~ delete _;
public float Duration
{
get
{
return Math.Max(Math.Max(
TranslationChannel?.Duration ?? 0, RotationChannel?.Duration ?? 0), ScaleChannel?.Duration ?? 0);
}
}
/*
public void Dispose()
{
delete TranslationChannel;
delete RotationChannel;
delete ScaleChannel;
}
*/
public JointPose GetCurrentPose(float timeStamp)
{
JointPose result;
if(TranslationChannel != null)
{
let (previous, next, interpolationValue) = TranslationChannel.GetSample(timeStamp);
switch(TranslationChannel.InterpolationMode)
{
case .Step:
result.Translation = previous;
case .Linear:
result.Translation = lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Translation = .Zero;
}
if(RotationChannel != null)
{
let (previous, next, interpolationValue) = RotationChannel.GetSample(timeStamp);
switch(RotationChannel.InterpolationMode)
{
case .Step:
result.Rotation = previous;
case .Linear:
result.Rotation = Quaternion.Slerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Rotation = .Identity;
}
if(ScaleChannel != null)
{
let (previous, next, interpolationValue) = ScaleChannel.GetSample(timeStamp);
//switch(ScaleChannel.InterpolationMode)
switch(InterpolationMode.Step)
{
case .Step:
result.Scale = previous;
case .Linear:
result.Scale = lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Scale = .One;
}
return result;
}
}
}