Compare commits
No commits in common. "feature/blend_tree_editor" and "main" have entirely different histories.
feature/bl
...
main
@ -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, "BLTAnimationNode"), "set_tree_root", "get_tree_root");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "SyncedAnimationNode"), "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);
|
||||||
@ -51,7 +51,7 @@ void BLTAnimationGraph::_update_properties_for_node(const String &p_base_path, R
|
|||||||
p_node->get_child_nodes(&children);
|
p_node->get_child_nodes(&children);
|
||||||
|
|
||||||
for (const Ref<BLTAnimationNode> &child_node : children) {
|
for (const Ref<BLTAnimationNode> &child_node : children) {
|
||||||
_update_properties_for_node(p_base_path + child_node->get_name() + "/", child_node);
|
_update_properties_for_node(p_base_path + child_node->name + "/", child_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,24 +132,20 @@ void BLTAnimationGraph::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::_graph_changed(const StringName &node_name) {
|
void BLTAnimationGraph::_tree_changed() {
|
||||||
print_line(vformat("Graph changed %x", (uintptr_t)this));
|
|
||||||
|
|
||||||
if (properties_dirty) {
|
if (properties_dirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callable_mp(this, &BLTAnimationGraph::_update_properties).call_deferred();
|
callable_mp(this, &BLTAnimationGraph::_update_properties).call_deferred();
|
||||||
callable_mp(this, &BLTAnimationGraph::_setup_graph).call_deferred();
|
|
||||||
|
|
||||||
properties_dirty = true;
|
properties_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::_notification(int p_what) {
|
void BLTAnimationGraph::_notification(int p_what) {
|
||||||
GodotProfileZone("BLTAnimationGraph::_notification");
|
GodotProfileZone("SyncedAnimationGraph::_notification");
|
||||||
|
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_ENTER_TREE: {
|
case Node::NOTIFICATION_READY: {
|
||||||
_setup_evaluation_context();
|
_setup_evaluation_context();
|
||||||
_setup_graph();
|
_setup_graph();
|
||||||
|
|
||||||
@ -234,9 +230,14 @@ AnimationMixer::AnimationCallbackModeDiscrete BLTAnimationGraph::get_callback_mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::set_animation_player(const NodePath &p_path) {
|
void BLTAnimationGraph::set_animation_player(const NodePath &p_path) {
|
||||||
print_line(vformat("set_animation_player(%s) ", p_path));
|
|
||||||
|
|
||||||
animation_player_path = p_path;
|
animation_player_path = p_path;
|
||||||
|
if (p_path.is_empty()) {
|
||||||
|
// set_root_node(SceneStringName(path_pp));
|
||||||
|
// while (animation_libraries.size()) {
|
||||||
|
// remove_animation_library(animation_libraries[0].name);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
||||||
|
|
||||||
_setup_evaluation_context();
|
_setup_evaluation_context();
|
||||||
_setup_graph();
|
_setup_graph();
|
||||||
@ -249,17 +250,15 @@ NodePath BLTAnimationGraph::get_animation_player() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::set_root_animation_node(const Ref<BLTAnimationNode> &p_animation_node) {
|
void BLTAnimationGraph::set_root_animation_node(const Ref<BLTAnimationNode> &p_animation_node) {
|
||||||
print_line(vformat("setting root node to node %s", p_animation_node->get_name()));
|
|
||||||
|
|
||||||
if (root_animation_node.is_valid()) {
|
if (root_animation_node.is_valid()) {
|
||||||
root_animation_node->disconnect(SNAME("node_changed"), callable_mp(this, &BLTAnimationGraph::_graph_changed));
|
root_animation_node->disconnect(SNAME("tree_changed"), callable_mp(this, &BLTAnimationGraph::_tree_changed));
|
||||||
}
|
}
|
||||||
|
|
||||||
root_animation_node = p_animation_node;
|
root_animation_node = p_animation_node;
|
||||||
|
|
||||||
if (root_animation_node.is_valid()) {
|
if (root_animation_node.is_valid()) {
|
||||||
_setup_graph();
|
_setup_graph();
|
||||||
root_animation_node->connect(SNAME("node_changed"), callable_mp(this, &BLTAnimationGraph::_graph_changed));
|
root_animation_node->connect(SNAME("tree_changed"), callable_mp(this, &BLTAnimationGraph::_tree_changed));
|
||||||
}
|
}
|
||||||
|
|
||||||
properties_dirty = true;
|
properties_dirty = true;
|
||||||
@ -272,9 +271,14 @@ Ref<BLTAnimationNode> BLTAnimationGraph::get_root_animation_node() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::set_skeleton(const NodePath &p_path) {
|
void BLTAnimationGraph::set_skeleton(const NodePath &p_path) {
|
||||||
print_line(vformat("set_skeleton(%s) ", p_path));
|
|
||||||
|
|
||||||
skeleton_path = p_path;
|
skeleton_path = p_path;
|
||||||
|
if (p_path.is_empty()) {
|
||||||
|
// set_root_node(SceneStringName(path_pp));
|
||||||
|
// while (animation_libraries.size()) {
|
||||||
|
// remove_animation_library(animation_libraries[0].name);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
graph_context.skeleton_3d = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
||||||
|
|
||||||
_setup_evaluation_context();
|
_setup_evaluation_context();
|
||||||
_setup_graph();
|
_setup_graph();
|
||||||
@ -287,15 +291,11 @@ NodePath BLTAnimationGraph::get_skeleton() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
void BLTAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
||||||
if (!root_animation_node.is_valid() || is_graph_initialization_valid == false) {
|
if (!root_animation_node.is_valid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (graph_context.skeleton_3d == nullptr || graph_context.animation_player == nullptr) {
|
GodotProfileZone("SyncedAnimationGraph::_process_graph");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GodotProfileZone("BLTAnimationGraph::_process_graph");
|
|
||||||
|
|
||||||
_update_properties();
|
_update_properties();
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ void BLTAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::_apply_animation_data(const AnimationData &output_data) const {
|
void BLTAnimationGraph::_apply_animation_data(const AnimationData &output_data) const {
|
||||||
GodotProfileZone("BLTAnimationGraph::_apply_animation_data");
|
GodotProfileZone("SyncedAnimationGraph::_apply_animation_data");
|
||||||
|
|
||||||
for (const KeyValue<Animation::TypeHash, size_t> &K : output_data.value_buffer_offset) {
|
for (const KeyValue<Animation::TypeHash, size_t> &K : output_data.value_buffer_offset) {
|
||||||
const AnimationData::TrackValue *track_value = output_data.get_value<AnimationData::TrackValue>(K.key);
|
const AnimationData::TrackValue *track_value = output_data.get_value<AnimationData::TrackValue>(K.key);
|
||||||
@ -353,20 +353,10 @@ void BLTAnimationGraph::_set_process(bool p_process, bool p_force) {
|
|||||||
processing = p_process;
|
processing = p_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationGraph::_setup_animation_player() {
|
|
||||||
if (!is_inside_tree()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
|
||||||
print_line(vformat("AnimationPlayer of graph %x is now %x", (uintptr_t)(this), (uintptr_t)graph_context.animation_player));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BLTAnimationGraph::_setup_evaluation_context() {
|
void BLTAnimationGraph::_setup_evaluation_context() {
|
||||||
print_line("_setup_evaluation_context()");
|
|
||||||
_cleanup_evaluation_context();
|
_cleanup_evaluation_context();
|
||||||
|
|
||||||
_setup_animation_player();
|
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
||||||
graph_context.skeleton_3d = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
graph_context.skeleton_3d = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,9 +370,7 @@ void BLTAnimationGraph::_setup_graph() {
|
|||||||
return;
|
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);
|
||||||
is_graph_initialization_valid = root_animation_node->initialize(graph_context);
|
|
||||||
print_line(vformat("is_graph_initialization_valid = %s", is_graph_initialization_valid ? "true" : "false"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BLTAnimationGraph::BLTAnimationGraph() {
|
BLTAnimationGraph::BLTAnimationGraph() {
|
||||||
|
|||||||
@ -19,12 +19,11 @@ private:
|
|||||||
mutable AHashMap<StringName, Pair<Ref<BLTAnimationNode>, StringName>> parameter_to_node_parameter_map;
|
mutable AHashMap<StringName, Pair<Ref<BLTAnimationNode>, StringName>> parameter_to_node_parameter_map;
|
||||||
|
|
||||||
mutable bool properties_dirty = true;
|
mutable bool properties_dirty = true;
|
||||||
bool is_graph_initialization_valid = false;
|
|
||||||
|
|
||||||
void _update_properties() const;
|
void _update_properties() const;
|
||||||
void _update_properties_for_node(const String &p_base_path, Ref<BLTAnimationNode> p_node) const;
|
void _update_properties_for_node(const String &p_base_path, Ref<BLTAnimationNode> p_node) const;
|
||||||
|
|
||||||
void _graph_changed(const StringName &node_name);
|
void _tree_changed();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
@ -76,7 +75,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void _set_process(bool p_process, bool p_force = false);
|
void _set_process(bool p_process, bool p_force = false);
|
||||||
|
|
||||||
void _setup_animation_player();
|
|
||||||
void _setup_evaluation_context();
|
void _setup_evaluation_context();
|
||||||
void _cleanup_evaluation_context();
|
void _cleanup_evaluation_context();
|
||||||
|
|
||||||
|
|||||||
@ -5,18 +5,9 @@
|
|||||||
#include "blendalot_animation_node.h"
|
#include "blendalot_animation_node.h"
|
||||||
|
|
||||||
void BLTAnimationNode::_bind_methods() {
|
void BLTAnimationNode::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_position", "position"), &BLTAnimationNode::set_position);
|
ADD_SIGNAL(MethodInfo("tree_changed"));
|
||||||
ClassDB::bind_method(D_METHOD("get_position"), &BLTAnimationNode::get_position);
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_position", "get_position");
|
|
||||||
|
|
||||||
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_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("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", "node"), &BLTAnimationNode::get_input_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {
|
void BLTAnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||||
@ -37,8 +28,8 @@ Variant BLTAnimationNode::get_parameter(const StringName &p_name) const {
|
|||||||
return Variant();
|
return Variant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNode::_node_changed() {
|
void BLTAnimationNode::_tree_changed() {
|
||||||
emit_signal(SNAME("node_changed"), get_name());
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNode::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
void BLTAnimationNode::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
|
||||||
@ -49,10 +40,119 @@ void BLTAnimationNode::_animation_node_removed(const ObjectID &p_oid, const Stri
|
|||||||
emit_signal(SNAME("animation_node_removed"), p_oid, p_node);
|
emit_signal(SNAME("animation_node_removed"), p_oid, p_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||||
|
String prop_name = node->name;
|
||||||
|
if (prop_name != "Output") {
|
||||||
|
p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
}
|
||||||
|
p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BLTAnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_value) const {
|
||||||
|
String prop_name = p_name;
|
||||||
|
if (prop_name.begins_with("nodes/")) {
|
||||||
|
String node_name = prop_name.get_slicec('/', 1);
|
||||||
|
String what = prop_name.get_slicec('/', 2);
|
||||||
|
int node_index = find_node_index_by_name(node_name);
|
||||||
|
|
||||||
|
if (what == "node") {
|
||||||
|
if (node_index != -1) {
|
||||||
|
r_value = tree_graph.nodes[node_index];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (what == "position") {
|
||||||
|
if (node_index != -1) {
|
||||||
|
r_value = tree_graph.nodes[node_index]->position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (prop_name == "node_connections") {
|
||||||
|
Array conns;
|
||||||
|
conns.resize(tree_graph.connections.size() * 3);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
||||||
|
conns[idx * 3 + 0] = connection.target_node->name;
|
||||||
|
conns[idx * 3 + 1] = connection.target_node->get_input_index(connection.target_port_name);
|
||||||
|
conns[idx * 3 + 2] = connection.source_node->name;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
r_value = conns;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BLTAnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
|
String prop_name = p_name;
|
||||||
|
if (prop_name.begins_with("nodes/")) {
|
||||||
|
String node_name = prop_name.get_slicec('/', 1);
|
||||||
|
String what = prop_name.get_slicec('/', 2);
|
||||||
|
|
||||||
|
if (what == "node") {
|
||||||
|
Ref<BLTAnimationNode> anode = p_value;
|
||||||
|
if (anode.is_valid()) {
|
||||||
|
anode->name = node_name;
|
||||||
|
add_node(anode);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (what == "position") {
|
||||||
|
int node_index = find_node_index_by_name(node_name);
|
||||||
|
if (node_index > -1) {
|
||||||
|
tree_graph.nodes[node_index]->position = p_value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (prop_name == "node_connections") {
|
||||||
|
Array conns = p_value;
|
||||||
|
ERR_FAIL_COND_V(conns.size() % 3 != 0, false);
|
||||||
|
|
||||||
|
for (int i = 0; i < conns.size(); i += 3) {
|
||||||
|
int target_node_index = find_node_index_by_name(conns[i]);
|
||||||
|
int target_node_port_index = conns[i + 1];
|
||||||
|
int source_node_index = find_node_index_by_name(conns[i + 2]);
|
||||||
|
|
||||||
|
Ref<BLTAnimationNode> target_node = tree_graph.nodes[target_node_index];
|
||||||
|
Vector<StringName> target_input_names;
|
||||||
|
target_node->get_input_names(target_input_names);
|
||||||
|
|
||||||
|
add_connection(tree_graph.nodes[source_node_index], target_node, target_input_names[target_node_port_index]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationData::sample_from_animation(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d, double p_time) {
|
void AnimationData::sample_from_animation(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d, double p_time) {
|
||||||
GodotProfileZone("AnimationData::sample_from_animation");
|
GodotProfileZone("AnimationData::sample_from_animation");
|
||||||
|
|
||||||
const LocalVector<Animation::Track *> &tracks = animation->get_tracks();
|
const LocalVector<Animation::Track *> tracks = animation->get_tracks();
|
||||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||||
|
|
||||||
int count = tracks.size();
|
int count = tracks.size();
|
||||||
@ -136,7 +236,7 @@ void AnimationData::allocate_track_value(const Animation::Track *animation_track
|
|||||||
void AnimationData::allocate_track_values(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d) {
|
void AnimationData::allocate_track_values(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d) {
|
||||||
GodotProfileZone("AnimationData::allocate_track_values");
|
GodotProfileZone("AnimationData::allocate_track_values");
|
||||||
|
|
||||||
const LocalVector<Animation::Track *> &tracks = animation->get_tracks();
|
const LocalVector<Animation::Track *> tracks = animation->get_tracks();
|
||||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||||
|
|
||||||
int count = tracks.size();
|
int count = tracks.size();
|
||||||
@ -154,30 +254,35 @@ void AnimationDataAllocator::register_track_values(const Ref<Animation> &animati
|
|||||||
default_data.allocate_track_values(animation, skeleton_3d);
|
default_data.allocate_track_values(animation, skeleton_3d);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// BLTAnimationNodeSampler
|
|
||||||
//
|
|
||||||
bool BLTAnimationNodeSampler::initialize(GraphEvaluationContext &context) {
|
bool BLTAnimationNodeSampler::initialize(GraphEvaluationContext &context) {
|
||||||
if (!BLTAnimationNode::initialize(context)) {
|
BLTAnimationNode::initialize(context);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
animation_player = context.animation_player;
|
animation = context.animation_player->get_animation(animation_name);
|
||||||
|
if (!animation.is_valid()) {
|
||||||
if (animation_player == nullptr) {
|
print_error(vformat("Cannot initialize node %s: animation '%s' not found in animation player.", name, animation_name));
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animation_name.is_empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!set_animation(animation_name)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.animation_data_allocator.register_track_values(animation, context.skeleton_3d);
|
context.animation_data_allocator.register_track_values(animation, context.skeleton_3d);
|
||||||
|
|
||||||
|
node_time_info.loop_mode = animation->get_loop_mode();
|
||||||
|
|
||||||
|
// Initialize Sync Track from marker
|
||||||
|
LocalVector<float> sync_markers;
|
||||||
|
int marker_index = 0;
|
||||||
|
StringName marker_name = itos(marker_index);
|
||||||
|
while (animation->has_marker(marker_name)) {
|
||||||
|
sync_markers.push_back(animation->get_marker_time(marker_name));
|
||||||
|
marker_index++;
|
||||||
|
marker_name = itos(marker_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync_markers.size() > 0) {
|
||||||
|
node_time_info.sync_track = SyncTrack::create_from_markers(animation->get_length(), sync_markers);
|
||||||
|
} else {
|
||||||
|
node_time_info.sync_track = SyncTrack::create_from_markers(animation->get_length(), { 0 });
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,134 +317,21 @@ void BLTAnimationNodeSampler::evaluate(GraphEvaluationContext &context, const Lo
|
|||||||
output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position);
|
output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNodeSampler::set_animation_player(AnimationPlayer *p_player) {
|
void BLTAnimationNodeSampler::set_animation(const StringName &p_name) {
|
||||||
animation_player = p_player;
|
|
||||||
_node_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BLTAnimationNodeSampler::set_animation(const StringName &p_name) {
|
|
||||||
bool has_animation_name_changed = p_name != animation_name;
|
|
||||||
animation_name = p_name;
|
animation_name = p_name;
|
||||||
|
|
||||||
if (animation_player == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!animation_player->has_animation(p_name)) {
|
|
||||||
if (has_animation_name_changed) {
|
|
||||||
_node_changed();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
animation = animation_player->get_animation(p_name);
|
|
||||||
if (!animation.is_valid()) {
|
|
||||||
print_error(vformat("Cannot initialize node %s: animation '%s' not found in animation player.", get_name(), animation_name));
|
|
||||||
|
|
||||||
_node_changed();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_time_info.loop_mode = animation->get_loop_mode();
|
|
||||||
|
|
||||||
// Initialize Sync Track from marker
|
|
||||||
LocalVector<float> sync_markers;
|
|
||||||
int marker_index = 0;
|
|
||||||
StringName marker_name = itos(marker_index);
|
|
||||||
while (animation->has_marker(marker_name)) {
|
|
||||||
sync_markers.push_back(animation->get_marker_time(marker_name));
|
|
||||||
marker_index++;
|
|
||||||
marker_name = itos(marker_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sync_markers.size() > 0) {
|
|
||||||
node_time_info.sync_track = SyncTrack::create_from_markers(animation->get_length(), sync_markers);
|
|
||||||
} else {
|
|
||||||
node_time_info.sync_track = SyncTrack::create_from_markers(animation->get_length(), { 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_animation_name_changed) {
|
|
||||||
_node_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringName BLTAnimationNodeSampler::get_animation() const {
|
StringName BLTAnimationNodeSampler::get_animation() const {
|
||||||
return animation_name;
|
return animation_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationPlayer *BLTAnimationNodeSampler::get_animation_player() const {
|
|
||||||
return animation_player;
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
void BLTAnimationNodeSampler::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_animation", "name"), &BLTAnimationNodeSampler::set_animation);
|
ClassDB::bind_method(D_METHOD("set_animation", "name"), &BLTAnimationNodeSampler::set_animation);
|
||||||
ClassDB::bind_method(D_METHOD("get_animation"), &BLTAnimationNodeSampler::get_animation);
|
ClassDB::bind_method(D_METHOD("get_animation"), &BLTAnimationNodeSampler::get_animation);
|
||||||
ClassDB::bind_method(D_METHOD("get_animation_player"), &BLTAnimationNodeSampler::get_animation_player);
|
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// BLTAnimationNodeTimeScale
|
|
||||||
//
|
|
||||||
void BLTAnimationNodeTimeScale::_get_property_list(List<PropertyInfo> *p_list) const {
|
|
||||||
p_list->push_back(PropertyInfo(Variant::FLOAT, scale_name, PROPERTY_HINT_RANGE, "-10,10,0.01,or_less,or_greater"));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BLTAnimationNodeTimeScale::_get(const StringName &p_name, Variant &r_value) const {
|
|
||||||
if (p_name == scale_name) {
|
|
||||||
r_value = scale;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BLTAnimationNodeTimeScale::_set(const StringName &p_name, const Variant &p_value) {
|
|
||||||
if (p_name == scale_name) {
|
|
||||||
scale = p_value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// BLTAnimationNodeBlend2
|
|
||||||
//
|
|
||||||
void BLTAnimationNodeBlend2::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
void BLTAnimationNodeBlend2::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
||||||
GodotProfileZone("AnimationBlend2Node::evaluate");
|
GodotProfileZone("AnimationBlend2Node::evaluate");
|
||||||
|
|
||||||
@ -386,6 +378,7 @@ Variant BLTAnimationNodeBlend2::get_parameter_default_value(const StringName &p_
|
|||||||
|
|
||||||
void BLTAnimationNodeBlend2::_get_property_list(List<PropertyInfo> *p_list) const {
|
void BLTAnimationNodeBlend2::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
p_list->push_back(PropertyInfo(Variant::FLOAT, blend_weight_pname, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
|
p_list->push_back(PropertyInfo(Variant::FLOAT, blend_weight_pname, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater"));
|
||||||
|
p_list->push_back(PropertyInfo(Variant::BOOL, sync_pname));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BLTAnimationNodeBlend2::_get(const StringName &p_name, Variant &r_value) const {
|
bool BLTAnimationNodeBlend2::_get(const StringName &p_name, Variant &r_value) const {
|
||||||
@ -394,6 +387,11 @@ bool BLTAnimationNodeBlend2::_get(const StringName &p_name, Variant &r_value) co
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p_name == sync_pname) {
|
||||||
|
r_value = sync;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,16 +401,18 @@ bool BLTAnimationNodeBlend2::_set(const StringName &p_name, const Variant &p_val
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p_name == sync_pname) {
|
||||||
|
sync = p_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// BLTAnimationNodeBlendTree
|
|
||||||
//
|
|
||||||
BLTAnimationNodeBlendTree::BLTBlendTreeGraph::BLTBlendTreeGraph() {
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph::BLTBlendTreeGraph() {
|
||||||
Ref<BLTAnimationNodeOutput> output_node;
|
Ref<BLTAnimationNodeOutput> output_node;
|
||||||
output_node.instantiate();
|
output_node.instantiate();
|
||||||
output_node->set_name("Output");
|
output_node->name = "Output";
|
||||||
add_node(output_node);
|
add_node(output_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,7 +421,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 (uint32_t i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
if (nodes[i] == node) {
|
if (nodes[i] == node) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -431,8 +431,8 @@ 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 (uint32_t i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
if (nodes[i]->get_name() == name) {
|
if (nodes[i]->name == name) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -441,83 +441,32 @@ int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimationNode> &node) {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimationNode> &node) {
|
||||||
StringName node_base_name = node->get_name();
|
StringName node_base_name = node->name;
|
||||||
if (node_base_name.is_empty()) {
|
if (node_base_name.is_empty()) {
|
||||||
node_base_name = node->get_class_name();
|
node_base_name = node->get_class_name();
|
||||||
}
|
}
|
||||||
node->set_name(node_base_name);
|
node->name = node_base_name;
|
||||||
|
|
||||||
int number_suffix = 1;
|
int number_suffix = 1;
|
||||||
while (find_node_index_by_name(node->get_name()) != -1) {
|
while (find_node_index_by_name(node->name) != -1) {
|
||||||
node->set_name(vformat("%s %d", node_base_name, number_suffix));
|
node->name = vformat("%s %d", node_base_name, number_suffix);
|
||||||
number_suffix++;
|
number_suffix++;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_node(const Ref<BLTAnimationNode> &node) {
|
|
||||||
if (node == get_output_node()) {
|
|
||||||
// Output node not allowed to be removed
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int removed_node_index = find_node_index(node);
|
|
||||||
assert(removed_node_index >= 0);
|
|
||||||
|
|
||||||
// Remove all connections to and from this node
|
|
||||||
for (int i = static_cast<int>(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection_info.parent_node_index > removed_node_index) {
|
|
||||||
connection_info.parent_node_index--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
LocalVector<Ref<BLTAnimationNode>> sorted_nodes;
|
Vector<Ref<BLTAnimationNode>> sorted_nodes;
|
||||||
LocalVector<NodeConnectionInfo> old_node_connection_info(node_connection_info);
|
LocalVector<NodeConnectionInfo> old_node_connection_info = node_connection_info;
|
||||||
for (unsigned int i = 0; i < sorted_node_indices.size(); i++) {
|
for (unsigned int i = 0; i < sorted_node_indices.size(); i++) {
|
||||||
int node_index = sorted_node_indices[i];
|
int node_index = sorted_node_indices[i];
|
||||||
sorted_nodes.push_back(nodes[node_index]);
|
sorted_nodes.push_back(nodes[node_index]);
|
||||||
node_connection_info[i] = old_node_connection_info[node_index];
|
node_connection_info[i] = old_node_connection_info[node_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes = sorted_nodes;
|
nodes = sorted_nodes;
|
||||||
|
|
||||||
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
||||||
@ -534,20 +483,6 @@ LocalVector<int> BLTAnimationNodeBlendTree::BLTBlendTreeGraph::get_sorted_node_i
|
|||||||
sort_nodes_recursive(0, result);
|
sort_nodes_recursive(0, result);
|
||||||
result.reverse();
|
result.reverse();
|
||||||
|
|
||||||
HashSet<int> connected_node_indices;
|
|
||||||
for (int node_index : result) {
|
|
||||||
connected_node_indices.insert(node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that nodes that are not reachable from the root node are still added to
|
|
||||||
// the sorted nodes indices.
|
|
||||||
for (Ref<BLTAnimationNode> &node : nodes) {
|
|
||||||
int node_index = find_node_index(node);
|
|
||||||
if (!connected_node_indices.has(node_index)) {
|
|
||||||
result.push_back(node_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,69 +495,18 @@ 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_index, int node_parent_index) {
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node, int node_parent) {
|
||||||
if (node_parent_index == -1) {
|
if (node_parent == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node_connection_info[node_parent_index].input_subtree_node_indices.insert(node_index);
|
node_connection_info[node_parent].input_subtree_node_indices.insert(node);
|
||||||
|
|
||||||
for (int index : node_connection_info[node_index].input_subtree_node_indices) {
|
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
||||||
node_connection_info[node_parent_index].input_subtree_node_indices.insert(index);
|
node_connection_info[node_parent].input_subtree_node_indices.insert(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_index_and_update_subtrees_recursive(node_parent_index, node_connection_info[node_parent_index].parent_node_index);
|
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].parent_node_index);
|
||||||
}
|
|
||||||
|
|
||||||
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::remove_subtree_and_update_subtrees_recursive(int node_index, const HashSet<int> &removed_subtree_indices) {
|
|
||||||
NodeConnectionInfo &connection_info = node_connection_info[node_index];
|
|
||||||
|
|
||||||
for (int subtree_node_index : removed_subtree_indices) {
|
|
||||||
connection_info.input_subtree_node_indices.erase(subtree_node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection_info.parent_node_index != -1) {
|
|
||||||
remove_subtree_and_update_subtrees_recursive(connection_info.parent_node_index, removed_subtree_indices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||||
@ -644,154 +528,42 @@ BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTr
|
|||||||
return CONNECTION_OK;
|
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 {
|
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, 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);
|
int source_node_index = find_node_index(source_node);
|
||||||
NodeConnectionInfo &connection_info = node_connection_info[source_node_index];
|
if (source_node_index == -1) {
|
||||||
|
print_error("Cannot connect nodes: source node not found.");
|
||||||
|
return CONNECTION_ERROR_NO_SOURCE_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
if (connection_info.parent_node_index != -1) {
|
if (node_connection_info[source_node_index].parent_node_index != -1) {
|
||||||
NodeConnectionInfo &parent_connection_info = node_connection_info[connection_info.parent_node_index];
|
print_error("Cannot connect node: source node already has a parent.");
|
||||||
parent_connection_info.input_subtree_node_indices.erase(source_node_index);
|
return CONNECTION_ERROR_PARENT_EXISTS;
|
||||||
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);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
connection_info.parent_node_index = -1;
|
Vector<StringName> target_inputs;
|
||||||
|
target_node->get_input_names(target_inputs);
|
||||||
|
|
||||||
uint32_t connection_index = find_connection_index(source_node, target_node, target_port_name);
|
if (!target_inputs.has(target_port_name)) {
|
||||||
assert(connection_index >= 0);
|
print_error("Cannot connect nodes: target port not found.");
|
||||||
connections.remove_at(connection_index);
|
return CONNECTION_ERROR_TARGET_PORT_NOT_FOUND;
|
||||||
} else {
|
}
|
||||||
return CONNECTION_ERROR_CONNECTION_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;
|
return CONNECTION_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_graph_offset", "graph_offset"), &BLTAnimationNodeBlendTree::set_graph_offset);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_graph_offset"), &BLTAnimationNodeBlendTree::get_graph_offset);
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset");
|
|
||||||
|
|
||||||
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 {
|
|
||||||
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
|
||||||
String prop_name = node->get_name();
|
|
||||||
if (prop_name != "Output") {
|
|
||||||
p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
|
|
||||||
}
|
|
||||||
p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + prop_name + "/graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
|
||||||
}
|
|
||||||
|
|
||||||
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BLTAnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_value) const {
|
|
||||||
String prop_name = p_name;
|
|
||||||
if (prop_name.begins_with("nodes/")) {
|
|
||||||
String node_name = prop_name.get_slicec('/', 1);
|
|
||||||
String what = prop_name.get_slicec('/', 2);
|
|
||||||
int node_index = find_node_index_by_name(node_name);
|
|
||||||
|
|
||||||
if (what == "node") {
|
|
||||||
if (node_index != -1) {
|
|
||||||
r_value = tree_graph.nodes[node_index];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (what == "graph_offset") {
|
|
||||||
if (node_index != -1) {
|
|
||||||
r_value = tree_graph.nodes[node_index]->position;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (prop_name == "node_connections") {
|
|
||||||
Array conns;
|
|
||||||
conns.resize(tree_graph.connections.size() * 3);
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
|
||||||
conns[idx * 3 + 0] = connection.target_node->get_name();
|
|
||||||
conns[idx * 3 + 1] = connection.target_node->get_input_index(connection.target_port_name);
|
|
||||||
conns[idx * 3 + 2] = connection.source_node->get_name();
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
r_value = conns;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BLTAnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) {
|
|
||||||
String prop_name = p_name;
|
|
||||||
if (prop_name.begins_with("nodes/")) {
|
|
||||||
String node_name = prop_name.get_slicec('/', 1);
|
|
||||||
String what = prop_name.get_slicec('/', 2);
|
|
||||||
|
|
||||||
if (what == "node") {
|
|
||||||
Ref<BLTAnimationNode> anode = p_value;
|
|
||||||
if (anode.is_valid()) {
|
|
||||||
anode->set_name(node_name);
|
|
||||||
add_node(anode);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (what == "graph_offset") {
|
|
||||||
int node_index = find_node_index_by_name(node_name);
|
|
||||||
if (node_index > -1) {
|
|
||||||
tree_graph.nodes[node_index]->position = p_value;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (prop_name == "node_connections") {
|
|
||||||
Array conns = p_value;
|
|
||||||
ERR_FAIL_COND_V(conns.size() % 3 != 0, false);
|
|
||||||
|
|
||||||
for (int i = 0; i < conns.size(); i += 3) {
|
|
||||||
int target_node_index = find_node_index_by_name(conns[i]);
|
|
||||||
int target_node_port_index = conns[i + 1];
|
|
||||||
int source_node_index = find_node_index_by_name(conns[i + 2]);
|
|
||||||
|
|
||||||
Ref<BLTAnimationNode> target_node = tree_graph.nodes[target_node_index];
|
|
||||||
Vector<StringName> target_input_names = target_node->get_input_names();
|
|
||||||
|
|
||||||
add_connection(tree_graph.nodes[source_node_index], target_node, target_input_names[target_node_port_index]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "scene/3d/skeleton_3d.h"
|
#include "scene/3d/skeleton_3d.h"
|
||||||
#include "scene/animation/animation_player.h"
|
#include "scene/animation/animation_player.h"
|
||||||
#include "scene/resources/animation_library.h"
|
|
||||||
#include "sync_track.h"
|
#include "sync_track.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@ -105,8 +104,7 @@ struct AnimationData {
|
|||||||
buffer = other.buffer;
|
buffer = other.buffer;
|
||||||
}
|
}
|
||||||
AnimationData(AnimationData &&other) noexcept :
|
AnimationData(AnimationData &&other) noexcept :
|
||||||
// We skip copying the offset as that should be identical for all nodes within a BLTAnimationGraph.
|
value_buffer_offset(std::exchange(other.value_buffer_offset, AHashMap<Animation::TypeHash, size_t, HashHasher>())),
|
||||||
// value_buffer_offset(std::exchange(other.value_buffer_offset, AHashMap<Animation::TypeHash, size_t, HashHasher>())),
|
|
||||||
buffer(std::exchange(other.buffer, LocalVector<uint8_t>())) {
|
buffer(std::exchange(other.buffer, LocalVector<uint8_t>())) {
|
||||||
}
|
}
|
||||||
AnimationData &operator=(const AnimationData &other) {
|
AnimationData &operator=(const AnimationData &other) {
|
||||||
@ -247,7 +245,7 @@ protected:
|
|||||||
virtual void set_parameter(const StringName &p_name, const Variant &p_value);
|
virtual void set_parameter(const StringName &p_name, const Variant &p_value);
|
||||||
virtual Variant get_parameter(const StringName &p_name) const;
|
virtual Variant get_parameter(const StringName &p_name) const;
|
||||||
|
|
||||||
virtual void _node_changed();
|
virtual void _tree_changed();
|
||||||
virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
|
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);
|
virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
|
||||||
|
|
||||||
@ -264,6 +262,7 @@ public:
|
|||||||
NodeTimeInfo node_time_info;
|
NodeTimeInfo node_time_info;
|
||||||
bool active = false;
|
bool active = false;
|
||||||
|
|
||||||
|
StringName name;
|
||||||
Vector2 position;
|
Vector2 position;
|
||||||
|
|
||||||
virtual ~BLTAnimationNode() override = default;
|
virtual ~BLTAnimationNode() override = default;
|
||||||
@ -275,11 +274,6 @@ public:
|
|||||||
virtual void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) {
|
virtual void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) {
|
||||||
// By default, all inputs nodes are activated.
|
// By default, all inputs nodes are activated.
|
||||||
for (const Ref<BLTAnimationNode> &node : input_nodes) {
|
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->active = true;
|
||||||
node->node_time_info.is_synced = node_time_info.is_synced;
|
node->node_time_info.is_synced = node_time_info.is_synced;
|
||||||
}
|
}
|
||||||
@ -291,7 +285,6 @@ public:
|
|||||||
node_time_info.loop_mode = input_nodes[0]->node_time_info.loop_mode;
|
node_time_info.loop_mode = input_nodes[0]->node_time_info.loop_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update_time(double p_time) {
|
virtual void update_time(double p_time) {
|
||||||
if (node_time_info.is_synced) {
|
if (node_time_info.is_synced) {
|
||||||
node_time_info.sync_position = p_time;
|
node_time_info.sync_position = p_time;
|
||||||
@ -300,41 +293,23 @@ public:
|
|||||||
node_time_info.position += p_time;
|
node_time_info.position += p_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) {
|
virtual void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &input_datas, AnimationData &output_data) {
|
||||||
GodotProfileZone("AnimationNode::evaluate");
|
|
||||||
// By default, use the AnimationData of the first input.
|
// By default, use the AnimationData of the first input.
|
||||||
if (input_datas.size() > 0) {
|
if (input_datas.size() > 0) {
|
||||||
output_data = std::move(*input_datas[0]);
|
output_data = *input_datas[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_position(const Vector2 &p_position) {
|
virtual void get_input_names(Vector<StringName> &inputs) const {}
|
||||||
position = p_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 get_position() const {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Vector<StringName> get_input_names() const { return {}; }
|
|
||||||
|
|
||||||
TypedArray<StringName> get_input_names_as_typed_array() const {
|
|
||||||
TypedArray<StringName> typed_arr;
|
|
||||||
Vector<StringName> vec = get_input_names();
|
|
||||||
typed_arr.resize(vec.size());
|
|
||||||
for (uint32_t i = 0; i < vec.size(); i++) {
|
|
||||||
typed_arr[i] = vec[i];
|
|
||||||
}
|
|
||||||
return typed_arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_input_index(const StringName &port_name) const {
|
int get_input_index(const StringName &port_name) const {
|
||||||
Vector<StringName> inputs = get_input_names();
|
Vector<StringName> inputs;
|
||||||
|
get_input_names(inputs);
|
||||||
return inputs.find(port_name);
|
return inputs.find(port_name);
|
||||||
}
|
}
|
||||||
int get_input_count() const {
|
int get_input_count() const {
|
||||||
Vector<StringName> inputs = get_input_names();
|
Vector<StringName> inputs;
|
||||||
|
get_input_names(inputs);
|
||||||
return inputs.size();
|
return inputs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,14 +322,9 @@ class BLTAnimationNodeSampler : public BLTAnimationNode {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
StringName animation_name;
|
StringName animation_name;
|
||||||
AnimationPlayer *animation_player = nullptr;
|
|
||||||
|
|
||||||
void set_animation_player(AnimationPlayer *p_player);
|
void set_animation(const StringName &p_name);
|
||||||
bool set_animation(const StringName &p_name);
|
|
||||||
StringName get_animation() const;
|
StringName get_animation() const;
|
||||||
AnimationPlayer *get_animation_player() const;
|
|
||||||
|
|
||||||
TypedArray<StringName> get_animations_as_typed_array() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ref<Animation> animation;
|
Ref<Animation> animation;
|
||||||
@ -367,53 +337,12 @@ protected:
|
|||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
};
|
};
|
||||||
|
|
||||||
class BLTAnimationNodeTimeScale : public BLTAnimationNode {
|
|
||||||
GDCLASS(BLTAnimationNodeTimeScale, BLTAnimationNode);
|
|
||||||
|
|
||||||
public:
|
|
||||||
float scale = 1.0f;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ref<Animation> animation;
|
|
||||||
|
|
||||||
Vector<StringName> get_input_names() const override {
|
|
||||||
return { "Input" };
|
|
||||||
}
|
|
||||||
|
|
||||||
bool initialize(GraphEvaluationContext &context) override {
|
|
||||||
node_time_info = {};
|
|
||||||
// TODO: it should not be necessary to force looping here. node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
|
||||||
if (node_time_info.is_synced) {
|
|
||||||
node_time_info.sync_track = input_nodes[0]->node_time_info.sync_track;
|
|
||||||
node_time_info.sync_track.duration *= scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void update_time(double p_time) override {
|
|
||||||
if (node_time_info.is_synced) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLTAnimationNode::update_time(p_time * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
|
||||||
bool _get(const StringName &p_name, Variant &r_value) const;
|
|
||||||
bool _set(const StringName &p_name, const Variant &p_value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
StringName scale_name = PNAME("scale");
|
|
||||||
};
|
|
||||||
|
|
||||||
class BLTAnimationNodeOutput : public BLTAnimationNode {
|
class BLTAnimationNodeOutput : public BLTAnimationNode {
|
||||||
GDCLASS(BLTAnimationNodeOutput, BLTAnimationNode);
|
GDCLASS(BLTAnimationNodeOutput, BLTAnimationNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vector<StringName> get_input_names() const override {
|
void get_input_names(Vector<StringName> &inputs) const override {
|
||||||
return { "Output" };
|
inputs.push_back("Input");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -424,26 +353,20 @@ public:
|
|||||||
float blend_weight = 0.0f;
|
float blend_weight = 0.0f;
|
||||||
bool sync = true;
|
bool sync = true;
|
||||||
|
|
||||||
Vector<StringName> get_input_names() const override {
|
void get_input_names(Vector<StringName> &inputs) const override {
|
||||||
return { "Input0", "Input1" };
|
inputs.push_back("Input0");
|
||||||
|
inputs.push_back("Input1");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initialize(GraphEvaluationContext &context) override {
|
bool initialize(GraphEvaluationContext &context) override {
|
||||||
if (!BLTAnimationNode::initialize(context)) {
|
bool result = BLTAnimationNode::initialize(context);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
// TODO: do we always want looping in this case or do we traverse the graph to check what's reasonable?
|
// TODO: do we always want looping in this case or do we traverse the graph to check what's reasonable?
|
||||||
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node_time_info.loop_mode != Animation::LOOP_LINEAR) {
|
return result;
|
||||||
print_line(vformat("Forcing loop mode to linear on nonde %s", get_name()));
|
|
||||||
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
input_nodes[0]->active = true;
|
input_nodes[0]->active = true;
|
||||||
@ -456,8 +379,7 @@ public:
|
|||||||
|
|
||||||
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
if (node_time_info.is_synced || sync) {
|
if (node_time_info.is_synced || sync) {
|
||||||
// TODO: figure out whether we need to enforce looping mode when syncing is enabled.
|
assert(input_nodes[0]->node_time_info.loop_mode == input_nodes[1]->node_time_info.loop_mode);
|
||||||
// assert(input_nodes[0]->node_time_info.loop_mode == input_nodes[1]->node_time_info.loop_mode);
|
|
||||||
node_time_info.sync_track = SyncTrack::blend(blend_weight, input_nodes[0]->node_time_info.sync_track, input_nodes[1]->node_time_info.sync_track);
|
node_time_info.sync_track = SyncTrack::blend(blend_weight, input_nodes[0]->node_time_info.sync_track, input_nodes[1]->node_time_info.sync_track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,13 +393,13 @@ public:
|
|||||||
if (!Math::is_zero_approx(node_time_info.sync_track.duration)) {
|
if (!Math::is_zero_approx(node_time_info.sync_track.duration)) {
|
||||||
node_time_info.position = Math::fposmod(static_cast<float>(node_time_info.position), node_time_info.sync_track.duration);
|
node_time_info.position = Math::fposmod(static_cast<float>(node_time_info.position), node_time_info.sync_track.duration);
|
||||||
node_time_info.sync_position = node_time_info.sync_track.calc_sync_from_abs_time(node_time_info.position);
|
node_time_info.sync_position = node_time_info.sync_track.calc_sync_from_abs_time(node_time_info.position);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
assert(false && !"Loop mode ping-pong not yet supported");
|
assert(false && !"Loop mode ping-pong not yet supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
void evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) override;
|
||||||
|
|
||||||
void set_use_sync(bool p_sync);
|
void set_use_sync(bool p_sync);
|
||||||
@ -501,9 +423,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BLTBlendTreeConnection {
|
struct BLTBlendTreeConnection {
|
||||||
Ref<BLTAnimationNode> source_node = nullptr;
|
const Ref<BLTAnimationNode> source_node = nullptr;
|
||||||
Ref<BLTAnimationNode> target_node = nullptr;
|
const Ref<BLTAnimationNode> target_node = nullptr;
|
||||||
StringName target_port_name = "";
|
const StringName target_port_name = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
||||||
@ -519,7 +441,6 @@ public:
|
|||||||
CONNECTION_ERROR_TARGET_PORT_NOT_FOUND,
|
CONNECTION_ERROR_TARGET_PORT_NOT_FOUND,
|
||||||
CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED,
|
CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED,
|
||||||
CONNECTION_ERROR_CONNECTION_CREATES_LOOP,
|
CONNECTION_ERROR_CONNECTION_CREATES_LOOP,
|
||||||
CONNECTION_ERROR_CONNECTION_NOT_FOUND
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -556,49 +477,32 @@ 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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalVector<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<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_index, int node_parent_index);
|
void add_index_and_update_subtrees_recursive(int node, int node_parent);
|
||||||
void remove_subtree_and_update_subtrees_recursive(int node, const HashSet<int> &removed_subtree_indices);
|
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const;
|
||||||
|
|
||||||
void add_node(const Ref<BLTAnimationNode> &node);
|
void add_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);
|
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:
|
||||||
BLTBlendTreeGraph tree_graph;
|
BLTBlendTreeGraph tree_graph;
|
||||||
bool tree_initialized = false;
|
bool tree_initialized = false;
|
||||||
GraphEvaluationContext *_graph_evaluation_context = nullptr;
|
|
||||||
|
|
||||||
void sort_nodes() {
|
void sort_nodes() {
|
||||||
_node_runtime_data.clear();
|
_node_runtime_data.clear();
|
||||||
@ -607,7 +511,7 @@ private:
|
|||||||
|
|
||||||
void setup_runtime_data() {
|
void setup_runtime_data() {
|
||||||
// Add nodes and allocate runtime data
|
// Add nodes and allocate runtime data
|
||||||
for (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
for (int 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;
|
||||||
@ -620,20 +524,16 @@ 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 (uint32_t i = 0; i < tree_graph.nodes.size(); i++) {
|
for (int 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];
|
||||||
|
|
||||||
for (int port_index = 0; port_index < node->get_input_count(); port_index++) {
|
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];
|
const int connected_node_index = tree_graph.node_connection_info[i].connected_child_node_index_at_port[port_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]);
|
node_runtime_data.input_nodes.push_back(tree_graph.nodes[connected_node_index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
@ -642,8 +542,6 @@ protected:
|
|||||||
bool _set(const StringName &p_name, const Variant &p_value);
|
bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vector2 graph_offset;
|
|
||||||
|
|
||||||
struct NodeRuntimeData {
|
struct NodeRuntimeData {
|
||||||
Vector<Ref<BLTAnimationNode>> input_nodes;
|
Vector<Ref<BLTAnimationNode>> input_nodes;
|
||||||
LocalVector<AnimationData *> input_data;
|
LocalVector<AnimationData *> input_data;
|
||||||
@ -651,144 +549,53 @@ public:
|
|||||||
};
|
};
|
||||||
LocalVector<NodeRuntimeData> _node_runtime_data;
|
LocalVector<NodeRuntimeData> _node_runtime_data;
|
||||||
|
|
||||||
void set_graph_offset(const Vector2 &p_graph_offset) {
|
Ref<BLTAnimationNode> get_output_node() const {
|
||||||
graph_offset = p_graph_offset;
|
return tree_graph.nodes[0];
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 get_graph_offset() const {
|
|
||||||
return graph_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_node_index(const Ref<BLTAnimationNode> &node) const {
|
int find_node_index(const Ref<BLTAnimationNode> &node) const {
|
||||||
return tree_graph.find_node_index(node);
|
return tree_graph.find_node_index(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_node_index_by_name(const StringName &p_name) const {
|
int find_node_index_by_name(const StringName &name) const {
|
||||||
return tree_graph.find_node_index_by_name(p_name);
|
return tree_graph.find_node_index_by_name(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<BLTAnimationNode> get_node(int node_index) {
|
||||||
|
if (node_index < 0 || node_index > tree_graph.nodes.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree_graph.nodes[node_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_node(const Ref<BLTAnimationNode> &node) {
|
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);
|
tree_graph.add_node(node);
|
||||||
|
|
||||||
if (_graph_evaluation_context != nullptr) {
|
|
||||||
node->initialize(*_graph_evaluation_context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_node(const Ref<BLTAnimationNode> &node) {
|
|
||||||
if (tree_graph.remove_node(node)) {
|
|
||||||
_node_changed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypedArray<StringName> get_node_names_as_typed_array() const {
|
|
||||||
Vector<StringName> vec;
|
|
||||||
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
|
||||||
vec.push_back(node->get_name());
|
|
||||||
}
|
|
||||||
|
|
||||||
TypedArray<StringName> typed_arr;
|
|
||||||
typed_arr.resize(vec.size());
|
|
||||||
for (uint32_t i = 0; i < vec.size(); i++) {
|
|
||||||
typed_arr[i] = vec[i];
|
|
||||||
}
|
|
||||||
return typed_arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<BLTAnimationNode> get_node(const StringName &node_name) const {
|
|
||||||
int node_index = tree_graph.find_node_index_by_name(node_name);
|
|
||||||
|
|
||||||
if (node_index >= 0) {
|
|
||||||
return tree_graph.nodes[node_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<BLTAnimationNode> get_node_by_index(int node_index) const {
|
|
||||||
if (node_index < 0 || node_index > static_cast<int>(tree_graph.nodes.size())) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree_graph.nodes[node_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<BLTAnimationNode> get_output_node() const {
|
|
||||||
return tree_graph.nodes[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
|
||||||
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) {
|
ConnectionError add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||||
ConnectionError result = tree_graph.add_connection(source_node, target_node, target_port_name);
|
if (tree_initialized) {
|
||||||
if (result == CONNECTION_OK) {
|
print_error("Cannot add connection to BlendTree: BlendTree already initialized.");
|
||||||
_node_changed();
|
return CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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) {
|
// overrides from SyncedAnimationNode
|
||||||
ConnectionError result = tree_graph.remove_connection(source_node, target_node, target_port_name);
|
|
||||||
if (result == CONNECTION_OK) {
|
|
||||||
_node_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array get_connections_as_array() const {
|
|
||||||
Array result;
|
|
||||||
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
|
||||||
result.push_back(connection.source_node);
|
|
||||||
result.push_back(connection.target_node);
|
|
||||||
result.push_back(connection.target_port_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _tree_node_changed(const StringName &node_name) {
|
|
||||||
_node_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// overrides from BLTAnimationNode
|
|
||||||
bool initialize(GraphEvaluationContext &context) override {
|
bool initialize(GraphEvaluationContext &context) override {
|
||||||
tree_initialized = false;
|
|
||||||
|
|
||||||
if (!BLTAnimationNode::initialize(context)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_graph_evaluation_context = &context;
|
|
||||||
|
|
||||||
sort_nodes();
|
sort_nodes();
|
||||||
setup_runtime_data();
|
setup_runtime_data();
|
||||||
|
|
||||||
const HashSet<int> &output_subtree = tree_graph.node_connection_info[0].input_subtree_node_indices;
|
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||||
|
|
||||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
|
||||||
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
|
||||||
|
|
||||||
// Initialize, but skip validation of nodes that are not part of the active tree.
|
|
||||||
if (!output_subtree.has(i)) {
|
|
||||||
node->initialize(context);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!node->initialize(context)) {
|
if (!node->initialize(context)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
|
||||||
|
|
||||||
for (const Ref<BLTAnimationNode> &input_node : node_runtime_data.input_nodes) {
|
|
||||||
if (!input_node.is_valid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_initialized = true;
|
tree_initialized = true;
|
||||||
@ -800,15 +607,8 @@ public:
|
|||||||
activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
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;
|
tree_graph.nodes[0]->active = true;
|
||||||
tree_graph.nodes[0]->node_time_info.is_synced = node_time_info.is_synced;
|
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) {
|
||||||
@ -822,7 +622,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 (uint32_t i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
for (int 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) {
|
||||||
@ -832,23 +632,16 @@ public:
|
|||||||
const NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
const NodeRuntimeData &node_runtime_data = _node_runtime_data[i];
|
||||||
|
|
||||||
node->calculate_sync_track(node_runtime_data.input_nodes);
|
node->calculate_sync_track(node_runtime_data.input_nodes);
|
||||||
|
|
||||||
if (i == 1) {
|
|
||||||
node_time_info = node->node_time_info;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_time(double p_delta) override {
|
void update_time(double p_delta) override {
|
||||||
GodotProfileZone("SyncedBlendTree::update_time");
|
GodotProfileZone("SyncedBlendTree::update_time");
|
||||||
|
|
||||||
BLTAnimationNode::update_time(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.delta = node_time_info.delta;
|
for (int i = 1; i < tree_graph.nodes.size(); i++) {
|
||||||
tree_graph.nodes[0]->node_time_info.position = node_time_info.position;
|
|
||||||
tree_graph.nodes[0]->node_time_info.sync_position = node_time_info.sync_position;
|
|
||||||
|
|
||||||
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) {
|
||||||
@ -866,9 +659,9 @@ 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 {
|
||||||
GodotProfileZone("SyncedBlendTree::evaluate");
|
ZoneScopedN("SyncedBlendTree::evaluate");
|
||||||
|
|
||||||
for (uint32_t i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
for (int 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) {
|
||||||
@ -896,9 +689,6 @@ public:
|
|||||||
for (const int child_index : tree_graph.node_connection_info[i].connected_child_node_index_at_port) {
|
for (const int child_index : tree_graph.node_connection_info[i].connected_child_node_index_at_port) {
|
||||||
context.animation_data_allocator.free(_node_runtime_data[child_index].output_data);
|
context.animation_data_allocator.free(_node_runtime_data[child_index].output_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node must be deactivated. It'll be activated when actually used next time.
|
|
||||||
node->active = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 20.02.26.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef MASTER_BLENDALOT_MATH_HELPER_H
|
|
||||||
#define MASTER_BLENDALOT_MATH_HELPER_H
|
|
||||||
|
|
||||||
inline int greatest_common_divisor(int a, int b) {
|
|
||||||
while (b != 0) {
|
|
||||||
int temp = b;
|
|
||||||
b = a % b;
|
|
||||||
a = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int least_common_multiple(int a, int b) {
|
|
||||||
return (a / greatest_common_divisor(a, b)) * b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //MASTER_BLENDALOT_MATH_HELPER_H
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
@tool
|
|
||||||
|
|
||||||
extends Control
|
|
||||||
class_name AnimationGraphEditor
|
|
||||||
|
|
||||||
@onready var breadcrumb_button_container: HBoxContainer = %BreadcrumbButtons
|
|
||||||
@onready var active_graph_control: Control = %ActiveGraphControl
|
|
||||||
|
|
||||||
var animation_graph:BLTAnimationGraph = null
|
|
||||||
var animation_graph_root_node:BLTAnimationNode = null
|
|
||||||
var graph_node_stack:Array[BLTAnimationNode] = []
|
|
||||||
var active_graph_edit:Control = null
|
|
||||||
var active_graph_edit_index = -1
|
|
||||||
|
|
||||||
|
|
||||||
func reset_graph_control():
|
|
||||||
for child in active_graph_control.get_children():
|
|
||||||
active_graph_control.remove_child(child)
|
|
||||||
child.queue_free()
|
|
||||||
|
|
||||||
|
|
||||||
func edit_animation_root_node(blt_node:BLTAnimationNode):
|
|
||||||
print("Setting root node")
|
|
||||||
graph_node_stack = []
|
|
||||||
active_graph_edit_index = -1
|
|
||||||
truncate_graph_stack(0)
|
|
||||||
|
|
||||||
blt_node.resource_name = "Root"
|
|
||||||
|
|
||||||
if blt_node is BLTAnimationNodeBlendTree:
|
|
||||||
animation_graph_root_node = blt_node
|
|
||||||
push_graph_stack(blt_node)
|
|
||||||
edit_graph(blt_node)
|
|
||||||
return
|
|
||||||
|
|
||||||
assert(is_instance_valid(animation_graph))
|
|
||||||
|
|
||||||
push_warning("Cannot edit node %s. Graph type %s not yet supported." % [blt_node.resource_name, blt_node.get_class()])
|
|
||||||
|
|
||||||
|
|
||||||
func push_graph_stack(blt_node:BLTAnimationNode):
|
|
||||||
graph_node_stack.append(blt_node)
|
|
||||||
active_graph_edit_index = graph_node_stack.size() - 1
|
|
||||||
|
|
||||||
var breadcrumb_button:Button = Button.new()
|
|
||||||
breadcrumb_button_container.add_child(breadcrumb_button)
|
|
||||||
breadcrumb_button.text = blt_node.resource_name
|
|
||||||
breadcrumb_button.toggle_mode = true
|
|
||||||
breadcrumb_button.set_meta("BLTAnimationNode", blt_node)
|
|
||||||
breadcrumb_button.set_meta("graph_edit_index", active_graph_edit_index)
|
|
||||||
breadcrumb_button.pressed.connect(on_breadcrumb_button_pressed.bind(active_graph_edit_index))
|
|
||||||
|
|
||||||
|
|
||||||
func truncate_graph_stack(level:int):
|
|
||||||
graph_node_stack.resize(level)
|
|
||||||
|
|
||||||
var is_above_stack_size = false
|
|
||||||
for child in breadcrumb_button_container.get_children():
|
|
||||||
if is_above_stack_size:
|
|
||||||
breadcrumb_button_container.remove_child(child)
|
|
||||||
child.queue_free()
|
|
||||||
continue
|
|
||||||
|
|
||||||
var button:Button = child as Button
|
|
||||||
if not is_instance_valid(button):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if button.get_meta("graph_edit_index") >= graph_node_stack.size():
|
|
||||||
is_above_stack_size = true
|
|
||||||
breadcrumb_button_container.remove_child(child)
|
|
||||||
child.queue_free()
|
|
||||||
|
|
||||||
|
|
||||||
func on_breadcrumb_button_pressed(graph_edit_index:int):
|
|
||||||
print("on_breadcrumb_button_pressed(%d)" % graph_edit_index)
|
|
||||||
active_graph_edit_index = graph_edit_index
|
|
||||||
update_breadcrumb_button_container()
|
|
||||||
|
|
||||||
edit_graph(graph_node_stack[graph_edit_index])
|
|
||||||
|
|
||||||
|
|
||||||
func update_breadcrumb_button_container():
|
|
||||||
for child in breadcrumb_button_container.get_children():
|
|
||||||
var button:Button = child as Button
|
|
||||||
if not is_instance_valid(button):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if button.get_meta("graph_edit_index") == active_graph_edit_index:
|
|
||||||
button.set_pressed_no_signal(true)
|
|
||||||
else:
|
|
||||||
button.set_pressed_no_signal(false)
|
|
||||||
|
|
||||||
|
|
||||||
func edit_graph(blt_node:BLTAnimationNode):
|
|
||||||
if blt_node is BLTAnimationNodeBlendTree:
|
|
||||||
reset_graph_control()
|
|
||||||
var blend_tree_graph_edit:BltBlendTreeEditor = preload ("res://addons/blendalot/blend_tree_editor.tscn").instantiate()
|
|
||||||
active_graph_control.add_child(blend_tree_graph_edit)
|
|
||||||
blend_tree_graph_edit.edit_blend_tree(blt_node)
|
|
||||||
blend_tree_graph_edit.edit_subgraph.connect(handle_subgraph_edit)
|
|
||||||
active_graph_edit = blend_tree_graph_edit
|
|
||||||
else:
|
|
||||||
push_error("Cannot edit graph of node type %s" % blt_node.get_class())
|
|
||||||
|
|
||||||
|
|
||||||
func handle_subgraph_edit(blt_node:BLTAnimationNode):
|
|
||||||
print("handling subgraph edit of node %s" % blt_node.resource_name)
|
|
||||||
truncate_graph_stack(active_graph_edit_index + 1)
|
|
||||||
push_graph_stack(blt_node)
|
|
||||||
edit_graph(blt_node)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://bxxipuj2s5gxu
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
[gd_scene format=3 uid="uid://bk5mssvanwjnh"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bxxipuj2s5gxu" path="res://addons/blendalot/animation_graph_editor.gd" id="1_un1ur"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://cptd46rpm0gl3" path="res://addons/blendalot/blend_tree_editor.tscn" id="2_utax0"]
|
|
||||||
|
|
||||||
[node name="AnimationGraphEditor" type="VBoxContainer" unique_id=768619585]
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
script = ExtResource("1_un1ur")
|
|
||||||
|
|
||||||
[node name="DebugContainer" type="HBoxContainer" parent="." unique_id=1984631897]
|
|
||||||
visible = false
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ResetGraphButton" type="Button" parent="DebugContainer" unique_id=670074781]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 0
|
|
||||||
text = "Reset"
|
|
||||||
|
|
||||||
[node name="FileNameLineEdit" type="LineEdit" parent="DebugContainer" unique_id=724929522]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
custom_minimum_size = Vector2(250, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "editor_test_tree.tres"
|
|
||||||
|
|
||||||
[node name="SaveButton" type="Button" parent="DebugContainer" unique_id=706843675]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 0
|
|
||||||
text = "Save
|
|
||||||
"
|
|
||||||
|
|
||||||
[node name="LoadButton" type="Button" parent="DebugContainer" unique_id=1467831200]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 0
|
|
||||||
text = "Load
|
|
||||||
"
|
|
||||||
|
|
||||||
[node name="ReinitializeButton" type="Button" parent="DebugContainer" unique_id=281924859]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 0
|
|
||||||
text = "Reinitialize"
|
|
||||||
|
|
||||||
[node name="TreeOptionButton" type="OptionButton" parent="DebugContainer" unique_id=2103827540]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
selected = 0
|
|
||||||
item_count = 2
|
|
||||||
popup/item_0/text = "AnimationSampler"
|
|
||||||
popup/item_0/id = 1
|
|
||||||
popup/item_1/text = "Blend2"
|
|
||||||
popup/item_1/id = 2
|
|
||||||
|
|
||||||
[node name="InstantiateTreeButton" type="Button" parent="DebugContainer" unique_id=735069321]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 0
|
|
||||||
text = "Instantiate"
|
|
||||||
|
|
||||||
[node name="NavigationBar" type="MarginContainer" parent="." unique_id=815609909]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="NavigationBar" unique_id=996737763]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="BreadcrumbButtons" type="HBoxContainer" parent="NavigationBar" unique_id=1375619232]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="PathLabel" type="Label" parent="NavigationBar/BreadcrumbButtons" unique_id=1544570774]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Path:"
|
|
||||||
|
|
||||||
[node name="ActiveGraphControl" type="Control" parent="." unique_id=1769528277]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 3
|
|
||||||
|
|
||||||
[node name="BlendTreeEditor" parent="ActiveGraphControl" unique_id=1313738200 instance=ExtResource("2_utax0")]
|
|
||||||
layout_mode = 1
|
|
||||||
@ -1,275 +0,0 @@
|
|||||||
@tool
|
|
||||||
|
|
||||||
extends Control
|
|
||||||
class_name BltBlendTreeEditor
|
|
||||||
|
|
||||||
@onready var blend_tree_graph_edit: GraphEdit = %BlendTreeGraphEdit
|
|
||||||
@onready var add_node_popup_menu: PopupMenu = %AddNodePopupMenu
|
|
||||||
|
|
||||||
signal edit_subgraph(blt_node:BLTAnimationNode)
|
|
||||||
signal graph_changed()
|
|
||||||
|
|
||||||
var blend_tree:BLTAnimationNodeBlendTree
|
|
||||||
|
|
||||||
var blend_tree_node_to_graph_node = {}
|
|
||||||
var graph_node_to_blend_tree_node = {}
|
|
||||||
|
|
||||||
var selected_nodes = {}
|
|
||||||
var last_selected_graph_node:GraphNode = null
|
|
||||||
var new_node_position:Vector2 = Vector2.ZERO
|
|
||||||
|
|
||||||
var registered_nodes = [
|
|
||||||
"BLTAnimationNodeSampler",
|
|
||||||
"BLTAnimationNodeBlend2",
|
|
||||||
"BLTAnimationNodeBlendTree",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
add_node_popup_menu.clear(true)
|
|
||||||
|
|
||||||
for node_name in registered_nodes:
|
|
||||||
add_node_popup_menu.add_item(node_name)
|
|
||||||
|
|
||||||
|
|
||||||
func _reset_editor():
|
|
||||||
for child in blend_tree_graph_edit.get_children():
|
|
||||||
if child.name == "_connection_layer":
|
|
||||||
continue
|
|
||||||
|
|
||||||
child.get_parent().remove_child(child)
|
|
||||||
child.queue_free()
|
|
||||||
|
|
||||||
blend_tree_graph_edit.clear_connections()
|
|
||||||
|
|
||||||
blend_tree = null
|
|
||||||
blend_tree_node_to_graph_node = {}
|
|
||||||
graph_node_to_blend_tree_node = {}
|
|
||||||
selected_nodes = {}
|
|
||||||
|
|
||||||
|
|
||||||
func edit_blend_tree(blt_blend_tree:BLTAnimationNodeBlendTree):
|
|
||||||
_reset_editor()
|
|
||||||
blend_tree = blt_blend_tree
|
|
||||||
blend_tree_graph_edit.scroll_offset = blend_tree.graph_offset
|
|
||||||
|
|
||||||
_update_editor_nodes_from_blend_tree()
|
|
||||||
_update_editor_connections_from_blend_tree()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_editor_nodes_from_blend_tree():
|
|
||||||
for node_name in blend_tree.get_node_names():
|
|
||||||
var blend_tree_node:BLTAnimationNode = blend_tree.get_node(node_name)
|
|
||||||
var graph_node:GraphNode = create_graph_node_for_blt_node(blend_tree_node)
|
|
||||||
blend_tree_graph_edit.add_child(graph_node)
|
|
||||||
|
|
||||||
blend_tree_node_to_graph_node[blend_tree_node] = graph_node
|
|
||||||
graph_node_to_blend_tree_node[graph_node] = blend_tree_node
|
|
||||||
|
|
||||||
|
|
||||||
func _update_editor_connections_from_blend_tree():
|
|
||||||
var connection_array = blend_tree.get_connections()
|
|
||||||
|
|
||||||
for i in range(len(connection_array) / 3):
|
|
||||||
var source_node:BLTAnimationNode = connection_array[i * 3]
|
|
||||||
var target_node:BLTAnimationNode = connection_array[i * 3 + 1]
|
|
||||||
var target_port = connection_array[i * 3 + 2]
|
|
||||||
|
|
||||||
var source_graph_node = blend_tree_node_to_graph_node[source_node]
|
|
||||||
|
|
||||||
var connect_result = blend_tree_graph_edit.connect_node(source_node.resource_name, 0, target_node.resource_name, target_node.get_input_index(target_port), true)
|
|
||||||
|
|
||||||
|
|
||||||
func create_graph_node_for_blt_node(blt_node: BLTAnimationNode) -> GraphNode:
|
|
||||||
var result_graph_node:GraphNode = GraphNode.new()
|
|
||||||
result_graph_node.name = blt_node.resource_name
|
|
||||||
result_graph_node.title = blt_node.resource_name
|
|
||||||
result_graph_node.position_offset = blt_node.position
|
|
||||||
|
|
||||||
var result_slot_offset = 0
|
|
||||||
|
|
||||||
if (blt_node.get_class() != "BLTAnimationNodeOutput"):
|
|
||||||
result_slot_offset = 1
|
|
||||||
var output_slot_label:Label = Label.new()
|
|
||||||
output_slot_label.text = "Result"
|
|
||||||
result_graph_node.add_child(output_slot_label)
|
|
||||||
result_graph_node.set_slot(0, false, 1, Color.WHITE, true, 1, Color.WHITE)
|
|
||||||
|
|
||||||
if blt_node.get_class() == "BLTAnimationNodeBlendTree":
|
|
||||||
result_graph_node.gui_input.connect(_on_node_gui_input.bind(result_graph_node))
|
|
||||||
|
|
||||||
var inputs = blt_node.get_input_names()
|
|
||||||
for i in range(len(inputs)):
|
|
||||||
var slot_label:Label = Label.new()
|
|
||||||
slot_label.text = inputs[i]
|
|
||||||
result_graph_node.add_child(slot_label)
|
|
||||||
result_graph_node.set_slot(i + result_slot_offset, true, 1, Color.WHITE, false, 1, Color.BLACK)
|
|
||||||
|
|
||||||
if blt_node.get_class() == "BLTAnimationNodeSampler":
|
|
||||||
var animation_sampler_node:BLTAnimationNodeSampler = blt_node as BLTAnimationNodeSampler
|
|
||||||
var animation_selector_button = OptionButton.new()
|
|
||||||
var animation_player:AnimationPlayer = animation_sampler_node.get_animation_player()
|
|
||||||
for animation_name in animation_player.get_animation_list():
|
|
||||||
animation_selector_button.add_item(animation_name)
|
|
||||||
if animation_name == animation_sampler_node.animation:
|
|
||||||
animation_selector_button.select(animation_selector_button.item_count - 1)
|
|
||||||
|
|
||||||
animation_selector_button.item_selected.connect(_on_animation_select.bind(animation_sampler_node, animation_selector_button))
|
|
||||||
|
|
||||||
result_graph_node.add_child(animation_selector_button)
|
|
||||||
|
|
||||||
blt_node.node_changed.connect(_trigger_graph_changed)
|
|
||||||
|
|
||||||
return result_graph_node
|
|
||||||
|
|
||||||
|
|
||||||
func _trigger_graph_changed(_node_name):
|
|
||||||
graph_changed.emit()
|
|
||||||
|
|
||||||
|
|
||||||
func _remove_node_connections(graph_node:GraphNode):
|
|
||||||
var node_connections:Array = []
|
|
||||||
|
|
||||||
for connection:Dictionary in blend_tree_graph_edit.connections:
|
|
||||||
if connection["from_node"] == graph_node.name or connection["to_node"] == graph_node.name:
|
|
||||||
node_connections.append(connection)
|
|
||||||
|
|
||||||
for node_connection:Dictionary in node_connections:
|
|
||||||
print("Removing connection %s" % str(node_connection))
|
|
||||||
blend_tree_graph_edit.disconnect_node(node_connection["from_node"], node_connection["from_port"], node_connection["to_node"], node_connection["to_port"])
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# GraphEdit signal handling
|
|
||||||
#
|
|
||||||
func _on_blend_tree_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
|
|
||||||
print("Trying to connect '%s' port %d to node '%s' port %d" % [from_node, from_port, to_node, to_port])
|
|
||||||
|
|
||||||
var source_node:BLTAnimationNode = blend_tree.get_node(from_node)
|
|
||||||
var target_node:BLTAnimationNode = blend_tree.get_node(to_node)
|
|
||||||
|
|
||||||
if target_node == null:
|
|
||||||
push_error("Invalid connection, target node %s not found." % to_node)
|
|
||||||
return
|
|
||||||
|
|
||||||
var target_node_port_name = target_node.get_input_names()[to_port]
|
|
||||||
|
|
||||||
var connection_result = blend_tree.is_connection_valid(source_node, target_node, target_node_port_name)
|
|
||||||
if connection_result != blend_tree.CONNECTION_OK:
|
|
||||||
push_error("Could not add connection (error %d)" % connection_result)
|
|
||||||
return
|
|
||||||
|
|
||||||
blend_tree.add_connection(source_node, target_node, target_node_port_name)
|
|
||||||
|
|
||||||
var connect_result = blend_tree_graph_edit.connect_node(from_node, from_port, to_node, to_port, true)
|
|
||||||
print("graph connect result: " + str(connect_result))
|
|
||||||
|
|
||||||
print("Success!")
|
|
||||||
|
|
||||||
|
|
||||||
func _on_blend_tree_graph_edit_disconnection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
|
|
||||||
var blend_tree_source_node = blend_tree.get_node(from_node)
|
|
||||||
var blend_tree_target_node = blend_tree.get_node(to_node)
|
|
||||||
var target_port_name = blend_tree_target_node.get_input_names()[to_port]
|
|
||||||
blend_tree.remove_connection(blend_tree_source_node, blend_tree_target_node, target_port_name)
|
|
||||||
|
|
||||||
blend_tree_graph_edit.disconnect_node(from_node, from_port, to_node, to_port)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_blend_tree_graph_edit_delete_nodes_request(nodes: Array[StringName]) -> void:
|
|
||||||
for node_name:StringName in nodes:
|
|
||||||
print("remove node '%s'" % node_name)
|
|
||||||
var blend_tree_node:BLTAnimationNode = blend_tree.get_node(node_name)
|
|
||||||
|
|
||||||
if blend_tree_node == null:
|
|
||||||
push_error("Cannot delete node '%s': node not found." % node_name)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if blend_tree_node == blend_tree.get_output_node():
|
|
||||||
push_warning("Output node not allowed to be removed.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
blend_tree_node.node_changed.disconnect(_trigger_graph_changed)
|
|
||||||
|
|
||||||
var graph_node:GraphNode = blend_tree_node_to_graph_node[blend_tree_node]
|
|
||||||
blend_tree.remove_node(blend_tree_node)
|
|
||||||
blend_tree_node_to_graph_node.erase(blend_tree_node)
|
|
||||||
|
|
||||||
_remove_node_connections(graph_node)
|
|
||||||
graph_node_to_blend_tree_node.erase(graph_node)
|
|
||||||
blend_tree_graph_edit.remove_child(graph_node)
|
|
||||||
_on_blend_tree_graph_edit_node_deselected(graph_node)
|
|
||||||
|
|
||||||
EditorInterface.get_inspector().edit(null)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_blend_tree_graph_edit_end_node_move() -> void:
|
|
||||||
for graph_node:GraphNode in selected_nodes.keys():
|
|
||||||
graph_node_to_blend_tree_node[graph_node].position = graph_node.position_offset
|
|
||||||
|
|
||||||
|
|
||||||
func _on_blend_tree_graph_edit_node_deselected(graph_node: Node) -> void:
|
|
||||||
if selected_nodes.has(graph_node):
|
|
||||||
selected_nodes.erase(graph_node)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_blend_tree_graph_edit_node_selected(graph_node: Node) -> void:
|
|
||||||
selected_nodes[graph_node] = graph_node
|
|
||||||
last_selected_graph_node = graph_node
|
|
||||||
EditorInterface.get_inspector().edit(graph_node_to_blend_tree_node[graph_node])
|
|
||||||
|
|
||||||
|
|
||||||
func _on_blend_tree_graph_edit_scroll_offset_changed(offset: Vector2) -> void:
|
|
||||||
if is_instance_valid(blend_tree):
|
|
||||||
blend_tree.graph_offset = offset
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# AddNodePopupMenu
|
|
||||||
#
|
|
||||||
func _on_blend_tree_graph_edit_popup_request(at_position: Vector2) -> void:
|
|
||||||
add_node_popup_menu.position = get_screen_position() + get_local_mouse_position()
|
|
||||||
add_node_popup_menu.reset_size()
|
|
||||||
add_node_popup_menu.popup()
|
|
||||||
new_node_position = blend_tree_graph_edit.scroll_offset + at_position
|
|
||||||
|
|
||||||
|
|
||||||
func _on_add_node_popup_menu_index_pressed(index: int) -> void:
|
|
||||||
var new_blend_tree_node: BLTAnimationNode = ClassDB.instantiate(registered_nodes[index])
|
|
||||||
blend_tree.add_node(new_blend_tree_node)
|
|
||||||
|
|
||||||
var graph_node:GraphNode = create_graph_node_for_blt_node(new_blend_tree_node)
|
|
||||||
blend_tree_graph_edit.add_child(graph_node)
|
|
||||||
|
|
||||||
graph_node_to_blend_tree_node[graph_node] = new_blend_tree_node
|
|
||||||
blend_tree_node_to_graph_node[new_blend_tree_node] = graph_node
|
|
||||||
|
|
||||||
if new_node_position != Vector2.INF:
|
|
||||||
graph_node.position_offset = new_node_position
|
|
||||||
new_blend_tree_node.position = new_node_position
|
|
||||||
|
|
||||||
new_node_position = Vector2.INF
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Handle Node double click
|
|
||||||
#
|
|
||||||
func _on_node_gui_input(input_event:InputEvent, graph_node:GraphNode):
|
|
||||||
# print("Got input event on graph node %s!" % graph_node.name)
|
|
||||||
|
|
||||||
var mouse_button_event:InputEventMouseButton = input_event as InputEventMouseButton
|
|
||||||
if mouse_button_event and mouse_button_event.double_click:
|
|
||||||
_on_node_double_click(graph_node)
|
|
||||||
|
|
||||||
func _on_node_double_click(graph_node:GraphNode):
|
|
||||||
var blend_tree_node:BLTAnimationNode = graph_node_to_blend_tree_node[graph_node]
|
|
||||||
|
|
||||||
if blend_tree_node is BLTAnimationNodeBlendTree:
|
|
||||||
edit_subgraph.emit(blend_tree_node)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Animation selection for BltAnimationNodeSampler
|
|
||||||
#
|
|
||||||
func _on_animation_select(index:int, blt_node_sampler:BLTAnimationNodeSampler, option_button:OptionButton):
|
|
||||||
blt_node_sampler.animation = option_button.get_item_text(index)
|
|
||||||
blt_node_sampler.node_changed.emit(blt_node_sampler.resource_name)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://dr0ndqekm21gy
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
[gd_scene format=3 uid="uid://cptd46rpm0gl3"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dr0ndqekm21gy" path="res://addons/blendalot/blend_tree_editor.gd" id="1_0srhh"]
|
|
||||||
|
|
||||||
[node name="BlendTreeEditor" type="Control" unique_id=1313738200]
|
|
||||||
layout_mode = 3
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
script = ExtResource("1_0srhh")
|
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="." unique_id=758924321]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
size_flags_vertical = 3
|
|
||||||
|
|
||||||
[node name="AddNodePopupMenu" type="PopupMenu" parent="Panel" unique_id=108570539]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
oversampling_override = 1.0
|
|
||||||
item_count = 3
|
|
||||||
item_0/text = "BLTAnimationNodeSampler"
|
|
||||||
item_0/id = 0
|
|
||||||
item_1/text = "BLTAnimationNodeBlend2"
|
|
||||||
item_1/id = 1
|
|
||||||
item_2/text = "BLTAnimationNodeBlendTree"
|
|
||||||
item_2/id = 2
|
|
||||||
|
|
||||||
[node name="BlendTreeGraphEdit" type="GraphEdit" parent="Panel" unique_id=391120290]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
right_disconnects = true
|
|
||||||
|
|
||||||
[connection signal="index_pressed" from="Panel/AddNodePopupMenu" to="." method="_on_add_node_popup_menu_index_pressed"]
|
|
||||||
[connection signal="connection_request" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_connection_request"]
|
|
||||||
[connection signal="delete_nodes_request" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_delete_nodes_request"]
|
|
||||||
[connection signal="disconnection_request" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_disconnection_request"]
|
|
||||||
[connection signal="end_node_move" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_end_node_move"]
|
|
||||||
[connection signal="node_deselected" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_node_deselected"]
|
|
||||||
[connection signal="node_selected" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_node_selected"]
|
|
||||||
[connection signal="popup_request" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_popup_request"]
|
|
||||||
[connection signal="scroll_offset_changed" from="Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_scroll_offset_changed"]
|
|
||||||
16
demo/addons/blendalot/blendalot_main_panel.gd
Normal file
16
demo/addons/blendalot/blendalot_main_panel.gd
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@tool
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _on_hit_me_button_pressed() -> void:
|
||||||
|
print("Hello from the main screen plugin!")
|
||||||
1
demo/addons/blendalot/blendalot_main_panel.gd.uid
Normal file
1
demo/addons/blendalot/blendalot_main_panel.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dvulvuytt81lw
|
||||||
33
demo/addons/blendalot/blendalot_main_panel.tscn
Normal file
33
demo/addons/blendalot/blendalot_main_panel.tscn
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://31c6depvs0y1"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dvulvuytt81lw" path="res://addons/blendalot/blendalot_main_panel.gd" id="1_427jg"]
|
||||||
|
|
||||||
|
[node name="BlendalotMainPanel" type="Control" unique_id=1259518158]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
script = ExtResource("1_427jg")
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=2044593527]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="HitMeButton" type="Button" parent="VBoxContainer" unique_id=1060776498]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
text = "Hit me!"
|
||||||
|
|
||||||
|
[node name="GraphFrame" type="GraphFrame" parent="VBoxContainer" unique_id=184673233]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[connection signal="pressed" from="VBoxContainer/HitMeButton" to="." method="_on_hit_me_button_pressed"]
|
||||||
@ -1,8 +1,9 @@
|
|||||||
@tool
|
@tool
|
||||||
extends EditorPlugin
|
extends EditorPlugin
|
||||||
|
|
||||||
var editor_dock:EditorDock = null
|
const MainPanel = preload("res://addons/blendalot/blendalot_main_panel.tscn")
|
||||||
var animation_graph_editor:AnimationGraphEditor = null
|
|
||||||
|
var main_panel_instance
|
||||||
|
|
||||||
func _enable_plugin() -> void:
|
func _enable_plugin() -> void:
|
||||||
# Add autoloads here.
|
# Add autoloads here.
|
||||||
@ -15,29 +16,25 @@ func _disable_plugin() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
func _enter_tree() -> void:
|
||||||
editor_dock = EditorDock.new()
|
main_panel_instance = MainPanel.instantiate()
|
||||||
editor_dock.title = "Animation Graph"
|
# Add the main panel to the editor's main viewport.
|
||||||
editor_dock.default_slot = EditorDock.DOCK_SLOT_BOTTOM
|
EditorInterface.get_editor_main_screen().add_child(main_panel_instance)
|
||||||
animation_graph_editor = preload ("res://addons/blendalot/animation_graph_editor.tscn").instantiate()
|
# Hide the main panel. Very much required.
|
||||||
editor_dock.add_child(animation_graph_editor)
|
_make_visible(false)
|
||||||
add_dock(editor_dock)
|
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
remove_dock(editor_dock)
|
if main_panel_instance:
|
||||||
editor_dock.queue_free()
|
main_panel_instance.queue_free()
|
||||||
editor_dock = null
|
|
||||||
|
|
||||||
animation_graph_editor.queue_free()
|
|
||||||
animation_graph_editor = null
|
|
||||||
|
|
||||||
|
|
||||||
func _has_main_screen() -> bool:
|
func _has_main_screen():
|
||||||
return false
|
return true
|
||||||
|
|
||||||
|
|
||||||
func _make_visible(visible):
|
func _make_visible(visible):
|
||||||
pass
|
if main_panel_instance:
|
||||||
|
main_panel_instance.visible = visible
|
||||||
|
|
||||||
|
|
||||||
func _get_plugin_name():
|
func _get_plugin_name():
|
||||||
@ -46,19 +43,3 @@ func _get_plugin_name():
|
|||||||
|
|
||||||
func _get_plugin_icon():
|
func _get_plugin_icon():
|
||||||
return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons")
|
return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons")
|
||||||
|
|
||||||
|
|
||||||
func _handles(obj: Object) -> bool:
|
|
||||||
return obj is BLTAnimationNodeBlendTree
|
|
||||||
|
|
||||||
|
|
||||||
func _edit(object: Object):
|
|
||||||
if not is_instance_valid(animation_graph_editor):
|
|
||||||
push_error("Cannot edit object as AnimationGraphEditor is not initialized")
|
|
||||||
return
|
|
||||||
|
|
||||||
if object is BLTAnimationNodeBlendTree:
|
|
||||||
animation_graph_editor.edit_animation_root_node(object)
|
|
||||||
return
|
|
||||||
|
|
||||||
print("Cannot (yet) edit object " + str(object))
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
[gd_resource type="AnimationNodeBlendTree" format=3 uid="uid://dqy0dgwsm8t46"]
|
[gd_resource type="AnimationNodeBlendTree" load_steps=4 format=3 uid="uid://dqy0dgwsm8t46"]
|
||||||
|
|
||||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_h2yge"]
|
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_h2yge"]
|
||||||
animation = &"Limping-InPlace"
|
animation = &"Limping-InPlace"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
[gd_resource type="AnimationNodeBlendTree" format=3 uid="uid://vsf71o82lkld"]
|
[gd_resource type="AnimationNodeBlendTree" load_steps=4 format=3 uid="uid://vsf71o82lkld"]
|
||||||
|
|
||||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_h2yge"]
|
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_h2yge"]
|
||||||
animation = &"Run-InPlace"
|
animation = &"Run-InPlace"
|
||||||
@ -8,28 +8,7 @@ animation = &"Walk-InPlace"
|
|||||||
|
|
||||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_lquwl"]
|
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_lquwl"]
|
||||||
|
|
||||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_vyt75"]
|
|
||||||
|
|
||||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_hom0r"]
|
|
||||||
|
|
||||||
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_vyt75"]
|
|
||||||
|
|
||||||
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_1rfsi"]
|
|
||||||
nodes/BlendTree/node = SubResource("AnimationNodeBlendTree_vyt75")
|
|
||||||
nodes/BlendTree/position = Vector2(694.9995, 215.55058)
|
|
||||||
|
|
||||||
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_8tpve"]
|
|
||||||
graph_offset = Vector2(-782, 179.47485)
|
|
||||||
nodes/Animation/node = SubResource("AnimationNodeAnimation_vyt75")
|
|
||||||
nodes/Animation/position = Vector2(-320, 140)
|
|
||||||
nodes/Blend2/node = SubResource("AnimationNodeBlend2_hom0r")
|
|
||||||
nodes/Blend2/position = Vector2(-115.66393, 127.37674)
|
|
||||||
nodes/BlendTree/node = SubResource("AnimationNodeBlendTree_1rfsi")
|
|
||||||
nodes/BlendTree/position = Vector2(-480, 400)
|
|
||||||
node_connections = [&"output", 0, &"Blend2", &"Blend2", 0, &"Animation", &"Blend2", 1, &"BlendTree"]
|
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
graph_offset = Vector2(-217.4643, 82.84979)
|
|
||||||
nodes/output/position = Vector2(540, 140)
|
nodes/output/position = Vector2(540, 140)
|
||||||
nodes/Animation/node = SubResource("AnimationNodeAnimation_1bvp3")
|
nodes/Animation/node = SubResource("AnimationNodeAnimation_1bvp3")
|
||||||
nodes/Animation/position = Vector2(120, 80)
|
nodes/Animation/position = Vector2(120, 80)
|
||||||
@ -37,6 +16,4 @@ nodes/Animation/position = Vector2(120, 80)
|
|||||||
"nodes/Animation 2/position" = Vector2(80, 320)
|
"nodes/Animation 2/position" = Vector2(80, 320)
|
||||||
nodes/Blend2/node = SubResource("AnimationNodeBlend2_lquwl")
|
nodes/Blend2/node = SubResource("AnimationNodeBlend2_lquwl")
|
||||||
nodes/Blend2/position = Vector2(360, 180)
|
nodes/Blend2/position = Vector2(360, 180)
|
||||||
nodes/BlendTree/node = SubResource("AnimationNodeBlendTree_8tpve")
|
|
||||||
nodes/BlendTree/position = Vector2(778.0867, 295.33868)
|
|
||||||
node_connections = [&"output", 0, &"Blend2", &"Blend2", 0, &"Animation", &"Blend2", 1, &"Animation 2"]
|
node_connections = [&"output", 0, &"Blend2", &"Blend2", 0, &"Animation", &"Blend2", 1, &"Animation 2"]
|
||||||
|
|||||||
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
|
||||||
|
|||||||
171
demo/main.tscn
171
demo/main.tscn
@ -1,11 +1,12 @@
|
|||||||
[gd_scene format=3 uid="uid://svj53e2xoio"]
|
[gd_scene load_steps=14 format=3 uid="uid://svj53e2xoio"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://d1xcqdqr1qeu6" path="res://assets/MixamoAmy.glb" id="1_0xm2m"]
|
[ext_resource type="PackedScene" uid="uid://d1xcqdqr1qeu6" path="res://assets/MixamoAmy.glb" id="1_0xm2m"]
|
||||||
[ext_resource type="Script" uid="uid://bjvgqujpqumj7" path="res://main.gd" id="1_1bvp3"]
|
[ext_resource type="Script" uid="uid://bjvgqujpqumj7" path="res://main.gd" id="1_1bvp3"]
|
||||||
[ext_resource type="AnimationLibrary" uid="uid://dwubn740aqx51" path="res://animation_library.res" id="3_1bvp3"]
|
[ext_resource type="AnimationLibrary" uid="uid://dwubn740aqx51" path="res://animation_library.res" id="3_1bvp3"]
|
||||||
[ext_resource type="AnimationNodeBlendTree" uid="uid://dqy0dgwsm8t46" path="res://animation_tree_walk_limp.tres" id="3_272bh"]
|
[ext_resource type="AnimationNodeBlendTree" uid="uid://dqy0dgwsm8t46" path="res://animation_tree_walk_limp.tres" id="3_272bh"]
|
||||||
|
[ext_resource type="BLTAnimationNodeBlendTree" uid="uid://2qfwr1xkiw0s" path="res://synced_blend_tree_walk_limp.tres" id="4_lquwl"]
|
||||||
|
[ext_resource type="BLTAnimationNodeBlendTree" uid="uid://qsk64ax2o47f" path="res://synced_blend_tree_walk_run.tres" id="5_7mycd"]
|
||||||
[ext_resource type="AnimationNodeBlendTree" uid="uid://vsf71o82lkld" path="res://animation_tree_walk_run.tres" id="6_5vw27"]
|
[ext_resource type="AnimationNodeBlendTree" uid="uid://vsf71o82lkld" path="res://animation_tree_walk_run.tres" id="6_5vw27"]
|
||||||
[ext_resource type="BLTAnimationNodeBlendTree" uid="uid://2qfwr1xkiw0s" path="res://synced_blend_tree_walk_limp.tres" id="6_272bh"]
|
|
||||||
|
|
||||||
[sub_resource type="Theme" id="Theme_272bh"]
|
[sub_resource type="Theme" id="Theme_272bh"]
|
||||||
default_font_size = 30
|
default_font_size = 30
|
||||||
@ -31,40 +32,6 @@ sky = SubResource("Sky_1bvp3")
|
|||||||
tonemap_mode = 2
|
tonemap_mode = 2
|
||||||
glow_enabled = true
|
glow_enabled = true
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_7mycd"]
|
|
||||||
resource_name = "BLTAnimationNodeBlend2"
|
|
||||||
position = Vector2(-320, -40)
|
|
||||||
blend_amount = 0.81
|
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_272bh"]
|
|
||||||
resource_name = "BLTAnimationNodeSampler"
|
|
||||||
position = Vector2(-490, 7)
|
|
||||||
animation = &"animation_library/Walk-InPlace"
|
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeBlendTree" id="BLTAnimationNodeBlendTree_5vw27"]
|
|
||||||
resource_name = "BLTAnimationNodeBlendTree"
|
|
||||||
position = Vector2(-640, -20)
|
|
||||||
graph_offset = Vector2(-760.67163, -24.823944)
|
|
||||||
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_272bh")
|
|
||||||
nodes/BLTAnimationNodeSampler/graph_offset = Vector2(-490, 7)
|
|
||||||
node_connections = ["Output", 0, "BLTAnimationNodeSampler"]
|
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_kek77"]
|
|
||||||
resource_name = "BLTAnimationNodeSampler"
|
|
||||||
position = Vector2(-620, 140)
|
|
||||||
animation = &"animation_library/Run-InPlace"
|
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeBlendTree" id="BLTAnimationNodeBlendTree_7mycd"]
|
|
||||||
resource_name = "Root"
|
|
||||||
graph_offset = Vector2(-869, -71)
|
|
||||||
nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_7mycd")
|
|
||||||
nodes/BLTAnimationNodeBlend2/graph_offset = Vector2(-320, -40)
|
|
||||||
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_kek77")
|
|
||||||
nodes/BLTAnimationNodeSampler/graph_offset = Vector2(-620, 140)
|
|
||||||
nodes/BLTAnimationNodeBlendTree/node = SubResource("BLTAnimationNodeBlendTree_5vw27")
|
|
||||||
nodes/BLTAnimationNodeBlendTree/graph_offset = Vector2(-640, -20)
|
|
||||||
node_connections = ["Output", 0, "BLTAnimationNodeBlend2", "BLTAnimationNodeBlend2", 0, "BLTAnimationNodeBlendTree", "BLTAnimationNodeBlend2", 1, "BLTAnimationNodeSampler"]
|
|
||||||
|
|
||||||
[node name="Main" type="Node3D" unique_id=933302313]
|
[node name="Main" type="Node3D" unique_id=933302313]
|
||||||
script = ExtResource("1_1bvp3")
|
script = ExtResource("1_1bvp3")
|
||||||
|
|
||||||
@ -170,7 +137,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.6, 0, 0)
|
|||||||
tree_root = ExtResource("6_5vw27")
|
tree_root = ExtResource("6_5vw27")
|
||||||
anim_player = NodePath("../AnimationPlayer")
|
anim_player = NodePath("../AnimationPlayer")
|
||||||
parameters/Blend2/blend_amount = 0.0
|
parameters/Blend2/blend_amount = 0.0
|
||||||
parameters/BlendTree/Blend2/blend_amount = 0.0
|
|
||||||
|
|
||||||
[node name="MixamoAmyWalkLimpSynced" parent="Characters" unique_id=1018815116 instance=ExtResource("1_0xm2m")]
|
[node name="MixamoAmyWalkLimpSynced" parent="Characters" unique_id=1018815116 instance=ExtResource("1_0xm2m")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
@ -181,145 +147,22 @@ libraries/animation_library = ExtResource("3_1bvp3")
|
|||||||
|
|
||||||
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkLimpSynced" unique_id=1866796918]
|
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkLimpSynced" unique_id=1866796918]
|
||||||
animation_player = NodePath("../AnimationPlayer2")
|
animation_player = NodePath("../AnimationPlayer2")
|
||||||
tree_root = ExtResource("6_272bh")
|
tree_root = ExtResource("4_lquwl")
|
||||||
skeleton = NodePath("../Armature/Skeleton3D")
|
skeleton = NodePath("../Armature/Skeleton3D")
|
||||||
parameters/BLTAnimationNodeBlend2/blend_amount = 1.0
|
parameters/BLTAnimationNodeBlend2/blend_amount = 0.4
|
||||||
|
|
||||||
[node name="MixamoAmyWalkRunSynced" parent="Characters" unique_id=2088190993 instance=ExtResource("1_0xm2m")]
|
[node name="MixamoAmyWalkRunSynced" parent="Characters" unique_id=2088190993 instance=ExtResource("1_0xm2m")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4, 0, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4, 0, 0)
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="Characters/MixamoAmyWalkRunSynced/Armature" parent_id_path=PackedInt32Array(2088190993, 1791722621) index="0" unique_id=1831928682]
|
|
||||||
bones/2/position = Vector3(0, 0, 0)
|
|
||||||
bones/2/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/3/position = Vector3(0, 0, 0)
|
|
||||||
bones/3/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/4/position = Vector3(0, 0, 0)
|
|
||||||
bones/4/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/5/position = Vector3(0, 0, 0)
|
|
||||||
bones/5/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/6/position = Vector3(0, 0, 0)
|
|
||||||
bones/6/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/7/position = Vector3(0, 0, 0)
|
|
||||||
bones/7/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/8/position = Vector3(0, 0, 0)
|
|
||||||
bones/9/position = Vector3(0, 0, 0)
|
|
||||||
bones/9/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/10/position = Vector3(0, 0, 0)
|
|
||||||
bones/10/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/11/position = Vector3(0, 0, 0)
|
|
||||||
bones/11/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/12/position = Vector3(0, 0, 0)
|
|
||||||
bones/12/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/13/position = Vector3(0, 0, 0)
|
|
||||||
bones/13/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/14/position = Vector3(0, 0, 0)
|
|
||||||
bones/14/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/15/position = Vector3(0, 0, 0)
|
|
||||||
bones/15/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/16/position = Vector3(0, 0, 0)
|
|
||||||
bones/17/position = Vector3(0, 0, 0)
|
|
||||||
bones/17/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/18/position = Vector3(0, 0, 0)
|
|
||||||
bones/18/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/19/position = Vector3(0, 0, 0)
|
|
||||||
bones/19/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/20/position = Vector3(0, 0, 0)
|
|
||||||
bones/21/position = Vector3(0, 0, 0)
|
|
||||||
bones/21/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/22/position = Vector3(0, 0, 0)
|
|
||||||
bones/22/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/23/position = Vector3(0, 0, 0)
|
|
||||||
bones/23/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/24/position = Vector3(0, 0, 0)
|
|
||||||
bones/25/position = Vector3(0, 0, 0)
|
|
||||||
bones/25/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/26/position = Vector3(0, 0, 0)
|
|
||||||
bones/26/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/27/position = Vector3(0, 0, 0)
|
|
||||||
bones/27/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/28/position = Vector3(0, 0, 0)
|
|
||||||
bones/29/position = Vector3(0, 0, 0)
|
|
||||||
bones/29/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/30/position = Vector3(0, 0, 0)
|
|
||||||
bones/30/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/31/position = Vector3(0, 0, 0)
|
|
||||||
bones/31/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/32/position = Vector3(0, 0, 0)
|
|
||||||
bones/33/position = Vector3(0, 0, 0)
|
|
||||||
bones/33/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/34/position = Vector3(0, 0, 0)
|
|
||||||
bones/34/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/35/position = Vector3(0, 0, 0)
|
|
||||||
bones/35/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/36/position = Vector3(0, 0, 0)
|
|
||||||
bones/36/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/37/position = Vector3(0, 0, 0)
|
|
||||||
bones/37/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/38/position = Vector3(0, 0, 0)
|
|
||||||
bones/38/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/39/position = Vector3(0, 0, 0)
|
|
||||||
bones/39/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/40/position = Vector3(0, 0, 0)
|
|
||||||
bones/41/position = Vector3(0, 0, 0)
|
|
||||||
bones/41/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/42/position = Vector3(0, 0, 0)
|
|
||||||
bones/42/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/43/position = Vector3(0, 0, 0)
|
|
||||||
bones/43/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/44/position = Vector3(0, 0, 0)
|
|
||||||
bones/45/position = Vector3(0, 0, 0)
|
|
||||||
bones/45/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/46/position = Vector3(0, 0, 0)
|
|
||||||
bones/46/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/47/position = Vector3(0, 0, 0)
|
|
||||||
bones/47/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/48/position = Vector3(0, 0, 0)
|
|
||||||
bones/49/position = Vector3(0, 0, 0)
|
|
||||||
bones/49/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/50/position = Vector3(0, 0, 0)
|
|
||||||
bones/50/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/51/position = Vector3(0, 0, 0)
|
|
||||||
bones/51/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/52/position = Vector3(0, 0, 0)
|
|
||||||
bones/53/position = Vector3(0, 0, 0)
|
|
||||||
bones/53/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/54/position = Vector3(0, 0, 0)
|
|
||||||
bones/54/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/55/position = Vector3(0, 0, 0)
|
|
||||||
bones/55/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/56/position = Vector3(0, 0, 0)
|
|
||||||
bones/57/position = Vector3(0, 0, 0)
|
|
||||||
bones/57/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/58/position = Vector3(0, 0, 0)
|
|
||||||
bones/58/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/59/position = Vector3(0, 0, 0)
|
|
||||||
bones/59/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/60/position = Vector3(0, 0, 0)
|
|
||||||
bones/60/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/61/position = Vector3(0, 0, 0)
|
|
||||||
bones/62/position = Vector3(0, 0, 0)
|
|
||||||
bones/62/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/63/position = Vector3(0, 0, 0)
|
|
||||||
bones/63/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/64/position = Vector3(0, 0, 0)
|
|
||||||
bones/64/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/65/position = Vector3(0, 0, 0)
|
|
||||||
bones/65/rotation = Quaternion(0, 0, 0, 1)
|
|
||||||
bones/66/position = Vector3(0, 0, 0)
|
|
||||||
|
|
||||||
[node name="AnimationPlayer" parent="Characters/MixamoAmyWalkRunSynced" index="1" unique_id=66984852]
|
|
||||||
active = false
|
|
||||||
|
|
||||||
[node name="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1255239074]
|
[node name="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1255239074]
|
||||||
active = false
|
|
||||||
libraries/animation_library = ExtResource("3_1bvp3")
|
libraries/animation_library = ExtResource("3_1bvp3")
|
||||||
|
|
||||||
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1602406394]
|
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1602406394]
|
||||||
animation_player = NodePath("../AnimationPlayer2")
|
animation_player = NodePath("../AnimationPlayer2")
|
||||||
tree_root = SubResource("BLTAnimationNodeBlendTree_7mycd")
|
tree_root = ExtResource("5_7mycd")
|
||||||
skeleton = NodePath("../Armature/Skeleton3D")
|
skeleton = NodePath("../Armature/Skeleton3D")
|
||||||
parameters/BLTAnimationNodeBlend2/blend_amount = 0.81
|
parameters/BLTAnimationNodeBlend2/blend_amount = 0.4
|
||||||
|
|
||||||
[connection signal="value_changed" from="UI/MarginContainer/HBoxContainer/BlendWeightSlider" to="." method="_on_blend_weight_slider_value_changed"]
|
[connection signal="value_changed" from="UI/MarginContainer/HBoxContainer/BlendWeightSlider" to="." method="_on_blend_weight_slider_value_changed"]
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ config_version=5
|
|||||||
|
|
||||||
config/name="Synced Blend Tree Test"
|
config/name="Synced Blend Tree Test"
|
||||||
run/main_scene="uid://svj53e2xoio"
|
run/main_scene="uid://svj53e2xoio"
|
||||||
config/features=PackedStringArray("4.6", "Forward Plus")
|
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
@ -23,7 +23,3 @@ window/size/viewport_height=1024
|
|||||||
[dotnet]
|
[dotnet]
|
||||||
|
|
||||||
project/assembly_name="Synced Blend Tree Test"
|
project/assembly_name="Synced Blend Tree Test"
|
||||||
|
|
||||||
[editor_plugins]
|
|
||||||
|
|
||||||
enabled=PackedStringArray("res://addons/blendalot/plugin.cfg")
|
|
||||||
|
|||||||
@ -1,15 +1,12 @@
|
|||||||
[gd_resource type="BLTAnimationNodeBlendTree" format=3]
|
[gd_resource type="BLTAnimationNodeBlendTree" load_steps=4 format=3]
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_bvt3d"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_bvt3d"]
|
||||||
resource_name = "BLTAnimationNodeSampler 1"
|
|
||||||
animation = &"animation_library/TestAnimationB"
|
animation = &"animation_library/TestAnimationB"
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
||||||
resource_name = "BLTAnimationNodeSampler"
|
|
||||||
animation = &"animation_library/TestAnimationA"
|
animation = &"animation_library/TestAnimationA"
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_n4m28"]
|
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_n4m28"]
|
||||||
resource_name = "Blend2"
|
|
||||||
sync = false
|
sync = false
|
||||||
blend_amount = 0.5
|
blend_amount = 0.5
|
||||||
sync = false
|
sync = false
|
||||||
@ -21,4 +18,4 @@ nodes/Blend2/position = Vector2(0, 0)
|
|||||||
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
||||||
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_sntl5")
|
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_sntl5")
|
||||||
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
||||||
node_connections = ["Blend2", 0, "BLTAnimationNodeSampler", "Blend2", 1, "BLTAnimationNodeSampler 1", "Output", 0, "Blend2"]
|
node_connections = [&"Blend2", 0, &"BLTAnimationNodeSampler", &"Blend2", 1, &"BLTAnimationNodeSampler 1", &"Output", 0, &"Blend2"]
|
||||||
|
|||||||
@ -1,25 +1,19 @@
|
|||||||
[gd_resource type="BLTAnimationNodeBlendTree" format=3 uid="uid://2qfwr1xkiw0s"]
|
[gd_resource type="BLTAnimationNodeBlendTree" load_steps=4 format=3 uid="uid://2qfwr1xkiw0s"]
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_bvt3d"]
|
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_bvt3d"]
|
||||||
resource_name = "BLTAnimationNodeBlend2"
|
blend_amount = 0.4
|
||||||
graph_offset = Vector2(-600, 180)
|
|
||||||
blend_amount = 1.0
|
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
||||||
resource_name = "BLTAnimationNodeSampler 1"
|
|
||||||
graph_offset = Vector2(-1200, 560)
|
|
||||||
animation = &"animation_library/Limping-InPlace"
|
animation = &"animation_library/Limping-InPlace"
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_n4m28"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_n4m28"]
|
||||||
resource_name = "BLTAnimationNodeSampler"
|
|
||||||
graph_offset = Vector2(-1300, -40)
|
|
||||||
animation = &"animation_library/Walk-InPlace"
|
animation = &"animation_library/Walk-InPlace"
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_bvt3d")
|
nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_bvt3d")
|
||||||
nodes/BLTAnimationNodeBlend2/graph_offset = Vector2(-600, 180)
|
nodes/BLTAnimationNodeBlend2/position = Vector2(0, 0)
|
||||||
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_sntl5")
|
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_sntl5")
|
||||||
"nodes/BLTAnimationNodeSampler 1/graph_offset" = Vector2(-1200, 560)
|
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
||||||
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_n4m28")
|
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_n4m28")
|
||||||
nodes/BLTAnimationNodeSampler/graph_offset = Vector2(-1300, -40)
|
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
||||||
node_connections = ["BLTAnimationNodeBlend2", 0, "BLTAnimationNodeSampler", "BLTAnimationNodeBlend2", 1, "BLTAnimationNodeSampler 1", "Output", 0, "BLTAnimationNodeBlend2"]
|
node_connections = [&"BLTAnimationNodeBlend2", 0, &"BLTAnimationNodeSampler", &"BLTAnimationNodeBlend2", 1, &"BLTAnimationNodeSampler 1", &"Output", 0, &"BLTAnimationNodeBlend2"]
|
||||||
|
|||||||
@ -1,25 +1,19 @@
|
|||||||
[gd_resource type="BLTAnimationNodeBlendTree" format=3 uid="uid://qsk64ax2o47f"]
|
[gd_resource type="BLTAnimationNodeBlendTree" load_steps=4 format=3 uid="uid://qsk64ax2o47f"]
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_bvt3d"]
|
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_bvt3d"]
|
||||||
resource_name = "BLTAnimationNodeBlend2"
|
blend_amount = 0.4
|
||||||
graph_offset = Vector2(-360, 140)
|
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
||||||
resource_name = "BLTAnimationNodeSampler 1"
|
|
||||||
graph_offset = Vector2(-1140, 440)
|
|
||||||
animation = &"animation_library/Run-InPlace"
|
animation = &"animation_library/Run-InPlace"
|
||||||
|
|
||||||
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_n4m28"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_n4m28"]
|
||||||
resource_name = "BLTAnimationNodeSampler"
|
|
||||||
graph_offset = Vector2(-1080, -40)
|
|
||||||
animation = &"animation_library/Walk-InPlace"
|
animation = &"animation_library/Walk-InPlace"
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
nodes/Output/position = Vector2(180, 80)
|
|
||||||
nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_bvt3d")
|
nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_bvt3d")
|
||||||
nodes/BLTAnimationNodeBlend2/position = Vector2(-360, 140)
|
nodes/BLTAnimationNodeBlend2/position = Vector2(0, 0)
|
||||||
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_sntl5")
|
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_sntl5")
|
||||||
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(-1140, 440)
|
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
||||||
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_n4m28")
|
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_n4m28")
|
||||||
nodes/BLTAnimationNodeSampler/position = Vector2(-1080, -40)
|
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
||||||
node_connections = ["BLTAnimationNodeBlend2", 0, "BLTAnimationNodeSampler", "BLTAnimationNodeBlend2", 1, "BLTAnimationNodeSampler 1", "Output", 0, "BLTAnimationNodeBlend2"]
|
node_connections = [&"BLTAnimationNodeBlend2", 0, &"BLTAnimationNodeSampler", &"BLTAnimationNodeBlend2", 1, &"BLTAnimationNodeSampler 1", &"Output", 0, &"BLTAnimationNodeBlend2"]
|
||||||
|
|||||||
@ -111,7 +111,7 @@ Some nodes have special names in the Blend Tree:
|
|||||||
Except for the output node of a Blend Tree the following properties hold:
|
Except for the output node of a Blend Tree the following properties hold:
|
||||||
|
|
||||||
* all Blend Tree nodes only operate on properties they own and any other data (e.g. inputs and outputs) are specified
|
* all Blend Tree nodes only operate on properties they own and any other data (e.g. inputs and outputs) are specified
|
||||||
via arguments to `BLTAnimationNode::evaluate(context, inputs, output)` function of the node.
|
via arguments to `SyncedAnimationNode::evaluate(context, inputs, output)` function of the node.
|
||||||
|
|
||||||
Advantages:
|
Advantages:
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,6 @@ void initialize_blendalot_animgraph_module(ModuleInitializationLevel p_level) {
|
|||||||
ClassDB::register_class<BLTAnimationNodeOutput>();
|
ClassDB::register_class<BLTAnimationNodeOutput>();
|
||||||
ClassDB::register_class<BLTAnimationNodeBlendTree>();
|
ClassDB::register_class<BLTAnimationNodeBlendTree>();
|
||||||
ClassDB::register_class<BLTAnimationNodeSampler>();
|
ClassDB::register_class<BLTAnimationNodeSampler>();
|
||||||
ClassDB::register_class<BLTAnimationNodeTimeScale>();
|
|
||||||
ClassDB::register_class<BLTAnimationNodeBlend2>();
|
ClassDB::register_class<BLTAnimationNodeBlend2>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
sync_track.h
34
sync_track.h
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "core/templates/local_vector.h"
|
#include "core/templates/local_vector.h"
|
||||||
|
|
||||||
#include "blendalot_math_helper.h"
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@ -22,7 +21,7 @@
|
|||||||
* duration. Blended SyncTracks always have their first interval start at t = 0.0s.
|
* duration. Blended SyncTracks always have their first interval start at t = 0.0s.
|
||||||
*/
|
*/
|
||||||
struct SyncTrack {
|
struct SyncTrack {
|
||||||
static constexpr int cSyncTrackMaxIntervals = 32;
|
static constexpr int cSyncTrackMaxIntervals = 8;
|
||||||
|
|
||||||
SyncTrack() :
|
SyncTrack() :
|
||||||
duration(0.f), num_intervals(1) {
|
duration(0.f), num_intervals(1) {
|
||||||
@ -60,12 +59,6 @@ struct SyncTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double calc_ratio_from_sync_time(double sync_time) const {
|
double calc_ratio_from_sync_time(double sync_time) const {
|
||||||
// When blending SyncTracks with differing numbers of intervals the resulting SyncTrack may have
|
|
||||||
// additional repeats of the animation (=> "virtual sync periods", https://youtu.be/Jkv0pbp0ckQ?t=8178).
|
|
||||||
//
|
|
||||||
// Therefore, we first have to transform it back to the numbers of intervals we actually have.
|
|
||||||
sync_time = fmod(sync_time, num_intervals);
|
|
||||||
|
|
||||||
float interval_ratio = fmod(sync_time, 1.0f);
|
float interval_ratio = fmod(sync_time, 1.0f);
|
||||||
int interval = int(sync_time - interval_ratio);
|
int interval = int(sync_time - interval_ratio);
|
||||||
|
|
||||||
@ -133,32 +126,19 @@ struct SyncTrack {
|
|||||||
*/
|
*/
|
||||||
static SyncTrack
|
static SyncTrack
|
||||||
blend(float weight, const SyncTrack &track_A, const SyncTrack &track_B) {
|
blend(float weight, const SyncTrack &track_A, const SyncTrack &track_B) {
|
||||||
if (Math::is_zero_approx(weight)) {
|
assert(track_A.num_intervals == track_B.num_intervals);
|
||||||
return track_A;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Math::is_zero_approx(1.0 - weight)) {
|
|
||||||
return track_B;
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncTrack result;
|
SyncTrack result;
|
||||||
|
|
||||||
if (track_A.num_intervals != track_B.num_intervals) {
|
|
||||||
result.num_intervals = least_common_multiple(track_A.num_intervals, track_B.num_intervals);
|
|
||||||
} else {
|
|
||||||
result.num_intervals = track_A.num_intervals;
|
result.num_intervals = track_A.num_intervals;
|
||||||
}
|
|
||||||
assert(result.num_intervals < cSyncTrackMaxIntervals);
|
|
||||||
|
|
||||||
float track_A_repeats = static_cast<float>(result.num_intervals / track_A.num_intervals);
|
result.duration =
|
||||||
float track_B_repeats = static_cast<float>(result.num_intervals / track_B.num_intervals);
|
(1.0f - weight) * track_A.duration + weight * track_B.duration;
|
||||||
|
|
||||||
result.duration = (1.0f - weight) * (track_A.duration * track_A_repeats) + weight * (track_B.duration * track_B_repeats);
|
|
||||||
result.interval_start_ratio[0] = 0.f;
|
result.interval_start_ratio[0] = 0.f;
|
||||||
|
|
||||||
for (int i = 0; i < result.num_intervals; i++) {
|
for (int i = 0; i < result.num_intervals; i++) {
|
||||||
float interval_duration_A = track_A.interval_duration_ratio[i % track_A.num_intervals] / track_A_repeats;
|
float interval_duration_A = track_A.interval_duration_ratio[i];
|
||||||
float interval_duration_B = track_B.interval_duration_ratio[i % track_B.num_intervals] / track_B_repeats;
|
float interval_duration_B = track_B.interval_duration_ratio[i];
|
||||||
result.interval_duration_ratio[i] =
|
result.interval_duration_ratio[i] =
|
||||||
(1.0f - weight) * interval_duration_A + weight * interval_duration_B;
|
(1.0f - weight) * interval_duration_A + weight * interval_duration_B;
|
||||||
|
|
||||||
@ -172,6 +152,8 @@ struct SyncTrack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(result.num_intervals < cSyncTrackMaxIntervals);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
#include "tests/test_macros.h"
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
namespace TestBlendalotAnimationGraph {
|
namespace TestSyncedAnimationGraph {
|
||||||
|
|
||||||
TEST_CASE("[Blendalot][SyncTrack] Basic") {
|
TEST_CASE("[SyncedAnimationGraph][SyncTrack] Basic") {
|
||||||
SyncTrack track_a;
|
SyncTrack track_a;
|
||||||
track_a.num_intervals = 2;
|
track_a.num_intervals = 2;
|
||||||
track_a.duration = 2.0;
|
track_a.duration = 2.0;
|
||||||
@ -84,7 +84,7 @@ TEST_CASE("[Blendalot][SyncTrack] Basic") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Blendalot][SyncTrack] Create Sync Track from markers") {
|
TEST_CASE("[SyncedAnimationGraph][SyncTrack] Create Sync Track from markers") {
|
||||||
SyncTrack track = SyncTrack::create_from_markers(2.0f, { 0.9f, 0.2f });
|
SyncTrack track = SyncTrack::create_from_markers(2.0f, { 0.9f, 0.2f });
|
||||||
|
|
||||||
WHEN("Querying Ratios") {
|
WHEN("Querying Ratios") {
|
||||||
@ -138,7 +138,7 @@ TEST_CASE("[Blendalot][SyncTrack] Create Sync Track from markers") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Blendalot][SyncTrack] Sync Track blending") {
|
TEST_CASE("[SyncedAnimationGraph][SyncTrack] Sync Track blending") {
|
||||||
SyncTrack track_a = SyncTrack::create_from_markers(2.0, { 0., 0.6, 1.8 });
|
SyncTrack track_a = SyncTrack::create_from_markers(2.0, { 0., 0.6, 1.8 });
|
||||||
SyncTrack track_b = SyncTrack::create_from_markers(1.5f, { 1.05, 1.35, 0.3 });
|
SyncTrack track_b = SyncTrack::create_from_markers(1.5f, { 1.05, 1.35, 0.3 });
|
||||||
|
|
||||||
@ -203,47 +203,4 @@ TEST_CASE("[Blendalot][SyncTrack] Sync Track blending") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Blendalot][SyncTrack] Sync Track blending non-matching interval count") {
|
} //namespace TestSyncedAnimationGraph
|
||||||
SyncTrack track_a = SyncTrack::create_from_markers(2.0, { 0., 0.6, 1.8 });
|
|
||||||
SyncTrack track_b = SyncTrack::create_from_markers(1.5f, { 1.05 });
|
|
||||||
|
|
||||||
WHEN("Blending two synctracks with weight 0.") {
|
|
||||||
SyncTrack blended = SyncTrack::blend(0.f, track_a, track_b);
|
|
||||||
|
|
||||||
blended.duration = track_a.duration;
|
|
||||||
blended.interval_start_ratio[0] = 0.0;
|
|
||||||
for (int i = 0; i < track_a.num_intervals; i++) {
|
|
||||||
CHECK(blended.interval_duration_ratio[i] == track_a.interval_duration_ratio[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WHEN("Blending two synctracks with weight 1.") {
|
|
||||||
SyncTrack blended = SyncTrack::blend(1.f, track_a, track_b);
|
|
||||||
|
|
||||||
blended.duration = track_b.duration;
|
|
||||||
blended.interval_start_ratio[0] = 0.0;
|
|
||||||
for (int i = 0; i < track_b.num_intervals; i++) {
|
|
||||||
CHECK(blended.interval_duration_ratio[i] == track_b.interval_duration_ratio[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Blending with weight 0.2") {
|
|
||||||
float weight = 0.2f;
|
|
||||||
SyncTrack blended = SyncTrack::blend(weight, track_a, track_b);
|
|
||||||
|
|
||||||
float track_a_repeats = static_cast<float>(blended.num_intervals / track_a.num_intervals);
|
|
||||||
float track_b_repeats = static_cast<float>(blended.num_intervals / track_b.num_intervals);
|
|
||||||
|
|
||||||
CHECK(
|
|
||||||
blended.duration == doctest::Approx(2.5));
|
|
||||||
CHECK(
|
|
||||||
blended.interval_start_ratio[0] == 0.0);
|
|
||||||
CHECK(
|
|
||||||
blended.interval_duration_ratio[0] == doctest::Approx((1.0 - weight) * track_a.interval_duration_ratio[0] / track_a_repeats + weight * track_b.interval_duration_ratio[0] / track_b_repeats));
|
|
||||||
CHECK(
|
|
||||||
blended.interval_duration_ratio[1] == doctest::Approx((1.0 - weight) * track_a.interval_duration_ratio[1] / track_a_repeats + weight * track_b.interval_duration_ratio[0] / track_b_repeats));
|
|
||||||
CHECK(
|
|
||||||
blended.interval_duration_ratio[2] == doctest::Approx((1.0 - weight) * track_a.interval_duration_ratio[2] / track_a_repeats + weight * track_b.interval_duration_ratio[0] / track_b_repeats));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} //namespace TestBlendalotAnimationGraph
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "tests/test_macros.h"
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
struct BlendTreeFixture {
|
struct SyncedAnimationGraphFixture {
|
||||||
Node *character_node;
|
Node *character_node;
|
||||||
Skeleton3D *skeleton_node;
|
Skeleton3D *skeleton_node;
|
||||||
AnimationPlayer *player_node;
|
AnimationPlayer *player_node;
|
||||||
@ -15,14 +15,13 @@ struct BlendTreeFixture {
|
|||||||
|
|
||||||
Ref<Animation> test_animation_a;
|
Ref<Animation> test_animation_a;
|
||||||
Ref<Animation> test_animation_b;
|
Ref<Animation> test_animation_b;
|
||||||
Ref<Animation> test_animation_c;
|
|
||||||
Ref<Animation> test_animation_sync_a;
|
Ref<Animation> test_animation_sync_a;
|
||||||
Ref<Animation> test_animation_sync_b;
|
Ref<Animation> test_animation_sync_b;
|
||||||
|
|
||||||
Ref<AnimationLibrary> animation_library;
|
Ref<AnimationLibrary> animation_library;
|
||||||
|
|
||||||
BLTAnimationGraph *animation_graph;
|
BLTAnimationGraph *synced_animation_graph;
|
||||||
BlendTreeFixture() {
|
SyncedAnimationGraphFixture() {
|
||||||
BLTAnimationGraph *scene_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
BLTAnimationGraph *scene_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
||||||
|
|
||||||
if (scene_animation_graph == nullptr) {
|
if (scene_animation_graph == nullptr) {
|
||||||
@ -51,12 +50,12 @@ struct BlendTreeFixture {
|
|||||||
|
|
||||||
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
||||||
|
|
||||||
animation_graph = memnew(BLTAnimationGraph);
|
synced_animation_graph = memnew(BLTAnimationGraph);
|
||||||
animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
synced_animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
||||||
SceneTree::get_singleton()->get_root()->add_child(animation_graph);
|
SceneTree::get_singleton()->get_root()->add_child(synced_animation_graph);
|
||||||
|
|
||||||
animation_graph->set_animation_player(player_node->get_path());
|
synced_animation_graph->set_animation_player(player_node->get_path());
|
||||||
animation_graph->set_skeleton(skeleton_node->get_path());
|
synced_animation_graph->set_skeleton(skeleton_node->get_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_animations() {
|
void setup_animations() {
|
||||||
@ -81,16 +80,6 @@ struct BlendTreeFixture {
|
|||||||
|
|
||||||
animation_library->add_animation("TestAnimationB", test_animation_b);
|
animation_library->add_animation("TestAnimationB", test_animation_b);
|
||||||
|
|
||||||
test_animation_c = memnew(Animation);
|
|
||||||
track_index = test_animation_c->add_track(Animation::TYPE_POSITION_3D);
|
|
||||||
CHECK(track_index == 0);
|
|
||||||
test_animation_c->track_insert_key(track_index, 0.0, Vector3(0., 0., 0.));
|
|
||||||
test_animation_c->track_insert_key(track_index, 3.0, Vector3(2., 4., 6.));
|
|
||||||
test_animation_c->track_set_path(track_index, NodePath(vformat("%s:%s", skeleton_node->get_path().get_concatenated_names(), "Hips")));
|
|
||||||
test_animation_c->set_loop_mode(Animation::LOOP_LINEAR);
|
|
||||||
|
|
||||||
animation_library->add_animation("TestAnimationC", test_animation_c);
|
|
||||||
|
|
||||||
test_animation_sync_a = memnew(Animation);
|
test_animation_sync_a = memnew(Animation);
|
||||||
track_index = test_animation_sync_a->add_track(Animation::TYPE_POSITION_3D);
|
track_index = test_animation_sync_a->add_track(Animation::TYPE_POSITION_3D);
|
||||||
CHECK(track_index == 0);
|
CHECK(track_index == 0);
|
||||||
@ -123,8 +112,8 @@ struct BlendTreeFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void assign_scene_variables() {
|
void assign_scene_variables() {
|
||||||
animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
synced_animation_graph = dynamic_cast<BLTAnimationGraph *>(SceneTree::get_singleton()->get_root()->find_child("SyncedAnimationGraphFixtureTestNode", true, false));
|
||||||
REQUIRE(animation_graph);
|
REQUIRE(synced_animation_graph);
|
||||||
character_node = (SceneTree::get_singleton()->get_root()->find_child("CharacterNode", true, false));
|
character_node = (SceneTree::get_singleton()->get_root()->find_child("CharacterNode", true, false));
|
||||||
REQUIRE(character_node != nullptr);
|
REQUIRE(character_node != nullptr);
|
||||||
skeleton_node = dynamic_cast<Skeleton3D *>((SceneTree::get_singleton()->get_root()->find_child("Skeleton", true, false)));
|
skeleton_node = dynamic_cast<Skeleton3D *>((SceneTree::get_singleton()->get_root()->find_child("Skeleton", true, false)));
|
||||||
@ -150,34 +139,34 @@ struct BlendTreeFixture {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace TestBlendalotAnimationGraph {
|
namespace TestSyncedAnimationGraph {
|
||||||
|
|
||||||
TEST_CASE("[Blendalot][BlendTree] Test BlendTree construction") {
|
TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
||||||
BLTAnimationNodeBlendTree::BLTBlendTreeGraph tree_constructor;
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph tree_constructor;
|
||||||
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
||||||
animation_sampler_node0.instantiate();
|
animation_sampler_node0.instantiate();
|
||||||
animation_sampler_node0->set_name("Sampler0");
|
animation_sampler_node0->name = "Sampler0";
|
||||||
tree_constructor.add_node(animation_sampler_node0);
|
tree_constructor.add_node(animation_sampler_node0);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node1;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node1;
|
||||||
animation_sampler_node1.instantiate();
|
animation_sampler_node1.instantiate();
|
||||||
animation_sampler_node1->set_name("Sampler1");
|
animation_sampler_node1->name = "Sampler1";
|
||||||
tree_constructor.add_node(animation_sampler_node1);
|
tree_constructor.add_node(animation_sampler_node1);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node2;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node2;
|
||||||
animation_sampler_node2.instantiate();
|
animation_sampler_node2.instantiate();
|
||||||
animation_sampler_node2->set_name("Sampler2");
|
animation_sampler_node2->name = "Sampler2";
|
||||||
tree_constructor.add_node(animation_sampler_node2);
|
tree_constructor.add_node(animation_sampler_node2);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeBlend2> node_blend0;
|
Ref<BLTAnimationNodeBlend2> node_blend0;
|
||||||
node_blend0.instantiate();
|
node_blend0.instantiate();
|
||||||
node_blend0->set_name("Blend0");
|
node_blend0->name = "Blend0";
|
||||||
tree_constructor.add_node(node_blend0);
|
tree_constructor.add_node(node_blend0);
|
||||||
|
|
||||||
Ref<BLTAnimationNodeBlend2> node_blend1;
|
Ref<BLTAnimationNodeBlend2> node_blend1;
|
||||||
node_blend1.instantiate();
|
node_blend1.instantiate();
|
||||||
node_blend1->set_name("Blend1");
|
node_blend1->name = "Blend1";
|
||||||
tree_constructor.add_node(node_blend1);
|
tree_constructor.add_node(node_blend1);
|
||||||
|
|
||||||
// Tree
|
// Tree
|
||||||
@ -212,7 +201,7 @@ TEST_CASE("[Blendalot][BlendTree] Test BlendTree construction") {
|
|||||||
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));
|
||||||
|
|
||||||
// Perform remaining connections
|
// Perform remaining connections
|
||||||
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Output"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Input"));
|
||||||
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == 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:
|
||||||
@ -224,15 +213,15 @@ TEST_CASE("[Blendalot][BlendTree] 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 (i is part of the subtree)
|
// Check that for node i all input nodes have a node index j > i.
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] Test AnimationData blending") {
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] Test AnimationData blending") {
|
||||||
AnimationData data_t0;
|
AnimationData data_t0;
|
||||||
data_t0.allocate_track_values(test_animation_a, skeleton_node);
|
data_t0.allocate_track_values(test_animation_a, skeleton_node);
|
||||||
data_t0.sample_from_animation(test_animation_a, skeleton_node, 0.0);
|
data_t0.sample_from_animation(test_animation_a, skeleton_node, 0.0);
|
||||||
@ -267,12 +256,12 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] Test AnimationData b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] SyncedAnimationGraph evaluation with an AnimationSampler as root node") {
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph evaluation with an AnimationSampler as root node") {
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node;
|
||||||
animation_sampler_node.instantiate();
|
animation_sampler_node.instantiate();
|
||||||
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
animation_sampler_node->animation_name = "animation_library/TestAnimationA";
|
||||||
|
|
||||||
animation_graph->set_root_animation_node(animation_sampler_node);
|
synced_animation_graph->set_root_animation_node(animation_sampler_node);
|
||||||
|
|
||||||
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||||
|
|
||||||
@ -289,7 +278,7 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot] SyncedAnimationGraph
|
|||||||
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree] BlendTree evaluation with a AnimationSamplerNode connected to the output") {
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree] BlendTree evaluation with a AnimationSamplerNode connected to the output") {
|
||||||
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||||
synced_blend_tree_node.instantiate();
|
synced_blend_tree_node.instantiate();
|
||||||
|
|
||||||
@ -298,11 +287,11 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree] BlendTree
|
|||||||
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(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(animation_sampler_node, synced_blend_tree_node->get_output_node(), "Output"));
|
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(animation_graph->get_context());
|
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||||
|
|
||||||
animation_graph->set_root_animation_node(synced_blend_tree_node);
|
synced_animation_graph->set_root_animation_node(synced_blend_tree_node);
|
||||||
|
|
||||||
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||||
|
|
||||||
@ -319,7 +308,7 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree] BlendTree
|
|||||||
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
CHECK(hip_bone_position.z == doctest::Approx(0.03));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Node] BlendTree evaluation with a Blend2Node connected to the output") {
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][BlendTree][Blend2Node] BlendTree evaluation with a Blend2Node connected to the output") {
|
||||||
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||||
synced_blend_tree_node.instantiate();
|
synced_blend_tree_node.instantiate();
|
||||||
|
|
||||||
@ -340,19 +329,20 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
|||||||
// Blend2
|
// Blend2
|
||||||
Ref<BLTAnimationNodeBlend2> blend2_node;
|
Ref<BLTAnimationNodeBlend2> blend2_node;
|
||||||
blend2_node.instantiate();
|
blend2_node.instantiate();
|
||||||
blend2_node->set_name("Blend2");
|
blend2_node->name = "Blend2";
|
||||||
blend2_node->blend_weight = 0.5;
|
blend2_node->blend_weight = 0.5;
|
||||||
blend2_node->sync = false;
|
blend2_node->sync = false;
|
||||||
|
|
||||||
synced_blend_tree_node->add_node(blend2_node);
|
synced_blend_tree_node->add_node(blend2_node);
|
||||||
|
|
||||||
// Connect nodes
|
// Connect nodes
|
||||||
Vector<StringName> blend2_inputs = blend2_node->get_input_names();
|
Vector<StringName> blend2_inputs;
|
||||||
|
blend2_node->get_input_names(blend2_inputs);
|
||||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == 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(BLTAnimationNodeBlendTree::CONNECTION_OK == 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(BLTAnimationNodeBlendTree::CONNECTION_OK == synced_blend_tree_node->add_connection(blend2_node, synced_blend_tree_node->get_output_node(), "Output"));
|
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(animation_graph->get_context());
|
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||||
|
|
||||||
int blend2_node_index = synced_blend_tree_node->find_node_index(blend2_node);
|
int blend2_node_index = synced_blend_tree_node->find_node_index(blend2_node);
|
||||||
const BLTAnimationNodeBlendTree::NodeRuntimeData &blend2_runtime_data = synced_blend_tree_node->_node_runtime_data[blend2_node_index];
|
const BLTAnimationNodeBlendTree::NodeRuntimeData &blend2_runtime_data = synced_blend_tree_node->_node_runtime_data[blend2_node_index];
|
||||||
@ -360,7 +350,7 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
|||||||
CHECK(blend2_runtime_data.input_nodes[0] == animation_sampler_node_a);
|
CHECK(blend2_runtime_data.input_nodes[0] == animation_sampler_node_a);
|
||||||
CHECK(blend2_runtime_data.input_nodes[1] == animation_sampler_node_b);
|
CHECK(blend2_runtime_data.input_nodes[1] == animation_sampler_node_b);
|
||||||
|
|
||||||
animation_graph->set_root_animation_node(synced_blend_tree_node);
|
synced_animation_graph->set_root_animation_node(synced_blend_tree_node);
|
||||||
|
|
||||||
SUBCASE("Perform default evaluation") {
|
SUBCASE("Perform default evaluation") {
|
||||||
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
Vector3 hip_bone_position = skeleton_node->get_bone_global_pose(hip_bone_index).origin;
|
||||||
@ -398,9 +388,9 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
|||||||
animation_sampler_node_a->animation_name = "animation_library/TestAnimationSyncA";
|
animation_sampler_node_a->animation_name = "animation_library/TestAnimationSyncA";
|
||||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationSyncB";
|
animation_sampler_node_b->animation_name = "animation_library/TestAnimationSyncB";
|
||||||
blend2_node->sync = true;
|
blend2_node->sync = true;
|
||||||
synced_blend_tree_node->initialize(animation_graph->get_context());
|
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||||
|
|
||||||
REQUIRE(animation_graph->get_root_animation_node().ptr() == synced_blend_tree_node.ptr());
|
REQUIRE(synced_animation_graph->get_root_animation_node().ptr() == synced_blend_tree_node.ptr());
|
||||||
|
|
||||||
// By blending both animations we get a SyncTrack of duration 1.5s with the following
|
// By blending both animations we get a SyncTrack of duration 1.5s with the following
|
||||||
// intervals:
|
// intervals:
|
||||||
@ -447,13 +437,13 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
|||||||
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");
|
||||||
REQUIRE(loaded_synced_blend_tree.is_valid());
|
REQUIRE(loaded_synced_blend_tree.is_valid());
|
||||||
|
|
||||||
Ref<BLTAnimationNodeBlend2> loaded_blend2_node = loaded_synced_blend_tree->get_node_by_index(loaded_synced_blend_tree->find_node_index_by_name("Blend2"));
|
Ref<BLTAnimationNodeBlend2> loaded_blend2_node = loaded_synced_blend_tree->get_node(loaded_synced_blend_tree->find_node_index_by_name("Blend2"));
|
||||||
REQUIRE(loaded_blend2_node.is_valid());
|
REQUIRE(loaded_blend2_node.is_valid());
|
||||||
CHECK(loaded_blend2_node->sync == false);
|
CHECK(loaded_blend2_node->sync == false);
|
||||||
CHECK(loaded_blend2_node->blend_weight == blend2_node->blend_weight);
|
CHECK(loaded_blend2_node->blend_weight == blend2_node->blend_weight);
|
||||||
|
|
||||||
loaded_synced_blend_tree->initialize(animation_graph->get_context());
|
loaded_synced_blend_tree->initialize(synced_animation_graph->get_context());
|
||||||
animation_graph->set_root_animation_node(loaded_synced_blend_tree);
|
synced_animation_graph->set_root_animation_node(loaded_synced_blend_tree);
|
||||||
|
|
||||||
// Re-evaluate using a different time. All animation samplers will start again from 0.
|
// Re-evaluate using a different time. All animation samplers will start again from 0.
|
||||||
SceneTree::get_singleton()->process(0.2);
|
SceneTree::get_singleton()->process(0.2);
|
||||||
@ -466,266 +456,4 @@ TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTree][Blend2Nod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][ChangeConnectivity] BlendTreeGraph with various nodes and connections that are removed") {
|
} //namespace TestSyncedAnimationGraph
|
||||||
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, SamplerC
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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 the connection is not present anymore.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
SUBCASE("Removing the output node does nothing") {
|
|
||||||
int num_nodes = blend_tree_graph.nodes.size();
|
|
||||||
int num_connections = blend_tree_graph.connections.size();
|
|
||||||
CHECK(blend_tree_graph.remove_node(blend_tree_graph.get_output_node()) == false);
|
|
||||||
CHECK(blend_tree_graph.connections.size() == num_connections);
|
|
||||||
CHECK(blend_tree_graph.nodes.size() == num_nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("Remove a node with no children") {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("Remove a node with parent and children") {
|
|
||||||
int num_nodes = blend_tree_graph.nodes.size();
|
|
||||||
blend_tree_graph.remove_node(blend2_node_a);
|
|
||||||
blend_tree_graph.sort_nodes_and_references();
|
|
||||||
|
|
||||||
CHECK(blend_tree_graph.nodes.size() == num_nodes - 1);
|
|
||||||
|
|
||||||
for (const BLTBlendTreeConnection &connection : blend_tree_graph.connections) {
|
|
||||||
bool is_connection_with_removed_node = connection.source_node == blend2_node_a || connection.target_node == blend2_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 animation_sampler_node_c_index_post_remove = blend_tree_graph.find_node_index(animation_sampler_node_c);
|
|
||||||
int blend2_node_b_index_post_remove = blend_tree_graph.find_node_index(blend2_node_b);
|
|
||||||
|
|
||||||
CHECK(blend_tree_graph.find_node_index(blend2_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);
|
|
||||||
|
|
||||||
CHECK(blend_tree_graph.node_connection_info[0].input_subtree_node_indices.size() == 1);
|
|
||||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.size() == 3);
|
|
||||||
blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove]._print_subtree();
|
|
||||||
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));
|
|
||||||
CHECK(blend_tree_graph.node_connection_info[blend2_node_b_index_post_remove].input_subtree_node_indices.has(animation_sampler_node_c_index_post_remove));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("Check evaluation of graph with modified connections") {
|
|
||||||
Ref<BLTAnimationNodeBlendTree> blend_tree_node;
|
|
||||||
blend_tree_node.instantiate();
|
|
||||||
blend_tree_node->add_node(animation_sampler_node_a);
|
|
||||||
blend_tree_node->add_node(animation_sampler_node_b);
|
|
||||||
blend_tree_node->add_node(animation_sampler_node_c);
|
|
||||||
blend_tree_node->add_node(blend2_node_a);
|
|
||||||
|
|
||||||
animation_graph->set_root_animation_node(blend_tree_node);
|
|
||||||
GraphEvaluationContext &graph_context = animation_graph->get_context();
|
|
||||||
CHECK(blend_tree_node->initialize(graph_context) == false);
|
|
||||||
|
|
||||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(animation_sampler_node_a, blend_tree_node->get_output_node(), "Output"));
|
|
||||||
CHECK(blend_tree_node->initialize(graph_context) == true);
|
|
||||||
|
|
||||||
AnimationData *graph_output = graph_context.animation_data_allocator.allocate();
|
|
||||||
blend_tree_node->activate_inputs(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree_node->calculate_sync_track(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree_node->update_time(0.825);
|
|
||||||
blend_tree_node->evaluate(graph_context, LocalVector<AnimationData *>(), *graph_output);
|
|
||||||
|
|
||||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(animation_sampler_node_b, blend2_node_a, "Input0"));
|
|
||||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(animation_sampler_node_c, blend2_node_a, "Input1"));
|
|
||||||
|
|
||||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->remove_connection(animation_sampler_node_a, blend_tree_node->get_output_node(), "Output"));
|
|
||||||
|
|
||||||
REQUIRE(BLTAnimationNodeBlendTree::CONNECTION_OK == blend_tree_node->add_connection(blend2_node_a, blend_tree_node->get_output_node(), "Output"));
|
|
||||||
CHECK(blend_tree_node->initialize(graph_context) == true);
|
|
||||||
|
|
||||||
blend_tree_node->activate_inputs(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree_node->calculate_sync_track(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree_node->update_time(0.825);
|
|
||||||
blend_tree_node->evaluate(graph_context, LocalVector<AnimationData *>(), *graph_output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BlendTreeFixture, "[SceneTree][Blendalot][BlendTreeGraph][EmbeddedBlendTree] BlendTree with an embedded BlendTree subgraph") {
|
|
||||||
// Embedded BlendTree
|
|
||||||
Ref<BLTAnimationNodeBlendTree> embedded_blend_tree;
|
|
||||||
embedded_blend_tree.instantiate();
|
|
||||||
|
|
||||||
// TestAnimationB
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node_b;
|
|
||||||
animation_sampler_node_b.instantiate();
|
|
||||||
|
|
||||||
embedded_blend_tree->add_node(animation_sampler_node_b);
|
|
||||||
embedded_blend_tree->add_connection(animation_sampler_node_b, embedded_blend_tree->get_output_node(), "Output");
|
|
||||||
|
|
||||||
Ref<BLTAnimationNodeBlendTree> blend_tree;
|
|
||||||
blend_tree.instantiate();
|
|
||||||
|
|
||||||
// Blend2
|
|
||||||
Ref<BLTAnimationNodeBlend2> blend2;
|
|
||||||
blend2.instantiate();
|
|
||||||
blend2->set_name("Blend2");
|
|
||||||
blend_tree->add_node(blend2);
|
|
||||||
|
|
||||||
// TestAnimationA
|
|
||||||
Ref<BLTAnimationNodeSampler> animation_sampler_node_a;
|
|
||||||
animation_sampler_node_a.instantiate();
|
|
||||||
|
|
||||||
blend_tree->add_node(animation_sampler_node_a);
|
|
||||||
|
|
||||||
blend_tree->add_node(embedded_blend_tree);
|
|
||||||
|
|
||||||
blend_tree->add_connection(animation_sampler_node_a, blend2, "Input0");
|
|
||||||
blend_tree->add_connection(embedded_blend_tree, blend2, "Input1");
|
|
||||||
blend_tree->add_connection(blend2, blend_tree->get_output_node(), "Output");
|
|
||||||
|
|
||||||
SUBCASE("Perform regular blend") {
|
|
||||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationB";
|
|
||||||
animation_sampler_node_a->animation_name = "animation_library/TestAnimationA";
|
|
||||||
blend2->blend_weight = 0.5;
|
|
||||||
blend2->sync = false;
|
|
||||||
|
|
||||||
// Trigger initialization
|
|
||||||
animation_graph->set_root_animation_node(blend_tree);
|
|
||||||
GraphEvaluationContext &graph_context = animation_graph->get_context();
|
|
||||||
REQUIRE(blend_tree->initialize(graph_context));
|
|
||||||
|
|
||||||
// Perform evaluation
|
|
||||||
AnimationData *graph_output = graph_context.animation_data_allocator.allocate();
|
|
||||||
blend_tree->activate_inputs(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree->calculate_sync_track(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree->update_time(0.1);
|
|
||||||
blend_tree->evaluate(graph_context, LocalVector<AnimationData *>(), *graph_output);
|
|
||||||
|
|
||||||
// Check values
|
|
||||||
AnimationData::TransformTrackValue *hip_transform_value = graph_output->get_value<AnimationData::TransformTrackValue>(test_animation_a->get_tracks()[0]->thash);
|
|
||||||
CHECK(hip_transform_value->loc[0] == doctest::Approx(0.15));
|
|
||||||
CHECK(hip_transform_value->loc[1] == doctest::Approx(0.3));
|
|
||||||
CHECK(hip_transform_value->loc[2] == doctest::Approx(0.45));
|
|
||||||
}
|
|
||||||
SUBCASE("Perform synced blend") {
|
|
||||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationSyncA";
|
|
||||||
animation_sampler_node_a->animation_name = "animation_library/TestAnimationSyncB";
|
|
||||||
blend2->blend_weight = 0.5;
|
|
||||||
blend2->sync = true;
|
|
||||||
|
|
||||||
// Trigger initialization
|
|
||||||
animation_graph->set_root_animation_node(blend_tree);
|
|
||||||
GraphEvaluationContext &graph_context = animation_graph->get_context();
|
|
||||||
REQUIRE(blend_tree->initialize(graph_context));
|
|
||||||
|
|
||||||
// Perform evaluation
|
|
||||||
AnimationData *graph_output = graph_context.animation_data_allocator.allocate();
|
|
||||||
blend_tree->activate_inputs(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree->calculate_sync_track(Vector<Ref<BLTAnimationNode>>());
|
|
||||||
blend_tree->update_time(0.825);
|
|
||||||
blend_tree->evaluate(graph_context, LocalVector<AnimationData *>(), *graph_output);
|
|
||||||
|
|
||||||
// Check values
|
|
||||||
AnimationData::TransformTrackValue *hip_transform_value = graph_output->get_value<AnimationData::TransformTrackValue>(test_animation_a->get_tracks()[0]->thash);
|
|
||||||
CHECK(hip_transform_value->loc[0] == doctest::Approx(1.5));
|
|
||||||
CHECK(hip_transform_value->loc[1] == doctest::Approx(3.0));
|
|
||||||
CHECK(hip_transform_value->loc[2] == doctest::Approx(4.5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} //namespace TestBlendalotAnimationGraph
|
|
||||||
Loading…
x
Reference in New Issue
Block a user