Added simple unit test that uses an AnimationSamplerNode and a procedural animation.
This commit is contained in:
parent
757c5ee51c
commit
1732ecb8bd
8
SCsub
8
SCsub
@ -2,4 +2,10 @@
|
||||
|
||||
Import('env')
|
||||
|
||||
env.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build
|
||||
module_env = env.Clone()
|
||||
|
||||
module_env.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build
|
||||
|
||||
if env["tests"]:
|
||||
module_env.Append(CPPDEFINES=["TESTS_ENABLED"])
|
||||
module_env.add_source_files(env.modules_sources, "./tests/*.cpp")
|
||||
@ -1,6 +1,5 @@
|
||||
def can_build(env, platform):
|
||||
return True
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
||||
void SyncedAnimationGraph::_bind_methods() {
|
||||
print_line(vformat("binding methods"));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_active", "active"), &SyncedAnimationGraph::set_active);
|
||||
ClassDB::bind_method(D_METHOD("is_active"), &SyncedAnimationGraph::is_active);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
|
||||
@ -50,6 +48,15 @@ void SyncedAnimationGraph::_notification(int p_what) {
|
||||
_process_graph(get_physics_process_delta_time());
|
||||
}
|
||||
} break;
|
||||
|
||||
case Node::NOTIFICATION_EXIT_TREE: {
|
||||
_cleanup_evaluation_context();
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,6 +122,9 @@ void SyncedAnimationGraph::set_animation_player(const NodePath &p_path) {
|
||||
}
|
||||
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
||||
|
||||
_setup_evaluation_context();
|
||||
_setup_graph();
|
||||
|
||||
emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor.
|
||||
}
|
||||
|
||||
@ -132,6 +142,9 @@ void SyncedAnimationGraph::set_skeleton(const NodePath &p_path) {
|
||||
}
|
||||
graph_context.skeleton_3d = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
||||
|
||||
_setup_evaluation_context();
|
||||
_setup_graph();
|
||||
|
||||
emit_signal(SNAME("skeleton_changed")); // Needs to unpin AnimationPlayerEditor.
|
||||
}
|
||||
|
||||
@ -139,22 +152,32 @@ NodePath SyncedAnimationGraph::get_skeleton() const {
|
||||
return skeleton_path;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::set_graph_root_node(const Ref<SyncedAnimationNode> &p_animation_node) {
|
||||
if (graph_root_node != p_animation_node) {
|
||||
graph_root_node = p_animation_node;
|
||||
_setup_graph();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<SyncedAnimationNode> SyncedAnimationGraph::get_graph_root_node() const {
|
||||
return graph_root_node;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
||||
if (root_node == nullptr) {
|
||||
if (!graph_root_node.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
root_node->activate_inputs();
|
||||
root_node->calculate_sync_track();
|
||||
root_node->update_time(p_delta);
|
||||
AnimationData output_data;
|
||||
root_node->evaluate(graph_context, output_data);
|
||||
graph_root_node->activate_inputs();
|
||||
graph_root_node->calculate_sync_track();
|
||||
graph_root_node->update_time(p_delta);
|
||||
graph_root_node->evaluate(graph_context, graph_output);
|
||||
|
||||
_apply_animation_data(output_data);
|
||||
_apply_animation_data(graph_output);
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::_apply_animation_data(AnimationData output_data) const {
|
||||
for (KeyValue<Animation::TypeHash, AnimationData::TrackValue *> &K : output_data.track_values) {
|
||||
void SyncedAnimationGraph::_apply_animation_data(const AnimationData& output_data) const {
|
||||
for (const KeyValue<Animation::TypeHash, AnimationData::TrackValue *> &K : output_data.track_values) {
|
||||
const AnimationData::TrackValue *track_value = K.value;
|
||||
switch (track_value->type) {
|
||||
case AnimationData::TrackType::TYPE_POSITION_3D: {
|
||||
@ -219,24 +242,11 @@ void SyncedAnimationGraph::_cleanup_evaluation_context() {
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::_setup_graph() {
|
||||
if (root_node != nullptr) {
|
||||
_cleanup_graph();
|
||||
}
|
||||
|
||||
AnimationSamplerNode *sampler_node = memnew(AnimationSamplerNode);
|
||||
sampler_node->animation_name = "animation_library/Walk-InPlace";
|
||||
|
||||
root_node = sampler_node;
|
||||
|
||||
root_node->initialize(graph_context);
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::_cleanup_graph() {
|
||||
if (root_node == nullptr) {
|
||||
if (graph_context.animation_player == nullptr || graph_context.skeleton_3d == nullptr || !graph_root_node.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
memfree(root_node);
|
||||
graph_root_node->initialize(graph_context);
|
||||
}
|
||||
|
||||
SyncedAnimationGraph::SyncedAnimationGraph() {
|
||||
|
||||
@ -15,13 +15,8 @@ private:
|
||||
NodePath skeleton_path;
|
||||
|
||||
GraphEvaluationContext graph_context = {};
|
||||
SyncedAnimationNode* root_node = nullptr;
|
||||
|
||||
void set_animation_player(const NodePath &p_path);
|
||||
NodePath get_animation_player() const;
|
||||
|
||||
void set_skeleton(const NodePath &p_path);
|
||||
NodePath get_skeleton() const;
|
||||
Ref<SyncedAnimationNode> graph_root_node = nullptr;
|
||||
AnimationData graph_output;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
@ -37,11 +32,20 @@ protected:
|
||||
|
||||
public:
|
||||
void _process_graph(double p_delta, bool p_update_only = false);
|
||||
void _apply_animation_data(AnimationData output_data) const;
|
||||
void _apply_animation_data(const AnimationData& output_data) const;
|
||||
|
||||
void set_active(bool p_active);
|
||||
bool is_active() const;
|
||||
|
||||
void set_animation_player(const NodePath &p_path);
|
||||
NodePath get_animation_player() const;
|
||||
|
||||
void set_skeleton(const NodePath &p_path);
|
||||
NodePath get_skeleton() const;
|
||||
|
||||
void set_graph_root_node(const Ref<SyncedAnimationNode> &p_animation_node);
|
||||
Ref<SyncedAnimationNode> get_graph_root_node() const;
|
||||
|
||||
void set_callback_mode_process(AnimationMixer::AnimationCallbackModeProcess p_mode);
|
||||
AnimationMixer::AnimationCallbackModeProcess get_callback_mode_process() const;
|
||||
|
||||
@ -60,5 +64,4 @@ private:
|
||||
void _cleanup_evaluation_context();
|
||||
|
||||
void _setup_graph();
|
||||
void _cleanup_graph();
|
||||
};
|
||||
|
||||
@ -10,11 +10,10 @@ void AnimationSamplerNode::initialize(GraphEvaluationContext &context) {
|
||||
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
||||
}
|
||||
|
||||
|
||||
void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, AnimationData &output) {
|
||||
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||
// real_t a_length = animation->get_length();
|
||||
|
||||
int count = tracks.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
AnimationData::TrackValue *track_value = nullptr;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
||||
#include "core/io/resource.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
|
||||
#include <cassert>
|
||||
@ -79,7 +80,9 @@ struct SyncTrack {
|
||||
|
||||
};
|
||||
|
||||
class SyncedAnimationNode {
|
||||
class SyncedAnimationNode: public Resource {
|
||||
GDCLASS(SyncedAnimationNode, Resource);
|
||||
|
||||
friend class SyncedAnimationGraph;
|
||||
|
||||
public:
|
||||
@ -141,6 +144,8 @@ private:
|
||||
};
|
||||
|
||||
class AnimationSamplerNode : public SyncedAnimationNode {
|
||||
GDCLASS(AnimationSamplerNode, SyncedAnimationNode);
|
||||
|
||||
public:
|
||||
StringName animation_name;
|
||||
|
||||
|
||||
65
tests/test_synced_animation_graph.h
Normal file
65
tests/test_synced_animation_graph.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "../synced_animation_graph.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "servers/rendering/rendering_server_default.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestSyncedAnimationGraph {
|
||||
TEST_CASE("[SceneTree][SyncedAnimationGraph] Simple") {
|
||||
Node* character_node = memnew(Node);
|
||||
character_node->set_name("CharacterNode");
|
||||
SceneTree::get_singleton()->get_root()->add_child(character_node);
|
||||
|
||||
Skeleton3D *skeleton_node = memnew(Skeleton3D);
|
||||
skeleton_node->set_name("Skeleton");
|
||||
character_node->add_child(skeleton_node);
|
||||
|
||||
skeleton_node->add_bone("Root");
|
||||
int hip_bone_index = skeleton_node->add_bone("Hips");
|
||||
|
||||
AnimationPlayer *player_node = memnew(AnimationPlayer);
|
||||
player_node->set_name("AnimationPlayer");
|
||||
|
||||
Ref<Animation> animation = memnew(Animation);
|
||||
const int track_index = animation->add_track(Animation::TYPE_POSITION_3D);
|
||||
CHECK(track_index == 0);
|
||||
animation->track_insert_key(track_index, 0.0, Vector3(0., 0., 0.));
|
||||
animation->track_insert_key(track_index, 1.0, Vector3(1., 2., 3.));
|
||||
animation->track_set_path(track_index, NodePath(vformat("%s:%s", skeleton_node->get_path().get_concatenated_names(),"Hips")));
|
||||
|
||||
Ref<AnimationLibrary> animation_library;
|
||||
animation_library.instantiate();
|
||||
animation_library->add_animation("TestAnimation", animation);
|
||||
|
||||
player_node->add_animation_library("animation_library", animation_library);
|
||||
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
||||
|
||||
SyncedAnimationGraph *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());
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user