From 20331d0765d07b32208bb421fcc3052fb4411c60 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Wed, 25 Feb 2026 18:33:44 +0100 Subject: [PATCH] Properly deactivate nodes to ensure proper evaluation after changed node connections. --- blendalot_animation_node.h | 20 +++++++++++--- tests/test_blendalot_animgraph.h | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/blendalot_animation_node.h b/blendalot_animation_node.h index 04d5310..3672d3f 100644 --- a/blendalot_animation_node.h +++ b/blendalot_animation_node.h @@ -767,14 +767,23 @@ public: sort_nodes(); setup_runtime_data(); - for (const Ref &node : tree_graph.nodes) { + const HashSet &output_subtree = tree_graph.node_connection_info[0].input_subtree_node_indices; + + for (int i = 0; i < tree_graph.nodes.size(); i++) { + const Ref &node = tree_graph.nodes[i]; + + // Initialize, but skip validation of nodes that are not part of the active tree. + if (!output_subtree.has(i)) { + node->initialize(context); + continue; + } + if (!node->initialize(context)) { return false; } - } - // All inputs must have a connected node. - for (const NodeRuntimeData &node_runtime_data : _node_runtime_data) { + const NodeRuntimeData &node_runtime_data = _node_runtime_data[i]; + for (const Ref &input_node : node_runtime_data.input_nodes) { if (!input_node.is_valid()) { return false; @@ -887,6 +896,9 @@ public: for (const int child_index : tree_graph.node_connection_info[i].connected_child_node_index_at_port) { context.animation_data_allocator.free(_node_runtime_data[child_index].output_data); } + + // Node must be deactivated. It'll be activated when actually used next time. + node->active = false; } } diff --git a/tests/test_blendalot_animgraph.h b/tests/test_blendalot_animgraph.h index a06a52b..e78377d 100644 --- a/tests/test_blendalot_animgraph.h +++ b/tests/test_blendalot_animgraph.h @@ -15,6 +15,7 @@ struct BlendTreeFixture { Ref test_animation_a; Ref test_animation_b; + Ref test_animation_c; Ref test_animation_sync_a; Ref test_animation_sync_b; @@ -80,6 +81,16 @@ struct BlendTreeFixture { animation_library->add_animation("TestAnimationB", test_animation_b); + test_animation_c = memnew(Animation); + track_index = test_animation_c->add_track(Animation::TYPE_POSITION_3D); + CHECK(track_index == 0); + test_animation_c->track_insert_key(track_index, 0.0, Vector3(0., 0., 0.)); + test_animation_c->track_insert_key(track_index, 3.0, Vector3(2., 4., 6.)); + test_animation_c->track_set_path(track_index, NodePath(vformat("%s:%s", skeleton_node->get_path().get_concatenated_names(), "Hips"))); + test_animation_c->set_loop_mode(Animation::LOOP_LINEAR); + + animation_library->add_animation("TestAnimationC", test_animation_c); + test_animation_sync_a = memnew(Animation); track_index = test_animation_sync_a->add_track(Animation::TYPE_POSITION_3D); CHECK(track_index == 0); @@ -597,6 +608,41 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][Chan CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.has(animation_sampler_node_c_index_post_remove)); } } + + SUBCASE("Check evaluation of graph with modified connections") { + Ref blend_tree_node; + blend_tree_node.instantiate(); + blend_tree_node->add_node(animation_sampler_node_a); + blend_tree_node->add_node(animation_sampler_node_b); + blend_tree_node->add_node(animation_sampler_node_c); + blend_tree_node->add_node(blend2_node_a); + + animation_graph->set_root_animation_node(blend_tree_node); + GraphEvaluationContext &graph_context = animation_graph->get_context(); + CHECK(blend_tree_node->initialize(graph_context) == false); + + REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(animation_sampler_node_a, blend_tree_node->get_output_node(), "Output")); + CHECK(blend_tree_node->initialize(graph_context) == true); + + AnimationData *graph_output = graph_context.animation_data_allocator.allocate(); + blend_tree_node->activate_inputs(Vector>()); + blend_tree_node->calculate_sync_track(Vector>()); + blend_tree_node->update_time(0.825); + blend_tree_node->evaluate(graph_context, LocalVector(), *graph_output); + + REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(animation_sampler_node_b, blend2_node_a, "Input0")); + REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(animation_sampler_node_c, blend2_node_a, "Input1")); + + REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->remove_connection(animation_sampler_node_a, blend_tree_node->get_output_node(), "Output")); + + REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(blend2_node_a, blend_tree_node->get_output_node(), "Output")); + CHECK(blend_tree_node->initialize(graph_context) == true); + + blend_tree_node->activate_inputs(Vector>()); + blend_tree_node->calculate_sync_track(Vector>()); + blend_tree_node->update_time(0.825); + blend_tree_node->evaluate(graph_context, LocalVector(), *graph_output); + } } TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][EmbeddedBlendTree] BlendTree with an embedded BlendTree subgraph") {