Initial support of actual blend tree evaluation.
This commit is contained in:
parent
ea2cb6b8e8
commit
e09995c3fa
@ -168,15 +168,15 @@ void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
graph_root_node->activate_inputs();
|
graph_root_node->activate_inputs(Vector<Ref<SyncedAnimationNode>>());
|
||||||
graph_root_node->calculate_sync_track();
|
graph_root_node->calculate_sync_track(Vector<Ref<SyncedAnimationNode>>());
|
||||||
graph_root_node->update_time(p_delta);
|
graph_root_node->update_time(p_delta);
|
||||||
graph_root_node->evaluate(graph_context, Vector<AnimationData*>(), graph_output);
|
graph_root_node->evaluate(graph_context, Vector<AnimationData *>(), graph_output);
|
||||||
|
|
||||||
_apply_animation_data(graph_output);
|
_apply_animation_data(graph_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_apply_animation_data(const AnimationData& output_data) const {
|
void SyncedAnimationGraph::_apply_animation_data(const AnimationData &output_data) const {
|
||||||
for (const KeyValue<Animation::TypeHash, AnimationData::TrackValue *> &K : output_data.track_values) {
|
for (const KeyValue<Animation::TypeHash, AnimationData::TrackValue *> &K : output_data.track_values) {
|
||||||
const AnimationData::TrackValue *track_value = K.value;
|
const AnimationData::TrackValue *track_value = K.value;
|
||||||
switch (track_value->type) {
|
switch (track_value->type) {
|
||||||
|
|||||||
@ -32,7 +32,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void _process_graph(double p_delta, bool p_update_only = false);
|
void _process_graph(double p_delta, bool p_update_only = false);
|
||||||
void _apply_animation_data(const AnimationData& output_data) const;
|
void _apply_animation_data(const AnimationData &output_data) const;
|
||||||
|
|
||||||
void set_active(bool p_active);
|
void set_active(bool p_active);
|
||||||
bool is_active() const;
|
bool is_active() const;
|
||||||
@ -55,6 +55,10 @@ public:
|
|||||||
void set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode);
|
void set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode);
|
||||||
AnimationMixer::AnimationCallbackModeDiscrete get_callback_mode_discrete() const;
|
AnimationMixer::AnimationCallbackModeDiscrete get_callback_mode_discrete() const;
|
||||||
|
|
||||||
|
GraphEvaluationContext &get_context() {
|
||||||
|
return graph_context;
|
||||||
|
}
|
||||||
|
|
||||||
SyncedAnimationGraph();
|
SyncedAnimationGraph();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -7,11 +7,6 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
struct GraphEvaluationContext {
|
|
||||||
AnimationPlayer *animation_player = nullptr;
|
|
||||||
Skeleton3D *skeleton_3d = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnimationData {
|
struct AnimationData {
|
||||||
enum TrackType : uint8_t {
|
enum TrackType : uint8_t {
|
||||||
TYPE_VALUE, // Set a value in a property, can be interpolated.
|
TYPE_VALUE, // Set a value in a property, can be interpolated.
|
||||||
@ -78,6 +73,11 @@ protected:
|
|||||||
struct SyncTrack {
|
struct SyncTrack {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GraphEvaluationContext {
|
||||||
|
AnimationPlayer *animation_player = nullptr;
|
||||||
|
Skeleton3D *skeleton_3d = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
class SyncedAnimationNode : public Resource {
|
class SyncedAnimationNode : public Resource {
|
||||||
GDCLASS(SyncedAnimationNode, Resource);
|
GDCLASS(SyncedAnimationNode, Resource);
|
||||||
|
|
||||||
@ -90,6 +90,7 @@ public:
|
|||||||
double sync_position = 0.0;
|
double sync_position = 0.0;
|
||||||
double delta = 0.0;
|
double delta = 0.0;
|
||||||
double sync_delta = 0.0;
|
double sync_delta = 0.0;
|
||||||
|
bool is_synced = false;
|
||||||
|
|
||||||
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
||||||
SyncTrack sync_track;
|
SyncTrack sync_track;
|
||||||
@ -109,7 +110,7 @@ public:
|
|||||||
virtual void initialize(GraphEvaluationContext &context) {}
|
virtual void initialize(GraphEvaluationContext &context) {}
|
||||||
virtual void activate_inputs(Vector<Ref<SyncedAnimationNode>> input_nodes) {
|
virtual void activate_inputs(Vector<Ref<SyncedAnimationNode>> input_nodes) {
|
||||||
// By default, all inputs nodes are activated.
|
// By default, all inputs nodes are activated.
|
||||||
for (Ref<SyncedAnimationNode> node: input_nodes) {
|
for (const Ref<SyncedAnimationNode>& node : input_nodes) {
|
||||||
node->active = true;
|
node->active = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,7 +247,7 @@ struct BlendTreeBuilder {
|
|||||||
// Map connected subtrees
|
// Map connected subtrees
|
||||||
HashSet<int> old_indices = input_subtree_node_indices;
|
HashSet<int> old_indices = input_subtree_node_indices;
|
||||||
input_subtree_node_indices.clear();
|
input_subtree_node_indices.clear();
|
||||||
for (int old_index: old_indices) {
|
for (int old_index : old_indices) {
|
||||||
input_subtree_node_indices.insert(node_index_mapping.find(old_index));
|
input_subtree_node_indices.insert(node_index_mapping.find(old_index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,7 +299,7 @@ struct BlendTreeBuilder {
|
|||||||
}
|
}
|
||||||
nodes = sorted_nodes;
|
nodes = sorted_nodes;
|
||||||
|
|
||||||
for (NodeConnectionInfo& connection_info: node_connection_info) {
|
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
||||||
connection_info.apply_node_mapping(sorted_node_indices);
|
connection_info.apply_node_mapping(sorted_node_indices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,7 +315,9 @@ struct BlendTreeBuilder {
|
|||||||
|
|
||||||
void sort_nodes_recursive(int node_index, LocalVector<int> &result) {
|
void sort_nodes_recursive(int node_index, LocalVector<int> &result) {
|
||||||
for (int input_node_index : node_connection_info[node_index].connected_child_node_index_at_port) {
|
for (int input_node_index : node_connection_info[node_index].connected_child_node_index_at_port) {
|
||||||
sort_nodes_recursive(input_node_index, result);
|
if (input_node_index >= 0) {
|
||||||
|
sort_nodes_recursive(input_node_index, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result.push_back(node_index);
|
result.push_back(node_index);
|
||||||
}
|
}
|
||||||
@ -406,7 +409,7 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
|
|
||||||
struct NodeRuntimeData {
|
struct NodeRuntimeData {
|
||||||
Vector<Ref<SyncedAnimationNode>> input_nodes;
|
Vector<Ref<SyncedAnimationNode>> input_nodes;
|
||||||
Vector<AnimationData*> input_data;
|
Vector<AnimationData *> input_data;
|
||||||
AnimationData *output_data = nullptr;
|
AnimationData *output_data = nullptr;
|
||||||
};
|
};
|
||||||
LocalVector<NodeRuntimeData> _node_runtime_data;
|
LocalVector<NodeRuntimeData> _node_runtime_data;
|
||||||
@ -422,7 +425,7 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
|
|
||||||
// Add nodes and allocate runtime data
|
// Add nodes and allocate runtime data
|
||||||
for (int i = 0; i < tree_builder.nodes.size(); i++) {
|
for (int i = 0; i < tree_builder.nodes.size(); i++) {
|
||||||
Ref<SyncedAnimationNode> node = tree_builder.nodes[i];
|
const Ref<SyncedAnimationNode> node = tree_builder.nodes[i];
|
||||||
nodes.push_back(node);
|
nodes.push_back(node);
|
||||||
|
|
||||||
NodeRuntimeData node_runtime_data;
|
NodeRuntimeData node_runtime_data;
|
||||||
@ -437,10 +440,11 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
Ref<SyncedAnimationNode> node = nodes[i];
|
Ref<SyncedAnimationNode> node = nodes[i];
|
||||||
NodeRuntimeData& node_runtime_data = _node_runtime_data[i];
|
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||||
|
|
||||||
for (int port_index = 0; port_index < node->get_node_input_count(); port_index++) {
|
for (int port_index = 0; port_index < node->get_node_input_count(); port_index++) {
|
||||||
node_runtime_data.input_nodes.push_back(nodes[tree_builder.node_connection_info[i].connected_child_node_index_at_port[port_index]]);
|
const int connected_node_index = tree_builder.node_connection_info[i].connected_child_node_index_at_port[port_index];
|
||||||
|
node_runtime_data.input_nodes.push_back(nodes[connected_node_index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,18 +452,11 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SyncedBlendTree() {
|
Ref<SyncedAnimationNode> get_output_node() const {
|
||||||
Ref<OutputNode> output_node;
|
return tree_builder.nodes[0];
|
||||||
output_node.instantiate();
|
|
||||||
output_node->name = "Output";
|
|
||||||
nodes.push_back(output_node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<SyncedAnimationNode> get_output_node() {
|
int get_node_index(const Ref<SyncedAnimationNode>& node) const {
|
||||||
return nodes[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_node_index(const Ref<SyncedAnimationNode> node) {
|
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
if (nodes[i] == node) {
|
if (nodes[i] == node) {
|
||||||
return i;
|
return i;
|
||||||
@ -489,6 +486,8 @@ public:
|
|||||||
|
|
||||||
// overrides from SyncedAnimationNode
|
// overrides from SyncedAnimationNode
|
||||||
void initialize(GraphEvaluationContext &context) override {
|
void initialize(GraphEvaluationContext &context) override {
|
||||||
|
setup_tree();
|
||||||
|
|
||||||
for (Ref<SyncedAnimationNode> node : nodes) {
|
for (Ref<SyncedAnimationNode> node : nodes) {
|
||||||
node->initialize(context);
|
node->initialize(context);
|
||||||
}
|
}
|
||||||
@ -503,9 +502,7 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeRuntimeData& node_runtime_data = _node_runtime_data[i];
|
const NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||||
node_runtime_data.output_data = memnew(AnimationData);
|
|
||||||
|
|
||||||
node->activate_inputs(node_runtime_data.input_nodes);
|
node->activate_inputs(node_runtime_data.input_nodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -518,28 +515,53 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeRuntimeData& node_runtime_data = _node_runtime_data[i];
|
const NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||||
node_runtime_data.output_data = memnew(AnimationData);
|
|
||||||
|
|
||||||
node->calculate_sync_track(node_runtime_data.input_nodes);
|
node->calculate_sync_track(node_runtime_data.input_nodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_time(double p_delta) override {
|
void update_time(double p_delta) override {
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
nodes[0]->node_time_info.delta = p_delta;
|
||||||
|
nodes[0]->node_time_info.position += p_delta;
|
||||||
|
|
||||||
|
for (int i = 1; i < nodes.size(); i++) {
|
||||||
Ref<SyncedAnimationNode> node = nodes[i];
|
Ref<SyncedAnimationNode> node = nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeRuntimeData& node_runtime_data = _node_runtime_data[i];
|
Ref<SyncedAnimationNode> node_parent = nodes[tree_builder.node_connection_info[i].parent_node_index];
|
||||||
node_runtime_data.output_data = memnew(AnimationData);
|
|
||||||
|
|
||||||
node->update_time(node_runtime_data.input_nodes);
|
if (node->node_time_info.is_synced) {
|
||||||
|
node->update_time(node_parent->node_time_info.position);
|
||||||
|
} else {
|
||||||
|
node->update_time(node_parent->node_time_info.delta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
void evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
||||||
|
for (int i = nodes.size() - 1; i > 0; i--) {
|
||||||
|
const Ref<SyncedAnimationNode>& node = nodes[i];
|
||||||
|
|
||||||
|
if (!node->active) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for (int child_index : tree_builder.node_connection_info[i].connected_child_node_index_at_port) {
|
||||||
|
memfree(_node_runtime_data[child_index].output_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "../synced_animation_graph.h"
|
#include "../synced_animation_graph.h"
|
||||||
#include "scene/main/window.h"
|
#include "scene/main/window.h"
|
||||||
#include "servers/rendering/rendering_server_default.h"
|
|
||||||
|
|
||||||
#include "tests/test_macros.h"
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
@ -85,8 +84,8 @@ TEST_CASE("[SyncedAnimationGraph] TestBlendTreeConstruction") {
|
|||||||
|
|
||||||
// Tree
|
// Tree
|
||||||
// Sampler0 -\
|
// Sampler0 -\
|
||||||
// Sampler1 -+- Blend0 -\
|
// Sampler1 -+ Blend0 -\
|
||||||
// Sampler2 ------------+ Blend1 - Output
|
// Sampler2 -----------+ Blend1 - Output
|
||||||
|
|
||||||
CHECK(tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
CHECK(tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
||||||
|
|
||||||
@ -124,36 +123,17 @@ TEST_CASE("[SyncedAnimationGraph] TestBlendTreeConstruction") {
|
|||||||
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(4));
|
||||||
CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(5));
|
CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(5));
|
||||||
|
|
||||||
print_line("-- Unsorted Nodes:");
|
|
||||||
for (unsigned int i = 0; i < tree_constructor.nodes.size(); i++) {
|
|
||||||
print_line(vformat("%d: node %10s", i, tree_constructor.nodes[i]->name));
|
|
||||||
tree_constructor.node_connection_info[i]._print_subtree();
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalVector<int> mapping = tree_constructor.get_sorted_node_indices();
|
|
||||||
for (unsigned int i = 0; i < mapping.size(); i++) {
|
|
||||||
print_line(vformat("%2d -> %2d", i, mapping[i]));
|
|
||||||
}
|
|
||||||
print_line(vformat("node %d is at index %d", 4, mapping.find(4)));
|
|
||||||
|
|
||||||
tree_constructor.sort_nodes_and_references();
|
tree_constructor.sort_nodes_and_references();
|
||||||
|
|
||||||
print_line("-- Sorted Nodes");
|
|
||||||
for (unsigned int i = 0; i < tree_constructor.nodes.size(); i++) {
|
|
||||||
print_line(vformat("%d: node %10s", i, tree_constructor.nodes[i]->name));
|
|
||||||
tree_constructor.node_connection_info[i]._print_subtree();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that for node i all input nodes have a node index j > i.
|
// 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++) {
|
for (unsigned int i = 0; i < tree_constructor.nodes.size(); i++) {
|
||||||
for (int input_index: tree_constructor.node_connection_info[i].input_subtree_node_indices) {
|
for (int input_index : tree_constructor.node_connection_info[i].input_subtree_node_indices) {
|
||||||
CHECK(input_index > i);
|
CHECK(input_index > i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SimpleAnimationSamplerTest" * doctest::skip(true)) {
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph with an AnimationSampler as root node") {
|
||||||
Ref<AnimationSamplerNode> animation_sampler_node;
|
Ref<AnimationSamplerNode> animation_sampler_node;
|
||||||
animation_sampler_node.instantiate();
|
animation_sampler_node.instantiate();
|
||||||
animation_sampler_node->animation_name = "animation_library/TestAnimation";
|
animation_sampler_node->animation_name = "animation_library/TestAnimation";
|
||||||
@ -175,15 +155,18 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently disabled!
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree] BlendTree with a AnimationSamplerNode connected to the output") {
|
||||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SimpleBlendTreeTest" * doctest::skip(true)) {
|
|
||||||
Ref<SyncedBlendTree> synced_blend_tree_node;
|
Ref<SyncedBlendTree> synced_blend_tree_node;
|
||||||
synced_blend_tree_node.instantiate();
|
synced_blend_tree_node.instantiate();
|
||||||
|
|
||||||
Ref<AnimationSamplerNode> animation_sampler_node;
|
Ref<AnimationSamplerNode> animation_sampler_node;
|
||||||
animation_sampler_node.instantiate();
|
animation_sampler_node.instantiate();
|
||||||
animation_sampler_node->animation_name = "animation_library/TestAnimation";
|
animation_sampler_node->animation_name = "animation_library/TestAnimation";
|
||||||
|
|
||||||
synced_blend_tree_node->add_node(animation_sampler_node);
|
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"));
|
||||||
|
|
||||||
|
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||||
|
|
||||||
synced_animation_graph->set_graph_root_node(synced_blend_tree_node);
|
synced_animation_graph->set_graph_root_node(synced_blend_tree_node);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user