Animation tree implemented
This commit is contained in:
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
@@ -13,6 +13,19 @@
|
||||
"string_view": "cpp",
|
||||
"atomic": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"stop_token": "cpp"
|
||||
"stop_token": "cpp",
|
||||
"random": "cpp",
|
||||
"future": "cpp",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"span": "cpp",
|
||||
"chrono": "cpp",
|
||||
"format": "cpp"
|
||||
}
|
||||
}
|
||||
BIN
assets/blender/vehicles/boat.blend
(Stored with Git LFS)
BIN
assets/blender/vehicles/boat.blend
(Stored with Git LFS)
Binary file not shown.
@@ -230,7 +230,6 @@ function StartGameQuest()
|
||||
local mc_is_free = function()
|
||||
this.boat_id = ecs_vehicle_set("boat", 0, 0, -20, 1.75)
|
||||
this.boat2_id = ecs_vehicle_set("boat", -10, 0, -20, 1.55)
|
||||
this.trigger_id = ecs_child_character_trigger(this.boat_id, "entered_boat", 0, 0, 0, 3, 3)
|
||||
this.npc_id = ecs_npc_set("normal-female.glb", 0, 2, -20, 1.75)
|
||||
this.boat = true
|
||||
-- ecs_set_slot(this.boat_id, this.npc_id, "captain_seat")
|
||||
|
||||
@@ -58,8 +58,7 @@ BoatModule::BoatModule(flecs::world &ecs)
|
||||
Ogre::ResourceGroupManager::
|
||||
AUTODETECT_RESOURCE_GROUP_NAME);
|
||||
SceneLoader loader;
|
||||
loader.load(scene, "General", attachment,
|
||||
(int)e.raw_id());
|
||||
loader.load(scene, "General", attachment, e);
|
||||
// attachment->loadChildren(type.resourceName);
|
||||
std::vector<Ogre::Node *> v =
|
||||
attachment->getChildren();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
project(gamedata)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
|
||||
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp
|
||||
BoatModule.cpp EventTriggerModule.cpp)
|
||||
BoatModule.cpp EventTriggerModule.cpp CharacterAnimationModule.cpp)
|
||||
target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet OgrePaging OgreTerrain OgreOverlay flecs::flecs_static lua sceneloader)
|
||||
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
483
src/gamedata/CharacterAnimationModule.h
Normal file
483
src/gamedata/CharacterAnimationModule.h
Normal file
@@ -0,0 +1,483 @@
|
||||
#ifndef CHARACTER_ANIMATION_MODULE_H_
|
||||
#define CHARACTER_ANIMATION_MODULE_H_
|
||||
#include <Ogre.h>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct Animation {
|
||||
Ogre::AnimationState *mAnimationState;
|
||||
Ogre::Animation *mSkelAnimation;
|
||||
Ogre::NodeAnimationTrack *mHipsTrack;
|
||||
Ogre::NodeAnimationTrack *mRootTrack;
|
||||
float m_weight;
|
||||
float m_accWeight;
|
||||
Animation(Ogre::AnimationState *animState,
|
||||
Ogre::Animation *skelAnimation)
|
||||
: mAnimationState(animState)
|
||||
, mSkelAnimation(skelAnimation)
|
||||
, m_weight(0)
|
||||
, m_accWeight(0)
|
||||
{
|
||||
int j;
|
||||
for (const auto &it : mSkelAnimation->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack *track = it.second;
|
||||
Ogre::String trackName =
|
||||
track->getAssociatedNode()->getName();
|
||||
if (trackName == "mixamorig:Hips") {
|
||||
mHipsTrack = track;
|
||||
} else if (trackName == "Root") {
|
||||
mRootTrack = track;
|
||||
// mRootTracks[i]->removeAllKeyFrames();
|
||||
}
|
||||
}
|
||||
OgreAssert(mHipsTrack, "no hips track");
|
||||
OgreAssert(mRootTrack, "no Root track");
|
||||
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
|
||||
Ogre::Vector3 motion = Ogre::Vector3::ZERO;
|
||||
for (j = 0; j < mRootTrack->getNumKeyFrames(); j++) {
|
||||
Ogre::Vector3 trans =
|
||||
mRootTrack->getNodeKeyFrame(j)->getTranslate();
|
||||
if (j == 0)
|
||||
delta = trans;
|
||||
else
|
||||
delta = trans - motion;
|
||||
mRootTrack->getNodeKeyFrame(j)->setTranslate(delta);
|
||||
motion = trans;
|
||||
}
|
||||
}
|
||||
Ogre::String getName()
|
||||
{
|
||||
return mAnimationState->getAnimationName();
|
||||
}
|
||||
void setLoop(bool loop)
|
||||
{
|
||||
mAnimationState->setLoop(loop);
|
||||
}
|
||||
bool getLoop() const
|
||||
{
|
||||
return mAnimationState->getLoop();
|
||||
}
|
||||
void setEnabled(bool enabled)
|
||||
{
|
||||
mAnimationState->setEnabled(enabled);
|
||||
}
|
||||
bool getEnabled() const
|
||||
{
|
||||
return mAnimationState->getEnabled();
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
bool enabled = weight > 0.001f;
|
||||
setEnabled(enabled);
|
||||
mAnimationState->setWeight(weight);
|
||||
m_weight = weight;
|
||||
}
|
||||
float getWeight() const
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
void addTime(float time)
|
||||
{
|
||||
mAnimationState->addTime(time);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
mAnimationState->setTimePosition(0);
|
||||
}
|
||||
void resetAccWeight()
|
||||
{
|
||||
m_accWeight = 0;
|
||||
}
|
||||
void increaseAccWeight(float weight)
|
||||
{
|
||||
m_accWeight += weight;
|
||||
}
|
||||
float getAccWeight() const
|
||||
{
|
||||
return m_accWeight;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationNode {
|
||||
std::vector<AnimationNode *> children;
|
||||
float m_weight;
|
||||
Ogre::String m_name;
|
||||
AnimationNode()
|
||||
: m_weight(0)
|
||||
{
|
||||
}
|
||||
virtual void addTime(float time) = 0;
|
||||
virtual void setWeight(float weight) = 0;
|
||||
virtual void reset() = 0;
|
||||
float getWeight()
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
const Ogre::String &getName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
void setName(const Ogre::String &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationNodeAnimation : AnimationNode {
|
||||
Animation *mAnimation;
|
||||
bool enabled;
|
||||
AnimationNodeAnimation(Animation *animation)
|
||||
: AnimationNode()
|
||||
, mAnimation(animation)
|
||||
{
|
||||
}
|
||||
void addTime(float time)
|
||||
{
|
||||
mAnimation->addTime(time);
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
enabled = weight > 0.001f;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
mAnimation->reset();
|
||||
}
|
||||
};
|
||||
struct AnimationNodeStateMachineState : AnimationNode {
|
||||
AnimationNodeStateMachineState()
|
||||
: AnimationNode()
|
||||
{
|
||||
}
|
||||
void addTime(float time)
|
||||
{
|
||||
children[0]->addTime(time);
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
};
|
||||
struct AnimationNodeSpeed : AnimationNode {
|
||||
float m_speed;
|
||||
bool enabled;
|
||||
AnimationNodeSpeed(float speed)
|
||||
: AnimationNode()
|
||||
, m_speed(speed)
|
||||
, enabled(false)
|
||||
{
|
||||
}
|
||||
void addTime(float time)
|
||||
{
|
||||
children[0]->addTime(time * m_speed);
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
};
|
||||
struct AnimationNodeStateMachine : AnimationNode {
|
||||
std::map<Ogre::String, AnimationNode *> stateMap;
|
||||
std::set<AnimationNode *> fade_in, fade_out;
|
||||
AnimationNode *currentAnim, *nextAnim;
|
||||
float fade_speed;
|
||||
Ogre::String mCurrentStateName;
|
||||
bool configured;
|
||||
AnimationNodeStateMachine(float fade_speed)
|
||||
: AnimationNode()
|
||||
, currentAnim(nullptr)
|
||||
, nextAnim(nullptr)
|
||||
, fade_speed(fade_speed)
|
||||
, mCurrentStateName("")
|
||||
, configured(false)
|
||||
{
|
||||
m_weight = 1.0f;
|
||||
}
|
||||
void addTime(float time)
|
||||
{
|
||||
int i;
|
||||
if (!configured) {
|
||||
configure();
|
||||
configured = true;
|
||||
}
|
||||
for (i = 0; i < children.size(); i++) {
|
||||
AnimationNode *child = children[i];
|
||||
if (fade_in.find(child) != fade_in.end()) {
|
||||
Ogre::Real newWeight =
|
||||
child->getWeight() + time * fade_speed;
|
||||
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
|
||||
newWeight * m_weight, 0, m_weight));
|
||||
if (newWeight >= m_weight)
|
||||
fade_in.erase(child);
|
||||
}
|
||||
if (fade_out.find(child) != fade_out.end()) {
|
||||
Ogre::Real newWeight =
|
||||
child->getWeight() - time * fade_speed;
|
||||
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
|
||||
newWeight * m_weight, 0, m_weight));
|
||||
if (newWeight <= 0)
|
||||
fade_out.erase(child);
|
||||
}
|
||||
}
|
||||
OgreAssert(currentAnim, "bad current anim");
|
||||
if (currentAnim)
|
||||
currentAnim->addTime(time);
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
/* do not update child state yet */
|
||||
}
|
||||
void addState(AnimationNode *state)
|
||||
{
|
||||
const Ogre::String &name = state->getName();
|
||||
stateMap[name] = state;
|
||||
state->setWeight(0);
|
||||
fade_in.erase(state);
|
||||
fade_out.erase(state);
|
||||
std::cout << "registered state: " << name << std::endl;
|
||||
}
|
||||
void configure()
|
||||
{
|
||||
int i;
|
||||
std::cout << "children: " << children.size() << std::endl;
|
||||
for (i = 0; i < children.size(); i++)
|
||||
addState(children[i]);
|
||||
std::cout << "configure called" << std::endl;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < children.size(); i++) {
|
||||
children[i]->reset();
|
||||
}
|
||||
}
|
||||
void setAnimation(const Ogre::String &anim_state, bool reset = false)
|
||||
{
|
||||
if (!configured) {
|
||||
configure();
|
||||
configured = true;
|
||||
}
|
||||
OgreAssert(stateMap.find(anim_state) != stateMap.end(),
|
||||
"Bad animation state: " + anim_state);
|
||||
std::cout << "STATE: " << anim_state << std::endl;
|
||||
nextAnim = stateMap[anim_state];
|
||||
if (nextAnim == currentAnim)
|
||||
return;
|
||||
if (currentAnim != nullptr) {
|
||||
fade_out.insert(currentAnim);
|
||||
fade_in.erase(currentAnim);
|
||||
}
|
||||
fade_out.erase(nextAnim);
|
||||
fade_in.insert(nextAnim);
|
||||
nextAnim->setWeight(0);
|
||||
if (reset)
|
||||
nextAnim->reset();
|
||||
currentAnim = nextAnim;
|
||||
mCurrentStateName = anim_state;
|
||||
}
|
||||
const Ogre::String &getCurrentState() const
|
||||
{
|
||||
return mCurrentStateName;
|
||||
}
|
||||
};
|
||||
|
||||
#define ANIM_FADE_SPEED \
|
||||
7.5f // animation crossfade speed in % of full weight per second
|
||||
|
||||
struct AnimationNodeOutput : AnimationNode {
|
||||
float m_weight;
|
||||
float m_speed;
|
||||
AnimationNodeOutput()
|
||||
: AnimationNode()
|
||||
, m_weight(1.0f)
|
||||
, m_speed(1.0f)
|
||||
{
|
||||
}
|
||||
void addTime(float time)
|
||||
{
|
||||
children[0]->addTime(time * m_speed);
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationSystem : AnimationNode {
|
||||
AnimationSystem()
|
||||
: m_builder(this)
|
||||
{
|
||||
}
|
||||
std::unordered_map<Ogre::String, Animation *> animation_list;
|
||||
std::vector<Animation *> vanimation_list;
|
||||
void add_animation(const Ogre::String &name, Animation *animation)
|
||||
{
|
||||
animation_list[name] = animation;
|
||||
vanimation_list.push_back(animation);
|
||||
}
|
||||
void clear_animations()
|
||||
{
|
||||
animation_list.clear();
|
||||
vanimation_list.clear();
|
||||
}
|
||||
struct AnimationSystemBuilder {
|
||||
AnimationSystem *mAnimationSystem;
|
||||
std::vector<AnimationNode *> animation_nodes;
|
||||
AnimationNode *parent;
|
||||
std::list<AnimationNode *> parent_stack;
|
||||
std::unordered_map<Ogre::String, AnimationNode *> nodeMap;
|
||||
std::vector<AnimationNodeAnimation *> animationNodeList;
|
||||
AnimationSystemBuilder(AnimationSystem *animationSystem)
|
||||
: mAnimationSystem(animationSystem)
|
||||
{
|
||||
}
|
||||
AnimationSystemBuilder *output()
|
||||
{
|
||||
AnimationNodeOutput *onode = new AnimationNodeOutput();
|
||||
animation_nodes.push_back(onode);
|
||||
parent = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *
|
||||
animation(const Ogre::String &animation_name)
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
Animation *animation =
|
||||
mAnimationSystem->animation_list[animation_name];
|
||||
AnimationNodeAnimation *onode =
|
||||
new AnimationNodeAnimation(animation);
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
animationNodeList.push_back(onode);
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *speed(float speed,
|
||||
const Ogre::String &anchor = "")
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeSpeed *onode =
|
||||
new AnimationNodeSpeed(speed);
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
if (anchor.length() > 0)
|
||||
nodeMap[anchor] = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *
|
||||
state_machine(float fade_time = ANIM_FADE_SPEED,
|
||||
const Ogre::String &anchor = "")
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeStateMachine *onode =
|
||||
new AnimationNodeStateMachine(fade_time);
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
if (anchor.length() > 0)
|
||||
nodeMap[anchor] = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *state(const Ogre::String &state_name)
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeStateMachineState *onode =
|
||||
new AnimationNodeStateMachineState;
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
onode->setName(state_name);
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *end()
|
||||
{
|
||||
parent = parent_stack.back();
|
||||
parent_stack.pop_back();
|
||||
return this;
|
||||
}
|
||||
};
|
||||
AnimationSystemBuilder m_builder;
|
||||
void addTime(float time)
|
||||
{
|
||||
int i;
|
||||
m_builder.animation_nodes[0]->addTime(time);
|
||||
for (i = 0; i < m_builder.animationNodeList.size(); i++) {
|
||||
AnimationNodeAnimation *anim =
|
||||
m_builder.animationNodeList[i];
|
||||
float weight = anim->getWeight();
|
||||
anim->mAnimation->increaseAccWeight(weight);
|
||||
std::cout << i << " weight: " << weight << std::endl;
|
||||
}
|
||||
for (i = 0; i < vanimation_list.size(); i++) {
|
||||
float weight = vanimation_list[i]->getAccWeight();
|
||||
vanimation_list[i]->setWeight(weight);
|
||||
vanimation_list[i]->resetAccWeight();
|
||||
std::cout << i << vanimation_list[i]->getName()
|
||||
<< " acc weight: " << weight << std::endl;
|
||||
}
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_builder.animation_nodes[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
m_builder.animation_nodes[0]->reset();
|
||||
}
|
||||
AnimationSystemBuilder *builder()
|
||||
{
|
||||
m_builder.animation_nodes.reserve(8);
|
||||
m_builder.parent = nullptr;
|
||||
return &m_builder;
|
||||
}
|
||||
template <class T> T *get(const Ogre::String &name)
|
||||
{
|
||||
return static_cast<T *>(m_builder.nodeMap[name]);
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationControl {
|
||||
// AnimID currentAnim;
|
||||
// AnimID nextAnim;
|
||||
// bool reset;
|
||||
bool configured;
|
||||
// Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list
|
||||
// Ogre::Animation *mSkelAnimations[NUM_ANIMS];
|
||||
// bool mFadingIn[NUM_ANIMS]; // which animations are fading in
|
||||
// bool mFadingOut[NUM_ANIMS]; // which animations are fading out
|
||||
// Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS];
|
||||
// Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS];
|
||||
// std::vector<AnimationNode *> mAnimations;
|
||||
// AnimationNodeStateMachine *mStateMachine;
|
||||
AnimationSystem *mAnimationSystem;
|
||||
};
|
||||
struct CharacterAnimationModule {
|
||||
CharacterAnimationModule(flecs::world &ecs);
|
||||
// void setAnimation(AnimationControl &anim);
|
||||
// void fadeAnimations(AnimationControl &anim, Ogre::Real deltaTime);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -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)
|
||||
|
||||
@@ -45,31 +45,8 @@ struct CharacterVelocity {
|
||||
Ogre::Vector3 gvelocity;
|
||||
Ogre::Vector3 velocity;
|
||||
};
|
||||
struct AnimationControl {
|
||||
enum AnimID {
|
||||
ANIM_IDLE = 0,
|
||||
ANIM_WALK,
|
||||
ANIM_RUN,
|
||||
ANIM_TREADING_WATER,
|
||||
ANIM_SWIMMING,
|
||||
NUM_ANIMS,
|
||||
ANIM_NONE = NUM_ANIMS
|
||||
};
|
||||
AnimID currentAnim;
|
||||
AnimID nextAnim;
|
||||
bool reset;
|
||||
bool configured;
|
||||
Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list
|
||||
Ogre::Animation *mSkelAnimations[NUM_ANIMS];
|
||||
bool mFadingIn[NUM_ANIMS]; // which animations are fading in
|
||||
bool mFadingOut[NUM_ANIMS]; // which animations are fading out
|
||||
Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS];
|
||||
Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS];
|
||||
};
|
||||
struct CharacterModule {
|
||||
CharacterModule(flecs::world &ecs);
|
||||
void setAnimation(AnimationControl &anim);
|
||||
void fadeAnimations(AnimationControl &anim, Ogre::Real deltaTime);
|
||||
void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
|
||||
Ogre::Real deltaPitch, Ogre::Real deltaZoom);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "WorldMapModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
@@ -47,6 +48,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
ecs.import <LuaModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
project(sceneloader)
|
||||
add_library(sceneloader loader.cpp)
|
||||
target_include_directories(sceneloader PUBLIC .)
|
||||
target_link_libraries(sceneloader PUBLIC OgreMain PRIVATE pugixml)
|
||||
target_link_libraries(sceneloader PUBLIC OgreMain PRIVATE pugixml GameData)
|
||||
target_link_libraries(sceneloader PUBLIC OgreTerrain)
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <OgreTerrain.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <pugixml.hpp>
|
||||
#include "GameData.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "loader.h"
|
||||
|
||||
using namespace Ogre;
|
||||
@@ -120,35 +122,6 @@ static ColourValue parseColour(pugi::xml_node &XMLNode)
|
||||
1);
|
||||
}
|
||||
|
||||
struct SceneCodec : public Codec {
|
||||
String magicNumberToFileExt(const char *magicNumberPtr,
|
||||
size_t maxbytes) const override
|
||||
{
|
||||
return "";
|
||||
}
|
||||
String getType() const override
|
||||
{
|
||||
return "scene";
|
||||
}
|
||||
void decode(const DataStreamPtr &stream,
|
||||
const Any &output) const override
|
||||
{
|
||||
DataStreamPtr _stream(stream);
|
||||
SceneLoader loader;
|
||||
loader.load(_stream,
|
||||
ResourceGroupManager::getSingleton()
|
||||
.getWorldResourceGroupName(),
|
||||
any_cast<SceneNode *>(output), 0);
|
||||
}
|
||||
|
||||
void encodeToFile(const Any &input,
|
||||
const String &outFileName) const override
|
||||
{
|
||||
SceneLoader loader;
|
||||
loader.exportScene(any_cast<SceneNode *>(input), outFileName);
|
||||
}
|
||||
};
|
||||
|
||||
SceneLoader::SceneLoader()
|
||||
: mSceneMgr(0)
|
||||
, mBackgroundColour(ColourValue::Black)
|
||||
@@ -160,12 +133,12 @@ SceneLoader::~SceneLoader()
|
||||
}
|
||||
|
||||
void SceneLoader::load(DataStreamPtr &stream, const String &groupName,
|
||||
SceneNode *rootNode, int counter)
|
||||
SceneNode *rootNode, flecs::entity e)
|
||||
{
|
||||
m_sGroupName = groupName;
|
||||
mSceneMgr = rootNode->getCreator();
|
||||
mNamePrefix = rootNode->getName();
|
||||
this->counter = counter;
|
||||
this->counter = (int)e.raw_id();
|
||||
if (counter >= 0) {
|
||||
if (mNamePrefix.length() > 0)
|
||||
mNamePrefix += "/";
|
||||
@@ -173,6 +146,7 @@ void SceneLoader::load(DataStreamPtr &stream, const String &groupName,
|
||||
}
|
||||
if (mNamePrefix.length() > 0)
|
||||
mNamePrefix += "/";
|
||||
base_e = e;
|
||||
|
||||
pugi::xml_document XMLDoc; // character type defaults to char
|
||||
|
||||
@@ -571,6 +545,23 @@ void SceneLoader::processNode(pugi::xml_node &XMLNode, SceneNode *pParent)
|
||||
// Process node animations (?)
|
||||
if (auto pElement = XMLNode.child("animations"))
|
||||
processNodeAnimations(pElement, pNode);
|
||||
Ogre::Any triggerAny =
|
||||
pNode->getUserObjectBindings().getUserAny("trigger");
|
||||
if (triggerAny.has_value()) {
|
||||
flecs::entity e = ECS::get().entity().child_of(base_e);
|
||||
Ogre::String event = Ogre::any_cast<Ogre::String>(triggerAny);
|
||||
float h = 2.0f, r = 3.0f;
|
||||
Ogre::Any anyh = pNode->getUserObjectBindings().getUserAny(
|
||||
"trigger_height");
|
||||
if (anyh.has_value())
|
||||
h = Ogre::any_cast<float>(anyh);
|
||||
Ogre::Any anyr = pNode->getUserObjectBindings().getUserAny(
|
||||
"trigger_radius");
|
||||
if (anyr.has_value())
|
||||
r = Ogre::any_cast<float>(anyr);
|
||||
e.set<ECS::EventTrigger>(
|
||||
{ pNode, Ogre::Vector3(0, 0, 0), h, r, event });
|
||||
}
|
||||
}
|
||||
|
||||
void SceneLoader::processLookTarget(pugi::xml_node &XMLNode, SceneNode *pParent)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <OgreString.h>
|
||||
#include <OgrePlugin.h>
|
||||
#include <OgreCodec.h>
|
||||
#include <flecs.h>
|
||||
|
||||
class SceneLoader {
|
||||
public:
|
||||
@@ -13,7 +14,7 @@ public:
|
||||
virtual ~SceneLoader();
|
||||
|
||||
void load(Ogre::DataStreamPtr &stream, const Ogre::String &groupName,
|
||||
Ogre::SceneNode *rootNode, int counter);
|
||||
Ogre::SceneNode *rootNode, flecs::entity e);
|
||||
|
||||
void exportScene(Ogre::SceneNode *rootNode,
|
||||
const Ogre::String &outFileName);
|
||||
@@ -74,6 +75,7 @@ protected:
|
||||
Ogre::ColourValue mBackgroundColour;
|
||||
Ogre::String mNamePrefix;
|
||||
int counter;
|
||||
flecs::entity base_e;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user