Animation tree implemented

This commit is contained in:
2025-09-22 20:34:12 +03:00
parent a62d781aa0
commit 9e5d08bfc6
13 changed files with 1088 additions and 400 deletions

View File

@@ -2,10 +2,11 @@
#include <Ogre.h>
#include <OgreBullet.h>
#include "GameData.h"
#include "CharacterModule.h"
#include "WaterModule.h"
#include "TerrainModule.h"
#include "Components.h"
#include "CharacterAnimationModule.h"
#include "CharacterModule.h"
namespace ECS
{
CharacterModule::CharacterModule(flecs::world &ecs)
@@ -81,111 +82,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
}
ECS::get().modified<ECS::Input>();
});
ecs.system<const CharacterBase, AnimationControl>("HandleAnimations")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
if (!anim.configured && ch.mSkeleton) {
int i, j;
ch.mSkeleton->setBlendMode(
Ogre::ANIMBLEND_CUMULATIVE);
Ogre::String
animNames[AnimationControl::NUM_ANIMS] = {
"idle", "walking", "running",
"treading_water", "swimming"
};
for (i = 0; i < AnimationControl::NUM_ANIMS;
i++) {
anim.mAnims[i] =
ch.mBodyEnt->getAnimationState(
animNames[i]);
anim.mAnims[i]->setLoop(true);
anim.mAnims[i]->setEnabled(true);
anim.mAnims[i]->setWeight(0);
anim.mFadingIn[i] = false;
anim.mFadingOut[i] = false;
anim.mSkelAnimations[i] =
ch.mSkeleton->getAnimation(
animNames[i]);
for (const auto &it :
anim.mSkelAnimations[i]
->_getNodeTrackList()) {
Ogre::NodeAnimationTrack *track =
it.second;
Ogre::String trackName =
track->getAssociatedNode()
->getName();
if (trackName ==
"mixamorig:Hips") {
anim.mHipsTracks[i] =
track;
} else if (trackName ==
"Root") {
anim.mRootTracks[i] =
track;
// mRootTracks[i]->removeAllKeyFrames();
}
}
Ogre::Vector3 delta =
Ogre::Vector3::ZERO;
Ogre::Vector3 motion =
Ogre::Vector3::ZERO;
for (j = 0;
j < anim.mRootTracks[i]
->getNumKeyFrames();
j++) {
Ogre::Vector3 trans =
anim.mRootTracks[i]
->getNodeKeyFrame(
j)
->getTranslate();
if (j == 0)
delta = trans;
else
delta = trans - motion;
anim.mRootTracks[i]
->getNodeKeyFrame(j)
->setTranslate(delta);
motion = trans;
}
}
anim.nextAnim = AnimationControl::ANIM_IDLE;
setAnimation(anim);
anim.configured = true;
}
});
ecs.system<AnimationControl>("HandleAnimations0")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, AnimationControl &anim) {
if (anim.currentAnim != anim.nextAnim)
setAnimation(anim);
});
ecs.system<const EngineData, const Input, CharacterBase,
AnimationControl>("HandleAnimations1")
.kind(flecs::OnUpdate)
.each([this](const EngineData &eng, const Input &input,
CharacterBase &ch, AnimationControl &anim) {
float delta = eng.delta;
Ogre::Real animSpeed = 1;
if (anim.currentAnim != AnimationControl::ANIM_NONE) {
if (anim.currentAnim ==
AnimationControl::ANIM_WALK)
anim.mAnims[anim.currentAnim]->addTime(
delta * 1.0f);
else if (anim.currentAnim ==
AnimationControl::ANIM_SWIMMING &&
input.fast)
anim.mAnims[anim.currentAnim]->addTime(
delta * 20.0f);
else
anim.mAnims[anim.currentAnim]->addTime(
delta * animSpeed);
}
fadeAnimations(anim, delta);
if (!ch.mRootBone)
return;
ch.mBoneMotion = ch.mRootBone->getPosition();
});
ecs.system<CharacterBase>()
.kind(flecs::OnUpdate)
.with<TerrainReady>()
@@ -261,186 +157,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
gr.gvelocity *= (1.0 - eng.delta);
gr.velocity.y *= (1.0 - eng.delta);
});
ecs.system<const EngineData, const AnimationControl,
const CharacterBase, CharacterVelocity>("HandleSwimming")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.with<CharacterBuoyancy>()
.each([this](flecs::entity e, const EngineData &eng,
const AnimationControl &anim,
const CharacterBase &ch, CharacterVelocity &gr) {
if (anim.currentAnim ==
AnimationControl::ANIM_SWIMMING) {
float h = Ogre::Math::Clamp(
0.0f - ch.mBodyNode->getPosition().y,
0.0f, 2000.0f);
if (h > 0.05 && h < 2.0f)
gr.gvelocity.y += 0.1f * (h + 1.0f) *
h * eng.delta;
}
});
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
"HandleRootMotionVelocity")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, CharacterVelocity &v) {
if (eng.delta < 0.0000001f)
return;
if (!ch.mBodyNode)
return;
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
Ogre::Vector3 boneMotion = ch.mBoneMotion;
v.velocity = rot * boneMotion / eng.delta;
if (eng.startupDelay <= 0.0f)
v.velocity += v.gvelocity;
v.velocity.y = Ogre::Math::Clamp(v.velocity.y, -10.5f,
1000000.0f);
});
ecs.system<const EngineData, CharacterBase, CharacterBody,
AnimationControl, CharacterVelocity>("HandleRootMotion")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, CharacterBody &body,
AnimationControl &anim, CharacterVelocity &v) {
if (!ch.mBodyNode)
return;
if (eng.delta < 0.0000001f)
return;
OgreAssert(eng.delta > 0.0f, "Zero delta");
int maxPen = 0;
Ogre::Vector3 colNormal;
bool is_on_floor = false;
bool penetration = false;
if (eng.startupDelay < 0.0f) {
if (body.mController) {
Ogre::Vector3 rotMotion =
v.velocity * eng.delta;
btVector3 currentPosition =
body.mGhostObject
->getWorldTransform()
.getOrigin();
is_on_floor =
body.mController->isOnFloor();
penetration = body.mController
->isPenetrating();
if (is_on_floor)
v.gvelocity = Ogre::Vector3(
0.0f, 0.0f, 0.0f);
btTransform from(
Ogre::Bullet::convert(
ch.mBodyNode
->getOrientation()),
Ogre::Bullet::convert(
ch.mBodyNode
->getPosition()));
ch.mBodyNode->setPosition(
ch.mBodyNode->getPosition() +
rotMotion);
ch.mBoneMotion = Ogre::Vector3(0, 0, 0);
}
}
});
ecs.system<const Input, const CharacterBase, AnimationControl>(
"HandlePlayerAnimations")
.kind(flecs::OnUpdate)
.with<Character>()
.with<Player>()
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim) {
if (!anim.configured)
return;
bool controls_idle = input.motion.zeroLength();
bool anim_is_idle =
anim.currentAnim ==
AnimationControl::ANIM_IDLE ||
anim.currentAnim ==
AnimationControl::ANIM_TREADING_WATER;
bool anim_is_walking = anim.currentAnim ==
AnimationControl::ANIM_WALK;
bool anim_is_running = anim.currentAnim ==
AnimationControl::ANIM_RUN;
bool anim_is_swimming = anim.currentAnim ==
AnimationControl::ANIM_SWIMMING;
bool anim_is_motion = anim_is_walking ||
anim_is_running ||
anim_is_swimming;
if (controls_idle) {
if (anim.currentAnim ==
AnimationControl::ANIM_IDLE &&
ch.is_submerged)
anim.nextAnim = AnimationControl::
ANIM_TREADING_WATER;
else if (anim.currentAnim ==
AnimationControl::
ANIM_TREADING_WATER &&
!ch.is_submerged)
anim.nextAnim =
AnimationControl::ANIM_IDLE;
}
if (!controls_idle && anim_is_idle) {
anim.reset = true;
if (ch.is_submerged) {
if (input.fast)
anim.nextAnim =
AnimationControl::
ANIM_SWIMMING;
else
anim.nextAnim =
AnimationControl::
ANIM_SWIMMING;
} else {
if (input.fast)
anim.nextAnim =
AnimationControl::ANIM_RUN;
else
anim.nextAnim =
AnimationControl::
ANIM_WALK;
}
} else
anim.reset = false;
if (controls_idle && anim_is_motion)
if (ch.is_submerged)
anim.nextAnim = AnimationControl::
ANIM_TREADING_WATER;
else
anim.nextAnim =
AnimationControl::ANIM_IDLE;
else if (!controls_idle && anim_is_motion) {
if (input.fast && anim_is_walking)
anim.nextAnim =
AnimationControl::ANIM_RUN;
else if (!input.fast && anim_is_running)
anim.nextAnim =
AnimationControl::ANIM_WALK;
if ((anim_is_walking || anim_is_running) &&
ch.is_submerged) {
if (input.fast)
anim.nextAnim =
AnimationControl::
ANIM_SWIMMING;
else
anim.nextAnim =
AnimationControl::
ANIM_SWIMMING;
} else if ((anim_is_swimming) &&
!ch.is_submerged) {
if (input.fast)
anim.nextAnim =
AnimationControl::ANIM_RUN;
else
anim.nextAnim =
AnimationControl::
ANIM_WALK;
}
}
});
#define TURN_SPEED 500.0f // character turning in degrees per second
ecs.system<const Input, const Camera, CharacterBase>("UpdateBody")
.kind(flecs::OnUpdate)
@@ -574,9 +290,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
OgreAssert(body.mCollisionShape, "No collision shape");
e.add<CharacterGravity>();
e.add<CharacterBuoyancy>();
anim.currentAnim = AnimationControl::ANIM_NONE;
anim.nextAnim = AnimationControl::ANIM_NONE;
anim.reset = false;
anim.configured = false;
});
ecs.system<const EngineData, CharacterBase, CharacterBody>(
@@ -777,54 +490,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
});
}
void CharacterModule::setAnimation(AnimationControl &anim)
{
OgreAssert(anim.nextAnim >= 0 &&
anim.nextAnim < AnimationControl::NUM_ANIMS,
"Bad animation");
if (anim.currentAnim != AnimationControl::ANIM_NONE) {
anim.mFadingIn[anim.currentAnim] = false;
anim.mFadingOut[anim.currentAnim] = true;
}
if (anim.nextAnim != AnimationControl::ANIM_NONE) {
anim.mAnims[anim.nextAnim]->setEnabled(true);
anim.mAnims[anim.nextAnim]->setWeight(0);
anim.mFadingOut[anim.nextAnim] = false;
anim.mFadingIn[anim.nextAnim] = true;
if (anim.reset)
anim.mAnims[anim.nextAnim]->setTimePosition(0);
}
anim.currentAnim = anim.nextAnim;
anim.reset = false;
}
#define ANIM_FADE_SPEED \
7.5f // animation crossfade speed in % of full weight per second
void CharacterModule::fadeAnimations(AnimationControl &anim, Ogre::Real delta)
{
int i;
for (i = 0; i < AnimationControl::NUM_ANIMS; i++) {
if (anim.mFadingIn[i]) {
// slowly fade this animation in until it has full weight
Ogre::Real newWeight = anim.mAnims[i]->getWeight() +
delta * ANIM_FADE_SPEED;
anim.mAnims[i]->setWeight(
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
if (newWeight >= 1)
anim.mFadingIn[i] = false;
} else if (anim.mFadingOut[i]) {
// slowly fade this animation out until it has no weight, and then disable it
Ogre::Real newWeight = anim.mAnims[i]->getWeight() -
delta * ANIM_FADE_SPEED;
anim.mAnims[i]->setWeight(
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
if (newWeight <= 0) {
anim.mAnims[i]->setEnabled(false);
anim.mFadingOut[i] = false;
}
}
}
}
void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
Ogre::Real deltaPitch,
Ogre::Real deltaZoom)