Now root motion works much better; raft/boat climbing

This commit is contained in:
2025-10-04 04:10:21 +03:00
parent 25280a9cbe
commit 19a1275a8a
14 changed files with 844 additions and 149 deletions

View File

@@ -3,9 +3,74 @@
#include <Ogre.h>
#include <flecs.h>
#include "GameData.h"
#include "CharacterModule.h"
#include "LuaData.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;
@@ -65,17 +130,20 @@ struct Animation {
Ogre::Animation *mSkelAnimation;
Ogre::NodeAnimationTrack *mHipsTrack;
Ogre::NodeAnimationTrack *mRootTrack;
RootMotionListener *mListener;
float m_weight;
float m_accWeight;
std::multimap<float, AnimationTrigger *> trigger_list;
Animation(Ogre::AnimationState *animState,
Ogre::Animation *skelAnimation)
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 =
@@ -87,10 +155,21 @@ struct Animation {
// 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);
#if 0
OgreAssert(mHipsTrack, "no hips track");
OgreAssert(mRootTrack, "no Root track");
#endif
#if 0
if (mRootTrack) {
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
Ogre::Vector3 motion = Ogre::Vector3::ZERO;
@@ -107,6 +186,55 @@ struct Animation {
motion = trans;
}
}
#endif
#if 0
if (!mMetaRootTrack) {
Ogre::Bone *bone = nullptr;
OgreAssert(skeleton->hasBone("MetaRoot"),
"no bone MetaRoot");
if (skeleton->hasBone("MetaRoot"))
bone = skeleton->getBone("MetaRoot");
#if 0
else
bone = skeleton->createBone("MetaRoot");
#endif
bone->setPosition(Ogre::Vector3::ZERO);
bone->setOrientation(Ogre::Quaternion::IDENTITY);
std::vector<
std::pair<float, std::pair<Ogre::Vector3,
Ogre::Quaternion> > >
keyframes;
for (j = 0; j < mRootTrack->getNumKeyFrames(); j++) {
Ogre::TransformKeyFrame *kf =
mRootTrack->getNodeKeyFrame(j);
const Ogre::Vector3 &pt = kf->getTranslate();
const Ogre::Quaternion &rt = kf->getRotation();
float tp = kf->getTime();
keyframes.push_back({ tp, { pt, rt } });
#if 0
new_kf->setTranslate(pt);
new_kf->setTranslate(kf->getTranslate());
new_kf->setRotation(kf->getRotation());
#endif
kf->setTranslate(Ogre::Vector3::ZERO);
kf->setRotation(Ogre::Quaternion::IDENTITY);
}
mMetaRootTrack = mSkelAnimation->createNodeTrack(
bone->getHandle());
OgreAssert(mMetaRootTrack,
"failed to create node track");
for (j = 0; j < keyframes.size(); j++) {
Ogre::TransformKeyFrame *new_kf =
mMetaRootTrack->createNodeKeyFrame(
keyframes[j].first);
new_kf->setTranslate(keyframes[j].second.first);
new_kf->setRotation(keyframes[j].second.second);
}
}
#if 0
mRootTrack = track;
#endif
#endif
}
Ogre::String getName()
{
@@ -139,11 +267,22 @@ struct Animation {
{
return m_weight;
}
void addTime(float time)
bool addTime(float time)
{
preUpdateTriggers();
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);
postUpdateTriggers(time);
index = mSkelAnimation->_getTimeIndex(
mAnimationState->getTimePosition());
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index);
return prev_index != next_index;
}
void reset()
{
@@ -161,55 +300,9 @@ struct Animation {
{
return m_accWeight;
}
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 = mAnimationState->getTimePosition() /
mAnimationState->getLength();
}
void postUpdateTriggers(float delta)
{
float postUpdateTime = mAnimationState->getTimePosition() /
mAnimationState->getLength();
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);
}
} else {
updateTriggers(currentTime, 1);
updateTriggers(0, nextTime);
}
}
float getLength() const
{
return mAnimationState->getLength();
if (getEnabled())
return mAnimationState->getLength();
else
@@ -217,6 +310,7 @@ struct Animation {
}
float getTimePosition() const
{
return mAnimationState->getTimePosition();
if (getEnabled())
return mAnimationState->getTimePosition();
else
@@ -228,11 +322,12 @@ struct AnimationNode {
std::vector<AnimationNode *> children;
float m_weight;
Ogre::String m_name;
std::multimap<float, AnimationTrigger *> trigger_list;
AnimationNode()
: m_weight(0)
{
}
virtual void addTime(float time) = 0;
virtual bool addTime(float time) = 0;
virtual void setWeight(float weight) = 0;
virtual void reset() = 0;
virtual float getLength() const = 0;
@@ -256,6 +351,52 @@ struct AnimationNode {
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 {
@@ -266,9 +407,13 @@ struct AnimationNodeAnimation : AnimationNode {
, mAnimation(animation)
{
}
void addTime(float time)
bool addTime(float time)
{
mAnimation->addTime(time);
bool ret;
preUpdateTriggers();
ret = mAnimation->addTime(time);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
@@ -281,6 +426,7 @@ struct AnimationNodeAnimation : AnimationNode {
}
float getLength() const
{
return mAnimation->getLength();
if (enabled)
return mAnimation->getLength();
else
@@ -288,6 +434,7 @@ struct AnimationNodeAnimation : AnimationNode {
}
float getTimePosition() const
{
return mAnimation->getTimePosition();
if (enabled)
return mAnimation->getTimePosition();
else
@@ -299,9 +446,13 @@ struct AnimationNodeStateMachineState : AnimationNode {
: AnimationNode()
{
}
void addTime(float time)
bool addTime(float time)
{
children[0]->addTime(time);
bool ret;
preUpdateTriggers();
ret = children[0]->addTime(time);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
@@ -319,7 +470,7 @@ struct AnimationNodeStateMachineState : AnimationNode {
}
float getTimePosition() const
{
return children[0]->getLength();
return children[0]->getTimePosition();
}
};
struct AnimationNodeSpeed : AnimationNode {
@@ -331,9 +482,13 @@ struct AnimationNodeSpeed : AnimationNode {
, enabled(false)
{
}
void addTime(float time)
bool addTime(float time)
{
children[0]->addTime(time * m_speed);
bool ret;
preUpdateTriggers();
ret = children[0]->addTime(time * m_speed);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
@@ -383,36 +538,43 @@ struct AnimationNodeStateMachine : AnimationNode {
{
m_weight = 1.0f;
}
void addTime(float time)
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);
}
@@ -426,8 +588,11 @@ struct AnimationNodeStateMachine : AnimationNode {
}
}
OgreAssert(currentAnim, "bad current anim");
bool ret = false;
if (currentAnim)
currentAnim->addTime(time);
ret = currentAnim->addTime(time);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
@@ -522,9 +687,13 @@ struct AnimationNodeOutput : AnimationNode {
, m_speed(1.0f)
{
}
void addTime(float time)
bool addTime(float time)
{
children[0]->addTime(time * m_speed);
bool ret;
preUpdateTriggers();
ret = children[0]->addTime(time * m_speed);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
@@ -614,8 +783,9 @@ struct AnimationSystem : AnimationNode {
Ogre::String event;
void operator()(const AnimationTrigger *trigger)
{
ECS::get_mut<LuaData>().call_handler(
event, ent, ent);
ECS::get_mut<LuaBase>()
.mLua->call_handler(event, ent,
ent);
}
EventSubscriber(flecs::entity e,
const Ogre::String &event)
@@ -625,15 +795,57 @@ struct AnimationSystem : AnimationNode {
}
};
OgreAssert(parent, "bad parent");
Animation *animation =
static_cast<AnimationNodeAnimation *>(parent)
->mAnimation;
OgreAssert(animation, "bad animation");
AnimationTrigger *trigger =
new AnimationTrigger(name, time, 0.1f);
EventSubscriber *sub = new EventSubscriber(e, event);
trigger->addSubscriber(sub);
animation->addTrigger(trigger);
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,
@@ -685,10 +897,11 @@ struct AnimationSystem : AnimationNode {
}
};
AnimationSystemBuilder m_builder;
void addTime(float time)
bool addTime(float time)
{
int i;
m_builder.animation_nodes[0]->addTime(time);
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];
@@ -706,13 +919,17 @@ struct AnimationSystem : AnimationNode {
float weight = vanimation_list[i]->getAccWeight();
vanimation_list[i]->setWeight(weight);
vanimation_list[i]->resetAccWeight();
#define VDEBUG
#ifdef VDEBUG
if (debug)
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)
{