AnimGraphEvalTests now properly evaluates.

This commit is contained in:
Martin Felis 2025-03-17 22:23:12 +01:00
parent 1870a9d214
commit b4eda31242
4 changed files with 120 additions and 31 deletions

View File

@ -48,6 +48,13 @@ struct AnimGraphBlendTree : public AnimNode {
// AnimNode overrides
bool Init(AnimGraphContext& context) override;
/// Determines which nodes in the BlendTree are active.
///
/// Note: this does not use the provided input_connections, instead it marks
/// all nodes directly connected to the BlendTree outputs as active and then
/// propagates the node state throught the tree. For this each active node's
/// AnimNode::MarkActiveInputs() gets called.
void MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack(

View File

@ -33,12 +33,12 @@ struct Blend2Node : public AnimNode {
}
if (input.m_target_socket_name == "Input0" && *i_blend_weight < 0.999) {
input_node->m_state = AnimNodeEvalState::Activated;
input_node->Activate(m_tick_number);
continue;
}
if (input.m_target_socket_name == "Input1" && *i_blend_weight > 0.001) {
input_node->m_state = AnimNodeEvalState::Activated;
input_node->Activate(m_tick_number);
continue;
}
}
@ -59,6 +59,8 @@ struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
RegisterProperty("Sync", &node->m_sync_blend);
}
virtual ~NodeDescriptor() = default;
void UpdateFlags() override {
Socket* weight_input_socket = FindSocket("Weight", m_inputs);
assert(weight_input_socket != nullptr);
@ -104,6 +106,8 @@ struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
RegisterOutput("Output", &node->o_output);
}
virtual ~NodeDescriptor() = default;
};
//
@ -132,6 +136,8 @@ struct NodeDescriptor<AnimSamplerNode> : public NodeDescriptorBase {
RegisterProperty("Filename", &node->m_filename);
}
virtual ~NodeDescriptor() = default;
};
//
@ -159,6 +165,8 @@ struct NodeDescriptor<LockTranslationNode> : public NodeDescriptorBase {
RegisterProperty("LockAxisY", &node->m_lock_y);
RegisterProperty("LockAxisZ", &node->m_lock_z);
}
virtual ~NodeDescriptor() = default;
};
//
@ -177,6 +185,8 @@ struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
RegisterOutput("ScalarOutput", &node->o_value);
RegisterProperty("ScalarValue", &node->value);
}
virtual ~NodeDescriptor() = default;
};
//
@ -202,6 +212,8 @@ struct NodeDescriptor<MathAddNode> : public NodeDescriptorBase {
RegisterInput("Input1", &node->i_input1);
RegisterOutput("Output", &node->o_output);
}
virtual ~NodeDescriptor() = default;
};
//
@ -232,6 +244,8 @@ struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
RegisterInput("Input2", &node->i_input2);
RegisterOutput("Output", &node->o_output);
}
virtual ~NodeDescriptor() = default;
};
AnimNode* AnimNodeFactory(const std::string& name);

View File

@ -45,8 +45,7 @@ struct AnimNode {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr) {
input_node->m_tick_number = m_tick_number;
input_node->m_state = AnimNodeEvalState::Activated;
input_node->Activate(m_tick_number);
}
}
}
@ -64,13 +63,18 @@ struct AnimNode {
}
}
virtual void UpdateTime(float time_last, float time_now) {
virtual void UpdateTime(const float time_last, const float time_now) {
m_time_last = time_last;
m_time_now = time_now;
m_state = AnimNodeEvalState::TimeUpdated;
}
virtual void Evaluate(AnimGraphContext& context){};
virtual void Evaluate(AnimGraphContext& context) {};
void Activate(const int tick_number) {
m_tick_number = tick_number;
m_state = AnimNodeEvalState::Activated;
}
};
#endif //ANIMTESTBED_ANIMNODE_H

View File

@ -4,7 +4,6 @@
#include "AnimGraph/AnimGraphBlendTree.h"
#include "AnimGraph/AnimGraphResource.h"
#include "AnimGraphEditor/AnimGraphEditor.h"
#include "catch.hpp"
#include "ozz/animation/offline/animation_builder.h"
#include "ozz/animation/offline/raw_animation.h"
@ -14,7 +13,6 @@
#include "ozz/animation/runtime/sampling_job.h"
#include "ozz/animation/runtime/skeleton.h"
#include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
#include "ozz/base/maths/soa_transform.h"
@ -66,7 +64,7 @@ struct SimpleAnimFixture {
bone0_translations.push_back(translation_key);
translation_key.time = 1.f;
translation_key.value = ozz::math::Float3(1.f, 0.f, 9.f);
translation_key.value = ozz::math::Float3(1.f, 0.f, 0.f);
bone0_translations.push_back(translation_key);
bone0_track.translations = bone0_translations;
@ -214,31 +212,97 @@ TEST_CASE_METHOD(
AnimData graph_anim_output;
graph_anim_output.m_local_matrices.resize(skeleton->num_joints());
blend_tree.SetOutput("GraphOutput", &graph_anim_output);
blend_tree.SetOutput("Output", &graph_anim_output);
// Evaluate graph
graph_float_input = 0.1f;
WHEN("Blend Weight == 0.") {
// Evaluate graph
graph_float_input = 0.f;
blend_tree.StartUpdateTick();
blend_tree.MarkActiveInputs(blend_tree.GetGraphOutputConnections());
blend_tree.StartUpdateTick();
blend_tree.MarkActiveInputs({});
CHECK(
blend_tree.m_nodes[trans_x_node_index]->m_state
== AnimNodeEvalState::Activated);
CHECK(
blend_tree.m_nodes[trans_y_node_index]->m_state
== AnimNodeEvalState::Activated);
CHECK(
blend_tree.m_nodes[blend_node_index]->m_state
== AnimNodeEvalState::Activated);
THEN("Only Blend2 and first input of Blend2 node is active.") {
CHECK(
blend_tree.m_nodes[trans_x_node_index]->m_state
== AnimNodeEvalState::Activated);
CHECK(
blend_tree.m_nodes[trans_y_node_index]->m_state
== AnimNodeEvalState::Deactivated);
CHECK(
blend_tree.m_nodes[blend_node_index]->m_state
== AnimNodeEvalState::Activated);
}
blend_tree.UpdateTime(0.0, 0.5f);
blend_tree.Evaluate(graph_context);
blend_tree.UpdateTime(0.0, 0.5f);
blend_tree.Evaluate(graph_context);
CHECK(
graph_anim_output.m_local_matrices[0].translation.x[0]
== Approx(0.5).margin(0.1));
CHECK(
graph_anim_output.m_local_matrices[0].translation.y[0]
== Approx(0.05).margin(0.01));
CHECK(
graph_anim_output.m_local_matrices[0].translation.x[0]
== Approx(0.5).margin(0.01));
CHECK(
graph_anim_output.m_local_matrices[0].translation.y[0]
== Approx(0.0).margin(0.01));
}
WHEN("Blend Weight 0.1") {
// Evaluate graph
graph_float_input = 0.1f;
blend_tree.StartUpdateTick();
blend_tree.MarkActiveInputs({});
THEN("All nodes are active.") {
CHECK(
blend_tree.m_nodes[trans_x_node_index]->m_state
== AnimNodeEvalState::Activated);
CHECK(
blend_tree.m_nodes[trans_y_node_index]->m_state
== AnimNodeEvalState::Activated);
CHECK(
blend_tree.m_nodes[blend_node_index]->m_state
== AnimNodeEvalState::Activated);
}
blend_tree.UpdateTime(0.0, 0.5f);
blend_tree.Evaluate(graph_context);
CHECK(
graph_anim_output.m_local_matrices[0].translation.x[0]
== Approx(0.45).margin(0.01));
CHECK(
graph_anim_output.m_local_matrices[0].translation.y[0]
== Approx(0.05).margin(0.01));
}
WHEN("Blend Weight 1.") {
// Evaluate graph
graph_float_input = 1.f;
blend_tree.StartUpdateTick();
blend_tree.MarkActiveInputs({});
THEN("Only Blend2 and second input of Blend2 are active.") {
CHECK(
blend_tree.m_nodes[trans_x_node_index]->m_state
== AnimNodeEvalState::Deactivated);
CHECK(
blend_tree.m_nodes[trans_y_node_index]->m_state
== AnimNodeEvalState::Activated);
CHECK(
blend_tree.m_nodes[blend_node_index]->m_state
== AnimNodeEvalState::Activated);
}
blend_tree.UpdateTime(0.0, 0.5f);
blend_tree.Evaluate(graph_context);
CHECK(
graph_anim_output.m_local_matrices[0].translation.x[0]
== Approx(0.).margin(0.01));
CHECK(
graph_anim_output.m_local_matrices[0].translation.y[0]
== Approx(0.5).margin(0.01));
}
delete blend_tree_resource;
}