diff --git a/blendalot_animation_node.h b/blendalot_animation_node.h index f7a6ef3..e3d07b3 100644 --- a/blendalot_animation_node.h +++ b/blendalot_animation_node.h @@ -257,8 +257,7 @@ public: double sync_position = 0.0; bool is_synced = false; - // TODO: 2026-02-17: how to initialize loop_mode e.g. for a BlendTree or a StateMachine? - Animation::LoopMode loop_mode = Animation::LOOP_LINEAR; + Animation::LoopMode loop_mode = Animation::LOOP_NONE; SyncTrack sync_track; }; NodeTimeInfo node_time_info; @@ -753,6 +752,8 @@ public: } tree_graph.nodes[0]->active = true; + tree_graph.nodes[0]->node_time_info.is_synced = node_time_info.is_synced; + for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) { const Ref &node = tree_graph.nodes[i]; @@ -777,14 +778,21 @@ public: const NodeRuntimeData &node_runtime_data = _node_runtime_data[i]; node->calculate_sync_track(node_runtime_data.input_nodes); + + if (i == 1) { + node_time_info = node->node_time_info; + } } } void update_time(double p_delta) override { GodotProfileZone("SyncedBlendTree::update_time"); - tree_graph.nodes[0]->node_time_info.delta = p_delta; - tree_graph.nodes[0]->node_time_info.position += p_delta; + BLTAnimationNode::update_time(p_delta); + + tree_graph.nodes[0]->node_time_info.delta = node_time_info.delta; + tree_graph.nodes[0]->node_time_info.position = node_time_info.position; + tree_graph.nodes[0]->node_time_info.sync_position = node_time_info.sync_position; for (uint32_t i = 1; i < tree_graph.nodes.size(); i++) { const Ref &node = tree_graph.nodes[i]; diff --git a/demo/animation_tree_walk_run.tres b/demo/animation_tree_walk_run.tres index fd1de9c..bf53c65 100644 --- a/demo/animation_tree_walk_run.tres +++ b/demo/animation_tree_walk_run.tres @@ -8,7 +8,28 @@ animation = &"Walk-InPlace" [sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_lquwl"] +[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_vyt75"] + +[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_hom0r"] + +[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_vyt75"] + +[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_1rfsi"] +nodes/BlendTree/node = SubResource("AnimationNodeBlendTree_vyt75") +nodes/BlendTree/position = Vector2(694.9995, 215.55058) + +[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_8tpve"] +graph_offset = Vector2(-782, 179.47485) +nodes/Animation/node = SubResource("AnimationNodeAnimation_vyt75") +nodes/Animation/position = Vector2(-320, 140) +nodes/Blend2/node = SubResource("AnimationNodeBlend2_hom0r") +nodes/Blend2/position = Vector2(-115.66393, 127.37674) +nodes/BlendTree/node = SubResource("AnimationNodeBlendTree_1rfsi") +nodes/BlendTree/position = Vector2(-480, 400) +node_connections = [&"output", 0, &"Blend2", &"Blend2", 0, &"Animation", &"Blend2", 1, &"BlendTree"] + [resource] +graph_offset = Vector2(-217.4643, 82.84979) nodes/output/position = Vector2(540, 140) nodes/Animation/node = SubResource("AnimationNodeAnimation_1bvp3") nodes/Animation/position = Vector2(120, 80) @@ -16,4 +37,6 @@ nodes/Animation/position = Vector2(120, 80) "nodes/Animation 2/position" = Vector2(80, 320) nodes/Blend2/node = SubResource("AnimationNodeBlend2_lquwl") nodes/Blend2/position = Vector2(360, 180) +nodes/BlendTree/node = SubResource("AnimationNodeBlendTree_8tpve") +nodes/BlendTree/position = Vector2(778.0867, 295.33868) node_connections = [&"output", 0, &"Blend2", &"Blend2", 0, &"Animation", &"Blend2", 1, &"Animation 2"] diff --git a/demo/main.tscn b/demo/main.tscn index 036b08e..9e8744d 100644 --- a/demo/main.tscn +++ b/demo/main.tscn @@ -34,6 +34,7 @@ glow_enabled = true [sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_7mycd"] resource_name = "BLTAnimationNodeBlend2" position = Vector2(-320, -40) +blend_amount = 0.81 [sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_272bh"] resource_name = "BLTAnimationNodeSampler" @@ -43,7 +44,7 @@ animation = &"animation_library/Walk-InPlace" [sub_resource type="BLTAnimationNodeBlendTree" id="BLTAnimationNodeBlendTree_5vw27"] resource_name = "BLTAnimationNodeBlendTree" position = Vector2(-640, -20) -graph_offset = Vector2(-766.67163, -102.823944) +graph_offset = Vector2(-760.67163, -24.823944) nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_272bh") nodes/BLTAnimationNodeSampler/graph_offset = Vector2(-490, 7) node_connections = ["Output", 0, "BLTAnimationNodeSampler"] @@ -51,11 +52,11 @@ node_connections = ["Output", 0, "BLTAnimationNodeSampler"] [sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_kek77"] resource_name = "BLTAnimationNodeSampler" position = Vector2(-620, 140) -animation = &"animation_library/Walk-InPlace" +animation = &"animation_library/Run-InPlace" [sub_resource type="BLTAnimationNodeBlendTree" id="BLTAnimationNodeBlendTree_7mycd"] resource_name = "Root" -graph_offset = Vector2(-1054.4585, -50.771484) +graph_offset = Vector2(-869, -71) nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_7mycd") nodes/BLTAnimationNodeBlend2/graph_offset = Vector2(-320, -40) nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_kek77") @@ -318,7 +319,7 @@ libraries/animation_library = ExtResource("3_1bvp3") animation_player = NodePath("../AnimationPlayer2") tree_root = SubResource("BLTAnimationNodeBlendTree_7mycd") skeleton = NodePath("../Armature/Skeleton3D") -parameters/BLTAnimationNodeBlend2/blend_amount = 0.0 +parameters/BLTAnimationNodeBlend2/blend_amount = 0.81 [connection signal="value_changed" from="UI/MarginContainer/HBoxContainer/BlendWeightSlider" to="." method="_on_blend_weight_slider_value_changed"] diff --git a/tests/test_blendalot_animgraph.h b/tests/test_blendalot_animgraph.h index 511b713..85231a8 100644 --- a/tests/test_blendalot_animgraph.h +++ b/tests/test_blendalot_animgraph.h @@ -607,7 +607,6 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][Embe // TestAnimationB Ref animation_sampler_node_b; animation_sampler_node_b.instantiate(); - animation_sampler_node_b->animation_name = "animation_library/TestAnimationB"; embedded_blend_tree->add_node(animation_sampler_node_b); embedded_blend_tree->add_connection(animation_sampler_node_b, embedded_blend_tree->get_output_node(), "Output"); @@ -619,15 +618,11 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][Embe Ref blend2; blend2.instantiate(); blend2->set_name("Blend2"); - blend2->blend_weight = 0.5; - blend2->sync = true; - blend_tree->add_node(blend2); // TestAnimationA Ref animation_sampler_node_a; animation_sampler_node_a.instantiate(); - animation_sampler_node_a->animation_name = "animation_library/TestAnimationA"; blend_tree->add_node(animation_sampler_node_a); @@ -637,23 +632,54 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][Embe blend_tree->add_connection(embedded_blend_tree, blend2, "Input1"); blend_tree->add_connection(blend2, blend_tree->get_output_node(), "Output"); - // Trigger initialization - animation_graph->set_root_animation_node(blend_tree); - GraphEvaluationContext &graph_context = animation_graph->get_context(); - REQUIRE(blend_tree->initialize(graph_context)); + SUBCASE("Perform regular blend") { + animation_sampler_node_b->animation_name = "animation_library/TestAnimationB"; + animation_sampler_node_a->animation_name = "animation_library/TestAnimationA"; + blend2->blend_weight = 0.5; + blend2->sync = false; - // Perform evaluation - AnimationData *graph_output = graph_context.animation_data_allocator.allocate(); - blend_tree->activate_inputs(Vector>()); - blend_tree->calculate_sync_track(Vector>()); - blend_tree->update_time(0.1); - blend_tree->evaluate(graph_context, LocalVector(), *graph_output); + // Trigger initialization + animation_graph->set_root_animation_node(blend_tree); + GraphEvaluationContext &graph_context = animation_graph->get_context(); + REQUIRE(blend_tree->initialize(graph_context)); - // Check values - AnimationData::TransformTrackValue *hip_transform_value = graph_output->get_value(test_animation_a->get_tracks()[0]->thash); - CHECK(hip_transform_value->loc[0] == doctest::Approx(0.15)); - CHECK(hip_transform_value->loc[1] == doctest::Approx(0.3)); - CHECK(hip_transform_value->loc[2] == doctest::Approx(0.45)); + // Perform evaluation + AnimationData *graph_output = graph_context.animation_data_allocator.allocate(); + blend_tree->activate_inputs(Vector>()); + blend_tree->calculate_sync_track(Vector>()); + blend_tree->update_time(0.1); + blend_tree->evaluate(graph_context, LocalVector(), *graph_output); + + // Check values + AnimationData::TransformTrackValue *hip_transform_value = graph_output->get_value(test_animation_a->get_tracks()[0]->thash); + CHECK(hip_transform_value->loc[0] == doctest::Approx(0.15)); + CHECK(hip_transform_value->loc[1] == doctest::Approx(0.3)); + CHECK(hip_transform_value->loc[2] == doctest::Approx(0.45)); + } + SUBCASE("Perform synced blend") { + animation_sampler_node_b->animation_name = "animation_library/TestAnimationSyncA"; + animation_sampler_node_a->animation_name = "animation_library/TestAnimationSyncB"; + blend2->blend_weight = 0.5; + blend2->sync = true; + + // Trigger initialization + animation_graph->set_root_animation_node(blend_tree); + GraphEvaluationContext &graph_context = animation_graph->get_context(); + REQUIRE(blend_tree->initialize(graph_context)); + + // Perform evaluation + AnimationData *graph_output = graph_context.animation_data_allocator.allocate(); + blend_tree->activate_inputs(Vector>()); + blend_tree->calculate_sync_track(Vector>()); + blend_tree->update_time(0.825); + blend_tree->evaluate(graph_context, LocalVector(), *graph_output); + + // Check values + AnimationData::TransformTrackValue *hip_transform_value = graph_output->get_value(test_animation_a->get_tracks()[0]->thash); + CHECK(hip_transform_value->loc[0] == doctest::Approx(1.5)); + CHECK(hip_transform_value->loc[1] == doctest::Approx(3.0)); + CHECK(hip_transform_value->loc[2] == doctest::Approx(4.5)); + } } } //namespace TestBlendalotAnimationGraph \ No newline at end of file