WIP animation based root bone transform calculation.
parent
89108f4e1f
commit
d4f9459060
|
@ -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>
|
||||
|
|
|
@ -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 ;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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];
|
||||
|
@ -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<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);
|
||||
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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<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 {
|
||||
input_nodes.push_back(m_input_node);
|
||||
virtual void GetInputNodes(
|
||||
std::vector<AnimNode*>& input_nodes) const override {
|
||||
input_nodes.push_back(m_input_node);
|
||||
};
|
||||
|
||||
virtual void DrawDebugUi() override;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue