Started exposing more functions to GDScript and made BLTBlendTreeGraph part of BLTBlendTree such that return codes look nicer in Godot.
This commit is contained in:
parent
0b6589f241
commit
625a3ae79f
@ -41,6 +41,18 @@ 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("get_output_node"), &BLTAnimationNodeBlendTree::get_output_node);
|
||||||
|
ClassDB::bind_method(D_METHOD("add_connection", "source_node", "target_node", "target_port_name"), &BLTAnimationNodeBlendTree::add_connection);
|
||||||
|
|
||||||
|
BIND_CONSTANT(CONNECTION_OK);
|
||||||
|
BIND_CONSTANT(CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED);
|
||||||
|
BIND_CONSTANT(CONNECTION_ERROR_NO_SOURCE_NODE);
|
||||||
|
BIND_CONSTANT(CONNECTION_ERROR_NO_TARGET_NODE);
|
||||||
|
BIND_CONSTANT(CONNECTION_ERROR_PARENT_EXISTS);
|
||||||
|
BIND_CONSTANT(CONNECTION_ERROR_TARGET_PORT_NOT_FOUND);
|
||||||
|
BIND_CONSTANT(CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED);
|
||||||
|
BIND_CONSTANT(CONNECTION_ERROR_CONNECTION_CREATES_LOOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
void BLTAnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
@ -396,3 +408,162 @@ bool BLTAnimationNodeBlend2::_set(const StringName &p_name, const Variant &p_val
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph::BLTBlendTreeGraph() {
|
||||||
|
Ref<BLTAnimationNodeOutput> output_node;
|
||||||
|
output_node.instantiate();
|
||||||
|
output_node->name = "Output";
|
||||||
|
add_node(output_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<BLTAnimationNode> BLTAnimationNodeBlendTree::BLTBlendTreeGraph::get_output_node() {
|
||||||
|
return nodes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index(const Ref<BLTAnimationNode> &node) const {
|
||||||
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
|
if (nodes[i] == node) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const StringName &name) const {
|
||||||
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
|
if (nodes[i]->name == name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimationNode> &node) {
|
||||||
|
StringName node_base_name = node->name;
|
||||||
|
if (node_base_name.is_empty()) {
|
||||||
|
node_base_name = node->get_class_name();
|
||||||
|
}
|
||||||
|
node->name = node_base_name;
|
||||||
|
|
||||||
|
int number_suffix = 1;
|
||||||
|
while (find_node_index_by_name(node->name) != -1) {
|
||||||
|
node->name = vformat("%s %d", node_base_name, number_suffix);
|
||||||
|
number_suffix++;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.push_back(node);
|
||||||
|
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
||||||
|
LocalVector<int> sorted_node_indices = get_sorted_node_indices();
|
||||||
|
|
||||||
|
Vector<Ref<BLTAnimationNode>> sorted_nodes;
|
||||||
|
LocalVector<NodeConnectionInfo> old_node_connection_info = node_connection_info;
|
||||||
|
for (unsigned int i = 0; i < sorted_node_indices.size(); i++) {
|
||||||
|
int node_index = sorted_node_indices[i];
|
||||||
|
sorted_nodes.push_back(nodes[node_index]);
|
||||||
|
node_connection_info[i] = old_node_connection_info[node_index];
|
||||||
|
}
|
||||||
|
nodes = sorted_nodes;
|
||||||
|
|
||||||
|
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
||||||
|
if (connection_info.parent_node_index != -1) {
|
||||||
|
connection_info.parent_node_index = sorted_node_indices[connection_info.parent_node_index];
|
||||||
|
}
|
||||||
|
connection_info.apply_node_mapping(sorted_node_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalVector<int> BLTAnimationNodeBlendTree::BLTBlendTreeGraph::get_sorted_node_indices() {
|
||||||
|
LocalVector<int> result;
|
||||||
|
|
||||||
|
sort_nodes_recursive(0, result);
|
||||||
|
result.reverse();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_recursive(int node_index, LocalVector<int> &result) {
|
||||||
|
for (int input_node_index : node_connection_info[node_index].connected_child_node_index_at_port) {
|
||||||
|
if (input_node_index >= 0) {
|
||||||
|
sort_nodes_recursive(input_node_index, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push_back(node_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node, int node_parent) {
|
||||||
|
if (node_parent == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_connection_info[node_parent].input_subtree_node_indices.insert(node);
|
||||||
|
|
||||||
|
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
||||||
|
node_connection_info[node_parent].input_subtree_node_indices.insert(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].parent_node_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||||
|
ConnectionError result = is_connection_valid(source_node, target_node, target_port_name);
|
||||||
|
if (result != CONNECTION_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int source_node_index = find_node_index(source_node);
|
||||||
|
int target_node_index = find_node_index(target_node);
|
||||||
|
int target_input_port_index = target_node->get_input_index(target_port_name);
|
||||||
|
|
||||||
|
node_connection_info[source_node_index].parent_node_index = target_node_index;
|
||||||
|
node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] = source_node_index;
|
||||||
|
connections.push_back(BLTBlendTreeConnection{ source_node, target_node, target_port_name });
|
||||||
|
|
||||||
|
add_index_and_update_subtrees_recursive(source_node_index, target_node_index);
|
||||||
|
|
||||||
|
return CONNECTION_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(target_inputs);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -428,11 +428,26 @@ struct BLTBlendTreeConnection {
|
|||||||
const StringName target_port_name = "";
|
const StringName target_port_name = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
||||||
|
GDCLASS(BLTAnimationNodeBlendTree, BLTAnimationNode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ConnectionError {
|
||||||
|
CONNECTION_OK,
|
||||||
|
CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED,
|
||||||
|
CONNECTION_ERROR_NO_SOURCE_NODE,
|
||||||
|
CONNECTION_ERROR_NO_TARGET_NODE,
|
||||||
|
CONNECTION_ERROR_PARENT_EXISTS,
|
||||||
|
CONNECTION_ERROR_TARGET_PORT_NOT_FOUND,
|
||||||
|
CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED,
|
||||||
|
CONNECTION_ERROR_CONNECTION_CREATES_LOOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
* @class BLTBlendTreeGraph
|
* @class BLTBlendTreeGraph
|
||||||
* Helper class that is used to build runtime blend trees and also to validate connections.
|
* Helper class that is used to build runtime blend trees and also to validate connections.
|
||||||
*/
|
*/
|
||||||
struct BLTBlendTreeGraph {
|
struct BLTBlendTreeGraph {
|
||||||
struct NodeConnectionInfo {
|
struct NodeConnectionInfo {
|
||||||
int parent_node_index = -1;
|
int parent_node_index = -1;
|
||||||
HashSet<int> input_subtree_node_indices; // Contains all nodes down to the tree leaves that influence this node.
|
HashSet<int> input_subtree_node_indices; // Contains all nodes down to the tree leaves that influence this node.
|
||||||
@ -447,20 +462,6 @@ struct BLTBlendTreeGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _print_subtree() const {
|
|
||||||
String result = vformat("subtree node indices (%d): ", input_subtree_node_indices.size());
|
|
||||||
bool is_first = true;
|
|
||||||
for (int index : input_subtree_node_indices) {
|
|
||||||
if (is_first) {
|
|
||||||
result += vformat("%d", index);
|
|
||||||
is_first = false;
|
|
||||||
} else {
|
|
||||||
result += vformat(", %d", index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print_line(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply_node_mapping(const LocalVector<int> &node_index_mapping) {
|
void apply_node_mapping(const LocalVector<int> &node_index_mapping) {
|
||||||
// Map connected node indices
|
// Map connected node indices
|
||||||
for (unsigned int j = 0; j < connected_child_node_index_at_port.size(); j++) {
|
for (unsigned int j = 0; j < connected_child_node_index_at_port.size(); j++) {
|
||||||
@ -475,174 +476,31 @@ struct BLTBlendTreeGraph {
|
|||||||
input_subtree_node_indices.insert(node_index_mapping.find(old_index));
|
input_subtree_node_indices.insert(node_index_mapping.find(old_index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _print_subtree() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector<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<BLTAnimationNodeOutput> output_node;
|
|
||||||
output_node.instantiate();
|
|
||||||
output_node->name = "Output";
|
|
||||||
add_node(output_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<BLTAnimationNode> get_output_node() const {
|
Ref<BLTAnimationNode> get_output_node();
|
||||||
return nodes[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_node_index(const Ref<BLTAnimationNode> &node) const {
|
int find_node_index(const Ref<BLTAnimationNode> &node) const;
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
int find_node_index_by_name(const StringName &name) const;
|
||||||
if (nodes[i] == node) {
|
void sort_nodes_and_references();
|
||||||
return i;
|
LocalVector<int> get_sorted_node_indices();
|
||||||
}
|
void sort_nodes_recursive(int node_index, LocalVector<int> &result);
|
||||||
}
|
void add_index_and_update_subtrees_recursive(int node, int node_parent);
|
||||||
|
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const;
|
||||||
|
|
||||||
return -1;
|
void add_node(const Ref<BLTAnimationNode> &node);
|
||||||
}
|
ConnectionError add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name);
|
||||||
|
};
|
||||||
int find_node_index_by_name(const StringName &name) const {
|
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
|
||||||
if (nodes[i]->name == name) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_node(const Ref<BLTAnimationNode> &node) {
|
|
||||||
StringName node_base_name = node->name;
|
|
||||||
if (node_base_name.is_empty()) {
|
|
||||||
node_base_name = node->get_class_name();
|
|
||||||
}
|
|
||||||
node->name = node_base_name;
|
|
||||||
|
|
||||||
int number_suffix = 1;
|
|
||||||
while (find_node_index_by_name(node->name) != -1) {
|
|
||||||
node->name = vformat("%s %d", node_base_name, number_suffix);
|
|
||||||
number_suffix++;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.push_back(node);
|
|
||||||
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort_nodes_and_references() {
|
|
||||||
LocalVector<int> sorted_node_indices = get_sorted_node_indices();
|
|
||||||
|
|
||||||
Vector<Ref<BLTAnimationNode>> sorted_nodes;
|
|
||||||
LocalVector<NodeConnectionInfo> old_node_connection_info = node_connection_info;
|
|
||||||
for (unsigned int i = 0; i < sorted_node_indices.size(); i++) {
|
|
||||||
int node_index = sorted_node_indices[i];
|
|
||||||
sorted_nodes.push_back(nodes[node_index]);
|
|
||||||
node_connection_info[i] = old_node_connection_info[node_index];
|
|
||||||
}
|
|
||||||
nodes = sorted_nodes;
|
|
||||||
|
|
||||||
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
|
||||||
if (connection_info.parent_node_index != -1) {
|
|
||||||
connection_info.parent_node_index = sorted_node_indices[connection_info.parent_node_index];
|
|
||||||
}
|
|
||||||
connection_info.apply_node_mapping(sorted_node_indices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalVector<int> get_sorted_node_indices() {
|
|
||||||
LocalVector<int> result;
|
|
||||||
|
|
||||||
sort_nodes_recursive(0, result);
|
|
||||||
result.reverse();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort_nodes_recursive(int node_index, LocalVector<int> &result) {
|
|
||||||
for (int input_node_index : node_connection_info[node_index].connected_child_node_index_at_port) {
|
|
||||||
if (input_node_index >= 0) {
|
|
||||||
sort_nodes_recursive(input_node_index, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.push_back(node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_index_and_update_subtrees_recursive(int node, int node_parent) {
|
|
||||||
if (node_parent == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_connection_info[node_parent].input_subtree_node_indices.insert(node);
|
|
||||||
|
|
||||||
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
|
||||||
node_connection_info[node_parent].input_subtree_node_indices.insert(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].parent_node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
|
||||||
if (!is_connection_valid(source_node, target_node, target_port_name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int source_node_index = find_node_index(source_node);
|
|
||||||
int target_node_index = find_node_index(target_node);
|
|
||||||
int target_input_port_index = target_node->get_input_index(target_port_name);
|
|
||||||
|
|
||||||
node_connection_info[source_node_index].parent_node_index = target_node_index;
|
|
||||||
node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] = source_node_index;
|
|
||||||
connections.push_back(BLTBlendTreeConnection{ source_node, target_node, target_port_name });
|
|
||||||
|
|
||||||
add_index_and_update_subtrees_recursive(source_node_index, target_node_index);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) {
|
|
||||||
int source_node_index = find_node_index(source_node);
|
|
||||||
if (source_node_index == -1) {
|
|
||||||
print_error("Cannot connect nodes: source node not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node_connection_info[source_node_index].parent_node_index != -1) {
|
|
||||||
print_error("Cannot connect node: source node already has a parent.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int target_node_index = find_node_index(target_node);
|
|
||||||
if (target_node_index == -1) {
|
|
||||||
print_error("Cannot connect nodes: target node not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<StringName> target_inputs;
|
|
||||||
target_node->get_input_names(target_inputs);
|
|
||||||
|
|
||||||
if (!target_inputs.has(target_port_name)) {
|
|
||||||
print_error("Cannot connect nodes: target port not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
|
||||||
GDCLASS(BLTAnimationNodeBlendTree, BLTAnimationNode);
|
|
||||||
|
|
||||||
|
private:
|
||||||
BLTBlendTreeGraph tree_graph;
|
BLTBlendTreeGraph tree_graph;
|
||||||
bool tree_initialized = false;
|
bool tree_initialized = false;
|
||||||
|
|
||||||
@ -720,10 +578,10 @@ public:
|
|||||||
tree_graph.add_node(node);
|
tree_graph.add_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool 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.");
|
||||||
return false;
|
return CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree_graph.add_connection(source_node, target_node, target_port_name);
|
return tree_graph.add_connection(source_node, target_node, target_port_name);
|
||||||
@ -745,7 +603,8 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
void
|
||||||
|
activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
||||||
|
|
||||||
tree_graph.nodes[0]->active = true;
|
tree_graph.nodes[0]->active = true;
|
||||||
@ -839,3 +698,5 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VARIANT_ENUM_CAST(BLTAnimationNodeBlendTree::ConnectionError)
|
||||||
|
|||||||
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,21 +1,21 @@
|
|||||||
[gd_resource type="SyncedBlendTree" load_steps=4 format=3]
|
[gd_resource type="BLTAnimationNodeBlendTree" load_steps=4 format=3]
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_bvt3d"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_bvt3d"]
|
||||||
animation = &"animation_library/TestAnimationB"
|
animation = &"animation_library/TestAnimationB"
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_sntl5"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
||||||
animation = &"animation_library/TestAnimationA"
|
animation = &"animation_library/TestAnimationA"
|
||||||
|
|
||||||
[sub_resource type="AnimationBlend2Node" id="AnimationBlend2Node_n4m28"]
|
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_n4m28"]
|
||||||
sync = false
|
sync = false
|
||||||
blend_amount = 0.5
|
blend_amount = 0.5
|
||||||
sync = false
|
sync = false
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
nodes/Blend2/node = SubResource("AnimationBlend2Node_n4m28")
|
nodes/Blend2/node = SubResource("BLTAnimationNodeBlend2_n4m28")
|
||||||
nodes/Blend2/position = Vector2(0, 0)
|
nodes/Blend2/position = Vector2(0, 0)
|
||||||
"nodes/AnimationSamplerNode 1/node" = SubResource("AnimationSamplerNode_bvt3d")
|
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_bvt3d")
|
||||||
"nodes/AnimationSamplerNode 1/position" = Vector2(0, 0)
|
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
||||||
nodes/AnimationSamplerNode/node = SubResource("AnimationSamplerNode_sntl5")
|
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_sntl5")
|
||||||
nodes/AnimationSamplerNode/position = Vector2(0, 0)
|
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
||||||
node_connections = [&"Blend2", 0, &"AnimationSamplerNode", &"Blend2", 1, &"AnimationSamplerNode 1", &"Output", 0, &"Blend2"]
|
node_connections = [&"Blend2", 0, &"BLTAnimationNodeSampler", &"Blend2", 1, &"BLTAnimationNodeSampler 1", &"Output", 0, &"Blend2"]
|
||||||
|
|||||||
@ -9,6 +9,7 @@ void initialize_blendalot_animgraph_module(ModuleInitializationLevel p_level) {
|
|||||||
}
|
}
|
||||||
ClassDB::register_class<BLTAnimationGraph>();
|
ClassDB::register_class<BLTAnimationGraph>();
|
||||||
ClassDB::register_class<BLTAnimationNode>();
|
ClassDB::register_class<BLTAnimationNode>();
|
||||||
|
ClassDB::register_class<BLTAnimationNodeOutput>();
|
||||||
ClassDB::register_class<BLTAnimationNodeBlendTree>();
|
ClassDB::register_class<BLTAnimationNodeBlendTree>();
|
||||||
ClassDB::register_class<BLTAnimationNodeSampler>();
|
ClassDB::register_class<BLTAnimationNodeSampler>();
|
||||||
ClassDB::register_class<BLTAnimationNodeBlend2>();
|
ClassDB::register_class<BLTAnimationNodeBlend2>();
|
||||||
|
|||||||
@ -142,7 +142,7 @@ struct SyncedAnimationGraphFixture {
|
|||||||
namespace TestSyncedAnimationGraph {
|
namespace TestSyncedAnimationGraph {
|
||||||
|
|
||||||
TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
||||||
BlendTreeGraph tree_constructor;
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph tree_constructor;
|
||||||
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
||||||
animation_sampler_node0.instantiate();
|
animation_sampler_node0.instantiate();
|
||||||
@ -174,7 +174,7 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
// Sampler1 -+ Blend0 -\
|
// Sampler1 -+ Blend0 -\
|
||||||
// Sampler2 -----------+ Blend1 - Output
|
// Sampler2 -----------+ Blend1 - Output
|
||||||
|
|
||||||
CHECK(tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
||||||
|
|
||||||
// Ensure that subtree is properly updated
|
// Ensure that subtree is properly updated
|
||||||
int sampler0_index = tree_constructor.find_node_index(animation_sampler_node0);
|
int sampler0_index = tree_constructor.find_node_index(animation_sampler_node0);
|
||||||
@ -182,12 +182,16 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
CHECK(tree_constructor.node_connection_info[blend0_index].input_subtree_node_indices.has(sampler0_index));
|
CHECK(tree_constructor.node_connection_info[blend0_index].input_subtree_node_indices.has(sampler0_index));
|
||||||
|
|
||||||
// Connect blend0 to blend1
|
// Connect blend0 to blend1
|
||||||
CHECK(tree_constructor.add_connection(node_blend0, node_blend1, "Input0"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend0, node_blend1, "Input0"));
|
||||||
|
|
||||||
|
// Creating a loop must fail
|
||||||
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_ERROR_CONNECTION_CREATES_LOOP == tree_constructor.add_connection(node_blend1, node_blend0, "Input1"));
|
||||||
|
|
||||||
|
// Correct connection of Sampler1 to Blend0
|
||||||
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input1"));
|
||||||
|
|
||||||
// Connecting to an already connected port must fail
|
// Connecting to an already connected port must fail
|
||||||
CHECK(!tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input0"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED == tree_constructor.add_connection(animation_sampler_node2, node_blend0, "Input0"));
|
||||||
// Correct connection of Sampler1 to Blend0
|
|
||||||
CHECK(tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input1"));
|
|
||||||
|
|
||||||
// Ensure that subtree is properly updated
|
// Ensure that subtree is properly updated
|
||||||
int sampler1_index = tree_constructor.find_node_index(animation_sampler_node0);
|
int sampler1_index = tree_constructor.find_node_index(animation_sampler_node0);
|
||||||
@ -196,12 +200,9 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(sampler0_index));
|
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(sampler0_index));
|
||||||
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(blend0_index));
|
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(blend0_index));
|
||||||
|
|
||||||
// Creating a loop must fail
|
|
||||||
CHECK(!tree_constructor.add_connection(node_blend1, node_blend0, "Input1"));
|
|
||||||
|
|
||||||
// Perform remaining connections
|
// Perform remaining connections
|
||||||
CHECK(tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Input"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Input"));
|
||||||
CHECK(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:
|
||||||
CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(1));
|
CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(1));
|
||||||
@ -286,7 +287,7 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
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(synced_blend_tree_node->add_connection(animation_sampler_node, synced_blend_tree_node->get_output_node(), "Input"));
|
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(synced_animation_graph->get_context());
|
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||||
|
|
||||||
@ -337,9 +338,9 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
// Connect nodes
|
// Connect nodes
|
||||||
Vector<StringName> blend2_inputs;
|
Vector<StringName> blend2_inputs;
|
||||||
blend2_node->get_input_names(blend2_inputs);
|
blend2_node->get_input_names(blend2_inputs);
|
||||||
REQUIRE(synced_blend_tree_node->add_connection(animation_sampler_node_a, blend2_node, blend2_inputs[0]));
|
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(animation_sampler_node_a, blend2_node, blend2_inputs[0]));
|
||||||
REQUIRE(synced_blend_tree_node->add_connection(animation_sampler_node_b, blend2_node, blend2_inputs[1]));
|
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(animation_sampler_node_b, blend2_node, blend2_inputs[1]));
|
||||||
REQUIRE(synced_blend_tree_node->add_connection(blend2_node, synced_blend_tree_node->get_output_node(), "Input"));
|
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(synced_animation_graph->get_context());
|
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||||
|
|
||||||
@ -430,7 +431,7 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
// Test saving and loading of the blend tree to a resource
|
// Test saving and loading of the blend tree to a resource
|
||||||
ResourceSaver::save(synced_blend_tree_node, "synced_blend_tree_node.tres");
|
ResourceSaver::save(synced_blend_tree_node, "synced_blend_tree_node.tres");
|
||||||
|
|
||||||
REQUIRE(ClassDB::class_exists("AnimationSamplerNode"));
|
REQUIRE(ClassDB::class_exists("BLTAnimationNodeSampler"));
|
||||||
|
|
||||||
// Load blend tree
|
// Load blend tree
|
||||||
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");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user