Compare commits
No commits in common. "6330e34ea55438ad03be16ffcb9fc0d423b89fd0" and "67acbb35b333c73264f3d56a61ef6d6ab98e9200" have entirely different histories.
6330e34ea5
...
67acbb35b3
@ -23,7 +23,7 @@ void BLTAnimationGraph::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tree_root", "animation_node"), &BLTAnimationGraph::set_root_animation_node);
|
||||
ClassDB::bind_method(D_METHOD("get_tree_root"), &BLTAnimationGraph::get_root_animation_node);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "BLTAnimationNode"), "set_tree_root", "get_tree_root");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "SyncedAnimationNode"), "set_tree_root", "get_tree_root");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &BLTAnimationGraph::set_skeleton);
|
||||
ClassDB::bind_method(D_METHOD("get_skeleton"), &BLTAnimationGraph::get_skeleton);
|
||||
@ -51,7 +51,7 @@ void BLTAnimationGraph::_update_properties_for_node(const String &p_base_path, R
|
||||
p_node->get_child_nodes(&children);
|
||||
|
||||
for (const Ref<BLTAnimationNode> &child_node : children) {
|
||||
_update_properties_for_node(p_base_path + child_node->get_name() + "/", child_node);
|
||||
_update_properties_for_node(p_base_path + child_node->name + "/", child_node);
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,10 +295,6 @@ void BLTAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (graph_context.skeleton_3d == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GodotProfileZone("SyncedAnimationGraph::_process_graph");
|
||||
|
||||
_update_properties();
|
||||
|
||||
@ -5,17 +5,9 @@
|
||||
#include "blendalot_animation_node.h"
|
||||
|
||||
void BLTAnimationNode::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_position", "position"), &BLTAnimationNode::set_position);
|
||||
ClassDB::bind_method(D_METHOD("get_position"), &BLTAnimationNode::get_position);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "set_position", "get_position");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("tree_changed"));
|
||||
ADD_SIGNAL(MethodInfo("animation_node_renamed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
|
||||
ADD_SIGNAL(MethodInfo("animation_node_removed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "name")));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_input_names"), &BLTAnimationNode::get_input_names_as_typed_array);
|
||||
ClassDB::bind_method(D_METHOD("get_input_count"), &BLTAnimationNode::get_input_count);
|
||||
ClassDB::bind_method(D_METHOD("get_input_index"), &BLTAnimationNode::get_input_index);
|
||||
}
|
||||
|
||||
void BLTAnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
@ -50,15 +42,8 @@ void BLTAnimationNode::_animation_node_removed(const ObjectID &p_oid, const Stri
|
||||
|
||||
void BLTAnimationNodeBlendTree::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_node", "animation_node"), &BLTAnimationNodeBlendTree::add_node);
|
||||
ClassDB::bind_method(D_METHOD("remove_node", "animation_node"), &BLTAnimationNodeBlendTree::remove_node);
|
||||
ClassDB::bind_method(D_METHOD("get_node", "node_name"), &BLTAnimationNodeBlendTree::get_node);
|
||||
ClassDB::bind_method(D_METHOD("get_output_node"), &BLTAnimationNodeBlendTree::get_output_node);
|
||||
ClassDB::bind_method(D_METHOD("get_node_names"), &BLTAnimationNodeBlendTree::get_node_names_as_typed_array);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_connection_valid", "source_node", "target_node", "target_port_name"), &BLTAnimationNodeBlendTree::is_connection_valid);
|
||||
ClassDB::bind_method(D_METHOD("add_connection", "source_node", "target_node", "target_port_name"), &BLTAnimationNodeBlendTree::add_connection);
|
||||
ClassDB::bind_method(D_METHOD("remove_connection", "source_node", "target_node", "target_port_name"), &BLTAnimationNodeBlendTree::remove_connection);
|
||||
ClassDB::bind_method(D_METHOD("get_connections"), &BLTAnimationNodeBlendTree::get_connections_as_array);
|
||||
|
||||
BIND_CONSTANT(CONNECTION_OK);
|
||||
BIND_CONSTANT(CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED);
|
||||
@ -72,7 +57,7 @@ void BLTAnimationNodeBlendTree::_bind_methods() {
|
||||
|
||||
void BLTAnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||
String prop_name = node->get_name();
|
||||
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));
|
||||
}
|
||||
@ -108,9 +93,9 @@ bool BLTAnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_value)
|
||||
|
||||
int idx = 0;
|
||||
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
||||
conns[idx * 3 + 0] = connection.target_node->get_name();
|
||||
conns[idx * 3 + 0] = connection.target_node->name;
|
||||
conns[idx * 3 + 1] = connection.target_node->get_input_index(connection.target_port_name);
|
||||
conns[idx * 3 + 2] = connection.source_node->get_name();
|
||||
conns[idx * 3 + 2] = connection.source_node->name;
|
||||
idx++;
|
||||
}
|
||||
|
||||
@ -130,7 +115,7 @@ bool BLTAnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_
|
||||
if (what == "node") {
|
||||
Ref<BLTAnimationNode> anode = p_value;
|
||||
if (anode.is_valid()) {
|
||||
anode->set_name(node_name);
|
||||
anode->name = node_name;
|
||||
add_node(anode);
|
||||
}
|
||||
return true;
|
||||
@ -153,7 +138,8 @@ bool BLTAnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_
|
||||
int source_node_index = find_node_index_by_name(conns[i + 2]);
|
||||
|
||||
Ref<BLTAnimationNode> target_node = tree_graph.nodes[target_node_index];
|
||||
Vector<StringName> target_input_names = target_node->get_input_names();
|
||||
Vector<StringName> target_input_names;
|
||||
target_node->get_input_names(target_input_names);
|
||||
|
||||
add_connection(tree_graph.nodes[source_node_index], target_node, target_input_names[target_node_port_index]);
|
||||
}
|
||||
@ -269,13 +255,11 @@ void AnimationDataAllocator::register_track_values(const Ref<Animation> &animati
|
||||
}
|
||||
|
||||
bool BLTAnimationNodeSampler::initialize(GraphEvaluationContext &context) {
|
||||
if (!BLTAnimationNode::initialize(context)) {
|
||||
return false;
|
||||
}
|
||||
BLTAnimationNode::initialize(context);
|
||||
|
||||
animation = context.animation_player->get_animation(animation_name);
|
||||
if (!animation.is_valid()) {
|
||||
print_error(vformat("Cannot initialize node %s: animation '%s' not found in animation player.", get_name(), animation_name));
|
||||
print_error(vformat("Cannot initialize node %s: animation '%s' not found in animation player.", name, animation_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -428,7 +412,7 @@ bool BLTAnimationNodeBlend2::_set(const StringName &p_name, const Variant &p_val
|
||||
BLTAnimationNodeBlendTree::BLTBlendTreeGraph::BLTBlendTreeGraph() {
|
||||
Ref<BLTAnimationNodeOutput> output_node;
|
||||
output_node.instantiate();
|
||||
output_node->set_name("Output");
|
||||
output_node->name = "Output";
|
||||
add_node(output_node);
|
||||
}
|
||||
|
||||
@ -437,7 +421,7 @@ Ref<BLTAnimationNode> BLTAnimationNodeBlendTree::BLTBlendTreeGraph::get_output_n
|
||||
}
|
||||
|
||||
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index(const Ref<BLTAnimationNode> &node) const {
|
||||
for (uint32_t i = 0; i < nodes.size(); i++) {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
if (nodes[i] == node) {
|
||||
return i;
|
||||
}
|
||||
@ -447,8 +431,8 @@ int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index(const Ref<BLTA
|
||||
}
|
||||
|
||||
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const StringName &name) const {
|
||||
for (uint32_t i = 0; i < nodes.size(); i++) {
|
||||
if (nodes[i]->get_name() == name) {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
if (nodes[i]->name == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -457,64 +441,20 @@ int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimationNode> &node) {
|
||||
StringName node_base_name = node->get_name();
|
||||
StringName node_base_name = node->name;
|
||||
if (node_base_name.is_empty()) {
|
||||
node_base_name = node->get_class_name();
|
||||
}
|
||||
node->set_name(node_base_name);
|
||||
node->name = node_base_name;
|
||||
|
||||
int number_suffix = 1;
|
||||
while (find_node_index_by_name(node->get_name()) != -1) {
|
||||
node->set_name(vformat("%s %d", node_base_name, number_suffix));
|
||||
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);
|
||||
|
||||
NodeConnectionInfo connection_info(node.ptr());
|
||||
connection_info.input_subtree_node_indices.insert(nodes.size() - 1);
|
||||
node_connection_info.push_back(connection_info);
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_node(const Ref<BLTAnimationNode> &node) {
|
||||
if (node == get_output_node()) {
|
||||
// Output node not allowed to be removed
|
||||
return;
|
||||
}
|
||||
|
||||
int removed_node_index = find_node_index(node);
|
||||
assert(removed_node_index >= 0);
|
||||
|
||||
// Remove all connections to and from this node
|
||||
for (int i = static_cast<int>(connections.size()) - 1; i >= 0; i--) {
|
||||
if (connections[i].source_node == node || connections[i].target_node == node) {
|
||||
remove_connection(connections[i].source_node, connections[i].target_node, connections[i].target_port_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the data directly related to this node
|
||||
node_connection_info.remove_at(removed_node_index);
|
||||
nodes.remove_at(removed_node_index);
|
||||
|
||||
// Ensure all indices are cleaned up.
|
||||
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
||||
for (unsigned int j = 0; j < connection_info.connected_child_node_index_at_port.size(); j++) {
|
||||
if (connection_info.connected_child_node_index_at_port[j] > removed_node_index) {
|
||||
connection_info.connected_child_node_index_at_port[j] = connection_info.connected_child_node_index_at_port[j] - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Map connected subtrees
|
||||
HashSet<int> old_indices = connection_info.input_subtree_node_indices;
|
||||
connection_info.input_subtree_node_indices.clear();
|
||||
for (int old_index : old_indices) {
|
||||
if (old_index > removed_node_index) {
|
||||
connection_info.input_subtree_node_indices.insert(old_index - 1);
|
||||
} else {
|
||||
connection_info.input_subtree_node_indices.insert(old_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
||||
@ -555,69 +495,18 @@ void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_recursive(int node
|
||||
result.push_back(node_index);
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node_index, int node_parent_index) {
|
||||
if (node_parent_index == -1) {
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node, int node_parent) {
|
||||
if (node_parent == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
node_connection_info[node_parent_index].input_subtree_node_indices.insert(node_index);
|
||||
node_connection_info[node_parent].input_subtree_node_indices.insert(node);
|
||||
|
||||
for (int index : node_connection_info[node_index].input_subtree_node_indices) {
|
||||
node_connection_info[node_parent_index].input_subtree_node_indices.insert(index);
|
||||
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
||||
node_connection_info[node_parent].input_subtree_node_indices.insert(index);
|
||||
}
|
||||
|
||||
add_index_and_update_subtrees_recursive(node_parent_index, node_connection_info[node_parent_index].parent_node_index);
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_subtree_and_update_subtrees_recursive(int node_index, const HashSet<int> &removed_subtree_indices) {
|
||||
NodeConnectionInfo &connection_info = node_connection_info[node_index];
|
||||
|
||||
for (int subtree_node_index : removed_subtree_indices) {
|
||||
connection_info.input_subtree_node_indices.erase(subtree_node_index);
|
||||
}
|
||||
|
||||
if (connection_info.parent_node_index != -1) {
|
||||
remove_subtree_and_update_subtrees_recursive(connection_info.parent_node_index, removed_subtree_indices);
|
||||
}
|
||||
}
|
||||
|
||||
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const {
|
||||
int source_node_index = find_node_index(source_node);
|
||||
if (source_node_index == -1) {
|
||||
print_error("Cannot connect nodes: source node not found.");
|
||||
return CONNECTION_ERROR_NO_SOURCE_NODE;
|
||||
}
|
||||
|
||||
if (node_connection_info[source_node_index].parent_node_index != -1) {
|
||||
print_error("Cannot connect node: source node already has a parent.");
|
||||
return CONNECTION_ERROR_PARENT_EXISTS;
|
||||
}
|
||||
|
||||
int target_node_index = find_node_index(target_node);
|
||||
if (target_node_index == -1) {
|
||||
print_error("Cannot connect nodes: target node not found.");
|
||||
return CONNECTION_ERROR_NO_TARGET_NODE;
|
||||
}
|
||||
|
||||
Vector<StringName> target_inputs = target_node->get_input_names();
|
||||
|
||||
if (!target_inputs.has(target_port_name)) {
|
||||
print_error("Cannot connect nodes: target port not found.");
|
||||
return CONNECTION_ERROR_TARGET_PORT_NOT_FOUND;
|
||||
}
|
||||
|
||||
int target_input_port_index = target_node->get_input_index(target_port_name);
|
||||
if (node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] != -1) {
|
||||
print_error("Cannot connect node: target port already connected");
|
||||
return CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
if (node_connection_info[source_node_index].input_subtree_node_indices.has(target_node_index)) {
|
||||
print_error("Cannot connect node: connection would create loop.");
|
||||
return CONNECTION_ERROR_CONNECTION_CREATES_LOOP;
|
||||
}
|
||||
|
||||
return CONNECTION_OK;
|
||||
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].parent_node_index);
|
||||
}
|
||||
|
||||
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||
@ -639,32 +528,41 @@ BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTr
|
||||
return CONNECTION_OK;
|
||||
}
|
||||
|
||||
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_connection_index(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) const {
|
||||
for (uint32_t i = 0; i < connections.size(); i++) {
|
||||
if (connections[i].source_node == source_node && connections[i].target_node == target_node && connections[i].target_port_name == target_port_name) {
|
||||
return i;
|
||||
}
|
||||
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const {
|
||||
int source_node_index = find_node_index(source_node);
|
||||
if (source_node_index == -1) {
|
||||
print_error("Cannot connect nodes: source node not found.");
|
||||
return CONNECTION_ERROR_NO_SOURCE_NODE;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (node_connection_info[source_node_index].parent_node_index != -1) {
|
||||
print_error("Cannot connect node: source node already has a parent.");
|
||||
return CONNECTION_ERROR_PARENT_EXISTS;
|
||||
}
|
||||
|
||||
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||
int source_node_index = find_node_index(source_node);
|
||||
NodeConnectionInfo &connection_info = node_connection_info[source_node_index];
|
||||
int target_node_index = find_node_index(target_node);
|
||||
if (target_node_index == -1) {
|
||||
print_error("Cannot connect nodes: target node not found.");
|
||||
return CONNECTION_ERROR_NO_TARGET_NODE;
|
||||
}
|
||||
|
||||
if (connection_info.parent_node_index != -1) {
|
||||
NodeConnectionInfo &parent_connection_info = node_connection_info[connection_info.parent_node_index];
|
||||
parent_connection_info.input_subtree_node_indices.erase(source_node_index);
|
||||
parent_connection_info.connected_child_node_index_at_port[target_node->get_input_index(target_port_name)] = -1;
|
||||
Vector<StringName> target_inputs;
|
||||
target_node->get_input_names(target_inputs);
|
||||
|
||||
remove_subtree_and_update_subtrees_recursive(connection_info.parent_node_index, connection_info.input_subtree_node_indices);
|
||||
if (!target_inputs.has(target_port_name)) {
|
||||
print_error("Cannot connect nodes: target port not found.");
|
||||
return CONNECTION_ERROR_TARGET_PORT_NOT_FOUND;
|
||||
}
|
||||
|
||||
connection_info.parent_node_index = -1;
|
||||
int target_input_port_index = target_node->get_input_index(target_port_name);
|
||||
if (node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] != -1) {
|
||||
print_error("Cannot connect node: target port already connected");
|
||||
return CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
uint32_t connection_index = find_connection_index(source_node, target_node, target_port_name);
|
||||
assert(connection_index >= 0);
|
||||
connections.remove_at(connection_index);
|
||||
if (node_connection_info[source_node_index].input_subtree_node_indices.has(target_node_index)) {
|
||||
print_error("Cannot connect node: connection would create loop.");
|
||||
return CONNECTION_ERROR_CONNECTION_CREATES_LOOP;
|
||||
}
|
||||
|
||||
return CONNECTION_OK;
|
||||
|
||||
@ -262,6 +262,7 @@ public:
|
||||
NodeTimeInfo node_time_info;
|
||||
bool active = false;
|
||||
|
||||
StringName name;
|
||||
Vector2 position;
|
||||
|
||||
virtual ~BLTAnimationNode() override = default;
|
||||
@ -284,7 +285,6 @@ public:
|
||||
node_time_info.loop_mode = input_nodes[0]->node_time_info.loop_mode;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void update_time(double p_time) {
|
||||
if (node_time_info.is_synced) {
|
||||
node_time_info.sync_position = p_time;
|
||||
@ -293,7 +293,6 @@ public:
|
||||
node_time_info.position += p_time;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -301,32 +300,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void set_position(const Vector2 &p_position) {
|
||||
position = p_position;
|
||||
}
|
||||
|
||||
Vector2 get_position() const {
|
||||
return position;
|
||||
}
|
||||
|
||||
virtual Vector<StringName> get_input_names() const { return {}; }
|
||||
|
||||
TypedArray<StringName> get_input_names_as_typed_array() const {
|
||||
TypedArray<StringName> typed_arr;
|
||||
Vector<StringName> vec = get_input_names();
|
||||
typed_arr.resize(vec.size());
|
||||
for (uint32_t i = 0; i < vec.size(); i++) {
|
||||
typed_arr[i] = vec[i];
|
||||
}
|
||||
return typed_arr;
|
||||
}
|
||||
virtual void get_input_names(Vector<StringName> &inputs) const {}
|
||||
|
||||
int get_input_index(const StringName &port_name) const {
|
||||
Vector<StringName> inputs = get_input_names();
|
||||
Vector<StringName> inputs;
|
||||
get_input_names(inputs);
|
||||
return inputs.find(port_name);
|
||||
}
|
||||
int get_input_count() const {
|
||||
Vector<StringName> inputs = get_input_names();
|
||||
Vector<StringName> inputs;
|
||||
get_input_names(inputs);
|
||||
return inputs.size();
|
||||
}
|
||||
|
||||
@ -358,8 +341,8 @@ class BLTAnimationNodeOutput : public BLTAnimationNode {
|
||||
GDCLASS(BLTAnimationNodeOutput, BLTAnimationNode);
|
||||
|
||||
public:
|
||||
Vector<StringName> get_input_names() const override {
|
||||
return { "Output" };
|
||||
void get_input_names(Vector<StringName> &inputs) const override {
|
||||
inputs.push_back("Input");
|
||||
}
|
||||
};
|
||||
|
||||
@ -370,21 +353,20 @@ public:
|
||||
float blend_weight = 0.0f;
|
||||
bool sync = true;
|
||||
|
||||
Vector<StringName> get_input_names() const override {
|
||||
return { "Input0", "Input1" };
|
||||
void get_input_names(Vector<StringName> &inputs) const override {
|
||||
inputs.push_back("Input0");
|
||||
inputs.push_back("Input1");
|
||||
}
|
||||
|
||||
bool initialize(GraphEvaluationContext &context) override {
|
||||
if (!BLTAnimationNode::initialize(context)) {
|
||||
return false;
|
||||
}
|
||||
bool result = BLTAnimationNode::initialize(context);
|
||||
|
||||
if (sync) {
|
||||
// TODO: do we always want looping in this case or do we traverse the graph to check what's reasonable?
|
||||
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||
input_nodes[0]->active = true;
|
||||
@ -441,9 +423,9 @@ private:
|
||||
};
|
||||
|
||||
struct BLTBlendTreeConnection {
|
||||
Ref<BLTAnimationNode> source_node = nullptr;
|
||||
Ref<BLTAnimationNode> target_node = nullptr;
|
||||
StringName target_port_name = "";
|
||||
const Ref<BLTAnimationNode> source_node = nullptr;
|
||||
const Ref<BLTAnimationNode> target_node = nullptr;
|
||||
const StringName target_port_name = "";
|
||||
};
|
||||
|
||||
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
||||
@ -495,43 +477,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void _print_subtree() const {
|
||||
String result = vformat("subtree node indices (%d): ", input_subtree_node_indices.size());
|
||||
bool is_first = true;
|
||||
for (int index : input_subtree_node_indices) {
|
||||
if (is_first) {
|
||||
result += vformat("%d", index);
|
||||
is_first = false;
|
||||
} else {
|
||||
result += vformat(", %d", index);
|
||||
}
|
||||
}
|
||||
print_line(result);
|
||||
}
|
||||
void _print_subtree() const;
|
||||
};
|
||||
|
||||
LocalVector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
||||
Vector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
||||
LocalVector<NodeConnectionInfo> node_connection_info;
|
||||
LocalVector<BLTBlendTreeConnection> connections;
|
||||
|
||||
BLTBlendTreeGraph();
|
||||
|
||||
Ref<BLTAnimationNode> get_output_node();
|
||||
|
||||
int find_node_index(const Ref<BLTAnimationNode> &node) const;
|
||||
int find_node_index_by_name(const StringName &name) const;
|
||||
void sort_nodes_and_references();
|
||||
LocalVector<int> get_sorted_node_indices();
|
||||
void sort_nodes_recursive(int node_index, LocalVector<int> &result);
|
||||
void add_index_and_update_subtrees_recursive(int node_index, int node_parent_index);
|
||||
void remove_subtree_and_update_subtrees_recursive(int node, const HashSet<int> &removed_subtree_indices);
|
||||
void add_index_and_update_subtrees_recursive(int node, int node_parent);
|
||||
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const;
|
||||
|
||||
void add_node(const Ref<BLTAnimationNode> &node);
|
||||
void remove_node(const Ref<BLTAnimationNode> &node);
|
||||
|
||||
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const;
|
||||
ConnectionError add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name);
|
||||
int find_connection_index(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) const;
|
||||
ConnectionError remove_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name);
|
||||
};
|
||||
|
||||
private:
|
||||
@ -545,7 +511,7 @@ private:
|
||||
|
||||
void setup_runtime_data() {
|
||||
// Add nodes and allocate runtime data
|
||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
const Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||
|
||||
NodeRuntimeData node_runtime_data;
|
||||
@ -558,7 +524,7 @@ private:
|
||||
}
|
||||
|
||||
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||
|
||||
@ -583,6 +549,10 @@ public:
|
||||
};
|
||||
LocalVector<NodeRuntimeData> _node_runtime_data;
|
||||
|
||||
Ref<BLTAnimationNode> get_output_node() const {
|
||||
return tree_graph.nodes[0];
|
||||
}
|
||||
|
||||
int find_node_index(const Ref<BLTAnimationNode> &node) const {
|
||||
return tree_graph.find_node_index(node);
|
||||
}
|
||||
@ -591,6 +561,14 @@ public:
|
||||
return tree_graph.find_node_index_by_name(name);
|
||||
}
|
||||
|
||||
Ref<BLTAnimationNode> get_node(int node_index) {
|
||||
if (node_index < 0 || node_index > tree_graph.nodes.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tree_graph.nodes[node_index];
|
||||
}
|
||||
|
||||
void add_node(const Ref<BLTAnimationNode> &node) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot add node to BlendTree: BlendTree already initialized.");
|
||||
@ -600,60 +578,6 @@ public:
|
||||
tree_graph.add_node(node);
|
||||
}
|
||||
|
||||
void remove_node(const Ref<BLTAnimationNode> &node) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot remove node from BlendTree: BlendTree already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
tree_graph.remove_node(node);
|
||||
}
|
||||
|
||||
TypedArray<StringName> get_node_names_as_typed_array() const {
|
||||
Vector<StringName> vec;
|
||||
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||
vec.push_back(node->get_name());
|
||||
}
|
||||
|
||||
TypedArray<StringName> typed_arr;
|
||||
typed_arr.resize(vec.size());
|
||||
for (uint32_t i = 0; i < vec.size(); i++) {
|
||||
typed_arr[i] = vec[i];
|
||||
}
|
||||
return typed_arr;
|
||||
}
|
||||
|
||||
Ref<BLTAnimationNode> get_node(const StringName &node_name) const {
|
||||
int node_index = tree_graph.find_node_index_by_name(node_name);
|
||||
|
||||
if (node_index >= 0) {
|
||||
return tree_graph.nodes[node_index];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<BLTAnimationNode> get_node_by_index(int node_index) const {
|
||||
if (node_index < 0 || node_index > static_cast<int>(tree_graph.nodes.size())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tree_graph.nodes[node_index];
|
||||
}
|
||||
|
||||
Ref<BLTAnimationNode> get_output_node() const {
|
||||
return tree_graph.nodes[0];
|
||||
}
|
||||
|
||||
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot add connection to BlendTree: BlendTree already initialized.");
|
||||
return CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
return tree_graph.is_connection_valid(source_node, target_node, target_port_name);
|
||||
}
|
||||
|
||||
ConnectionError add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot add connection to BlendTree: BlendTree already initialized.");
|
||||
@ -663,32 +587,8 @@ public:
|
||||
return tree_graph.add_connection(source_node, target_node, target_port_name);
|
||||
}
|
||||
|
||||
ConnectionError remove_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot remove connection to BlendTree: BlendTree already initialized.");
|
||||
return CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
return tree_graph.remove_connection(source_node, target_node, target_port_name);
|
||||
}
|
||||
|
||||
Array get_connections_as_array() const {
|
||||
Array result;
|
||||
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
||||
result.push_back(connection.source_node);
|
||||
result.push_back(connection.target_node);
|
||||
result.push_back(connection.target_port_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// overrides from BLTAnimationNode
|
||||
// overrides from SyncedAnimationNode
|
||||
bool initialize(GraphEvaluationContext &context) override {
|
||||
if (!BLTAnimationNode::initialize(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sort_nodes();
|
||||
setup_runtime_data();
|
||||
|
||||
@ -708,7 +608,7 @@ public:
|
||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
||||
|
||||
tree_graph.nodes[0]->active = true;
|
||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
@ -722,7 +622,7 @@ public:
|
||||
|
||||
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||
GodotProfileZone("SyncedBlendTree::calculate_sync_track");
|
||||
for (uint32_t i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
@ -741,7 +641,7 @@ public:
|
||||
tree_graph.nodes[0]->node_time_info.delta = p_delta;
|
||||
tree_graph.nodes[0]->node_time_info.position += p_delta;
|
||||
|
||||
for (uint32_t i = 1; i < tree_graph.nodes.size(); i++) {
|
||||
for (int i = 1; i < tree_graph.nodes.size(); i++) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
@ -761,7 +661,7 @@ public:
|
||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
||||
ZoneScopedN("SyncedBlendTree::evaluate");
|
||||
|
||||
for (uint32_t i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
|
||||
@ -1,286 +1,16 @@
|
||||
@tool
|
||||
class_name BlendalotMainPanel
|
||||
extends Control
|
||||
|
||||
@onready var blend_tree_graph_edit: GraphEdit = %BlendTreeGraphEdit
|
||||
@onready var file_name_line_edit: LineEdit = %FileNameLineEdit
|
||||
@onready var tree_option_button: OptionButton = %TreeOptionButton
|
||||
@onready var add_node_popup_menu: PopupMenu = %AddNodePopupMenu
|
||||
|
||||
var blend_tree:BLTAnimationNodeBlendTree = BLTAnimationNodeBlendTree.new()
|
||||
var blend_tree_node_to_graph_node = {}
|
||||
var graph_node_to_blend_tree_node = {}
|
||||
var selected_nodes = {}
|
||||
var new_node_position = Vector2.INF
|
||||
|
||||
var registered_nodes = [
|
||||
"BLTAnimationNodeSampler",
|
||||
"BLTAnimationNodeBlend2",
|
||||
"BLTAnimationNodeBlendTree",
|
||||
]
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
add_node_popup_menu.clear(true)
|
||||
|
||||
for node_name in registered_nodes:
|
||||
add_node_popup_menu.add_item(node_name)
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
func create_node_for_blt_node(blt_node: BLTAnimationNode) -> GraphNode:
|
||||
var result_graph_node:GraphNode = GraphNode.new()
|
||||
result_graph_node.name = blt_node.resource_name
|
||||
result_graph_node.title = blt_node.resource_name
|
||||
result_graph_node.position_offset = blt_node.position
|
||||
|
||||
var result_slot_offset = 0
|
||||
|
||||
if (blt_node.get_class() != "BLTAnimationNodeOutput"):
|
||||
result_slot_offset = 1
|
||||
var output_slot_label:Label = Label.new()
|
||||
output_slot_label.text = "Result"
|
||||
result_graph_node.add_child(output_slot_label)
|
||||
result_graph_node.set_slot(0, false, 1, Color.WHITE, true, 1, Color.WHITE)
|
||||
|
||||
var inputs = blt_node.get_input_names()
|
||||
for i in range(len(inputs)):
|
||||
var slot_label:Label = Label.new()
|
||||
slot_label.text = inputs[i]
|
||||
result_graph_node.add_child(slot_label)
|
||||
result_graph_node.set_slot(i + result_slot_offset, true, 1, Color.WHITE, false, 1, Color.BLACK)
|
||||
|
||||
return result_graph_node
|
||||
|
||||
|
||||
func _on_add_node_button_pressed() -> void:
|
||||
blend_tree_graph_edit.add_child(create_node_for_blt_node(BLTAnimationNodeOutput.new()))
|
||||
blend_tree_graph_edit.add_child(create_node_for_blt_node(BLTAnimationNodeBlend2.new()))
|
||||
|
||||
|
||||
func _on_reset_graph_button_pressed() -> void:
|
||||
_reset_editor()
|
||||
_update_editor_from_blend_tree()
|
||||
|
||||
var graph_rect:Rect2 = blend_tree_graph_edit.get_rect()
|
||||
blend_tree_graph_edit.scroll_offset = graph_rect.size * -0.5 - Vector2(200,0)
|
||||
|
||||
|
||||
func _reset_editor():
|
||||
for child in blend_tree_graph_edit.get_children():
|
||||
if child.name == "_connection_layer":
|
||||
continue
|
||||
|
||||
child.get_parent().remove_child(child)
|
||||
child.queue_free()
|
||||
|
||||
blend_tree_graph_edit.clear_connections()
|
||||
|
||||
blend_tree = BLTAnimationNodeBlendTree.new()
|
||||
blend_tree_node_to_graph_node = {}
|
||||
graph_node_to_blend_tree_node = {}
|
||||
selected_nodes = {}
|
||||
|
||||
|
||||
func edit_blend_tree(blend_tree_animation_node:BLTAnimationNode):
|
||||
print("Starting to edit blend_tree_animation_node " + str(blend_tree_animation_node))
|
||||
print("Owner: %s" % blend_tree_animation_node)
|
||||
_reset_editor()
|
||||
blend_tree = blend_tree_animation_node
|
||||
|
||||
_update_editor_nodes_from_blend_tree()
|
||||
_update_editor_connections_from_blend_tree()
|
||||
|
||||
func _update_editor_nodes_from_blend_tree():
|
||||
for node_name in blend_tree.get_node_names():
|
||||
var blend_tree_node:BLTAnimationNode = blend_tree.get_node(node_name)
|
||||
var graph_node:GraphNode = create_node_for_blt_node(blend_tree_node)
|
||||
blend_tree_graph_edit.add_child(graph_node)
|
||||
|
||||
blend_tree_node_to_graph_node[blend_tree_node] = graph_node
|
||||
graph_node_to_blend_tree_node[graph_node] = blend_tree_node
|
||||
|
||||
|
||||
func _update_editor_connections_from_blend_tree():
|
||||
var connection_array = blend_tree.get_connections()
|
||||
|
||||
for i in range(len(connection_array) / 3):
|
||||
var source_node:BLTAnimationNode = connection_array[i * 3]
|
||||
var target_node:BLTAnimationNode = connection_array[i * 3 + 1]
|
||||
var target_port = connection_array[i * 3 + 2]
|
||||
|
||||
var source_graph_node = blend_tree_node_to_graph_node[source_node]
|
||||
|
||||
var connect_result = blend_tree_graph_edit.connect_node(source_node.resource_name, 0, target_node.resource_name, target_node.get_input_index(target_port), true)
|
||||
|
||||
|
||||
func _update_editor_from_blend_tree():
|
||||
_update_editor_nodes_from_blend_tree()
|
||||
_update_editor_connections_from_blend_tree()
|
||||
|
||||
|
||||
func _on_save_button_pressed() -> void:
|
||||
ResourceSaver.save(blend_tree, "res://" + file_name_line_edit.text)
|
||||
|
||||
|
||||
func _on_load_button_pressed() -> void:
|
||||
var loaded_blend_tree:BLTAnimationNodeBlendTree = ResourceLoader.load("res://" + file_name_line_edit.text, "BLTAnimationNodeBlendTree", 0)
|
||||
|
||||
if loaded_blend_tree:
|
||||
_reset_editor()
|
||||
blend_tree = loaded_blend_tree
|
||||
_update_editor_from_blend_tree()
|
||||
|
||||
|
||||
func _on_instantiate_tree_button_pressed() -> void:
|
||||
var graph_name = tree_option_button.get_item_text(tree_option_button.selected)
|
||||
|
||||
if graph_name == "AnimationSampler":
|
||||
_reset_editor()
|
||||
|
||||
var sampler_node:BLTAnimationNodeSampler = BLTAnimationNodeSampler.new()
|
||||
sampler_node.animation = "SampleLibrary/Idle"
|
||||
|
||||
blend_tree.add_node(sampler_node)
|
||||
blend_tree.add_connection(sampler_node, blend_tree.get_output_node(), "Output")
|
||||
|
||||
_update_editor_from_blend_tree()
|
||||
elif graph_name == "Blend2":
|
||||
_reset_editor()
|
||||
|
||||
var sampler_node_walk:BLTAnimationNodeSampler = BLTAnimationNodeSampler.new()
|
||||
sampler_node_walk.animation = "SampleLibrary/Walk"
|
||||
|
||||
var sampler_node_run:BLTAnimationNodeSampler = BLTAnimationNodeSampler.new()
|
||||
sampler_node_run.animation = "SampleLibrary/Run"
|
||||
|
||||
var blend2_node:BLTAnimationNodeBlend2 = BLTAnimationNodeBlend2.new()
|
||||
|
||||
blend_tree.add_node(sampler_node_walk)
|
||||
blend_tree.add_node(sampler_node_run)
|
||||
blend_tree.add_node(blend2_node)
|
||||
|
||||
blend_tree.add_connection(blend2_node, blend_tree.get_output_node(), "Output")
|
||||
|
||||
blend_tree.add_connection(sampler_node_walk, blend2_node, "Input0")
|
||||
blend_tree.add_connection(sampler_node_run, blend2_node, "Input1")
|
||||
|
||||
_update_editor_from_blend_tree()
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
|
||||
print("Trying to connect '%s' port %d to node '%s' port %d" % [from_node, from_port, to_node, to_port])
|
||||
|
||||
var source_node:BLTAnimationNode = blend_tree.get_node(from_node)
|
||||
var target_node:BLTAnimationNode = blend_tree.get_node(to_node)
|
||||
|
||||
if target_node == null:
|
||||
push_error("Invalid connection, target node %s not found." % to_node)
|
||||
return
|
||||
|
||||
var target_node_port_name = target_node.get_input_names()[to_port]
|
||||
|
||||
var connection_result = blend_tree.is_connection_valid(source_node, target_node, target_node_port_name)
|
||||
if connection_result != blend_tree.CONNECTION_OK:
|
||||
push_error("Could not add connection (error %d)" % connection_result)
|
||||
return
|
||||
|
||||
blend_tree.add_connection(source_node, target_node, target_node_port_name)
|
||||
|
||||
var connect_result = blend_tree_graph_edit.connect_node(from_node, from_port, to_node, to_port, true)
|
||||
print("graph connect result: " + str(connect_result))
|
||||
|
||||
print("Success!")
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_end_node_move() -> void:
|
||||
for graph_node:GraphNode in selected_nodes.keys():
|
||||
graph_node_to_blend_tree_node[graph_node].position = graph_node.position
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_begin_node_move() -> void:
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_node_selected(graph_node: Node) -> void:
|
||||
selected_nodes[graph_node] = graph_node
|
||||
EditorInterface.get_inspector().edit(graph_node_to_blend_tree_node[graph_node])
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_node_deselected(graph_node: Node) -> void:
|
||||
if selected_nodes.has(graph_node):
|
||||
selected_nodes.erase(graph_node)
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_popup_request(at_position: Vector2) -> void:
|
||||
add_node_popup_menu.position = get_screen_position() + get_local_mouse_position()
|
||||
add_node_popup_menu.reset_size()
|
||||
add_node_popup_menu.popup()
|
||||
new_node_position = blend_tree_graph_edit.scroll_offset + at_position
|
||||
|
||||
|
||||
func _on_add_node_popup_menu_index_pressed(index: int) -> void:
|
||||
var new_blend_tree_node: BLTAnimationNode = ClassDB.instantiate(registered_nodes[index])
|
||||
blend_tree.add_node(new_blend_tree_node)
|
||||
var graph_node:GraphNode = create_node_for_blt_node(new_blend_tree_node)
|
||||
|
||||
graph_node_to_blend_tree_node[graph_node] = new_blend_tree_node
|
||||
blend_tree_node_to_graph_node[new_blend_tree_node] = graph_node
|
||||
|
||||
blend_tree_graph_edit.add_child(graph_node)
|
||||
|
||||
if new_node_position != Vector2.INF:
|
||||
graph_node.position_offset = new_node_position
|
||||
|
||||
new_node_position = Vector2.INF
|
||||
|
||||
|
||||
func _blend_tree_graph_edit_remove_node_connections(graph_node:GraphNode):
|
||||
var node_connections:Array = []
|
||||
|
||||
for connection:Dictionary in blend_tree_graph_edit.connections:
|
||||
if connection["from_node"] == graph_node.name or connection["to_node"] == graph_node.name:
|
||||
node_connections.append(connection)
|
||||
|
||||
for node_connection:Dictionary in node_connections:
|
||||
print("Removing connection %s" % str(node_connection))
|
||||
blend_tree_graph_edit.disconnect_node(node_connection["from_node"], node_connection["from_port"], node_connection["to_node"], node_connection["to_port"])
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_delete_nodes_request(nodes: Array[StringName]) -> void:
|
||||
for node_name:StringName in nodes:
|
||||
print("remove node '%s'" % node_name)
|
||||
var blend_tree_node:BLTAnimationNode = blend_tree.get_node(node_name)
|
||||
|
||||
if blend_tree_node == null:
|
||||
push_error("Cannot delete node '%s': node not found." % node_name)
|
||||
continue
|
||||
|
||||
if blend_tree_node == blend_tree.get_output_node():
|
||||
push_warning("Output node not allowed to be removed.")
|
||||
continue
|
||||
|
||||
var graph_node:GraphNode = blend_tree_node_to_graph_node[blend_tree_node]
|
||||
blend_tree.remove_node(blend_tree_node)
|
||||
blend_tree_node_to_graph_node.erase(blend_tree_node)
|
||||
|
||||
_blend_tree_graph_edit_remove_node_connections(graph_node)
|
||||
graph_node_to_blend_tree_node.erase(graph_node)
|
||||
blend_tree_graph_edit.remove_child(graph_node)
|
||||
_on_blend_tree_graph_edit_node_deselected(graph_node)
|
||||
|
||||
EditorInterface.get_inspector().edit(null)
|
||||
|
||||
|
||||
func _on_blend_tree_graph_edit_disconnection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
|
||||
print("removing connection")
|
||||
|
||||
var blend_tree_source_node = blend_tree.get_node(from_node)
|
||||
var blend_tree_target_node = blend_tree.get_node(to_node)
|
||||
var target_port_name = blend_tree_target_node.get_input_names()[to_port]
|
||||
blend_tree.remove_connection(blend_tree_source_node, blend_tree_target_node, target_port_name)
|
||||
|
||||
blend_tree_graph_edit.disconnect_node(from_node, from_port, to_node, to_port)
|
||||
func _on_hit_me_button_pressed() -> void:
|
||||
print("Hello from the main screen plugin!")
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
[gd_scene format=3 uid="uid://31c6depvs0y1"]
|
||||
[gd_scene load_steps=2 format=3 uid="uid://31c6depvs0y1"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dvulvuytt81lw" path="res://addons/blendalot/blendalot_main_panel.gd" id="1_427jg"]
|
||||
|
||||
@ -13,7 +13,7 @@ size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_427jg")
|
||||
|
||||
[node name="BlendTreeEditorContainer" type="VBoxContainer" parent="." unique_id=2044593527]
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=2044593527]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
@ -21,88 +21,13 @@ anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="BlendTreeEditorContainer" unique_id=1742071203]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ResetGraphButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=1060776498]
|
||||
[node name="HitMeButton" type="Button" parent="VBoxContainer" unique_id=1060776498]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Reset"
|
||||
text = "Hit me!"
|
||||
|
||||
[node name="FileNameLineEdit" type="LineEdit" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=2033619565]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(250, 0)
|
||||
layout_mode = 2
|
||||
text = "editor_test_tree.tres"
|
||||
|
||||
[node name="SaveButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=597228263]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Save
|
||||
"
|
||||
|
||||
[node name="LoadButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=559146765]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Load
|
||||
"
|
||||
|
||||
[node name="AddNodeButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=985452697]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Add Node"
|
||||
|
||||
[node name="TreeOptionButton" type="OptionButton" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=760974122]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
selected = 0
|
||||
item_count = 2
|
||||
popup/item_0/text = "AnimationSampler"
|
||||
popup/item_0/id = 1
|
||||
popup/item_1/text = "Blend2"
|
||||
popup/item_1/id = 2
|
||||
|
||||
[node name="InstantiateTreeButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=127759440]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "Instantiate"
|
||||
|
||||
[node name="Panel" type="Panel" parent="BlendTreeEditorContainer" unique_id=424652158]
|
||||
[node name="GraphFrame" type="GraphFrame" parent="VBoxContainer" unique_id=184673233]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="AddNodePopupMenu" type="PopupMenu" parent="BlendTreeEditorContainer/Panel" unique_id=2020489213]
|
||||
unique_name_in_owner = true
|
||||
oversampling_override = 1.0
|
||||
item_count = 3
|
||||
item_0/text = "BLTAnimationNodeSampler"
|
||||
item_0/id = 0
|
||||
item_1/text = "BLTAnimationNodeBlend2"
|
||||
item_1/id = 1
|
||||
item_2/text = "BLTAnimationNodeBlendTree"
|
||||
item_2/id = 2
|
||||
|
||||
[node name="BlendTreeGraphEdit" type="GraphEdit" parent="BlendTreeEditorContainer/Panel" unique_id=387715755]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
right_disconnects = true
|
||||
|
||||
[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/ResetGraphButton" to="." method="_on_reset_graph_button_pressed"]
|
||||
[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/SaveButton" to="." method="_on_save_button_pressed"]
|
||||
[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/LoadButton" to="." method="_on_load_button_pressed"]
|
||||
[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/AddNodeButton" to="." method="_on_add_node_button_pressed"]
|
||||
[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/InstantiateTreeButton" to="." method="_on_instantiate_tree_button_pressed"]
|
||||
[connection signal="index_pressed" from="BlendTreeEditorContainer/Panel/AddNodePopupMenu" to="." method="_on_add_node_popup_menu_index_pressed"]
|
||||
[connection signal="begin_node_move" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_begin_node_move"]
|
||||
[connection signal="connection_request" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_connection_request"]
|
||||
[connection signal="delete_nodes_request" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_delete_nodes_request"]
|
||||
[connection signal="disconnection_request" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_disconnection_request"]
|
||||
[connection signal="end_node_move" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_end_node_move"]
|
||||
[connection signal="node_deselected" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_node_deselected"]
|
||||
[connection signal="node_selected" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_node_selected"]
|
||||
[connection signal="popup_request" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_popup_request"]
|
||||
[connection signal="pressed" from="VBoxContainer/HitMeButton" to="." method="_on_hit_me_button_pressed"]
|
||||
|
||||
@ -3,7 +3,7 @@ extends EditorPlugin
|
||||
|
||||
const MainPanel = preload("res://addons/blendalot/blendalot_main_panel.tscn")
|
||||
|
||||
var main_panel_instance:BlendalotMainPanel
|
||||
var main_panel_instance
|
||||
|
||||
func _enable_plugin() -> void:
|
||||
# Add autoloads here.
|
||||
@ -43,14 +43,3 @@ func _get_plugin_name():
|
||||
|
||||
func _get_plugin_icon():
|
||||
return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons")
|
||||
|
||||
|
||||
func _handles(obj: Object) -> bool:
|
||||
return obj is BLTAnimationNodeBlendTree
|
||||
|
||||
func _edit(object: Object):
|
||||
if object is BLTAnimationNodeBlendTree:
|
||||
main_panel_instance.edit_blend_tree(object)
|
||||
return
|
||||
|
||||
print("Cannot (yet) edit object " + str(object))
|
||||
|
||||
13
demo/main.gd
13
demo/main.gd
@ -12,6 +12,19 @@ extends Node3D
|
||||
func _ready() -> void:
|
||||
blend_weight_slider.value = 0.5
|
||||
|
||||
var blend_tree: BLTAnimationNodeBlendTree = BLTAnimationNodeBlendTree.new()
|
||||
var output_node: BLTAnimationNodeOutput = blend_tree.get_output_node()
|
||||
var sampler_node_1: BLTAnimationNodeSampler = BLTAnimationNodeSampler.new()
|
||||
|
||||
sampler_node_1.animation = "animation_library/Walk-InPlace"
|
||||
|
||||
blend_tree.add_node(sampler_node_1)
|
||||
var result = blend_tree.add_connection(sampler_node_1, output_node, "Input")
|
||||
var anim_graph: BLTAnimationGraph = mixamo_amy_walk_run_synced.get_node("SyncedAnimationGraph")
|
||||
|
||||
anim_graph.tree_root = blend_tree
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
[gd_scene format=3 uid="uid://svj53e2xoio"]
|
||||
[gd_scene load_steps=14 format=3 uid="uid://svj53e2xoio"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://d1xcqdqr1qeu6" path="res://assets/MixamoAmy.glb" id="1_0xm2m"]
|
||||
[ext_resource type="Script" uid="uid://bjvgqujpqumj7" path="res://main.gd" id="1_1bvp3"]
|
||||
[ext_resource type="AnimationLibrary" uid="uid://dwubn740aqx51" path="res://animation_library.res" id="3_1bvp3"]
|
||||
[ext_resource type="AnimationNodeBlendTree" uid="uid://dqy0dgwsm8t46" path="res://animation_tree_walk_limp.tres" id="3_272bh"]
|
||||
[ext_resource type="BLTAnimationNodeBlendTree" uid="uid://2qfwr1xkiw0s" path="res://synced_blend_tree_walk_limp.tres" id="4_lquwl"]
|
||||
[ext_resource type="BLTAnimationNodeBlendTree" uid="uid://qsk64ax2o47f" path="res://synced_blend_tree_walk_run.tres" id="5_7mycd"]
|
||||
[ext_resource type="AnimationNodeBlendTree" uid="uid://vsf71o82lkld" path="res://animation_tree_walk_run.tres" id="6_5vw27"]
|
||||
|
||||
[sub_resource type="Theme" id="Theme_272bh"]
|
||||
@ -31,17 +32,6 @@ sky = SubResource("Sky_1bvp3")
|
||||
tonemap_mode = 2
|
||||
glow_enabled = true
|
||||
|
||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_7mycd"]
|
||||
resource_name = "BLTAnimationNodeSampler"
|
||||
position = Vector2(149.47485, 361.29053)
|
||||
animation = &"animation_library/Walk-InPlace"
|
||||
|
||||
[sub_resource type="BLTAnimationNodeBlendTree" id="BLTAnimationNodeBlendTree_272bh"]
|
||||
nodes/Output/position = Vector2(602.0149, 281.4305)
|
||||
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_7mycd")
|
||||
nodes/BLTAnimationNodeSampler/position = Vector2(149.47485, 361.29053)
|
||||
node_connections = ["Output", 0, "BLTAnimationNodeSampler"]
|
||||
|
||||
[node name="Main" type="Node3D" unique_id=933302313]
|
||||
script = ExtResource("1_1bvp3")
|
||||
|
||||
@ -166,13 +156,13 @@ unique_name_in_owner = true
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4, 0, 0)
|
||||
|
||||
[node name="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1255239074]
|
||||
active = false
|
||||
libraries/animation_library = ExtResource("3_1bvp3")
|
||||
|
||||
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1602406394]
|
||||
animation_player = NodePath("../AnimationPlayer2")
|
||||
tree_root = SubResource("BLTAnimationNodeBlendTree_272bh")
|
||||
tree_root = ExtResource("5_7mycd")
|
||||
skeleton = NodePath("../Armature/Skeleton3D")
|
||||
parameters/BLTAnimationNodeBlend2/blend_amount = 0.4
|
||||
|
||||
[connection signal="value_changed" from="UI/MarginContainer/HBoxContainer/BlendWeightSlider" to="." method="_on_blend_weight_slider_value_changed"]
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ config_version=5
|
||||
|
||||
config/name="Synced Blend Tree Test"
|
||||
run/main_scene="uid://svj53e2xoio"
|
||||
config/features=PackedStringArray("4.6", "Forward Plus")
|
||||
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[display]
|
||||
@ -23,7 +23,3 @@ window/size/viewport_height=1024
|
||||
[dotnet]
|
||||
|
||||
project/assembly_name="Synced Blend Tree Test"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/blendalot/plugin.cfg")
|
||||
|
||||
@ -111,7 +111,7 @@ Some nodes have special names in the Blend Tree:
|
||||
Except for the output node of a Blend Tree the following properties hold:
|
||||
|
||||
* all Blend Tree nodes only operate on properties they own and any other data (e.g. inputs and outputs) are specified
|
||||
via arguments to `BLTAnimationNode::evaluate(context, inputs, output)` function of the node.
|
||||
via arguments to `SyncedAnimationNode::evaluate(context, inputs, output)` function of the node.
|
||||
|
||||
Advantages:
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestBlendalotAnimationGraph {
|
||||
namespace TestSyncedAnimationGraph {
|
||||
|
||||
TEST_CASE("[SyncedAnimationGraph][SyncTrack] Basic") {
|
||||
SyncTrack track_a;
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
struct BlendTreeFixture {
|
||||
struct SyncedAnimationGraphFixture {
|
||||
Node *character_node;
|
||||
Skeleton3D *skeleton_node;
|
||||
AnimationPlayer *player_node;
|
||||
@ -20,8 +20,8 @@ struct BlendTreeFixture {
|
||||
|
||||
Ref<AnimationLibrary> animation_library;
|
||||
|
||||
BLTAnimationGraph *animation_graph;
|
||||
BlendTreeFixture() {
|
||||
BLTAnimationGraph *synced_animation_graph;
|
||||
SyncedAnimationGraphFixture() {
|
||||
BLTAnimationGraph *scene_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
||||
|
||||
if (scene_animation_graph == nullptr) {
|
||||
@ -50,12 +50,12 @@ struct BlendTreeFixture {
|
||||
|
||||
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
||||
|
||||
animation_graph = memnew(BLTAnimationGraph);
|
||||
animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
||||
SceneTree::get_singleton()->get_root()->add_child(animation_graph);
|
||||
synced_animation_graph = memnew(BLTAnimationGraph);
|
||||
synced_animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
||||
SceneTree::get_singleton()->get_root()->add_child(synced_animation_graph);
|
||||
|
||||
animation_graph->set_animation_player(player_node->get_path());
|
||||
animation_graph->set_skeleton(skeleton_node->get_path());
|
||||
synced_animation_graph->set_animation_player(player_node->get_path());
|
||||
synced_animation_graph->set_skeleton(skeleton_node->get_path());
|
||||
}
|
||||
|
||||
void setup_animations() {
|
||||
@ -112,8 +112,8 @@ struct BlendTreeFixture {
|
||||
}
|
||||
|
||||
void assign_scene_variables() {
|
||||
animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
||||
REQUIRE(animation_graph);
|
||||
synced_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
||||
REQUIRE(synced_animation_graph);
|
||||
character_node = (SceneTree::get_singleton()->get_root()->find_child("CharacterNode", true, false));
|
||||
REQUIRE(character_node != nullptr);
|
||||
skeleton_node = dynamic_cast<Skeleton3D *>((SceneTree::get_singleton()->get_root()->find_child("Skeleton", true, false)));
|
||||
@ -139,34 +139,34 @@ struct BlendTreeFixture {
|
||||
}
|
||||
};
|
||||
|
||||
namespace TestBlendalotAnimationGraph {
|
||||
namespace TestSyncedAnimationGraph {
|
||||
|
||||
TEST_CASE("[Blendalot][BlendTree] Test BlendTree construction") {
|
||||
TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
||||
BLTAnimationNodeBlendTree::BLTBlendTreeGraph tree_constructor;
|
||||
|
||||
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
||||
animation_sampler_node0.instantiate();
|
||||
animation_sampler_node0->set_name("Sampler0");
|
||||
animation_sampler_node0->name = "Sampler0";
|
||||
tree_constructor.add_node(animation_sampler_node0);
|
||||
|
||||
Ref<BLTAnimationNodeSampler> animation_sampler_node1;
|
||||
animation_sampler_node1.instantiate();
|
||||
animation_sampler_node1->set_name("Sampler1");
|
||||
animation_sampler_node1->name = "Sampler1";
|
||||
tree_constructor.add_node(animation_sampler_node1);
|
||||
|
||||
Ref<BLTAnimationNodeSampler> animation_sampler_node2;
|
||||
animation_sampler_node2.instantiate();
|
||||
animation_sampler_node2->set_name("Sampler2");
|
||||
animation_sampler_node2->name = "Sampler2";
|
||||
tree_constructor.add_node(animation_sampler_node2);
|
||||
|
||||
Ref<BLTAnimationNodeBlend2> node_blend0;
|
||||
node_blend0.instantiate();
|
||||
node_blend0->set_name("Blend0");
|
||||
node_blend0->name = "Blend0";
|
||||
tree_constructor.add_node(node_blend0);
|
||||
|
||||
Ref<BLTAnimationNodeBlend2> node_blend1;
|
||||
node_blend1.instantiate();
|
||||
node_blend1->set_name("Blend1");
|
||||
node_blend1->name = "Blend1";
|
||||
tree_constructor.add_node(node_blend1);
|
||||
|
||||
// Tree
|
||||
@ -201,7 +201,7 @@ TEST_CASE("[Blendalot][BlendTree] Test BlendTree construction") {
|
||||
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(blend0_index));
|
||||
|
||||
// Perform remaining connections
|
||||
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Output"));
|
||||
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Input"));
|
||||
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(animation_sampler_node2, node_blend1, "Input1"));
|
||||
|
||||
// Output node must have all nodes in its subtree:
|
||||
@ -213,15 +213,15 @@ TEST_CASE("[Blendalot][BlendTree] Test BlendTree construction") {
|
||||
|
||||
tree_constructor.sort_nodes_and_references();
|
||||
|
||||
// Check that for node i all input nodes have a node index j >= i (i is part of the subtree)
|
||||
// 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 (int input_index : tree_constructor.node_connection_info[i].input_subtree_node_indices) {
|
||||
CHECK(input_index >= i);
|
||||
CHECK(input_index > i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] Test AnimationData blending") {
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] Test AnimationData blending") {
|
||||
AnimationData data_t0;
|
||||
data_t0.allocate_track_values(test_animation_a, skeleton_node);
|
||||
data_t0.sample_from_animation(test_animation_a, skeleton_node, 0.0);
|
||||
@ -256,12 +256,12 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] Test AnimationData b
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] SyncedAnimationGraph evaluation with an AnimationSampler as root node") {
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph evaluation with an AnimationSampler as root node") {
|
||||
Ref<BLTAnimationNodeSampler> animation_sampler_node;
|
||||
animation_sampler_node.instantiate();
|
||||
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
||||
|
||||
animation_graph->set_root_animation_node(animation_sampler_node);
|
||||
synced_animation_graph->set_root_animation_node(animation_sampler_node);
|
||||
|
||||
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||
|
||||
@ -278,7 +278,7 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] SyncedAnimationGraph
|
||||
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree] BlendTree evaluation with a AnimationSamplerNode connected to the output") {
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree] BlendTree evaluation with a AnimationSamplerNode connected to the output") {
|
||||
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||
synced_blend_tree_node.instantiate();
|
||||
|
||||
@ -287,11 +287,11 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree] BlendTree
|
||||
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
||||
|
||||
synced_blend_tree_node->add_node(animation_sampler_node);
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(animation_sampler_node, synced_blend_tree_node->get_output_node(), "Output"));
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(animation_sampler_node, synced_blend_tree_node->get_output_node(), "Input"));
|
||||
|
||||
synced_blend_tree_node->initialize(animation_graph->get_context());
|
||||
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||
|
||||
animation_graph->set_root_animation_node(synced_blend_tree_node);
|
||||
synced_animation_graph->set_root_animation_node(synced_blend_tree_node);
|
||||
|
||||
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||
|
||||
@ -308,7 +308,7 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree] BlendTree
|
||||
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Node] BlendTree evaluation with a Blend2Node connected to the output") {
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree][Blend2Node] BlendTree evaluation with a Blend2Node connected to the output") {
|
||||
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||
synced_blend_tree_node.instantiate();
|
||||
|
||||
@ -329,19 +329,20 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
||||
// Blend2
|
||||
Ref<BLTAnimationNodeBlend2> blend2_node;
|
||||
blend2_node.instantiate();
|
||||
blend2_node->set_name("Blend2");
|
||||
blend2_node->name = "Blend2";
|
||||
blend2_node->blend_weight = 0.5;
|
||||
blend2_node->sync = false;
|
||||
|
||||
synced_blend_tree_node->add_node(blend2_node);
|
||||
|
||||
// Connect nodes
|
||||
Vector<StringName> blend2_inputs = blend2_node->get_input_names();
|
||||
Vector<StringName> blend2_inputs;
|
||||
blend2_node->get_input_names(blend2_inputs);
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(animation_sampler_node_a, blend2_node, blend2_inputs[0]));
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(animation_sampler_node_b, blend2_node, blend2_inputs[1]));
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(blend2_node, synced_blend_tree_node->get_output_node(), "Output"));
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(blend2_node, synced_blend_tree_node->get_output_node(), "Input"));
|
||||
|
||||
synced_blend_tree_node->initialize(animation_graph->get_context());
|
||||
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||
|
||||
int blend2_node_index = synced_blend_tree_node->find_node_index(blend2_node);
|
||||
const BLTAnimationNodeBlendTree::NodeRuntimeData &blend2_runtime_data = synced_blend_tree_node->_node_runtime_data[blend2_node_index];
|
||||
@ -349,7 +350,7 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
||||
CHECK(blend2_runtime_data.input_nodes[0] == animation_sampler_node_a);
|
||||
CHECK(blend2_runtime_data.input_nodes[1] == animation_sampler_node_b);
|
||||
|
||||
animation_graph->set_root_animation_node(synced_blend_tree_node);
|
||||
synced_animation_graph->set_root_animation_node(synced_blend_tree_node);
|
||||
|
||||
SUBCASE("Perform default evaluation") {
|
||||
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||
@ -387,9 +388,9 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
||||
animation_sampler_node_a->animation_name = "animation_library/TestAnimationSyncA";
|
||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationSyncB";
|
||||
blend2_node->sync = true;
|
||||
synced_blend_tree_node->initialize(animation_graph->get_context());
|
||||
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||
|
||||
REQUIRE(animation_graph->get_root_animation_node().ptr() == synced_blend_tree_node.ptr());
|
||||
REQUIRE(synced_animation_graph->get_root_animation_node().ptr() == synced_blend_tree_node.ptr());
|
||||
|
||||
// By blending both animations we get a SyncTrack of duration 1.5s with the following
|
||||
// intervals:
|
||||
@ -436,13 +437,13 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
||||
Ref<BLTAnimationNodeBlendTree> loaded_synced_blend_tree = ResourceLoader::load("synced_blend_tree_node.tres");
|
||||
REQUIRE(loaded_synced_blend_tree.is_valid());
|
||||
|
||||
Ref<BLTAnimationNodeBlend2> loaded_blend2_node = loaded_synced_blend_tree->get_node_by_index(loaded_synced_blend_tree->find_node_index_by_name("Blend2"));
|
||||
Ref<BLTAnimationNodeBlend2> loaded_blend2_node = loaded_synced_blend_tree->get_node(loaded_synced_blend_tree->find_node_index_by_name("Blend2"));
|
||||
REQUIRE(loaded_blend2_node.is_valid());
|
||||
CHECK(loaded_blend2_node->sync == false);
|
||||
CHECK(loaded_blend2_node->blend_weight == blend2_node->blend_weight);
|
||||
|
||||
loaded_synced_blend_tree->initialize(animation_graph->get_context());
|
||||
animation_graph->set_root_animation_node(loaded_synced_blend_tree);
|
||||
loaded_synced_blend_tree->initialize(synced_animation_graph->get_context());
|
||||
synced_animation_graph->set_root_animation_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);
|
||||
@ -455,136 +456,4 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][ChangeConnectivity] BlendTreeGraph with various nodes and connections that are removed") {
|
||||
BLTAnimationNodeBlendTree::BLTBlendTreeGraph blend_tree_graph;
|
||||
|
||||
// TestAnimationA
|
||||
Ref<BLTAnimationNodeSampler> animation_sampler_node_a;
|
||||
animation_sampler_node_a.instantiate();
|
||||
animation_sampler_node_a->animation_name = "animation_library/TestAnimationA";
|
||||
|
||||
blend_tree_graph.add_node(animation_sampler_node_a);
|
||||
|
||||
// TestAnimationB
|
||||
Ref<BLTAnimationNodeSampler> animation_sampler_node_b;
|
||||
animation_sampler_node_b.instantiate();
|
||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationB";
|
||||
|
||||
blend_tree_graph.add_node(animation_sampler_node_b);
|
||||
|
||||
// TestAnimationB
|
||||
Ref<BLTAnimationNodeSampler> animation_sampler_node_c;
|
||||
animation_sampler_node_c.instantiate();
|
||||
animation_sampler_node_c->animation_name = "animation_library/TestAnimationC";
|
||||
|
||||
blend_tree_graph.add_node(animation_sampler_node_c);
|
||||
|
||||
// Blend2A
|
||||
Ref<BLTAnimationNodeBlend2> blend2_node_a;
|
||||
blend2_node_a.instantiate();
|
||||
blend2_node_a->set_name("Blend2A");
|
||||
blend2_node_a->blend_weight = 0.5;
|
||||
blend2_node_a->sync = false;
|
||||
|
||||
blend_tree_graph.add_node(blend2_node_a);
|
||||
|
||||
// Blend2B
|
||||
Ref<BLTAnimationNodeBlend2> blend2_node_b;
|
||||
blend2_node_b.instantiate();
|
||||
blend2_node_b->set_name("Blend2A");
|
||||
blend2_node_b->blend_weight = 0.5;
|
||||
blend2_node_b->sync = false;
|
||||
|
||||
blend_tree_graph.add_node(blend2_node_b);
|
||||
|
||||
// Connect nodes: Subgraph Output, Blend2A, SamplerA
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(blend2_node_a, blend_tree_graph.get_output_node(), "Output"));
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(animation_sampler_node_a, blend2_node_a, "Input0"));
|
||||
|
||||
// Connect nodes: Subgraph Blend2A, SamplerB, SamplerC
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(animation_sampler_node_b, blend2_node_b, "Input0"));
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(animation_sampler_node_c, blend2_node_b, "Input1"));
|
||||
|
||||
SUBCASE("Add and remove a connection") {
|
||||
HashSet<int> subgraph_output_initial = blend_tree_graph.node_connection_info[0].input_subtree_node_indices;
|
||||
HashSet<int> subgraph_blend2a_initial = blend_tree_graph.node_connection_info[blend_tree_graph.find_node_index(blend2_node_a)].input_subtree_node_indices;
|
||||
HashSet<int> subgraph_blend2b_initial = blend_tree_graph.node_connection_info[blend_tree_graph.find_node_index(blend2_node_b)].input_subtree_node_indices;
|
||||
|
||||
// Add and remove connection
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(blend2_node_b, blend2_node_a, "Input1"));
|
||||
blend_tree_graph.remove_connection(blend2_node_b, blend2_node_a, "Input1");
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.is_connection_valid(blend2_node_b, blend2_node_a, "Input1"));
|
||||
|
||||
// Check that we have the same subgraphs as before the connection
|
||||
CHECK(subgraph_output_initial == blend_tree_graph.node_connection_info[0].input_subtree_node_indices);
|
||||
CHECK(subgraph_blend2a_initial == blend_tree_graph.node_connection_info[blend_tree_graph.find_node_index(blend2_node_a)].input_subtree_node_indices);
|
||||
CHECK(subgraph_blend2b_initial == blend_tree_graph.node_connection_info[blend_tree_graph.find_node_index(blend2_node_b)].input_subtree_node_indices);
|
||||
|
||||
// Check that we also do not
|
||||
for (const BLTBlendTreeConnection &connection : blend_tree_graph.connections) {
|
||||
bool connection_equals_removed_connection = connection.source_node == blend2_node_b && connection.target_node == blend2_node_a && connection.target_port_name == "Input1";
|
||||
CHECK(connection_equals_removed_connection == false);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Remove a node") {
|
||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(blend2_node_b, blend2_node_a, "Input1"));
|
||||
|
||||
int animation_sampler_node_b_index_pre_remove = blend_tree_graph.find_node_index(animation_sampler_node_b);
|
||||
int blend2_node_a_index_pre_remove = blend_tree_graph.find_node_index(blend2_node_a);
|
||||
int blend2_node_b_index_pre_remove = blend_tree_graph.find_node_index(blend2_node_b);
|
||||
|
||||
CHECK(blend_tree_graph.node_connection_info[0].input_subtree_node_indices.size() == 6);
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_a_index_pre_remove].input_subtree_node_indices.size() == 5);
|
||||
|
||||
SUBCASE("Remove animation_sampler_node_a") {
|
||||
blend_tree_graph.remove_node(animation_sampler_node_a);
|
||||
|
||||
for (const BLTBlendTreeConnection &connection : blend_tree_graph.connections) {
|
||||
bool is_connection_with_removed_node = connection.source_node == animation_sampler_node_a || connection.target_node == animation_sampler_node_a;
|
||||
CHECK(is_connection_with_removed_node == false);
|
||||
}
|
||||
|
||||
int animation_sampler_node_b_index_post_remove = blend_tree_graph.find_node_index(animation_sampler_node_b);
|
||||
int blend2_node_a_index_post_remove = blend_tree_graph.find_node_index(blend2_node_a);
|
||||
int blend2_node_b_index_post_remove = blend_tree_graph.find_node_index(blend2_node_b);
|
||||
|
||||
CHECK(blend_tree_graph.find_node_index(animation_sampler_node_a) == -1);
|
||||
CHECK(blend2_node_b_index_post_remove == blend2_node_b_index_pre_remove - 1);
|
||||
CHECK(animation_sampler_node_b_index_post_remove == animation_sampler_node_b_index_pre_remove - 1);
|
||||
|
||||
CHECK(blend_tree_graph.node_connection_info[0].input_subtree_node_indices.size() == 5);
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_a_index_post_remove].input_subtree_node_indices.size() == 4);
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_a_index_post_remove].connected_child_node_index_at_port[0] == -1);
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_a_index_post_remove].connected_child_node_index_at_port[1] == blend2_node_b_index_post_remove);
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.has(blend2_node_b_index_post_remove));
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.has(animation_sampler_node_b_index_post_remove));
|
||||
}
|
||||
|
||||
SUBCASE("Remove blend2_node_a") {
|
||||
blend_tree_graph.remove_node(blend2_node_a);
|
||||
|
||||
for (const BLTBlendTreeConnection &connection : blend_tree_graph.connections) {
|
||||
bool is_connection_with_removed_node = connection.source_node == blend2_node_a || connection.target_node == blend2_node_a;
|
||||
CHECK(is_connection_with_removed_node == false);
|
||||
}
|
||||
|
||||
int animation_sampler_node_b_index_post_remove = blend_tree_graph.find_node_index(animation_sampler_node_b);
|
||||
int animation_sampler_node_c_index_post_remove = blend_tree_graph.find_node_index(animation_sampler_node_c);
|
||||
int blend2_node_b_index_post_remove = blend_tree_graph.find_node_index(blend2_node_b);
|
||||
|
||||
CHECK(blend_tree_graph.find_node_index(blend2_node_a) == -1);
|
||||
CHECK(blend2_node_b_index_post_remove == blend2_node_b_index_pre_remove - 1);
|
||||
CHECK(animation_sampler_node_b_index_post_remove == animation_sampler_node_b_index_pre_remove);
|
||||
|
||||
CHECK(blend_tree_graph.node_connection_info[0].input_subtree_node_indices.size() == 1);
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.size() == 3);
|
||||
blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove]._print_subtree();
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.has(blend2_node_b_index_post_remove));
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.has(animation_sampler_node_b_index_post_remove));
|
||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.has(animation_sampler_node_c_index_post_remove));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestBlendalotAnimationGraph
|
||||
} //namespace TestSyncedAnimationGraph
|
||||
Loading…
x
Reference in New Issue
Block a user