Intagrated Tracy, debugged animations
This commit is contained in:
@@ -0,0 +1,900 @@
|
||||
#ifndef ANIMATIONSYSTEM_H
|
||||
#define ANIMATIONSYSTEM_H
|
||||
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <flecs.h>
|
||||
#include <tracy/Tracy.hpp>
|
||||
#include "GameData.h"
|
||||
#include "EventModule.h"
|
||||
|
||||
namespace AnimationSystem
|
||||
{
|
||||
struct AnimationTrigger;
|
||||
struct AnimationTriggerSubscriber {
|
||||
virtual void operator()(const AnimationTrigger *trigger) = 0;
|
||||
};
|
||||
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
|
||||
public:
|
||||
RootMotionListener()
|
||||
: Ogre::NodeAnimationTrack::Listener()
|
||||
{
|
||||
}
|
||||
bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t,
|
||||
const Ogre::TimeIndex &timeIndex,
|
||||
Ogre::KeyFrame *kf) override
|
||||
{
|
||||
#if 0
|
||||
Ogre::TransformKeyFrame *vkf =
|
||||
static_cast<Ogre::TransformKeyFrame *>(kf);
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
Ogre::TransformKeyFrame *k1, *k2;
|
||||
unsigned short firstKeyIndex;
|
||||
float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2,
|
||||
&firstKeyIndex);
|
||||
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
|
||||
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
|
||||
Ogre::Vector3 translation;
|
||||
Ogre::Quaternion rotation;
|
||||
Ogre::Vector3 deltaMotion;
|
||||
Ogre::Vector3 prevMotion;
|
||||
if (tm == 0.0f) {
|
||||
rotation = k1->getRotation();
|
||||
translation = k1->getTranslate();
|
||||
deltaMotion = translation;
|
||||
} else {
|
||||
rotation = Ogre::Quaternion::nlerp(
|
||||
tm, k1->getRotation(), k2->getRotation(), true);
|
||||
translation =
|
||||
k1->getTranslate() +
|
||||
(k2->getTranslate() - k1->getTranslate()) * tm;
|
||||
deltaMotion = translation - prevTranslation;
|
||||
if (deltaMotion.squaredLength() >
|
||||
translation.squaredLength())
|
||||
deltaMotion = translation;
|
||||
}
|
||||
vkf->setTranslate(deltaMotion);
|
||||
vkf->setRotation(rotation);
|
||||
vkf->setScale(Ogre::Vector3(1, 1, 1));
|
||||
prevTranslation = translation;
|
||||
e.get_mut<CharacterBase>().mBoneMotion = deltaMotion;
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
|
||||
e.modified<CharacterBase>();
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
};
|
||||
struct AnimationTrigger {
|
||||
Ogre::String name;
|
||||
float time;
|
||||
float weight;
|
||||
std::vector<AnimationTriggerSubscriber *> subscriber_list;
|
||||
float getTriggerTime() const
|
||||
{
|
||||
return time;
|
||||
}
|
||||
float getMinWeight() const
|
||||
{
|
||||
return weight;
|
||||
}
|
||||
const Ogre::String &getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
void notify(float weight)
|
||||
{
|
||||
int i;
|
||||
if (weight < this->weight)
|
||||
return;
|
||||
for (i = 0; i < subscriber_list.size(); i++)
|
||||
(*subscriber_list[i])(this);
|
||||
}
|
||||
void addSubscriber(AnimationTriggerSubscriber *sub)
|
||||
{
|
||||
if (std::find(subscriber_list.begin(), subscriber_list.end(),
|
||||
sub) != subscriber_list.end())
|
||||
return;
|
||||
subscriber_list.push_back(sub);
|
||||
}
|
||||
void removeSubscriber(AnimationTriggerSubscriber *sub)
|
||||
{
|
||||
auto it = std::find(subscriber_list.begin(),
|
||||
subscriber_list.end(), sub);
|
||||
if (it != subscriber_list.end())
|
||||
subscriber_list.erase(it);
|
||||
}
|
||||
void clearSubscribers()
|
||||
{
|
||||
subscriber_list.clear();
|
||||
}
|
||||
AnimationTrigger(const Ogre::String name, float time, float weight)
|
||||
: name(name)
|
||||
, time(time)
|
||||
, weight(weight)
|
||||
{
|
||||
}
|
||||
};
|
||||
struct Animation {
|
||||
struct SetupTracks {
|
||||
Ogre::NodeAnimationTrack *mHipsTrack;
|
||||
Ogre::NodeAnimationTrack *mRootTrack;
|
||||
RootMotionListener *mListener;
|
||||
Ogre::Vector3 mRootTranslation;
|
||||
SetupTracks(Ogre::Skeleton *skeleton,
|
||||
Ogre::Animation *animation)
|
||||
: mListener(OGRE_NEW RootMotionListener())
|
||||
{
|
||||
mHipsTrack = nullptr;
|
||||
mRootTrack = nullptr;
|
||||
for (const auto &it : animation->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack *track = it.second;
|
||||
Ogre::String trackName =
|
||||
track->getAssociatedNode()->getName();
|
||||
if (trackName == "mixamorig:Hips") {
|
||||
mHipsTrack = track;
|
||||
} else if (trackName == "Root") {
|
||||
mRootTrack = track;
|
||||
}
|
||||
}
|
||||
if (!mRootTrack) {
|
||||
Ogre::Bone *bone = skeleton->getBone("Root");
|
||||
mRootTrack = animation->createNodeTrack(
|
||||
bone->getHandle(), bone);
|
||||
Ogre::TransformKeyFrame *kf =
|
||||
mRootTrack->createNodeKeyFrame(0.0f);
|
||||
kf->setTranslate(Ogre::Vector3::ZERO);
|
||||
kf->setRotation(Ogre::Quaternion::IDENTITY);
|
||||
}
|
||||
Ogre::TransformKeyFrame *tkfBeg =
|
||||
(Ogre::TransformKeyFrame *)
|
||||
mRootTrack->getKeyFrame(0);
|
||||
Ogre::TransformKeyFrame *tkfEnd =
|
||||
(Ogre::TransformKeyFrame *)
|
||||
mRootTrack->getKeyFrame(
|
||||
mRootTrack->getNumKeyFrames() -
|
||||
1);
|
||||
mRootTranslation =
|
||||
tkfEnd->getTranslate() - tkfBeg->getTranslate();
|
||||
}
|
||||
};
|
||||
Ogre::AnimationState *mAnimationState;
|
||||
Ogre::Animation *mSkelAnimation;
|
||||
SetupTracks *mTracks;
|
||||
float m_weight;
|
||||
float m_accWeight;
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
|
||||
Ogre::Animation *skelAnimation)
|
||||
: mTracks(OGRE_NEW SetupTracks(skeleton, skelAnimation))
|
||||
, mAnimationState(animState)
|
||||
, mSkelAnimation(skelAnimation)
|
||||
, m_weight(0)
|
||||
, m_accWeight(0)
|
||||
, mSkeleton(skeleton)
|
||||
{
|
||||
}
|
||||
Ogre::String getName()
|
||||
{
|
||||
return mAnimationState->getAnimationName();
|
||||
}
|
||||
void setLoop(bool loop)
|
||||
{
|
||||
mAnimationState->setLoop(loop);
|
||||
}
|
||||
bool getLoop() const
|
||||
{
|
||||
return mAnimationState->getLoop();
|
||||
}
|
||||
void setEnabled(bool enabled)
|
||||
{
|
||||
mAnimationState->setEnabled(enabled);
|
||||
}
|
||||
bool getEnabled() const
|
||||
{
|
||||
return mAnimationState->getEnabled();
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
bool enabled = weight > 0.001f;
|
||||
setEnabled(enabled);
|
||||
mAnimationState->setWeight(weight);
|
||||
m_weight = weight;
|
||||
}
|
||||
float getWeight() const
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Vector3 getRootMotionDelta()
|
||||
{
|
||||
if (mAnimationState->getEnabled())
|
||||
return rootMotion * mAnimationState->getWeight();
|
||||
else
|
||||
return Ogre::Vector3(0, 0, 0);
|
||||
}
|
||||
void getKeyframeIndices(Ogre::Real timePos, unsigned short *pindex)
|
||||
{
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
mTracks->mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2,
|
||||
pindex);
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool result = mAnimationState->getEnabled();
|
||||
if (!result)
|
||||
return result;
|
||||
unsigned short prev_index, next_index;
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(
|
||||
mAnimationState->getTimePosition());
|
||||
getKeyframeIndices(mAnimationState->getTimePosition(),
|
||||
&prev_index);
|
||||
unsigned int previous_frame = index.getKeyIndex();
|
||||
float lastTime = mAnimationState->getTimePosition();
|
||||
mAnimationState->addTime(time);
|
||||
float thisTime = mAnimationState->getTimePosition();
|
||||
float length = mAnimationState->getLength();
|
||||
bool loop = mAnimationState->getLoop();
|
||||
int loops =
|
||||
loop ? (int)std::round((lastTime + time - thisTime) /
|
||||
length) :
|
||||
0;
|
||||
Ogre::TransformKeyFrame tkf(0, 0);
|
||||
mTracks->mRootTrack->getInterpolatedKeyFrame(lastTime, &tkf);
|
||||
Ogre::Vector3 lastRootPos = tkf.getTranslate();
|
||||
mTracks->mRootTrack->getInterpolatedKeyFrame(thisTime, &tkf);
|
||||
Ogre::Vector3 thisRootPos = tkf.getTranslate();
|
||||
rootMotion = (thisRootPos - lastRootPos) +
|
||||
(loops * mTracks->mRootTranslation);
|
||||
|
||||
getKeyframeIndices(mAnimationState->getTimePosition(),
|
||||
&next_index);
|
||||
// updateRootMotion(mAnimationState->getTimePosition());
|
||||
return prev_index != next_index;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
mAnimationState->setTimePosition(0);
|
||||
}
|
||||
void resetAccWeight()
|
||||
{
|
||||
m_accWeight = 0;
|
||||
}
|
||||
void increaseAccWeight(float weight)
|
||||
{
|
||||
m_accWeight += weight;
|
||||
}
|
||||
float getAccWeight() const
|
||||
{
|
||||
return m_accWeight;
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return mAnimationState->getLength();
|
||||
if (getEnabled())
|
||||
return mAnimationState->getLength();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return mAnimationState->getTimePosition();
|
||||
if (getEnabled())
|
||||
return mAnimationState->getTimePosition();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationNode {
|
||||
std::vector<AnimationNode *> children;
|
||||
float m_weight;
|
||||
Ogre::String m_name;
|
||||
std::multimap<float, AnimationTrigger *> trigger_list;
|
||||
AnimationNode()
|
||||
: m_weight(0)
|
||||
{
|
||||
}
|
||||
virtual bool addTime(float time) = 0;
|
||||
virtual void setWeight(float weight) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual float getLength() const = 0;
|
||||
virtual float getTimePosition() const = 0;
|
||||
float getWeight()
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
const Ogre::String &getName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
void setName(const Ogre::String &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
virtual float getTime() const
|
||||
{
|
||||
float l = getLength();
|
||||
if (l > 0.0f)
|
||||
return getTimePosition() / l;
|
||||
return 0.0f;
|
||||
}
|
||||
void addTrigger(AnimationTrigger *trigger)
|
||||
{
|
||||
trigger_list.insert(std::pair<float, AnimationTrigger *>(
|
||||
trigger->getTriggerTime(), trigger));
|
||||
}
|
||||
void clearTriggers()
|
||||
{
|
||||
auto it = trigger_list.begin();
|
||||
while (it != trigger_list.end()) {
|
||||
delete it->second;
|
||||
it++;
|
||||
}
|
||||
trigger_list.clear();
|
||||
}
|
||||
float mpreUpdateTime;
|
||||
void preUpdateTriggers()
|
||||
{
|
||||
mpreUpdateTime = getTime();
|
||||
}
|
||||
void postUpdateTriggers(float delta)
|
||||
{
|
||||
float postUpdateTime = getTime();
|
||||
bool positive = delta >= 0.0f;
|
||||
if (positive)
|
||||
updateTriggers(mpreUpdateTime, postUpdateTime);
|
||||
else
|
||||
updateTriggers(postUpdateTime, mpreUpdateTime);
|
||||
}
|
||||
void updateTriggers(float currentTime, float nextTime)
|
||||
{
|
||||
int i;
|
||||
float weight = getWeight();
|
||||
if (currentTime <= nextTime) {
|
||||
auto it = trigger_list.lower_bound(currentTime);
|
||||
while (it != trigger_list.end()) {
|
||||
if (nextTime <=
|
||||
it->second->getTriggerTime()) // in future, sorrted by time
|
||||
return;
|
||||
it->second->notify(weight);
|
||||
it++;
|
||||
}
|
||||
} else {
|
||||
updateTriggers(currentTime, 1);
|
||||
updateTriggers(0, nextTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
struct AnimationNodeAnimation : AnimationNode {
|
||||
Animation *mAnimation;
|
||||
bool enabled;
|
||||
AnimationNodeAnimation(Animation *animation)
|
||||
: AnimationNode()
|
||||
, mAnimation(animation)
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = mAnimation->addTime(time);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
enabled = weight > 0.001f;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
mAnimation->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return mAnimation->getLength();
|
||||
if (enabled)
|
||||
return mAnimation->getLength();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return mAnimation->getTimePosition();
|
||||
if (enabled)
|
||||
return mAnimation->getTimePosition();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
struct AnimationNodeStateMachineState : AnimationNode {
|
||||
AnimationNodeStateMachineState()
|
||||
: AnimationNode()
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = children[0]->addTime(time);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return children[0]->getLength();
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return children[0]->getTimePosition();
|
||||
}
|
||||
};
|
||||
struct AnimationNodeSpeed : AnimationNode {
|
||||
float m_speed;
|
||||
bool enabled;
|
||||
AnimationNodeSpeed(float speed)
|
||||
: AnimationNode()
|
||||
, m_speed(speed)
|
||||
, enabled(false)
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = children[0]->addTime(time * m_speed);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
if (m_speed > 0.0f || m_speed < 0.0f)
|
||||
return children[0]->getLength() / m_speed;
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
if (m_speed > 0.0f || m_speed < 0.0f)
|
||||
return children[0]->getTimePosition() / m_speed;
|
||||
return 0.0f;
|
||||
}
|
||||
float getTime() const override
|
||||
{
|
||||
float l = children[0]->getLength();
|
||||
if (l > 0.0f)
|
||||
return children[0]->getTimePosition() / l;
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
struct AnimationNodeStateMachine : AnimationNode {
|
||||
std::map<Ogre::String, AnimationNode *> stateMap;
|
||||
std::set<AnimationNode *> fade_in, fade_out;
|
||||
AnimationNode *currentAnim, *nextAnim;
|
||||
float fade_speed;
|
||||
Ogre::String mCurrentStateName;
|
||||
bool configured;
|
||||
bool debug;
|
||||
AnimationNodeStateMachine(float fade_speed, bool debug = false)
|
||||
: AnimationNode()
|
||||
, currentAnim(nullptr)
|
||||
, nextAnim(nullptr)
|
||||
, fade_speed(fade_speed)
|
||||
, mCurrentStateName("")
|
||||
, configured(false)
|
||||
, debug(debug)
|
||||
{
|
||||
m_weight = 1.0f;
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
int i;
|
||||
preUpdateTriggers();
|
||||
if (!configured) {
|
||||
configure();
|
||||
configured = true;
|
||||
}
|
||||
#ifdef VDEBUG
|
||||
if (debug) {
|
||||
std::cout << "state machine addTime" << std::endl;
|
||||
std::cout
|
||||
<< "state machine children: " << children.size()
|
||||
<< std::endl;
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < children.size(); i++) {
|
||||
#ifdef VDEBUG
|
||||
if (debug)
|
||||
std::cout << "child weight: " << i << " "
|
||||
<< children[i]->getWeight()
|
||||
<< std::endl;
|
||||
#endif
|
||||
AnimationNode *child = children[i];
|
||||
if (fade_in.find(child) != fade_in.end()) {
|
||||
Ogre::Real newWeight =
|
||||
child->getWeight() + time * fade_speed;
|
||||
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
|
||||
newWeight * m_weight, 0, m_weight));
|
||||
#ifdef VDEBUG
|
||||
if (debug) {
|
||||
std::cout << "fade in: " << newWeight
|
||||
<< std::endl;
|
||||
std::cout << "m_weight: " << m_weight
|
||||
<< std::endl;
|
||||
}
|
||||
#endif
|
||||
if (newWeight >= 1)
|
||||
fade_in.erase(child);
|
||||
}
|
||||
if (fade_out.find(child) != fade_out.end()) {
|
||||
Ogre::Real newWeight =
|
||||
child->getWeight() - time * fade_speed;
|
||||
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
|
||||
newWeight * m_weight, 0, 1));
|
||||
if (newWeight <= 0)
|
||||
fade_out.erase(child);
|
||||
}
|
||||
}
|
||||
OgreAssert(currentAnim, "bad current anim");
|
||||
bool ret = false;
|
||||
if (currentAnim)
|
||||
ret = currentAnim->addTime(time);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
int i;
|
||||
if (weight > m_weight && currentAnim)
|
||||
fade_in.insert(currentAnim);
|
||||
if (weight < m_weight && currentAnim &&
|
||||
currentAnim->getWeight() > weight)
|
||||
currentAnim->setWeight(weight);
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
/* do not update child state yet */
|
||||
}
|
||||
void addState(AnimationNode *state)
|
||||
{
|
||||
const Ogre::String &name = state->getName();
|
||||
stateMap[name] = state;
|
||||
state->setWeight(0);
|
||||
fade_in.erase(state);
|
||||
fade_out.erase(state);
|
||||
std::cout << "registered state: " << name << std::endl;
|
||||
}
|
||||
void configure()
|
||||
{
|
||||
int i;
|
||||
if (debug)
|
||||
std::cout << "children: " << children.size()
|
||||
<< std::endl;
|
||||
for (i = 0; i < children.size(); i++)
|
||||
addState(children[i]);
|
||||
if (debug)
|
||||
std::cout << "configure called" << std::endl;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < children.size(); i++)
|
||||
children[i]->reset();
|
||||
}
|
||||
void setAnimation(const Ogre::String &anim_state, bool reset = false)
|
||||
{
|
||||
if (!configured) {
|
||||
configure();
|
||||
configured = true;
|
||||
}
|
||||
OgreAssert(stateMap.find(anim_state) != stateMap.end(),
|
||||
"Bad animation state: " + anim_state);
|
||||
nextAnim = stateMap[anim_state];
|
||||
if (nextAnim == currentAnim)
|
||||
return;
|
||||
if (currentAnim != nullptr) {
|
||||
fade_out.insert(currentAnim);
|
||||
fade_in.erase(currentAnim);
|
||||
}
|
||||
fade_out.erase(nextAnim);
|
||||
fade_in.insert(nextAnim);
|
||||
nextAnim->setWeight(0);
|
||||
if (reset)
|
||||
nextAnim->reset();
|
||||
currentAnim = nextAnim;
|
||||
mCurrentStateName = anim_state;
|
||||
}
|
||||
const Ogre::String &getCurrentState() const
|
||||
{
|
||||
return mCurrentStateName;
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
if (currentAnim)
|
||||
return currentAnim->getLength();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
if (currentAnim)
|
||||
return currentAnim->getTimePosition();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
struct AnimationSystem : AnimationNode {
|
||||
bool debug;
|
||||
|
||||
#define ANIM_FADE_SPEED \
|
||||
7.5f // animation crossfade speed in % of full weight per second
|
||||
|
||||
struct AnimationNodeOutput : AnimationNode {
|
||||
float m_weight;
|
||||
float m_speed;
|
||||
AnimationNodeOutput()
|
||||
: AnimationNode()
|
||||
, m_weight(1.0f)
|
||||
, m_speed(1.0f)
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = children[0]->addTime(time * m_speed);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return children[0]->getLength();
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return children[0]->getTimePosition();
|
||||
}
|
||||
};
|
||||
|
||||
AnimationSystem(bool debug = false)
|
||||
: debug(debug)
|
||||
, m_builder(this, debug)
|
||||
{
|
||||
}
|
||||
std::unordered_map<Ogre::String, Animation *> animation_list;
|
||||
std::vector<Animation *> vanimation_list;
|
||||
void add_animation(const Ogre::String &name, Animation *animation)
|
||||
{
|
||||
OgreAssert(animation, "No animation " + name);
|
||||
animation_list[name] = animation;
|
||||
vanimation_list.push_back(animation);
|
||||
}
|
||||
void clear_animations()
|
||||
{
|
||||
animation_list.clear();
|
||||
vanimation_list.clear();
|
||||
}
|
||||
struct AnimationSystemBuilder {
|
||||
AnimationSystem *mAnimationSystem;
|
||||
std::vector<AnimationNode *> animation_nodes;
|
||||
AnimationNode *parent;
|
||||
std::list<AnimationNode *> parent_stack;
|
||||
std::unordered_map<Ogre::String, AnimationNode *> nodeMap;
|
||||
std::vector<AnimationNodeAnimation *> animationNodeList;
|
||||
bool debug;
|
||||
AnimationSystemBuilder(AnimationSystem *animationSystem,
|
||||
bool debug = false)
|
||||
: mAnimationSystem(animationSystem)
|
||||
, debug(debug)
|
||||
{
|
||||
}
|
||||
AnimationSystemBuilder *output()
|
||||
{
|
||||
AnimationNodeOutput *onode = new AnimationNodeOutput();
|
||||
animation_nodes.push_back(onode);
|
||||
parent = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *
|
||||
animation(const Ogre::String &animation_name)
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
Animation *animation =
|
||||
mAnimationSystem->animation_list[animation_name];
|
||||
OgreAssert(animation,
|
||||
"bad animation " + animation_name);
|
||||
AnimationNodeAnimation *onode =
|
||||
new AnimationNodeAnimation(animation);
|
||||
OgreAssert(onode, "bad animation");
|
||||
OgreAssert(onode->mAnimation, "bad animation");
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
animationNodeList.push_back(onode);
|
||||
return this;
|
||||
}
|
||||
/* FIXME: need to remove flecs dependency */
|
||||
AnimationSystemBuilder *
|
||||
trigger_entity(flecs::entity e, const Ogre::String &name,
|
||||
float time, const Ogre::String &event)
|
||||
{
|
||||
struct EntityEventSubscriber
|
||||
: AnimationTriggerSubscriber {
|
||||
Ogre::String event;
|
||||
flecs::entity ent;
|
||||
void operator()(const AnimationTrigger *trigger)
|
||||
{
|
||||
ent.get_mut<ECS::EventData>().add(
|
||||
ent, event, ent, ent);
|
||||
}
|
||||
EntityEventSubscriber(flecs::entity e,
|
||||
const Ogre::String &event)
|
||||
: event(event)
|
||||
, ent(e)
|
||||
{
|
||||
}
|
||||
};
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationTrigger *trigger =
|
||||
new AnimationTrigger(name, time, 0.1f);
|
||||
EntityEventSubscriber *sub =
|
||||
new EntityEventSubscriber(e, event);
|
||||
trigger->addSubscriber(sub);
|
||||
parent->addTrigger(trigger);
|
||||
return this;
|
||||
} // leaf too...
|
||||
AnimationSystemBuilder *
|
||||
transition_end(const Ogre::String &state_from,
|
||||
const Ogre::String &state_to)
|
||||
{
|
||||
struct EndTransitionSubscriber
|
||||
: AnimationTriggerSubscriber {
|
||||
AnimationNodeStateMachine *sm;
|
||||
Ogre::String next_state;
|
||||
bool reset;
|
||||
void operator()(const AnimationTrigger *trigger)
|
||||
{
|
||||
sm->setAnimation(next_state, reset);
|
||||
}
|
||||
EndTransitionSubscriber(
|
||||
AnimationNodeStateMachine *sm,
|
||||
const Ogre::String &next_state,
|
||||
bool reset = true)
|
||||
: sm(sm)
|
||||
, next_state(next_state)
|
||||
, reset(reset)
|
||||
{
|
||||
}
|
||||
};
|
||||
OgreAssert(parent, "no parent");
|
||||
AnimationNodeStateMachine *sm =
|
||||
static_cast<AnimationNodeStateMachine *>(
|
||||
parent);
|
||||
OgreAssert(sm, "no state machine");
|
||||
AnimationTrigger *trigger = new AnimationTrigger(
|
||||
"transition:" + state_from + "_" + state_to,
|
||||
0.99f, 0.1f);
|
||||
EndTransitionSubscriber *sub =
|
||||
new EndTransitionSubscriber(sm, state_to);
|
||||
int i;
|
||||
bool ok = false;
|
||||
for (i = 0; i < sm->children.size(); i++) {
|
||||
if (sm->children[i]->getName() == state_from) {
|
||||
trigger->addSubscriber(sub);
|
||||
sm->children[i]->addTrigger(trigger);
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
OgreAssert(ok, "Failed to set transition");
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *speed(float speed,
|
||||
const Ogre::String &anchor = "")
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeSpeed *onode =
|
||||
new AnimationNodeSpeed(speed);
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
if (anchor.length() > 0)
|
||||
nodeMap[anchor] = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *
|
||||
state_machine(float fade_time = ANIM_FADE_SPEED,
|
||||
const Ogre::String &anchor = "")
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeStateMachine *onode =
|
||||
new AnimationNodeStateMachine(fade_time, debug);
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
if (anchor.length() > 0)
|
||||
nodeMap[anchor] = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *state(const Ogre::String &state_name)
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeStateMachineState *onode =
|
||||
new AnimationNodeStateMachineState;
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
onode->setName(state_name);
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *end()
|
||||
{
|
||||
parent = parent_stack.back();
|
||||
parent_stack.pop_back();
|
||||
return this;
|
||||
}
|
||||
};
|
||||
AnimationSystemBuilder m_builder;
|
||||
Ogre::Vector3 getRootMotionDelta();
|
||||
bool addTime(float time);
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_builder.animation_nodes[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
m_builder.animation_nodes[0]->reset();
|
||||
}
|
||||
AnimationSystemBuilder *builder()
|
||||
{
|
||||
m_builder.animation_nodes.reserve(8);
|
||||
m_builder.parent = nullptr;
|
||||
return &m_builder;
|
||||
}
|
||||
template <class T> T *get(const Ogre::String &name)
|
||||
{
|
||||
return static_cast<T *>(m_builder.nodeMap[name]);
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return m_builder.animation_nodes[0]->getLength();
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return m_builder.animation_nodes[0]->getTimePosition();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // ANIMATIONSYSTEM_H
|
||||
Reference in New Issue
Block a user