diff --git a/synced_animation_graph.cpp b/synced_animation_graph.cpp index 2eed8b6..e9f8f6f 100644 --- a/synced_animation_graph.cpp +++ b/synced_animation_graph.cpp @@ -168,15 +168,15 @@ void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) { return; } - graph_root_node->activate_inputs(); - graph_root_node->calculate_sync_track(); + graph_root_node->activate_inputs(Vector>()); + graph_root_node->calculate_sync_track(Vector>()); graph_root_node->update_time(p_delta); - graph_root_node->evaluate(graph_context, Vector(), graph_output); + graph_root_node->evaluate(graph_context, Vector(), graph_output); _apply_animation_data(graph_output); } -void SyncedAnimationGraph::_apply_animation_data(const AnimationData& output_data) const { +void SyncedAnimationGraph::_apply_animation_data(const AnimationData &output_data) const { for (const KeyValue &K : output_data.track_values) { const AnimationData::TrackValue *track_value = K.value; switch (track_value->type) { diff --git a/synced_animation_graph.h b/synced_animation_graph.h index cea3c9f..406da7e 100644 --- a/synced_animation_graph.h +++ b/synced_animation_graph.h @@ -32,7 +32,7 @@ protected: public: void _process_graph(double p_delta, bool p_update_only = false); - void _apply_animation_data(const AnimationData& output_data) const; + void _apply_animation_data(const AnimationData &output_data) const; void set_active(bool p_active); bool is_active() const; @@ -55,6 +55,10 @@ public: void set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode); AnimationMixer::AnimationCallbackModeDiscrete get_callback_mode_discrete() const; + GraphEvaluationContext &get_context() { + return graph_context; + } + SyncedAnimationGraph(); private: diff --git a/synced_animation_node.h b/synced_animation_node.h index d9786d8..f8dd178 100644 --- a/synced_animation_node.h +++ b/synced_animation_node.h @@ -7,11 +7,6 @@ #include -struct GraphEvaluationContext { - AnimationPlayer *animation_player = nullptr; - Skeleton3D *skeleton_3d = nullptr; -}; - struct AnimationData { enum TrackType : uint8_t { TYPE_VALUE, // Set a value in a property, can be interpolated. @@ -78,6 +73,11 @@ protected: struct SyncTrack { }; +struct GraphEvaluationContext { + AnimationPlayer *animation_player = nullptr; + Skeleton3D *skeleton_3d = nullptr; +}; + class SyncedAnimationNode : public Resource { GDCLASS(SyncedAnimationNode, Resource); @@ -90,6 +90,7 @@ public: double sync_position = 0.0; double delta = 0.0; double sync_delta = 0.0; + bool is_synced = false; Animation::LoopMode loop_mode = Animation::LOOP_NONE; SyncTrack sync_track; @@ -109,7 +110,7 @@ public: virtual void initialize(GraphEvaluationContext &context) {} virtual void activate_inputs(Vector> input_nodes) { // By default, all inputs nodes are activated. - for (Ref node: input_nodes) { + for (const Ref& node : input_nodes) { node->active = true; } } @@ -246,7 +247,7 @@ struct BlendTreeBuilder { // Map connected subtrees HashSet old_indices = input_subtree_node_indices; input_subtree_node_indices.clear(); - for (int old_index: old_indices) { + for (int old_index : old_indices) { input_subtree_node_indices.insert(node_index_mapping.find(old_index)); } } @@ -298,7 +299,7 @@ struct BlendTreeBuilder { } nodes = sorted_nodes; - for (NodeConnectionInfo& connection_info: node_connection_info) { + for (NodeConnectionInfo &connection_info : node_connection_info) { connection_info.apply_node_mapping(sorted_node_indices); } } @@ -314,7 +315,9 @@ struct BlendTreeBuilder { void sort_nodes_recursive(int node_index, LocalVector &result) { for (int input_node_index : node_connection_info[node_index].connected_child_node_index_at_port) { - sort_nodes_recursive(input_node_index, result); + if (input_node_index >= 0) { + sort_nodes_recursive(input_node_index, result); + } } result.push_back(node_index); } @@ -406,7 +409,7 @@ class SyncedBlendTree : public SyncedAnimationNode { struct NodeRuntimeData { Vector> input_nodes; - Vector input_data; + Vector input_data; AnimationData *output_data = nullptr; }; LocalVector _node_runtime_data; @@ -422,7 +425,7 @@ class SyncedBlendTree : public SyncedAnimationNode { // Add nodes and allocate runtime data for (int i = 0; i < tree_builder.nodes.size(); i++) { - Ref node = tree_builder.nodes[i]; + const Ref node = tree_builder.nodes[i]; nodes.push_back(node); NodeRuntimeData node_runtime_data; @@ -437,10 +440,11 @@ class SyncedBlendTree : public SyncedAnimationNode { // Populate runtime data (only now is this.nodes populated to retrieve the nodes) for (int i = 0; i < nodes.size(); i++) { Ref node = nodes[i]; - NodeRuntimeData& node_runtime_data = _node_runtime_data[i]; + NodeRuntimeData &node_runtime_data = _node_runtime_data[i]; for (int port_index = 0; port_index < node->get_node_input_count(); port_index++) { - node_runtime_data.input_nodes.push_back(nodes[tree_builder.node_connection_info[i].connected_child_node_index_at_port[port_index]]); + const int connected_node_index = tree_builder.node_connection_info[i].connected_child_node_index_at_port[port_index]; + node_runtime_data.input_nodes.push_back(nodes[connected_node_index]); } } @@ -448,18 +452,11 @@ class SyncedBlendTree : public SyncedAnimationNode { } public: - SyncedBlendTree() { - Ref output_node; - output_node.instantiate(); - output_node->name = "Output"; - nodes.push_back(output_node); + Ref get_output_node() const { + return tree_builder.nodes[0]; } - Ref get_output_node() { - return nodes[0]; - } - - int get_node_index(const Ref node) { + int get_node_index(const Ref& node) const { for (int i = 0; i < nodes.size(); i++) { if (nodes[i] == node) { return i; @@ -489,6 +486,8 @@ public: // overrides from SyncedAnimationNode void initialize(GraphEvaluationContext &context) override { + setup_tree(); + for (Ref node : nodes) { node->initialize(context); } @@ -503,9 +502,7 @@ public: continue; } - NodeRuntimeData& node_runtime_data = _node_runtime_data[i]; - node_runtime_data.output_data = memnew(AnimationData); - + const NodeRuntimeData &node_runtime_data = _node_runtime_data[i]; node->activate_inputs(node_runtime_data.input_nodes); } } @@ -518,28 +515,53 @@ public: continue; } - NodeRuntimeData& node_runtime_data = _node_runtime_data[i]; - node_runtime_data.output_data = memnew(AnimationData); + const NodeRuntimeData &node_runtime_data = _node_runtime_data[i]; node->calculate_sync_track(node_runtime_data.input_nodes); } } void update_time(double p_delta) override { - for (int i = 0; i < nodes.size(); i++) { + nodes[0]->node_time_info.delta = p_delta; + nodes[0]->node_time_info.position += p_delta; + + for (int i = 1; i < nodes.size(); i++) { Ref node = nodes[i]; if (!node->active) { continue; } - NodeRuntimeData& node_runtime_data = _node_runtime_data[i]; - node_runtime_data.output_data = memnew(AnimationData); + Ref node_parent = nodes[tree_builder.node_connection_info[i].parent_node_index]; - node->update_time(node_runtime_data.input_nodes); + if (node->node_time_info.is_synced) { + node->update_time(node_parent->node_time_info.position); + } else { + node->update_time(node_parent->node_time_info.delta); + } } } void evaluate(GraphEvaluationContext &context, const Vector &input_datas, AnimationData &output_data) override { + for (int i = nodes.size() - 1; i > 0; i--) { + const Ref& node = nodes[i]; + + if (!node->active) { + continue; + } + + NodeRuntimeData &node_runtime_data = _node_runtime_data[i]; + + if (i == 1) { + node_runtime_data.output_data = &output_data; + } else { + node_runtime_data.output_data = memnew(AnimationData); + } + node->evaluate(context, node_runtime_data.input_data, *node_runtime_data.output_data); + + for (int child_index : tree_builder.node_connection_info[i].connected_child_node_index_at_port) { + memfree(_node_runtime_data[child_index].output_data); + } + } } }; diff --git a/tests/test_synced_animation_graph.h b/tests/test_synced_animation_graph.h index e4fd036..6278626 100644 --- a/tests/test_synced_animation_graph.h +++ b/tests/test_synced_animation_graph.h @@ -2,7 +2,6 @@ #include "../synced_animation_graph.h" #include "scene/main/window.h" -#include "servers/rendering/rendering_server_default.h" #include "tests/test_macros.h" @@ -85,8 +84,8 @@ TEST_CASE("[SyncedAnimationGraph] TestBlendTreeConstruction") { // Tree // Sampler0 -\ - // Sampler1 -+- Blend0 -\ - // Sampler2 ------------+ Blend1 - Output + // Sampler1 -+ Blend0 -\ + // Sampler2 -----------+ Blend1 - Output CHECK(tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0")); @@ -124,36 +123,17 @@ TEST_CASE("[SyncedAnimationGraph] TestBlendTreeConstruction") { CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(4)); CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(5)); - print_line("-- Unsorted Nodes:"); - for (unsigned int i = 0; i < tree_constructor.nodes.size(); i++) { - print_line(vformat("%d: node %10s", i, tree_constructor.nodes[i]->name)); - tree_constructor.node_connection_info[i]._print_subtree(); - } - - LocalVector mapping = tree_constructor.get_sorted_node_indices(); - for (unsigned int i = 0; i < mapping.size(); i++) { - print_line(vformat("%2d -> %2d", i, mapping[i])); - } - print_line(vformat("node %d is at index %d", 4, mapping.find(4))); - tree_constructor.sort_nodes_and_references(); - print_line("-- Sorted Nodes"); - for (unsigned int i = 0; i < tree_constructor.nodes.size(); i++) { - print_line(vformat("%d: node %10s", i, tree_constructor.nodes[i]->name)); - tree_constructor.node_connection_info[i]._print_subtree(); - } - // Check that for node i all input nodes have a node index j > i. for (unsigned int i = 0; i < tree_constructor.nodes.size(); i++) { - for (int input_index: tree_constructor.node_connection_info[i].input_subtree_node_indices) { + for (int input_index : tree_constructor.node_connection_info[i].input_subtree_node_indices) { CHECK(input_index > i); } } - } -TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SimpleAnimationSamplerTest" * doctest::skip(true)) { +TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph with an AnimationSampler as root node") { Ref animation_sampler_node; animation_sampler_node.instantiate(); animation_sampler_node->animation_name = "animation_library/TestAnimation"; @@ -175,15 +155,18 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph CHECK(hip_bone_position.z == doctest::Approx(0.03)); } -// Currently disabled! -TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SimpleBlendTreeTest" * doctest::skip(true)) { +TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree] BlendTree with a AnimationSamplerNode connected to the output") { Ref synced_blend_tree_node; synced_blend_tree_node.instantiate(); Ref animation_sampler_node; animation_sampler_node.instantiate(); animation_sampler_node->animation_name = "animation_library/TestAnimation"; + synced_blend_tree_node->add_node(animation_sampler_node); + REQUIRE(synced_blend_tree_node->add_connection(animation_sampler_node, synced_blend_tree_node->get_output_node(), "Input")); + + synced_blend_tree_node->initialize(synced_animation_graph->get_context()); synced_animation_graph->set_graph_root_node(synced_blend_tree_node);