Improved movement animaitons

Added a SetBlendWeight event that sets the weight for all nodes with a name.
This commit is contained in:
viktorljung
2016-03-08 11:21:40 +01:00
parent c1f5cf65a7
commit 2ba245368b
13 changed files with 331 additions and 81 deletions
@@ -7,6 +7,7 @@
#include "../Game/Events/EDashAbility.h"
#include "InputHandler.h"
#include "Rendering/EAutoAnimationBlend.h"
#include "Rendering/ESetBlendWeight.h"
template <typename EventContext>
class FirstPersonInputController : public InputController<EventContext>
@@ -45,7 +46,6 @@ protected:
bool m_Crouching = false;
//assault dash membervariables - needed to calculate the doubletap- and dashlogic
double m_AssaultDashDoubleTapDeltaTime = 0.0;
double m_DashEffectResetTimer = 0.0;
//i will let m_AssaultDashDoubleTapSensitivityTimer stay hardcoded, its not really a gamevariable (more an inputvariable),
//and its very unlikely that someone wants to change that value
const float m_AssaultDashDoubleTapSensitivityTimer = 0.25f;
@@ -137,6 +137,7 @@ bool FirstPersonInputController<EventContext>::OnCommand(const Events::InputComm
}
aeb.RootNode = playerModel;
aeb.Start = true;
aeb.SingleLevelBlend = true;
m_EventBroker->Publish(aeb);
} else if (val < 0) {
Events::AutoAnimationBlend aeb;
@@ -148,6 +149,7 @@ bool FirstPersonInputController<EventContext>::OnCommand(const Events::InputComm
}
aeb.RootNode = playerModel;
aeb.Start = true;
aeb.SingleLevelBlend = true;
aeb.Reverse = true;
m_EventBroker->Publish(aeb);
} else {
@@ -169,6 +171,7 @@ bool FirstPersonInputController<EventContext>::OnCommand(const Events::InputComm
aeb.Duration = 0.1;
aeb.NodeName = "Right";
aeb.RootNode = playerModel;
aeb.SingleLevelBlend = true;
aeb.Start = true;
m_EventBroker->Publish(aeb);
} else if (val < 0) {
@@ -176,6 +179,7 @@ bool FirstPersonInputController<EventContext>::OnCommand(const Events::InputComm
aeb.Duration = 0.1;
aeb.NodeName = "Left";
aeb.RootNode = playerModel;
aeb.SingleLevelBlend = true;
aeb.Start = true;
m_EventBroker->Publish(aeb);
} else {
@@ -186,16 +190,28 @@ bool FirstPersonInputController<EventContext>::OnCommand(const Events::InputComm
}
}
if (glm::length2(m_Movement) > 0) {
m_Movement = glm::normalize(m_Movement);
if (glm::length2(m_Movement) < 0.1f) {
if (m_PlayerEntity.Valid()) {
EntityWrapper playerModel = m_PlayerEntity.FirstChildByName("PlayerModel");
if (playerModel.Valid()) {
Events::AutoAnimationBlend aeb;
aeb.Duration = 0.2;
aeb.NodeName = "Idle";
aeb.RootNode = playerModel;
m_EventBroker->Publish(aeb);
}
}
} else {
if (m_PlayerEntity.Valid()) {
EntityWrapper playerModel = m_PlayerEntity.FirstChildByName("PlayerModel");
if (playerModel.Valid()) {
Events::AutoAnimationBlend aeb;
aeb.Duration = 0.1;
aeb.NodeName = "Idle";
aeb.Duration = 0.2;
aeb.NodeName = "MovementBlend";
aeb.RootNode = playerModel;
m_EventBroker->Publish(aeb);
}
@@ -203,6 +219,29 @@ bool FirstPersonInputController<EventContext>::OnCommand(const Events::InputComm
}
}
if (glm::length2(m_Movement) > 0) {
m_Movement = glm::normalize(m_Movement);
if (m_PlayerEntity.Valid()) {
EntityWrapper playerModel = m_PlayerEntity.FirstChildByName("PlayerModel");
if (playerModel.Valid()) {
glm::vec2 direction = glm::normalize(glm::vec2(m_Movement.x, m_Movement.z));
double weight = glm::abs(glm::dot(glm::vec2(1, 0), direction));
LOG_INFO("Weight %f", weight);
Events::SetBlendWeight sbw;
sbw.NodeName = "MovementBlend";
sbw.Weight = weight;
sbw.RootNode = playerModel;
m_EventBroker->Publish(sbw);
}
}
}
if (e.Command == "Forward" || e.Command == "Right") {
if (e.Value != 0) {
m_CurrentDirectionVector = e.Command == "Right" ? (e.Value > 0 ? "Right" : "Left") : (e.Value > 0 ? "Forward" : "Backward");
@@ -288,17 +327,10 @@ bool FirstPersonInputController<EventContext>::OnLockMouse(const Events::LockMou
template <typename EventContext>
void FirstPersonInputController<EventContext>::AssaultDashCheck(double dt, bool isJumping, double assaultDashCoolDownMaxTimer, double& assaultDashCoolDownTimer, EntityID playerID) {
m_AssaultDashDoubleTapDeltaTime += dt;
m_DashEffectResetTimer += dt;
assaultDashCoolDownTimer -= dt;
//cooldown = assaultDashCoolDownMaxTimer sec, pretend the dash lasts 0.25 sec (for friction to do its work)
if (assaultDashCoolDownTimer > (assaultDashCoolDownMaxTimer - 0.25f)) {
m_PlayerIsDashing = true;
if (m_DashEffectResetTimer > 0.05) {
Events::DashAbility e;
e.Player = playerID;
m_EventBroker->Publish(e);
m_DashEffectResetTimer = 0.0;
}
} else {
m_PlayerIsDashing = false;
}
@@ -310,6 +342,7 @@ void FirstPersonInputController<EventContext>::AssaultDashCheck(double dt, bool
assaultDashCoolDownTimer = assaultDashCoolDownMaxTimer;
m_AssaultDashDoubleTapped = true;
m_AssaultDashDoubleTapDeltaTime = 0.f;
Events::DashAbility e;
e.Player = playerID;
m_EventBroker->Publish(e);
@@ -13,6 +13,8 @@
#include "../Core/EntityWrapper.h"
#include "Rendering/AutoBlendQueue.h"
#include "../Input/EInputCommand.h"
#include "../Core/EEntityDeleted.h"
#include "Rendering/ESetBlendWeight.h"
#include "imgui/imgui.h"
class AnimationSystem : public ImpureSystem
@@ -28,6 +30,13 @@ private:
EventRelay<AnimationSystem, Events::AutoAnimationBlend> m_EAutoAnimationBlend;
bool OnAutoAnimationBlend(Events::AutoAnimationBlend& e);
EventRelay<AnimationSystem, Events::EntityDeleted> m_EEntityDeleted;
bool OnEntityDeleted(Events::EntityDeleted& e);
EventRelay<AnimationSystem, Events::SetBlendWeight> m_ESetBlendWeight;
bool OnSetBlendWeight(Events::SetBlendWeight& e);
std::unordered_map<EntityWrapper, AutoBlendQueue> m_AutoBlendQueues;
};
@@ -37,6 +37,8 @@ public:
std::shared_ptr<BlendTree> GetBlendTree();
AutoBlendQueue::AutoBlendJob& GetActiveBlendJob();
bool Empty() { return m_BlendQueue.empty(); }
private:
std::list<AutoblendNode> m_BlendQueue;
+6 -3
View File
@@ -75,7 +75,6 @@ public:
std::vector<glm::mat4> GetFinalPose() { return m_FinalPose; }
glm::mat4 GetBoneTransform(int boneID);
bool IsValid() { return (m_Root == nullptr ? false : true); }
void PrintTree();
BlendTree::AutoBlendInfo AutoBlendStep(AutoBlendInfo blendInfo);
@@ -84,16 +83,20 @@ public:
EntityWrapper GetSubTreeRoot(std::string nodeName);
std::vector<EntityWrapper> GetSingleLevelRoots(std::string name);
std::vector<EntityWrapper> GetEntitesByName(std::string name);
void SetWeightByName(std::string name, double weight);
private:
Skeleton* m_Skeleton = nullptr;
Node* m_Root = nullptr;
std::vector<glm::mat4> m_FinalPose;
std::map<int, glm::mat4> m_FinalBoneTransforms;
std::vector<BlendTree::Node*> FindNodesByName(std::string name);
std::vector<glm::mat4> AccumulateFinalPose();
BlendTree::Node* FillTreeByName(Node* parentNode, std::string name, EntityWrapper parentEntity);
std::vector<BlendTree::Node*> FindNodesByName(std::string name);
void Blend(std::map<int, Skeleton::PoseData>& pose);
};
@@ -18,7 +18,6 @@ struct AutoAnimationBlend : Event
bool Reverse = false;
bool Restart = false;
bool SingleLevelBlend = false;
double Weight = -1.0;
EntityWrapper AnimationEntity = EntityWrapper::Invalid;
@@ -0,0 +1,20 @@
#ifndef Events_SetBlendWeight_h__
#define Events_SetBlendWeight_h__
#include "../Core/EventBroker.h"
#include "../Core/EntityWrapper.h"
namespace Events
{
//Sets the blend weight for all nodes with "NodeName"
struct SetBlendWeight : Event
{
EntityWrapper RootNode = EntityWrapper::Invalid;
std::string NodeName;
double Weight;
};
}
#endif
+3 -8
View File
@@ -2,17 +2,12 @@
<Entity name="DashEffect" xmlns:c="components" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Types/Entity.xsd">
<Components>
<c:Animation/>
<c:Lifetime>
<Lifetime>0.5</Lifetime>
</c:Lifetime>
<c:ExplosionEffect>
<Velocity X="0" Y="0" Z="0"/>
<TimeSinceDeath>0</TimeSinceDeath>
<ExplosionDuration>0.5</ExplosionDuration>
<EndColor A="0" B="0" G="0" R="0"/>
</c:ExplosionEffect>
<c:Model/>
<c:Transform/>
</Components>
<Children/>
</Entity>
+7 -7
View File
@@ -875,7 +875,7 @@
<c:Animation>
<AnimationName>RunF</AnimationName>
<Time>0.34392228616198395</Time>
<Speed>1</Speed>
<Speed>1.5</Speed>
<Play>true</Play>
</c:Animation>
<c:Transform/>
@@ -899,7 +899,7 @@
<c:Animation>
<AnimationName>StrafeRightF</AnimationName>
<Time>0.27701693305173336</Time>
<Speed>1</Speed>
<Speed>2</Speed>
<Play>true</Play>
</c:Animation>
<c:Transform/>
@@ -911,7 +911,7 @@
<c:Animation>
<AnimationName>StrafeLeftF</AnimationName>
<Time>0.25350140317161873</Time>
<Speed>1</Speed>
<Speed>2</Speed>
<Play>true</Play>
</c:Animation>
<c:Transform/>
@@ -986,7 +986,7 @@
<c:Animation>
<AnimationName>DashForwardF</AnimationName>
<Time>1</Time>
<Speed>2</Speed>
<Speed>3</Speed>
<Loop>false</Loop>
</c:Animation>
<c:Transform/>
@@ -998,7 +998,7 @@
<c:Animation>
<AnimationName>DashBackwardF</AnimationName>
<Time>1</Time>
<Speed>2</Speed>
<Speed>3</Speed>
<Loop>false</Loop>
</c:Animation>
<c:Transform/>
@@ -1022,7 +1022,7 @@
<c:Animation>
<AnimationName>DashLeftF</AnimationName>
<Time>1</Time>
<Speed>2</Speed>
<Speed>3</Speed>
<Loop>false</Loop>
</c:Animation>
<c:Transform/>
@@ -1034,7 +1034,7 @@
<c:Animation>
<AnimationName>DashRightF</AnimationName>
<Time>1</Time>
<Speed>2</Speed>
<Speed>3</Speed>
<Loop>false</Loop>
</c:Animation>
<c:Transform/>
+160 -37
View File
@@ -4,6 +4,8 @@ AnimationSystem::AnimationSystem(SystemParams params)
: System(params)
{
EVENT_SUBSCRIBE_MEMBER(m_EAutoAnimationBlend, &AnimationSystem::OnAutoAnimationBlend);
EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &AnimationSystem::OnEntityDeleted);
EVENT_SUBSCRIBE_MEMBER(m_ESetBlendWeight, &AnimationSystem::OnSetBlendWeight);
}
void AnimationSystem::Update(double dt)
@@ -122,16 +124,13 @@ void AnimationSystem::UpdateAnimations(double dt)
}
}
void AnimationSystem::UpdateWeights(double dt)
{
for (auto& autoBlendQueue : m_AutoBlendQueues) {
if(autoBlendQueue.second.HasActiveBlendJob()) {
AutoBlendQueue::AutoBlendJob& blendJob = autoBlendQueue.second.GetActiveBlendJob();
std::shared_ptr<BlendTree> blendTree = autoBlendQueue.second.GetBlendTree();
for (auto it = m_AutoBlendQueues.begin(); it != m_AutoBlendQueues.end(); ) {
if(it->second.HasActiveBlendJob()) {
AutoBlendQueue::AutoBlendJob& blendJob = it->second.GetActiveBlendJob();
std::shared_ptr<BlendTree> blendTree = it->second.GetBlendTree();
if (blendTree != nullptr) {
if (blendJob.Duration != 0.0) {
blendJob.BlendInfo.progress = glm::clamp(blendJob.CurrentTime / blendJob.Duration, 0.0, 1.0);
@@ -140,10 +139,17 @@ void AnimationSystem::UpdateWeights(double dt)
}
blendJob.BlendInfo = blendTree->AutoBlendStep(blendJob.BlendInfo);
} else {
it = m_AutoBlendQueues.erase(it);
}
it++;
} else {
if (it->second.Empty()) {
it = m_AutoBlendQueues.erase(it);
} else {
it++;
}
}
}
}
@@ -177,46 +183,163 @@ bool AnimationSystem::OnAutoAnimationBlend(Events::AutoAnimationBlend& e)
return false;
}
EntityWrapper subTreeRoot;
EntityWrapper subTreeRoot = blendTree->GetSubTreeRoot(e.NodeName);
if (e.SingleLevelBlend) {
subTreeRoot = blendTree->GetSubTreeRoot(e.NodeName);
if (!subTreeRoot.Valid()) {
return false;
}
if (!subTreeRoot.Valid()) {
return false;
}
AutoBlendQueue::AutoBlendJob abj;
abj.AnimationEntity = e.AnimationEntity;
abj.CurrentTime = 0.0;
abj.Delay = e.Delay;
abj.Duration = e.Duration;
abj.RootNode = e.RootNode;
AutoBlendQueue::AutoBlendJob abj;
abj.AnimationEntity = e.AnimationEntity;
abj.CurrentTime = 0.0;
abj.Delay = e.Delay;
abj.Duration = e.Duration;
abj.RootNode = e.RootNode;
abj.BlendInfo.NodeName = e.NodeName;
abj.BlendInfo.progress = 0.0;
abj.BlendInfo.Start = e.Start;
abj.BlendInfo.SingleBlend = e.SingleLevelBlend;
abj.BlendInfo.Weight = e.Weight;
abj.BlendInfo.NodeName = e.NodeName;
abj.BlendInfo.progress = 0.0;
abj.BlendInfo.Start = e.Start;
abj.BlendInfo.SingleBlend = e.SingleLevelBlend;
abj.BlendInfo.Weight = e.Weight;
EntityWrapper nodeEntity = subTreeRoot.FirstChildByName(e.NodeName); // more than one
if (nodeEntity.Valid()) {
if (nodeEntity.HasComponent("Animation")) {
const Skeleton::Animation* animation = skeleton->GetAnimation(nodeEntity["Animation"]["AnimationName"]);
(bool&)nodeEntity["Animation"]["Reverse"] = e.Reverse;
std::vector<EntityWrapper> animationEntities = blendTree->GetEntitesByName(e.NodeName); // more than one
for(auto entity : animationEntities)
if (entity.Valid()) {
if (entity.HasComponent("Animation")) {
const Skeleton::Animation* animation = skeleton->GetAnimation(entity["Animation"]["AnimationName"]);
(bool&)entity["Animation"]["Reverse"] = e.Reverse;
if (e.Restart) {
if (animation != nullptr) {
if (e.Restart) {
if (e.Reverse) {
(double&)nodeEntity["Animation"]["Time"] = animation->Duration;
} else {
(double&)nodeEntity["Animation"]["Time"] = 0.0;
if (e.Restart) {
if (animation != nullptr) {
if (e.Restart) {
if (e.Reverse) {
(double&)entity["Animation"]["Time"] = animation->Duration;
} else {
(double&)entity["Animation"]["Time"] = 0.0;
}
}
}
}
}
}
m_AutoBlendQueues[subTreeRoot].Insert(abj);
} else {
std::vector<EntityWrapper> subtreeroots = blendTree->GetSingleLevelRoots(e.NodeName);
for(auto entity : subtreeroots) {
subTreeRoot = entity;
if (!subTreeRoot.Valid()) {
return false;
}
AutoBlendQueue::AutoBlendJob abj;
abj.AnimationEntity = e.AnimationEntity;
abj.CurrentTime = 0.0;
abj.Delay = e.Delay;
abj.Duration = e.Duration;
abj.RootNode = e.RootNode;
abj.BlendInfo.NodeName = e.NodeName;
abj.BlendInfo.progress = 0.0;
abj.BlendInfo.Start = e.Start;
abj.BlendInfo.SingleBlend = e.SingleLevelBlend;
abj.BlendInfo.Weight = e.Weight;
m_AutoBlendQueues[subTreeRoot].Insert(abj);
}
std::vector<EntityWrapper> animationEntities = blendTree->GetEntitesByName(e.NodeName); // more than one
for (auto entity : animationEntities)
if (entity.Valid()) {
if (entity.HasComponent("Animation")) {
const Skeleton::Animation* animation = skeleton->GetAnimation(entity["Animation"]["AnimationName"]);
(bool&)entity["Animation"]["Reverse"] = e.Reverse;
if (e.Restart) {
if (animation != nullptr) {
if (e.Restart) {
if (e.Reverse) {
(double&)entity["Animation"]["Time"] = animation->Duration;
} else {
(double&)entity["Animation"]["Time"] = 0.0;
}
}
}
}
}
}
}
m_AutoBlendQueues[subTreeRoot].Insert(abj);
return true;
}
bool AnimationSystem::OnEntityDeleted(Events::EntityDeleted& e)
{
EntityWrapper entity = EntityWrapper(m_World, e.DeletedEntity);
if (entity.HasComponent("Model")) {
Model* model;
try {
model = ResourceManager::Load<::Model, true>(entity["Model"]["Resource"]);
} catch (const std::exception&) {
return false;
}
Skeleton* skeleton = model->m_RawModel->m_Skeleton;
if (skeleton == nullptr) {
return false;
}
if (skeleton->BlendTrees.find(entity) != skeleton->BlendTrees.end()) {
skeleton->BlendTrees.erase(entity);
}
}
}
bool AnimationSystem::OnSetBlendWeight(Events::SetBlendWeight& e)
{
if (!e.RootNode.Valid()) {
return false;
}
if (!e.RootNode.HasComponent("Model")) {
return false;
}
Model* model;
try {
model = ResourceManager::Load<::Model, true>((std::string)e.RootNode["Model"]["Resource"]);
} catch (const std::exception&) {
return false;
}
Skeleton* skeleton = model->m_RawModel->m_Skeleton;
if (skeleton == nullptr) {
return false;
}
std::shared_ptr<BlendTree> blendTree;
if (skeleton->BlendTrees.find(e.RootNode) != skeleton->BlendTrees.end()) {
blendTree = skeleton->BlendTrees.at(e.RootNode);
} else {
return false;
}
if(e.Weight >= 0 && e.Weight <= 1) {
blendTree->SetWeightByName(e.NodeName, e.Weight);
return true;
} else {
return false;
}
}
+1 -2
View File
@@ -75,7 +75,6 @@ void AutoBlendQueue::Insert(AutoBlendJob autoBlendJob)
}
}
m_BlendQueue.clear();
m_BlendQueue.push_back(blendNode);
}
@@ -124,7 +123,7 @@ bool AutoBlendQueue::HasActiveBlendJob()
try {
model = ResourceManager::Load<::Model, true>(blendJob.RootNode["Model"]["Resource"]);
} catch (const std::exception&) {
m_BlendQueue.pop_front();
//m_BlendQueue.pop_front();
return HasActiveBlendJob();
}
+53 -4
View File
@@ -2,11 +2,8 @@
BlendTree::BlendTree(EntityWrapper ModelEntity, Skeleton* skeleton)
{
m_Skeleton = skeleton;
if (ModelEntity.HasComponent("Animation")) {
const Skeleton::Animation* animation = skeleton->GetAnimation(ModelEntity["Animation"]["AnimationName"]);
if (animation == nullptr) {
@@ -270,7 +267,7 @@ BlendTree::AutoBlendInfo BlendTree::AutoBlendStep(AutoBlendInfo blendInfo)
currentNode = currentNode->Parent;
if (blendInfo.SingleBlend) {
break;
return blendInfo;;
}
}
} else if(goalNodes.size() >= 2) {
@@ -331,6 +328,9 @@ BlendTree::AutoBlendInfo BlendTree::AutoBlendStep(AutoBlendInfo blendInfo)
lastNode = currentNode;
currentNode = currentNode->Parent;
if (blendInfo.SingleBlend) {
return blendInfo;
}
}
}
@@ -427,6 +427,55 @@ EntityWrapper BlendTree::GetSubTreeRoot(std::string nodeName)
return subTreeRoots.front()->Entity;
}
std::vector<EntityWrapper> BlendTree::GetSingleLevelRoots(std::string name)
{
std::vector<Node*> nodes = FindNodesByName(name);
std::vector<EntityWrapper> entities;
for (auto it = nodes.begin(); it != nodes.end(); it++) {
Node* currentNode = (*it)->Parent;
if(currentNode->Entity.Valid()) {
entities.push_back(currentNode->Entity);
}
}
return entities;
}
std::vector<EntityWrapper> BlendTree::GetEntitesByName(std::string name)
{
std::vector<Node*> nodes = FindNodesByName(name);
std::vector<EntityWrapper> entities;
for (auto it = nodes.begin(); it != nodes.end(); it++) {
Node* currentNode = (*it);
if (currentNode->Entity.Valid()) {
entities.push_back(currentNode->Entity);
}
}
return entities;
}
void BlendTree::SetWeightByName(std::string name, double weight)
{
std::vector<Node*> nodes = FindNodesByName(name);
for (auto node : nodes) {
EntityWrapper entity = node->Entity;
if(entity.HasComponent("Blend")) {
entity["Blend"]["Weight"] = weight;
node->Weight = weight;
}
}
}
void BlendTree::Blend(std::map<int, Skeleton::PoseData>& pose)
{
Node* currentNode;
+2
View File
@@ -321,6 +321,8 @@ Skeleton::~Skeleton()
for (auto &kv : Bones) {
delete kv.second;
}
BlendTrees.clear();
}
const Skeleton::Animation* Skeleton::GetAnimation(std::string name)
+23 -7
View File
@@ -443,18 +443,34 @@ bool PlayerMovementSystem::OnDashAbility(Events::DashAbility & e)
}
}
}
/* EntityWrapper dashEffectModel;
dashEffectModel = playerModel.Clone(dashEffect);
player["Transform"].Copy(dashEffectModel["Transform"]);
dashEffectModel.AttachComponent("ExplosionEffect");
dashEffectModel["ExplosionEffect"]["EndColor"] = (glm::vec4)playerModel["Model"]["Color"];
((glm::vec4&)dashEffectModel["ExplosionEffect"]["EndColor"]).w = 0.f;
(double&)dashEffectModel["ExplosionEffect"]["ExplosionDuration"] = dashEffect["Lifetime"]["Lifetime"];
(glm::vec3&)dashEffectModel["ExplosionEffect"]["ExplosionOrigin"] = glm::vec3(0, 1, 0) + (0.2f * controller->Movement());
auto animationChildren = dashEffectModel.ChildrenWithComponent("Animation");
for (auto animationEntity : animationChildren) {
(bool&)animationEntity["Animation"]["Play"] = false;
}*/
}
}
}
/*
auto playerEntityAnimation = playerModel["Animation"];
playerEntityModel.Copy(dashEffect["Model"]);
playerEntityAnimation.Copy(dashEffect["Animation"]);
dashEffect["ExplosionEffect"]["EndColor"] = (glm::vec4)playerEntityModel["Color"];
((glm::vec4&)dashEffect["ExplosionEffect"]["EndColor"]).w = 0.f;
*/