WIP: making BlendTree Editor usable.
This commit is contained in:
parent
6330e34ea5
commit
0554691e46
@ -132,12 +132,16 @@ void BLTAnimationGraph::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
}
|
||||
}
|
||||
|
||||
void BLTAnimationGraph::_tree_changed() {
|
||||
void BLTAnimationGraph::_graph_changed(const StringName &node_name) {
|
||||
print_line(vformat("Graph changed %x", (uintptr_t)this));
|
||||
|
||||
if (properties_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
callable_mp(this, &BLTAnimationGraph::_update_properties).call_deferred();
|
||||
callable_mp(this, &BLTAnimationGraph::_setup_graph).call_deferred();
|
||||
|
||||
properties_dirty = true;
|
||||
}
|
||||
|
||||
@ -239,6 +243,8 @@ void BLTAnimationGraph::set_animation_player(const NodePath &p_path) {
|
||||
}
|
||||
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
||||
|
||||
print_line(vformat("Setting animation player of graph %x to %x", (uintptr_t)(this), (uintptr_t)graph_context.animation_player));
|
||||
|
||||
_setup_evaluation_context();
|
||||
_setup_graph();
|
||||
|
||||
@ -251,14 +257,15 @@ NodePath BLTAnimationGraph::get_animation_player() const {
|
||||
|
||||
void BLTAnimationGraph::set_root_animation_node(const Ref<BLTAnimationNode> &p_animation_node) {
|
||||
if (root_animation_node.is_valid()) {
|
||||
root_animation_node->disconnect(SNAME("tree_changed"), callable_mp(this, &BLTAnimationGraph::_tree_changed));
|
||||
root_animation_node->disconnect(SNAME("node_changed"), callable_mp(this, &BLTAnimationGraph::_graph_changed));
|
||||
}
|
||||
|
||||
root_animation_node = p_animation_node;
|
||||
|
||||
if (root_animation_node.is_valid()) {
|
||||
_setup_graph();
|
||||
root_animation_node->connect(SNAME("tree_changed"), callable_mp(this, &BLTAnimationGraph::_tree_changed));
|
||||
root_animation_node->connect(SNAME("node_changed"), callable_mp(this, &BLTAnimationGraph::_graph_changed));
|
||||
print_line(vformat("connected node_changed event to graph %x", (uintptr_t)this));
|
||||
}
|
||||
|
||||
properties_dirty = true;
|
||||
@ -295,7 +302,7 @@ void BLTAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (graph_context.skeleton_3d == nullptr) {
|
||||
if (graph_context.skeleton_3d == nullptr || graph_context.animation_player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -358,6 +365,7 @@ void BLTAnimationGraph::_set_process(bool p_process, bool p_force) {
|
||||
}
|
||||
|
||||
void BLTAnimationGraph::_setup_evaluation_context() {
|
||||
print_line("_setup_evaluation_context()");
|
||||
_cleanup_evaluation_context();
|
||||
|
||||
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
||||
@ -374,6 +382,8 @@ void BLTAnimationGraph::_setup_graph() {
|
||||
return;
|
||||
}
|
||||
|
||||
print_line(vformat("_setup_graph() on graph %x and root node %x", (uintptr_t)(void *)(this), (uintptr_t)(root_animation_node.ptr())));
|
||||
|
||||
root_animation_node->initialize(graph_context);
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ private:
|
||||
void _update_properties() const;
|
||||
void _update_properties_for_node(const String &p_base_path, Ref<BLTAnimationNode> p_node) const;
|
||||
|
||||
void _tree_changed();
|
||||
void _graph_changed(const StringName &node_name);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
@ -9,10 +9,11 @@ void BLTAnimationNode::_bind_methods() {
|
||||
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("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(SNAME("node_changed"), PropertyInfo(Variant::STRING_NAME, "node_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);
|
||||
@ -36,8 +37,8 @@ Variant BLTAnimationNode::get_parameter(const StringName &p_name) const {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void BLTAnimationNode::_tree_changed() {
|
||||
emit_signal(SNAME("tree_changed"));
|
||||
void BLTAnimationNode::_node_changed() {
|
||||
emit_signal(SNAME("node_changed"), get_name());
|
||||
}
|
||||
|
||||
void BLTAnimationNode::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||
@ -59,7 +60,6 @@ void BLTAnimationNodeBlendTree::_bind_methods() {
|
||||
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_ERROR_GRAPH_ALREADY_INITIALIZED);
|
||||
BIND_CONSTANT(CONNECTION_ERROR_NO_SOURCE_NODE);
|
||||
@ -273,7 +273,12 @@ bool BLTAnimationNodeSampler::initialize(GraphEvaluationContext &context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
animation = context.animation_player->get_animation(animation_name);
|
||||
animation_player = context.animation_player;
|
||||
if (animation_name.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
animation = animation_player->get_animation(animation_name);
|
||||
if (!animation.is_valid()) {
|
||||
print_error(vformat("Cannot initialize node %s: animation '%s' not found in animation player.", get_name(), animation_name));
|
||||
return false;
|
||||
@ -333,6 +338,11 @@ void BLTAnimationNodeSampler::evaluate(GraphEvaluationContext &context, const Lo
|
||||
output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position);
|
||||
}
|
||||
|
||||
void BLTAnimationNodeSampler::set_animation_player(AnimationPlayer *p_player) {
|
||||
animation_player = p_player;
|
||||
_node_changed();
|
||||
}
|
||||
|
||||
void BLTAnimationNodeSampler::set_animation(const StringName &p_name) {
|
||||
animation_name = p_name;
|
||||
}
|
||||
@ -341,11 +351,42 @@ StringName BLTAnimationNodeSampler::get_animation() const {
|
||||
return animation_name;
|
||||
}
|
||||
|
||||
TypedArray<StringName> BLTAnimationNodeSampler::get_animations_as_typed_array() const {
|
||||
TypedArray<StringName> typed_arr;
|
||||
|
||||
if (animation_player == nullptr) {
|
||||
print_error(vformat("BLTAnimationNodeSampler '%s' not yet initialized", get_name()));
|
||||
return typed_arr;
|
||||
}
|
||||
|
||||
Vector<StringName> vec;
|
||||
|
||||
List<StringName> animation_libraries;
|
||||
animation_player->get_animation_library_list(&animation_libraries);
|
||||
|
||||
for (const StringName &library_name : animation_libraries) {
|
||||
Ref<AnimationLibrary> library = animation_player->get_animation_library(library_name);
|
||||
List<StringName> animation_list;
|
||||
library->get_animation_list(&animation_list);
|
||||
for (const StringName &library_animation : animation_list) {
|
||||
vec.push_back(library_animation);
|
||||
}
|
||||
}
|
||||
|
||||
typed_arr.resize(vec.size());
|
||||
for (uint32_t i = 0; i < vec.size(); i++) {
|
||||
typed_arr[i] = vec[i];
|
||||
}
|
||||
return typed_arr;
|
||||
}
|
||||
|
||||
void BLTAnimationNodeSampler::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_animation", "name"), &BLTAnimationNodeSampler::set_animation);
|
||||
ClassDB::bind_method(D_METHOD("get_animation"), &BLTAnimationNodeSampler::get_animation);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_animations"), &BLTAnimationNodeSampler::get_animations_as_typed_array);
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlend2::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
||||
@ -476,10 +517,10 @@ void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimati
|
||||
node_connection_info.push_back(connection_info);
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_node(const Ref<BLTAnimationNode> &node) {
|
||||
bool BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_node(const Ref<BLTAnimationNode> &node) {
|
||||
if (node == get_output_node()) {
|
||||
// Output node not allowed to be removed
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
int removed_node_index = find_node_index(node);
|
||||
@ -515,6 +556,8 @@ void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_node(const Ref<BLTAnim
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
||||
@ -665,6 +708,8 @@ BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTr
|
||||
uint32_t connection_index = find_connection_index(source_node, target_node, target_port_name);
|
||||
assert(connection_index >= 0);
|
||||
connections.remove_at(connection_index);
|
||||
} else {
|
||||
return CONNECTION_ERROR_CONNECTION_NOT_FOUND;
|
||||
}
|
||||
|
||||
return CONNECTION_OK;
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/resources/animation_library.h"
|
||||
#include "sync_track.h"
|
||||
|
||||
#include <cassert>
|
||||
@ -245,7 +246,7 @@ protected:
|
||||
virtual void set_parameter(const StringName &p_name, const Variant &p_value);
|
||||
virtual Variant get_parameter(const StringName &p_name) const;
|
||||
|
||||
virtual void _tree_changed();
|
||||
virtual void _node_changed();
|
||||
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
|
||||
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
|
||||
|
||||
@ -273,6 +274,11 @@ public:
|
||||
virtual void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) {
|
||||
// By default, all inputs nodes are activated.
|
||||
for (const Ref<BLTAnimationNode> &node : input_nodes) {
|
||||
if (node.ptr() == nullptr) {
|
||||
// TODO: add checking whether tree can be evaluated, i.e. whether all inputs are properly connected.
|
||||
continue;
|
||||
}
|
||||
|
||||
node->active = true;
|
||||
node->node_time_info.is_synced = node_time_info.is_synced;
|
||||
}
|
||||
@ -339,10 +345,14 @@ class BLTAnimationNodeSampler : public BLTAnimationNode {
|
||||
|
||||
public:
|
||||
StringName animation_name;
|
||||
AnimationPlayer *animation_player = nullptr;
|
||||
|
||||
void set_animation_player(AnimationPlayer *p_player);
|
||||
void set_animation(const StringName &p_name);
|
||||
StringName get_animation() const;
|
||||
|
||||
TypedArray<StringName> get_animations_as_typed_array() const;
|
||||
|
||||
private:
|
||||
Ref<Animation> animation;
|
||||
|
||||
@ -459,6 +469,7 @@ public:
|
||||
CONNECTION_ERROR_TARGET_PORT_NOT_FOUND,
|
||||
CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED,
|
||||
CONNECTION_ERROR_CONNECTION_CREATES_LOOP,
|
||||
CONNECTION_ERROR_CONNECTION_NOT_FOUND
|
||||
};
|
||||
|
||||
/**
|
||||
@ -526,7 +537,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);
|
||||
bool 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);
|
||||
@ -537,6 +548,7 @@ public:
|
||||
private:
|
||||
BLTBlendTreeGraph tree_graph;
|
||||
bool tree_initialized = false;
|
||||
GraphEvaluationContext *_graph_evaluation_context = nullptr;
|
||||
|
||||
void sort_nodes() {
|
||||
_node_runtime_data.clear();
|
||||
@ -564,7 +576,11 @@ private:
|
||||
|
||||
for (int port_index = 0; port_index < node->get_input_count(); port_index++) {
|
||||
const int connected_node_index = tree_graph.node_connection_info[i].connected_child_node_index_at_port[port_index];
|
||||
node_runtime_data.input_nodes.push_back(tree_graph.nodes[connected_node_index]);
|
||||
if (connected_node_index == -1) {
|
||||
node_runtime_data.input_nodes.push_back(nullptr);
|
||||
} else {
|
||||
node_runtime_data.input_nodes.push_back(tree_graph.nodes[connected_node_index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -587,26 +603,22 @@ public:
|
||||
return tree_graph.find_node_index(node);
|
||||
}
|
||||
|
||||
int find_node_index_by_name(const StringName &name) const {
|
||||
return tree_graph.find_node_index_by_name(name);
|
||||
int find_node_index_by_name(const StringName &p_name) const {
|
||||
return tree_graph.find_node_index_by_name(p_name);
|
||||
}
|
||||
|
||||
void add_node(const Ref<BLTAnimationNode> &node) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot add node to BlendTree: BlendTree already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
tree_graph.add_node(node);
|
||||
|
||||
if (_graph_evaluation_context != nullptr) {
|
||||
node->initialize(*_graph_evaluation_context);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_node(const Ref<BLTAnimationNode> &node) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot remove node from BlendTree: BlendTree already initialized.");
|
||||
return;
|
||||
if (tree_graph.remove_node(node)) {
|
||||
_node_changed();
|
||||
}
|
||||
|
||||
tree_graph.remove_node(node);
|
||||
}
|
||||
|
||||
TypedArray<StringName> get_node_names_as_typed_array() const {
|
||||
@ -646,30 +658,25 @@ public:
|
||||
}
|
||||
|
||||
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) {
|
||||
if (tree_initialized) {
|
||||
print_error("Cannot add connection to BlendTree: BlendTree already initialized.");
|
||||
return CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED;
|
||||
ConnectionError result = tree_graph.add_connection(source_node, target_node, target_port_name);
|
||||
if (result == CONNECTION_OK) {
|
||||
_node_changed();
|
||||
}
|
||||
|
||||
return tree_graph.add_connection(source_node, target_node, target_port_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
ConnectionError result = tree_graph.remove_connection(source_node, target_node, target_port_name);
|
||||
if (result == CONNECTION_OK) {
|
||||
_node_changed();
|
||||
}
|
||||
|
||||
return tree_graph.remove_connection(source_node, target_node, target_port_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
Array get_connections_as_array() const {
|
||||
@ -689,6 +696,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
_graph_evaluation_context = &context;
|
||||
|
||||
sort_nodes();
|
||||
setup_runtime_data();
|
||||
|
||||
@ -707,6 +716,11 @@ public:
|
||||
activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
||||
|
||||
// TODO: add checking whether tree can be evaluated, i.e. whether all inputs are properly connected.
|
||||
if (tree_graph.nodes.size() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
tree_graph.nodes[0]->active = true;
|
||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
@ -759,7 +773,7 @@ public:
|
||||
}
|
||||
|
||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) override {
|
||||
ZoneScopedN("SyncedBlendTree::evaluate");
|
||||
GodotProfileZone("SyncedBlendTree::evaluate");
|
||||
|
||||
for (uint32_t i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user