Added Blend2 node (no syncing, yet).
This commit is contained in:
parent
56fde580c3
commit
46f940a67c
@ -171,7 +171,7 @@ void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
||||
graph_root_node->activate_inputs(Vector<Ref<SyncedAnimationNode>>());
|
||||
graph_root_node->calculate_sync_track(Vector<Ref<SyncedAnimationNode>>());
|
||||
graph_root_node->update_time(p_delta);
|
||||
graph_root_node->evaluate(graph_context, Vector<AnimationData *>(), graph_output);
|
||||
graph_root_node->evaluate(graph_context, LocalVector<AnimationData *>(), graph_output);
|
||||
|
||||
_apply_animation_data(graph_output);
|
||||
}
|
||||
|
||||
@ -71,12 +71,14 @@ void AnimationSamplerNode::initialize(GraphEvaluationContext &context) {
|
||||
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
||||
}
|
||||
|
||||
void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &inputs, AnimationData &output) {
|
||||
void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
||||
assert(inputs.size() == 0);
|
||||
|
||||
output.clear();
|
||||
output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position);
|
||||
}
|
||||
|
||||
void AnimationBlend2Node::evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &inputs, AnimationData &output) {
|
||||
void AnimationBlend2Node::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
||||
output = *inputs[0];
|
||||
output.blend(*inputs[1], blend_weight);
|
||||
}
|
||||
@ -72,6 +72,7 @@ struct AnimationData {
|
||||
|
||||
TrackValue *clone() const override {
|
||||
PositionTrackValue *result = memnew(PositionTrackValue);
|
||||
result->track = track;
|
||||
result->bone_idx = bone_idx;
|
||||
result->position = position;
|
||||
return result;
|
||||
@ -100,6 +101,7 @@ struct AnimationData {
|
||||
|
||||
TrackValue *clone() const override {
|
||||
RotationTrackValue *result = memnew(RotationTrackValue);
|
||||
result->track = track;
|
||||
result->bone_idx = bone_idx;
|
||||
result->rotation = rotation;
|
||||
return result;
|
||||
@ -135,7 +137,7 @@ struct AnimationData {
|
||||
}
|
||||
|
||||
void
|
||||
set_value(const Animation::TypeHash& thash, TrackValue *value) {
|
||||
set_value(const Animation::TypeHash &thash, TrackValue *value) {
|
||||
if (!track_values.has(thash)) {
|
||||
track_values.insert(thash, value);
|
||||
} else {
|
||||
@ -262,7 +264,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &input_datas, AnimationData &output_data) {
|
||||
virtual void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) {
|
||||
// By default, use the AnimationData of the first input.
|
||||
if (input_datas.size() > 0) {
|
||||
output_data = *input_datas[0];
|
||||
@ -294,7 +296,7 @@ private:
|
||||
Ref<Animation> animation;
|
||||
|
||||
void initialize(GraphEvaluationContext &context) override;
|
||||
void evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &inputs, AnimationData &output) override;
|
||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
||||
};
|
||||
|
||||
class OutputNode : public SyncedAnimationNode {
|
||||
@ -313,7 +315,7 @@ public:
|
||||
inputs.push_back("Input1");
|
||||
}
|
||||
|
||||
void evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &inputs, AnimationData &output) override;
|
||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
||||
};
|
||||
|
||||
struct BlendTreeConnection {
|
||||
@ -519,22 +521,16 @@ struct BlendTreeBuilder {
|
||||
class SyncedBlendTree : public SyncedAnimationNode {
|
||||
Vector<Ref<SyncedAnimationNode>> nodes;
|
||||
|
||||
struct NodeRuntimeData {
|
||||
Vector<Ref<SyncedAnimationNode>> input_nodes;
|
||||
Vector<AnimationData *> input_data;
|
||||
AnimationData *output_data = nullptr;
|
||||
};
|
||||
LocalVector<NodeRuntimeData> _node_runtime_data;
|
||||
|
||||
BlendTreeBuilder tree_builder;
|
||||
bool tree_initialized = false;
|
||||
|
||||
void setup_tree() {
|
||||
void sort_nodes() {
|
||||
nodes.clear();
|
||||
_node_runtime_data.clear();
|
||||
|
||||
tree_builder.sort_nodes_and_references();
|
||||
}
|
||||
|
||||
void setup_runtime_data() {
|
||||
// Add nodes and allocate runtime data
|
||||
for (int i = 0; i < tree_builder.nodes.size(); i++) {
|
||||
const Ref<SyncedAnimationNode> node = tree_builder.nodes[i];
|
||||
@ -559,11 +555,16 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
||||
node_runtime_data.input_nodes.push_back(nodes[connected_node_index]);
|
||||
}
|
||||
}
|
||||
|
||||
tree_initialized = true;
|
||||
}
|
||||
|
||||
public:
|
||||
struct NodeRuntimeData {
|
||||
Vector<Ref<SyncedAnimationNode>> input_nodes;
|
||||
LocalVector<AnimationData *> input_data;
|
||||
AnimationData *output_data = nullptr;
|
||||
};
|
||||
LocalVector<NodeRuntimeData> _node_runtime_data;
|
||||
|
||||
Ref<SyncedAnimationNode> get_output_node() const {
|
||||
return tree_builder.nodes[0];
|
||||
}
|
||||
@ -598,11 +599,14 @@ public:
|
||||
|
||||
// overrides from SyncedAnimationNode
|
||||
void initialize(GraphEvaluationContext &context) override {
|
||||
setup_tree();
|
||||
sort_nodes();
|
||||
setup_runtime_data();
|
||||
|
||||
for (Ref<SyncedAnimationNode> node : nodes) {
|
||||
node->initialize(context);
|
||||
}
|
||||
|
||||
tree_initialized = true;
|
||||
}
|
||||
|
||||
void activate_inputs(Vector<Ref<SyncedAnimationNode>> input_nodes) override {
|
||||
@ -654,7 +658,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
||||
for (int i = nodes.size() - 1; i > 0; i--) {
|
||||
const Ref<SyncedAnimationNode> &node = nodes[i];
|
||||
|
||||
@ -664,13 +668,22 @@ public:
|
||||
|
||||
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||
|
||||
// Populate the inputs
|
||||
for (unsigned int j = 0; j < node_runtime_data.input_data.size(); j++) {
|
||||
int child_index = tree_builder.node_connection_info[i].connected_child_node_index_at_port[j];
|
||||
node_runtime_data.input_data[j] = _node_runtime_data[child_index].output_data;
|
||||
}
|
||||
|
||||
// Set output pointer
|
||||
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);
|
||||
|
||||
// All inputs have been consumed and can now be freed.
|
||||
for (int child_index : tree_builder.node_connection_info[i].connected_child_node_index_at_port) {
|
||||
memfree(_node_runtime_data[child_index].output_data);
|
||||
}
|
||||
|
||||
@ -12,7 +12,8 @@ struct SyncedAnimationGraphFixture {
|
||||
|
||||
int hip_bone_index = -1;
|
||||
|
||||
Ref<Animation> test_animation;
|
||||
Ref<Animation> test_animation_a;
|
||||
Ref<Animation> test_animation_b;
|
||||
Ref<AnimationLibrary> animation_library;
|
||||
|
||||
SyncedAnimationGraph *synced_animation_graph;
|
||||
@ -31,17 +32,8 @@ struct SyncedAnimationGraphFixture {
|
||||
player_node = memnew(AnimationPlayer);
|
||||
player_node->set_name("AnimationPlayer");
|
||||
|
||||
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.));
|
||||
test_animation->track_set_path(track_index, NodePath(vformat("%s:%s", skeleton_node->get_path().get_concatenated_names(), "Hips")));
|
||||
setup_animations();
|
||||
|
||||
animation_library.instantiate();
|
||||
animation_library->add_animation("TestAnimation", test_animation);
|
||||
|
||||
player_node->add_animation_library("animation_library", animation_library);
|
||||
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
||||
|
||||
synced_animation_graph = memnew(SyncedAnimationGraph);
|
||||
@ -50,6 +42,29 @@ struct SyncedAnimationGraphFixture {
|
||||
synced_animation_graph->set_animation_player(player_node->get_path());
|
||||
synced_animation_graph->set_skeleton(skeleton_node->get_path());
|
||||
}
|
||||
|
||||
void setup_animations() {
|
||||
test_animation_a = memnew(Animation);
|
||||
int track_index = test_animation_a->add_track(Animation::TYPE_POSITION_3D);
|
||||
CHECK(track_index == 0);
|
||||
test_animation_a->track_insert_key(track_index, 0.0, Vector3(0., 0., 0.));
|
||||
test_animation_a->track_insert_key(track_index, 1.0, Vector3(1., 2., 3.));
|
||||
test_animation_a->track_set_path(track_index, NodePath(vformat("%s:%s", skeleton_node->get_path().get_concatenated_names(), "Hips")));
|
||||
|
||||
animation_library.instantiate();
|
||||
animation_library->add_animation("TestAnimationA", test_animation_a);
|
||||
|
||||
test_animation_b = memnew(Animation);
|
||||
track_index = test_animation_b->add_track(Animation::TYPE_POSITION_3D);
|
||||
CHECK(track_index == 0);
|
||||
test_animation_b->track_insert_key(track_index, 0.0, Vector3(0., 0., 0.));
|
||||
test_animation_b->track_insert_key(track_index, 1.0, Vector3(2., 4., 6.));
|
||||
test_animation_b->track_set_path(track_index, NodePath(vformat("%s:%s", skeleton_node->get_path().get_concatenated_names(), "Hips")));
|
||||
|
||||
animation_library->add_animation("TestAnimationB", test_animation_b);
|
||||
|
||||
player_node->add_animation_library("animation_library", animation_library);
|
||||
}
|
||||
};
|
||||
|
||||
namespace TestSyncedAnimationGraph {
|
||||
@ -135,13 +150,13 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
||||
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] Test AnimationData blending") {
|
||||
AnimationData data_t0;
|
||||
data_t0.sample_from_animation(test_animation, skeleton_node, 0.0);
|
||||
data_t0.sample_from_animation(test_animation_a, skeleton_node, 0.0);
|
||||
|
||||
AnimationData data_t1;
|
||||
data_t1.sample_from_animation(test_animation, skeleton_node, 1.0);
|
||||
data_t1.sample_from_animation(test_animation_a, skeleton_node, 1.0);
|
||||
|
||||
AnimationData data_t0_5;
|
||||
data_t0_5.sample_from_animation(test_animation, skeleton_node, 0.5);
|
||||
data_t0_5.sample_from_animation(test_animation_a, skeleton_node, 0.5);
|
||||
|
||||
AnimationData data_blended = data_t0;
|
||||
data_blended.blend(data_t1, 0.5);
|
||||
@ -164,7 +179,7 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph with an AnimationSampler as root node") {
|
||||
Ref<AnimationSamplerNode> animation_sampler_node;
|
||||
animation_sampler_node.instantiate();
|
||||
animation_sampler_node->animation_name = "animation_library/TestAnimation";
|
||||
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
||||
|
||||
synced_animation_graph->set_graph_root_node(animation_sampler_node);
|
||||
|
||||
@ -189,7 +204,7 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
||||
|
||||
Ref<AnimationSamplerNode> animation_sampler_node;
|
||||
animation_sampler_node.instantiate();
|
||||
animation_sampler_node->animation_name = "animation_library/TestAnimation";
|
||||
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
||||
|
||||
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"));
|
||||
@ -213,4 +228,66 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
||||
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree][Blend2Node] BlendTree with a Blend2Node connected to the output") {
|
||||
Ref<SyncedBlendTree> synced_blend_tree_node;
|
||||
synced_blend_tree_node.instantiate();
|
||||
|
||||
// TestAnimationA
|
||||
Ref<AnimationSamplerNode> animation_sampler_node_a;
|
||||
animation_sampler_node_a.instantiate();
|
||||
animation_sampler_node_a->animation_name = "animation_library/TestAnimationA";
|
||||
|
||||
synced_blend_tree_node->add_node(animation_sampler_node_a);
|
||||
|
||||
// TestAnimationB
|
||||
Ref<AnimationSamplerNode> animation_sampler_node_b;
|
||||
animation_sampler_node_b.instantiate();
|
||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationB";
|
||||
|
||||
synced_blend_tree_node->add_node(animation_sampler_node_b);
|
||||
|
||||
// Blend2
|
||||
Ref<AnimationBlend2Node> blend2_node;
|
||||
blend2_node.instantiate();
|
||||
blend2_node->blend_weight = 0.5;
|
||||
|
||||
synced_blend_tree_node->add_node(blend2_node);
|
||||
|
||||
// Connect nodes
|
||||
Vector<StringName> blend2_inputs;
|
||||
blend2_node->get_input_names(blend2_inputs);
|
||||
REQUIRE(synced_blend_tree_node->add_connection(animation_sampler_node_a, blend2_node, blend2_inputs[0]));
|
||||
REQUIRE(synced_blend_tree_node->add_connection(animation_sampler_node_b, blend2_node, blend2_inputs[1]));
|
||||
REQUIRE(synced_blend_tree_node->add_connection(blend2_node, synced_blend_tree_node->get_output_node(), "Input"));
|
||||
|
||||
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||
|
||||
// int sampler_node_1_index = synced_blend_tree_node->get_node_index(animation_sampler_node_1);
|
||||
// const SyncedBlendTree::NodeRuntimeData &sampler_node_1_runtime_data = synced_blend_tree_node->_node_runtime_data[sampler_node_1_index];
|
||||
|
||||
// int sampler_node_2_index = synced_blend_tree_node->get_node_index(animation_sampler_node_2);
|
||||
// const SyncedBlendTree::NodeRuntimeData &sampler_node_2_runtime_data = synced_blend_tree_node->_node_runtime_data[sampler_node_2_index];
|
||||
int blend2_node_index = synced_blend_tree_node->get_node_index(blend2_node);
|
||||
const SyncedBlendTree::NodeRuntimeData &blend2_runtime_data = synced_blend_tree_node->_node_runtime_data[blend2_node_index];
|
||||
|
||||
CHECK(blend2_runtime_data.input_nodes[0] == animation_sampler_node_a);
|
||||
CHECK(blend2_runtime_data.input_nodes[1] == animation_sampler_node_b);
|
||||
|
||||
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.5);
|
||||
|
||||
hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||
|
||||
CHECK(hip_bone_position.x == doctest::Approx(0.75));
|
||||
CHECK(hip_bone_position.y == doctest::Approx(1.5));
|
||||
CHECK(hip_bone_position.z == doctest::Approx(2.25));
|
||||
}
|
||||
|
||||
} //namespace TestSyncedAnimationGraph
|
||||
Loading…
x
Reference in New Issue
Block a user