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

@@ -0,0 +1,557 @@
#include <iostream>
#include "Components.h"
#include "CharacterModule.h"
#include "CharacterAnimationModule.h"
namespace ECS
{
#if 0
struct AnimationSystem {
struct AnimationDataNode {
float weight;
};
int root_index;
struct AnimationSystemBuilder {
struct AnimationNode {
enum {
ANIMATION_NODE_OUTPUT,
ANIMATION_NODE_BLEND2,
ANIMATION_NODE_ANIMATION,
ANIMATION_NODE_STATEMACHINE,
ANIMATION_NODE_STATE,
ANIMATION_NODE_TRANSITION
};
AnimationNode *outputs[32];
int output_count;
int type;
std::string anchor;
};
AnimationNode *parent;
std::list<AnimationNode *> parent_stack;
std::vector<AnimationNode *> animation_nodes;
struct ANOutput : AnimationNode {};
struct ANBlend2 : AnimationNode {
float weight;
};
struct ANAnimation : AnimationNode {
std::string animation;
};
struct ANStateMachine : AnimationNode {};
struct ANState : AnimationNode {
std::string name;
bool start;
bool end;
};
struct ANStateMachineTransition : AnimationNode {
std::string from, to;
bool at_end;
};
AnimationSystemBuilder *output()
{
ANOutput *onode = new ANOutput;
onode->output_count = 0;
onode->type = AnimationNode::ANIMATION_NODE_OUTPUT;
onode->anchor = "";
animation_nodes.push_back(onode);
parent = onode;
return this;
}
AnimationSystemBuilder *blend2(float blend,
const std::string &anchor = "")
{
ANBlend2 *onode = new ANBlend2;
onode->output_count = 0;
onode->type = AnimationNode::ANIMATION_NODE_BLEND2;
onode->anchor = anchor;
onode->weight = blend;
animation_nodes.push_back(onode);
parent->outputs[parent->output_count++] = onode;
parent_stack.push_back(parent);
parent = onode;
return this;
}
AnimationSystemBuilder *
animation(const std::string &animation,
const std::string &anchor = "")
{
ANAnimation *onode = new ANAnimation;
onode->output_count = 0;
onode->type = AnimationNode::ANIMATION_NODE_ANIMATION;
onode->anchor = anchor;
onode->animation = animation;
animation_nodes.push_back(onode);
parent->outputs[parent->output_count++] = onode;
parent_stack.push_back(parent);
parent = onode;
return this;
}
AnimationSystemBuilder *end()
{
parent = parent_stack.back();
parent_stack.pop_back();
return this;
}
AnimationSystemBuilder *
state_machine(const std::string &anchor = "")
{
ANStateMachine *onode = new ANStateMachine;
onode->output_count = 0;
onode->type =
AnimationNode::ANIMATION_NODE_STATEMACHINE;
onode->anchor = anchor;
animation_nodes.push_back(onode);
parent->outputs[parent->output_count++] = onode;
parent_stack.push_back(parent);
parent = onode;
return this;
}
AnimationSystemBuilder *state(const std::string &name)
{
ANState *onode = new ANState;
onode->output_count = 0;
onode->type = AnimationNode::ANIMATION_NODE_STATE;
onode->anchor = "";
onode->name = name;
animation_nodes.push_back(onode);
parent->outputs[parent->output_count++] = onode;
parent_stack.push_back(parent);
parent = onode;
return this;
}
AnimationSystemBuilder *
transition(const std::string &from, const std::string &to,
bool at_end, const std::string &anchor = "")
{
ANStateMachineTransition *onode =
new ANStateMachineTransition;
onode->output_count = 0;
onode->type = AnimationNode::ANIMATION_NODE_TRANSITION;
onode->anchor = anchor;
onode->from = from;
onode->to = to;
onode->at_end = at_end;
animation_nodes.push_back(onode);
parent->outputs[parent->output_count++] = onode;
parent_stack.push_back(parent);
parent = onode;
return this;
}
void build()
{
}
};
AnimationSystemBuilder m_builder;
std::map<AnimationSystem::AnimationSystemBuilder::AnimationNode *,
AnimationDataNode>
mruntimeData;
AnimationSystemBuilder *builder()
{
m_builder.animation_nodes.reserve(8);
m_builder.parent = nullptr;
return &m_builder;
}
void update(float delta)
{
std::deque<AnimationSystemBuilder::AnimationNode *> queue;
root_index = -1;
if (mruntimeData.size() == 0) {
int i;
for (i = 0; i < m_builder.animation_nodes.size(); i++) {
AnimationDataNode data;
data.weight = 0;
mruntimeData[m_builder.animation_nodes[i]] =
data;
std::cout << "type: "
<< m_builder.animation_nodes[i]->type
<< std::endl;
}
for (i = 0; i < m_builder.animation_nodes.size(); i++) {
if (m_builder.animation_nodes[i]->type ==
AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_OUTPUT) {
root_index = i;
break;
}
}
}
std::cout << "root index: " << root_index << " "
<< m_builder.animation_nodes.size() << std::endl;
queue.push_back(m_builder.animation_nodes[root_index]);
int i;
while (queue.size() > 0) {
AnimationSystemBuilder::AnimationNode *item =
queue.front();
queue.pop_front();
switch (item->type) {
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_OUTPUT:
for (i = 0; i < item->output_count; i++) {
AnimationSystemBuilder::AnimationNode
*out = item->outputs[i];
mruntimeData[out].weight = 1.0f;
}
break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_BLEND2: {
float weight =
static_cast<AnimationSystemBuilder::
ANBlend2 *>(item)
->weight;
for (i = 0; i < item->output_count; i++) {
AnimationSystemBuilder::AnimationNode
*out = item->outputs[i];
if (i == 0)
mruntimeData[out].weight =
weight;
else if (i == 0)
mruntimeData[out].weight =
(1.0f - weight);
else
mruntimeData[out].weight = 0.0f;
}
} break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_ANIMATION: {
const std::string &animation =
static_cast<AnimationSystemBuilder::
ANAnimation *>(item)
->animation;
std::cout << "animation: " << animation << " "
<< mruntimeData[item].weight
<< std::endl;
} break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_STATEMACHINE:
break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_STATE:
break;
}
for (i = 0; i < item->output_count; i++) {
queue.push_back(item->outputs[i]);
}
}
#if 0
while (breadth) {
int i;
switch (breadth->type) {
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_OUTPUT:
for (i = 0; i < breadth->output_count; i++) {
AnimationSystemBuilder::AnimationNode
*out = breadth->outputs[i];
mruntimeData[out].weight = 1.0f;
}
break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_BLEND2: {
float weight =
static_cast<AnimationSystemBuilder::
ANBlend2 *>(breadth)
->weight;
for (i = 0; i < breadth->output_count; i++) {
AnimationSystemBuilder::AnimationNode
*out = breadth->outputs[i];
if (i == 0)
mruntimeData[out].weight =
weight;
else if (i == 0)
mruntimeData[out].weight =
(1.0f - weight);
else
mruntimeData[out].weight = 0.0f;
}
} break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_ANIMATION:
break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_STATEMACHINE:
break;
case AnimationSystemBuilder::AnimationNode::
ANIMATION_NODE_STATE:
break;
}
visited.insert(breadth);
AnimationSystemBuilder::AnimationNode *next = nullptr;
while (!next && parents.size() > 0) {
for (i = 0; i < breadth->output_count; i++) {
if (visited.find(breadth->outputs[i]) ==
visited.end()) {
next = breadth->outputs[i];
break;
}
}
if (next)
break;
else {
breadth = parents.back();
parents.pop_back();
}
}
if (next) {
parents.push_back(breadth);
breadth = next;
}
}
#endif
OgreAssert(false, "update");
}
};
struct AnimationTree {
AnimationSystem *manimationSystem;
};
#endif
CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
{
ecs.module<CharacterAnimationModule>();
ecs.component<AnimationControl>();
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[] = { "idle", "walking",
"running",
"treading_water",
"swimming" };
int state_count = sizeof(animNames) /
sizeof(animNames[0]);
anim.mAnimationSystem = new AnimationSystem();
for (i = 0; i < state_count; i++) {
Animation *animation = new Animation(
ch.mBodyEnt->getAnimationState(
animNames[i]),
ch.mSkeleton->getAnimation(
animNames[i])
);
animation->setLoop(true);
anim.mAnimationSystem->add_animation(
animNames[i], animation);
}
anim.mAnimationSystem
->builder()
/* clang-format off */
->output()
->state_machine(ANIM_FADE_SPEED, "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();
/* clang-format on */
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"state")
->setAnimation("idle");
anim.configured = true;
}
});
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;
anim.mAnimationSystem->addTime(delta);
// fadeAnimations(anim, delta);
if (!ch.mRootBone)
return;
ch.mBoneMotion += ch.mRootBone->getPosition();
});
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, 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.mAnimationSystem
->get<AnimationNodeStateMachine>("state")
->getCurrentState() == "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, 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;
if (input.fast)
std::cout << "fast!" << std::endl;
AnimationNodeStateMachine *state_machine =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"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;
if (controls_idle && anim_is_idle) {
if (current_state != "treading_water" &&
ch.is_submerged)
state_machine->setAnimation(
"treading_water");
else if (current_state != "idle" &&
!ch.is_submerged)
state_machine->setAnimation("idle");
} else if (start_motion) {
if (ch.is_submerged) {
if (input.fast)
state_machine->setAnimation(
"swimming-fast", true);
else
state_machine->setAnimation(
"swimming", true);
} else {
if (input.fast)
state_machine->setAnimation(
"running", true);
else
state_machine->setAnimation(
"walking", 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) {
state_machine->setAnimation(
"swimming-fast");
} else if (!input.fast &&
!anim_is_swimming_slow) {
state_machine->setAnimation(
"swimming");
}
} else {
if (input.fast && !anim_is_running)
state_machine->setAnimation(
"running");
else if (!input.fast &&
!anim_is_walking)
state_machine->setAnimation(
"walking");
}
}
});
}
}