feature/blend_tree_editor #1
@ -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, "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("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);
|
||||||
|
|||||||
@ -50,12 +50,14 @@ 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_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("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("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);
|
ClassDB::bind_method(D_METHOD("get_connections"), &BLTAnimationNodeBlendTree::get_connections_as_array);
|
||||||
|
|
||||||
BIND_CONSTANT(CONNECTION_OK);
|
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 {
|
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) {
|
if (nodes[i] == node) {
|
||||||
return i;
|
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 {
|
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) {
|
if (nodes[i]->get_name() == name) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -474,6 +476,42 @@ void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimati
|
|||||||
node_connection_info.push_back(connection_info);
|
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() {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
||||||
LocalVector<int> sorted_node_indices = get_sorted_node_indices();
|
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<NodeConnectionInfo> node_connection_info;
|
||||||
LocalVector<BLTBlendTreeConnection> connections;
|
LocalVector<BLTBlendTreeConnection> connections;
|
||||||
|
|
||||||
@ -526,6 +526,7 @@ public:
|
|||||||
void remove_subtree_and_update_subtrees_recursive(int node, const HashSet<int> &removed_subtree_indices);
|
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);
|
||||||
|
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 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);
|
||||||
@ -544,7 +545,7 @@ private:
|
|||||||
|
|
||||||
void setup_runtime_data() {
|
void setup_runtime_data() {
|
||||||
// Add nodes and allocate 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];
|
const Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||||
|
|
||||||
NodeRuntimeData node_runtime_data;
|
NodeRuntimeData node_runtime_data;
|
||||||
@ -557,7 +558,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 (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];
|
Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||||
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||||
|
|
||||||
@ -599,6 +600,15 @@ 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 {
|
TypedArray<StringName> get_node_names_as_typed_array() const {
|
||||||
Vector<StringName> vec;
|
Vector<StringName> vec;
|
||||||
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||||
@ -624,7 +634,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ref<BLTAnimationNode> get_node_by_index(int node_index) const {
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -673,7 +683,7 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrides from SyncedAnimationNode
|
// overrides from BLTAnimationNode
|
||||||
bool initialize(GraphEvaluationContext &context) override {
|
bool initialize(GraphEvaluationContext &context) override {
|
||||||
if (!BLTAnimationNode::initialize(context)) {
|
if (!BLTAnimationNode::initialize(context)) {
|
||||||
return false;
|
return false;
|
||||||
@ -698,7 +708,7 @@ public:
|
|||||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
||||||
|
|
||||||
tree_graph.nodes[0]->active = true;
|
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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
@ -712,7 +722,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 (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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
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.delta = p_delta;
|
||||||
tree_graph.nodes[0]->node_time_info.position += 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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
@ -751,7 +761,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 (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];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
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_b, blend2_node_b, "Input0"));
|
||||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_graph.add_connection(animation_sampler_node_c, blend2_node_b, "Input1"));
|
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_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_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;
|
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";
|
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);
|
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
|
} //namespace TestSyncedAnimationGraph
|
||||||
Loading…
x
Reference in New Issue
Block a user