#ifndef CHARACTER_ANIMATION_MODULE_H_ #define CHARACTER_ANIMATION_MODULE_H_ #include #include #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(kf); Ogre::KeyFrame *kf1, *kf2; Ogre::TransformKeyFrame *k1, *k2; unsigned short firstKeyIndex; float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2, &firstKeyIndex); k1 = static_cast(kf1); k2 = static_cast(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().mBoneMotion = deltaMotion; e.get_mut().mBonePrevMotion = prevTranslation; e.modified(); return true; } }; struct AnimationTrigger; struct AnimationTriggerSubscriber { virtual void operator()(const AnimationTrigger *trigger) = 0; }; struct AnimationTrigger { Ogre::String name; float time; float weight; std::vector 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 children; float m_weight; Ogre::String m_name; std::multimap 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( 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 stateMap; std::set 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( 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( 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 animation_list; std::vector 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 animation_nodes; AnimationNode *parent; std::list parent_stack; std::unordered_map nodeMap; std::vector 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().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( 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 T *get(const Ogre::String &name) { return static_cast(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 > animations; }; struct CharacterAnimationModule { CharacterAnimationModule(flecs::world &ecs); }; } #endif