commit 15bcc60da9b8940974858ffc283eac84be2fbc79 Author: Martin Felis Date: Sun Nov 9 17:22:44 2025 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77e2c56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.*swp +__pycache__ diff --git a/SCsub b/SCsub new file mode 100644 index 0000000..3c12e73 --- /dev/null +++ b/SCsub @@ -0,0 +1,5 @@ +# SCsub + +Import('env') + +env.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..d22f945 --- /dev/null +++ b/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return True + + +def configure(env): + pass diff --git a/register_types.cpp b/register_types.cpp new file mode 100644 index 0000000..e37dd15 --- /dev/null +++ b/register_types.cpp @@ -0,0 +1,18 @@ +#include "register_types.h" + +#include "core/object/class_db.h" +#include "synced_animation_graph.h" + +void initialize_synced_blend_tree_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + ClassDB::register_class(); +} + +void uninitialize_synced_blend_tree_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + // Nothing to do here in this example. +} \ No newline at end of file diff --git a/register_types.h b/register_types.h new file mode 100644 index 0000000..593197e --- /dev/null +++ b/register_types.h @@ -0,0 +1,4 @@ +#include "modules/register_module_types.h" + +void initialize_synced_blend_tree_module(ModuleInitializationLevel p_level); +void uninitialize_synced_blend_tree_module(ModuleInitializationLevel p_level); diff --git a/synced_animation_graph.cpp b/synced_animation_graph.cpp new file mode 100644 index 0000000..c5f7b42 --- /dev/null +++ b/synced_animation_graph.cpp @@ -0,0 +1,156 @@ +#include "synced_animation_graph.h" + +#include "scene/animation/animation_player.h" + +void SyncedAnimationGraph::_bind_methods() { + print_line(vformat("binding methods")); + + ClassDB::bind_method(D_METHOD("set_tree_root", "animation_node"), &SyncedAnimationGraph::set_root_animation_node); + ClassDB::bind_method(D_METHOD("get_tree_root"), &SyncedAnimationGraph::get_root_animation_node); + + ClassDB::bind_method(D_METHOD("set_animation_player", "path"), &SyncedAnimationGraph::set_animation_player); + ClassDB::bind_method(D_METHOD("get_animation_player"), &SyncedAnimationGraph::get_animation_player); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); + + ADD_SIGNAL(MethodInfo(SNAME("animation_player_changed"))); +} + +void SyncedAnimationGraph::_set_active(bool p_active) { + _set_process(p_active); + started = p_active; +} + +void SyncedAnimationGraph::_process_animation(double p_delta, bool p_update_only) { + print_line(vformat("updating blend tree! %f", p_delta)); + if (!root_animation_node.is_valid()) { + return; + } + + Ref blend_tree = root_animation_node; + if (!blend_tree.is_valid()) { + print_line("Cannot process animation graph: root not AnimationNodeBlendTree"); + return; + } + + LocalVector node_names = blend_tree->get_node_list(); + for (StringName node_name : node_names) { + print_line(vformat(" %s", node_name)); + } +} + +SyncedAnimationGraph::SyncedAnimationGraph() { +} + +void SyncedAnimationGraph::_update_properties() const { + if (!properties_dirty) { + return; + } + + properties_dirty = false; + + const_cast(this)->notify_property_list_changed(); +} + +void SyncedAnimationGraph::set_root_animation_node(const Ref &p_animation_node) { + if (root_animation_node.is_valid()) { + root_animation_node->disconnect(SNAME("tree_changed"), callable_mp(this, &SyncedAnimationGraph::_tree_changed)); + // root_animation_node->disconnect(SNAME("animation_node_renamed"), callable_mp(this, &SyncedAnimationGraph::_animation_node_renamed)); + // root_animation_node->disconnect(SNAME("animation_node_removed"), callable_mp(this, &SyncedAnimationGraph::_animation_node_removed)); + } + + root_animation_node = p_animation_node; + + if (root_animation_node.is_valid()) { + root_animation_node->connect(SNAME("tree_changed"), callable_mp(this, &SyncedAnimationGraph::_tree_changed)); + // root_animation_node->connect(SNAME("animation_node_renamed"), callable_mp(this, &SyncedAnimationGraph::_animation_node_renamed)); + // root_animation_node->connect(SNAME("animation_node_removed"), callable_mp(this, &SyncedAnimationGraph::_animation_node_removed)); + } + + properties_dirty = true; + + update_configuration_warnings(); +} + +Ref SyncedAnimationGraph::get_root_animation_node() const { + return root_animation_node; +} + +void SyncedAnimationGraph::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + _setup_animation_player(); + if (active) { + _set_process(true); + } + } break; + } +} + +void SyncedAnimationGraph::_tree_changed() { + if (properties_dirty) { + return; + } + + callable_mp(this, &SyncedAnimationGraph::_update_properties).call_deferred(); + properties_dirty = true; +} + +void SyncedAnimationGraph::set_animation_player(const NodePath &p_path) { + animation_player = p_path; + if (p_path.is_empty()) { + set_root_node(SceneStringName(path_pp)); + while (animation_libraries.size()) { + remove_animation_library(animation_libraries[0].name); + } + } + emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor. + _setup_animation_player(); + notify_property_list_changed(); +} + +NodePath SyncedAnimationGraph::get_animation_player() const { + return animation_player; +} + +void SyncedAnimationGraph::_setup_animation_player() { + if (!is_inside_tree()) { + return; + } + + cache_valid = false; + + if (animation_player.is_empty()) { + clear_caches(); + return; + } + + // Using AnimationPlayer here is for compatibility. Changing to AnimationMixer needs extra work like error handling. + AnimationPlayer *player = Object::cast_to(get_node_or_null(animation_player)); + if (player) { + if (!player->is_connected(SNAME("caches_cleared"), callable_mp(this, &SyncedAnimationGraph::_setup_animation_player))) { + player->connect(SNAME("caches_cleared"), callable_mp(this, &SyncedAnimationGraph::_setup_animation_player), CONNECT_DEFERRED); + } + if (!player->is_connected(SNAME("animation_list_changed"), callable_mp(this, &SyncedAnimationGraph::_setup_animation_player))) { + player->connect(SNAME("animation_list_changed"), callable_mp(this, &SyncedAnimationGraph::_setup_animation_player), CONNECT_DEFERRED); + } + Node *root = player->get_node_or_null(player->get_root_node()); + if (root) { + set_root_node(get_path_to(root, true)); + } + while (animation_libraries.size()) { + remove_animation_library(animation_libraries[0].name); + } + List list; + player->get_animation_library_list(&list); + for (const StringName &E : list) { + Ref lib = player->get_animation_library(E); + if (lib.is_valid()) { + add_animation_library(E, lib); + } + } + } + + clear_caches(); +} diff --git a/synced_animation_graph.h b/synced_animation_graph.h new file mode 100644 index 0000000..021d763 --- /dev/null +++ b/synced_animation_graph.h @@ -0,0 +1,43 @@ +#pragma once + +#include "scene/animation/animation_blend_tree.h" +#include "scene/animation/animation_tree.h" + +class SyncedAnimationGraph : public AnimationMixer { + GDCLASS(SyncedAnimationGraph, AnimationMixer); + +private: + bool started = true; + + friend class AnimationNode; + + Ref root_animation_node; + + mutable bool properties_dirty = true; + + void _update_properties() const; + + NodePath animation_player; + void set_animation_player(const NodePath &p_path); + NodePath get_animation_player() const; + + void set_root_animation_node(const Ref &p_animation_node); + Ref get_root_animation_node() const; + + void _tree_changed(); + + void _setup_animation_player(); + void _animation_player_changed(); + + void _notification(int p_what); + + virtual void _set_active(bool p_active) override; + +protected: + static void _bind_methods(); + +public: + void _process_animation(double p_delta, bool p_update_only = false) override; + + SyncedAnimationGraph(); +}; \ No newline at end of file