diff --git a/CMakeLists.txt b/CMakeLists.txt index cbb0da7..0a86cf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,7 @@ target_link_libraries(AnimTestbed AnimTestbedCode glfw ozz_base ozz_geometry ozz # Tests add_executable(runtests) -target_sources(runtests PRIVATE tests/main.cc tests/SyncTrackTests.cc) +target_sources(runtests PRIVATE tests/main.cc tests/AnimSampleNodeTests.cc tests/SyncTrackTests.cc) target_include_directories( runtests PUBLIC $ diff --git a/src/AnimNode.h b/src/AnimNode.h index 0ef9be5..f35f241 100644 --- a/src/AnimNode.h +++ b/src/AnimNode.h @@ -5,6 +5,8 @@ #ifndef ANIMTESTBED_ANIMNODE_H #define ANIMTESTBED_ANIMNODE_H +#include + #include #include "AnimationController.h" @@ -42,7 +44,8 @@ struct AnimNode { // Evaluate the current node and write output to local_matrices. virtual void Evaluate( - ozz::vector* local_matrices) = 0; + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) = 0; // Returns a list of animation nodes that provide input for this node. virtual void GetInputNodes(std::vector& input_nodes) const = 0 ; diff --git a/src/AnimNodes/AnimSamplerNode.cc b/src/AnimNodes/AnimSamplerNode.cc index 27458e5..9900570 100644 --- a/src/AnimNodes/AnimSamplerNode.cc +++ b/src/AnimNodes/AnimSamplerNode.cc @@ -21,7 +21,8 @@ void AnimSamplerNode::SetAnimation(ozz::animation::Animation* animation, const S } void AnimSamplerNode::Evaluate( - ozz::vector* local_matrices) { + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) { ozz::animation::SamplingJob sampling_job; sampling_job.animation = m_animation; sampling_job.cache = &m_sampling_cache; @@ -30,6 +31,36 @@ void AnimSamplerNode::Evaluate( if (!sampling_job.Run()) { ozz::log::Err() << "Error sampling animation." << std::endl; } + + if (root_transform == nullptr) { + return; + } + + ozz::vector root_bone_prev; + root_bone_prev.push_back(ozz::math::SoaTransform()); + sampling_job.ratio = m_anim_ratio_prev; + sampling_job.output = make_span (root_bone_prev); + if (!sampling_job.Run()) { + ozz::log::Err() << "Error sampling animation." << std::endl; + } + + ozz::math::SoaFloat3 translation = + root_bone_prev[m_root_bone_index].translation; + float root_prev_trans_x[4]; + float root_prev_trans_y[4]; + float root_prev_trans_z[4]; + _mm_store_ps(root_prev_trans_x, translation.x); + _mm_store_ps(root_prev_trans_y, translation.y); + _mm_store_ps(root_prev_trans_z, translation.z); + + translation = + (*local_matrices)[m_root_bone_index].translation; + float root_current_trans_x[4]; + float root_current_trans_y[4]; + float root_current_trans_z[4]; + _mm_store_ps(root_current_trans_x, translation.x); + _mm_store_ps(root_current_trans_y, translation.y); + _mm_store_ps(root_current_trans_z, translation.z); } void AnimSamplerNode::DrawDebugUi() { diff --git a/src/AnimNodes/AnimSamplerNode.h b/src/AnimNodes/AnimSamplerNode.h index 6053873..4fc8c69 100644 --- a/src/AnimNodes/AnimSamplerNode.h +++ b/src/AnimNodes/AnimSamplerNode.h @@ -11,7 +11,8 @@ struct AnimSamplerNode : public AnimNode { AnimSamplerNode(AnimationController* animation_controller) : AnimNode(animation_controller), m_override_ratio(false), - m_anim_ratio(0.f) { + m_anim_ratio(0.f), + m_root_bone_index(0) { assert(m_current_time < 100.0f); m_anim_node_type = AnimNodeType::AnimSampler; }; @@ -21,26 +22,40 @@ struct AnimSamplerNode : public AnimNode { bool m_override_ratio; float m_anim_ratio; + float m_anim_ratio_prev; + bool m_root_bone_index; ozz::vector m_local_matrices; + ozz::vector m_root_output; ozz::animation::SamplingCache m_sampling_cache; - void SetAnimation(ozz::animation::Animation* animation, const SyncTrack& sync_track); + void SetAnimation( + ozz::animation::Animation* animation, + const SyncTrack& sync_track); virtual void UpdateIsSynced(bool is_synced) override { m_is_time_synced = is_synced; } - virtual void UpdateSyncTrack() override { - } + virtual void UpdateSyncTrack() override {} virtual void UpdateTime(float dt) override { m_current_time += dt; if (!m_override_ratio) { if (m_is_time_synced) { m_anim_ratio = m_sync_track.CalcRatioFromSyncTime(m_current_time); + float prev_sync_time = m_current_time - dt; + if (m_current_time < 0) { + prev_sync_time += m_sync_track.m_num_intervals; + } + m_anim_ratio_prev = m_sync_track.CalcRatioFromSyncTime(prev_sync_time); } else { - m_anim_ratio = fmodf((float)m_current_time / m_animation->duration(), 1.0f); + m_anim_ratio = + fmodf((float)m_current_time / m_animation->duration(), 1.0f); + m_anim_ratio_prev = fmodf((float)m_current_time / m_animation->duration(), 1.0f); + if (m_anim_ratio_prev < 0) { + m_anim_ratio_prev += 1.0f; + } } } @@ -50,9 +65,11 @@ struct AnimSamplerNode : public AnimNode { } virtual void Evaluate( - ozz::vector* local_matrices) override; + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) override; - virtual void GetInputNodes(std::vector& input_nodes) const override{}; + virtual void GetInputNodes( + std::vector& input_nodes) const override{}; virtual void DrawDebugUi(); }; diff --git a/src/AnimNodes/BlendNode.cc b/src/AnimNodes/BlendNode.cc index 5d1b75b..d511e8e 100644 --- a/src/AnimNodes/BlendNode.cc +++ b/src/AnimNodes/BlendNode.cc @@ -23,11 +23,17 @@ BlendNode::BlendNode(AnimationController* animation_controller) m_local_matrices_B.resize(num_soa_joints); } -void BlendNode::Evaluate(ozz::vector* local_matrices) { +void BlendNode::Evaluate( + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) { 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); + m_input_A->Evaluate( + &m_local_matrices_A, + root_transform != nullptr ? &m_root_transform_A : nullptr); + m_input_B->Evaluate( + &m_local_matrices_B, + root_transform != nullptr ? &m_root_transform_B : nullptr); // perform blend ozz::animation::BlendingJob::Layer layers[2]; @@ -50,7 +56,10 @@ void BlendNode::Evaluate(ozz::vector* local_matrices) { void BlendNode::DrawDebugUi() { if (ImGui::SliderFloat("Weight", &m_weight, 0.f, 1.f)) { - m_sync_track = SyncTrack::Blend(m_weight, m_input_A->m_sync_track, m_input_B->m_sync_track); + m_sync_track = SyncTrack::Blend( + m_weight, + m_input_A->m_sync_track, + m_input_B->m_sync_track); } ImGui::Checkbox("Sync Inputs", &m_sync_inputs); diff --git a/src/AnimNodes/BlendNode.h b/src/AnimNodes/BlendNode.h index 997ff87..76b97b3 100644 --- a/src/AnimNodes/BlendNode.h +++ b/src/AnimNodes/BlendNode.h @@ -19,6 +19,8 @@ struct BlendNode : public AnimNode { ozz::vector m_local_matrices_A; ozz::vector m_local_matrices_B; + ozz::math::Transform m_root_transform_A; + ozz::math::Transform m_root_transform_B; virtual void Reset() { m_current_time = 0.f; @@ -45,11 +47,14 @@ struct BlendNode : public AnimNode { virtual void UpdateTime(float dt) { if (m_is_time_synced) { + float sync_time_old = m_sync_track.CalcSyncFromAbsTime(m_current_time); m_current_time = fmodf(m_current_time + dt, m_sync_track.m_duration); - float current_sync_time = m_sync_track.CalcSyncFromAbsTime(m_current_time); + float sync_time_dt = m_sync_track.CalcSyncFromAbsTime(m_current_time) - sync_time_old; - m_input_A->UpdateTime(current_sync_time - m_input_A->m_current_time); - m_input_B->UpdateTime(current_sync_time - m_input_B->m_current_time); + m_input_A->m_current_time = sync_time_old; + m_input_B->m_current_time = sync_time_old; + m_input_A->UpdateTime(sync_time_dt); + m_input_B->UpdateTime(sync_time_dt); } else { m_current_time += dt; m_input_A->UpdateTime(dt); @@ -58,7 +63,8 @@ struct BlendNode : public AnimNode { } virtual void Evaluate( - ozz::vector* local_matrices) override; + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) override; virtual void GetInputNodes(std::vector& input_nodes) const override { input_nodes.push_back(m_input_A); diff --git a/src/AnimNodes/BlendSpace1D.cc b/src/AnimNodes/BlendSpace1D.cc index 5905fa6..9020dc2 100644 --- a/src/AnimNodes/BlendSpace1D.cc +++ b/src/AnimNodes/BlendSpace1D.cc @@ -25,11 +25,17 @@ BlendSpace1D::BlendSpace1D(AnimationController* animation_controller) m_local_matrices_1.resize(num_soa_joints); } -void BlendSpace1D::Evaluate(ozz::vector* local_matrices) { +void BlendSpace1D::Evaluate( + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) { const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; - m_input_0->Evaluate(&m_local_matrices_0); - m_input_1->Evaluate(&m_local_matrices_1); + m_input_0->Evaluate( + &m_local_matrices_0, + root_transform != nullptr ? &m_root_transform_0 : nullptr); + m_input_1->Evaluate( + &m_local_matrices_1, + root_transform != nullptr ? &m_root_transform_1 : nullptr); // perform blend ozz::animation::BlendingJob::Layer layers[2]; @@ -54,7 +60,10 @@ void BlendSpace1D::DrawDebugUi() { float min_weight = m_input_weights[0]; float max_weight = m_input_weights.back(); if (ImGui::SliderFloat("Weight", &m_weight, min_weight, max_weight)) { - m_sync_track = SyncTrack::Blend(m_weight, m_input_0->m_sync_track, m_input_1->m_sync_track); + m_sync_track = SyncTrack::Blend( + m_weight, + m_input_0->m_sync_track, + m_input_1->m_sync_track); } ImGui::Checkbox("Sync Inputs", &m_sync_inputs); diff --git a/src/AnimNodes/BlendSpace1D.h b/src/AnimNodes/BlendSpace1D.h index 827124a..345fc2b 100644 --- a/src/AnimNodes/BlendSpace1D.h +++ b/src/AnimNodes/BlendSpace1D.h @@ -26,6 +26,8 @@ struct BlendSpace1D : public AnimNode { float m_weight_1; ozz::vector m_local_matrices_0; ozz::vector m_local_matrices_1; + ozz::math::Transform m_root_transform_0; + ozz::math::Transform m_root_transform_1; virtual void Reset() { m_current_time = 0.f; @@ -84,7 +86,8 @@ struct BlendSpace1D : public AnimNode { } virtual void Evaluate( - ozz::vector* local_matrices) override; + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) override; virtual void GetInputNodes(std::vector& input_nodes) const override { for (int i = 0; i < m_inputs.size(); i++) { diff --git a/src/AnimNodes/LockTranslationNode.cc b/src/AnimNodes/LockTranslationNode.cc index 8b4f049..23d016d 100644 --- a/src/AnimNodes/LockTranslationNode.cc +++ b/src/AnimNodes/LockTranslationNode.cc @@ -5,12 +5,15 @@ #include "LockTranslationNode.h" #include + #include "ozz/base/maths/soa_transform.h" void LockTranslationNode::Evaluate( - ozz::vector* local_matrices) { - m_input->Evaluate(local_matrices); - ozz::math::SoaFloat3 translation = (*local_matrices)[m_locked_bone_index].translation; + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) { + m_input->Evaluate(local_matrices, root_transform); + ozz::math::SoaFloat3 translation = + (*local_matrices)[m_locked_bone_index].translation; float x[4]; float y[4]; float z[4]; @@ -35,13 +38,14 @@ void LockTranslationNode::Evaluate( translation.z = _mm_load_ps(z); //translation = ozz::math::SoaFloat3::zero(); -// ozz::math::SetX(translation, 0.f); -// ozz::math::SetZ(translation, 0.f); - (*local_matrices)[m_locked_bone_index].translation = translation; + // ozz::math::SetX(translation, 0.f); + // ozz::math::SetZ(translation, 0.f); + (*local_matrices)[m_locked_bone_index].translation = translation; } void LockTranslationNode::DrawDebugUi() { - const ozz::animation::Skeleton& skeleton = m_animation_controller->m_skinned_mesh->m_skeleton; + const ozz::animation::Skeleton& skeleton = + m_animation_controller->m_skinned_mesh->m_skeleton; ozz::span joint_names = skeleton.joint_names(); const char* items[255] = {0}; @@ -54,6 +58,4 @@ void LockTranslationNode::DrawDebugUi() { ImGui::Checkbox("Lock X", &m_lock_x); ImGui::Checkbox("Lock Y", &m_lock_y); ImGui::Checkbox("Lock Z", &m_lock_z); - - } \ No newline at end of file diff --git a/src/AnimNodes/LockTranslationNode.h b/src/AnimNodes/LockTranslationNode.h index 7aa197d..6708cfa 100644 --- a/src/AnimNodes/LockTranslationNode.h +++ b/src/AnimNodes/LockTranslationNode.h @@ -39,7 +39,8 @@ struct LockTranslationNode : public AnimNode { virtual void UpdateTime(float dt) { m_input->UpdateTime(dt); } virtual void Evaluate( - ozz::vector* local_matrices) override; + ozz::vector* local_matrices, + ozz::math::Transform* root_transform = nullptr) override; virtual void GetInputNodes( std::vector& input_nodes) const override { diff --git a/src/AnimNodes/SpeedScaleNode.h b/src/AnimNodes/SpeedScaleNode.h index e957b58..5addcf3 100644 --- a/src/AnimNodes/SpeedScaleNode.h +++ b/src/AnimNodes/SpeedScaleNode.h @@ -26,7 +26,8 @@ struct SpeedScaleNode : public AnimNode { virtual void UpdateSyncTrack() override { assert(fabs(m_time_scale) >= 0.01f); m_sync_track = m_input_node->m_sync_track; - m_sync_track.m_duration =fabsf(m_input_node->m_sync_track.m_duration / m_time_scale); + m_sync_track.m_duration = + fabsf(m_input_node->m_sync_track.m_duration / m_time_scale); } virtual void UpdateTime(float dt) { @@ -40,12 +41,14 @@ struct SpeedScaleNode : public AnimNode { } virtual void Evaluate( - ozz::vector* local_matrices) override { - m_input_node->Evaluate(local_matrices); + ozz::vector* local_matrices, + ozz::math::Transform* root_transform) override { + m_input_node->Evaluate(local_matrices, root_transform); }; - virtual void GetInputNodes(std::vector& input_nodes) const override { - input_nodes.push_back(m_input_node); + virtual void GetInputNodes( + std::vector& input_nodes) const override { + input_nodes.push_back(m_input_node); }; virtual void DrawDebugUi() override; diff --git a/src/AnimationController.cc b/src/AnimationController.cc index 77f8757..b6e1fbe 100644 --- a/src/AnimationController.cc +++ b/src/AnimationController.cc @@ -28,17 +28,23 @@ AnimationController::AnimationController(SkinnedMesh* skinned_mesh) AnimSamplerNode* sampler_node0 = new AnimSamplerNode(this); sampler_node0->m_name = "AnimSampler0"; - sampler_node0->SetAnimation(skinned_mesh->m_animations[1], skinned_mesh->m_animation_sync_track[1]); + sampler_node0->SetAnimation( + skinned_mesh->m_animations[1], + skinned_mesh->m_animation_sync_track[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], skinned_mesh->m_animation_sync_track[2]); + sampler_node1->SetAnimation( + skinned_mesh->m_animations[2], + skinned_mesh->m_animation_sync_track[2]); m_anim_nodes.push_back(sampler_node1); AnimSamplerNode* sampler_node2 = new AnimSamplerNode(this); sampler_node2->m_name = "AnimSampler2"; - sampler_node2->SetAnimation(skinned_mesh->m_animations[3], skinned_mesh->m_animation_sync_track[3]); + sampler_node2->SetAnimation( + skinned_mesh->m_animations[3], + skinned_mesh->m_animation_sync_track[3]); m_anim_nodes.push_back(sampler_node2); BlendSpace1D* blend_space = new BlendSpace1D(this); @@ -145,7 +151,9 @@ void AnimationController::Evaluate() { return; } - m_output_node->Evaluate(&m_skinned_mesh->m_local_matrices); + m_output_node->Evaluate( + &m_skinned_mesh->m_local_matrices, + m_calc_root_transform ? &m_root_transform : nullptr); }; void AnimationController::DrawDebugUi() { @@ -187,7 +195,7 @@ void AnimationController::DrawDebugUi() { ImGui::End(); } - ImGui::Text ("Node States"); + ImGui::Text("Node States"); ImGui::Columns(4, "Node States"); // 4-ways, with border ImGui::Separator(); diff --git a/src/AnimationController.h b/src/AnimationController.h index 7895ab9..1d33963 100644 --- a/src/AnimationController.h +++ b/src/AnimationController.h @@ -9,6 +9,7 @@ #include #include #include +#include #include struct SkinnedMesh; @@ -33,6 +34,8 @@ struct AnimationController { float m_current_time; bool m_paused; + bool m_calc_root_transform; + ozz::math::Transform m_root_transform; SkinnedMesh* m_skinned_mesh = nullptr; diff --git a/tests/AnimSampleNodeTests.cc b/tests/AnimSampleNodeTests.cc new file mode 100644 index 0000000..f2b0397 --- /dev/null +++ b/tests/AnimSampleNodeTests.cc @@ -0,0 +1,18 @@ +// +// Created by martin on 21.11.21. +// + +#include "AnimNodes/AnimSamplerNode.h" +#include "catch.hpp" + +TEST_CASE("AnimSamplerNode", "[AnimSamplerNode]") { + SkinnedMesh skinned_mesh; + skinned_mesh.LoadSkeleton("../media/MixamoYBot-skeleton.ozz"); + int anim_idx = 0; + skinned_mesh.LoadAnimation("../media/Walking-loop.ozz"); + float walking_markers[] = {0.293, 0.762}; + skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers(skinned_mesh.m_animations[anim_idx]->duration(), 2, walking_markers); + + AnimationController anim_controller(&skinned_mesh); + AnimSamplerNode test_node (&anim_controller); +} \ No newline at end of file