904 lines
21 KiB
C++
904 lines
21 KiB
C++
#ifndef CHARACTER_ANIMATION_MODULE_H_
|
|
#define CHARACTER_ANIMATION_MODULE_H_
|
|
#include <Ogre.h>
|
|
#include <flecs.h>
|
|
#include "GameData.h"
|
|
#include "CharacterModule.h"
|
|
#include "LuaData.h"
|
|
#include "EventModule.h"
|
|
namespace ECS
|
|
{
|
|
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
|
|
Ogre::Vector3 prevTranslation;
|
|
mutable Ogre::Vector3 deltaMotion;
|
|
flecs::entity e;
|
|
|
|
public:
|
|
RootMotionListener(flecs::entity e)
|
|
: Ogre::NodeAnimationTrack::Listener()
|
|
, e(e)
|
|
, prevTranslation(Ogre::Vector3::ZERO)
|
|
, deltaMotion(Ogre::Vector3::ZERO)
|
|
{
|
|
}
|
|
bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t,
|
|
const Ogre::TimeIndex &timeIndex,
|
|
Ogre::KeyFrame *kf) override
|
|
{
|
|
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;
|
|
if (tm == 0.0f) {
|
|
rotation = k1->getRotation();
|
|
translation = k1->getTranslate();
|
|
deltaMotion = translation;
|
|
|
|
// vkf->setRotation(k1->getRotation());
|
|
// vkf->setTranslate(k1->getTranslate());
|
|
// vkf->setScale(k1->getScale());
|
|
} 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;
|
|
}
|
|
#if 0
|
|
std::cout << "time: " << tm
|
|
<< " Position: " << deltaMotion;
|
|
std::cout << " Quaternion: " << rotation;
|
|
std::cout << std::endl;
|
|
#endif
|
|
vkf->setTranslate(deltaMotion);
|
|
// vkf->setTranslate(translation);
|
|
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;
|
|
}
|
|
};
|
|
struct AnimationTrigger;
|
|
struct AnimationTriggerSubscriber {
|
|
virtual void operator()(const AnimationTrigger *trigger) = 0;
|
|
};
|
|
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 {
|
|
Ogre::AnimationState *mAnimationState;
|
|
Ogre::Animation *mSkelAnimation;
|
|
Ogre::NodeAnimationTrack *mHipsTrack;
|
|
Ogre::NodeAnimationTrack *mRootTrack;
|
|
RootMotionListener *mListener;
|
|
float m_weight;
|
|
float m_accWeight;
|
|
Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
|
|
Ogre::Animation *skelAnimation, flecs::entity e)
|
|
: mAnimationState(animState)
|
|
, mSkelAnimation(skelAnimation)
|
|
, mListener(OGRE_NEW RootMotionListener(e))
|
|
, m_weight(0)
|
|
, m_accWeight(0)
|
|
{
|
|
int j;
|
|
mRootTrack = nullptr;
|
|
mHipsTrack = nullptr;
|
|
for (const auto &it : mSkelAnimation->_getNodeTrackList()) {
|
|
Ogre::NodeAnimationTrack *track = it.second;
|
|
Ogre::String trackName =
|
|
track->getAssociatedNode()->getName();
|
|
if (trackName == "mixamorig:Hips") {
|
|
mHipsTrack = track;
|
|
} else if (trackName == "Root") {
|
|
mRootTrack = track;
|
|
// mRootTracks[i]->removeAllKeyFrames();
|
|
}
|
|
}
|
|
if (!mRootTrack) {
|
|
Ogre::Bone *bone = skeleton->getBone("Root");
|
|
mRootTrack = mSkelAnimation->createNodeTrack(
|
|
bone->getHandle(), bone);
|
|
Ogre::TransformKeyFrame *kf =
|
|
mRootTrack->createNodeKeyFrame(0.0f);
|
|
kf->setTranslate(Ogre::Vector3::ZERO);
|
|
kf->setRotation(Ogre::Quaternion::IDENTITY);
|
|
}
|
|
mRootTrack->setListener(mListener);
|
|
}
|
|
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;
|
|
}
|
|
bool addTime(float time)
|
|
{
|
|
bool result = mAnimationState->getEnabled();
|
|
if (!result)
|
|
return result;
|
|
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(
|
|
mAnimationState->getTimePosition());
|
|
Ogre::KeyFrame *kf1, *kf2;
|
|
unsigned short prev_index, next_index;
|
|
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &prev_index);
|
|
unsigned int previous_frame = index.getKeyIndex();
|
|
mAnimationState->addTime(time);
|
|
index = mSkelAnimation->_getTimeIndex(
|
|
mAnimationState->getTimePosition());
|
|
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index);
|
|
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;
|
|
}
|
|
};
|
|
|
|
#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();
|
|
}
|
|
};
|
|
|
|
struct AnimationSystem : AnimationNode {
|
|
bool debug;
|
|
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;
|
|
}
|
|
AnimationSystemBuilder *trigger(flecs::entity e,
|
|
const Ogre::String &name,
|
|
float time,
|
|
const Ogre::String &event)
|
|
{
|
|
struct EventSubscriber : AnimationTriggerSubscriber {
|
|
flecs::entity ent;
|
|
Ogre::String event;
|
|
void operator()(const AnimationTrigger *trigger)
|
|
{
|
|
ent.get_mut<EventData>().add(ent, event,
|
|
ent, ent);
|
|
}
|
|
EventSubscriber(flecs::entity e,
|
|
const Ogre::String &event)
|
|
: ent(e)
|
|
, event(event)
|
|
{
|
|
}
|
|
};
|
|
OgreAssert(parent, "bad parent");
|
|
AnimationTrigger *trigger =
|
|
new AnimationTrigger(name, time, 0.1f);
|
|
EventSubscriber *sub = new EventSubscriber(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;
|
|
bool addTime(float time)
|
|
{
|
|
int i;
|
|
preUpdateTriggers();
|
|
bool ret = m_builder.animation_nodes[0]->addTime(time);
|
|
for (i = 0; i < m_builder.animationNodeList.size(); i++) {
|
|
AnimationNodeAnimation *anim =
|
|
m_builder.animationNodeList[i];
|
|
OgreAssert(anim->mAnimation, "No animation");
|
|
float weight = anim->getWeight();
|
|
anim->mAnimation->increaseAccWeight(weight);
|
|
#ifdef VDEBUG
|
|
if (debug)
|
|
std::cout << i << " node: "
|
|
<< anim->mAnimation->getName() << " "
|
|
<< weight << std::endl;
|
|
#endif
|
|
}
|
|
for (i = 0; i < vanimation_list.size(); i++) {
|
|
float weight = vanimation_list[i]->getAccWeight();
|
|
vanimation_list[i]->setWeight(weight);
|
|
vanimation_list[i]->resetAccWeight();
|
|
#define VDEBUG
|
|
#ifdef VDEBUG
|
|
if (debug && vanimation_list[i]->getEnabled())
|
|
std::cout << i << " animation: "
|
|
<< vanimation_list[i]->getName()
|
|
<< " " << weight << std::endl;
|
|
#endif
|
|
#undef VDEBUG
|
|
}
|
|
postUpdateTriggers(time);
|
|
return ret;
|
|
}
|
|
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();
|
|
}
|
|
};
|
|
|
|
struct AnimationControl {
|
|
bool configured;
|
|
AnimationSystem *mAnimationSystem;
|
|
};
|
|
struct DefaultAnimation {
|
|
std::vector<std::pair<std::string, std::string> > animations;
|
|
};
|
|
struct CharacterAnimationModule {
|
|
CharacterAnimationModule(flecs::world &ecs);
|
|
};
|
|
}
|
|
#endif
|