BlendTree connection can now be removed.
This commit is contained in:
parent
d3fe4afc57
commit
1e7dd4ba45
@ -468,7 +468,10 @@ void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimati
|
|||||||
}
|
}
|
||||||
|
|
||||||
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::sort_nodes_and_references() {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
||||||
@ -509,37 +512,30 @@ 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, int node_parent) {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node_index, int node_parent_index) {
|
||||||
if (node_parent == -1) {
|
if (node_parent_index == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node_connection_info[node_parent].input_subtree_node_indices.insert(node);
|
node_connection_info[node_parent_index].input_subtree_node_indices.insert(node_index);
|
||||||
|
|
||||||
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
for (int index : node_connection_info[node_index].input_subtree_node_indices) {
|
||||||
node_connection_info[node_parent].input_subtree_node_indices.insert(index);
|
node_connection_info[node_parent_index].input_subtree_node_indices.insert(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].parent_node_index);
|
add_index_and_update_subtrees_recursive(node_parent_index, node_connection_info[node_parent_index].parent_node_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_subtree_and_update_subtrees_recursive(int node_index, const HashSet<int> &removed_subtree_indices) {
|
||||||
ConnectionError result = is_connection_valid(source_node, target_node, target_port_name);
|
NodeConnectionInfo &connection_info = node_connection_info[node_index];
|
||||||
if (result != CONNECTION_OK) {
|
|
||||||
return result;
|
for (int subtree_node_index : removed_subtree_indices) {
|
||||||
|
connection_info.input_subtree_node_indices.erase(subtree_node_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int source_node_index = find_node_index(source_node);
|
if (connection_info.parent_node_index != -1) {
|
||||||
int target_node_index = find_node_index(target_node);
|
remove_subtree_and_update_subtrees_recursive(connection_info.parent_node_index, removed_subtree_indices);
|
||||||
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 {
|
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const {
|
||||||
@ -580,3 +576,53 @@ BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTr
|
|||||||
|
|
||||||
return CONNECTION_OK;
|
return CONNECTION_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_connection_index(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) const {
|
||||||
|
for (uint32_t i = 0; i < connections.size(); i++) {
|
||||||
|
if (connections[i].source_node == source_node && connections[i].target_node == target_node && connections[i].target_port_name == target_port_name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||||
|
int source_node_index = find_node_index(source_node);
|
||||||
|
NodeConnectionInfo &connection_info = node_connection_info[source_node_index];
|
||||||
|
|
||||||
|
if (connection_info.parent_node_index != -1) {
|
||||||
|
NodeConnectionInfo &parent_connection_info = node_connection_info[connection_info.parent_node_index];
|
||||||
|
parent_connection_info.input_subtree_node_indices.erase(source_node_index);
|
||||||
|
parent_connection_info.connected_child_node_index_at_port[target_node->get_input_index(target_port_name)] = -1;
|
||||||
|
|
||||||
|
remove_subtree_and_update_subtrees_recursive(connection_info.parent_node_index, connection_info.input_subtree_node_indices);
|
||||||
|
|
||||||
|
connection_info.parent_node_index = -1;
|
||||||
|
|
||||||
|
uint32_t connection_index = find_connection_index(source_node, target_node, target_port_name);
|
||||||
|
assert(connection_index >= 0);
|
||||||
|
connections.remove_at(connection_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONNECTION_OK;
|
||||||
|
}
|
||||||
|
|||||||
@ -441,9 +441,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BLTBlendTreeConnection {
|
struct BLTBlendTreeConnection {
|
||||||
const Ref<BLTAnimationNode> source_node = nullptr;
|
Ref<BLTAnimationNode> source_node = nullptr;
|
||||||
const Ref<BLTAnimationNode> target_node = nullptr;
|
Ref<BLTAnimationNode> target_node = nullptr;
|
||||||
const StringName target_port_name = "";
|
StringName target_port_name = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
||||||
@ -495,7 +495,19 @@ 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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
Vector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
||||||
@ -505,17 +517,20 @@ public:
|
|||||||
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, int node_parent);
|
void add_index_and_update_subtrees_recursive(int node_index, int node_parent_index);
|
||||||
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const;
|
void remove_subtree_and_update_subtrees_recursive(int node, const HashSet<int> &removed_subtree_indices);
|
||||||
|
|
||||||
void add_node(const Ref<BLTAnimationNode> &node);
|
void add_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:
|
||||||
@ -638,6 +653,15 @@ 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) {
|
||||||
|
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 get_connections_as_array() const {
|
||||||
Array result;
|
Array result;
|
||||||
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
||||||
|
|||||||
@ -213,10 +213,10 @@ TEST_CASE("[SyncedAnimationGraph] 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.
|
// Check that for node i all input nodes have a node index j >= i (i is part of the subtree)
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,4 +455,75 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTreeGraph][ChangeConnectivity] BlendTreeGraph with various nodes and connections that are removed") {
|
||||||
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph blend_tree_graph;
|
||||||
|
|
||||||
|
// TestAnimationA
|
||||||
|
Ref<BLTAnimationNodeSampler> animation_sampler_node_a;
|
||||||
|
animation_sampler_node_a.instantiate();
|
||||||
|
animation_sampler_node_a->animation_name = "animation_library/TestAnimationA";
|
||||||
|
|
||||||
|
blend_tree_graph.add_node(animation_sampler_node_a);
|
||||||
|
|
||||||
|
// TestAnimationB
|
||||||
|
Ref<BLTAnimationNodeSampler> animation_sampler_node_b;
|
||||||
|
animation_sampler_node_b.instantiate();
|
||||||
|
animation_sampler_node_b->animation_name = "animation_library/TestAnimationB";
|
||||||
|
|
||||||
|
blend_tree_graph.add_node(animation_sampler_node_b);
|
||||||
|
|
||||||
|
// TestAnimationB
|
||||||
|
Ref<BLTAnimationNodeSampler> animation_sampler_node_c;
|
||||||
|
animation_sampler_node_c.instantiate();
|
||||||
|
animation_sampler_node_c->animation_name = "animation_library/TestAnimationC";
|
||||||
|
|
||||||
|
blend_tree_graph.add_node(animation_sampler_node_c);
|
||||||
|
|
||||||
|
// Blend2A
|
||||||
|
Ref<BLTAnimationNodeBlend2> blend2_node_a;
|
||||||
|
blend2_node_a.instantiate();
|
||||||
|
blend2_node_a->set_name("Blend2A");
|
||||||
|
blend2_node_a->blend_weight = 0.5;
|
||||||
|
blend2_node_a->sync = false;
|
||||||
|
|
||||||
|
blend_tree_graph.add_node(blend2_node_a);
|
||||||
|
|
||||||
|
// Blend2B
|
||||||
|
Ref<BLTAnimationNodeBlend2> blend2_node_b;
|
||||||
|
blend2_node_b.instantiate();
|
||||||
|
blend2_node_b->set_name("Blend2A");
|
||||||
|
blend2_node_b->blend_weight = 0.5;
|
||||||
|
blend2_node_b->sync = false;
|
||||||
|
|
||||||
|
blend_tree_graph.add_node(blend2_node_b);
|
||||||
|
|
||||||
|
// Connect nodes: Subgraph Output, Blend2A, SamplerA
|
||||||
|
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(blend2_node_a, blend_tree_graph.get_output_node(), "Output"));
|
||||||
|
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(animation_sampler_node_a, blend2_node_a, "Input0"));
|
||||||
|
|
||||||
|
// Connect nodes: Subgraph Blend2A, SamplerB, SamplerB
|
||||||
|
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"));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace TestSyncedAnimationGraph
|
} //namespace TestSyncedAnimationGraph
|
||||||
Loading…
x
Reference in New Issue
Block a user