verus/Verus/src/Anim/Motion.h

238 lines
7.1 KiB
C++

#pragma once
namespace verus
{
namespace Anim
{
struct MotionDelegate
{
virtual void Motion_OnTrigger(CSZ name, int state) = 0;
};
VERUS_TYPEDEFS(MotionDelegate);
//! Motion holds a series of keyframes and provides the ability to interpolate between them.
//! Motion can be stored in XAN format. Motion's rate can be
//! scaled and even reversed. Animation object (Animation) can be used to
//! handle multiple motion objects.
//!
class Motion : public Object
{
public:
class Bone : public AllocatorAware
{
friend class Motion;
class Rotation
{
public:
Quat _q;
Rotation();
Rotation(RcQuat q);
Rotation(RcVector3 euler);
};
VERUS_TYPEDEFS(Rotation);
typedef Map<int, Rotation> TMapRot;
typedef Map<int, Vector3> TMapPos;
typedef Map<int, Vector3> TMapScale;
typedef Map<int, int> TMapTrigger;
String _name;
Motion* _pMotion = nullptr;
TMapRot _mapRot; //!< Rotation keyframes.
TMapPos _mapPos; //!< Position keyframes.
TMapScale _mapScale; //!< Scaling keyframes.
TMapTrigger _mapTrigger; //!< Trigger keyframes.
int _lastTriggerState = 0;
template<typename TMap, typename T>
float GetAlpha(const TMap& m, T& prev, T& next, const T& null, float time) const
{
if (m.empty()) // No frames at all, so return null.
{
prev = null;
next = null;
return 0;
}
float alpha;
const int frame = static_cast<int>(_pMotion->GetFps()*time); // Frame is before or at 'time'.
typename TMap::const_iterator it = m.upper_bound(frame); // Find frame after 'time'.
if (it != m.end()) // There are frames greater (after 'time'):
{
if (it != m.begin()) // And there are less than (before 'time'), full interpolation:
{
typename TMap::const_iterator itPrev = it;
itPrev--;
const float prevTime = itPrev->first*_pMotion->GetFpsInv();
const float nextTime = it->first*_pMotion->GetFpsInv();
const float delta = nextTime - prevTime;
const float offset = nextTime - time;
alpha = 1 - offset / delta;
prev = itPrev->second;
next = it->second;
}
else // But there are no less than:
{
const float nextTime = it->first*_pMotion->GetFpsInv();
const float delta = nextTime;
const float offset = time;
alpha = offset / delta;
prev = null;
next = it->second;
}
}
else // There are no frames greater, but there are less than:
{
it--;
alpha = 0;
prev = it->second;
next = null;
}
return alpha;
}
public:
enum class Type : int
{
rotation,
position,
scale,
trigger
};
Bone(Motion* pMotion = nullptr);
~Bone();
Str GetName() const { return _C(_name); }
void Rename(CSZ name) { _name = name; }
int GetLastTriggerState() const { return _lastTriggerState; }
void SetLastTriggerState(int state) { _lastTriggerState = state; }
void DeleteAll();
void InsertKeyframeRotation(int frame, RcQuat q);
void InsertKeyframeRotation(int frame, RcVector3 euler);
void InsertKeyframePosition(int frame, RcVector3 pos);
void InsertKeyframeScale(int frame, RcVector3 scale);
void InsertKeyframeTrigger(int frame, int state);
void DeleteKeyframeRotation(int frame);
void DeleteKeyframePosition(int frame);
void DeleteKeyframeScale(int frame);
void DeleteKeyframeTrigger(int frame);
bool FindKeyframeRotation(int frame, RVector3 euler, RQuat q) const;
bool FindKeyframePosition(int frame, RVector3 pos) const;
bool FindKeyframeScale(int frame, RVector3 scale) const;
bool FindKeyframeTrigger(int frame, int& state) const;
void ComputeRotationAt(float time, RVector3 euler, RQuat q) const;
void ComputePositionAt(float time, RVector3 pos) const;
void ComputeScaleAt(float time, RVector3 scale) const;
void ComputeTriggerAt(float time, int& state) const;
void ComputeMatrixAt(float time, RTransform3 mat);
void MoveKeyframe(int direction, Type type, int frame);
int GetNumKeysRotation() const { return Utils::Cast32(_mapRot.size()); }
int GetNumKeysPosition() const { return Utils::Cast32(_mapPos.size()); }
int GetNumKeysScale() const { return Utils::Cast32(_mapScale.size()); }
int GetNumKeysTrigger() const { return Utils::Cast32(_mapTrigger.size()); }
VERUS_P(void Serialize(IO::RStream stream));
VERUS_P(void Deserialize(IO::RStream stream));
void DeleteRedundantKeyframes();
void DeleteOddKeyframes();
void InsertLoopKeyframes();
void Cut(int frame, bool before);
void Fix(bool speedLimit);
void ApplyScaleBias(RcVector3 scale, RcVector3 bias);
void Scatter(int srcFrom, int srcTo, int dMin, int dMax);
int GetLastKeyframe() const;
};
VERUS_TYPEDEFS(Bone);
private:
enum { xanVersion = 0x0101, maxFps = 10000, maxNumBones = 10000, maxNumFrames = 32 * 1024 * 1024 };
typedef Map<String, Bone> TMapBones;
TMapBones _mapBones;
Motion* _pBlendMotion = nullptr;
int _numFrames = 50;
int _fps = 10;
float _fpsInv = 0.1f;
float _blendAlpha = 0;
float _playbackSpeed = 1;
float _playbackSpeedInv = 1;
bool _reversed = false;
public:
Motion();
~Motion();
void Init();
void Done();
int GetFps() const { return _fps; }
float GetFpsInv() const { return _fpsInv; }
void SetFps(int fps) { _fps = fps; _fpsInv = 1.f / _fps; }
int GetNumFrames() const { return _numFrames; }
void SetNumFrames(int num) { _numFrames = num; }
int GetNumBones() const { return Utils::Cast32(_mapBones.size()); }
float GetDuration() const { return GetNativeDuration()*_playbackSpeedInv; }
float GetNativeDuration() const { return _numFrames * _fpsInv; }
PBone GetBoneByIndex(int index);
int GetBoneIndex(CSZ name) const;
PBone InsertBone(CSZ name);
void DeleteBone(CSZ name);
void DeleteAllBones();
PBone FindBone(CSZ name);
void Serialize(IO::RStream stream);
void Deserialize(IO::RStream stream);
void BakeMotionAt(float time, Motion& dest) const;
void BindBlendMotion(Motion* p, float alpha);
Motion* GetBlendMotion() const { return _pBlendMotion; }
float GetBlendAlpha() const { return _blendAlpha; }
void DeleteRedundantKeyframes();
void DeleteOddKeyframes();
void InsertLoopKeyframes();
void Cut(int frame, bool before = true);
void Fix(bool speedLimit);
// Triggers:
void ProcessTriggers(float time, PMotionDelegate p, int* pUserTriggerStates = nullptr);
void ResetTriggers(int* pUserTriggerStates = nullptr);
void SkipTriggers(float time, int* pUserTriggerStates = nullptr);
void ApplyScaleBias(CSZ name, RcVector3 scale, RcVector3 bias);
float GetPlaybackSpeed() const { return _playbackSpeed; }
float GetPlaybackSpeedInv() const { return _playbackSpeedInv; }
void SetPlaybackSpeed(float x);
void ComputePlaybackSpeed(float duration);
bool IsReversed() const { return _reversed; }
void Exec(CSZ code);
int GetLastKeyframe() const;
};
VERUS_TYPEDEFS(Motion);
}
}