Initial commit

This commit is contained in:
Martin Felis 2025-11-09 17:22:44 +01:00
commit 15bcc60da9
7 changed files with 234 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.*swp
__pycache__

5
SCsub Normal file
View File

@ -0,0 +1,5 @@
# SCsub
Import('env')
env.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build

6
config.py Normal file
View File

@ -0,0 +1,6 @@
def can_build(env, platform):
return True
def configure(env):
pass

18
register_types.cpp Normal file
View File

@ -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<SyncedAnimationGraph>();
}
void uninitialize_synced_blend_tree_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
// Nothing to do here in this example.
}

4
register_types.h Normal file
View File

@ -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);

156
synced_animation_graph.cpp Normal file
View File

@ -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<AnimationNodeBlendTree> blend_tree = root_animation_node;
if (!blend_tree.is_valid()) {
print_line("Cannot process animation graph: root not AnimationNodeBlendTree");
return;
}
LocalVector<StringName> 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<SyncedAnimationGraph *>(this)->notify_property_list_changed();
}
void SyncedAnimationGraph::set_root_animation_node(const Ref<AnimationRootNode> &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<AnimationRootNode> 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<AnimationPlayer>(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<StringName> list;
player->get_animation_library_list(&list);
for (const StringName &E : list) {
Ref<AnimationLibrary> lib = player->get_animation_library(E);
if (lib.is_valid()) {
add_animation_library(E, lib);
}
}
}
clear_caches();
}

43
synced_animation_graph.h Normal file
View File

@ -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<AnimationRootNode> 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<AnimationRootNode> &p_animation_node);
Ref<AnimationRootNode> 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();
};