WIP animation based root bone transform calculation.

AnimGraphEditor
Martin Felis 2021-11-21 12:33:22 +01:00
parent 89108f4e1f
commit d4f9459060
14 changed files with 156 additions and 43 deletions

View File

@ -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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>

View File

@ -5,6 +5,8 @@
#ifndef ANIMTESTBED_ANIMNODE_H
#define ANIMTESTBED_ANIMNODE_H
#include <ozz/base/maths/transform.h>
#include <string>
#include "AnimationController.h"
@ -42,7 +44,8 @@ struct AnimNode {
// Evaluate the current node and write output to local_matrices.
virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices) = 0;
ozz::vector<ozz::math::SoaTransform>* 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<AnimNode*>& input_nodes) const = 0 ;

View File

@ -21,7 +21,8 @@ void AnimSamplerNode::SetAnimation(ozz::animation::Animation* animation, const S
}
void AnimSamplerNode::Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices) {
ozz::vector<ozz::math::SoaTransform>* 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<ozz::math::SoaTransform> 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() {

View File

@ -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<ozz::math::SoaTransform> m_local_matrices;
ozz::vector<ozz::math::SoaTransform> 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<ozz::math::SoaTransform>* local_matrices) override;
ozz::vector<ozz::math::SoaTransform>* local_matrices,
ozz::math::Transform* root_transform) override;
virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const override{};
virtual void GetInputNodes(
std::vector<AnimNode*>& input_nodes) const override{};
virtual void DrawDebugUi();
};

View File

@ -23,11 +23,17 @@ BlendNode::BlendNode(AnimationController* animation_controller)
m_local_matrices_B.resize(num_soa_joints);
}
void BlendNode::Evaluate(ozz::vector<ozz::math::SoaTransform>* local_matrices) {
void BlendNode::Evaluate(
ozz::vector<ozz::math::SoaTransform>* 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<ozz::math::SoaTransform>* 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);

View File

@ -19,6 +19,8 @@ struct BlendNode : public AnimNode {
ozz::vector<ozz::math::SoaTransform> m_local_matrices_A;
ozz::vector<ozz::math::SoaTransform> 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<ozz::math::SoaTransform>* local_matrices) override;
ozz::vector<ozz::math::SoaTransform>* local_matrices,
ozz::math::Transform* root_transform) override;
virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const override {
input_nodes.push_back(m_input_A);

View File

@ -25,11 +25,17 @@ BlendSpace1D::BlendSpace1D(AnimationController* animation_controller)
m_local_matrices_1.resize(num_soa_joints);
}
void BlendSpace1D::Evaluate(ozz::vector<ozz::math::SoaTransform>* local_matrices) {
void BlendSpace1D::Evaluate(
ozz::vector<ozz::math::SoaTransform>* 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);

View File

@ -26,6 +26,8 @@ struct BlendSpace1D : public AnimNode {
float m_weight_1;
ozz::vector<ozz::math::SoaTransform> m_local_matrices_0;
ozz::vector<ozz::math::SoaTransform> 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<ozz::math::SoaTransform>* local_matrices) override;
ozz::vector<ozz::math::SoaTransform>* local_matrices,
ozz::math::Transform* root_transform) override;
virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const override {
for (int i = 0; i < m_inputs.size(); i++) {

View File

@ -5,12 +5,15 @@
#include "LockTranslationNode.h"
#include <imgui.h>
#include "ozz/base/maths/soa_transform.h"
void LockTranslationNode::Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices) {
m_input->Evaluate(local_matrices);
ozz::math::SoaFloat3 translation = (*local_matrices)[m_locked_bone_index].translation;
ozz::vector<ozz::math::SoaTransform>* 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];
@ -41,7 +44,8 @@ void LockTranslationNode::Evaluate(
}
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<const char* const> 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);
}

View File

@ -39,7 +39,8 @@ struct LockTranslationNode : public AnimNode {
virtual void UpdateTime(float dt) { m_input->UpdateTime(dt); }
virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices) override;
ozz::vector<ozz::math::SoaTransform>* local_matrices,
ozz::math::Transform* root_transform = nullptr) override;
virtual void GetInputNodes(
std::vector<AnimNode*>& input_nodes) const override {

View File

@ -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,11 +41,13 @@ struct SpeedScaleNode : public AnimNode {
}
virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices) override {
m_input_node->Evaluate(local_matrices);
ozz::vector<ozz::math::SoaTransform>* local_matrices,
ozz::math::Transform* root_transform) override {
m_input_node->Evaluate(local_matrices, root_transform);
};
virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const override {
virtual void GetInputNodes(
std::vector<AnimNode*>& input_nodes) const override {
input_nodes.push_back(m_input_node);
};

View File

@ -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() {

View File

@ -9,6 +9,7 @@
#include <ozz/animation/runtime/sampling_job.h>
#include <ozz/base/containers/vector.h>
#include <ozz/base/maths/soa_transform.h>
#include <ozz/base/maths/transform.h>
#include <ozz/base/maths/vec_float.h>
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;

View File

@ -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);
}