Animation tree implemented
This commit is contained in:
557
src/gamedata/CharacterAnimationModule.cpp
Normal file
557
src/gamedata/CharacterAnimationModule.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user