Added saving and loading blend tree resources.
This commit is contained in:
parent
46f940a67c
commit
537712c806
@ -8,6 +8,10 @@ void initialize_synced_blend_tree_module(ModuleInitializationLevel p_level) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClassDB::register_class<SyncedAnimationGraph>();
|
ClassDB::register_class<SyncedAnimationGraph>();
|
||||||
|
ClassDB::register_class<SyncedAnimationNode>();
|
||||||
|
ClassDB::register_class<SyncedBlendTree>();
|
||||||
|
ClassDB::register_class<AnimationSamplerNode>();
|
||||||
|
ClassDB::register_class<AnimationBlend2Node>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninitialize_synced_blend_tree_module(ModuleInitializationLevel p_level) {
|
void uninitialize_synced_blend_tree_module(ModuleInitializationLevel p_level) {
|
||||||
|
|||||||
@ -4,6 +4,100 @@
|
|||||||
|
|
||||||
#include "synced_animation_node.h"
|
#include "synced_animation_node.h"
|
||||||
|
|
||||||
|
void SyncedBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
for (const Ref<SyncedAnimationNode> &node : nodes) {
|
||||||
|
String prop_name = node->name;
|
||||||
|
if (prop_name != "Output") {
|
||||||
|
p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
}
|
||||||
|
p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyncedBlendTree::_get(const StringName &p_name, Variant &r_value) const {
|
||||||
|
String prop_name = p_name;
|
||||||
|
if (prop_name.begins_with("nodes/")) {
|
||||||
|
String node_name = prop_name.get_slicec('/', 1);
|
||||||
|
String what = prop_name.get_slicec('/', 2);
|
||||||
|
int node_index = find_node_index_by_name(node_name);
|
||||||
|
|
||||||
|
if (what == "node") {
|
||||||
|
if (node_index != -1) {
|
||||||
|
r_value = nodes[node_index];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (what == "position") {
|
||||||
|
if (node_index != -1) {
|
||||||
|
r_value = nodes[node_index]->position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (prop_name == "node_connections") {
|
||||||
|
Array conns;
|
||||||
|
conns.resize(tree_builder.connections.size() * 3);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (const BlendTreeConnection &connection : tree_builder.connections) {
|
||||||
|
conns[idx * 3 + 0] = connection.target_node->name;
|
||||||
|
conns[idx * 3 + 1] = connection.target_node->get_node_input_index(connection.target_port_name);
|
||||||
|
conns[idx * 3 + 2] = connection.source_node->name;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
r_value = conns;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyncedBlendTree::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
|
String prop_name = p_name;
|
||||||
|
if (prop_name.begins_with("nodes/")) {
|
||||||
|
String node_name = prop_name.get_slicec('/', 1);
|
||||||
|
String what = prop_name.get_slicec('/', 2);
|
||||||
|
|
||||||
|
if (what == "node") {
|
||||||
|
Ref<SyncedAnimationNode> anode = p_value;
|
||||||
|
if (anode.is_valid()) {
|
||||||
|
anode->name = node_name;
|
||||||
|
add_node(anode);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (what == "position") {
|
||||||
|
int node_index = find_node_index_by_name(node_name);
|
||||||
|
if (node_index > -1) {
|
||||||
|
tree_builder.nodes[node_index]->position = p_value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (prop_name == "node_connections") {
|
||||||
|
Array conns = p_value;
|
||||||
|
ERR_FAIL_COND_V(conns.size() % 3 != 0, false);
|
||||||
|
|
||||||
|
for (int i = 0; i < conns.size(); i += 3) {
|
||||||
|
int target_node_index = find_node_index_by_name(conns[i]);
|
||||||
|
int target_node_port_index = conns[i + 1];
|
||||||
|
int source_node_index = find_node_index_by_name(conns[i + 2]);
|
||||||
|
|
||||||
|
Ref<SyncedAnimationNode> target_node = tree_builder.nodes[target_node_index];
|
||||||
|
Vector<StringName> target_input_names;
|
||||||
|
target_node->get_input_names(target_input_names);
|
||||||
|
|
||||||
|
add_connection(tree_builder.nodes[source_node_index], target_node, target_input_names[target_node_port_index]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationData::sample_from_animation(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d, double p_time) {
|
void AnimationData::sample_from_animation(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d, double p_time) {
|
||||||
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
||||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||||
@ -78,7 +172,59 @@ void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const Local
|
|||||||
output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position);
|
output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimationSamplerNode::set_animation(const StringName &p_name) {
|
||||||
|
animation_name = p_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringName AnimationSamplerNode::get_animation() const {
|
||||||
|
return animation_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationSamplerNode::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationSamplerNode::set_animation);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationSamplerNode::get_animation);
|
||||||
|
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationBlend2Node::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
void AnimationBlend2Node::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
||||||
output = *inputs[0];
|
output = *inputs[0];
|
||||||
output.blend(*inputs[1], blend_weight);
|
output.blend(*inputs[1], blend_weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationBlend2Node::set_use_sync(bool p_sync) {
|
||||||
|
sync = p_sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationBlend2Node::is_using_sync() const {
|
||||||
|
return sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationBlend2Node::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationBlend2Node::set_use_sync);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationBlend2Node::is_using_sync);
|
||||||
|
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationBlend2Node::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
p_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationBlend2Node::_get(const StringName &p_name, Variant &r_value) const {
|
||||||
|
if (p_name == blend_amount) {
|
||||||
|
r_value = blend_weight;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationBlend2Node::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
|
if (p_name == blend_amount) {
|
||||||
|
blend_weight = p_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
@ -225,6 +225,7 @@ public:
|
|||||||
bool active = false;
|
bool active = false;
|
||||||
|
|
||||||
StringName name;
|
StringName name;
|
||||||
|
Vector2 position;
|
||||||
|
|
||||||
virtual ~SyncedAnimationNode() override = default;
|
virtual ~SyncedAnimationNode() override = default;
|
||||||
virtual void initialize(GraphEvaluationContext &context) {}
|
virtual void initialize(GraphEvaluationContext &context) {}
|
||||||
@ -284,6 +285,11 @@ public:
|
|||||||
get_input_names(inputs);
|
get_input_names(inputs);
|
||||||
return inputs.size();
|
return inputs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//protected:
|
||||||
|
// void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
|
// bool _get(const StringName &p_name, Variant &r_value) const;
|
||||||
|
// bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimationSamplerNode : public SyncedAnimationNode {
|
class AnimationSamplerNode : public SyncedAnimationNode {
|
||||||
@ -292,14 +298,22 @@ class AnimationSamplerNode : public SyncedAnimationNode {
|
|||||||
public:
|
public:
|
||||||
StringName animation_name;
|
StringName animation_name;
|
||||||
|
|
||||||
|
void set_animation(const StringName &p_name);
|
||||||
|
StringName get_animation() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ref<Animation> animation;
|
Ref<Animation> animation;
|
||||||
|
|
||||||
void initialize(GraphEvaluationContext &context) override;
|
void initialize(GraphEvaluationContext &context) override;
|
||||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
};
|
};
|
||||||
|
|
||||||
class OutputNode : public SyncedAnimationNode {
|
class OutputNode : public SyncedAnimationNode {
|
||||||
|
GDCLASS(OutputNode, SyncedAnimationNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void get_input_names(Vector<StringName> &inputs) const override {
|
void get_input_names(Vector<StringName> &inputs) const override {
|
||||||
inputs.push_back("Input");
|
inputs.push_back("Input");
|
||||||
@ -307,8 +321,12 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class AnimationBlend2Node : public SyncedAnimationNode {
|
class AnimationBlend2Node : public SyncedAnimationNode {
|
||||||
|
GDCLASS(AnimationBlend2Node, SyncedAnimationNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
StringName blend_amount = PNAME("blend_amount");
|
||||||
float blend_weight = 0.0f;
|
float blend_weight = 0.0f;
|
||||||
|
bool sync = false;
|
||||||
|
|
||||||
void get_input_names(Vector<StringName> &inputs) const override {
|
void get_input_names(Vector<StringName> &inputs) const override {
|
||||||
inputs.push_back("Input0");
|
inputs.push_back("Input0");
|
||||||
@ -316,6 +334,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
||||||
|
|
||||||
|
void set_use_sync(bool p_sync);
|
||||||
|
bool is_using_sync() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
|
bool _get(const StringName &p_name, Variant &r_value) const;
|
||||||
|
bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlendTreeConnection {
|
struct BlendTreeConnection {
|
||||||
@ -375,7 +403,7 @@ struct BlendTreeBuilder {
|
|||||||
|
|
||||||
Vector<Ref<SyncedAnimationNode>> nodes; // All added nodes
|
Vector<Ref<SyncedAnimationNode>> nodes; // All added nodes
|
||||||
LocalVector<NodeConnectionInfo> node_connection_info;
|
LocalVector<NodeConnectionInfo> node_connection_info;
|
||||||
Vector<BlendTreeConnection> connections;
|
LocalVector<BlendTreeConnection> connections;
|
||||||
|
|
||||||
BlendTreeBuilder() {
|
BlendTreeBuilder() {
|
||||||
Ref<OutputNode> output_node;
|
Ref<OutputNode> output_node;
|
||||||
@ -388,7 +416,7 @@ struct BlendTreeBuilder {
|
|||||||
return nodes[0];
|
return nodes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_node_index(const Ref<SyncedAnimationNode> &node) const {
|
int find_node_index(const Ref<SyncedAnimationNode> &node) const {
|
||||||
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;
|
||||||
@ -398,7 +426,29 @@ struct BlendTreeBuilder {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int find_node_index_by_name(const StringName &name) const {
|
||||||
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
|
if (nodes[i]->name == name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void add_node(const Ref<SyncedAnimationNode> &node) {
|
void add_node(const Ref<SyncedAnimationNode> &node) {
|
||||||
|
StringName node_base_name = node->name;
|
||||||
|
if (node_base_name.is_empty()) {
|
||||||
|
node_base_name = node->get_class_name();
|
||||||
|
}
|
||||||
|
node->name = node_base_name;
|
||||||
|
|
||||||
|
int number_suffix = 1;
|
||||||
|
while (find_node_index_by_name(node->name) != -1) {
|
||||||
|
node->name = vformat("%s %d", node_base_name, number_suffix);
|
||||||
|
number_suffix++;
|
||||||
|
}
|
||||||
|
|
||||||
nodes.push_back(node);
|
nodes.push_back(node);
|
||||||
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
||||||
}
|
}
|
||||||
@ -460,12 +510,13 @@ struct BlendTreeBuilder {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int source_node_index = get_node_index(source_node);
|
int source_node_index = find_node_index(source_node);
|
||||||
int target_node_index = get_node_index(target_node);
|
int target_node_index = find_node_index(target_node);
|
||||||
int target_input_port_index = target_node->get_node_input_index(target_port_name);
|
int target_input_port_index = target_node->get_node_input_index(target_port_name);
|
||||||
|
|
||||||
node_connection_info[source_node_index].parent_node_index = target_node_index;
|
node_connection_info[source_node_index].parent_node_index = target_node_index;
|
||||||
node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] = source_node_index;
|
node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] = source_node_index;
|
||||||
|
connections.push_back(BlendTreeConnection{ source_node, target_node, target_port_name });
|
||||||
|
|
||||||
add_index_and_update_subtrees_recursive(source_node_index, target_node_index);
|
add_index_and_update_subtrees_recursive(source_node_index, target_node_index);
|
||||||
|
|
||||||
@ -473,7 +524,7 @@ struct BlendTreeBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_connection_valid(const Ref<SyncedAnimationNode> &source_node, const Ref<SyncedAnimationNode> &target_node, StringName target_port_name) {
|
bool is_connection_valid(const Ref<SyncedAnimationNode> &source_node, const Ref<SyncedAnimationNode> &target_node, StringName target_port_name) {
|
||||||
int source_node_index = get_node_index(source_node);
|
int source_node_index = find_node_index(source_node);
|
||||||
if (source_node_index == -1) {
|
if (source_node_index == -1) {
|
||||||
print_error("Cannot connect nodes: source node not found.");
|
print_error("Cannot connect nodes: source node not found.");
|
||||||
return false;
|
return false;
|
||||||
@ -484,17 +535,12 @@ struct BlendTreeBuilder {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int target_node_index = get_node_index(target_node);
|
int target_node_index = find_node_index(target_node);
|
||||||
if (target_node_index == -1) {
|
if (target_node_index == -1) {
|
||||||
print_error("Cannot connect nodes: target node not found.");
|
print_error("Cannot connect nodes: target node not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target_node == get_output_node() && connections.size() > 0) {
|
|
||||||
print_error("Cannot add connection to output node: output node is already connected");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<StringName> target_inputs;
|
Vector<StringName> target_inputs;
|
||||||
target_node->get_input_names(target_inputs);
|
target_node->get_input_names(target_inputs);
|
||||||
|
|
||||||
@ -519,6 +565,8 @@ struct BlendTreeBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class SyncedBlendTree : public SyncedAnimationNode {
|
class SyncedBlendTree : public SyncedAnimationNode {
|
||||||
|
GDCLASS(SyncedBlendTree, SyncedAnimationNode);
|
||||||
|
|
||||||
Vector<Ref<SyncedAnimationNode>> nodes;
|
Vector<Ref<SyncedAnimationNode>> nodes;
|
||||||
|
|
||||||
BlendTreeBuilder tree_builder;
|
BlendTreeBuilder tree_builder;
|
||||||
@ -557,6 +605,11 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
|
bool _get(const StringName &p_name, Variant &r_value) const;
|
||||||
|
bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct NodeRuntimeData {
|
struct NodeRuntimeData {
|
||||||
Vector<Ref<SyncedAnimationNode>> input_nodes;
|
Vector<Ref<SyncedAnimationNode>> input_nodes;
|
||||||
@ -569,14 +622,12 @@ public:
|
|||||||
return tree_builder.nodes[0];
|
return tree_builder.nodes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_node_index(const Ref<SyncedAnimationNode> &node) const {
|
int find_node_index(const Ref<SyncedAnimationNode> &node) const {
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
return tree_builder.find_node_index(node);
|
||||||
if (nodes[i] == node) {
|
}
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
int find_node_index_by_name(const StringName &name) const {
|
||||||
|
return tree_builder.find_node_index_by_name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_node(const Ref<SyncedAnimationNode> &node) {
|
void add_node(const Ref<SyncedAnimationNode> &node) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../synced_animation_graph.h"
|
#include "../synced_animation_graph.h"
|
||||||
|
#include "scene/animation/animation_tree.h"
|
||||||
#include "scene/main/window.h"
|
#include "scene/main/window.h"
|
||||||
|
|
||||||
#include "tests/test_macros.h"
|
#include "tests/test_macros.h"
|
||||||
@ -105,8 +106,8 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
CHECK(tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
CHECK(tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
||||||
|
|
||||||
// Ensure that subtree is properly updated
|
// Ensure that subtree is properly updated
|
||||||
int sampler0_index = tree_constructor.get_node_index(animation_sampler_node0);
|
int sampler0_index = tree_constructor.find_node_index(animation_sampler_node0);
|
||||||
int blend0_index = tree_constructor.get_node_index(node_blend0);
|
int blend0_index = tree_constructor.find_node_index(node_blend0);
|
||||||
CHECK(tree_constructor.node_connection_info[blend0_index].input_subtree_node_indices.has(sampler0_index));
|
CHECK(tree_constructor.node_connection_info[blend0_index].input_subtree_node_indices.has(sampler0_index));
|
||||||
|
|
||||||
// Connect blend0 to blend1
|
// Connect blend0 to blend1
|
||||||
@ -118,8 +119,8 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
CHECK(tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input1"));
|
CHECK(tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input1"));
|
||||||
|
|
||||||
// Ensure that subtree is properly updated
|
// Ensure that subtree is properly updated
|
||||||
int sampler1_index = tree_constructor.get_node_index(animation_sampler_node0);
|
int sampler1_index = tree_constructor.find_node_index(animation_sampler_node0);
|
||||||
int blend1_index = tree_constructor.get_node_index(node_blend1);
|
int blend1_index = tree_constructor.find_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(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(sampler0_index));
|
||||||
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(blend0_index));
|
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(blend0_index));
|
||||||
@ -262,12 +263,7 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
|
|
||||||
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
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);
|
int blend2_node_index = synced_blend_tree_node->find_node_index(blend2_node);
|
||||||
// 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];
|
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[0] == animation_sampler_node_a);
|
||||||
@ -288,6 +284,48 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
CHECK(hip_bone_position.x == doctest::Approx(0.75));
|
CHECK(hip_bone_position.x == doctest::Approx(0.75));
|
||||||
CHECK(hip_bone_position.y == doctest::Approx(1.5));
|
CHECK(hip_bone_position.y == doctest::Approx(1.5));
|
||||||
CHECK(hip_bone_position.z == doctest::Approx(2.25));
|
CHECK(hip_bone_position.z == doctest::Approx(2.25));
|
||||||
|
|
||||||
|
// Test saving and loading of the blend tree to a resource
|
||||||
|
ResourceSaver::save(synced_blend_tree_node, "synced_blend_tree_node.tres");
|
||||||
|
|
||||||
|
REQUIRE(ClassDB::class_exists("AnimationSamplerNode"));
|
||||||
|
|
||||||
|
// Load blend tree
|
||||||
|
Ref<SyncedBlendTree> loaded_synced_blend_tree = ResourceLoader::load("synced_blend_tree_node.tres");
|
||||||
|
REQUIRE(loaded_synced_blend_tree.is_valid());
|
||||||
|
|
||||||
|
loaded_synced_blend_tree->initialize(synced_animation_graph->get_context());
|
||||||
|
synced_animation_graph->set_graph_root_node(loaded_synced_blend_tree);
|
||||||
|
|
||||||
|
// Re-evaluate using a different time. All animation samplers will start again from 0.
|
||||||
|
SceneTree::get_singleton()->process(0.2);
|
||||||
|
|
||||||
|
hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||||
|
|
||||||
|
CHECK(hip_bone_position.x == doctest::Approx(0.3));
|
||||||
|
CHECK(hip_bone_position.y == doctest::Approx(0.6));
|
||||||
|
CHECK(hip_bone_position.z == doctest::Approx(0.9));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree][Blend2Node] Serialize AnimationTree" * doctest::skip(true)) {
|
||||||
|
AnimationTree *animation_tree = memnew(AnimationTree);
|
||||||
|
|
||||||
|
character_node->add_child(animation_tree);
|
||||||
|
animation_tree->set_animation_player(player_node->get_path());
|
||||||
|
animation_tree->set_root_node(character_node->get_path());
|
||||||
|
Ref<AnimationNodeAnimation> animation_node_animation;
|
||||||
|
animation_node_animation.instantiate();
|
||||||
|
animation_node_animation->set_animation("TestAnimationA");
|
||||||
|
|
||||||
|
Ref<AnimationNodeBlendTree> animation_node_blend_tree;
|
||||||
|
animation_node_blend_tree.instantiate();
|
||||||
|
animation_node_blend_tree->add_node("SamplerTestAnimationA", animation_node_animation, Vector2(0, 0));
|
||||||
|
animation_node_blend_tree->connect_node("output", 0, "SamplerTestAnimationA");
|
||||||
|
animation_node_blend_tree->setup_local_to_scene();
|
||||||
|
|
||||||
|
animation_tree->set_root_animation_node(animation_node_blend_tree);
|
||||||
|
|
||||||
|
ResourceSaver::save(animation_node_blend_tree, "animation_tree.tres");
|
||||||
}
|
}
|
||||||
|
|
||||||
} //namespace TestSyncedAnimationGraph
|
} //namespace TestSyncedAnimationGraph
|
||||||
Loading…
x
Reference in New Issue
Block a user