2025-12-05 17:20:35 +01:00
# pragma once
# include "../synced_animation_graph.h"
# include "scene/main/window.h"
# include "tests/test_macros.h"
2025-12-08 22:47:00 +01:00
struct SyncedAnimationGraphFixture {
2025-12-12 10:44:18 +01:00
Node * character_node ;
Skeleton3D * skeleton_node ;
AnimationPlayer * player_node ;
2025-12-08 22:47:00 +01:00
int hip_bone_index = - 1 ;
2025-12-05 17:20:35 +01:00
2025-12-08 22:47:00 +01:00
Ref < Animation > test_animation ;
Ref < AnimationLibrary > animation_library ;
2025-12-05 17:20:35 +01:00
2025-12-12 10:44:18 +01:00
SyncedAnimationGraph * synced_animation_graph ;
2025-12-08 22:47:00 +01:00
SyncedAnimationGraphFixture ( ) {
character_node = memnew ( Node ) ;
character_node - > set_name ( " CharacterNode " ) ;
SceneTree : : get_singleton ( ) - > get_root ( ) - > add_child ( character_node ) ;
2025-12-05 17:20:35 +01:00
2025-12-08 22:47:00 +01:00
skeleton_node = memnew ( Skeleton3D ) ;
skeleton_node - > set_name ( " Skeleton " ) ;
character_node - > add_child ( skeleton_node ) ;
2025-12-05 17:20:35 +01:00
2025-12-08 22:47:00 +01:00
skeleton_node - > add_bone ( " Root " ) ;
hip_bone_index = skeleton_node - > add_bone ( " Hips " ) ;
2025-12-05 17:20:35 +01:00
2025-12-08 22:47:00 +01:00
player_node = memnew ( AnimationPlayer ) ;
player_node - > set_name ( " AnimationPlayer " ) ;
2025-12-05 17:20:35 +01:00
2025-12-08 22:47:00 +01:00
test_animation = memnew ( Animation ) ;
const int track_index = test_animation - > add_track ( Animation : : TYPE_POSITION_3D ) ;
CHECK ( track_index = = 0 ) ;
test_animation - > track_insert_key ( track_index , 0.0 , Vector3 ( 0. , 0. , 0. ) ) ;
test_animation - > track_insert_key ( track_index , 1.0 , Vector3 ( 1. , 2. , 3. ) ) ;
2025-12-12 10:44:18 +01:00
test_animation - > track_set_path ( track_index , NodePath ( vformat ( " %s:%s " , skeleton_node - > get_path ( ) . get_concatenated_names ( ) , " Hips " ) ) ) ;
2025-12-05 17:20:35 +01:00
2025-12-08 22:47:00 +01:00
animation_library . instantiate ( ) ;
animation_library - > add_animation ( " TestAnimation " , test_animation ) ;
2025-12-05 17:20:35 +01:00
2025-12-08 22:47:00 +01:00
player_node - > add_animation_library ( " animation_library " , animation_library ) ;
SceneTree : : get_singleton ( ) - > get_root ( ) - > add_child ( player_node ) ;
synced_animation_graph = memnew ( SyncedAnimationGraph ) ;
SceneTree : : get_singleton ( ) - > get_root ( ) - > add_child ( synced_animation_graph ) ;
synced_animation_graph - > set_animation_player ( player_node - > get_path ( ) ) ;
synced_animation_graph - > set_skeleton ( skeleton_node - > get_path ( ) ) ;
}
} ;
namespace TestSyncedAnimationGraph {
2025-12-05 17:20:35 +01:00
2025-12-22 00:37:27 +01:00
TEST_CASE ( " [SyncedAnimationGraph] Test BlendTree construction " ) {
2025-12-13 22:38:45 +01:00
BlendTreeBuilder tree_constructor ;
2025-12-10 09:22:33 +01:00
Ref < AnimationSamplerNode > animation_sampler_node0 ;
animation_sampler_node0 . instantiate ( ) ;
animation_sampler_node0 - > name = " Sampler0 " ;
tree_constructor . add_node ( animation_sampler_node0 ) ;
Ref < AnimationSamplerNode > animation_sampler_node1 ;
animation_sampler_node1 . instantiate ( ) ;
animation_sampler_node1 - > name = " Sampler1 " ;
tree_constructor . add_node ( animation_sampler_node1 ) ;
2025-12-12 10:44:18 +01:00
Ref < AnimationSamplerNode > animation_sampler_node2 ;
animation_sampler_node2 . instantiate ( ) ;
animation_sampler_node2 - > name = " Sampler2 " ;
tree_constructor . add_node ( animation_sampler_node2 ) ;
2025-12-10 09:22:33 +01:00
Ref < AnimationBlend2Node > node_blend0 ;
node_blend0 . instantiate ( ) ;
2025-12-12 10:44:18 +01:00
node_blend0 - > name = " Blend0 " ;
2025-12-10 09:22:33 +01:00
tree_constructor . add_node ( node_blend0 ) ;
Ref < AnimationBlend2Node > node_blend1 ;
node_blend1 . instantiate ( ) ;
2025-12-12 10:44:18 +01:00
node_blend1 - > name = " Blend1 " ;
2025-12-10 09:22:33 +01:00
tree_constructor . add_node ( node_blend1 ) ;
2025-12-12 10:44:18 +01:00
// Tree
/ / Sampler0 - \
2025-12-19 10:53:19 +01:00
/ / Sampler1 - + Blend0 - \
// Sampler2 -----------+ Blend1 - Output
2025-12-12 10:44:18 +01:00
2025-12-10 09:22:33 +01:00
CHECK ( tree_constructor . add_connection ( animation_sampler_node0 , node_blend0 , " Input0 " ) ) ;
2025-12-12 10:44:18 +01:00
// Ensure that subtree is properly updated
int sampler0_index = tree_constructor . get_node_index ( animation_sampler_node0 ) ;
int blend0_index = tree_constructor . get_node_index ( node_blend0 ) ;
CHECK ( tree_constructor . node_connection_info [ blend0_index ] . input_subtree_node_indices . has ( sampler0_index ) ) ;
// Connect blend0 to blend1
CHECK ( tree_constructor . add_connection ( node_blend0 , node_blend1 , " Input0 " ) ) ;
// Connecting to an already connected port must fail
CHECK ( ! tree_constructor . add_connection ( animation_sampler_node1 , node_blend0 , " Input0 " ) ) ;
// Correct connection of Sampler1 to Blend0
CHECK ( tree_constructor . add_connection ( animation_sampler_node1 , node_blend0 , " Input1 " ) ) ;
// Ensure that subtree is properly updated
int sampler1_index = tree_constructor . get_node_index ( animation_sampler_node0 ) ;
int blend1_index = tree_constructor . get_node_index ( node_blend1 ) ;
CHECK ( tree_constructor . node_connection_info [ blend1_index ] . input_subtree_node_indices . has ( sampler1_index ) ) ;
CHECK ( tree_constructor . node_connection_info [ blend1_index ] . input_subtree_node_indices . has ( sampler0_index ) ) ;
CHECK ( tree_constructor . node_connection_info [ blend1_index ] . input_subtree_node_indices . has ( blend0_index ) ) ;
// Creating a loop must fail
2025-12-10 09:22:33 +01:00
CHECK ( ! tree_constructor . add_connection ( node_blend1 , node_blend0 , " Input1 " ) ) ;
2025-12-12 10:44:18 +01:00
// Perform remaining connections
CHECK ( tree_constructor . add_connection ( node_blend1 , tree_constructor . get_output_node ( ) , " Input " ) ) ;
CHECK ( tree_constructor . add_connection ( animation_sampler_node2 , node_blend1 , " Input1 " ) ) ;
// Output node must have all nodes in its subtree:
CHECK ( tree_constructor . node_connection_info [ 0 ] . input_subtree_node_indices . has ( 1 ) ) ;
CHECK ( tree_constructor . node_connection_info [ 0 ] . input_subtree_node_indices . has ( 2 ) ) ;
CHECK ( tree_constructor . node_connection_info [ 0 ] . input_subtree_node_indices . has ( 3 ) ) ;
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 ) ) ;
2025-12-13 22:38:45 +01:00
tree_constructor . sort_nodes_and_references ( ) ;
// 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 + + ) {
2025-12-19 10:53:19 +01:00
for ( int input_index : tree_constructor . node_connection_info [ i ] . input_subtree_node_indices ) {
2025-12-13 22:38:45 +01:00
CHECK ( input_index > i ) ;
}
}
2025-12-10 09:22:33 +01:00
}
2025-12-22 00:37:27 +01:00
TEST_CASE_FIXTURE ( SyncedAnimationGraphFixture , " [SceneTree][SyncedAnimationGraph] Test AnimationData blending " ) {
AnimationData data_t0 ;
data_t0 . sample_from_animation ( test_animation , skeleton_node , 0.0 ) ;
AnimationData data_t1 ;
data_t1 . sample_from_animation ( test_animation , skeleton_node , 1.0 ) ;
AnimationData data_t0_5 ;
data_t0_5 . sample_from_animation ( test_animation , skeleton_node , 0.5 ) ;
AnimationData data_blended = data_t0 ;
data_blended . blend ( data_t1 , 0.5 ) ;
REQUIRE ( data_blended . has_same_tracks ( data_t0_5 ) ) ;
for ( const KeyValue < Animation : : TypeHash , AnimationData : : TrackValue * > & K : data_blended . track_values ) {
CHECK ( K . value - > operator = = ( * data_t0_5 . track_values . find ( K . key ) - > value ) ) ;
}
// And also check that values do not match
data_blended = data_t0 ;
data_blended . blend ( data_t1 , 0.3 ) ;
REQUIRE ( data_blended . has_same_tracks ( data_t0_5 ) ) ;
for ( const KeyValue < Animation : : TypeHash , AnimationData : : TrackValue * > & K : data_blended . track_values ) {
CHECK ( K . value - > operator ! = ( * data_t0_5 . track_values . find ( K . key ) - > value ) ) ;
}
}
2025-12-19 10:53:19 +01:00
TEST_CASE_FIXTURE ( SyncedAnimationGraphFixture , " [SceneTree][SyncedAnimationGraph] SyncedAnimationGraph with an AnimationSampler as root node " ) {
2025-12-05 17:20:35 +01:00
Ref < AnimationSamplerNode > animation_sampler_node ;
animation_sampler_node . instantiate ( ) ;
animation_sampler_node - > animation_name = " animation_library/TestAnimation " ;
synced_animation_graph - > set_graph_root_node ( animation_sampler_node ) ;
Vector3 hip_bone_position = skeleton_node - > get_bone_global_pose ( hip_bone_index ) . origin ;
CHECK ( hip_bone_position . x = = doctest : : Approx ( 0.0 ) ) ;
CHECK ( hip_bone_position . y = = doctest : : Approx ( 0.0 ) ) ;
CHECK ( hip_bone_position . z = = doctest : : Approx ( 0.0 ) ) ;
SceneTree : : get_singleton ( ) - > process ( 0.01 ) ;
hip_bone_position = skeleton_node - > get_bone_global_pose ( hip_bone_index ) . origin ;
CHECK ( hip_bone_position . x = = doctest : : Approx ( 0.01 ) ) ;
CHECK ( hip_bone_position . y = = doctest : : Approx ( 0.02 ) ) ;
CHECK ( hip_bone_position . z = = doctest : : Approx ( 0.03 ) ) ;
}
2025-12-08 22:47:00 +01:00
2025-12-19 10:53:19 +01:00
TEST_CASE_FIXTURE ( SyncedAnimationGraphFixture , " [SceneTree][SyncedAnimationGraph][BlendTree] BlendTree with a AnimationSamplerNode connected to the output " ) {
2025-12-08 22:47:00 +01:00
Ref < SyncedBlendTree > synced_blend_tree_node ;
synced_blend_tree_node . instantiate ( ) ;
Ref < AnimationSamplerNode > animation_sampler_node ;
animation_sampler_node . instantiate ( ) ;
animation_sampler_node - > animation_name = " animation_library/TestAnimation " ;
2025-12-19 10:53:19 +01:00
2025-12-08 22:47:00 +01:00
synced_blend_tree_node - > add_node ( animation_sampler_node ) ;
2025-12-19 10:53:19 +01:00
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 ( ) ) ;
2025-12-08 22:47:00 +01:00
synced_animation_graph - > set_graph_root_node ( synced_blend_tree_node ) ;
Vector3 hip_bone_position = skeleton_node - > get_bone_global_pose ( hip_bone_index ) . origin ;
CHECK ( hip_bone_position . x = = doctest : : Approx ( 0.0 ) ) ;
CHECK ( hip_bone_position . y = = doctest : : Approx ( 0.0 ) ) ;
CHECK ( hip_bone_position . z = = doctest : : Approx ( 0.0 ) ) ;
SceneTree : : get_singleton ( ) - > process ( 0.01 ) ;
hip_bone_position = skeleton_node - > get_bone_global_pose ( hip_bone_index ) . origin ;
CHECK ( hip_bone_position . x = = doctest : : Approx ( 0.01 ) ) ;
CHECK ( hip_bone_position . y = = doctest : : Approx ( 0.02 ) ) ;
CHECK ( hip_bone_position . z = = doctest : : Approx ( 0.03 ) ) ;
2025-12-05 17:20:35 +01:00
}
2025-12-08 22:47:00 +01:00
2025-12-12 10:44:18 +01:00
} //namespace TestSyncedAnimationGraph