185 lines
4.7 KiB
C++
185 lines
4.7 KiB
C++
#pragma once
|
|
#include "src/Core/Core.h"
|
|
#include <src/Rendering/Mesh/Bone.h>
|
|
|
|
#include "src/Resource/Serializable.h"
|
|
#include "src/Resource/Resource.h"
|
|
|
|
namespace Nuake
|
|
{
|
|
struct SkeletonAnimationBoneAnimation
|
|
{
|
|
std::string Name;
|
|
Vector3 Positions;
|
|
Quat Rotations;
|
|
Vector3 Scalings;
|
|
};
|
|
|
|
class BoneTransformTrack
|
|
{
|
|
private:
|
|
std::vector<float> m_PositionTimestamps = {};
|
|
std::vector<float> m_RotationTimestamps = {};
|
|
std::vector<float> m_ScaleTimestamps = {};
|
|
|
|
std::vector<Vector3> m_Positions = {};
|
|
std::vector<Quat> m_Rotations = {};
|
|
std::vector<Vector3> m_Scales = {};
|
|
|
|
Matrix4 m_PositionTransform;
|
|
Matrix4 m_RotationTransform;
|
|
Matrix4 m_ScaleTransform;
|
|
Matrix4 m_FinalTransform;
|
|
|
|
bool m_IsEmpty = true;
|
|
public:
|
|
BoneTransformTrack();
|
|
~BoneTransformTrack() = default;
|
|
|
|
bool IsEmpty() const { return m_IsEmpty; }
|
|
|
|
void PushPositionKeyframe(float timestamp, const Vector3& position)
|
|
{
|
|
m_IsEmpty = false;
|
|
m_PositionTimestamps.push_back(timestamp);
|
|
m_Positions.push_back(position);
|
|
}
|
|
|
|
void PushRotationKeyframe(float timestamp, const Quat& rotation)
|
|
{
|
|
m_IsEmpty = false;
|
|
m_RotationTimestamps.push_back(timestamp);
|
|
m_Rotations.push_back(rotation);
|
|
}
|
|
|
|
void PushScaleKeyframe(float timestamp, const Vector3& scale)
|
|
{
|
|
m_IsEmpty = false;
|
|
m_ScaleTimestamps.push_back(timestamp);
|
|
m_Scales.push_back(scale);
|
|
}
|
|
|
|
void Update(float time)
|
|
{
|
|
// We cannot use const reference here because we would compare the reference to previous pos
|
|
// to the m_PositionTransform and previous pos always equale.
|
|
// Making the hasChanged always false. We would end up comparing the same values always.
|
|
const Matrix4 previousPos = m_PositionTransform;
|
|
const Matrix4 previousRot = m_RotationTransform;
|
|
const Matrix4 previousSca = m_ScaleTransform;
|
|
m_PositionTransform = InterpolatePosition(time);
|
|
m_RotationTransform = InterpolateRotation(time);
|
|
m_ScaleTransform = InterpolateScale(time);
|
|
|
|
bool hasChanged = previousPos != m_PositionTransform || previousRot != m_RotationTransform || previousSca != m_ScaleTransform;
|
|
if (hasChanged)
|
|
{
|
|
m_FinalTransform = m_PositionTransform * m_RotationTransform * m_ScaleTransform;
|
|
}
|
|
}
|
|
|
|
const Matrix4& GetFinalTransform() const
|
|
{
|
|
return m_FinalTransform;
|
|
}
|
|
|
|
int GetPositionIndex(float animationTime)
|
|
{
|
|
if (m_Positions.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (int index = 0; index < m_Positions.size() - 1; index++)
|
|
{
|
|
if (animationTime < m_PositionTimestamps[index + 1])
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return static_cast<int>(m_Positions.size()) - 1;
|
|
}
|
|
|
|
/* Gets the current index on mKeyRotations to interpolate to based on the
|
|
current animation time*/
|
|
int GetRotationIndex(float animationTime)
|
|
{
|
|
for (int index = 0; index < m_Rotations.size() - 1; ++index)
|
|
{
|
|
if (animationTime < m_RotationTimestamps[index + 1])
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return static_cast<int>(m_Rotations.size()) - 1;
|
|
}
|
|
|
|
/* Gets the current index on mKeyScalings to interpolate to based on the
|
|
current animation time */
|
|
int GetScaleIndex(float animationTime)
|
|
{
|
|
for (int index = 0; index < m_Scales.size() - 1; ++index)
|
|
{
|
|
if (animationTime < m_ScaleTimestamps[index + 1])
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return static_cast<int>(m_Scales.size()) - 1;
|
|
}
|
|
|
|
float GetScaleFactor(float lastTime, float nextTime, float animationTime);
|
|
Matrix4 InterpolatePosition(float time);
|
|
Matrix4 InterpolateRotation(float time);
|
|
Matrix4 InterpolateScale(float time);
|
|
|
|
json Serialize();
|
|
bool Deserialize(const json& j);
|
|
};
|
|
|
|
class SkeletalAnimation : public Resource, ISerializable
|
|
{
|
|
private:
|
|
std::unordered_map<std::string, BoneTransformTrack> m_Tracks;
|
|
float m_CurrentTime;
|
|
float m_Duration;
|
|
float m_TicksPerSecond;
|
|
std::string m_Name;
|
|
bool m_Loop = false;
|
|
|
|
public:
|
|
SkeletalAnimation() = default;
|
|
SkeletalAnimation(const std::string& name, float duration, float ticksPerSecond);
|
|
|
|
~SkeletalAnimation() = default;
|
|
|
|
void SetCurrentTime(float time)
|
|
{
|
|
if (m_Loop)
|
|
{
|
|
m_CurrentTime = fmod(time, m_Duration);
|
|
}
|
|
else
|
|
{
|
|
m_CurrentTime = std::max(time, m_Duration);
|
|
}
|
|
}
|
|
|
|
float GetCurrentTime() const { return m_CurrentTime; }
|
|
void SetDuration(float duration) { m_Duration = duration; }
|
|
float GetDuration() const { return m_Duration; }
|
|
float GetTicksPerSecond() const { return m_TicksPerSecond; }
|
|
void SetTicksPerSecond(float ticks) { m_TicksPerSecond = ticks; }
|
|
|
|
const std::string& GetName() const { return m_Name; }
|
|
BoneTransformTrack& GetTrack(const std::string& name);
|
|
std::unordered_map<std::string, BoneTransformTrack>& GetTracks() { return m_Tracks; }
|
|
|
|
json Serialize() override;
|
|
bool Deserialize(const json& j) override;
|
|
};
|
|
}
|