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

@@ -1,5 +1,6 @@
#include <iostream>
#include "Components.h"
#include "EventTriggerModule.h"
#include "CharacterModule.h"
#include "CharacterAnimationModule.h"
namespace ECS
@@ -17,10 +18,18 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
ch.mSkeleton->setBlendMode(
Ogre::ANIMBLEND_CUMULATIVE);
Ogre::String animNames[] = {
"idle", "walking",
"running", "treading_water",
"swimming", "hanging-idle",
"hanging-climb", "swimming-hold-edge"
"idle",
"walking",
"running",
"treading_water",
"swimming",
"hanging-idle",
"hanging-climb",
"swimming-hold-edge",
"swimming-edge-climb",
"character-talk",
"pass-character",
"idle-act"
};
int state_count = sizeof(animNames) /
sizeof(animNames[0]);
@@ -28,15 +37,17 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
new AnimationSystem(false);
for (i = 0; i < state_count; i++) {
Animation *animation = new Animation(
ch.mSkeleton,
ch.mBodyEnt->getAnimationState(
animNames[i]),
ch.mSkeleton->getAnimation(
animNames[i])
);
animNames[i]),
e);
#ifdef VDEBUG
std::cout
<< "animation: " << animNames[i]
<< std::endl;
#endif
animation->setLoop(true);
anim.mAnimationSystem->add_animation(
animNames[i], animation);
@@ -71,16 +82,34 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
->end()
->end()
->state("actuator")
->state_machine(ANIM_FADE_SPEED, "actuator-state")
->state_machine(ANIM_FADE_SPEED * 10.0f, "actuator-state")
->state("hanging-idle")
->animation("hanging-idle")
->end()
->state("swimming-hold-edge")
->animation("swimming-hold-edge")
->end()
->state("swimming-edge-climb")
->animation("swimming-edge-climb")
->trigger(e, "end_of_climb", 0.99f, "animation:swimming-edge-climb:end")
->end()
->state("hanging-climb")
->animation("hanging-climb")
->trigger(e, "end_of_climb2", 0.99f, "animation:hanging-climb:end")
->end()
->state("idle")
->animation("idle-act")
->end()
->state("pass-character")
->animation("pass-character")
->trigger(e, "pass-character", 0.99f, "animation:pass-character:end")
->end()
->state("character-talk")
->animation("character-talk")
->end()
->transition_end("swimming-edge-climb", "idle")
->transition_end("hanging-climb", "idle")
->transition_end("pass-character", "idle")
->end()
->end()
->end();
@@ -88,32 +117,85 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
anim.mAnimationSystem
->get<AnimationNodeStateMachine>("main")
->setAnimation("locomotion");
->setAnimation("locomotion", true);
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"locomotion-state")
->setAnimation("idle");
->setAnimation("idle", true);
anim.configured = true;
}
});
#if 0
ecs.system<CharacterBase>("RootMotionStart")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, CharacterBase &ch) {
ch.mBoneMotion = Ogre::Vector3::ZERO;
});
#endif
ecs.system<const EngineData, CharacterBase, AnimationControl>(
"HandleAnimations1")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, AnimationControl &anim) {
float delta = eng.delta;
anim.mAnimationSystem->addTime(delta);
// ch.mBoneMotion = Ogre::Vector3::ZERO;
bool result = anim.mAnimationSystem->addTime(delta);
if (!ch.mRootBone)
return;
ch.mBoneMotion += ch.mRootBone->getPosition();
// The value we get is interpolated value. When result is true it is new step
#if 0
Ogre::Vector3 offset = ch.mRootBone->getPosition();
ch.mBoneMotion = static_cast<RootMotionListener *>(
anim.mListener)
->getDeltaMotion();
ch.mRootBone->setPosition(Ogre::Vector3::ZERO);
Ogre::Vector3 d = offset - ch.mBonePrevMotion;
ch.mBonePrevMotion = offset;
std::cout << "length: " << d.length() << std::endl;
if (d.squaredLength() > 0.02f * 0.02f)
d = offset;
if (d.squaredLength() > 0.02f * 0.02f)
d = Ogre::Vector3::ZERO;
std::cout << "length2: " << d.length() << std::endl;
OgreAssert(d.length() < 0.5f, "bad offset");
ch.mBoneMotion = d;
#endif
#if 0
if (result) {
if (d.squaredLength() > 0.0f)
ch.mBoneMotion =
ch.mRootBone->getPosition() -
ch.mBoneMotion;
else
ch.mBoneMotion =
ch.mRootBone->getPosition();
} else {
ch.mBoneMotion = ch.mRootBone->getPosition() -
ch.mBoneMotion;
}
#endif
#undef VDEBUG
#ifdef VDEBUG
std::cout << "root motion: " << delta << ": "
<< ch.mBoneMotion << " - "
<< ch.mRootBone->getPosition()
<< " result: " << result << std::endl;
#endif
#undef VDEBUG
#if 0
// ch.mRootBone->setPosition(Ogre::Vector3::ZERO);
ch.mBonePrevMotion = offset;
#endif
});
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
ecs.system<const EngineData, CharacterBase, CharacterVelocity>(
"HandleRootMotionVelocity")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, CharacterVelocity &v) {
CharacterBase &ch, CharacterVelocity &v) {
if (eng.delta < 0.0000001f)
return;
if (!ch.mBodyNode)
@@ -121,7 +203,9 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
Ogre::Vector3 boneMotion = ch.mBoneMotion;
v.velocity = rot * boneMotion / eng.delta;
v.velocity = Ogre::Math::lerp(
v.velocity, rot * boneMotion / eng.delta,
0.99f);
if (!e.has<CharacterDisablePhysics>() &&
!e.has<CharacterInActuator>()) {
if (eng.startupDelay <= 0.0f)
@@ -129,6 +213,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
v.velocity.y = Ogre::Math::Clamp(
v.velocity.y, -10.5f, 1000000.0f);
}
// ch.mBoneMotion = Ogre::Vector3::ZERO;
});
ecs.system<const EngineData, const AnimationControl,
const CharacterBase, CharacterVelocity>("HandleSwimming")
@@ -182,8 +267,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
penetration = body.mController
->isPenetrating();
if (is_on_floor)
v.gvelocity = Ogre::Vector3(
0.0f, 0.0f, 0.0f);
v.gvelocity =
Ogre::Vector3::ZERO;
btTransform from(
Ogre::Bullet::convert(
@@ -192,14 +277,21 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
Ogre::Bullet::convert(
ch.mBodyNode
->getPosition()));
ch.mBodyNode->setPosition(
ch.mBodyNode->getPosition() +
ch.mBodyNode->_setDerivedPosition(
ch.mBodyNode
->_getDerivedPosition() +
rotMotion);
ch.mBoneMotion = Ogre::Vector3(0, 0, 0);
}
}
});
ecs.system<CharacterVelocity, CharacterBase>("HandleRootMotionEnd")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, CharacterVelocity &v,
CharacterBase &ch) {
// zero the velocity;
// v.velocity = Ogre::Vector3::ZERO;
// ch.mBoneMotion = Ogre::Vector3::ZERO;
});
ecs.system<const Input, const CharacterBase, AnimationControl>(
"HandleNPCAnimations")
@@ -243,8 +335,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
"actuator-state");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "actuator")
main_sm->setAnimation("actuator");
actuator_sm->setAnimation(inact.animationState);
main_sm->setAnimation("actuator", true);
actuator_sm->setAnimation(inact.animationState, true);
});
ecs.system<const CharacterBase, AnimationControl>(
"HandlePlayerAnimationsNoActuator")
@@ -261,7 +353,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
"main");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "locomotion")
main_sm->setAnimation("locomotion");
main_sm->setAnimation("locomotion", true);
});
ecs.system<const Input, const CharacterBase, AnimationControl>(
"HandlePlayerAnimations")
@@ -303,7 +395,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
else if (current_state != "idle" &&
!ch.is_submerged)
next_state = "idle";
state_machine->setAnimation(next_state, true);
state_machine->setAnimation(next_state);
} else if (start_motion) {
if (ch.is_submerged) {
if (input.fast)
@@ -343,5 +435,187 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
state_machine->setAnimation(next_state);
}
});
ecs.system<const Input, const CharacterBase, AnimationControl,
CharacterInActuator>("HandlePlayerAnimations2")
.kind(flecs::OnUpdate)
.with<Character>()
.with<Player>()
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim,
CharacterInActuator &act) {
bool controls_idle = input.motion.zeroLength();
if (!controls_idle) {
std::cout << "motion.z: "
<< Ogre::Math::Abs(input.motion.z -
act.prevMotion.z)
<< std::endl;
bool trigger_event = false;
e.each<InTrigger>([&](flecs::entity trig) {
if (Ogre::Math::Abs(input.motion.z -
act.prevMotion.z) >
0.001f) {
if (input.motion.z < 0)
ECS::get<LuaBase>()
.mLua
->call_handler(
"actuator_forward",
trig,
e);
if (input.motion.z > 0)
ECS::get_mut<LuaBase>()
.mLua
->call_handler(
"actuator_backward",
trig,
e);
}
if (input.act_pressed)
ECS::get_mut<LuaBase>()
.mLua->call_handler(
"actuator_action",
trig, e);
// ECS::get_mut<LuaData>().call_handler(
// "actuator_update", trig, e);
trigger_event = true;
});
if (!trigger_event) {
if (Ogre::Math::Abs(input.motion.z -
act.prevMotion.z) >
0.001f) {
if (input.motion.z < 0)
ECS::get<LuaBase>()
.mLua
->call_handler(
"_in_actuator_forward",
e, e);
if (input.motion.z > 0)
ECS::get_mut<LuaBase>()
.mLua
->call_handler(
"_in_actuator_backward",
e, e);
}
if (input.act_pressed)
ECS::get_mut<LuaBase>()
.mLua->call_handler(
"_in_actuator_action",
e, e);
// ECS::get_mut<LuaData>().call_handler(
// "actuator_update", trig, e);
}
act.prevMotion.x = input.motion.x;
act.prevMotion.y = input.motion.y;
act.prevMotion.z = input.motion.z;
}
#if 0
if (!controls_idle) {
if (Ogre::Math::Abs(input.motion.z - act.prevMotion.z) > 0.001f) {
if (input.motion.z < 0)
ECS::get_mut<LuaData>().call_handler("actuator_forward", e);
}
ECS::get_mut<LuaData>().call_handler("actuator_controls_update");
}
#endif
act.prevMotion = input.motion;
});
#ifdef VDEBUG
ecs.system<const CharacterBase>("CharacterGravityStatus")
.kind(flecs::OnUpdate)
.with<Character>()
.with<Player>()
.each([](flecs::entity e, const CharacterBase &ch) {
if (e.has<CharacterGravity>())
std::cout << "gravity\n";
else
std::cout << "no gravity\n";
if (e.has<InWater>())
std::cout << "in water\n";
else
std::cout << "out of water\n";
std::cout
<< "h=" << ch.mBodyNode->_getDerivedPosition().y
<< std::endl;
});
#endif
ecs.system<const EngineData, const CharacterBase, CharacterBody>(
"UpdateBodyCast")
.kind(flecs::OnUpdate)
.without<CharacterInActuator>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, CharacterBody &body) {
struct ResultCallback
: public btCollisionWorld::RayResultCallback {
btCollisionObject *m_me;
btVector3 m_from, m_to, m_hitNormalWorld,
m_hitPointWorld;
ResultCallback(btCollisionObject *me,
const btVector3 &from,
const btVector3 &to)
: m_me(me)
, m_from(from)
, m_to(to)
{
}
btScalar addSingleResult(
btCollisionWorld::LocalRayResult
&rayResult,
bool normalInWorldSpace) override
{
if (rayResult.m_collisionObject == m_me)
return 1.0f;
if (!btPairCachingGhostObject::upcast(
rayResult.m_collisionObject))
return 1.0f;
if (!(rayResult.m_collisionObject
->getCollisionFlags() &
btCollisionObject::
CF_CHARACTER_OBJECT))
return 1.0f;
m_closestHitFraction =
rayResult.m_hitFraction;
m_collisionObject =
rayResult.m_collisionObject;
if (normalInWorldSpace)
m_hitNormalWorld =
rayResult
.m_hitNormalLocal;
else
m_hitNormalWorld =
m_collisionObject
->getWorldTransform()
.getBasis() *
rayResult
.m_hitNormalLocal;
m_hitPointWorld.setInterpolate3(
m_from, m_to,
rayResult.m_hitFraction);
return rayResult.m_hitFraction;
}
};
Ogre::Vector3 offset(0.0f, 0.5f, 0.0f);
float dist = 0.5f;
btVector3 a = Ogre::Bullet::convert(
ch.mBodyNode->getPosition() + offset),
b(Ogre::Bullet::convert(
ch.mBodyNode->getPosition() +
ch.mBodyNode->getOrientation() *
Ogre::Vector3(0, 0, dist) +
offset));
ResultCallback result(body.mGhostObject, a, b);
// body.mGhostObject->rayTest(a, b, result);
eng.mWorld->getBtWorld()->rayTest(a, b, result);
if (result.hasHit()) {
std::cout << "Hit!!! " << result.m_hitPointWorld
<< std::endl;
e.set<CharacterInActuator>(
{ "idle", { 0, 0, 0 } });
ECS::get<LuaBase>().mLua->call_handler(
"character_enter", e,
ECS::get<Body2Entity>().entities.at(
const_cast<btCollisionObject *>(
result.m_collisionObject)));
}
});
}
}