Compare commits
	
		
			5 Commits
		
	
	
		
			37d2845b09
			...
			30f1025947
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 30f1025947 | ||
|   | c659caebb9 | ||
|   | 1f858d68c0 | ||
|   | ba4869149b | ||
|   | 200d0fecc8 | 
| @ -46,8 +46,11 @@ target_sources(AnimTestbed PRIVATE | ||||
|         src/Camera.c | ||||
|         src/SkinnedMesh.cc | ||||
|         src/SkinnedMesh.h | ||||
|         src/AnimNode.cc | ||||
|         src/AnimNodes/AnimSamplerNode.cc | ||||
|         src/AnimNodes/SpeedScaleNode.cc | ||||
|         src/AnimNodes/BlendNode.cc | ||||
|         src/AnimationController.cc | ||||
|         src/BlendNode.cc | ||||
|         3rdparty/glfw/deps/glad_gl.c | ||||
|         3rdparty/imgui/imgui.cpp | ||||
|         3rdparty/imgui/imgui_draw.cpp | ||||
|  | ||||
							
								
								
									
										5
									
								
								src/AnimNode.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/AnimNode.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "AnimNode.h" | ||||
							
								
								
									
										50
									
								
								src/AnimNode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/AnimNode.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #ifndef ANIMTESTBED_ANIMNODE_H | ||||
| #define ANIMTESTBED_ANIMNODE_H | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include "AnimationController.h" | ||||
| 
 | ||||
| enum class AnimNodeType { | ||||
|   Blend, | ||||
|   SpeedScale, | ||||
|   AnimSampler | ||||
| }; | ||||
| 
 | ||||
| struct AnimNode { | ||||
|   AnimNode(AnimationController* animation_controller) | ||||
|       : m_animation_controller(animation_controller), | ||||
|         m_current_time(0.f), | ||||
|         m_is_time_synced(false), | ||||
|         m_anim_duration(0.f) {} | ||||
|   virtual ~AnimNode(){}; | ||||
| 
 | ||||
|   AnimNodeType m_anim_node_type; | ||||
|   std::string m_name; | ||||
|   float m_current_time; | ||||
|   bool m_is_time_synced; | ||||
|   float m_anim_duration; | ||||
|   AnimationController* m_animation_controller; | ||||
|   ozz::vector<ozz::math::SoaTransform> m_local_matrices; | ||||
| 
 | ||||
|   virtual void Reset() { m_current_time = 0.f; } | ||||
| 
 | ||||
|   virtual void UpdateIsSynced(bool is_synced) = 0; | ||||
| 
 | ||||
|   virtual void EvalAnimDuration() = 0; | ||||
| 
 | ||||
|   virtual void UpdateTime(float dt) { m_current_time += dt; } | ||||
| 
 | ||||
|   virtual void Evaluate( | ||||
|       ozz::vector<ozz::math::SoaTransform>* local_matrices) = 0; | ||||
| 
 | ||||
|   virtual void CollectNodeOrdering(std::vector<AnimNode*>& anim_nodes) = 0; | ||||
| 
 | ||||
|   virtual void DrawDebugUi(){}; | ||||
| }; | ||||
| 
 | ||||
| #endif  //ANIMTESTBED_ANIMNODE_H
 | ||||
							
								
								
									
										59
									
								
								src/AnimNodes/AnimSamplerNode.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/AnimNodes/AnimSamplerNode.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "AnimSamplerNode.h" | ||||
| 
 | ||||
| #include <imgui.h> | ||||
| 
 | ||||
| #include "../SkinnedMesh.h" | ||||
| 
 | ||||
| void AnimSamplerNode::SetAnimation(ozz::animation::Animation* animation) { | ||||
|   m_animation = animation; | ||||
| 
 | ||||
|   const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; | ||||
|   const int num_soa_joints = skinned_mesh->m_skeleton.num_soa_joints(); | ||||
|   const int num_joints = skinned_mesh->m_skeleton.num_joints(); | ||||
|   m_local_matrices.resize(num_soa_joints); | ||||
|   m_sampling_cache.Resize(num_joints); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void AnimSamplerNode::Evaluate(ozz::vector<ozz::math::SoaTransform>* local_matrices) { | ||||
|   ozz::animation::SamplingJob sampling_job; | ||||
|   sampling_job.animation = m_animation; | ||||
|   sampling_job.cache = &m_sampling_cache; | ||||
|   sampling_job.ratio = m_anim_ratio; | ||||
|   sampling_job.output = make_span(*local_matrices); | ||||
|   if (!sampling_job.Run()) { | ||||
|     ozz::log::Err() << "Error sampling animation." << std::endl; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void AnimSamplerNode::DrawDebugUi() { | ||||
|   std::string node_name = "AnimSamplerNode: " + m_name; | ||||
|   if (ImGui::TreeNode(node_name.c_str())) { | ||||
|     const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; | ||||
|     int anim_count = skinned_mesh->m_animation_names.size(); | ||||
|     const char* items[255] = {0}; | ||||
|     int item_current = 0; | ||||
|     for (int i = 0; i < anim_count; i++) { | ||||
|       items[i] = skinned_mesh->m_animation_names[i].c_str(); | ||||
|       if (skinned_mesh->m_animations[i] == m_animation) { | ||||
|         item_current = i; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (ImGui::Combo("Animation", &item_current, items, anim_count)) { | ||||
|       m_animation = skinned_mesh->m_animations[item_current]; | ||||
|     } | ||||
|     ImGui::Checkbox("Override", &m_override_ratio); | ||||
|     ImGui::SameLine(); | ||||
|     ImGui::SliderFloat("Ratio", &m_anim_ratio, 0.f, 1.f); | ||||
| 
 | ||||
|     ImGui::TreePop(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										65
									
								
								src/AnimNodes/AnimSamplerNode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/AnimNodes/AnimSamplerNode.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #ifndef ANIMTESTBED_ANIMSAMPLERNODE_H | ||||
| #define ANIMTESTBED_ANIMSAMPLERNODE_H | ||||
| 
 | ||||
| #include "../AnimNode.h" | ||||
| 
 | ||||
| struct AnimSamplerNode : public AnimNode { | ||||
|   AnimSamplerNode(AnimationController* animation_controller) | ||||
|       : AnimNode(animation_controller), | ||||
|         m_override_ratio(false), | ||||
|         m_anim_ratio(0.f) { | ||||
|     assert(m_current_time < 100.0f); | ||||
|     m_anim_node_type = AnimNodeType::AnimSampler; | ||||
|   }; | ||||
|   virtual ~AnimSamplerNode() {} | ||||
| 
 | ||||
|   ozz::animation::Animation* m_animation; | ||||
| 
 | ||||
|   bool m_override_ratio; | ||||
|   float m_anim_ratio; | ||||
| 
 | ||||
|   ozz::animation::SamplingCache m_sampling_cache; | ||||
| 
 | ||||
|   void SetAnimation(ozz::animation::Animation* animation); | ||||
| 
 | ||||
|   virtual void UpdateIsSynced(bool is_synced) override { | ||||
|     m_is_time_synced = is_synced; | ||||
|   } | ||||
| 
 | ||||
|   virtual void EvalAnimDuration() override { | ||||
|     m_anim_duration = m_animation->duration(); | ||||
|   } | ||||
| 
 | ||||
|   virtual void UpdateTime(float dt) override { | ||||
|     m_current_time += dt; | ||||
|     if (!m_override_ratio) { | ||||
|       if (m_is_time_synced) { | ||||
|         m_anim_ratio = fmodf((float)m_current_time, 1.0f); | ||||
|         m_current_time = m_anim_ratio; | ||||
|       } else { | ||||
|         const float duration = m_animation->duration(); | ||||
|         m_anim_ratio = fmodf((float)m_current_time / duration, 1.0f); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (m_anim_ratio < 0.f) { | ||||
|       m_anim_ratio += 1.0f; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   virtual void Evaluate( | ||||
|       ozz::vector<ozz::math::SoaTransform>* local_matrices) override; | ||||
| 
 | ||||
|   virtual void CollectNodeOrdering( | ||||
|       std::vector<AnimNode*>& anim_nodes) override { | ||||
|     anim_nodes.push_back(this); | ||||
|   }; | ||||
| 
 | ||||
|   virtual void DrawDebugUi(); | ||||
| }; | ||||
| 
 | ||||
| #endif  //ANIMTESTBED_ANIMSAMPLERNODE_H
 | ||||
							
								
								
									
										62
									
								
								src/AnimNodes/BlendNode.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/AnimNodes/BlendNode.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "BlendNode.h" | ||||
| 
 | ||||
| #include <imgui.h> | ||||
| #include <ozz/animation/runtime/blending_job.h> | ||||
| 
 | ||||
| #include "../SkinnedMesh.h" | ||||
| 
 | ||||
| BlendNode::BlendNode(AnimationController* animation_controller) | ||||
|     : AnimNode(animation_controller), | ||||
|       m_input_A(nullptr), | ||||
|       m_input_B(nullptr), | ||||
|       m_weight(0.f), | ||||
|       m_sync_inputs(false) { | ||||
|   m_anim_node_type = AnimNodeType::Blend; | ||||
|   const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; | ||||
|   const int num_soa_joints = skinned_mesh->m_skeleton.num_soa_joints(); | ||||
|   const int num_joints = skinned_mesh->m_skeleton.num_joints(); | ||||
|   m_local_matrices_A.resize(num_soa_joints); | ||||
|   m_local_matrices_B.resize(num_soa_joints); | ||||
| } | ||||
| 
 | ||||
| void BlendNode::Evaluate(ozz::vector<ozz::math::SoaTransform>* local_matrices) { | ||||
|   const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; | ||||
| 
 | ||||
|   m_input_A->Evaluate(&m_local_matrices_A); | ||||
|   m_input_B->Evaluate(&m_local_matrices_B); | ||||
| 
 | ||||
|   // perform blend
 | ||||
|   ozz::animation::BlendingJob::Layer layers[2]; | ||||
|   layers[0].transform = make_span(m_local_matrices_A); | ||||
|   layers[0].weight = (1.0f - m_weight); | ||||
| 
 | ||||
|   layers[1].transform = make_span(m_local_matrices_B); | ||||
|   layers[1].weight = (m_weight); | ||||
| 
 | ||||
|   ozz::animation::BlendingJob blend_job; | ||||
|   blend_job.threshold = ozz::animation::BlendingJob().threshold; | ||||
|   blend_job.layers = layers; | ||||
|   blend_job.bind_pose = skinned_mesh->m_skeleton.joint_bind_poses(); | ||||
|   blend_job.output = make_span(*local_matrices); | ||||
| 
 | ||||
|   if (!blend_job.Run()) { | ||||
|     ozz::log::Err() << "Error blending animations." << std::endl; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void BlendNode::DrawDebugUi() { | ||||
|   std::string node_name = "BlendNode: " + m_name; | ||||
|   if (ImGui::TreeNode(node_name.c_str())) { | ||||
|     ImGui::Text("Input A:"); | ||||
|     m_input_A->DrawDebugUi(); | ||||
|     ImGui::Text("Input B:"); | ||||
|     m_input_B->DrawDebugUi(); | ||||
|     ImGui::SliderFloat("Weight", &m_weight, 0.f, 1.f); | ||||
|     ImGui::Checkbox("Sync Inputs", &m_sync_inputs); | ||||
|     ImGui::TreePop(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										72
									
								
								src/AnimNodes/BlendNode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/AnimNodes/BlendNode.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #ifndef ANIMTESTBED_BLENDNODE_H | ||||
| #define ANIMTESTBED_BLENDNODE_H | ||||
| 
 | ||||
| #include "../AnimNode.h" | ||||
| 
 | ||||
| struct BlendNode : public AnimNode { | ||||
|   BlendNode(AnimationController* animation_controller); | ||||
| 
 | ||||
|   virtual ~BlendNode() {} | ||||
| 
 | ||||
|   AnimNode* m_input_A; | ||||
|   AnimNode* m_input_B; | ||||
|   float m_weight; | ||||
|   bool m_sync_inputs; | ||||
| 
 | ||||
|   ozz::vector<ozz::math::SoaTransform> m_local_matrices_A; | ||||
|   ozz::vector<ozz::math::SoaTransform> m_local_matrices_B; | ||||
| 
 | ||||
|   virtual void Reset() { | ||||
|     m_current_time = 0.f; | ||||
|   } | ||||
| 
 | ||||
|   virtual void UpdateIsSynced(bool is_synced) override { | ||||
|     m_is_time_synced = is_synced; | ||||
| 
 | ||||
|     if (m_sync_inputs) { | ||||
|       m_is_time_synced = true; | ||||
|     } | ||||
| 
 | ||||
|     m_input_B->UpdateIsSynced(m_is_time_synced); | ||||
|     m_input_A->UpdateIsSynced(m_is_time_synced); | ||||
|   } | ||||
| 
 | ||||
|   virtual void EvalAnimDuration() override { | ||||
|     if (m_is_time_synced) { | ||||
|       m_anim_duration = (1.0f - m_weight) * m_input_A->m_anim_duration + m_weight * m_input_B->m_anim_duration; | ||||
|     } else { | ||||
|       assert (false); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   virtual void UpdateTime(float dt) { | ||||
|     if (m_is_time_synced) { | ||||
|       float current_time_absolute = m_current_time * m_anim_duration + dt; | ||||
|       m_current_time = fmodf(current_time_absolute / m_anim_duration, 1.0); | ||||
| 
 | ||||
|       m_input_A->UpdateTime(m_current_time - m_input_A->m_current_time); | ||||
|       m_input_B->UpdateTime(m_current_time - m_input_B->m_current_time); | ||||
|     } else { | ||||
|       m_current_time += dt; | ||||
|       m_input_A->UpdateTime(dt); | ||||
|       m_input_B->UpdateTime(dt); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   virtual void Evaluate( | ||||
|       ozz::vector<ozz::math::SoaTransform>* local_matrices) override; | ||||
| 
 | ||||
|   virtual void CollectNodeOrdering (std::vector<AnimNode*>& anim_nodes) override { | ||||
|     anim_nodes.push_back(this); | ||||
|     m_input_A->CollectNodeOrdering(anim_nodes); | ||||
|     m_input_B->CollectNodeOrdering(anim_nodes); | ||||
|   } | ||||
| 
 | ||||
|   virtual void DrawDebugUi(); | ||||
| }; | ||||
| 
 | ||||
| #endif  //ANIMTESTBED_BLENDNODE_H
 | ||||
							
								
								
									
										27
									
								
								src/AnimNodes/SpeedScaleNode.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/AnimNodes/SpeedScaleNode.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "SpeedScaleNode.h" | ||||
| 
 | ||||
| #include <imgui.h> | ||||
| 
 | ||||
| void SpeedScaleNode::DrawDebugUi() { | ||||
|   std::string node_name = "SpeedScaleNode: " + m_name; | ||||
|   if (ImGui::TreeNode(node_name.c_str())) { | ||||
|     bool is_negative = m_time_scale < 0.f; | ||||
|     if (ImGui::Checkbox("Reverse Time", &is_negative)) { | ||||
|       m_time_scale = m_time_scale * -1.f; | ||||
|     } | ||||
| 
 | ||||
|     // ensure m_time_scale is positive
 | ||||
|     m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f); | ||||
|     ImGui::SliderFloat("Time Scale", &m_time_scale, 0.01f, 5.f); | ||||
|     // and back to the original negative or positive sign
 | ||||
|     m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f); | ||||
| 
 | ||||
|     m_input_node->DrawDebugUi(); | ||||
| 
 | ||||
|     ImGui::TreePop(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/AnimNodes/SpeedScaleNode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/AnimNodes/SpeedScaleNode.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| //
 | ||||
| // Created by martin on 12.11.21.
 | ||||
| //
 | ||||
| 
 | ||||
| #ifndef ANIMTESTBED_SPEEDSCALENODE_H | ||||
| #define ANIMTESTBED_SPEEDSCALENODE_H | ||||
| 
 | ||||
| #include "../AnimNode.h" | ||||
| 
 | ||||
| struct SpeedScaleNode : public AnimNode { | ||||
|   SpeedScaleNode(AnimationController* animation_controller) | ||||
|       : AnimNode(animation_controller), m_time_scale(1.f) { | ||||
|     m_anim_node_type = AnimNodeType::SpeedScale; | ||||
|   } | ||||
| 
 | ||||
|   float m_time_scale; | ||||
|   AnimNode* m_input_node; | ||||
| 
 | ||||
|   virtual void Reset() { m_current_time = 0.f; } | ||||
| 
 | ||||
|   virtual void UpdateIsSynced(bool is_synced) override { | ||||
|     m_is_time_synced = is_synced; | ||||
|     m_input_node->UpdateIsSynced(is_synced); | ||||
|   } | ||||
| 
 | ||||
|   virtual void EvalAnimDuration() override { | ||||
|     assert(fabs(m_time_scale) >= 0.01f); | ||||
|     m_anim_duration = fabsf(m_input_node->m_anim_duration / m_time_scale); | ||||
|   } | ||||
| 
 | ||||
|   virtual void UpdateTime(float dt) { | ||||
|     if (!m_is_time_synced) { | ||||
|       m_current_time += dt * m_time_scale; | ||||
|       m_input_node->UpdateTime(dt * m_time_scale); | ||||
|     } else { | ||||
|       m_current_time += dt; | ||||
|       m_input_node->UpdateTime(dt); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   virtual void Evaluate( | ||||
|       ozz::vector<ozz::math::SoaTransform>* local_matrices) override { | ||||
|     m_input_node->Evaluate(local_matrices); | ||||
|   }; | ||||
| 
 | ||||
|   virtual void CollectNodeOrdering( | ||||
|       std::vector<AnimNode*>& anim_nodes) override { | ||||
|     anim_nodes.push_back(this); | ||||
|     m_input_node->CollectNodeOrdering(anim_nodes); | ||||
|   } | ||||
| 
 | ||||
|   virtual void DrawDebugUi() override; | ||||
| }; | ||||
| 
 | ||||
| #endif  //ANIMTESTBED_SPEEDSCALENODE_H
 | ||||
| @ -9,130 +9,158 @@ | ||||
| #include <ozz/animation/runtime/local_to_model_job.h> | ||||
| #include <ozz/animation/runtime/sampling_job.h> | ||||
| 
 | ||||
| #include "AnimNodes/AnimSamplerNode.h" | ||||
| #include "AnimNodes/BlendNode.h" | ||||
| #include "AnimNodes/SpeedScaleNode.h" | ||||
| #include "SkinnedMesh.h" | ||||
| 
 | ||||
| AnimationController::AnimationController(SkinnedMesh* skinned_mesh) | ||||
|     : m_current_time(0.f), | ||||
|       m_skinned_mesh(skinned_mesh), | ||||
|       m_blend_anim_A(nullptr), | ||||
|       m_blend_anim_B(nullptr), | ||||
|       m_blend_weight(0.f), | ||||
|       m_time_scale_A(1.f), | ||||
|       m_time_scale_B(1.f), | ||||
|       m_override_ratio_A(false), | ||||
|       m_override_ratio_B(false) { | ||||
|     : m_current_time(0.f), m_skinned_mesh(skinned_mesh) { | ||||
|   const int num_soa_joints = skinned_mesh->m_skeleton.num_soa_joints(); | ||||
|   const int num_joints = skinned_mesh->m_skeleton.num_joints(); | ||||
|   skinned_mesh->m_local_matrices.resize(num_soa_joints); | ||||
|   skinned_mesh->m_model_matrices.resize(num_joints); | ||||
| 
 | ||||
|   m_sampling_cache_A.Resize(num_joints); | ||||
|   m_sampling_cache_B.Resize(num_joints); | ||||
| 
 | ||||
|   m_local_matrices_A.resize(num_soa_joints); | ||||
|   m_local_matrices_B.resize(num_soa_joints); | ||||
|   m_local_matrices_blended.resize(num_soa_joints); | ||||
|   m_local_matrices.resize(num_soa_joints); | ||||
| 
 | ||||
|   assert(skinned_mesh->m_animations.size() > 1); | ||||
|   SetBlendAnims(skinned_mesh->m_animations[1], skinned_mesh->m_animations[0]); | ||||
|   ResetAnims(); | ||||
| 
 | ||||
|   AnimSamplerNode* sampler_node0 = new AnimSamplerNode(this); | ||||
|   sampler_node0->m_name = "AnimSampler0"; | ||||
|   sampler_node0->SetAnimation(skinned_mesh->m_animations[1]); | ||||
|   m_anim_nodes.push_back(sampler_node0); | ||||
| 
 | ||||
|   AnimSamplerNode* sampler_node1 = new AnimSamplerNode(this); | ||||
|   sampler_node1->m_name = "AnimSampler1"; | ||||
|   sampler_node1->SetAnimation(skinned_mesh->m_animations[2]); | ||||
|   m_anim_nodes.push_back(sampler_node1); | ||||
| 
 | ||||
|   SpeedScaleNode* speed_node = new SpeedScaleNode(this); | ||||
|   speed_node->m_name = "SpeedNode0"; | ||||
|   speed_node->m_input_node = sampler_node0; | ||||
|   m_anim_nodes.push_back(speed_node); | ||||
| 
 | ||||
|   BlendNode* blend_node = new BlendNode(this); | ||||
|   blend_node->m_name = "Blend0"; | ||||
|   blend_node->m_input_A = speed_node; | ||||
|   blend_node->m_input_B = sampler_node1; | ||||
|   blend_node->m_sync_inputs = true; | ||||
|   m_anim_nodes.push_back(blend_node); | ||||
| 
 | ||||
|   SpeedScaleNode* speed_node1 = new SpeedScaleNode(this); | ||||
|   speed_node1->m_name = "SpeedNode1"; | ||||
|   speed_node1->m_input_node = blend_node; | ||||
|   m_anim_nodes.push_back(speed_node1); | ||||
| 
 | ||||
|   m_output_node = speed_node1; | ||||
| 
 | ||||
|   m_output_node->CollectNodeOrdering(m_ordered_nodes); | ||||
| 
 | ||||
|   m_output_node->Reset(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| AnimationController::~AnimationController() { | ||||
|   while (m_anim_nodes.size() > 0) { | ||||
|     delete m_anim_nodes[m_anim_nodes.size() - 1]; | ||||
|     m_anim_nodes.pop_back(); | ||||
|   } | ||||
| 
 | ||||
|   m_output_node = nullptr; | ||||
| } | ||||
| 
 | ||||
| void AnimationController::ResetAnims() { | ||||
|   for (int i = 0; i < m_ordered_nodes.size(); i++) { | ||||
|     m_ordered_nodes[i]->Reset(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void AnimationController::Update(float dt) { | ||||
|   m_current_time += dt; | ||||
|   if (!m_override_ratio_A) { | ||||
|     m_anim_time_A += dt * m_time_scale_A; | ||||
|     const float duration_A = m_blend_anim_A->duration(); | ||||
|     m_anim_ratio_A = fmodf((float)m_anim_time_A / duration_A, 1.0f); | ||||
|   if (m_output_node == nullptr) { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   if (!m_override_ratio_B) { | ||||
|     m_anim_time_B += dt * m_time_scale_B; | ||||
|     const float duration_B = m_blend_anim_B->duration(); | ||||
|     m_anim_ratio_B = fmodf((float)m_anim_time_B / duration_B, 1.0f); | ||||
|   m_output_node->UpdateIsSynced(false); | ||||
| 
 | ||||
|   for (int i = m_ordered_nodes.size() - 1; i >= 0; i--) { | ||||
|     AnimNode* node = m_ordered_nodes[i]; | ||||
|     if (node->m_is_time_synced) { | ||||
|       node->EvalAnimDuration(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   m_output_node->UpdateTime(dt); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void AnimationController::Evaluate() { | ||||
|   // sample animation A
 | ||||
|   ozz::animation::SamplingJob sampling_job; | ||||
|   sampling_job.animation = m_blend_anim_A; | ||||
|   sampling_job.cache = &m_sampling_cache_A; | ||||
|   sampling_job.ratio = m_anim_ratio_A; | ||||
|   sampling_job.output = make_span(m_local_matrices_A); | ||||
|   if (!sampling_job.Run()) { | ||||
|     ozz::log::Err() << "Error sampling animation." << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   // sample animation B
 | ||||
|   sampling_job.animation = m_blend_anim_B; | ||||
|   sampling_job.cache = &m_sampling_cache_B; | ||||
|   sampling_job.ratio = m_anim_ratio_B; | ||||
|   sampling_job.output = make_span(m_local_matrices_B); | ||||
|   if (!sampling_job.Run()) { | ||||
|     ozz::log::Err() << "Error sampling animation." << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   // perform blend
 | ||||
|   ozz::animation::BlendingJob::Layer layers[2]; | ||||
|   layers[0].transform = make_span(m_local_matrices_A); | ||||
|   layers[0].weight = (1.0f - m_blend_weight); | ||||
| 
 | ||||
|   layers[1].transform = make_span(m_local_matrices_B); | ||||
|   layers[1].weight = (m_blend_weight); | ||||
| 
 | ||||
|   ozz::animation::BlendingJob blend_job; | ||||
|   blend_job.threshold = ozz::animation::BlendingJob().threshold; | ||||
|   blend_job.layers = layers; | ||||
|   blend_job.bind_pose = m_skinned_mesh->m_skeleton.joint_bind_poses(); | ||||
|   blend_job.output = make_span(m_local_matrices_blended); | ||||
| 
 | ||||
|   if (!blend_job.Run()) { | ||||
|     ozz::log::Err() << "Error blending animations." << std::endl; | ||||
|   if (m_output_node == nullptr) { | ||||
|     return; | ||||
|   } | ||||
|   m_output_node->Evaluate(&m_local_matrices); | ||||
| 
 | ||||
|   // convert joint matrices from local to model space
 | ||||
|   ozz::animation::LocalToModelJob ltm_job; | ||||
|   ltm_job.skeleton = &m_skinned_mesh->m_skeleton; | ||||
|   ltm_job.input = make_span(m_local_matrices_blended); | ||||
|   ltm_job.input = make_span(m_local_matrices); | ||||
|   ltm_job.output = make_span(m_skinned_mesh->m_model_matrices); | ||||
|   ltm_job.Run(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void AnimationController::DrawDebugUi() { | ||||
|   ImGui::SetNextWindowSize(ImVec2(500, 300), ImGuiCond_FirstUseEver); | ||||
|   ImGui::Begin("AnimationController"); | ||||
| 
 | ||||
|   int anim_count = m_skinned_mesh->m_animation_names.size(); | ||||
|   const char* items[255] = {0}; | ||||
|   int item_current_A = 0; | ||||
|   int item_current_B = 0; | ||||
|   for (int i = 0; i < anim_count; i++) { | ||||
|     items[i] = m_skinned_mesh->m_animation_names[i].c_str(); | ||||
|     if (m_skinned_mesh->m_animations[i] == m_blend_anim_A) { | ||||
|       item_current_A = i; | ||||
|     } | ||||
|     if (m_skinned_mesh->m_animations[i] == m_blend_anim_B) { | ||||
|       item_current_B = i; | ||||
|     } | ||||
|   if (ImGui::Button("Reset")) { | ||||
|     ResetAnims(); | ||||
|   } | ||||
| 
 | ||||
|   bool anim_changed = false; | ||||
|   anim_changed = | ||||
|       ImGui::Combo("Animation A", &item_current_A, items, anim_count); | ||||
|   ImGui::SliderFloat("Time Scale", &m_time_scale_A, 0.01f, 5.f); | ||||
|   ImGui::Checkbox("Override", &m_override_ratio_A); | ||||
|   ImGui::SameLine(); | ||||
|   ImGui::SliderFloat("Ratio", &m_anim_ratio_A, 0.f, 1.f); | ||||
|   anim_changed = ImGui::Combo("Animation B", &item_current_B, items, anim_count) | ||||
|                  || anim_changed; | ||||
|   ImGui::SliderFloat("Time Scale##", &m_time_scale_B, 0.01f, 5.f); | ||||
|   ImGui::Checkbox("Override##", &m_override_ratio_B); | ||||
|   ImGui::SameLine(); | ||||
|   ImGui::SliderFloat("Ratio##", &m_anim_ratio_B, 0.f, 1.f); | ||||
|   if (m_output_node && ImGui::TreeNode("Output")) { | ||||
|     m_output_node->DrawDebugUi(); | ||||
| 
 | ||||
|     ImGui::TreePop(); | ||||
|   } | ||||
| 
 | ||||
|   if (m_output_node && ImGui::TreeNode("Nodes")) { | ||||
|     ImGui::Columns(4, "NodeOverview"); // 4-ways, with border
 | ||||
|     ImGui::Separator(); | ||||
|     ImGui::Text("Name"); ImGui::NextColumn(); | ||||
|     ImGui::Text("Synced"); ImGui::NextColumn(); | ||||
|     ImGui::Text("Duration"); ImGui::NextColumn(); | ||||
|     ImGui::Text("Time"); ImGui::NextColumn(); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
|     static int selected = -1; | ||||
| 
 | ||||
|     for (int i = 0; i < m_ordered_nodes.size(); i++) { | ||||
|       AnimNode* node = m_ordered_nodes[i]; | ||||
|       if (ImGui::Selectable(node->m_name.c_str(), selected == i, ImGuiSelectableFlags_SpanAllColumns)) | ||||
|         selected = i; | ||||
|       bool hovered = ImGui::IsItemHovered(); | ||||
|       ImGui::NextColumn(); | ||||
| 
 | ||||
|       ImGui::Text(node->m_is_time_synced ? "X" : "-"); ImGui::NextColumn(); | ||||
| 
 | ||||
|       ImGui::PushID((void*)&node->m_anim_duration); | ||||
|       ImGui::Text("%2.3f", node->m_anim_duration);      ImGui::NextColumn(); | ||||
|       ImGui::PopID(); | ||||
| 
 | ||||
|       ImGui::PushID((void*)&node->m_current_time); | ||||
|       ImGui::Text("%2.3f", node->m_current_time);      ImGui::NextColumn(); | ||||
|       ImGui::PopID(); | ||||
|     } | ||||
| 
 | ||||
|     ImGui::Columns(1); | ||||
|     ImGui::Separator(); | ||||
| 
 | ||||
|     ImGui::TreePop(); | ||||
|   } | ||||
| 
 | ||||
|   SetBlendAnims( | ||||
|       m_skinned_mesh->m_animations[item_current_A], | ||||
|       m_skinned_mesh->m_animations[item_current_B]); | ||||
|   ImGui::SliderFloat("Weight", &m_blend_weight, 0.f, 1.f); | ||||
|   ImGui::End(); | ||||
| } | ||||
| @ -5,28 +5,25 @@ | ||||
| #ifndef ANIMTESTBED_ANIMATIONCONTROLLER_H | ||||
| #define ANIMTESTBED_ANIMATIONCONTROLLER_H | ||||
| 
 | ||||
| #include <ozz/animation/runtime/animation.h> | ||||
| #include <ozz/animation/runtime/sampling_job.h> | ||||
| 
 | ||||
| #include "ozz/animation/runtime/animation.h" | ||||
| #include "ozz/base/containers/vector.h" | ||||
| #include "ozz/base/maths/soa_transform.h" | ||||
| #include "ozz/base/maths/vec_float.h" | ||||
| #include <ozz/base/containers/vector.h> | ||||
| #include <ozz/base/maths/soa_transform.h> | ||||
| #include <ozz/base/maths/vec_float.h> | ||||
| 
 | ||||
| struct SkinnedMesh; | ||||
| struct AnimNode; | ||||
| 
 | ||||
| struct AnimationController { | ||||
|   explicit AnimationController(SkinnedMesh* skinned_mesh); | ||||
|   virtual ~AnimationController(); | ||||
| 
 | ||||
|   void SetBlendAnims( | ||||
|       ozz::animation::Animation* blend_anim_A, | ||||
|       ozz::animation::Animation* blend_anim_B) { | ||||
|     m_blend_anim_A = blend_anim_A; | ||||
|     m_blend_anim_B = blend_anim_B; | ||||
|   } | ||||
|   void ResetAnims() { | ||||
|     m_anim_time_A = 0.f; | ||||
|     m_anim_time_B = 0.f; | ||||
|   } | ||||
|   void ResetAnims(); | ||||
| 
 | ||||
|   void Update(float dt); | ||||
|   void Evaluate(); | ||||
| 
 | ||||
| @ -34,27 +31,17 @@ struct AnimationController { | ||||
| 
 | ||||
|   float m_current_time; | ||||
| 
 | ||||
|   ozz::animation::Animation* m_blend_anim_A; | ||||
|   ozz::animation::Animation* m_blend_anim_B; | ||||
| 
 | ||||
|   float m_time_scale_A; | ||||
|   float m_time_scale_B; | ||||
|   float m_anim_time_A; | ||||
|   float m_anim_time_B; | ||||
|   float m_anim_ratio_A; | ||||
|   float m_anim_ratio_B; | ||||
|   bool m_override_ratio_A; | ||||
|   bool m_override_ratio_B; | ||||
|   float m_blend_weight; | ||||
| 
 | ||||
|   ozz::vector<ozz::math::SoaTransform> m_local_matrices_A; | ||||
|   ozz::vector<ozz::math::SoaTransform> m_local_matrices_B; | ||||
|   ozz::vector<ozz::math::SoaTransform> m_local_matrices; | ||||
|   ozz::vector<ozz::math::SoaTransform> m_local_matrices_blended; | ||||
| 
 | ||||
|   ozz::animation::SamplingCache m_sampling_cache_A; | ||||
|   ozz::animation::SamplingCache m_sampling_cache_B; | ||||
| 
 | ||||
|   SkinnedMesh* m_skinned_mesh = nullptr; | ||||
| 
 | ||||
|   AnimNode* m_output_node; | ||||
| 
 | ||||
|   std::vector<AnimNode*> m_anim_nodes; | ||||
|   std::vector<AnimNode*> m_ordered_nodes; | ||||
| }; | ||||
| 
 | ||||
| #endif  //ANIMTESTBED_ANIMATIONCONTROLLER_H
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user