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("set_tree_root", "animation_node"), &BLTAnimationGraph::set_root_animation_node);
|
||||||
ClassDB::bind_method(D_METHOD("get_tree_root"), &BLTAnimationGraph::get_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("set_skeleton", "skeleton"), &BLTAnimationGraph::set_skeleton);
|
||||||
ClassDB::bind_method(D_METHOD("get_skeleton"), &BLTAnimationGraph::get_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);
|
p_node->get_child_nodes(&children);
|
||||||
|
|
||||||
for (const Ref<BLTAnimationNode> &child_node : 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (graph_context.skeleton_3d == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GodotProfileZone("SyncedAnimationGraph::_process_graph");
|
GodotProfileZone("SyncedAnimationGraph::_process_graph");
|
||||||
|
|
||||||
_update_properties();
|
_update_properties();
|
||||||
|
|||||||
@ -5,17 +5,9 @@
|
|||||||
#include "blendalot_animation_node.h"
|
#include "blendalot_animation_node.h"
|
||||||
|
|
||||||
void BLTAnimationNode::_bind_methods() {
|
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("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_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")));
|
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 {
|
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() {
|
void BLTAnimationNodeBlendTree::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("add_node", "animation_node"), &BLTAnimationNodeBlendTree::add_node);
|
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_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("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_OK);
|
||||||
BIND_CONSTANT(CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED);
|
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 {
|
void BLTAnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||||
String prop_name = node->get_name();
|
String prop_name = node->name;
|
||||||
if (prop_name != "Output") {
|
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::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;
|
int idx = 0;
|
||||||
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
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 + 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++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +115,7 @@ bool BLTAnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_
|
|||||||
if (what == "node") {
|
if (what == "node") {
|
||||||
Ref<BLTAnimationNode> anode = p_value;
|
Ref<BLTAnimationNode> anode = p_value;
|
||||||
if (anode.is_valid()) {
|
if (anode.is_valid()) {
|
||||||
anode->set_name(node_name);
|
anode->name = node_name;
|
||||||
add_node(anode);
|
add_node(anode);
|
||||||
}
|
}
|
||||||
return true;
|
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]);
|
int source_node_index = find_node_index_by_name(conns[i + 2]);
|
||||||
|
|
||||||
Ref<BLTAnimationNode> target_node = tree_graph.nodes[target_node_index];
|
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]);
|
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) {
|
bool BLTAnimationNodeSampler::initialize(GraphEvaluationContext &context) {
|
||||||
if (!BLTAnimationNode::initialize(context)) {
|
BLTAnimationNode::initialize(context);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
animation = context.animation_player->get_animation(animation_name);
|
animation = context.animation_player->get_animation(animation_name);
|
||||||
if (!animation.is_valid()) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +412,7 @@ bool BLTAnimationNodeBlend2::_set(const StringName &p_name, const Variant &p_val
|
|||||||
BLTAnimationNodeBlendTree::BLTBlendTreeGraph::BLTBlendTreeGraph() {
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph::BLTBlendTreeGraph() {
|
||||||
Ref<BLTAnimationNodeOutput> output_node;
|
Ref<BLTAnimationNodeOutput> output_node;
|
||||||
output_node.instantiate();
|
output_node.instantiate();
|
||||||
output_node->set_name("Output");
|
output_node->name = "Output";
|
||||||
add_node(output_node);
|
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 {
|
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) {
|
if (nodes[i] == node) {
|
||||||
return i;
|
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 {
|
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const StringName &name) const {
|
||||||
for (uint32_t i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
if (nodes[i]->get_name() == name) {
|
if (nodes[i]->name == name) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,64 +441,20 @@ int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimationNode> &node) {
|
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()) {
|
if (node_base_name.is_empty()) {
|
||||||
node_base_name = node->get_class_name();
|
node_base_name = node->get_class_name();
|
||||||
}
|
}
|
||||||
node->set_name(node_base_name);
|
node->name = node_base_name;
|
||||||
|
|
||||||
int number_suffix = 1;
|
int number_suffix = 1;
|
||||||
while (find_node_index_by_name(node->get_name()) != -1) {
|
while (find_node_index_by_name(node->name) != -1) {
|
||||||
node->set_name(vformat("%s %d", node_base_name, number_suffix));
|
node->name = vformat("%s %d", node_base_name, number_suffix);
|
||||||
number_suffix++;
|
number_suffix++;
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.push_back(node);
|
nodes.push_back(node);
|
||||||
|
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
||||||
@ -555,69 +495,18 @@ void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_recursive(int node
|
|||||||
result.push_back(node_index);
|
result.push_back(node_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node_index, int node_parent_index) {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node, int node_parent) {
|
||||||
if (node_parent_index == -1) {
|
if (node_parent == -1) {
|
||||||
return;
|
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) {
|
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
||||||
node_connection_info[node_parent_index].input_subtree_node_indices.insert(index);
|
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);
|
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
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;
|
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 {
|
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const {
|
||||||
for (uint32_t i = 0; i < connections.size(); i++) {
|
int source_node_index = find_node_index(source_node);
|
||||||
if (connections[i].source_node == source_node && connections[i].target_node == target_node && connections[i].target_port_name == target_port_name) {
|
if (source_node_index == -1) {
|
||||||
return i;
|
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 target_node_index = find_node_index(target_node);
|
||||||
int source_node_index = find_node_index(source_node);
|
if (target_node_index == -1) {
|
||||||
NodeConnectionInfo &connection_info = node_connection_info[source_node_index];
|
print_error("Cannot connect nodes: target node not found.");
|
||||||
|
return CONNECTION_ERROR_NO_TARGET_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
if (connection_info.parent_node_index != -1) {
|
Vector<StringName> target_inputs;
|
||||||
NodeConnectionInfo &parent_connection_info = node_connection_info[connection_info.parent_node_index];
|
target_node->get_input_names(target_inputs);
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
if (node_connection_info[source_node_index].input_subtree_node_indices.has(target_node_index)) {
|
||||||
assert(connection_index >= 0);
|
print_error("Cannot connect node: connection would create loop.");
|
||||||
connections.remove_at(connection_index);
|
return CONNECTION_ERROR_CONNECTION_CREATES_LOOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CONNECTION_OK;
|
return CONNECTION_OK;
|
||||||
|
|||||||
@ -262,6 +262,7 @@ public:
|
|||||||
NodeTimeInfo node_time_info;
|
NodeTimeInfo node_time_info;
|
||||||
bool active = false;
|
bool active = false;
|
||||||
|
|
||||||
|
StringName name;
|
||||||
Vector2 position;
|
Vector2 position;
|
||||||
|
|
||||||
virtual ~BLTAnimationNode() override = default;
|
virtual ~BLTAnimationNode() override = default;
|
||||||
@ -284,7 +285,6 @@ public:
|
|||||||
node_time_info.loop_mode = input_nodes[0]->node_time_info.loop_mode;
|
node_time_info.loop_mode = input_nodes[0]->node_time_info.loop_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update_time(double p_time) {
|
virtual void update_time(double p_time) {
|
||||||
if (node_time_info.is_synced) {
|
if (node_time_info.is_synced) {
|
||||||
node_time_info.sync_position = p_time;
|
node_time_info.sync_position = p_time;
|
||||||
@ -293,7 +293,6 @@ public:
|
|||||||
node_time_info.position += p_time;
|
node_time_info.position += p_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) {
|
virtual void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) {
|
||||||
// By default, use the AnimationData of the first input.
|
// By default, use the AnimationData of the first input.
|
||||||
if (input_datas.size() > 0) {
|
if (input_datas.size() > 0) {
|
||||||
@ -301,32 +300,16 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_position(const Vector2 &p_position) {
|
virtual void get_input_names(Vector<StringName> &inputs) const {}
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_input_index(const StringName &port_name) 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);
|
return inputs.find(port_name);
|
||||||
}
|
}
|
||||||
int get_input_count() const {
|
int get_input_count() const {
|
||||||
Vector<StringName> inputs = get_input_names();
|
Vector<StringName> inputs;
|
||||||
|
get_input_names(inputs);
|
||||||
return inputs.size();
|
return inputs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,8 +341,8 @@ class BLTAnimationNodeOutput : public BLTAnimationNode {
|
|||||||
GDCLASS(BLTAnimationNodeOutput, BLTAnimationNode);
|
GDCLASS(BLTAnimationNodeOutput, BLTAnimationNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vector<StringName> get_input_names() const override {
|
void get_input_names(Vector<StringName> &inputs) const override {
|
||||||
return { "Output" };
|
inputs.push_back("Input");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -370,21 +353,20 @@ public:
|
|||||||
float blend_weight = 0.0f;
|
float blend_weight = 0.0f;
|
||||||
bool sync = true;
|
bool sync = true;
|
||||||
|
|
||||||
Vector<StringName> get_input_names() const override {
|
void get_input_names(Vector<StringName> &inputs) const override {
|
||||||
return { "Input0", "Input1" };
|
inputs.push_back("Input0");
|
||||||
|
inputs.push_back("Input1");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initialize(GraphEvaluationContext &context) override {
|
bool initialize(GraphEvaluationContext &context) override {
|
||||||
if (!BLTAnimationNode::initialize(context)) {
|
bool result = BLTAnimationNode::initialize(context);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
// TODO: do we always want looping in this case or do we traverse the graph to check what's reasonable?
|
// 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;
|
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return result;
|
||||||
}
|
}
|
||||||
void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
input_nodes[0]->active = true;
|
input_nodes[0]->active = true;
|
||||||
@ -441,9 +423,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BLTBlendTreeConnection {
|
struct BLTBlendTreeConnection {
|
||||||
Ref<BLTAnimationNode> source_node = nullptr;
|
const Ref<BLTAnimationNode> source_node = nullptr;
|
||||||
Ref<BLTAnimationNode> target_node = nullptr;
|
const Ref<BLTAnimationNode> target_node = nullptr;
|
||||||
StringName target_port_name = "";
|
const StringName target_port_name = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
||||||
@ -495,43 +477,27 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _print_subtree() const {
|
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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalVector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
Vector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
||||||
LocalVector<NodeConnectionInfo> node_connection_info;
|
LocalVector<NodeConnectionInfo> node_connection_info;
|
||||||
LocalVector<BLTBlendTreeConnection> connections;
|
LocalVector<BLTBlendTreeConnection> connections;
|
||||||
|
|
||||||
BLTBlendTreeGraph();
|
BLTBlendTreeGraph();
|
||||||
|
|
||||||
Ref<BLTAnimationNode> get_output_node();
|
Ref<BLTAnimationNode> get_output_node();
|
||||||
|
|
||||||
int find_node_index(const Ref<BLTAnimationNode> &node) const;
|
int find_node_index(const Ref<BLTAnimationNode> &node) const;
|
||||||
int find_node_index_by_name(const StringName &name) const;
|
int find_node_index_by_name(const StringName &name) const;
|
||||||
void sort_nodes_and_references();
|
void sort_nodes_and_references();
|
||||||
LocalVector<int> get_sorted_node_indices();
|
LocalVector<int> get_sorted_node_indices();
|
||||||
void sort_nodes_recursive(int node_index, LocalVector<int> &result);
|
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 add_index_and_update_subtrees_recursive(int node, int node_parent);
|
||||||
void remove_subtree_and_update_subtrees_recursive(int node, const HashSet<int> &removed_subtree_indices);
|
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 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);
|
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:
|
private:
|
||||||
@ -545,7 +511,7 @@ private:
|
|||||||
|
|
||||||
void setup_runtime_data() {
|
void setup_runtime_data() {
|
||||||
// Add nodes and allocate 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];
|
const Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||||
|
|
||||||
NodeRuntimeData node_runtime_data;
|
NodeRuntimeData node_runtime_data;
|
||||||
@ -558,7 +524,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
||||||
for (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];
|
Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||||
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||||
|
|
||||||
@ -583,6 +549,10 @@ public:
|
|||||||
};
|
};
|
||||||
LocalVector<NodeRuntimeData> _node_runtime_data;
|
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 {
|
int find_node_index(const Ref<BLTAnimationNode> &node) const {
|
||||||
return tree_graph.find_node_index(node);
|
return tree_graph.find_node_index(node);
|
||||||
}
|
}
|
||||||
@ -591,6 +561,14 @@ public:
|
|||||||
return tree_graph.find_node_index_by_name(name);
|
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) {
|
void add_node(const Ref<BLTAnimationNode> &node) {
|
||||||
if (tree_initialized) {
|
if (tree_initialized) {
|
||||||
print_error("Cannot add node to BlendTree: BlendTree already initialized.");
|
print_error("Cannot add node to BlendTree: BlendTree already initialized.");
|
||||||
@ -600,60 +578,6 @@ public:
|
|||||||
tree_graph.add_node(node);
|
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) {
|
ConnectionError add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||||
if (tree_initialized) {
|
if (tree_initialized) {
|
||||||
print_error("Cannot add connection to BlendTree: BlendTree already 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);
|
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) {
|
// overrides from SyncedAnimationNode
|
||||||
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
|
|
||||||
bool initialize(GraphEvaluationContext &context) override {
|
bool initialize(GraphEvaluationContext &context) override {
|
||||||
if (!BLTAnimationNode::initialize(context)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sort_nodes();
|
sort_nodes();
|
||||||
setup_runtime_data();
|
setup_runtime_data();
|
||||||
|
|
||||||
@ -708,7 +608,7 @@ public:
|
|||||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
||||||
|
|
||||||
tree_graph.nodes[0]->active = true;
|
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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
@ -722,7 +622,7 @@ public:
|
|||||||
|
|
||||||
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
GodotProfileZone("SyncedBlendTree::calculate_sync_track");
|
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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
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.delta = p_delta;
|
||||||
tree_graph.nodes[0]->node_time_info.position += 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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
@ -761,7 +661,7 @@ public:
|
|||||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
||||||
ZoneScopedN("SyncedBlendTree::evaluate");
|
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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
|
|||||||
@ -1,286 +1,16 @@
|
|||||||
@tool
|
@tool
|
||||||
class_name BlendalotMainPanel
|
|
||||||
extends Control
|
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.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
add_node_popup_menu.clear(true)
|
pass # Replace with function body.
|
||||||
|
|
||||||
for node_name in registered_nodes:
|
|
||||||
add_node_popup_menu.add_item(node_name)
|
|
||||||
|
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
pass
|
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_hit_me_button_pressed() -> void:
|
||||||
func _on_add_node_button_pressed() -> void:
|
print("Hello from the main screen plugin!")
|
||||||
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)
|
|
||||||
|
|||||||
@ -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"]
|
[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
|
size_flags_vertical = 3
|
||||||
script = ExtResource("1_427jg")
|
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
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
@ -21,88 +21,13 @@ anchor_bottom = 1.0
|
|||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="BlendTreeEditorContainer" unique_id=1742071203]
|
[node name="HitMeButton" type="Button" parent="VBoxContainer" unique_id=1060776498]
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ResetGraphButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=1060776498]
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 0
|
size_flags_horizontal = 0
|
||||||
text = "Reset"
|
text = "Hit me!"
|
||||||
|
|
||||||
[node name="FileNameLineEdit" type="LineEdit" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=2033619565]
|
[node name="GraphFrame" type="GraphFrame" parent="VBoxContainer" unique_id=184673233]
|
||||||
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]
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="AddNodePopupMenu" type="PopupMenu" parent="BlendTreeEditorContainer/Panel" unique_id=2020489213]
|
[connection signal="pressed" from="VBoxContainer/HitMeButton" to="." method="_on_hit_me_button_pressed"]
|
||||||
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"]
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ extends EditorPlugin
|
|||||||
|
|
||||||
const MainPanel = preload("res://addons/blendalot/blendalot_main_panel.tscn")
|
const MainPanel = preload("res://addons/blendalot/blendalot_main_panel.tscn")
|
||||||
|
|
||||||
var main_panel_instance:BlendalotMainPanel
|
var main_panel_instance
|
||||||
|
|
||||||
func _enable_plugin() -> void:
|
func _enable_plugin() -> void:
|
||||||
# Add autoloads here.
|
# Add autoloads here.
|
||||||
@ -43,14 +43,3 @@ func _get_plugin_name():
|
|||||||
|
|
||||||
func _get_plugin_icon():
|
func _get_plugin_icon():
|
||||||
return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons")
|
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:
|
func _ready() -> void:
|
||||||
blend_weight_slider.value = 0.5
|
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.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
pass
|
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="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="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="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="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://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"]
|
[ext_resource type="AnimationNodeBlendTree" uid="uid://vsf71o82lkld" path="res://animation_tree_walk_run.tres" id="6_5vw27"]
|
||||||
|
|
||||||
[sub_resource type="Theme" id="Theme_272bh"]
|
[sub_resource type="Theme" id="Theme_272bh"]
|
||||||
@ -31,17 +32,6 @@ sky = SubResource("Sky_1bvp3")
|
|||||||
tonemap_mode = 2
|
tonemap_mode = 2
|
||||||
glow_enabled = true
|
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]
|
[node name="Main" type="Node3D" unique_id=933302313]
|
||||||
script = ExtResource("1_1bvp3")
|
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)
|
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]
|
[node name="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1255239074]
|
||||||
active = false
|
|
||||||
libraries/animation_library = ExtResource("3_1bvp3")
|
libraries/animation_library = ExtResource("3_1bvp3")
|
||||||
|
|
||||||
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1602406394]
|
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1602406394]
|
||||||
animation_player = NodePath("../AnimationPlayer2")
|
animation_player = NodePath("../AnimationPlayer2")
|
||||||
tree_root = SubResource("BLTAnimationNodeBlendTree_272bh")
|
tree_root = ExtResource("5_7mycd")
|
||||||
skeleton = NodePath("../Armature/Skeleton3D")
|
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"]
|
[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"
|
config/name="Synced Blend Tree Test"
|
||||||
run/main_scene="uid://svj53e2xoio"
|
run/main_scene="uid://svj53e2xoio"
|
||||||
config/features=PackedStringArray("4.6", "Forward Plus")
|
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
@ -23,7 +23,3 @@ window/size/viewport_height=1024
|
|||||||
[dotnet]
|
[dotnet]
|
||||||
|
|
||||||
project/assembly_name="Synced Blend Tree Test"
|
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:
|
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
|
* 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:
|
Advantages:
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "tests/test_macros.h"
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
namespace TestBlendalotAnimationGraph {
|
namespace TestSyncedAnimationGraph {
|
||||||
|
|
||||||
TEST_CASE("[SyncedAnimationGraph][SyncTrack] Basic") {
|
TEST_CASE("[SyncedAnimationGraph][SyncTrack] Basic") {
|
||||||
SyncTrack track_a;
|
SyncTrack track_a;
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "tests/test_macros.h"
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
struct BlendTreeFixture {
|
struct SyncedAnimationGraphFixture {
|
||||||
Node *character_node;
|
Node *character_node;
|
||||||
Skeleton3D *skeleton_node;
|
Skeleton3D *skeleton_node;
|
||||||
AnimationPlayer *player_node;
|
AnimationPlayer *player_node;
|
||||||
@ -20,8 +20,8 @@ struct BlendTreeFixture {
|
|||||||
|
|
||||||
Ref<AnimationLibrary> animation_library;
|
Ref<AnimationLibrary> animation_library;
|
||||||
|
|
||||||
BLTAnimationGraph *animation_graph;
|
BLTAnimationGraph *synced_animation_graph;
|
||||||
BlendTreeFixture() {
|
SyncedAnimationGraphFixture() {
|
||||||
BLTAnimationGraph *scene_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
BLTAnimationGraph *scene_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
||||||
|
|
||||||
if (scene_animation_graph == nullptr) {
|
if (scene_animation_graph == nullptr) {
|
||||||
@ -50,12 +50,12 @@ struct BlendTreeFixture {
|
|||||||
|
|
||||||
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
||||||
|
|
||||||
animation_graph = memnew(BLTAnimationGraph);
|
synced_animation_graph = memnew(BLTAnimationGraph);
|
||||||
animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
synced_animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
||||||
SceneTree::get_singleton()->get_root()->add_child(animation_graph);
|
SceneTree::get_singleton()->get_root()->add_child(synced_animation_graph);
|
||||||
|
|
||||||
animation_graph->set_animation_player(player_node->get_path());
|
synced_animation_graph->set_animation_player(player_node->get_path());
|
||||||
animation_graph->set_skeleton(skeleton_node->get_path());
|
synced_animation_graph->set_skeleton(skeleton_node->get_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_animations() {
|
void setup_animations() {
|
||||||
@ -112,8 +112,8 @@ struct BlendTreeFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void assign_scene_variables() {
|
void assign_scene_variables() {
|
||||||
animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
synced_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
||||||
REQUIRE(animation_graph);
|
REQUIRE(synced_animation_graph);
|
||||||
character_node = (SceneTree::get_singleton()->get_root()->find_child("CharacterNode", true, false));
|
character_node = (SceneTree::get_singleton()->get_root()->find_child("CharacterNode", true, false));
|
||||||
REQUIRE(character_node != nullptr);
|
REQUIRE(character_node != nullptr);
|
||||||
skeleton_node = dynamic_cast<Skeleton3D *>((SceneTree::get_singleton()->get_root()->find_child("Skeleton", true, false)));
|
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;
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph tree_constructor;
|
||||||
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
||||||
animation_sampler_node0.instantiate();
|
animation_sampler_node0.instantiate();
|
||||||
animation_sampler_node0->set_name("Sampler0");
|
animation_sampler_node0->name = "Sampler0";
|
||||||
tree_constructor.add_node(animation_sampler_node0);
|
tree_constructor.add_node(animation_sampler_node0);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node1;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node1;
|
||||||
animation_sampler_node1.instantiate();
|
animation_sampler_node1.instantiate();
|
||||||
animation_sampler_node1->set_name("Sampler1");
|
animation_sampler_node1->name = "Sampler1";
|
||||||
tree_constructor.add_node(animation_sampler_node1);
|
tree_constructor.add_node(animation_sampler_node1);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node2;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node2;
|
||||||
animation_sampler_node2.instantiate();
|
animation_sampler_node2.instantiate();
|
||||||
animation_sampler_node2->set_name("Sampler2");
|
animation_sampler_node2->name = "Sampler2";
|
||||||
tree_constructor.add_node(animation_sampler_node2);
|
tree_constructor.add_node(animation_sampler_node2);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeBlend2> node_blend0;
|
Ref<BLTAnimationNodeBlend2> node_blend0;
|
||||||
node_blend0.instantiate();
|
node_blend0.instantiate();
|
||||||
node_blend0->set_name("Blend0");
|
node_blend0->name = "Blend0";
|
||||||
tree_constructor.add_node(node_blend0);
|
tree_constructor.add_node(node_blend0);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeBlend2> node_blend1;
|
Ref<BLTAnimationNodeBlend2> node_blend1;
|
||||||
node_blend1.instantiate();
|
node_blend1.instantiate();
|
||||||
node_blend1->set_name("Blend1");
|
node_blend1->name = "Blend1";
|
||||||
tree_constructor.add_node(node_blend1);
|
tree_constructor.add_node(node_blend1);
|
||||||
|
|
||||||
// Tree
|
// 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));
|
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(blend0_index));
|
||||||
|
|
||||||
// Perform remaining connections
|
// 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"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(animation_sampler_node2, node_blend1, "Input1"));
|
||||||
|
|
||||||
// Output node must have all nodes in its subtree:
|
// 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();
|
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 (unsigned int i = 0; i < tree_constructor.nodes.size(); i++) {
|
||||||
for (int input_index : tree_constructor.node_connection_info[i].input_subtree_node_indices) {
|
for (int input_index : tree_constructor.node_connection_info[i].input_subtree_node_indices) {
|
||||||
CHECK(input_index >= i);
|
CHECK(input_index > i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] Test AnimationData blending") {
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] Test AnimationData blending") {
|
||||||
AnimationData data_t0;
|
AnimationData data_t0;
|
||||||
data_t0.allocate_track_values(test_animation_a, skeleton_node);
|
data_t0.allocate_track_values(test_animation_a, skeleton_node);
|
||||||
data_t0.sample_from_animation(test_animation_a, skeleton_node, 0.0);
|
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;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node;
|
||||||
animation_sampler_node.instantiate();
|
animation_sampler_node.instantiate();
|
||||||
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
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;
|
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));
|
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;
|
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||||
synced_blend_tree_node.instantiate();
|
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";
|
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
||||||
|
|
||||||
synced_blend_tree_node->add_node(animation_sampler_node);
|
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;
|
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));
|
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;
|
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||||
synced_blend_tree_node.instantiate();
|
synced_blend_tree_node.instantiate();
|
||||||
|
|
||||||
@ -329,19 +329,20 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
|||||||
// Blend2
|
// Blend2
|
||||||
Ref<BLTAnimationNodeBlend2> blend2_node;
|
Ref<BLTAnimationNodeBlend2> blend2_node;
|
||||||
blend2_node.instantiate();
|
blend2_node.instantiate();
|
||||||
blend2_node->set_name("Blend2");
|
blend2_node->name = "Blend2";
|
||||||
blend2_node->blend_weight = 0.5;
|
blend2_node->blend_weight = 0.5;
|
||||||
blend2_node->sync = false;
|
blend2_node->sync = false;
|
||||||
|
|
||||||
synced_blend_tree_node->add_node(blend2_node);
|
synced_blend_tree_node->add_node(blend2_node);
|
||||||
|
|
||||||
// Connect nodes
|
// 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_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(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);
|
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];
|
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[0] == animation_sampler_node_a);
|
||||||
CHECK(blend2_runtime_data.input_nodes[1] == animation_sampler_node_b);
|
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") {
|
SUBCASE("Perform default evaluation") {
|
||||||
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
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_a->animation_name = "animation_library/TestAnimationSyncA";
|
||||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationSyncB";
|
animation_sampler_node_b->animation_name = "animation_library/TestAnimationSyncB";
|
||||||
blend2_node->sync = true;
|
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
|
// By blending both animations we get a SyncTrack of duration 1.5s with the following
|
||||||
// intervals:
|
// 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");
|
Ref<BLTAnimationNodeBlendTree> loaded_synced_blend_tree = ResourceLoader::load("synced_blend_tree_node.tres");
|
||||||
REQUIRE(loaded_synced_blend_tree.is_valid());
|
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());
|
REQUIRE(loaded_blend2_node.is_valid());
|
||||||
CHECK(loaded_blend2_node->sync == false);
|
CHECK(loaded_blend2_node->sync == false);
|
||||||
CHECK(loaded_blend2_node->blend_weight == blend2_node->blend_weight);
|
CHECK(loaded_blend2_node->blend_weight == blend2_node->blend_weight);
|
||||||
|
|
||||||
loaded_synced_blend_tree->initialize(animation_graph->get_context());
|
loaded_synced_blend_tree->initialize(synced_animation_graph->get_context());
|
||||||
animation_graph->set_root_animation_node(loaded_synced_blend_tree);
|
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.
|
// Re-evaluate using a different time. All animation samplers will start again from 0.
|
||||||
SceneTree::get_singleton()->process(0.2);
|
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") {
|
} //namespace TestSyncedAnimationGraph
|
||||||
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
|
|
||||||
Loading…
x
Reference in New Issue
Block a user