Compare commits
2 Commits
ecf3b0fef2
...
625a3ae79f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
625a3ae79f | ||
|
|
0b6589f241 |
@ -1,37 +1,37 @@
|
|||||||
#include "synced_animation_graph.h"
|
#include "blendalot_animation_graph.h"
|
||||||
|
|
||||||
#include "core/os/time.h"
|
#include "core/os/time.h"
|
||||||
#include "core/profiling/profiling.h"
|
#include "core/profiling/profiling.h"
|
||||||
#include "scene/3d/skeleton_3d.h"
|
#include "scene/3d/skeleton_3d.h"
|
||||||
#include "scene/animation/animation_player.h"
|
#include "scene/animation/animation_player.h"
|
||||||
|
|
||||||
void SyncedAnimationGraph::_bind_methods() {
|
void BLTAnimationGraph::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_active", "active"), &SyncedAnimationGraph::set_active);
|
ClassDB::bind_method(D_METHOD("set_active", "active"), &BLTAnimationGraph::set_active);
|
||||||
ClassDB::bind_method(D_METHOD("is_active"), &SyncedAnimationGraph::is_active);
|
ClassDB::bind_method(D_METHOD("is_active"), &BLTAnimationGraph::is_active);
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_callback_mode_process", "mode"), &SyncedAnimationGraph::set_callback_mode_process);
|
ClassDB::bind_method(D_METHOD("set_callback_mode_process", "mode"), &BLTAnimationGraph::set_callback_mode_process);
|
||||||
ClassDB::bind_method(D_METHOD("get_callback_mode_process"), &SyncedAnimationGraph::get_callback_mode_process);
|
ClassDB::bind_method(D_METHOD("get_callback_mode_process"), &BLTAnimationGraph::get_callback_mode_process);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_callback_mode_method", "mode"), &SyncedAnimationGraph::set_callback_mode_method);
|
ClassDB::bind_method(D_METHOD("set_callback_mode_method", "mode"), &BLTAnimationGraph::set_callback_mode_method);
|
||||||
ClassDB::bind_method(D_METHOD("get_callback_mode_method"), &SyncedAnimationGraph::get_callback_mode_method);
|
ClassDB::bind_method(D_METHOD("get_callback_mode_method"), &BLTAnimationGraph::get_callback_mode_method);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_animation_player", "animation_player"), &SyncedAnimationGraph::set_animation_player);
|
ClassDB::bind_method(D_METHOD("set_animation_player", "animation_player"), &BLTAnimationGraph::set_animation_player);
|
||||||
ClassDB::bind_method(D_METHOD("get_animation_player"), &SyncedAnimationGraph::get_animation_player);
|
ClassDB::bind_method(D_METHOD("get_animation_player"), &BLTAnimationGraph::get_animation_player);
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");
|
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");
|
||||||
ADD_SIGNAL(MethodInfo(SNAME("animation_player_changed")));
|
ADD_SIGNAL(MethodInfo(SNAME("animation_player_changed")));
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_tree_root", "animation_node"), &SyncedAnimationGraph::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"), &SyncedAnimationGraph::get_root_animation_node);
|
ClassDB::bind_method(D_METHOD("get_tree_root"), &BLTAnimationGraph::get_root_animation_node);
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "SyncedAnimationNode"), "set_tree_root", "get_tree_root");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "SyncedAnimationNode"), "set_tree_root", "get_tree_root");
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &SyncedAnimationGraph::set_skeleton);
|
ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &BLTAnimationGraph::set_skeleton);
|
||||||
ClassDB::bind_method(D_METHOD("get_skeleton"), &SyncedAnimationGraph::get_skeleton);
|
ClassDB::bind_method(D_METHOD("get_skeleton"), &BLTAnimationGraph::get_skeleton);
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_skeleton", "get_skeleton");
|
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_skeleton", "get_skeleton");
|
||||||
ADD_SIGNAL(MethodInfo(SNAME("skeleton_changed")));
|
ADD_SIGNAL(MethodInfo(SNAME("skeleton_changed")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_update_properties_for_node(const String &p_base_path, Ref<SyncedAnimationNode> p_node) const {
|
void BLTAnimationGraph::_update_properties_for_node(const String &p_base_path, Ref<BLTAnimationNode> p_node) const {
|
||||||
ERR_FAIL_COND(p_node.is_null());
|
ERR_FAIL_COND(p_node.is_null());
|
||||||
|
|
||||||
List<PropertyInfo> plist;
|
List<PropertyInfo> plist;
|
||||||
@ -40,22 +40,22 @@ void SyncedAnimationGraph::_update_properties_for_node(const String &p_base_path
|
|||||||
StringName key = pinfo.name;
|
StringName key = pinfo.name;
|
||||||
|
|
||||||
if (!parameter_to_node_parameter_map.has(p_base_path + key)) {
|
if (!parameter_to_node_parameter_map.has(p_base_path + key)) {
|
||||||
parameter_to_node_parameter_map[p_base_path + key] = Pair<Ref<SyncedAnimationNode>, StringName>(p_node, key);
|
parameter_to_node_parameter_map[p_base_path + key] = Pair<Ref<BLTAnimationNode>, StringName>(p_node, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pinfo.name = p_base_path + key;
|
pinfo.name = p_base_path + key;
|
||||||
properties.push_back(pinfo);
|
properties.push_back(pinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Ref<SyncedAnimationNode>> children;
|
List<Ref<BLTAnimationNode>> children;
|
||||||
p_node->get_child_nodes(&children);
|
p_node->get_child_nodes(&children);
|
||||||
|
|
||||||
for (const Ref<SyncedAnimationNode> &child_node : children) {
|
for (const Ref<BLTAnimationNode> &child_node : children) {
|
||||||
_update_properties_for_node(p_base_path + child_node->name + "/", child_node);
|
_update_properties_for_node(p_base_path + child_node->name + "/", child_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_update_properties() const {
|
void BLTAnimationGraph::_update_properties() const {
|
||||||
if (!properties_dirty) {
|
if (!properties_dirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -69,10 +69,10 @@ void SyncedAnimationGraph::_update_properties() const {
|
|||||||
|
|
||||||
properties_dirty = false;
|
properties_dirty = false;
|
||||||
|
|
||||||
const_cast<SyncedAnimationGraph *>(this)->notify_property_list_changed();
|
const_cast<BLTAnimationGraph *>(this)->notify_property_list_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncedAnimationGraph::_set(const StringName &p_name, const Variant &p_value) {
|
bool BLTAnimationGraph::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
#ifndef DISABLE_DEPRECATED
|
#ifndef DISABLE_DEPRECATED
|
||||||
String name = p_name;
|
String name = p_name;
|
||||||
if (name == "process_callback") {
|
if (name == "process_callback") {
|
||||||
@ -85,7 +85,7 @@ bool SyncedAnimationGraph::_set(const StringName &p_name, const Variant &p_value
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parameter_to_node_parameter_map.has(p_name)) {
|
if (parameter_to_node_parameter_map.has(p_name)) {
|
||||||
const Pair<Ref<SyncedAnimationNode>, StringName> &property_node = parameter_to_node_parameter_map[p_name];
|
const Pair<Ref<BLTAnimationNode>, StringName> &property_node = parameter_to_node_parameter_map[p_name];
|
||||||
if (!property_node.first.is_valid()) {
|
if (!property_node.first.is_valid()) {
|
||||||
print_error(vformat("Cannot set property '%s' node not found.", p_name));
|
print_error(vformat("Cannot set property '%s' node not found.", p_name));
|
||||||
return false;
|
return false;
|
||||||
@ -98,7 +98,7 @@ bool SyncedAnimationGraph::_set(const StringName &p_name, const Variant &p_value
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncedAnimationGraph::_get(const StringName &p_name, Variant &r_ret) const {
|
bool BLTAnimationGraph::_get(const StringName &p_name, Variant &r_ret) const {
|
||||||
#ifndef DISABLE_DEPRECATED
|
#ifndef DISABLE_DEPRECATED
|
||||||
if (p_name == "process_callback") {
|
if (p_name == "process_callback") {
|
||||||
r_ret = get_callback_mode_process();
|
r_ret = get_callback_mode_process();
|
||||||
@ -110,7 +110,7 @@ bool SyncedAnimationGraph::_get(const StringName &p_name, Variant &r_ret) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parameter_to_node_parameter_map.has(p_name)) {
|
if (parameter_to_node_parameter_map.has(p_name)) {
|
||||||
const Pair<Ref<SyncedAnimationNode>, StringName> &property_node = parameter_to_node_parameter_map[p_name];
|
const Pair<Ref<BLTAnimationNode>, StringName> &property_node = parameter_to_node_parameter_map[p_name];
|
||||||
if (!property_node.first.is_valid()) {
|
if (!property_node.first.is_valid()) {
|
||||||
print_error(vformat("Cannot get property '%s' node not found.", p_name));
|
print_error(vformat("Cannot get property '%s' node not found.", p_name));
|
||||||
return false;
|
return false;
|
||||||
@ -122,7 +122,7 @@ bool SyncedAnimationGraph::_get(const StringName &p_name, Variant &r_ret) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_get_property_list(List<PropertyInfo> *p_list) const {
|
void BLTAnimationGraph::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
if (properties_dirty) {
|
if (properties_dirty) {
|
||||||
_update_properties();
|
_update_properties();
|
||||||
}
|
}
|
||||||
@ -132,16 +132,16 @@ void SyncedAnimationGraph::_get_property_list(List<PropertyInfo> *p_list) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_tree_changed() {
|
void BLTAnimationGraph::_tree_changed() {
|
||||||
if (properties_dirty) {
|
if (properties_dirty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callable_mp(this, &SyncedAnimationGraph::_update_properties).call_deferred();
|
callable_mp(this, &BLTAnimationGraph::_update_properties).call_deferred();
|
||||||
properties_dirty = true;
|
properties_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_notification(int p_what) {
|
void BLTAnimationGraph::_notification(int p_what) {
|
||||||
GodotProfileZone("SyncedAnimationGraph::_notification");
|
GodotProfileZone("SyncedAnimationGraph::_notification");
|
||||||
|
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
@ -177,7 +177,7 @@ void SyncedAnimationGraph::_notification(int p_what) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::set_active(bool p_active) {
|
void BLTAnimationGraph::set_active(bool p_active) {
|
||||||
if (active == p_active) {
|
if (active == p_active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -186,11 +186,11 @@ void SyncedAnimationGraph::set_active(bool p_active) {
|
|||||||
_set_process(processing, true);
|
_set_process(processing, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncedAnimationGraph::is_active() const {
|
bool BLTAnimationGraph::is_active() const {
|
||||||
return active;
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::set_callback_mode_process(AnimationMixer::AnimationCallbackModeProcess p_mode) {
|
void BLTAnimationGraph::set_callback_mode_process(AnimationMixer::AnimationCallbackModeProcess p_mode) {
|
||||||
if (callback_mode_process == p_mode) {
|
if (callback_mode_process == p_mode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -207,29 +207,29 @@ void SyncedAnimationGraph::set_callback_mode_process(AnimationMixer::AnimationCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationMixer::AnimationCallbackModeProcess SyncedAnimationGraph::get_callback_mode_process() const {
|
AnimationMixer::AnimationCallbackModeProcess BLTAnimationGraph::get_callback_mode_process() const {
|
||||||
return callback_mode_process;
|
return callback_mode_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::set_callback_mode_method(AnimationMixer::AnimationCallbackModeMethod p_mode) {
|
void BLTAnimationGraph::set_callback_mode_method(AnimationMixer::AnimationCallbackModeMethod p_mode) {
|
||||||
callback_mode_method = p_mode;
|
callback_mode_method = p_mode;
|
||||||
emit_signal(SNAME("mixer_updated"));
|
emit_signal(SNAME("mixer_updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationMixer::AnimationCallbackModeMethod SyncedAnimationGraph::get_callback_mode_method() const {
|
AnimationMixer::AnimationCallbackModeMethod BLTAnimationGraph::get_callback_mode_method() const {
|
||||||
return callback_mode_method;
|
return callback_mode_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode) {
|
void BLTAnimationGraph::set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode) {
|
||||||
callback_mode_discrete = p_mode;
|
callback_mode_discrete = p_mode;
|
||||||
emit_signal(SNAME("mixer_updated"));
|
emit_signal(SNAME("mixer_updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationMixer::AnimationCallbackModeDiscrete SyncedAnimationGraph::get_callback_mode_discrete() const {
|
AnimationMixer::AnimationCallbackModeDiscrete BLTAnimationGraph::get_callback_mode_discrete() const {
|
||||||
return callback_mode_discrete;
|
return callback_mode_discrete;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::set_animation_player(const NodePath &p_path) {
|
void BLTAnimationGraph::set_animation_player(const NodePath &p_path) {
|
||||||
animation_player_path = p_path;
|
animation_player_path = p_path;
|
||||||
if (p_path.is_empty()) {
|
if (p_path.is_empty()) {
|
||||||
// set_root_node(SceneStringName(path_pp));
|
// set_root_node(SceneStringName(path_pp));
|
||||||
@ -245,20 +245,20 @@ void SyncedAnimationGraph::set_animation_player(const NodePath &p_path) {
|
|||||||
emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor.
|
emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor.
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePath SyncedAnimationGraph::get_animation_player() const {
|
NodePath BLTAnimationGraph::get_animation_player() const {
|
||||||
return animation_player_path;
|
return animation_player_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::set_root_animation_node(const Ref<SyncedAnimationNode> &p_animation_node) {
|
void BLTAnimationGraph::set_root_animation_node(const Ref<BLTAnimationNode> &p_animation_node) {
|
||||||
if (root_animation_node.is_valid()) {
|
if (root_animation_node.is_valid()) {
|
||||||
root_animation_node->disconnect(SNAME("tree_changed"), callable_mp(this, &SyncedAnimationGraph::_tree_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("tree_changed"), callable_mp(this, &SyncedAnimationGraph::_tree_changed));
|
root_animation_node->connect(SNAME("tree_changed"), callable_mp(this, &BLTAnimationGraph::_tree_changed));
|
||||||
}
|
}
|
||||||
|
|
||||||
properties_dirty = true;
|
properties_dirty = true;
|
||||||
@ -266,11 +266,11 @@ void SyncedAnimationGraph::set_root_animation_node(const Ref<SyncedAnimationNode
|
|||||||
update_configuration_warnings();
|
update_configuration_warnings();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<SyncedAnimationNode> SyncedAnimationGraph::get_root_animation_node() const {
|
Ref<BLTAnimationNode> BLTAnimationGraph::get_root_animation_node() const {
|
||||||
return root_animation_node;
|
return root_animation_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::set_skeleton(const NodePath &p_path) {
|
void BLTAnimationGraph::set_skeleton(const NodePath &p_path) {
|
||||||
skeleton_path = p_path;
|
skeleton_path = p_path;
|
||||||
if (p_path.is_empty()) {
|
if (p_path.is_empty()) {
|
||||||
// set_root_node(SceneStringName(path_pp));
|
// set_root_node(SceneStringName(path_pp));
|
||||||
@ -286,11 +286,11 @@ void SyncedAnimationGraph::set_skeleton(const NodePath &p_path) {
|
|||||||
emit_signal(SNAME("skeleton_changed")); // Needs to unpin AnimationPlayerEditor.
|
emit_signal(SNAME("skeleton_changed")); // Needs to unpin AnimationPlayerEditor.
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePath SyncedAnimationGraph::get_skeleton() const {
|
NodePath BLTAnimationGraph::get_skeleton() const {
|
||||||
return skeleton_path;
|
return skeleton_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_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()) {
|
if (!root_animation_node.is_valid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -300,8 +300,8 @@ void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
|||||||
_update_properties();
|
_update_properties();
|
||||||
|
|
||||||
AnimationData *graph_output = graph_context.animation_data_allocator.allocate();
|
AnimationData *graph_output = graph_context.animation_data_allocator.allocate();
|
||||||
root_animation_node->activate_inputs(Vector<Ref<SyncedAnimationNode>>());
|
root_animation_node->activate_inputs(Vector<Ref<BLTAnimationNode>>());
|
||||||
root_animation_node->calculate_sync_track(Vector<Ref<SyncedAnimationNode>>());
|
root_animation_node->calculate_sync_track(Vector<Ref<BLTAnimationNode>>());
|
||||||
root_animation_node->update_time(p_delta);
|
root_animation_node->update_time(p_delta);
|
||||||
root_animation_node->evaluate(graph_context, LocalVector<AnimationData *>(), *graph_output);
|
root_animation_node->evaluate(graph_context, LocalVector<AnimationData *>(), *graph_output);
|
||||||
|
|
||||||
@ -310,7 +310,7 @@ void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
|||||||
graph_context.animation_data_allocator.free(graph_output);
|
graph_context.animation_data_allocator.free(graph_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_apply_animation_data(const AnimationData &output_data) const {
|
void BLTAnimationGraph::_apply_animation_data(const AnimationData &output_data) const {
|
||||||
GodotProfileZone("SyncedAnimationGraph::_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) {
|
||||||
@ -342,7 +342,7 @@ void SyncedAnimationGraph::_apply_animation_data(const AnimationData &output_dat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_set_process(bool p_process, bool p_force) {
|
void BLTAnimationGraph::_set_process(bool p_process, bool p_force) {
|
||||||
if (processing == p_process && !p_force) {
|
if (processing == p_process && !p_force) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -353,19 +353,19 @@ void SyncedAnimationGraph::_set_process(bool p_process, bool p_force) {
|
|||||||
processing = p_process;
|
processing = p_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_setup_evaluation_context() {
|
void BLTAnimationGraph::_setup_evaluation_context() {
|
||||||
_cleanup_evaluation_context();
|
_cleanup_evaluation_context();
|
||||||
|
|
||||||
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_cleanup_evaluation_context() {
|
void BLTAnimationGraph::_cleanup_evaluation_context() {
|
||||||
graph_context.animation_player = nullptr;
|
graph_context.animation_player = nullptr;
|
||||||
graph_context.skeleton_3d = nullptr;
|
graph_context.skeleton_3d = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_setup_graph() {
|
void BLTAnimationGraph::_setup_graph() {
|
||||||
if (graph_context.animation_player == nullptr || graph_context.skeleton_3d == nullptr || !root_animation_node.is_valid()) {
|
if (graph_context.animation_player == nullptr || graph_context.skeleton_3d == nullptr || !root_animation_node.is_valid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -373,5 +373,5 @@ void SyncedAnimationGraph::_setup_graph() {
|
|||||||
root_animation_node->initialize(graph_context);
|
root_animation_node->initialize(graph_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedAnimationGraph::SyncedAnimationGraph() {
|
BLTAnimationGraph::BLTAnimationGraph() {
|
||||||
}
|
}
|
||||||
@ -1,27 +1,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "blendalot_animation_node.h"
|
||||||
#include "scene/animation/animation_player.h"
|
#include "scene/animation/animation_player.h"
|
||||||
#include "synced_animation_node.h"
|
|
||||||
|
|
||||||
class Skeleton3D;
|
class Skeleton3D;
|
||||||
|
|
||||||
class SyncedAnimationGraph : public Node {
|
class BLTAnimationGraph : public Node {
|
||||||
GDCLASS(SyncedAnimationGraph, Node);
|
GDCLASS(BLTAnimationGraph, Node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodePath animation_player_path;
|
NodePath animation_player_path;
|
||||||
Ref<SyncedAnimationNode> root_animation_node;
|
Ref<BLTAnimationNode> root_animation_node;
|
||||||
NodePath skeleton_path;
|
NodePath skeleton_path;
|
||||||
|
|
||||||
GraphEvaluationContext graph_context = {};
|
GraphEvaluationContext graph_context = {};
|
||||||
|
|
||||||
mutable List<PropertyInfo> properties;
|
mutable List<PropertyInfo> properties;
|
||||||
mutable AHashMap<StringName, Pair<Ref<SyncedAnimationNode>, 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;
|
||||||
|
|
||||||
void _update_properties() const;
|
void _update_properties() const;
|
||||||
void _update_properties_for_node(const String &p_base_path, Ref<SyncedAnimationNode> p_node) const;
|
void _update_properties_for_node(const String &p_base_path, Ref<BLTAnimationNode> p_node) const;
|
||||||
|
|
||||||
void _tree_changed();
|
void _tree_changed();
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ public:
|
|||||||
void set_animation_player(const NodePath &p_path);
|
void set_animation_player(const NodePath &p_path);
|
||||||
NodePath get_animation_player() const;
|
NodePath get_animation_player() const;
|
||||||
|
|
||||||
void set_root_animation_node(const Ref<SyncedAnimationNode> &p_animation_node);
|
void set_root_animation_node(const Ref<BLTAnimationNode> &p_animation_node);
|
||||||
Ref<SyncedAnimationNode> get_root_animation_node() const;
|
Ref<BLTAnimationNode> get_root_animation_node() const;
|
||||||
|
|
||||||
void set_skeleton(const NodePath &p_path);
|
void set_skeleton(const NodePath &p_path);
|
||||||
NodePath get_skeleton() const;
|
NodePath get_skeleton() const;
|
||||||
@ -70,7 +70,7 @@ public:
|
|||||||
return graph_context;
|
return graph_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedAnimationGraph();
|
BLTAnimationGraph();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _set_process(bool p_process, bool p_force = false);
|
void _set_process(bool p_process, bool p_force = false);
|
||||||
@ -2,46 +2,61 @@
|
|||||||
// Created by martin on 03.12.25.
|
// Created by martin on 03.12.25.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "synced_animation_node.h"
|
#include "blendalot_animation_node.h"
|
||||||
|
|
||||||
void SyncedAnimationNode::_bind_methods() {
|
void BLTAnimationNode::_bind_methods() {
|
||||||
ADD_SIGNAL(MethodInfo("tree_changed"));
|
ADD_SIGNAL(MethodInfo("tree_changed"));
|
||||||
ADD_SIGNAL(MethodInfo("animation_node_renamed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
|
ADD_SIGNAL(MethodInfo("animation_node_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")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {
|
void BLTAnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant SyncedAnimationNode::get_parameter_default_value(const StringName &p_parameter) const {
|
Variant BLTAnimationNode::get_parameter_default_value(const StringName &p_parameter) const {
|
||||||
return Variant();
|
return Variant();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncedAnimationNode::is_parameter_read_only(const StringName &p_parameter) const {
|
bool BLTAnimationNode::is_parameter_read_only(const StringName &p_parameter) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) {
|
void BLTAnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant SyncedAnimationNode::get_parameter(const StringName &p_name) const {
|
Variant BLTAnimationNode::get_parameter(const StringName &p_name) const {
|
||||||
return Variant();
|
return Variant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationNode::_tree_changed() {
|
void BLTAnimationNode::_tree_changed() {
|
||||||
emit_signal(SNAME("tree_changed"));
|
emit_signal(SNAME("tree_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationNode::_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) {
|
||||||
emit_signal(SNAME("animation_node_renamed"), p_oid, p_old_name, p_new_name);
|
emit_signal(SNAME("animation_node_renamed"), p_oid, p_old_name, p_new_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationNode::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
void BLTAnimationNode::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
|
||||||
emit_signal(SNAME("animation_node_removed"), p_oid, p_node);
|
emit_signal(SNAME("animation_node_removed"), p_oid, p_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
void BLTAnimationNodeBlendTree::_bind_methods() {
|
||||||
for (const Ref<SyncedAnimationNode> &node : tree_graph.nodes) {
|
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;
|
String prop_name = node->name;
|
||||||
if (prop_name != "Output") {
|
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::OBJECT, "nodes/" + prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
|
||||||
@ -52,7 +67,7 @@ void SyncedBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||||||
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncedBlendTree::_get(const StringName &p_name, Variant &r_value) const {
|
bool BLTAnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_value) const {
|
||||||
String prop_name = p_name;
|
String prop_name = p_name;
|
||||||
if (prop_name.begins_with("nodes/")) {
|
if (prop_name.begins_with("nodes/")) {
|
||||||
String node_name = prop_name.get_slicec('/', 1);
|
String node_name = prop_name.get_slicec('/', 1);
|
||||||
@ -77,7 +92,7 @@ bool SyncedBlendTree::_get(const StringName &p_name, Variant &r_value) const {
|
|||||||
conns.resize(tree_graph.connections.size() * 3);
|
conns.resize(tree_graph.connections.size() * 3);
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (const BlendTreeConnection &connection : tree_graph.connections) {
|
for (const BLTBlendTreeConnection &connection : tree_graph.connections) {
|
||||||
conns[idx * 3 + 0] = connection.target_node->name;
|
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 + 1] = connection.target_node->get_input_index(connection.target_port_name);
|
||||||
conns[idx * 3 + 2] = connection.source_node->name;
|
conns[idx * 3 + 2] = connection.source_node->name;
|
||||||
@ -91,14 +106,14 @@ bool SyncedBlendTree::_get(const StringName &p_name, Variant &r_value) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyncedBlendTree::_set(const StringName &p_name, const Variant &p_value) {
|
bool BLTAnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
String prop_name = p_name;
|
String prop_name = p_name;
|
||||||
if (prop_name.begins_with("nodes/")) {
|
if (prop_name.begins_with("nodes/")) {
|
||||||
String node_name = prop_name.get_slicec('/', 1);
|
String node_name = prop_name.get_slicec('/', 1);
|
||||||
String what = prop_name.get_slicec('/', 2);
|
String what = prop_name.get_slicec('/', 2);
|
||||||
|
|
||||||
if (what == "node") {
|
if (what == "node") {
|
||||||
Ref<SyncedAnimationNode> anode = p_value;
|
Ref<BLTAnimationNode> anode = p_value;
|
||||||
if (anode.is_valid()) {
|
if (anode.is_valid()) {
|
||||||
anode->name = node_name;
|
anode->name = node_name;
|
||||||
add_node(anode);
|
add_node(anode);
|
||||||
@ -122,7 +137,7 @@ bool SyncedBlendTree::_set(const StringName &p_name, const Variant &p_value) {
|
|||||||
int target_node_port_index = conns[i + 1];
|
int target_node_port_index = conns[i + 1];
|
||||||
int source_node_index = find_node_index_by_name(conns[i + 2]);
|
int source_node_index = find_node_index_by_name(conns[i + 2]);
|
||||||
|
|
||||||
Ref<SyncedAnimationNode> target_node = tree_graph.nodes[target_node_index];
|
Ref<BLTAnimationNode> target_node = tree_graph.nodes[target_node_index];
|
||||||
Vector<StringName> target_input_names;
|
Vector<StringName> target_input_names;
|
||||||
target_node->get_input_names(target_input_names);
|
target_node->get_input_names(target_input_names);
|
||||||
|
|
||||||
@ -239,8 +254,8 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationSamplerNode::initialize(GraphEvaluationContext &context) {
|
bool BLTAnimationNodeSampler::initialize(GraphEvaluationContext &context) {
|
||||||
SyncedAnimationNode::initialize(context);
|
BLTAnimationNode::initialize(context);
|
||||||
|
|
||||||
animation = context.animation_player->get_animation(animation_name);
|
animation = context.animation_player->get_animation(animation_name);
|
||||||
if (!animation.is_valid()) {
|
if (!animation.is_valid()) {
|
||||||
@ -271,8 +286,8 @@ bool AnimationSamplerNode::initialize(GraphEvaluationContext &context) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationSamplerNode::update_time(double p_time) {
|
void BLTAnimationNodeSampler::update_time(double p_time) {
|
||||||
SyncedAnimationNode::update_time(p_time);
|
BLTAnimationNode::update_time(p_time);
|
||||||
|
|
||||||
if (node_time_info.is_synced) {
|
if (node_time_info.is_synced) {
|
||||||
// Any potential looping has already been performed in the sync-controlling node.
|
// Any potential looping has already been performed in the sync-controlling node.
|
||||||
@ -290,7 +305,7 @@ void AnimationSamplerNode::update_time(double p_time) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
void BLTAnimationNodeSampler::evaluate(GraphEvaluationContext &context, const LocalVector<AnimationData *> &inputs, AnimationData &output) {
|
||||||
GodotProfileZone("AnimationSamplerNode::evaluate");
|
GodotProfileZone("AnimationSamplerNode::evaluate");
|
||||||
|
|
||||||
assert(inputs.size() == 0);
|
assert(inputs.size() == 0);
|
||||||
@ -302,58 +317,58 @@ void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const Local
|
|||||||
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 AnimationSamplerNode::set_animation(const StringName &p_name) {
|
void BLTAnimationNodeSampler::set_animation(const StringName &p_name) {
|
||||||
animation_name = p_name;
|
animation_name = p_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringName AnimationSamplerNode::get_animation() const {
|
StringName BLTAnimationNodeSampler::get_animation() const {
|
||||||
return animation_name;
|
return animation_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationSamplerNode::_bind_methods() {
|
void BLTAnimationNodeSampler::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationSamplerNode::set_animation);
|
ClassDB::bind_method(D_METHOD("set_animation", "name"), &BLTAnimationNodeSampler::set_animation);
|
||||||
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationSamplerNode::get_animation);
|
ClassDB::bind_method(D_METHOD("get_animation"), &BLTAnimationNodeSampler::get_animation);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
|
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationBlend2Node::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");
|
||||||
|
|
||||||
output = std::move(*inputs[0]);
|
output = std::move(*inputs[0]);
|
||||||
output.blend(*inputs[1], blend_weight);
|
output.blend(*inputs[1], blend_weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationBlend2Node::set_use_sync(bool p_sync) {
|
void BLTAnimationNodeBlend2::set_use_sync(bool p_sync) {
|
||||||
sync = p_sync;
|
sync = p_sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationBlend2Node::is_using_sync() const {
|
bool BLTAnimationNodeBlend2::is_using_sync() const {
|
||||||
return sync;
|
return sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationBlend2Node::_bind_methods() {
|
void BLTAnimationNodeBlend2::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationBlend2Node::set_use_sync);
|
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &BLTAnimationNodeBlend2::set_use_sync);
|
||||||
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationBlend2Node::is_using_sync);
|
ClassDB::bind_method(D_METHOD("is_using_sync"), &BLTAnimationNodeBlend2::is_using_sync);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationBlend2Node::get_parameter_list(List<PropertyInfo> *p_list) const {
|
void BLTAnimationNodeBlend2::get_parameter_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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationBlend2Node::set_parameter(const StringName &p_name, const Variant &p_value) {
|
void BLTAnimationNodeBlend2::set_parameter(const StringName &p_name, const Variant &p_value) {
|
||||||
_set(p_name, p_value);
|
_set(p_name, p_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant AnimationBlend2Node::get_parameter(const StringName &p_name) const {
|
Variant BLTAnimationNodeBlend2::get_parameter(const StringName &p_name) const {
|
||||||
Variant result;
|
Variant result;
|
||||||
_get(p_name, result);
|
_get(p_name, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant AnimationBlend2Node::get_parameter_default_value(const StringName &p_parameter) const {
|
Variant BLTAnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const {
|
||||||
if (p_parameter == blend_weight_pname) {
|
if (p_parameter == blend_weight_pname) {
|
||||||
return blend_weight;
|
return blend_weight;
|
||||||
}
|
}
|
||||||
@ -361,12 +376,12 @@ Variant AnimationBlend2Node::get_parameter_default_value(const StringName &p_par
|
|||||||
return Variant();
|
return Variant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationBlend2Node::_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));
|
p_list->push_back(PropertyInfo(Variant::BOOL, sync_pname));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationBlend2Node::_get(const StringName &p_name, Variant &r_value) const {
|
bool BLTAnimationNodeBlend2::_get(const StringName &p_name, Variant &r_value) const {
|
||||||
if (p_name == blend_weight_pname) {
|
if (p_name == blend_weight_pname) {
|
||||||
r_value = blend_weight;
|
r_value = blend_weight;
|
||||||
return true;
|
return true;
|
||||||
@ -380,7 +395,7 @@ bool AnimationBlend2Node::_get(const StringName &p_name, Variant &r_value) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationBlend2Node::_set(const StringName &p_name, const Variant &p_value) {
|
bool BLTAnimationNodeBlend2::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
if (p_name == blend_weight_pname) {
|
if (p_name == blend_weight_pname) {
|
||||||
blend_weight = p_value;
|
blend_weight = p_value;
|
||||||
return true;
|
return true;
|
||||||
@ -392,4 +407,163 @@ bool AnimationBlend2Node::_set(const StringName &p_name, const Variant &p_value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph::BLTBlendTreeGraph() {
|
||||||
|
Ref<BLTAnimationNodeOutput> output_node;
|
||||||
|
output_node.instantiate();
|
||||||
|
output_node->name = "Output";
|
||||||
|
add_node(output_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<BLTAnimationNode> BLTAnimationNodeBlendTree::BLTBlendTreeGraph::get_output_node() {
|
||||||
|
return nodes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index(const Ref<BLTAnimationNode> &node) const {
|
||||||
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
|
if (nodes[i] == node) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLTAnimationNodeBlendTree::BLTBlendTreeGraph::find_node_index_by_name(const StringName &name) const {
|
||||||
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
|
if (nodes[i]->name == name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_node(const Ref<BLTAnimationNode> &node) {
|
||||||
|
StringName node_base_name = node->name;
|
||||||
|
if (node_base_name.is_empty()) {
|
||||||
|
node_base_name = node->get_class_name();
|
||||||
|
}
|
||||||
|
node->name = node_base_name;
|
||||||
|
|
||||||
|
int number_suffix = 1;
|
||||||
|
while (find_node_index_by_name(node->name) != -1) {
|
||||||
|
node->name = vformat("%s %d", node_base_name, number_suffix);
|
||||||
|
number_suffix++;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.push_back(node);
|
||||||
|
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_and_references() {
|
||||||
|
LocalVector<int> sorted_node_indices = get_sorted_node_indices();
|
||||||
|
|
||||||
|
Vector<Ref<BLTAnimationNode>> sorted_nodes;
|
||||||
|
LocalVector<NodeConnectionInfo> old_node_connection_info = node_connection_info;
|
||||||
|
for (unsigned int i = 0; i < sorted_node_indices.size(); i++) {
|
||||||
|
int node_index = sorted_node_indices[i];
|
||||||
|
sorted_nodes.push_back(nodes[node_index]);
|
||||||
|
node_connection_info[i] = old_node_connection_info[node_index];
|
||||||
|
}
|
||||||
|
nodes = sorted_nodes;
|
||||||
|
|
||||||
|
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
||||||
|
if (connection_info.parent_node_index != -1) {
|
||||||
|
connection_info.parent_node_index = sorted_node_indices[connection_info.parent_node_index];
|
||||||
|
}
|
||||||
|
connection_info.apply_node_mapping(sorted_node_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalVector<int> BLTAnimationNodeBlendTree::BLTBlendTreeGraph::get_sorted_node_indices() {
|
||||||
|
LocalVector<int> result;
|
||||||
|
|
||||||
|
sort_nodes_recursive(0, result);
|
||||||
|
result.reverse();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::sort_nodes_recursive(int node_index, LocalVector<int> &result) {
|
||||||
|
for (int input_node_index : node_connection_info[node_index].connected_child_node_index_at_port) {
|
||||||
|
if (input_node_index >= 0) {
|
||||||
|
sort_nodes_recursive(input_node_index, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push_back(node_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_index_and_update_subtrees_recursive(int node, int node_parent) {
|
||||||
|
if (node_parent == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_connection_info[node_parent].input_subtree_node_indices.insert(node);
|
||||||
|
|
||||||
|
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
||||||
|
node_connection_info[node_parent].input_subtree_node_indices.insert(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].parent_node_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLTAnimationNodeBlendTree::ConnectionError BLTAnimationNodeBlendTree::BLTBlendTreeGraph::add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name) {
|
||||||
|
ConnectionError result = is_connection_valid(source_node, target_node, target_port_name);
|
||||||
|
if (result != CONNECTION_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int source_node_index = find_node_index(source_node);
|
||||||
|
int target_node_index = find_node_index(target_node);
|
||||||
|
int target_input_port_index = target_node->get_input_index(target_port_name);
|
||||||
|
|
||||||
|
node_connection_info[source_node_index].parent_node_index = target_node_index;
|
||||||
|
node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] = source_node_index;
|
||||||
|
connections.push_back(BLTBlendTreeConnection{ source_node, target_node, target_port_name });
|
||||||
|
|
||||||
|
add_index_and_update_subtrees_recursive(source_node_index, target_node_index);
|
||||||
|
|
||||||
|
return CONNECTION_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(target_inputs);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@ -227,13 +227,13 @@ struct GraphEvaluationContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class SyncedAnimationNode
|
* @class BLTAnimationNode
|
||||||
* Base class for all nodes in an SyncedAnimationGraph including BlendTree nodes and StateMachine states.
|
* Base class for all nodes in an SyncedAnimationGraph including BlendTree nodes and StateMachine states.
|
||||||
*/
|
*/
|
||||||
class SyncedAnimationNode : public Resource {
|
class BLTAnimationNode : public Resource {
|
||||||
GDCLASS(SyncedAnimationNode, Resource);
|
GDCLASS(BLTAnimationNode, Resource);
|
||||||
|
|
||||||
friend class SyncedAnimationGraph;
|
friend class BLTAnimationGraph;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
@ -265,20 +265,20 @@ public:
|
|||||||
StringName name;
|
StringName name;
|
||||||
Vector2 position;
|
Vector2 position;
|
||||||
|
|
||||||
virtual ~SyncedAnimationNode() override = default;
|
virtual ~BLTAnimationNode() override = default;
|
||||||
virtual bool initialize(GraphEvaluationContext &context) {
|
virtual bool initialize(GraphEvaluationContext &context) {
|
||||||
node_time_info = {};
|
node_time_info = {};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void activate_inputs(const Vector<Ref<SyncedAnimationNode>> &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<SyncedAnimationNode> &node : input_nodes) {
|
for (const Ref<BLTAnimationNode> &node : input_nodes) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void calculate_sync_track(const Vector<Ref<SyncedAnimationNode>> &input_nodes) {
|
virtual void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) {
|
||||||
// By default, use the SyncTrack of the first input.
|
// By default, use the SyncTrack of the first input.
|
||||||
if (input_nodes.size() > 0) {
|
if (input_nodes.size() > 0) {
|
||||||
node_time_info.sync_track = input_nodes[0]->node_time_info.sync_track;
|
node_time_info.sync_track = input_nodes[0]->node_time_info.sync_track;
|
||||||
@ -314,11 +314,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates a list of nodes nested within the current node. E.g. all nodes within a BlendTree node.
|
// Creates a list of nodes nested within the current node. E.g. all nodes within a BlendTree node.
|
||||||
virtual void get_child_nodes(List<Ref<SyncedAnimationNode>> *r_child_nodes) const {}
|
virtual void get_child_nodes(List<Ref<BLTAnimationNode>> *r_child_nodes) const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimationSamplerNode : public SyncedAnimationNode {
|
class BLTAnimationNodeSampler : public BLTAnimationNode {
|
||||||
GDCLASS(AnimationSamplerNode, SyncedAnimationNode);
|
GDCLASS(BLTAnimationNodeSampler, BLTAnimationNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StringName animation_name;
|
StringName animation_name;
|
||||||
@ -337,8 +337,8 @@ protected:
|
|||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
};
|
};
|
||||||
|
|
||||||
class OutputNode : public SyncedAnimationNode {
|
class BLTAnimationNodeOutput : public BLTAnimationNode {
|
||||||
GDCLASS(OutputNode, SyncedAnimationNode);
|
GDCLASS(BLTAnimationNodeOutput, BLTAnimationNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void get_input_names(Vector<StringName> &inputs) const override {
|
void get_input_names(Vector<StringName> &inputs) const override {
|
||||||
@ -346,8 +346,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimationBlend2Node : public SyncedAnimationNode {
|
class BLTAnimationNodeBlend2 : public BLTAnimationNode {
|
||||||
GDCLASS(AnimationBlend2Node, SyncedAnimationNode);
|
GDCLASS(BLTAnimationNodeBlend2, BLTAnimationNode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float blend_weight = 0.0f;
|
float blend_weight = 0.0f;
|
||||||
@ -359,7 +359,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool initialize(GraphEvaluationContext &context) override {
|
bool initialize(GraphEvaluationContext &context) override {
|
||||||
bool result = SyncedAnimationNode::initialize(context);
|
bool result = BLTAnimationNode::initialize(context);
|
||||||
|
|
||||||
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?
|
||||||
@ -368,7 +368,7 @@ public:
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
void activate_inputs(const Vector<Ref<SyncedAnimationNode>> &input_nodes) override {
|
void activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
input_nodes[0]->active = true;
|
input_nodes[0]->active = true;
|
||||||
input_nodes[1]->active = true;
|
input_nodes[1]->active = true;
|
||||||
|
|
||||||
@ -377,7 +377,7 @@ public:
|
|||||||
input_nodes[1]->node_time_info.is_synced = node_time_info.is_synced || sync;
|
input_nodes[1]->node_time_info.is_synced = node_time_info.is_synced || sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculate_sync_track(const Vector<Ref<SyncedAnimationNode>> &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) {
|
||||||
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);
|
||||||
@ -385,7 +385,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update_time(double p_delta) override {
|
void update_time(double p_delta) override {
|
||||||
SyncedAnimationNode::update_time(p_delta);
|
BLTAnimationNode::update_time(p_delta);
|
||||||
|
|
||||||
if (sync && !node_time_info.is_synced) {
|
if (sync && !node_time_info.is_synced) {
|
||||||
if (node_time_info.loop_mode != Animation::LOOP_NONE) {
|
if (node_time_info.loop_mode != Animation::LOOP_NONE) {
|
||||||
@ -422,228 +422,86 @@ private:
|
|||||||
StringName sync_pname = PNAME("sync");
|
StringName sync_pname = PNAME("sync");
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlendTreeConnection {
|
struct BLTBlendTreeConnection {
|
||||||
const Ref<SyncedAnimationNode> source_node = nullptr;
|
const Ref<BLTAnimationNode> source_node = nullptr;
|
||||||
const Ref<SyncedAnimationNode> target_node = nullptr;
|
const Ref<BLTAnimationNode> target_node = nullptr;
|
||||||
const StringName target_port_name = "";
|
const StringName target_port_name = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
class BLTAnimationNodeBlendTree : public BLTAnimationNode {
|
||||||
* @class BlendTreeGraph
|
GDCLASS(BLTAnimationNodeBlendTree, BLTAnimationNode);
|
||||||
* Helper class that is used to build runtime blend trees and also to validate connections.
|
|
||||||
*/
|
|
||||||
struct BlendTreeGraph {
|
|
||||||
struct NodeConnectionInfo {
|
|
||||||
int parent_node_index = -1;
|
|
||||||
HashSet<int> input_subtree_node_indices; // Contains all nodes down to the tree leaves that influence this node.
|
|
||||||
LocalVector<int> connected_child_node_index_at_port; // Contains for each input port the index of the node that is connected to it.
|
|
||||||
|
|
||||||
NodeConnectionInfo() = default;
|
public:
|
||||||
|
enum ConnectionError {
|
||||||
explicit NodeConnectionInfo(const SyncedAnimationNode *node) {
|
CONNECTION_OK,
|
||||||
parent_node_index = -1;
|
CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED,
|
||||||
for (int i = 0; i < node->get_input_count(); i++) {
|
CONNECTION_ERROR_NO_SOURCE_NODE,
|
||||||
connected_child_node_index_at_port.push_back(-1);
|
CONNECTION_ERROR_NO_TARGET_NODE,
|
||||||
}
|
CONNECTION_ERROR_PARENT_EXISTS,
|
||||||
}
|
CONNECTION_ERROR_TARGET_PORT_NOT_FOUND,
|
||||||
|
CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED,
|
||||||
void _print_subtree() const {
|
CONNECTION_ERROR_CONNECTION_CREATES_LOOP,
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply_node_mapping(const LocalVector<int> &node_index_mapping) {
|
|
||||||
// Map connected node indices
|
|
||||||
for (unsigned int j = 0; j < connected_child_node_index_at_port.size(); j++) {
|
|
||||||
int connected_node_index = connected_child_node_index_at_port[j];
|
|
||||||
connected_child_node_index_at_port[j] = node_index_mapping.find(connected_node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map connected subtrees
|
|
||||||
HashSet<int> old_indices = input_subtree_node_indices;
|
|
||||||
input_subtree_node_indices.clear();
|
|
||||||
for (int old_index : old_indices) {
|
|
||||||
input_subtree_node_indices.insert(node_index_mapping.find(old_index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector<Ref<SyncedAnimationNode>> nodes; // All added nodes
|
/**
|
||||||
LocalVector<NodeConnectionInfo> node_connection_info;
|
* @class BLTBlendTreeGraph
|
||||||
LocalVector<BlendTreeConnection> connections;
|
* Helper class that is used to build runtime blend trees and also to validate connections.
|
||||||
|
*/
|
||||||
|
struct BLTBlendTreeGraph {
|
||||||
|
struct NodeConnectionInfo {
|
||||||
|
int parent_node_index = -1;
|
||||||
|
HashSet<int> input_subtree_node_indices; // Contains all nodes down to the tree leaves that influence this node.
|
||||||
|
LocalVector<int> connected_child_node_index_at_port; // Contains for each input port the index of the node that is connected to it.
|
||||||
|
|
||||||
BlendTreeGraph() {
|
NodeConnectionInfo() = default;
|
||||||
Ref<OutputNode> output_node;
|
|
||||||
output_node.instantiate();
|
|
||||||
output_node->name = "Output";
|
|
||||||
add_node(output_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SyncedAnimationNode> get_output_node() const {
|
explicit NodeConnectionInfo(const BLTAnimationNode *node) {
|
||||||
return nodes[0];
|
parent_node_index = -1;
|
||||||
}
|
for (int i = 0; i < node->get_input_count(); i++) {
|
||||||
|
connected_child_node_index_at_port.push_back(-1);
|
||||||
int find_node_index(const Ref<SyncedAnimationNode> &node) const {
|
}
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
|
||||||
if (nodes[i] == node) {
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
void apply_node_mapping(const LocalVector<int> &node_index_mapping) {
|
||||||
}
|
// Map connected node indices
|
||||||
|
for (unsigned int j = 0; j < connected_child_node_index_at_port.size(); j++) {
|
||||||
|
int connected_node_index = connected_child_node_index_at_port[j];
|
||||||
|
connected_child_node_index_at_port[j] = node_index_mapping.find(connected_node_index);
|
||||||
|
}
|
||||||
|
|
||||||
int find_node_index_by_name(const StringName &name) const {
|
// Map connected subtrees
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
HashSet<int> old_indices = input_subtree_node_indices;
|
||||||
if (nodes[i]->name == name) {
|
input_subtree_node_indices.clear();
|
||||||
return i;
|
for (int old_index : old_indices) {
|
||||||
|
input_subtree_node_indices.insert(node_index_mapping.find(old_index));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
void _print_subtree() const;
|
||||||
}
|
};
|
||||||
|
|
||||||
void add_node(const Ref<SyncedAnimationNode> &node) {
|
Vector<Ref<BLTAnimationNode>> nodes; // All added nodes
|
||||||
StringName node_base_name = node->name;
|
LocalVector<NodeConnectionInfo> node_connection_info;
|
||||||
if (node_base_name.is_empty()) {
|
LocalVector<BLTBlendTreeConnection> connections;
|
||||||
node_base_name = node->get_class_name();
|
|
||||||
}
|
|
||||||
node->name = node_base_name;
|
|
||||||
|
|
||||||
int number_suffix = 1;
|
BLTBlendTreeGraph();
|
||||||
while (find_node_index_by_name(node->name) != -1) {
|
|
||||||
node->name = vformat("%s %d", node_base_name, number_suffix);
|
|
||||||
number_suffix++;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.push_back(node);
|
Ref<BLTAnimationNode> get_output_node();
|
||||||
node_connection_info.push_back(NodeConnectionInfo(node.ptr()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort_nodes_and_references() {
|
int find_node_index(const Ref<BLTAnimationNode> &node) const;
|
||||||
LocalVector<int> sorted_node_indices = get_sorted_node_indices();
|
int find_node_index_by_name(const StringName &name) const;
|
||||||
|
void sort_nodes_and_references();
|
||||||
|
LocalVector<int> get_sorted_node_indices();
|
||||||
|
void sort_nodes_recursive(int node_index, LocalVector<int> &result);
|
||||||
|
void add_index_and_update_subtrees_recursive(int node, int node_parent);
|
||||||
|
ConnectionError is_connection_valid(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, StringName target_port_name) const;
|
||||||
|
|
||||||
Vector<Ref<SyncedAnimationNode>> sorted_nodes;
|
void add_node(const Ref<BLTAnimationNode> &node);
|
||||||
LocalVector<NodeConnectionInfo> old_node_connection_info = node_connection_info;
|
ConnectionError add_connection(const Ref<BLTAnimationNode> &source_node, const Ref<BLTAnimationNode> &target_node, const StringName &target_port_name);
|
||||||
for (unsigned int i = 0; i < sorted_node_indices.size(); i++) {
|
};
|
||||||
int node_index = sorted_node_indices[i];
|
|
||||||
sorted_nodes.push_back(nodes[node_index]);
|
|
||||||
node_connection_info[i] = old_node_connection_info[node_index];
|
|
||||||
}
|
|
||||||
nodes = sorted_nodes;
|
|
||||||
|
|
||||||
for (NodeConnectionInfo &connection_info : node_connection_info) {
|
private:
|
||||||
if (connection_info.parent_node_index != -1) {
|
BLTBlendTreeGraph tree_graph;
|
||||||
connection_info.parent_node_index = sorted_node_indices[connection_info.parent_node_index];
|
|
||||||
}
|
|
||||||
connection_info.apply_node_mapping(sorted_node_indices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalVector<int> get_sorted_node_indices() {
|
|
||||||
LocalVector<int> result;
|
|
||||||
|
|
||||||
sort_nodes_recursive(0, result);
|
|
||||||
result.reverse();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort_nodes_recursive(int node_index, LocalVector<int> &result) {
|
|
||||||
for (int input_node_index : node_connection_info[node_index].connected_child_node_index_at_port) {
|
|
||||||
if (input_node_index >= 0) {
|
|
||||||
sort_nodes_recursive(input_node_index, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.push_back(node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_index_and_update_subtrees_recursive(int node, int node_parent) {
|
|
||||||
if (node_parent == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_connection_info[node_parent].input_subtree_node_indices.insert(node);
|
|
||||||
|
|
||||||
for (int index : node_connection_info[node].input_subtree_node_indices) {
|
|
||||||
node_connection_info[node_parent].input_subtree_node_indices.insert(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_index_and_update_subtrees_recursive(node_parent, node_connection_info[node_parent].parent_node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool add_connection(const Ref<SyncedAnimationNode> &source_node, const Ref<SyncedAnimationNode> &target_node, const StringName &target_port_name) {
|
|
||||||
if (!is_connection_valid(source_node, target_node, target_port_name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int source_node_index = find_node_index(source_node);
|
|
||||||
int target_node_index = find_node_index(target_node);
|
|
||||||
int target_input_port_index = target_node->get_input_index(target_port_name);
|
|
||||||
|
|
||||||
node_connection_info[source_node_index].parent_node_index = target_node_index;
|
|
||||||
node_connection_info[target_node_index].connected_child_node_index_at_port[target_input_port_index] = source_node_index;
|
|
||||||
connections.push_back(BlendTreeConnection{ source_node, target_node, target_port_name });
|
|
||||||
|
|
||||||
add_index_and_update_subtrees_recursive(source_node_index, target_node_index);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_connection_valid(const Ref<SyncedAnimationNode> &source_node, const Ref<SyncedAnimationNode> &target_node, StringName target_port_name) {
|
|
||||||
int source_node_index = find_node_index(source_node);
|
|
||||||
if (source_node_index == -1) {
|
|
||||||
print_error("Cannot connect nodes: source node not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node_connection_info[source_node_index].parent_node_index != -1) {
|
|
||||||
print_error("Cannot connect node: source node already has a parent.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int target_node_index = find_node_index(target_node);
|
|
||||||
if (target_node_index == -1) {
|
|
||||||
print_error("Cannot connect nodes: target node not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<StringName> target_inputs;
|
|
||||||
target_node->get_input_names(target_inputs);
|
|
||||||
|
|
||||||
if (!target_inputs.has(target_port_name)) {
|
|
||||||
print_error("Cannot connect nodes: target port not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SyncedBlendTree : public SyncedAnimationNode {
|
|
||||||
GDCLASS(SyncedBlendTree, SyncedAnimationNode);
|
|
||||||
|
|
||||||
BlendTreeGraph tree_graph;
|
|
||||||
bool tree_initialized = false;
|
bool tree_initialized = false;
|
||||||
|
|
||||||
void sort_nodes() {
|
void sort_nodes() {
|
||||||
@ -654,7 +512,7 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
void setup_runtime_data() {
|
void setup_runtime_data() {
|
||||||
// Add nodes and allocate runtime data
|
// Add nodes and allocate runtime data
|
||||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||||
const Ref<SyncedAnimationNode> node = tree_graph.nodes[i];
|
const Ref<BLTAnimationNode> node = tree_graph.nodes[i];
|
||||||
|
|
||||||
NodeRuntimeData node_runtime_data;
|
NodeRuntimeData node_runtime_data;
|
||||||
for (int ni = 0; ni < node->get_input_count(); ni++) {
|
for (int ni = 0; ni < node->get_input_count(); ni++) {
|
||||||
@ -667,7 +525,7 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
|
|
||||||
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
// Populate runtime data (only now is this.nodes populated to retrieve the nodes)
|
||||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||||
Ref<SyncedAnimationNode> 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++) {
|
||||||
@ -678,23 +536,24 @@ class SyncedBlendTree : public SyncedAnimationNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
bool _get(const StringName &p_name, Variant &r_value) const;
|
bool _get(const StringName &p_name, Variant &r_value) const;
|
||||||
bool _set(const StringName &p_name, const Variant &p_value);
|
bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct NodeRuntimeData {
|
struct NodeRuntimeData {
|
||||||
Vector<Ref<SyncedAnimationNode>> input_nodes;
|
Vector<Ref<BLTAnimationNode>> input_nodes;
|
||||||
LocalVector<AnimationData *> input_data;
|
LocalVector<AnimationData *> input_data;
|
||||||
AnimationData *output_data = nullptr;
|
AnimationData *output_data = nullptr;
|
||||||
};
|
};
|
||||||
LocalVector<NodeRuntimeData> _node_runtime_data;
|
LocalVector<NodeRuntimeData> _node_runtime_data;
|
||||||
|
|
||||||
Ref<SyncedAnimationNode> get_output_node() const {
|
Ref<BLTAnimationNode> get_output_node() const {
|
||||||
return tree_graph.nodes[0];
|
return tree_graph.nodes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_node_index(const Ref<SyncedAnimationNode> &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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,7 +561,7 @@ public:
|
|||||||
return tree_graph.find_node_index_by_name(name);
|
return tree_graph.find_node_index_by_name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<SyncedAnimationNode> get_node(int node_index) {
|
Ref<BLTAnimationNode> get_node(int node_index) {
|
||||||
if (node_index < 0 || node_index > tree_graph.nodes.size()) {
|
if (node_index < 0 || node_index > tree_graph.nodes.size()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -710,7 +569,7 @@ public:
|
|||||||
return tree_graph.nodes[node_index];
|
return tree_graph.nodes[node_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_node(const Ref<SyncedAnimationNode> &node) {
|
void add_node(const Ref<BLTAnimationNode> &node) {
|
||||||
if (tree_initialized) {
|
if (tree_initialized) {
|
||||||
print_error("Cannot add node to BlendTree: BlendTree already initialized.");
|
print_error("Cannot add node to BlendTree: BlendTree already initialized.");
|
||||||
return;
|
return;
|
||||||
@ -719,10 +578,10 @@ public:
|
|||||||
tree_graph.add_node(node);
|
tree_graph.add_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool add_connection(const Ref<SyncedAnimationNode> &source_node, const Ref<SyncedAnimationNode> &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) {
|
||||||
if (tree_initialized) {
|
if (tree_initialized) {
|
||||||
print_error("Cannot add connection to BlendTree: BlendTree already initialized.");
|
print_error("Cannot add connection to BlendTree: BlendTree already initialized.");
|
||||||
return false;
|
return CONNECTION_ERROR_GRAPH_ALREADY_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree_graph.add_connection(source_node, target_node, target_port_name);
|
return tree_graph.add_connection(source_node, target_node, target_port_name);
|
||||||
@ -733,7 +592,7 @@ public:
|
|||||||
sort_nodes();
|
sort_nodes();
|
||||||
setup_runtime_data();
|
setup_runtime_data();
|
||||||
|
|
||||||
for (const Ref<SyncedAnimationNode> &node : tree_graph.nodes) {
|
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||||
if (!node->initialize(context)) {
|
if (!node->initialize(context)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -744,12 +603,13 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void activate_inputs(const Vector<Ref<SyncedAnimationNode>> &input_nodes) override {
|
void
|
||||||
|
activate_inputs(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
GodotProfileZone("SyncedBlendTree::activate_inputs");
|
||||||
|
|
||||||
tree_graph.nodes[0]->active = true;
|
tree_graph.nodes[0]->active = true;
|
||||||
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
for (int i = 0; i < tree_graph.nodes.size(); i++) {
|
||||||
const Ref<SyncedAnimationNode> &node = tree_graph.nodes[i];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
continue;
|
continue;
|
||||||
@ -760,10 +620,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculate_sync_track(const Vector<Ref<SyncedAnimationNode>> &input_nodes) override {
|
void calculate_sync_track(const Vector<Ref<BLTAnimationNode>> &input_nodes) override {
|
||||||
GodotProfileZone("SyncedBlendTree::calculate_sync_track");
|
GodotProfileZone("SyncedBlendTree::calculate_sync_track");
|
||||||
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||||
const Ref<SyncedAnimationNode> &node = tree_graph.nodes[i];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
continue;
|
continue;
|
||||||
@ -782,13 +642,13 @@ public:
|
|||||||
tree_graph.nodes[0]->node_time_info.position += p_delta;
|
tree_graph.nodes[0]->node_time_info.position += p_delta;
|
||||||
|
|
||||||
for (int i = 1; i < tree_graph.nodes.size(); i++) {
|
for (int i = 1; i < tree_graph.nodes.size(); i++) {
|
||||||
const Ref<SyncedAnimationNode> &node = tree_graph.nodes[i];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Ref<SyncedAnimationNode> &node_parent = tree_graph.nodes[tree_graph.node_connection_info[i].parent_node_index];
|
const Ref<BLTAnimationNode> &node_parent = tree_graph.nodes[tree_graph.node_connection_info[i].parent_node_index];
|
||||||
|
|
||||||
if (node->node_time_info.is_synced) {
|
if (node->node_time_info.is_synced) {
|
||||||
node->update_time(node_parent->node_time_info.sync_position);
|
node->update_time(node_parent->node_time_info.sync_position);
|
||||||
@ -802,7 +662,7 @@ public:
|
|||||||
ZoneScopedN("SyncedBlendTree::evaluate");
|
ZoneScopedN("SyncedBlendTree::evaluate");
|
||||||
|
|
||||||
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
for (int i = tree_graph.nodes.size() - 1; i > 0; i--) {
|
||||||
const Ref<SyncedAnimationNode> &node = tree_graph.nodes[i];
|
const Ref<BLTAnimationNode> &node = tree_graph.nodes[i];
|
||||||
|
|
||||||
if (!node->active) {
|
if (!node->active) {
|
||||||
continue;
|
continue;
|
||||||
@ -832,9 +692,11 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_child_nodes(List<Ref<SyncedAnimationNode>> *r_child_nodes) const override {
|
void get_child_nodes(List<Ref<BLTAnimationNode>> *r_child_nodes) const override {
|
||||||
for (const Ref<SyncedAnimationNode> &node : tree_graph.nodes) {
|
for (const Ref<BLTAnimationNode> &node : tree_graph.nodes) {
|
||||||
r_child_nodes->push_back(node.ptr());
|
r_child_nodes->push_back(node.ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VARIANT_ENUM_CAST(BLTAnimationNodeBlendTree::ConnectionError)
|
||||||
17
demo/main.gd
17
demo/main.gd
@ -12,15 +12,28 @@ 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
|
||||||
|
|
||||||
func _on_blend_weight_slider_value_changed(value: float) -> void:
|
func _on_blend_weight_slider_value_changed(value: float) -> void:
|
||||||
mixamo_amy_walk_limp.get_node("AnimationTree").set("parameters/Blend2/blend_amount", value)
|
mixamo_amy_walk_limp.get_node("AnimationTree").set("parameters/Blend2/blend_amount", value)
|
||||||
mixamo_amy_walk_limp_synced.get_node("SyncedAnimationGraph").set("parameters/AnimationBlend2Node/blend_amount", value)
|
mixamo_amy_walk_limp_synced.get_node("SyncedAnimationGraph").set("parameters/BLTAnimationNodeBlend2/blend_amount", value)
|
||||||
|
|
||||||
mixamo_amy_walk_run.get_node("AnimationTree").set("parameters/Blend2/blend_amount", value)
|
mixamo_amy_walk_run.get_node("AnimationTree").set("parameters/Blend2/blend_amount", value)
|
||||||
mixamo_amy_walk_run_synced.get_node("SyncedAnimationGraph").set("parameters/AnimationBlend2Node/blend_amount", value)
|
mixamo_amy_walk_run_synced.get_node("SyncedAnimationGraph").set("parameters/BLTAnimationNodeBlend2/blend_amount", value)
|
||||||
|
|
||||||
blend_weight_label.text = str(value)
|
blend_weight_label.text = str(value)
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
[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="SyncedBlendTree" uid="uid://2qfwr1xkiw0s" path="res://synced_blend_tree_walk_limp.tres" id="4_lquwl"]
|
[ext_resource type="BLTAnimationNodeBlendTree" uid="uid://2qfwr1xkiw0s" path="res://synced_blend_tree_walk_limp.tres" id="4_lquwl"]
|
||||||
[ext_resource type="SyncedBlendTree" uid="uid://qsk64ax2o47f" path="res://synced_blend_tree_walk_run.tres" id="5_7mycd"]
|
[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"]
|
||||||
|
|
||||||
[sub_resource type="Theme" id="Theme_272bh"]
|
[sub_resource type="Theme" id="Theme_272bh"]
|
||||||
@ -32,12 +32,12 @@ sky = SubResource("Sky_1bvp3")
|
|||||||
tonemap_mode = 2
|
tonemap_mode = 2
|
||||||
glow_enabled = true
|
glow_enabled = true
|
||||||
|
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D" unique_id=933302313]
|
||||||
script = ExtResource("1_1bvp3")
|
script = ExtResource("1_1bvp3")
|
||||||
|
|
||||||
[node name="UI" type="CanvasLayer" parent="."]
|
[node name="UI" type="CanvasLayer" parent="." unique_id=833570844]
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="UI"]
|
[node name="MarginContainer" type="MarginContainer" parent="UI" unique_id=2079852348]
|
||||||
anchors_preset = 7
|
anchors_preset = 7
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_top = 1.0
|
anchor_top = 1.0
|
||||||
@ -52,15 +52,15 @@ size_flags_horizontal = 4
|
|||||||
size_flags_vertical = 8
|
size_flags_vertical = 8
|
||||||
theme = SubResource("Theme_272bh")
|
theme = SubResource("Theme_272bh")
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="UI/MarginContainer"]
|
[node name="HBoxContainer" type="HBoxContainer" parent="UI/MarginContainer" unique_id=965781454]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/separation = 14
|
theme_override_constants/separation = 14
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="UI/MarginContainer/HBoxContainer"]
|
[node name="Label" type="Label" parent="UI/MarginContainer/HBoxContainer" unique_id=634588892]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Blend Weight"
|
text = "Blend Weight"
|
||||||
|
|
||||||
[node name="BlendWeightSlider" type="HSlider" parent="UI/MarginContainer/HBoxContainer"]
|
[node name="BlendWeightSlider" type="HSlider" parent="UI/MarginContainer/HBoxContainer" unique_id=708139207]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(200, 0)
|
custom_minimum_size = Vector2(200, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@ -68,105 +68,101 @@ size_flags_vertical = 4
|
|||||||
max_value = 1.0
|
max_value = 1.0
|
||||||
step = 0.001
|
step = 0.001
|
||||||
|
|
||||||
[node name="BlendWeightLabel" type="Label" parent="UI/MarginContainer/HBoxContainer"]
|
[node name="BlendWeightLabel" type="Label" parent="UI/MarginContainer/HBoxContainer" unique_id=1745243570]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(80, 0)
|
custom_minimum_size = Vector2(80, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "0.0"
|
text = "0.0"
|
||||||
horizontal_alignment = 2
|
horizontal_alignment = 2
|
||||||
|
|
||||||
[node name="MarginContainer2" type="MarginContainer" parent="UI"]
|
[node name="MarginContainer2" type="MarginContainer" parent="UI" unique_id=1639984576]
|
||||||
anchors_preset = 10
|
anchors_preset = 10
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_bottom = 23.0
|
offset_bottom = 23.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
theme_override_constants/margin_top = 80
|
theme_override_constants/margin_top = 80
|
||||||
|
|
||||||
[node name="HBoxContainer2" type="HBoxContainer" parent="UI/MarginContainer2"]
|
[node name="HBoxContainer2" type="HBoxContainer" parent="UI/MarginContainer2" unique_id=1134641196]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 0
|
size_flags_vertical = 0
|
||||||
theme_override_constants/separation = 400
|
theme_override_constants/separation = 400
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="UI/MarginContainer2/HBoxContainer2"]
|
[node name="Label" type="Label" parent="UI/MarginContainer2/HBoxContainer2" unique_id=1630716301]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 32
|
theme_override_font_sizes/font_size = 32
|
||||||
text = "Unsynced"
|
text = "Unsynced"
|
||||||
|
|
||||||
[node name="EmptyLabel" type="Label" parent="UI/MarginContainer2/HBoxContainer2"]
|
[node name="EmptyLabel" type="Label" parent="UI/MarginContainer2/HBoxContainer2" unique_id=991022601]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label3" type="Label" parent="UI/MarginContainer2/HBoxContainer2"]
|
[node name="Label3" type="Label" parent="UI/MarginContainer2/HBoxContainer2" unique_id=736671898]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 32
|
theme_override_font_sizes/font_size = 32
|
||||||
text = "Synced"
|
text = "Synced"
|
||||||
|
|
||||||
[node name="Level" type="Node3D" parent="."]
|
[node name="Level" type="Node3D" parent="." unique_id=1485026735]
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Level"]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="Level" unique_id=2025315417]
|
||||||
mesh = SubResource("PlaneMesh_h2yge")
|
mesh = SubResource("PlaneMesh_h2yge")
|
||||||
skeleton = NodePath("../..")
|
skeleton = NodePath("../..")
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Level"]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Level" unique_id=468068224]
|
||||||
transform = Transform3D(1, 0, 0, 0, 0.14081486, 0.99003595, 0, -0.99003595, 0.14081486, 0, 2.0531263, 4.9494514)
|
transform = Transform3D(1, 0, 0, 0, 0.14081486, 0.99003595, 0, -0.99003595, 0.14081486, 0, 2.0531263, 4.9494514)
|
||||||
shadow_enabled = true
|
shadow_enabled = true
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="Level"]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="Level" unique_id=1787092986]
|
||||||
environment = SubResource("Environment_lquwl")
|
environment = SubResource("Environment_lquwl")
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="Level"]
|
[node name="Camera3D" type="Camera3D" parent="Level" unique_id=1534208216]
|
||||||
transform = Transform3D(1, 0, 0, 0, 0.95413065, 0.29939055, 0, -0.29939055, 0.95413065, 0, 1.649, 3.197)
|
transform = Transform3D(1, 0, 0, 0, 0.95413065, 0.29939055, 0, -0.29939055, 0.95413065, 0, 1.649, 3.197)
|
||||||
fov = 36.8
|
fov = 36.8
|
||||||
|
|
||||||
[node name="Characters" type="Node3D" parent="."]
|
[node name="Characters" type="Node3D" parent="." unique_id=1282309438]
|
||||||
|
|
||||||
[node name="MixamoAmyWalkLimp" parent="Characters" instance=ExtResource("1_0xm2m")]
|
[node name="MixamoAmyWalkLimp" parent="Characters" unique_id=829927137 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="AnimationTree" type="AnimationTree" parent="Characters/MixamoAmyWalkLimp"]
|
[node name="AnimationTree" type="AnimationTree" parent="Characters/MixamoAmyWalkLimp" unique_id=1713995200]
|
||||||
tree_root = ExtResource("3_272bh")
|
tree_root = ExtResource("3_272bh")
|
||||||
anim_player = NodePath("../AnimationPlayer")
|
anim_player = NodePath("../AnimationPlayer")
|
||||||
parameters/Blend2/blend_amount = 0.0
|
parameters/Blend2/blend_amount = 0.0
|
||||||
|
|
||||||
[node name="MixamoAmyWalkRun" parent="Characters" instance=ExtResource("1_0xm2m")]
|
[node name="MixamoAmyWalkRun" parent="Characters" unique_id=1431806836 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, -0.6, 0, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.6, 0, 0)
|
||||||
|
|
||||||
[node name="AnimationTree" type="AnimationTree" parent="Characters/MixamoAmyWalkRun"]
|
[node name="AnimationTree" type="AnimationTree" parent="Characters/MixamoAmyWalkRun" unique_id=1079690622]
|
||||||
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
|
||||||
|
|
||||||
[node name="MixamoAmyWalkLimpSynced" parent="Characters" 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
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.6, 0, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.6, 0, 0)
|
||||||
|
|
||||||
[node name="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkLimpSynced"]
|
[node name="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkLimpSynced" unique_id=379737684]
|
||||||
libraries = {
|
libraries/animation_library = ExtResource("3_1bvp3")
|
||||||
&"animation_library": ExtResource("3_1bvp3")
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="SyncedAnimationGraph" type="SyncedAnimationGraph" parent="Characters/MixamoAmyWalkLimpSynced"]
|
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkLimpSynced" unique_id=1866796918]
|
||||||
animation_player = NodePath("../AnimationPlayer2")
|
animation_player = NodePath("../AnimationPlayer2")
|
||||||
tree_root = ExtResource("4_lquwl")
|
tree_root = ExtResource("4_lquwl")
|
||||||
skeleton = NodePath("../Armature/Skeleton3D")
|
skeleton = NodePath("../Armature/Skeleton3D")
|
||||||
parameters/AnimationBlend2Node/blend_amount = 0.0
|
parameters/BLTAnimationNodeBlend2/blend_amount = 0.4
|
||||||
|
|
||||||
[node name="MixamoAmyWalkRunSynced" parent="Characters" 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="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkRunSynced"]
|
[node name="AnimationPlayer2" type="AnimationPlayer" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1255239074]
|
||||||
libraries = {
|
libraries/animation_library = ExtResource("3_1bvp3")
|
||||||
&"animation_library": ExtResource("3_1bvp3")
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="SyncedAnimationGraph" type="SyncedAnimationGraph" parent="Characters/MixamoAmyWalkRunSynced"]
|
[node name="SyncedAnimationGraph" type="BLTAnimationGraph" parent="Characters/MixamoAmyWalkRunSynced" unique_id=1602406394]
|
||||||
animation_player = NodePath("../AnimationPlayer2")
|
animation_player = NodePath("../AnimationPlayer2")
|
||||||
tree_root = ExtResource("5_7mycd")
|
tree_root = ExtResource("5_7mycd")
|
||||||
skeleton = NodePath("../Armature/Skeleton3D")
|
skeleton = NodePath("../Armature/Skeleton3D")
|
||||||
parameters/AnimationBlend2Node/blend_amount = 0.0
|
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"]
|
||||||
|
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
[gd_resource type="SyncedBlendTree" load_steps=4 format=3]
|
[gd_resource type="BLTAnimationNodeBlendTree" load_steps=4 format=3]
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_bvt3d"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_bvt3d"]
|
||||||
animation = &"animation_library/TestAnimationB"
|
animation = &"animation_library/TestAnimationB"
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_sntl5"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
||||||
animation = &"animation_library/TestAnimationA"
|
animation = &"animation_library/TestAnimationA"
|
||||||
|
|
||||||
[sub_resource type="AnimationBlend2Node" id="AnimationBlend2Node_n4m28"]
|
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_n4m28"]
|
||||||
sync = false
|
sync = false
|
||||||
blend_amount = 0.5
|
blend_amount = 0.5
|
||||||
sync = false
|
sync = false
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
nodes/Blend2/node = SubResource("AnimationBlend2Node_n4m28")
|
nodes/Blend2/node = SubResource("BLTAnimationNodeBlend2_n4m28")
|
||||||
nodes/Blend2/position = Vector2(0, 0)
|
nodes/Blend2/position = Vector2(0, 0)
|
||||||
"nodes/AnimationSamplerNode 1/node" = SubResource("AnimationSamplerNode_bvt3d")
|
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_bvt3d")
|
||||||
"nodes/AnimationSamplerNode 1/position" = Vector2(0, 0)
|
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
||||||
nodes/AnimationSamplerNode/node = SubResource("AnimationSamplerNode_sntl5")
|
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_sntl5")
|
||||||
nodes/AnimationSamplerNode/position = Vector2(0, 0)
|
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
||||||
node_connections = [&"Blend2", 0, &"AnimationSamplerNode", &"Blend2", 1, &"AnimationSamplerNode 1", &"Output", 0, &"Blend2"]
|
node_connections = [&"Blend2", 0, &"BLTAnimationNodeSampler", &"Blend2", 1, &"BLTAnimationNodeSampler 1", &"Output", 0, &"Blend2"]
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
[gd_resource type="SyncedBlendTree" load_steps=4 format=3 uid="uid://2qfwr1xkiw0s"]
|
[gd_resource type="BLTAnimationNodeBlendTree" load_steps=4 format=3 uid="uid://2qfwr1xkiw0s"]
|
||||||
|
|
||||||
[sub_resource type="AnimationBlend2Node" id="AnimationBlend2Node_bvt3d"]
|
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_bvt3d"]
|
||||||
|
blend_amount = 0.4
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_sntl5"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
||||||
animation = &"animation_library/Limping-InPlace"
|
animation = &"animation_library/Limping-InPlace"
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_n4m28"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_n4m28"]
|
||||||
animation = &"animation_library/Walk-InPlace"
|
animation = &"animation_library/Walk-InPlace"
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
nodes/AnimationBlend2Node/node = SubResource("AnimationBlend2Node_bvt3d")
|
nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_bvt3d")
|
||||||
nodes/AnimationBlend2Node/position = Vector2(0, 0)
|
nodes/BLTAnimationNodeBlend2/position = Vector2(0, 0)
|
||||||
"nodes/AnimationSamplerNode 1/node" = SubResource("AnimationSamplerNode_sntl5")
|
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_sntl5")
|
||||||
"nodes/AnimationSamplerNode 1/position" = Vector2(0, 0)
|
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
||||||
nodes/AnimationSamplerNode/node = SubResource("AnimationSamplerNode_n4m28")
|
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_n4m28")
|
||||||
nodes/AnimationSamplerNode/position = Vector2(0, 0)
|
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
||||||
node_connections = [&"AnimationBlend2Node", 0, &"AnimationSamplerNode", &"AnimationBlend2Node", 1, &"AnimationSamplerNode 1", &"Output", 0, &"AnimationBlend2Node"]
|
node_connections = [&"BLTAnimationNodeBlend2", 0, &"BLTAnimationNodeSampler", &"BLTAnimationNodeBlend2", 1, &"BLTAnimationNodeSampler 1", &"Output", 0, &"BLTAnimationNodeBlend2"]
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
[gd_resource type="SyncedBlendTree" load_steps=4 format=3 uid="uid://qsk64ax2o47f"]
|
[gd_resource type="BLTAnimationNodeBlendTree" load_steps=4 format=3 uid="uid://qsk64ax2o47f"]
|
||||||
|
|
||||||
[sub_resource type="AnimationBlend2Node" id="AnimationBlend2Node_bvt3d"]
|
[sub_resource type="BLTAnimationNodeBlend2" id="BLTAnimationNodeBlend2_bvt3d"]
|
||||||
|
blend_amount = 0.4
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_sntl5"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_sntl5"]
|
||||||
animation = &"animation_library/Run-InPlace"
|
animation = &"animation_library/Run-InPlace"
|
||||||
|
|
||||||
[sub_resource type="AnimationSamplerNode" id="AnimationSamplerNode_n4m28"]
|
[sub_resource type="BLTAnimationNodeSampler" id="BLTAnimationNodeSampler_n4m28"]
|
||||||
animation = &"animation_library/Walk-InPlace"
|
animation = &"animation_library/Walk-InPlace"
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
nodes/AnimationBlend2Node/node = SubResource("AnimationBlend2Node_bvt3d")
|
nodes/BLTAnimationNodeBlend2/node = SubResource("BLTAnimationNodeBlend2_bvt3d")
|
||||||
nodes/AnimationBlend2Node/position = Vector2(0, 0)
|
nodes/BLTAnimationNodeBlend2/position = Vector2(0, 0)
|
||||||
"nodes/AnimationSamplerNode 1/node" = SubResource("AnimationSamplerNode_sntl5")
|
"nodes/BLTAnimationNodeSampler 1/node" = SubResource("BLTAnimationNodeSampler_sntl5")
|
||||||
"nodes/AnimationSamplerNode 1/position" = Vector2(0, 0)
|
"nodes/BLTAnimationNodeSampler 1/position" = Vector2(0, 0)
|
||||||
nodes/AnimationSamplerNode/node = SubResource("AnimationSamplerNode_n4m28")
|
nodes/BLTAnimationNodeSampler/node = SubResource("BLTAnimationNodeSampler_n4m28")
|
||||||
nodes/AnimationSamplerNode/position = Vector2(0, 0)
|
nodes/BLTAnimationNodeSampler/position = Vector2(0, 0)
|
||||||
node_connections = [&"AnimationBlend2Node", 0, &"AnimationSamplerNode", &"AnimationBlend2Node", 1, &"AnimationSamplerNode 1", &"Output", 0, &"AnimationBlend2Node"]
|
node_connections = [&"BLTAnimationNodeBlend2", 0, &"BLTAnimationNodeSampler", &"BLTAnimationNodeBlend2", 1, &"BLTAnimationNodeSampler 1", &"Output", 0, &"BLTAnimationNodeBlend2"]
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
#include "register_types.h"
|
#include "register_types.h"
|
||||||
|
|
||||||
|
#include "blendalot_animation_graph.h"
|
||||||
#include "core/object/class_db.h"
|
#include "core/object/class_db.h"
|
||||||
#include "synced_animation_graph.h"
|
|
||||||
|
|
||||||
void initialize_blendalot_animgraph_module(ModuleInitializationLevel p_level) {
|
void initialize_blendalot_animgraph_module(ModuleInitializationLevel p_level) {
|
||||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClassDB::register_class<SyncedAnimationGraph>();
|
ClassDB::register_class<BLTAnimationGraph>();
|
||||||
ClassDB::register_class<SyncedAnimationNode>();
|
ClassDB::register_class<BLTAnimationNode>();
|
||||||
ClassDB::register_class<SyncedBlendTree>();
|
ClassDB::register_class<BLTAnimationNodeOutput>();
|
||||||
ClassDB::register_class<AnimationSamplerNode>();
|
ClassDB::register_class<BLTAnimationNodeBlendTree>();
|
||||||
ClassDB::register_class<AnimationBlend2Node>();
|
ClassDB::register_class<BLTAnimationNodeSampler>();
|
||||||
|
ClassDB::register_class<BLTAnimationNodeBlend2>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninitialize_blendalot_animgraph_module(ModuleInitializationLevel p_level) {
|
void uninitialize_blendalot_animgraph_module(ModuleInitializationLevel p_level) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../synced_animation_graph.h"
|
#include "../blendalot_animation_graph.h"
|
||||||
#include "scene/animation/animation_tree.h"
|
#include "scene/animation/animation_tree.h"
|
||||||
#include "scene/main/window.h"
|
#include "scene/main/window.h"
|
||||||
|
|
||||||
@ -20,9 +20,9 @@ struct SyncedAnimationGraphFixture {
|
|||||||
|
|
||||||
Ref<AnimationLibrary> animation_library;
|
Ref<AnimationLibrary> animation_library;
|
||||||
|
|
||||||
SyncedAnimationGraph *synced_animation_graph;
|
BLTAnimationGraph *synced_animation_graph;
|
||||||
SyncedAnimationGraphFixture() {
|
SyncedAnimationGraphFixture() {
|
||||||
SyncedAnimationGraph *scene_animation_graph = dynamic_cast<SyncedAnimationGraph *>(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) {
|
||||||
setup_test_scene();
|
setup_test_scene();
|
||||||
@ -50,7 +50,7 @@ struct SyncedAnimationGraphFixture {
|
|||||||
|
|
||||||
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
SceneTree::get_singleton()->get_root()->add_child(player_node);
|
||||||
|
|
||||||
synced_animation_graph = memnew(SyncedAnimationGraph);
|
synced_animation_graph = memnew(BLTAnimationGraph);
|
||||||
synced_animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
synced_animation_graph->set_name("SyncedAnimationGraphFixtureTestNode");
|
||||||
SceneTree::get_singleton()->get_root()->add_child(synced_animation_graph);
|
SceneTree::get_singleton()->get_root()->add_child(synced_animation_graph);
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ struct SyncedAnimationGraphFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void assign_scene_variables() {
|
void assign_scene_variables() {
|
||||||
synced_animation_graph = dynamic_cast<SyncedAnimationGraph *>(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(synced_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);
|
||||||
@ -142,29 +142,29 @@ struct SyncedAnimationGraphFixture {
|
|||||||
namespace TestSyncedAnimationGraph {
|
namespace TestSyncedAnimationGraph {
|
||||||
|
|
||||||
TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
||||||
BlendTreeGraph tree_constructor;
|
BLTAnimationNodeBlendTree::BLTBlendTreeGraph tree_constructor;
|
||||||
|
|
||||||
Ref<AnimationSamplerNode> animation_sampler_node0;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node0;
|
||||||
animation_sampler_node0.instantiate();
|
animation_sampler_node0.instantiate();
|
||||||
animation_sampler_node0->name = "Sampler0";
|
animation_sampler_node0->name = "Sampler0";
|
||||||
tree_constructor.add_node(animation_sampler_node0);
|
tree_constructor.add_node(animation_sampler_node0);
|
||||||
|
|
||||||
Ref<AnimationSamplerNode> animation_sampler_node1;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node1;
|
||||||
animation_sampler_node1.instantiate();
|
animation_sampler_node1.instantiate();
|
||||||
animation_sampler_node1->name = "Sampler1";
|
animation_sampler_node1->name = "Sampler1";
|
||||||
tree_constructor.add_node(animation_sampler_node1);
|
tree_constructor.add_node(animation_sampler_node1);
|
||||||
|
|
||||||
Ref<AnimationSamplerNode> animation_sampler_node2;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node2;
|
||||||
animation_sampler_node2.instantiate();
|
animation_sampler_node2.instantiate();
|
||||||
animation_sampler_node2->name = "Sampler2";
|
animation_sampler_node2->name = "Sampler2";
|
||||||
tree_constructor.add_node(animation_sampler_node2);
|
tree_constructor.add_node(animation_sampler_node2);
|
||||||
|
|
||||||
Ref<AnimationBlend2Node> node_blend0;
|
Ref<BLTAnimationNodeBlend2> node_blend0;
|
||||||
node_blend0.instantiate();
|
node_blend0.instantiate();
|
||||||
node_blend0->name = "Blend0";
|
node_blend0->name = "Blend0";
|
||||||
tree_constructor.add_node(node_blend0);
|
tree_constructor.add_node(node_blend0);
|
||||||
|
|
||||||
Ref<AnimationBlend2Node> node_blend1;
|
Ref<BLTAnimationNodeBlend2> node_blend1;
|
||||||
node_blend1.instantiate();
|
node_blend1.instantiate();
|
||||||
node_blend1->name = "Blend1";
|
node_blend1->name = "Blend1";
|
||||||
tree_constructor.add_node(node_blend1);
|
tree_constructor.add_node(node_blend1);
|
||||||
@ -174,7 +174,7 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
// Sampler1 -+ Blend0 -\
|
// Sampler1 -+ Blend0 -\
|
||||||
// Sampler2 -----------+ Blend1 - Output
|
// Sampler2 -----------+ Blend1 - Output
|
||||||
|
|
||||||
CHECK(tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(animation_sampler_node0, node_blend0, "Input0"));
|
||||||
|
|
||||||
// Ensure that subtree is properly updated
|
// Ensure that subtree is properly updated
|
||||||
int sampler0_index = tree_constructor.find_node_index(animation_sampler_node0);
|
int sampler0_index = tree_constructor.find_node_index(animation_sampler_node0);
|
||||||
@ -182,12 +182,16 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
CHECK(tree_constructor.node_connection_info[blend0_index].input_subtree_node_indices.has(sampler0_index));
|
CHECK(tree_constructor.node_connection_info[blend0_index].input_subtree_node_indices.has(sampler0_index));
|
||||||
|
|
||||||
// Connect blend0 to blend1
|
// Connect blend0 to blend1
|
||||||
CHECK(tree_constructor.add_connection(node_blend0, node_blend1, "Input0"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend0, node_blend1, "Input0"));
|
||||||
|
|
||||||
|
// Creating a loop must fail
|
||||||
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_ERROR_CONNECTION_CREATES_LOOP == tree_constructor.add_connection(node_blend1, node_blend0, "Input1"));
|
||||||
|
|
||||||
|
// Correct connection of Sampler1 to Blend0
|
||||||
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input1"));
|
||||||
|
|
||||||
// Connecting to an already connected port must fail
|
// Connecting to an already connected port must fail
|
||||||
CHECK(!tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input0"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_ERROR_TARGET_PORT_ALREADY_CONNECTED == tree_constructor.add_connection(animation_sampler_node2, node_blend0, "Input0"));
|
||||||
// Correct connection of Sampler1 to Blend0
|
|
||||||
CHECK(tree_constructor.add_connection(animation_sampler_node1, node_blend0, "Input1"));
|
|
||||||
|
|
||||||
// Ensure that subtree is properly updated
|
// Ensure that subtree is properly updated
|
||||||
int sampler1_index = tree_constructor.find_node_index(animation_sampler_node0);
|
int sampler1_index = tree_constructor.find_node_index(animation_sampler_node0);
|
||||||
@ -196,12 +200,9 @@ TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
|||||||
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(sampler0_index));
|
CHECK(tree_constructor.node_connection_info[blend1_index].input_subtree_node_indices.has(sampler0_index));
|
||||||
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));
|
||||||
|
|
||||||
// Creating a loop must fail
|
|
||||||
CHECK(!tree_constructor.add_connection(node_blend1, node_blend0, "Input1"));
|
|
||||||
|
|
||||||
// Perform remaining connections
|
// Perform remaining connections
|
||||||
CHECK(tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Input"));
|
CHECK(BLTAnimationNodeBlendTree::CONNECTION_OK == tree_constructor.add_connection(node_blend1, tree_constructor.get_output_node(), "Input"));
|
||||||
CHECK(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:
|
||||||
CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(1));
|
CHECK(tree_constructor.node_connection_info[0].input_subtree_node_indices.has(1));
|
||||||
@ -256,7 +257,7 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph evaluation with an AnimationSampler as root node") {
|
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph evaluation with an AnimationSampler as root node") {
|
||||||
Ref<AnimationSamplerNode> 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";
|
||||||
|
|
||||||
@ -278,15 +279,15 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][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<SyncedBlendTree> synced_blend_tree_node;
|
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||||
synced_blend_tree_node.instantiate();
|
synced_blend_tree_node.instantiate();
|
||||||
|
|
||||||
Ref<AnimationSamplerNode> 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";
|
||||||
|
|
||||||
synced_blend_tree_node->add_node(animation_sampler_node);
|
synced_blend_tree_node->add_node(animation_sampler_node);
|
||||||
REQUIRE(synced_blend_tree_node->add_connection(animation_sampler_node, synced_blend_tree_node->get_output_node(), "Input"));
|
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(synced_animation_graph->get_context());
|
synced_blend_tree_node->initialize(synced_animation_graph->get_context());
|
||||||
|
|
||||||
@ -308,25 +309,25 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph][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<SyncedBlendTree> synced_blend_tree_node;
|
Ref<BLTAnimationNodeBlendTree> synced_blend_tree_node;
|
||||||
synced_blend_tree_node.instantiate();
|
synced_blend_tree_node.instantiate();
|
||||||
|
|
||||||
// TestAnimationA
|
// TestAnimationA
|
||||||
Ref<AnimationSamplerNode> animation_sampler_node_a;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node_a;
|
||||||
animation_sampler_node_a.instantiate();
|
animation_sampler_node_a.instantiate();
|
||||||
animation_sampler_node_a->animation_name = "animation_library/TestAnimationA";
|
animation_sampler_node_a->animation_name = "animation_library/TestAnimationA";
|
||||||
|
|
||||||
synced_blend_tree_node->add_node(animation_sampler_node_a);
|
synced_blend_tree_node->add_node(animation_sampler_node_a);
|
||||||
|
|
||||||
// TestAnimationB
|
// TestAnimationB
|
||||||
Ref<AnimationSamplerNode> animation_sampler_node_b;
|
Ref<BLTAnimationNodeSampler> animation_sampler_node_b;
|
||||||
animation_sampler_node_b.instantiate();
|
animation_sampler_node_b.instantiate();
|
||||||
animation_sampler_node_b->animation_name = "animation_library/TestAnimationB";
|
animation_sampler_node_b->animation_name = "animation_library/TestAnimationB";
|
||||||
|
|
||||||
synced_blend_tree_node->add_node(animation_sampler_node_b);
|
synced_blend_tree_node->add_node(animation_sampler_node_b);
|
||||||
|
|
||||||
// Blend2
|
// Blend2
|
||||||
Ref<AnimationBlend2Node> blend2_node;
|
Ref<BLTAnimationNodeBlend2> blend2_node;
|
||||||
blend2_node.instantiate();
|
blend2_node.instantiate();
|
||||||
blend2_node->name = "Blend2";
|
blend2_node->name = "Blend2";
|
||||||
blend2_node->blend_weight = 0.5;
|
blend2_node->blend_weight = 0.5;
|
||||||
@ -337,14 +338,14 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
// Connect nodes
|
// Connect nodes
|
||||||
Vector<StringName> blend2_inputs;
|
Vector<StringName> blend2_inputs;
|
||||||
blend2_node->get_input_names(blend2_inputs);
|
blend2_node->get_input_names(blend2_inputs);
|
||||||
REQUIRE(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(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(synced_blend_tree_node->add_connection(blend2_node, synced_blend_tree_node->get_output_node(), "Input"));
|
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(synced_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 SyncedBlendTree::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];
|
||||||
|
|
||||||
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);
|
||||||
@ -430,13 +431,13 @@ TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph
|
|||||||
// Test saving and loading of the blend tree to a resource
|
// Test saving and loading of the blend tree to a resource
|
||||||
ResourceSaver::save(synced_blend_tree_node, "synced_blend_tree_node.tres");
|
ResourceSaver::save(synced_blend_tree_node, "synced_blend_tree_node.tres");
|
||||||
|
|
||||||
REQUIRE(ClassDB::class_exists("AnimationSamplerNode"));
|
REQUIRE(ClassDB::class_exists("BLTAnimationNodeSampler"));
|
||||||
|
|
||||||
// Load blend tree
|
// Load blend tree
|
||||||
Ref<SyncedBlendTree> 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<AnimationBlend2Node> loaded_blend2_node = loaded_synced_blend_tree->get_node(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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user