BlendTree nodes can now be removed.
This commit is contained in:
parent
1e7dd4ba45
commit
4c428a865a
@ -23,7 +23,7 @@ void BLTAnimationGraph::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tree_root", "animation_node"), &BLTAnimationGraph::set_root_animation_node);
|
||||
ClassDB::bind_method(D_METHOD("get_tree_root"), &BLTAnimationGraph::get_root_animation_node);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "SyncedAnimationNode"), "set_tree_root", "get_tree_root");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "BLTAnimationNode"), "set_tree_root", "get_tree_root");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &BLTAnimationGraph::set_skeleton);
|
||||
ClassDB::bind_method(D_METHOD("get_skeleton"), &BLTAnimationGraph::get_skeleton);
|
||||
|
||||
@ -50,12 +50,14 @@ void BLTAnimationNode::_animation_node_removed(const ObjectID &p_oid, const Stri
|
||||
|
||||
void BLTAnimationNodeBlendTree::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_node", "animation_node"), &BLTAnimationNodeBlendTree::add_node);
|
||||
ClassDB::bind_method(D_METHOD("remove_node", "animation_node"), &BLTAnimationNodeBlendTree::remove_node);
|
||||
ClassDB::bind_method(D_METHOD("get_node", "node_name"), &BLTAnimationNodeBlendTree::get_node);
|
||||
ClassDB::bind_method(D_METHOD("get_output_node"), &BLTAnimationNodeBlendTree::get_output_node);
|
||||
ClassDB::bind_method(D_METHOD("get_node_names"), &BLTAnimationNodeBlendTree::get_node_names_as_typed_array);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_connection_valid", "source_node", "target_node", "target_port_name"), &BLTAnimationNodeBlendTree::is_connection_valid);
|
||||
ClassDB::bind_method(D_METHOD("add_connection", "source_node", "target_node", "target_port_name"), &BLTAnimationNodeBlendTree::add_connection);
|
||||
ClassDB::bind_method(D_METHOD("remove_connection", "source_node", "target_node", "target_port_name"), &BLTAnimationNodeBlendTree::remove_connection);
|
||||
ClassDB::bind_method(D_METHOD("get_connections"), &BLTAnimationNodeBlendTree::get_connections_as_array);
|
||||
|
||||
BIND_CONSTANT(CONNECTION_OK);
|
||||
@ -435,7 +437,7 @@ Ref<BLTAnimationNode> BLTAnimationNodeBlendTree::BLTBlendTreeGraph::get_output_n
|
||||
}
|
||||
|
||||
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index(const Ref<BLTAnimationNode> &node) const {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
for (uint32_t i = 0; i < nodes.size(); i++) {
|
||||
if (nodes[i] == node) {
|
||||
return i;
|
||||
}
|
||||
@ -445,7 +447,7 @@ int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index(const Ref<BLTA
|
||||
}
|
||||
|
||||
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const StringName &name) const {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
for (uint32_t i = 0; i < nodes.size(); i++) {
|
||||
if (nodes[i]->get_name() == name) {
|
||||
return i;
|
||||
}
|
||||
@ -474,6 +476,42 @@ void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimati
|
||||
node_connection_info.push_back(connection_info);
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_node(const Ref<BLTAnimationNode> &node) {
|
||||
int removed_node_index = find_node_index(node);
|
||||
assert(removed_node_index >= 0);
|
||||
|
||||
// Remove all connections to and from this node
|
||||
for (uint32_t i = 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() {
|
||||
LocalVector<int> sorted_node_indices = get_sorted_node_indices();
|
||||
|
||||
|
||||
@ -510,7 +510,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
||||
LocalVector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
||||
LocalVector<NodeConnectionInfo> node_connection_info;
|
||||
LocalVector<BLTBlendTreeConnection> connections;
|
||||
|
||||
@ -526,6 +526,7 @@ public:
|
||||
void remove_subtree_and_update_subtrees_recursive(int node, const HashSet<int> &removed_subtree_indices);
|
||||
|
||||
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);
|
||||
@ -544,7 +545,7 @@ private:
|
||||
|
||||
void setup_runtime_data() {
|
||||
// Add nodes and allocate runtime data
|
||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
const Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||
|
||||
NodeRuntimeData node_runtime_data;
|
||||
@ -557,7 +558,7 @@ private:
|
||||
}
|
||||
|
||||
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||
|
||||
@ -599,6 +600,15 @@ public:
|
||||
tree_graph.add_node(node);
|
||||
}
|
||||
|
||||
void remove_node(const Ref<BLTAnimationNode> &node) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot remove node from BlendTree: BlendTree already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
tree_graph.remove_node(node);
|
||||
}
|
||||
|
||||
TypedArray<StringName> get_node_names_as_typed_array() const {
|
||||
Vector<StringName> vec;
|
||||
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||
@ -624,7 +634,7 @@ public:
|
||||
}
|
||||
|
||||
Ref<BLTAnimationNode> get_node_by_index(int node_index) const {
|
||||
if (node_index < 0 || node_index > tree_graph.nodes.size()) {
|
||||
if (node_index < 0 || node_index > static_cast<int>(tree_graph.nodes.size())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -673,7 +683,7 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
// overrides from SyncedAnimationNode
|
||||
// overrides from BLTAnimationNode
|
||||
bool initialize(GraphEvaluationContext &context) override {
|
||||
if (!BLTAnimationNode::initialize(context)) {
|
||||
return false;
|
||||
@ -698,7 +708,7 @@ public:
|
||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
||||
|
||||
tree_graph.nodes[0]->active = true;
|
||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
@ -712,7 +722,7 @@ public:
|
||||
|
||||
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||
GodotProfileZone("SyncedBlendTree::calculate_sync_track");
|
||||
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
for (uint32_t i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
@ -731,7 +741,7 @@ public:
|
||||
tree_graph.nodes[0]->node_time_info.delta = p_delta;
|
||||
tree_graph.nodes[0]->node_time_info.position += p_delta;
|
||||
|
||||
for (int i = 1; i < tree_graph.nodes.size(); i++) {
|
||||
for (uint32_t i = 1; i < tree_graph.nodes.size(); i++) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
@ -751,7 +761,7 @@ public:
|
||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
||||
ZoneScopedN("SyncedBlendTree::evaluate");
|
||||
|
||||
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
for (uint32_t i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
if (!node->active) {
|
||||
|
||||
@ -505,6 +505,7 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
||||
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;
|
||||
@ -524,6 +525,40 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestSyncedAnimationGraph
|
||||
Loading…
x
Reference in New Issue
Block a user