Files
ogre-prototype/src/gamedata/CharacterAnimationModule.cpp

597 lines
20 KiB
C++

#include <iostream>
#include "Components.h"
#include "EventTriggerModule.h"
#include "CharacterModule.h"
#include "PhysicsModule.h"
#include "CharacterAnimationModule.h"
#include "EventModule.h"
#include "TerrainModule.h"
#include "WaterModule.h"
#include "world-build.h"
#include "AnimationSystem.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<CharacterAnimationModule>();
ecs.component<AnimationControl>();
ecs.import <EventModule>();
ecs.import <TerrainModule>();
ecs.import <WaterModule>();
ecs.import <PhysicsModule>();
ecs.system<const CharacterBase, AnimationControl>("HandleAnimations")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
ZoneScopedN("HandleAnimations");
if (!anim.configured) {
int i, j;
e.set<EventData>({});
Ogre::SceneNode *n =
ECS::get<CharacterModule>()
.characterNodes.at(e);
Ogre::Entity *ent = static_cast<Ogre::Entity *>(
n->getAttachedObject(0));
ent->getSkeleton()->setBlendMode(
Ogre::ANIMBLEND_CUMULATIVE);
Ogre::AnimationStateSet *animStateSet =
ent->getAllAnimationStates();
const Ogre::AnimationStateMap &animMap =
animStateSet->getAnimationStates();
anim.mAnimationSystem =
new AnimationSystem::AnimationSystem(
false);
ent->getSkeleton()
->getBone("Root")
->removeAllChildren();
for (auto it = animMap.begin();
it != animMap.end(); it++) {
AnimationSystem::Animation *animation =
new AnimationSystem::Animation(
ent->getSkeleton(),
it->second,
ent->getSkeleton()
->getAnimation(
it->first));
#ifdef VDEBUG
std::cout
<< "animation: " << animNames[i]
<< std::endl;
#endif
animation->setLoop(true);
anim.mAnimationSystem->add_animation(
it->first, animation);
}
anim.mAnimationSystem
->builder()
/* clang-format off */
->output()
->state_machine(ANIM_FADE_SPEED, "main")
->state("locomotion")
->state_machine(ANIM_FADE_SPEED, "locomotion-state")
->state("idle")
->animation("idle")
->end()
->state("walking")
->animation("walking")
->end()
->state("running")
->animation("running")
->end()
->state("treading_water")
->animation("treading_water")
->end()
->state("swimming")
->animation("swimming")
->end()
->state("swimming-fast")
->speed(20.0f)
->animation("swimming")
->end()
->end()
->end()
->end()
->state("actuator")
->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_entity(e, "end_of_climb", 0.99f, "animation:swimming-edge-climb:end")
->end()
->state("hanging-climb")
->animation("hanging-climb")
->trigger_entity(e, "end_of_climb2", 0.99f, "animation:hanging-climb:end")
->end()
->state("idle")
->animation("idle-act")
->end()
->state("pass-character")
->animation("pass-character")
->trigger_entity(e, "pass-character", 0.99f, "animation:pass-character:end")
->end()
->state("character-talk")
->animation("character-talk")
->end()
->state("sitting")
->animation("sitting-chair")
->end()
->state("sitting-ground")
->animation("sitting-ground")
->end()
->state("sitting-chair")
->animation("sitting-chair")
->end()
->transition_end("swimming-edge-climb", "idle")
->transition_end("hanging-climb", "idle")
->transition_end("pass-character", "idle")
->end()
->end()
->end();
/* clang-format on */
anim.mAnimationSystem
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main")
->setAnimation("locomotion", true);
anim.mAnimationSystem
->get<AnimationSystem::
AnimationNodeStateMachine>(
"locomotion-state")
->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) {
ZoneScopedN("HandleAnimations1");
float delta = eng.delta;
// ch.mBoneMotion = Ogre::Vector3::ZERO;
if (!anim.mAnimationSystem)
return;
bool result = anim.mAnimationSystem->addTime(delta);
Ogre::Vector3 rootMotion =
anim.mAnimationSystem->getRootMotionDelta();
ch.mBonePrevMotion = ch.mBoneMotion;
ch.mBoneMotion = rootMotion;
#if 0
{
ZoneScopedN("Ogre::Entity::_updateAnimation");
ch.mBodyEnt->_updateAnimation();
}
ch.mBodyEnt->getSkeleton()->getBone("Root")->setPosition(
Ogre::Vector3::ZERO);
#endif
// The value we get is interpolated value. When result is true it is new step
#undef VDEBUG
#ifdef VDEBUG
std::cout << "root motion: " << delta << ": "
<< ch.mBoneMotion << " - "
<< ch.mRootBone->getPosition()
<< " result: " << result << std::endl;
#endif
#undef VDEBUG
});
ecs.system<const EngineData, CharacterBase, CharacterVelocity>(
"HandleRootMotionVelocity")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, CharacterVelocity &v) {
ZoneScopedN("HandleRootMotionVelocity");
if (eng.delta < 0.0000001f)
return;
if (ECS::get<CharacterModule>().characterNodes.find(
e) ==
ECS::get<CharacterModule>().characterNodes.end())
return;
const Ogre::SceneNode *n =
ECS::get<CharacterModule>().characterNodes.at(
e);
Ogre::Quaternion rot = n->getOrientation();
Ogre::Vector3 pos = n->getPosition();
Ogre::Vector3 boneMotion = ch.mBoneMotion;
v.velocity = Ogre::Vector3::ZERO;
if (eng.delta <= 0.005)
return;
float safeDelta =
Ogre::Math::Clamp(eng.delta, 0.005f, 0.99f);
#if 0
if (!e.has<CharacterInActuator>()) {
v.velocity = Ogre::Math::lerp(
v.velocity,
rot * boneMotion / safeDelta, 0.99f);
} else {
// v.velocity = rot * boneMotion / safeDelta;
v.velocity = Ogre::Math::lerp(
v.velocity,
rot * boneMotion / safeDelta, 0.99f);
}
#endif
v.velocity = rot * boneMotion / safeDelta;
// if (v.velocity.squaredLength() > 1.4f * 1.4f)
// v.velocity = v.velocity.normalisedCopy() * 1.4f;
// ch.mBoneMotion = Ogre::Vector3::ZERO;
// safety
// std::cout << "velocity: " << v.velocity << std::endl;
v.velocity.x =
Ogre::Math::Clamp(v.velocity.x, -16.0f, 16.0f);
v.velocity.z =
Ogre::Math::Clamp(v.velocity.z, -16.0f, 16.0f);
v.velocity.y =
Ogre::Math::Clamp(v.velocity.y, -10.5f, 10.0f);
#if 0
v.velocity.y = 0.0f;
#endif
OgreAssert(v.velocity.squaredLength() < 1000.0f,
"Bad velocity setting " + Ogre::StringConverter::toString(safeDelta) + " " + Ogre::StringConverter::toString(boneMotion));
});
ecs.system<const EngineData, CharacterBase, AnimationControl,
CharacterVelocity>("HandleRootMotion")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, AnimationControl &anim,
CharacterVelocity &v) {
ZoneScopedN("HandleRootMotion");
#if 0
if (!ch.mBodyNode)
return;
#endif
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;
});
ecs.system<const Input, const CharacterBase, AnimationControl>(
"HandleNPCAnimations")
.kind(flecs::OnUpdate)
.with<Character>()
.without<Player>()
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim) {
ZoneScopedNC("HandleNPCAnimations", 0xFF2020);
if (!anim.configured)
return;
if (!anim.mAnimationSystem)
return;
AnimationSystem::AnimationNodeStateMachine
*state_machine = anim.mAnimationSystem->get<
AnimationSystem::
AnimationNodeStateMachine>(
"locomotion-state");
Ogre::String current_state =
state_machine->getCurrentState();
Ogre::String next_state = "idle";
if (current_state != "treading_water" &&
e.has<InWater>())
next_state = "treading_water";
if (current_state != "idle" && !e.has<InWater>())
next_state = "idle";
state_machine->setAnimation(next_state);
{
ZoneScoped;
ZoneTextF("animation: next_state: %s %d %d",
next_state.c_str(),
(int)ch.is_submerged,
(int)e.has<InWater>());
}
});
ecs.system<const CharacterBase, const CharacterInActuator,
AnimationControl>("HandlePlayerAnimationsActuator")
.kind(flecs::OnUpdate)
.with<Character>()
.each([](flecs::entity e, const CharacterBase &ch,
const CharacterInActuator &inact,
AnimationControl &anim) {
ZoneScopedN("HandlePlayerAnimationsActuator");
if (!anim.configured)
return;
AnimationSystem::AnimationNodeStateMachine *main_sm =
anim.mAnimationSystem
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main");
AnimationSystem::AnimationNodeStateMachine *actuator_sm =
anim.mAnimationSystem
->get<AnimationSystem::
AnimationNodeStateMachine>(
"actuator-state");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "actuator")
main_sm->setAnimation("actuator", true);
actuator_sm->setAnimation(inact.animationState, true);
});
ecs.system<const CharacterBase, AnimationControl>(
"HandlePlayerAnimationsNoActuator")
.kind(flecs::OnUpdate)
.with<Character>()
.without<CharacterInActuator>()
.without<CharacterControlDisable>()
.each([](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
ZoneScopedN("HandlePlayerAnimationsNoActuator");
if (!anim.configured)
return;
if (!anim.mAnimationSystem)
return;
AnimationSystem::AnimationNodeStateMachine *main_sm =
anim.mAnimationSystem
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "locomotion")
main_sm->setAnimation("locomotion", true);
});
ecs.system<const Input, const CharacterBase, AnimationControl>(
"HandlePlayerAnimations")
.kind(flecs::OnUpdate)
.with<Character>()
.with<Player>()
.without<CharacterInActuator>()
.without<CharacterControlDisable>()
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim) {
ZoneScopedN("HandlePlayerAnimations");
if (!anim.configured)
return;
AnimationSystem::AnimationNodeStateMachine
*state_machine = anim.mAnimationSystem->get<
AnimationSystem::
AnimationNodeStateMachine>(
"locomotion-state");
Ogre::String current_state =
state_machine->getCurrentState();
bool controls_idle = input.motion.zeroLength();
bool anim_is_idle = current_state == "idle" ||
current_state == "treading_water";
bool anim_is_walking = current_state == "walking";
bool anim_is_running = current_state == "running";
bool anim_is_swimming_slow = current_state ==
"swimming";
bool anim_is_swimming_fast = current_state ==
"swimming-fast";
bool anim_is_swimming = anim_is_swimming_slow ||
anim_is_swimming_fast;
bool anim_is_motion = anim_is_walking ||
anim_is_running ||
anim_is_swimming;
bool start_motion = !controls_idle && anim_is_idle;
bool end_motion = controls_idle && !anim_is_idle;
Ogre::String next_state = current_state;
if (controls_idle && anim_is_idle) {
if (current_state != "treading_water" &&
ch.is_submerged)
next_state = "treading_water";
else if (current_state != "idle" &&
!ch.is_submerged)
next_state = "idle";
state_machine->setAnimation(next_state);
} else if (start_motion) {
if (ch.is_submerged) {
if (input.fast)
next_state = "swimming-fast";
else
next_state = "swimming";
} else {
if (input.fast)
next_state = "running";
else
next_state = "walking";
}
state_machine->setAnimation(next_state, true);
} else if (end_motion) {
if (ch.is_submerged)
state_machine->setAnimation(
"treading_water");
else
state_machine->setAnimation("idle");
} else {
if (ch.is_submerged) {
if (input.fast &&
!anim_is_swimming_fast) {
next_state = "swimming-fast";
} else if (!input.fast &&
!anim_is_swimming_slow) {
next_state = "swimming";
}
} else {
if (input.fast && !anim_is_running)
next_state = "running";
else if (!input.fast &&
!anim_is_walking)
next_state = "walking";
}
if (current_state != next_state)
state_machine->setAnimation(next_state);
}
});
ecs.system<const Input, const CharacterBase, AnimationControl,
CharacterInActuator>("HandlePlayerAnimations2")
.kind(flecs::OnUpdate)
.with<Character>()
.with<Player>()
.without<CharacterControlDisable>()
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim,
CharacterInActuator &act) {
ZoneScopedN("HandlePlayerAnimations2");
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) {
trig.get_mut<EventData>()
.add(e,
"actuator_forward",
trig, e);
trig.modified<
EventData>();
}
if (input.motion.z > 0) {
trig.get_mut<EventData>()
.add(e,
"actuator_backward",
trig, e);
trig.modified<
EventData>();
}
}
if (input.act_pressed) {
trig.get_mut<EventData>().add(
e, "actuator_action",
trig, e);
trig.modified<EventData>();
}
// 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) {
e.get_mut<EventData>().add(
e,
"_in_actuator_forward",
e, e);
}
if (input.motion.z > 0) {
e.get_mut<EventData>().add(
e,
"_in_actuator_backward",
e, e);
}
}
if (input.act_pressed) {
e.get_mut<EventData>().add(
e,
"_in_actuator_action",
e, e);
}
}
act.prevMotion.x = input.motion.x;
act.prevMotion.y = input.motion.y;
act.prevMotion.z = input.motion.z;
}
act.prevMotion = input.motion;
});
ecs.system<EventData>("UpdateEvents")
.kind(flecs::OnUpdate)
.with<Character>()
.each([](flecs::entity e, EventData &evt) {
ZoneScopedN("HandleEvents");
for (auto ev : evt.events) {
std::cout << "character event: " << ev.event
<< std::endl;
/* parse character events */
e.each<InTrigger>([&](flecs::entity trig) {
/* if triggered, dispatch events to trigger */
trig.get_mut<EventData>().add(
ev.sender, ev.event,
trig, // it is easier this way to identify trigger entity
ev.e2);
});
}
evt.events.clear();
});
#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
struct AnimationSetCommand : public GameWorld::Command {
int operator()(const std::vector<GameWorld::Parameter *> &args)
override
{
ZoneScoped;
GameWorld::ValueParameter<flecs::entity> *param_e =
static_cast<GameWorld::ValueParameter<
flecs::entity> *>(args[0]);
OgreAssert(param_e->get().is_valid(), "bad entity");
GameWorld::ValueParameter<std::string> *param_node =
static_cast<GameWorld::ValueParameter<
std::string> *>(args[1]);
GameWorld::ValueParameter<std::string> *param_state =
static_cast<GameWorld::ValueParameter<
std::string> *>(args[2]);
if (param_e->get().has<AnimationControl>() &&
param_e->get().get<AnimationControl>().configured ==
true) {
const AnimationControl &control =
param_e->get().get<AnimationControl>();
AnimationSystem::AnimationNodeStateMachine *sm =
control.mAnimationSystem->get<
AnimationSystem::
AnimationNodeStateMachine>(
param_node->get());
bool reset = false;
if (args.size() == 4) {
GameWorld::ValueParameter<bool>
*param_reset = static_cast<
GameWorld::ValueParameter<
bool> *>(
args[3]);
reset = param_reset->get();
}
sm->setAnimation(param_state->get(), reset);
std::cout << "animation switch: "
<< param_node->get() << " "
<< param_state->get() << std::endl;
}
return 0;
}
};
ECS::get_mut<GameWorld>().add_command<AnimationSetCommand>(
"set_animation_state");
}
}