4 Commits
Author SHA1 Message Date
Martin Felis 2848909981 Added graph design ramblings. 2025-11-21 13:16:32 +01:00
Martin Felis ed7c7902ec Minor tweaks 2025-11-11 09:29:16 +01:00
Martin Felis 79c878a8e5 Scaled up the UI. 2025-11-11 09:14:33 +01:00
Martin Felis 791bf36b90 Reviving old code. 2025-11-11 09:03:24 +01:00
21 changed files with 10547 additions and 513 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.3) cmake_minimum_required (VERSION 3.20)
# Defines the project's name # Defines the project's name
project(ozz) project(ozz)
@@ -49,7 +49,7 @@ class StdAllocator {
StdAllocator(const StdAllocator&) noexcept {} StdAllocator(const StdAllocator&) noexcept {}
template <class _Other> template <class _Other>
StdAllocator<value_type>(const StdAllocator<_Other>&) noexcept {} StdAllocator(const StdAllocator<_Other>&) noexcept {}
template <class _Other> template <class _Other>
struct rebind { struct rebind {
+1 -2
View File
@@ -49,7 +49,6 @@ add_library(AnimTestbedCode OBJECT
src/AnimNode.cc src/AnimNode.cc
src/AnimNodes/AnimSamplerNode.cc src/AnimNodes/AnimSamplerNode.cc
src/AnimNodes/SpeedScaleNode.cc src/AnimNodes/SpeedScaleNode.cc
src/AnimNodes/BlendSpace1D.cc
src/AnimNodes/BlendNode.cc src/AnimNodes/BlendNode.cc
src/AnimNodes/LockTranslationNode.cc src/AnimNodes/LockTranslationNode.cc
src/AnimationController.cc src/AnimationController.cc
@@ -84,7 +83,7 @@ target_link_libraries(AnimTestbed AnimTestbedCode glfw ozz_base ozz_geometry ozz
# Tests # Tests
add_executable(runtests) add_executable(runtests)
target_sources(runtests PRIVATE tests/main.cc tests/AnimSampleNodeTests.cc tests/SyncTrackTests.cc) target_sources(runtests PRIVATE tests/main.cc tests/SyncTrackTests.cc)
target_include_directories( target_include_directories(
runtests runtests
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src> PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+35
View File
@@ -0,0 +1,35 @@
## Graph
### 1. Support of math nodes (or non-AnimNodes in general)
* Enables animators to add custom math for blend inputs or to adjust other inputs (e.g. LookAt or IK
targets).
**Open Issues**
1. When to do the evaluation? Two types of subgraphs:
a) Instant inputs (needed for blend node inputs) that have to be evaluated before
UpdateConnections
b) Processing nodes, e.g. for extracted bones.
### 2. Support of multiple output sockets
* E.g. extract Bone transform
* Increases Node complexity:
* AnimOutput
* AnimOutput + Data
* Data
(Data = bool, float, vec3, quat, ...)
**Open Issues**
1. Unclear when this is actually needed. Using more specific nodes that perform the desired logic
may be better (
c.f. https://dev.epicgames.com/documentation/en-us/unreal-engine/animation-blueprint-bone-driven-controller-in-unreal-engine).
Likely this is not crucial so should be avoided for now.
### 3. Multi-skeleton evaluation
Use case: riding on a horse, interaction between two characters.
+1 -4
View File
@@ -5,8 +5,6 @@
#ifndef ANIMTESTBED_ANIMNODE_H #ifndef ANIMTESTBED_ANIMNODE_H
#define ANIMTESTBED_ANIMNODE_H #define ANIMTESTBED_ANIMNODE_H
#include <ozz/base/maths/transform.h>
#include <string> #include <string>
#include "AnimationController.h" #include "AnimationController.h"
@@ -44,8 +42,7 @@ struct AnimNode {
// Evaluate the current node and write output to local_matrices. // Evaluate the current node and write output to local_matrices.
virtual void Evaluate( virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices, ozz::vector<ozz::math::SoaTransform>* local_matrices) = 0;
ozz::math::Transform* root_transform) = 0;
// Returns a list of animation nodes that provide input for this node. // Returns a list of animation nodes that provide input for this node.
virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const = 0 ; virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const = 0 ;
+1 -44
View File
@@ -21,8 +21,7 @@ void AnimSamplerNode::SetAnimation(ozz::animation::Animation* animation, const S
} }
void AnimSamplerNode::Evaluate( 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; ozz::animation::SamplingJob sampling_job;
sampling_job.animation = m_animation; sampling_job.animation = m_animation;
sampling_job.cache = &m_sampling_cache; sampling_job.cache = &m_sampling_cache;
@@ -31,48 +30,6 @@ void AnimSamplerNode::Evaluate(
if (!sampling_job.Run()) { if (!sampling_job.Run()) {
ozz::log::Err() << "Error sampling animation." << std::endl; 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::vector<ozz::math::SoaTransform> root_bone_cur;
root_bone_prev.push_back(ozz::math::SoaTransform());
sampling_job.ratio = m_anim_ratio;
sampling_job.output = make_span (root_bone_cur);
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);
root_transform->translation.x = root_current_trans_x[0];
root_transform->translation.y = root_current_trans_y[0];
root_transform->translation.z = root_current_trans_z[0];
} }
void AnimSamplerNode::DrawDebugUi() { void AnimSamplerNode::DrawDebugUi() {
+7 -24
View File
@@ -11,8 +11,7 @@ struct AnimSamplerNode : public AnimNode {
AnimSamplerNode(AnimationController* animation_controller) AnimSamplerNode(AnimationController* animation_controller)
: AnimNode(animation_controller), : AnimNode(animation_controller),
m_override_ratio(false), 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); assert(m_current_time < 100.0f);
m_anim_node_type = AnimNodeType::AnimSampler; m_anim_node_type = AnimNodeType::AnimSampler;
}; };
@@ -22,40 +21,26 @@ struct AnimSamplerNode : public AnimNode {
bool m_override_ratio; bool m_override_ratio;
float m_anim_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_local_matrices;
ozz::vector<ozz::math::SoaTransform> m_root_output;
ozz::animation::SamplingCache m_sampling_cache; ozz::animation::SamplingCache m_sampling_cache;
void SetAnimation( void SetAnimation(ozz::animation::Animation* animation, const SyncTrack& sync_track);
ozz::animation::Animation* animation,
const SyncTrack& sync_track);
virtual void UpdateIsSynced(bool is_synced) override { virtual void UpdateIsSynced(bool is_synced) override {
m_is_time_synced = is_synced; m_is_time_synced = is_synced;
} }
virtual void UpdateSyncTrack() override {} virtual void UpdateSyncTrack() override {
}
virtual void UpdateTime(float dt) override { virtual void UpdateTime(float dt) override {
m_current_time += dt; m_current_time += dt;
if (!m_override_ratio) { if (!m_override_ratio) {
if (m_is_time_synced) { if (m_is_time_synced) {
m_anim_ratio = m_sync_track.CalcRatioFromSyncTime(m_current_time); 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 { } else {
m_anim_ratio = m_anim_ratio = fmodf((float)m_current_time / m_animation->duration(), 1.0f);
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;
}
} }
} }
@@ -65,11 +50,9 @@ struct AnimSamplerNode : public AnimNode {
} }
virtual void Evaluate( virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices, ozz::vector<ozz::math::SoaTransform>* local_matrices) override;
ozz::math::Transform* root_transform) override;
virtual void GetInputNodes( virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const override{};
std::vector<AnimNode*>& input_nodes) const override{};
virtual void DrawDebugUi(); virtual void DrawDebugUi();
}; };
+4 -13
View File
@@ -23,17 +23,11 @@ BlendNode::BlendNode(AnimationController* animation_controller)
m_local_matrices_B.resize(num_soa_joints); m_local_matrices_B.resize(num_soa_joints);
} }
void BlendNode::Evaluate( void BlendNode::Evaluate(ozz::vector<ozz::math::SoaTransform>* local_matrices) {
ozz::vector<ozz::math::SoaTransform>* local_matrices,
ozz::math::Transform* root_transform) {
const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh;
m_input_A->Evaluate( m_input_A->Evaluate(&m_local_matrices_A);
&m_local_matrices_A, m_input_B->Evaluate(&m_local_matrices_B);
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 // perform blend
ozz::animation::BlendingJob::Layer layers[2]; ozz::animation::BlendingJob::Layer layers[2];
@@ -56,10 +50,7 @@ void BlendNode::Evaluate(
void BlendNode::DrawDebugUi() { void BlendNode::DrawDebugUi() {
if (ImGui::SliderFloat("Weight", &m_weight, 0.f, 1.f)) { if (ImGui::SliderFloat("Weight", &m_weight, 0.f, 1.f)) {
m_sync_track = SyncTrack::Blend( m_sync_track = SyncTrack::Blend(m_weight, m_input_A->m_sync_track, m_input_B->m_sync_track);
m_weight,
m_input_A->m_sync_track,
m_input_B->m_sync_track);
} }
ImGui::Checkbox("Sync Inputs", &m_sync_inputs); ImGui::Checkbox("Sync Inputs", &m_sync_inputs);
+4 -10
View File
@@ -19,8 +19,6 @@ struct BlendNode : public AnimNode {
ozz::vector<ozz::math::SoaTransform> m_local_matrices_A; 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_B;
ozz::math::Transform m_root_transform_A;
ozz::math::Transform m_root_transform_B;
virtual void Reset() { virtual void Reset() {
m_current_time = 0.f; m_current_time = 0.f;
@@ -47,14 +45,11 @@ struct BlendNode : public AnimNode {
virtual void UpdateTime(float dt) { virtual void UpdateTime(float dt) {
if (m_is_time_synced) { 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); m_current_time = fmodf(m_current_time + dt, m_sync_track.m_duration);
float sync_time_dt = m_sync_track.CalcSyncFromAbsTime(m_current_time) - sync_time_old; float current_sync_time = m_sync_track.CalcSyncFromAbsTime(m_current_time);
m_input_A->m_current_time = sync_time_old; m_input_A->UpdateTime(current_sync_time - m_input_A->m_current_time);
m_input_B->m_current_time = sync_time_old; m_input_B->UpdateTime(current_sync_time - m_input_B->m_current_time);
m_input_A->UpdateTime(sync_time_dt);
m_input_B->UpdateTime(sync_time_dt);
} else { } else {
m_current_time += dt; m_current_time += dt;
m_input_A->UpdateTime(dt); m_input_A->UpdateTime(dt);
@@ -63,8 +58,7 @@ struct BlendNode : public AnimNode {
} }
virtual void Evaluate( virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices, ozz::vector<ozz::math::SoaTransform>* local_matrices) override;
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 {
input_nodes.push_back(m_input_A); input_nodes.push_back(m_input_A);
-72
View File
@@ -1,72 +0,0 @@
//
// Created by martin on 19.11.21.
//
#include "BlendSpace1D.h"
#include <imgui.h>
#include <ozz/animation/runtime/blending_job.h>
#include "../SkinnedMesh.h"
BlendSpace1D::BlendSpace1D(AnimationController* animation_controller)
: AnimNode(animation_controller),
m_input_0(nullptr),
m_weight_0(0.f),
m_input_1(nullptr),
m_weight_1(0.f),
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_0.resize(num_soa_joints);
m_local_matrices_1.resize(num_soa_joints);
}
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,
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];
layers[0].transform = make_span(m_local_matrices_0);
layers[0].weight = (1.0f - m_normalized_weight);
layers[1].transform = make_span(m_local_matrices_1);
layers[1].weight = (m_normalized_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 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);
}
ImGui::Checkbox("Sync Inputs", &m_sync_inputs);
ImGui::Text("SyncTrack");
m_sync_track.DrawDebugUi();
}
-102
View File
@@ -1,102 +0,0 @@
//
// Created by martin on 19.11.21.
//
#ifndef ANIMTESTBED_BLENDSPACE1D_H
#define ANIMTESTBED_BLENDSPACE1D_H
#include "../AnimNode.h"
struct BlendSpace1D : public AnimNode {
BlendSpace1D(AnimationController* animation_controller);
virtual ~BlendSpace1D() {}
int m_num_inputs;
std::vector<float> m_input_weights;
std::vector<AnimNode*> m_inputs;
float m_weight;
bool m_sync_inputs;
AnimNode* m_input_0;
AnimNode* m_input_1;
float m_normalized_weight;
float m_weight_0;
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;
}
virtual void UpdateIsSynced(bool is_synced) override {
m_is_time_synced = is_synced;
if (m_sync_inputs) {
m_is_time_synced = true;
}
assert (m_input_weights.size() > 0);
assert (m_weight >= m_input_weights[0] && m_weight <= m_input_weights[m_input_weights.size() - 1]);
int prev_idx = 0;
for (int next_idx = 1; next_idx < m_input_weights.size(); next_idx++) {
if (m_input_weights[prev_idx] <= m_weight && m_input_weights[next_idx] >= m_weight) {
m_input_0 = m_inputs[prev_idx];
m_weight_0 = m_input_weights[prev_idx];
m_input_1 = m_inputs[next_idx];
m_weight_1 = m_input_weights[next_idx];
break;
}
prev_idx = next_idx;
}
m_input_0->UpdateIsSynced(m_is_time_synced);
m_input_1->UpdateIsSynced(m_is_time_synced);
m_normalized_weight = (m_weight - m_weight_0) / (m_weight_1 - m_weight_0);
}
virtual void UpdateSyncTrack() override {
if (m_is_time_synced) {
m_sync_track = SyncTrack::Blend(m_normalized_weight, m_input_0->m_sync_track, m_input_1->m_sync_track);
} else {
assert (false);
}
}
virtual void UpdateTime(float dt) {
if (m_is_time_synced) {
m_current_time = fmodf(m_current_time + dt, m_sync_track.m_duration);
float current_sync_time = m_sync_track.CalcSyncFromAbsTime(m_current_time);
m_input_0->UpdateTime(current_sync_time - m_input_0->m_current_time);
m_input_1->UpdateTime(current_sync_time - m_input_1->m_current_time);
} else {
m_current_time += dt;
m_input_0->UpdateTime(dt);
m_input_1->UpdateTime(dt);
}
}
virtual void Evaluate(
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++) {
input_nodes.push_back(m_inputs[i]);
}
};
virtual void DrawDebugUi();
};
#endif //ANIMTESTBED_BLENDSPACE1D_H
+8 -10
View File
@@ -5,15 +5,12 @@
#include "LockTranslationNode.h" #include "LockTranslationNode.h"
#include <imgui.h> #include <imgui.h>
#include "ozz/base/maths/soa_transform.h" #include "ozz/base/maths/soa_transform.h"
void LockTranslationNode::Evaluate( void LockTranslationNode::Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices, ozz::vector<ozz::math::SoaTransform>* local_matrices) {
ozz::math::Transform* root_transform) { m_input->Evaluate(local_matrices);
m_input->Evaluate(local_matrices, root_transform); ozz::math::SoaFloat3 translation = (*local_matrices)[m_locked_bone_index].translation;
ozz::math::SoaFloat3 translation =
(*local_matrices)[m_locked_bone_index].translation;
float x[4]; float x[4];
float y[4]; float y[4];
float z[4]; float z[4];
@@ -38,14 +35,13 @@ void LockTranslationNode::Evaluate(
translation.z = _mm_load_ps(z); translation.z = _mm_load_ps(z);
//translation = ozz::math::SoaFloat3::zero(); //translation = ozz::math::SoaFloat3::zero();
// ozz::math::SetX(translation, 0.f); // ozz::math::SetX(translation, 0.f);
// ozz::math::SetZ(translation, 0.f); // ozz::math::SetZ(translation, 0.f);
(*local_matrices)[m_locked_bone_index].translation = translation; (*local_matrices)[m_locked_bone_index].translation = translation;
} }
void LockTranslationNode::DrawDebugUi() { void LockTranslationNode::DrawDebugUi() {
const ozz::animation::Skeleton& skeleton = const ozz::animation::Skeleton& skeleton = m_animation_controller->m_skinned_mesh->m_skeleton;
m_animation_controller->m_skinned_mesh->m_skeleton;
ozz::span<const char* const> joint_names = skeleton.joint_names(); ozz::span<const char* const> joint_names = skeleton.joint_names();
const char* items[255] = {0}; const char* items[255] = {0};
@@ -58,4 +54,6 @@ void LockTranslationNode::DrawDebugUi() {
ImGui::Checkbox("Lock X", &m_lock_x); ImGui::Checkbox("Lock X", &m_lock_x);
ImGui::Checkbox("Lock Y", &m_lock_y); ImGui::Checkbox("Lock Y", &m_lock_y);
ImGui::Checkbox("Lock Z", &m_lock_z); ImGui::Checkbox("Lock Z", &m_lock_z);
} }
+1 -2
View File
@@ -39,8 +39,7 @@ struct LockTranslationNode : public AnimNode {
virtual void UpdateTime(float dt) { m_input->UpdateTime(dt); } virtual void UpdateTime(float dt) { m_input->UpdateTime(dt); }
virtual void Evaluate( virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices, ozz::vector<ozz::math::SoaTransform>* local_matrices) override;
ozz::math::Transform* root_transform = nullptr) override;
virtual void GetInputNodes( virtual void GetInputNodes(
std::vector<AnimNode*>& input_nodes) const override { std::vector<AnimNode*>& input_nodes) const override {
+5 -1
View File
@@ -14,7 +14,11 @@ void SpeedScaleNode::DrawDebugUi() {
// ensure m_time_scale is positive // ensure m_time_scale is positive
m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f); m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f);
ImGui::SliderFloat("Time Scale", &m_time_scale, 0.01f, 5.f); if (ImGui::SliderFloat("Time Scale", &m_time_scale, 0.01f, 2.f)) {
if (fabs(m_time_scale - 1.0) < 0.2) {
m_time_scale = 1.0;
}
}
// and back to the original negative or positive sign // and back to the original negative or positive sign
m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f); m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f);
} }
+4 -7
View File
@@ -26,8 +26,7 @@ struct SpeedScaleNode : public AnimNode {
virtual void UpdateSyncTrack() override { virtual void UpdateSyncTrack() override {
assert(fabs(m_time_scale) >= 0.01f); assert(fabs(m_time_scale) >= 0.01f);
m_sync_track = m_input_node->m_sync_track; m_sync_track = m_input_node->m_sync_track;
m_sync_track.m_duration = m_sync_track.m_duration =fabsf(m_input_node->m_sync_track.m_duration / m_time_scale);
fabsf(m_input_node->m_sync_track.m_duration / m_time_scale);
} }
virtual void UpdateTime(float dt) { virtual void UpdateTime(float dt) {
@@ -41,13 +40,11 @@ struct SpeedScaleNode : public AnimNode {
} }
virtual void Evaluate( virtual void Evaluate(
ozz::vector<ozz::math::SoaTransform>* local_matrices, ozz::vector<ozz::math::SoaTransform>* local_matrices) override {
ozz::math::Transform* root_transform) override { m_input_node->Evaluate(local_matrices);
m_input_node->Evaluate(local_matrices, root_transform);
}; };
virtual void GetInputNodes( virtual void GetInputNodes(std::vector<AnimNode*>& input_nodes) const override {
std::vector<AnimNode*>& input_nodes) const override {
input_nodes.push_back(m_input_node); input_nodes.push_back(m_input_node);
}; };
+6 -35
View File
@@ -12,7 +12,6 @@
#include "AnimNodes/AnimSamplerNode.h" #include "AnimNodes/AnimSamplerNode.h"
#include "AnimNodes/BlendNode.h" #include "AnimNodes/BlendNode.h"
#include "AnimNodes/BlendSpace1D.h"
#include "AnimNodes/LockTranslationNode.h" #include "AnimNodes/LockTranslationNode.h"
#include "AnimNodes/SpeedScaleNode.h" #include "AnimNodes/SpeedScaleNode.h"
#include "SkinnedMesh.h" #include "SkinnedMesh.h"
@@ -28,36 +27,14 @@ AnimationController::AnimationController(SkinnedMesh* skinned_mesh)
AnimSamplerNode* sampler_node0 = new AnimSamplerNode(this); AnimSamplerNode* sampler_node0 = new AnimSamplerNode(this);
sampler_node0->m_name = "AnimSampler0"; sampler_node0->m_name = "AnimSampler0";
sampler_node0->SetAnimation( sampler_node0->SetAnimation(skinned_mesh->m_animations[1], skinned_mesh->m_animation_sync_track[1]);
skinned_mesh->m_animations[1],
skinned_mesh->m_animation_sync_track[1]);
m_anim_nodes.push_back(sampler_node0); m_anim_nodes.push_back(sampler_node0);
AnimSamplerNode* sampler_node1 = new AnimSamplerNode(this); AnimSamplerNode* sampler_node1 = new AnimSamplerNode(this);
sampler_node1->m_name = "AnimSampler1"; sampler_node1->m_name = "AnimSampler1";
sampler_node1->SetAnimation( sampler_node1->SetAnimation(skinned_mesh->m_animations[2], skinned_mesh->m_animation_sync_track[2]);
skinned_mesh->m_animations[2],
skinned_mesh->m_animation_sync_track[2]);
m_anim_nodes.push_back(sampler_node1); 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]);
m_anim_nodes.push_back(sampler_node2);
BlendSpace1D* blend_space = new BlendSpace1D(this);
blend_space->m_name = "BlendSpace0";
blend_space->m_weight = 0.f;
blend_space->m_inputs.push_back(sampler_node0);
blend_space->m_input_weights.push_back(-1.f);
blend_space->m_inputs.push_back(sampler_node1);
blend_space->m_input_weights.push_back(0.f);
blend_space->m_inputs.push_back(sampler_node2);
blend_space->m_input_weights.push_back(1.f);
m_anim_nodes.push_back(blend_space);
SpeedScaleNode* speed_node = new SpeedScaleNode(this); SpeedScaleNode* speed_node = new SpeedScaleNode(this);
speed_node->m_name = "SpeedNode0"; speed_node->m_name = "SpeedNode0";
speed_node->m_input_node = sampler_node0; speed_node->m_input_node = sampler_node0;
@@ -72,7 +49,7 @@ AnimationController::AnimationController(SkinnedMesh* skinned_mesh)
SpeedScaleNode* speed_node1 = new SpeedScaleNode(this); SpeedScaleNode* speed_node1 = new SpeedScaleNode(this);
speed_node1->m_name = "SpeedNode1"; speed_node1->m_name = "SpeedNode1";
speed_node1->m_input_node = blend_space; speed_node1->m_input_node = blend_node;
m_anim_nodes.push_back(speed_node1); m_anim_nodes.push_back(speed_node1);
LockTranslationNode* lock_node = new LockTranslationNode(this); LockTranslationNode* lock_node = new LockTranslationNode(this);
@@ -123,17 +100,13 @@ void AnimationController::UpdateOrderedNodes() {
} }
void AnimationController::UpdateTime(float dt) { void AnimationController::UpdateTime(float dt) {
if (m_output_node == nullptr) { if (m_paused || m_output_node == nullptr) {
return; return;
} }
// Mark all nodes that evaluate time using sync tracks. // Mark all nodes that evaluate time using sync tracks.
m_output_node->UpdateIsSynced(false); m_output_node->UpdateIsSynced(false);
if (m_paused) {
return;
}
// For all synced nodes calculate their current sync track durations // For all synced nodes calculate their current sync track durations
for (int i = m_ordered_nodes.size() - 1; i >= 0; i--) { for (int i = m_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_ordered_nodes[i]; AnimNode* node = m_ordered_nodes[i];
@@ -151,9 +124,7 @@ void AnimationController::Evaluate() {
return; return;
} }
m_output_node->Evaluate( m_output_node->Evaluate(&m_skinned_mesh->m_local_matrices);
&m_skinned_mesh->m_local_matrices,
m_calc_root_transform ? &m_root_transform : nullptr);
}; };
void AnimationController::DrawDebugUi() { void AnimationController::DrawDebugUi() {
@@ -195,7 +166,7 @@ void AnimationController::DrawDebugUi() {
ImGui::End(); ImGui::End();
} }
ImGui::Text("Node States"); ImGui::Text ("Node States");
ImGui::Columns(4, "Node States"); // 4-ways, with border ImGui::Columns(4, "Node States"); // 4-ways, with border
ImGui::Separator(); ImGui::Separator();
-3
View File
@@ -9,7 +9,6 @@
#include <ozz/animation/runtime/sampling_job.h> #include <ozz/animation/runtime/sampling_job.h>
#include <ozz/base/containers/vector.h> #include <ozz/base/containers/vector.h>
#include <ozz/base/maths/soa_transform.h> #include <ozz/base/maths/soa_transform.h>
#include <ozz/base/maths/transform.h>
#include <ozz/base/maths/vec_float.h> #include <ozz/base/maths/vec_float.h>
struct SkinnedMesh; struct SkinnedMesh;
@@ -34,8 +33,6 @@ struct AnimationController {
float m_current_time; float m_current_time;
bool m_paused; bool m_paused;
bool m_calc_root_transform;
ozz::math::Transform m_root_transform;
SkinnedMesh* m_skinned_mesh = nullptr; SkinnedMesh* m_skinned_mesh = nullptr;
+3 -3
View File
@@ -42,8 +42,8 @@ inline void Camera_Init(Camera* camera) {
camera->pitch = 10 * M_PI / 180.0f; camera->pitch = 10 * M_PI / 180.0f;
memcpy(&camera->mtxView, &mtx_identity, sizeof(camera->mtxView)); memcpy(&camera->mtxView, &mtx_identity, sizeof(camera->mtxView));
Camera_CalcToMatrix(camera, &camera->mtxView); Camera_CalcToMatrix(camera, &camera->mtxView[0]);
Camera_CalcFromMatrix(camera, &camera->mtxView); Camera_CalcFromMatrix(camera, &camera->mtxView[0]);
} }
void Camera_CalcFromMatrix(Camera* camera, float* mat) { void Camera_CalcFromMatrix(Camera* camera, float* mat) {
@@ -153,6 +153,6 @@ inline void Camera_Update(
camera->vel[i] = camera->vel[i] * 0.1; camera->vel[i] = camera->vel[i] * 0.1;
} }
Camera_CalcToMatrix(camera, &camera->mtxView); Camera_CalcToMatrix(camera, &camera->mtxView[0]);
} }
} }
+10441
View File
File diff suppressed because it is too large Load Diff
+21 -5
View File
@@ -17,9 +17,10 @@
#include "Camera.h" #include "Camera.h"
#include "SkinnedMesh.h" #include "SkinnedMesh.h"
#include "GLFW/glfw3.h" #include "GLFW/glfw3.h"
#include "embedded_fonts.h"
const int Width = 1024; const int Width = 2420;
const int Height = 768; const int Height = 1480;
const int MaxVertices = (1 << 16); const int MaxVertices = (1 << 16);
const int MaxIndices = MaxVertices * 3; const int MaxIndices = MaxVertices * 3;
@@ -233,7 +234,6 @@ int main() {
AnimationController animation_controller (&skinned_mesh); AnimationController animation_controller (&skinned_mesh);
// state.ozz = std::make_unique<ozz_t>();
state.time.factor = 1.0f; state.time.factor = 1.0f;
Camera_Init(&state.camera); Camera_Init(&state.camera);
@@ -242,8 +242,24 @@ int main() {
ImGui::CreateContext(); ImGui::CreateContext();
ImGui::StyleColorsDark(); ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.IniFilename = nullptr; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.Fonts->AddFontDefault(); io.IniFilename = "ATPImgui.ini";
ImGui::GetStyle().ScaleAllSizes(2.0);
//io.Fonts->AddFontDefault();
ImFontConfig font_config;
font_config.OversampleH = 4;
font_config.OversampleV = 4;
font_config.GlyphExtraSpacing.x = 1.0f;
io.Fonts->AddFontFromMemoryCompressedTTF(
// roboto_medium_ttf_compressed_data,
// roboto_medium_ttf_compressed_size,
droid_sans_ttf_compressed_data,
droid_sans_ttf_compressed_size,
28,
&font_config);
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
-171
View File
@@ -1,171 +0,0 @@
//
// Created by martin on 21.11.21.
//
#include "AnimNodes/AnimSamplerNode.h"
#define OZZ_INCLUDE_PRIVATE_HEADER
#include "../src/animation/runtime/animation_keyframe.h"
#include "catch.hpp"
void get_bone_transform(
int i_bone_idx,
const ozz::vector<ozz::math::SoaTransform>& i_local_matrices,
ozz::math::Transform& o_transform) {
int matrix_index = i_bone_idx / 4;
short simd_component = i_bone_idx % 4;
o_transform.translation.x =
i_local_matrices[matrix_index].translation.x[simd_component];
o_transform.translation.y =
i_local_matrices[matrix_index].translation.y[simd_component];
o_transform.translation.z =
i_local_matrices[matrix_index].translation.z[simd_component];
}
void sample_bone_transform(
float ratio,
int i_bone_idx,
const ozz::animation::Animation* i_animation,
ozz::animation::SamplingCache& io_cache,
ozz::vector<ozz::math::SoaTransform>& io_local_matrices,
ozz::math::Transform& o_transform) {
ozz::animation::SamplingJob sampling_job;
sampling_job.animation = i_animation;
sampling_job.cache = &io_cache;
sampling_job.ratio = ratio;
sampling_job.output = make_span(io_local_matrices);
if (!sampling_job.Run()) {
ozz::log::Err() << "Error sampling animation." << std::endl;
}
get_bone_transform(i_bone_idx, io_local_matrices, o_transform);
}
void calc_bone_translation(
float ratio,
int i_bone_idx,
const ozz::animation::Animation* i_animation,
ozz::math::Transform& o_transform) {
int key_idx = 0;
const ozz::span<const ozz::animation::Float3Key>& translations =
i_animation->translations();
int key_idx_prev = 0;
int key_idx_next = 0;
while (key_idx < translations.size()) {
key_idx_next = key_idx;
if (translations[key_idx].track == i_bone_idx) {
if (translations[key_idx].ratio < ratio) {
key_idx_prev = key_idx;
} else {
break;
}
}
key_idx++;
}
const ozz::animation::Float3Key& key_prev = translations[key_idx_prev];
const ozz::animation::Float3Key& key_next = translations[key_idx_next];
float r = 0.f;
if (key_prev.ratio != key_next.ratio) {
r = (ratio - key_prev.ratio) / (key_next.ratio - key_prev.ratio);
}
o_transform.translation.x =
ozz::math::HalfToFloat(key_prev.value[0])
+ r
* (ozz::math::HalfToFloat(key_next.value[0])
- ozz::math::HalfToFloat(key_prev.value[0]));
o_transform.translation.y =
ozz::math::HalfToFloat(key_prev.value[1])
+ r
* (ozz::math::HalfToFloat(key_next.value[1])
- ozz::math::HalfToFloat(key_prev.value[1]));
o_transform.translation.z =
ozz::math::HalfToFloat(key_prev.value[2])
+ r
* (ozz::math::HalfToFloat(key_next.value[2])
- ozz::math::HalfToFloat(key_prev.value[2]));
}
TEST_CASE("Sample single bone channel", "[AnimSamplerNode]") {
SkinnedMesh skinned_mesh;
skinned_mesh.LoadSkeleton("../media/MixamoYBot-skeleton.ozz");
skinned_mesh.LoadAnimation("../media/Walking-loop.ozz");
ozz::animation::Animation* animation = skinned_mesh.m_animations[0];
const int num_soa_joints = skinned_mesh.m_skeleton.num_soa_joints();
const int num_joints = skinned_mesh.m_skeleton.num_joints();
ozz::vector<ozz::math::SoaTransform> local_matrices;
ozz::animation::SamplingCache sampling_cache;
local_matrices.resize(num_soa_joints);
sampling_cache.Resize(num_joints);
// sample at ratio 0.
float ratio = 0.f;
ozz::animation::SamplingJob sampling_job;
sampling_job.animation = animation;
sampling_job.cache = &sampling_cache;
sampling_job.ratio = ratio;
sampling_job.output = make_span(local_matrices);
if (!sampling_job.Run()) {
ozz::log::Err() << "Error sampling animation." << std::endl;
}
ozz::math::Transform root_transform_0;
sample_bone_transform(
0.f,
0,
animation,
sampling_cache,
local_matrices,
root_transform_0);
int n_samples = 20;
for (int i = 0; i <= n_samples; i++) {
float ratio = i * 1.f / n_samples;
ozz::math::Transform sampled_root_transform;
sample_bone_transform(
i * 1.f / n_samples,
0,
animation,
sampling_cache,
local_matrices,
sampled_root_transform);
ozz::math::Transform calculated_root_transform;
calc_bone_translation(ratio, 0, animation, calculated_root_transform);
std::cout << "ratio: " << ratio << "\t" << "err: " << ozz::math::Length(sampled_root_transform.translation - calculated_root_transform.translation) << std::endl;
}
ozz::math::Transform root_transform_1;
calc_bone_translation(0.f, 0, animation, root_transform_0);
calc_bone_translation(1.f, 0, animation, root_transform_1);
}
TEST_CASE("Root Bone Sampling", "[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);
test_node.SetAnimation(skinned_mesh.m_animations[0], SyncTrack());
test_node.Reset();
test_node.UpdateTime(0.2f);
ozz::math::Transform root_transform;
// test_node.Evaluate(&skinned_mesh.m_local_matrices, &root_transform);
}