#include "Rendering/AnimationSystem.h" 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); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &AnimationSystem::OnInputCommand); } void AnimationSystem::Update(double dt) { UpdateAnimations(dt); CreateBlendTrees(); UpdateWeights(dt); EntityWrapper aimEntity = m_World->GetFirstEntityByName("ThirdPerson").FirstChildByName("Aim"); (Field)aimEntity["Animation"]["Time"] = glm::clamp((const double&)(Field)aimEntity["Animation"]["Time"] + ((aimMove * 0.2f) * dt), 0.0, 1.0); EntityWrapper aimTextEntity = m_World->GetFirstEntityByName("AimText"); if (aimMove > 0) { (Field)aimTextEntity["Text"]["Content"] = "Up"; } else if (aimMove < 0) { (Field)aimTextEntity["Text"]["Content"] = "Down"; } else { (Field)aimTextEntity["Text"]["Content"] = " "; } for(auto& autoBlendQueue : m_AutoBlendQueues) { autoBlendQueue.second.UpdateTime(dt); } } void AnimationSystem::CreateBlendTrees() { auto modelComponents = m_World->GetComponents("Model"); if (modelComponents == nullptr) { return; } for (auto& modelC : *modelComponents) { EntityWrapper entity = EntityWrapper(m_World, modelC.EntityID); Model* model; try { model = ResourceManager::Load<::Model, true>(entity["Model"]["Resource"]); } catch (const std::exception&) { continue;; } Skeleton* skeleton = model->m_RawModel->m_Skeleton; if (skeleton == nullptr) { continue; } if (entity.HasComponent("Blend") || entity.HasComponent("BlendOverride") || entity.HasComponent("BlendAdditive") || entity.HasComponent("Animation")) { std::shared_ptr blendTree = std::shared_ptr(new BlendTree(entity, skeleton)); if(blendTree->IsValid()) { skeleton->BlendTrees[entity] = blendTree; } } } } void AnimationSystem::UpdateAnimations(double dt) { auto animationComponents = m_World->GetComponents("Animation"); if(animationComponents == nullptr) { return; } for (auto& animationC : *animationComponents) { EntityWrapper entity = EntityWrapper(m_World, animationC.EntityID); EntityWrapper modelEntity; if(!entity.HasComponent("Model")) { modelEntity = entity.FirstParentWithComponent("Model"); } else { modelEntity = entity; } Model* model; try { Field res = modelEntity["Model"]["Resource"]; model = ResourceManager::Load<::Model, true>(res); } catch (const std::exception&) { continue; } Skeleton* skeleton = model->m_RawModel->m_Skeleton; if (skeleton == nullptr) { continue; } const Skeleton::Animation* animation = skeleton->GetAnimation(animationC["AnimationName"]); if (animation == nullptr) { continue; } double animationSpeed = (const double&)animationC["Speed"]; if((const bool&)animationC["Reverse"]) { animationSpeed *= -1; } if ((const bool&)animationC["Play"]) { double nextTime = (Field)animationC["Time"] + animationSpeed * dt; if (!(Field)animationC["Loop"]) { if (nextTime > animation->Duration) { nextTime = animation->Duration; (Field)animationC["Play"] = false; } else if (nextTime < 0) { nextTime = 0; (Field)animationC["Play"] = false; } } else { if (nextTime > animation->Duration) { while (nextTime > animation->Duration) { nextTime -= animation->Duration; } } else if (nextTime < 0) { while (nextTime < 0) { nextTime += animation->Duration; } } } (Field)animationC["Time"] = nextTime; } } } void AnimationSystem::UpdateWeights(double dt) { for (auto it = m_AutoBlendQueues.begin(); it != m_AutoBlendQueues.end(); ) { //LOG_INFO("%s", it->first.Name().c_str()); //it->second.PrintQueue(); if(it->second.HasActiveBlendJob()) { AutoBlendQueue::AutoBlendJob& blendJob = it->second.GetActiveBlendJob(); //LOG_INFO("%s", blendJob.RootNode.Name().c_str()); std::shared_ptr 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); } else { blendJob.BlendInfo.progress = 1.0; } 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++; } } } } bool AnimationSystem::OnAutoAnimationBlend(Events::AutoAnimationBlend& e) { if (!e.RootNode.Valid()) { LOG_ERROR("%s, RootNode invalid %s", e.NodeName, e.RootNode.Name().c_str()); return false; } if (!e.RootNode.HasComponent("Model")) { LOG_ERROR("%s, RootNode has no model %s", e.NodeName, e.RootNode.Name().c_str()); return false; } Model* model; try { model = ResourceManager::Load<::Model, true>((std::string)e.RootNode["Model"]["Resource"]); } catch (const std::exception&) { LOG_ERROR("%s, RootNode model not finished loading %s", e.NodeName, e.RootNode.Name().c_str()); return false; } Skeleton* skeleton = model->m_RawModel->m_Skeleton; if (skeleton == nullptr) { LOG_ERROR("%s, RootNode skeleton invalid %s", e.NodeName, e.RootNode.Name().c_str()); return false; } std::shared_ptr blendTree; if (skeleton->BlendTrees.find(e.RootNode) != skeleton->BlendTrees.end()) { blendTree = skeleton->BlendTrees.at(e.RootNode); } else { LOG_ERROR("%s, No blendtree was found invalid %s", e.NodeName, e.RootNode.Name().c_str()); return false; } EntityWrapper subTreeRoot; if (e.SingleLevelBlend) { subTreeRoot = blendTree->GetSubTreeRoot(e.NodeName); if (!subTreeRoot.Valid()) { LOG_ERROR("%s, subTreeRoot invalid", e.NodeName); 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; std::vector 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"]); (Field)entity["Animation"]["Reverse"] = e.Reverse; if (e.Restart) { if (animation != nullptr) { if (e.Restart) { if (e.Reverse) { (Field)entity["Animation"]["Time"] = animation->Duration; } else { (Field)entity["Animation"]["Time"] = 0.0; } } } } } } m_AutoBlendQueues[subTreeRoot].Insert(abj); } else { std::vector subtreeroots = blendTree->GetSingleLevelRoots(e.NodeName); for(auto entity : subtreeroots) { subTreeRoot = entity; if (!subTreeRoot.Valid()) { LOG_ERROR("%s, subTreeRoot invalid", e.NodeName); 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 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"]); entity["Animation"]["Reverse"] = e.Reverse; if (e.Restart) { if (animation != nullptr) { if (e.Restart) { if (e.Reverse) { entity["Animation"]["Time"] = animation->Duration; } else { entity["Animation"]["Time"] = 0.0; } } } } } } } return true; } bool AnimationSystem::OnEntityDeleted(Events::EntityDeleted& e) { EntityWrapper entity = 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; 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; } } bool AnimationSystem::OnInputCommand(Events::InputCommand& e) { EntityWrapper firstPerson = m_World->GetFirstEntityByName("FirstPerson"); EntityWrapper thirdPerson = m_World->GetFirstEntityByName("ThirdPerson"); EntityWrapper currentBlend = m_World->GetFirstEntityByName("CurrentBlend"); EntityWrapper queuedBlend = m_World->GetFirstEntityByName("QueuedBlend"); EntityWrapper MovementText = m_World->GetFirstEntityByName("MovementText"); if (e.Command == "ClearText") { if (e.Value > 0) { (Field)currentBlend["Text"]["Content"] = " "; (Field)queuedBlend["Text"]["Content"] = " "; } } if(e.Command == "Jump") { if(e.Value > 0) { (Field)currentBlend["Text"]["Content"] = "Jump"; (Field)queuedBlend["Text"]["Content"] = "Movement"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.2f; blendEvent.NodeName = "Jump"; blendEvent.Start = true; blendEvent.Restart = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventQueued; blendEventQueued.RootNode = thirdPerson; blendEventQueued.Delay = -0.1; blendEventQueued.Duration = 0.3f; blendEventQueued.NodeName = "MovementBlend"; blendEventQueued.AnimationEntity = thirdPerson.FirstChildByName("Jump"); m_EventBroker->Publish(blendEventQueued); } } if (e.Command == "DashL") { if (e.Value > 0) { (Field)currentBlend["Text"]["Content"] = "Dash Left"; (Field)queuedBlend["Text"]["Content"] = "Movement"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.2f; blendEvent.NodeName = "DashLeft"; blendEvent.Start = true; blendEvent.Restart = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventQueued; blendEventQueued.RootNode = thirdPerson; blendEventQueued.Delay = -0.1; blendEventQueued.Duration = 0.3f; blendEventQueued.NodeName = "MovementBlend"; blendEventQueued.AnimationEntity = thirdPerson.FirstChildByName("DashLeft"); m_EventBroker->Publish(blendEventQueued); } } if (e.Command == "DashR") { if (e.Value > 0) { (Field)currentBlend["Text"]["Content"] = "Dash Right"; (Field)queuedBlend["Text"]["Content"] = "Movement"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.2f; blendEvent.NodeName = "DashRight"; blendEvent.Start = true; blendEvent.Restart = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventQueued; blendEventQueued.RootNode = thirdPerson; blendEventQueued.Delay = -0.1; blendEventQueued.Duration = 0.3f; blendEventQueued.NodeName = "MovementBlend"; blendEventQueued.AnimationEntity = thirdPerson.FirstChildByName("DashRight"); m_EventBroker->Publish(blendEventQueued); } } if (e.Command == "DashF") { if (e.Value > 0) { (Field)currentBlend["Text"]["Content"] = "Dash Forward"; (Field)queuedBlend["Text"]["Content"] = "Movement"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.2f; blendEvent.NodeName = "DashForward"; blendEvent.Start = true; blendEvent.Restart = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventQueued; blendEventQueued.RootNode = thirdPerson; blendEventQueued.Delay = -0.1; blendEventQueued.Duration = 0.3f; blendEventQueued.NodeName = "MovementBlend"; blendEventQueued.AnimationEntity = thirdPerson.FirstChildByName("DashForward"); m_EventBroker->Publish(blendEventQueued); } } if (e.Command == "DashB") { if (e.Value > 0) { (Field)currentBlend["Text"]["Content"] = "Dash Backward"; (Field)queuedBlend["Text"]["Content"] = "Movement"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.2f; blendEvent.NodeName = "DashBackward"; blendEvent.Start = true; blendEvent.Restart = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventQueued; blendEventQueued.RootNode = thirdPerson; blendEventQueued.Delay = -0.1; blendEventQueued.Duration = 0.3f; blendEventQueued.NodeName = "MovementBlend"; blendEventQueued.AnimationEntity = thirdPerson.FirstChildByName("DashBackward"); m_EventBroker->Publish(blendEventQueued); } } if (e.Command == "Right") { if (e.Value > 0) { (Field)MovementText["Text"]["Content"] = "Right"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.3f; blendEvent.NodeName = "Right"; blendEvent.Start = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.3f; blendEventFP.NodeName = "Run"; blendEventFP.Start = true; m_EventBroker->Publish(blendEventFP); } else if (e.Value < 0) { (Field)MovementText["Text"]["Content"] = "Left"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.3f; blendEvent.NodeName = "Left"; blendEvent.Start = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.3f; blendEventFP.NodeName = "Run"; blendEventFP.Reverse = true; blendEventFP.Start = true; m_EventBroker->Publish(blendEventFP); } }else if (e.Command == "Up") { if (e.Value > 0) { (Field)MovementText["Text"]["Content"] = "Forward"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.3f; if (Crouch) { blendEvent.NodeName = "Walk"; Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.3f; blendEventFP.NodeName = "Idle"; blendEventFP.Start = true; m_EventBroker->Publish(blendEventFP); } else { blendEvent.NodeName = "Run"; Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.3f; blendEventFP.NodeName = "Run"; blendEventFP.Start = true; m_EventBroker->Publish(blendEventFP); } blendEvent.Start = true; m_EventBroker->Publish(blendEvent); }else if (e.Value < 0) { (Field)MovementText["Text"]["Content"] = "Backward"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.3f; if(Crouch) { blendEvent.NodeName = "Walk"; Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.3f; blendEventFP.NodeName = "Idle"; blendEventFP.Start = true; m_EventBroker->Publish(blendEventFP); } else { blendEvent.NodeName = "Run"; Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.3f; blendEventFP.NodeName = "Run"; blendEventFP.Reverse = true; blendEventFP.Start = true; m_EventBroker->Publish(blendEventFP); } blendEvent.Start = true; blendEvent.Reverse = true; m_EventBroker->Publish(blendEvent); } } if (e.Command == "Idle"){ if (e.Value > 0) { (Field)MovementText["Text"]["Content"] = "Idle"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.5f; blendEvent.NodeName = "Idle"; blendEvent.Start = true; blendEvent.Reverse = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.3f; blendEventFP.NodeName = "Idle"; blendEventFP.Start = true; m_EventBroker->Publish(blendEventFP); } } if (e.Command == "Crouch") { if (e.Value > 0) { Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.5f; blendEvent.NodeName = "CrouchMovement"; blendEvent.Start = true; blendEvent.Reverse = true; m_EventBroker->Publish(blendEvent); Crouch = true; } else { Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.5f; blendEvent.NodeName = "StandMovement"; blendEvent.Start = true; blendEvent.Reverse = true; m_EventBroker->Publish(blendEvent); Crouch = false; } } if (e.Command == "Aim") { aimMove = e.Value; } if (e.Command == "Shoot") { if (e.Value > 0) { (Field)currentBlend["Text"]["Content"] = "Fire"; (Field)queuedBlend["Text"]["Content"] = "Idle"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.1f; blendEvent.NodeName = "Fire"; blendEvent.Start = true; blendEvent.Restart = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.1f; blendEventFP.NodeName = "Fire"; blendEventFP.Start = true; blendEventFP.Restart = true; m_EventBroker->Publish(blendEventFP); } } if (e.Command == "Reload") { if (e.Value > 0) { (Field)currentBlend["Text"]["Content"] = "Reload"; (Field)queuedBlend["Text"]["Content"] = "Idle"; Events::AutoAnimationBlend blendEvent; blendEvent.RootNode = thirdPerson; blendEvent.Duration = 0.1f; blendEvent.NodeName = "Reload"; blendEvent.Start = true; blendEvent.Restart = true; m_EventBroker->Publish(blendEvent); Events::AutoAnimationBlend blendEventQ; blendEventQ.RootNode = thirdPerson; blendEventQ.Duration = 0.2f; blendEventQ.Delay = -0.1; blendEventQ.NodeName = "Fire"; blendEventQ.Start = false; blendEventQ.Restart = true; blendEventQ.AnimationEntity = thirdPerson.FirstChildByName("Reload"); m_EventBroker->Publish(blendEventQ); Events::AutoAnimationBlend blendEventFP; blendEventFP.RootNode = firstPerson; blendEventFP.Duration = 0.1f; blendEventFP.NodeName = "Reload"; blendEventFP.Start = true; blendEventFP.Restart = true; m_EventBroker->Publish(blendEventFP); Events::AutoAnimationBlend blendEventFPQ; blendEventFPQ.RootNode = firstPerson; blendEventFPQ.Duration = 0.2f; blendEventFPQ.Delay = -0.1; blendEventFPQ.NodeName = "Fire"; blendEventFPQ.Start = false; blendEventFPQ.Restart = true; blendEventFPQ.AnimationEntity = thirdPerson.FirstChildByName("Reload"); m_EventBroker->Publish(blendEventFPQ); } } return true; }