Added demo, otherwise WIP.
Also needs AnimationMixer to be modified such that SyncedAnimationGraph is a friend class of AnimationMixer.
This commit is contained in:
parent
9a9adfbfb4
commit
c642fa2a04
29
.gitignore
vendored
29
.gitignore
vendored
@ -1,2 +1,31 @@
|
||||
.*swp
|
||||
__pycache__
|
||||
|
||||
# Godot 4+ specific ignores
|
||||
demo/.godot/
|
||||
demo/android/
|
||||
|
||||
demo/.nomedia
|
||||
|
||||
# Godot-specific ignores
|
||||
demo/.import/
|
||||
demo/export.cfg
|
||||
demo/export_credentials.cfg
|
||||
demo/*.tmp
|
||||
|
||||
# Imported translations (automatically generated from CSV files)
|
||||
demo/*.translation
|
||||
|
||||
# Mono-specific ignores
|
||||
demo/.mono/
|
||||
demo/data_*/
|
||||
demo/mono_crash.*.json
|
||||
|
||||
/demo/assets/MixamoAmy_Ch46_1001_Diffuse.png
|
||||
/demo/assets/MixamoAmy_Ch46_1001_Diffuse.png.import
|
||||
/demo/assets/MixamoAmy_Ch46_1001_Glossiness.png
|
||||
/demo/assets/MixamoAmy_Ch46_1001_Glossiness.png.import
|
||||
/demo/assets/MixamoAmy_Ch46_1001_Normal.png
|
||||
/demo/assets/MixamoAmy_Ch46_1001_Normal.png.import
|
||||
/demo/assets/MixamoAmy_Image.png
|
||||
/demo/assets/MixamoAmy_Image.png.import
|
||||
|
||||
17
README.md
Normal file
17
README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Synced Animation Graphs for Godot
|
||||
|
||||
|
||||
## Questions
|
||||
|
||||
1. Given an animation "Walk" with a call-method track and given that it is used as an input to a Blend2 node: will the
|
||||
method be called twice?
|
||||
1. a)
|
||||
|
||||
## Open Issues
|
||||
|
||||
1. Dynamic Track Caches
|
||||
|
||||
When AnimationMixer performs blends it c
|
||||
|
||||
AnimationMixer still has all sampled Animations and therefore their Tracks individually. However for the SAG
|
||||
evaluation all operations are
|
||||
4
demo/.editorconfig
Normal file
4
demo/.editorconfig
Normal file
@ -0,0 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
20
demo/.gitignore
vendored
Normal file
20
demo/.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
|
||||
.nomedia
|
||||
|
||||
# Godot-specific ignores
|
||||
.import/
|
||||
export.cfg
|
||||
export_credentials.cfg
|
||||
*.tmp
|
||||
|
||||
# Imported translations (automatically generated from CSV files)
|
||||
*.translation
|
||||
|
||||
# Mono-specific ignores
|
||||
.mono/
|
||||
data_*/
|
||||
mono_crash.*.json
|
||||
|
||||
BIN
demo/animation_library.res
Normal file
BIN
demo/animation_library.res
Normal file
Binary file not shown.
BIN
demo/assets/MixamoAmy.glb
Normal file
BIN
demo/assets/MixamoAmy.glb
Normal file
Binary file not shown.
42
demo/assets/MixamoAmy.glb.import
Normal file
42
demo/assets/MixamoAmy.glb.import
Normal file
@ -0,0 +1,42 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://d1xcqdqr1qeu6"
|
||||
path="res://.godot/imported/MixamoAmy.glb-5f8a7101bbea7c3cd32f3ebd9335cd1a.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/MixamoAmy.glb"
|
||||
dest_files=["res://.godot/imported/MixamoAmy.glb-5f8a7101bbea7c3cd32f3ebd9335cd1a.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=true
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
1
demo/icon.svg
Normal file
1
demo/icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
||||
|
After Width: | Height: | Size: 995 B |
43
demo/icon.svg.import
Normal file
43
demo/icon.svg.import
Normal file
@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://4q335xxs2p"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
29
demo/main.tscn
Normal file
29
demo/main.tscn
Normal file
@ -0,0 +1,29 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://svj53e2xoio"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://d1xcqdqr1qeu6" path="res://assets/MixamoAmy.glb" id="1_0xm2m"]
|
||||
[ext_resource type="AnimationNodeBlendTree" uid="uid://c7o0gt3li5p4g" path="res://walk_limp_blend_tree.tres" id="2_h2yge"]
|
||||
[ext_resource type="AnimationLibrary" uid="uid://dwubn740aqx51" path="res://animation_library.res" id="3_h2yge"]
|
||||
|
||||
[node name="Node3D" type="Node3D"]
|
||||
|
||||
[node name="MixamoAmy" parent="." instance=ExtResource("1_0xm2m")]
|
||||
|
||||
[node name="AnimationTree" type="AnimationTree" parent="."]
|
||||
active = false
|
||||
root_node = NodePath("../MixamoAmy")
|
||||
tree_root = ExtResource("2_h2yge")
|
||||
anim_player = NodePath("../MixamoAmy/AnimationPlayer")
|
||||
parameters/Blend2/blend_amount = 0.44
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
active = false
|
||||
root_node = NodePath("../MixamoAmy")
|
||||
libraries = {
|
||||
&"animation_library": ExtResource("3_h2yge")
|
||||
}
|
||||
|
||||
[node name="SyncedAnimationGraph" type="SyncedAnimationGraph" parent="."]
|
||||
animation_tree = NodePath("../AnimationTree")
|
||||
skeleton = NodePath("../MixamoAmy/Armature/Skeleton3D")
|
||||
|
||||
[editable path="MixamoAmy"]
|
||||
19
demo/project.godot
Normal file
19
demo/project.godot
Normal file
@ -0,0 +1,19 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="Synced Blend Tree Test"
|
||||
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[dotnet]
|
||||
|
||||
project/assembly_name="Synced Blend Tree Test"
|
||||
19
demo/walk_limp_blend_tree.tres
Normal file
19
demo/walk_limp_blend_tree.tres
Normal file
@ -0,0 +1,19 @@
|
||||
[gd_resource type="AnimationNodeBlendTree" load_steps=4 format=3 uid="uid://c7o0gt3li5p4g"]
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_fpiwu"]
|
||||
animation = &"Limping-InPlace"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_4sffn"]
|
||||
animation = &"Walk-InPlace"
|
||||
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_w6plo"]
|
||||
|
||||
[resource]
|
||||
nodes/output/position = Vector2(860, 160)
|
||||
nodes/Animation/node = SubResource("AnimationNodeAnimation_4sffn")
|
||||
nodes/Animation/position = Vector2(280, 100)
|
||||
"nodes/Animation 2/node" = SubResource("AnimationNodeAnimation_fpiwu")
|
||||
"nodes/Animation 2/position" = Vector2(280, 300)
|
||||
nodes/Blend2/node = SubResource("AnimationNodeBlend2_w6plo")
|
||||
nodes/Blend2/position = Vector2(640, 160)
|
||||
node_connections = [&"output", 0, &"Blend2", &"Blend2", 0, &"Animation", &"Blend2", 1, &"Animation 2"]
|
||||
@ -1,35 +1,108 @@
|
||||
#include "synced_animation_graph.h"
|
||||
|
||||
#include "core/os/time.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
||||
|
||||
void SyncedAnimationGraph::_bind_methods() {
|
||||
print_line(vformat("binding methods"));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_active", "active"), &SyncedAnimationGraph::set_active);
|
||||
ClassDB::bind_method(D_METHOD("is_active"), &SyncedAnimationGraph::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("get_callback_mode_process"), &SyncedAnimationGraph::get_callback_mode_process);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_callback_mode_method", "mode"), &SyncedAnimationGraph::set_callback_mode_method);
|
||||
ClassDB::bind_method(D_METHOD("get_callback_mode_method"), &SyncedAnimationGraph::get_callback_mode_method);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_animation_tree", "animation_tree"), &SyncedAnimationGraph::set_animation_tree);
|
||||
ClassDB::bind_method(D_METHOD("get_animation_tree"), &SyncedAnimationGraph::get_animation_tree);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_tree", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationTree"), "set_animation_tree", "get_animation_tree");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &SyncedAnimationGraph::set_skeleton);
|
||||
ClassDB::bind_method(D_METHOD("get_skeleton"), &SyncedAnimationGraph::get_skeleton);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_skeleton", "get_skeleton");
|
||||
|
||||
ADD_SIGNAL(MethodInfo(SNAME("animation_tree_changed")));
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
case Node::NOTIFICATION_READY: {
|
||||
_set_process(true);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
_process_animation(get_process_delta_time());
|
||||
case Node::NOTIFICATION_INTERNAL_PROCESS: {
|
||||
if (active) {
|
||||
_process_graph(get_process_delta_time());
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
_process_animation(get_physics_process_delta_time());
|
||||
case Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (active) {
|
||||
_process_graph(get_physics_process_delta_time());
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::set_active(bool p_active) {
|
||||
if (active == p_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
active = p_active;
|
||||
_set_process(processing, true);
|
||||
}
|
||||
|
||||
bool SyncedAnimationGraph::is_active() const {
|
||||
return active;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::set_callback_mode_process(AnimationMixer::AnimationCallbackModeProcess p_mode) {
|
||||
if (callback_mode_process == p_mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool was_active = is_active();
|
||||
if (was_active) {
|
||||
set_active(false);
|
||||
}
|
||||
|
||||
callback_mode_process = p_mode;
|
||||
|
||||
if (was_active) {
|
||||
set_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationMixer::AnimationCallbackModeProcess SyncedAnimationGraph::get_callback_mode_process() const {
|
||||
return callback_mode_process;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::set_callback_mode_method(AnimationMixer::AnimationCallbackModeMethod p_mode) {
|
||||
callback_mode_method = p_mode;
|
||||
emit_signal(SNAME("mixer_updated"));
|
||||
}
|
||||
|
||||
AnimationMixer::AnimationCallbackModeMethod SyncedAnimationGraph::get_callback_mode_method() const {
|
||||
return callback_mode_method;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode) {
|
||||
callback_mode_discrete = p_mode;
|
||||
emit_signal(SNAME("mixer_updated"));
|
||||
}
|
||||
|
||||
AnimationMixer::AnimationCallbackModeDiscrete SyncedAnimationGraph::get_callback_mode_discrete() const {
|
||||
return callback_mode_discrete;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::set_animation_tree(const NodePath &p_path) {
|
||||
animation_tree = p_path;
|
||||
animation_tree_path = p_path;
|
||||
if (p_path.is_empty()) {
|
||||
// set_root_node(SceneStringName(path_pp));
|
||||
// while (animation_libraries.size()) {
|
||||
@ -40,31 +113,134 @@ void SyncedAnimationGraph::set_animation_tree(const NodePath &p_path) {
|
||||
}
|
||||
|
||||
NodePath SyncedAnimationGraph::get_animation_tree() const {
|
||||
return animation_tree;
|
||||
return animation_tree_path;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::set_skeleton(const NodePath &p_path) {
|
||||
skeleton_path = p_path;
|
||||
if (p_path.is_empty()) {
|
||||
// set_root_node(SceneStringName(path_pp));
|
||||
// while (animation_libraries.size()) {
|
||||
// remove_animation_library(animation_libraries[0].name);
|
||||
// }
|
||||
}
|
||||
emit_signal(SNAME("skeleton_changed")); // Needs to unpin AnimationPlayerEditor.
|
||||
}
|
||||
|
||||
NodePath SyncedAnimationGraph::get_skeleton() const {
|
||||
return skeleton_path;
|
||||
}
|
||||
|
||||
void SyncedAnimationGraph::_ready(const NodePath &p_path) {
|
||||
print_line(vformat("synced animation graph ready!"));
|
||||
}
|
||||
|
||||
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));
|
||||
// }
|
||||
void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
||||
if (skeleton_path.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
float current_time = Time::get_singleton()->get_unix_time_from_system();
|
||||
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
AnimationTree *animation_tree = Object::cast_to<AnimationTree>(get_node_or_null(animation_tree_path));
|
||||
if (!animation_tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Animation> animation = animation_tree->get_animation("Walk-InPlace");
|
||||
if (!animation.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// LocalVector<AnimationMixer::TrackCache *> &track_num_to_track_cache = animation_track_num_to_track_cache[a];
|
||||
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||
// real_t a_length = animation->get_length();
|
||||
int count = tracks.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
const Animation::Track *animation_track = tracks_ptr[i];
|
||||
if (!animation_track->enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Animation::TrackType ttype = animation_track->type;
|
||||
switch (ttype) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
AnimationMixer::TrackCacheTransform *track_xform = memnew(AnimationMixer::TrackCacheTransform);
|
||||
track_xform->type = Animation::TYPE_POSITION_3D;
|
||||
track_xform->bone_idx = -1;
|
||||
track_xform->skeleton_id = skeleton->get_instance_id();
|
||||
NodePath path = animation->track_get_path(i);
|
||||
|
||||
double animation_time = Math::fposmod(current_time, animation->get_length());
|
||||
if (path.get_subname_count() == 1) {
|
||||
int bone_idx = skeleton->find_bone(path.get_subname(0));
|
||||
if (bone_idx != -1) {
|
||||
track_xform->bone_idx = bone_idx;
|
||||
Vector3 pos;
|
||||
animation->try_position_track_interpolate(i, animation_time, &pos);
|
||||
skeleton->set_bone_pose_position(bone_idx, pos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
AnimationMixer::TrackCacheTransform *track_xform = memnew(AnimationMixer::TrackCacheTransform);
|
||||
track_xform->type = Animation::TYPE_POSITION_3D;
|
||||
track_xform->bone_idx = -1;
|
||||
track_xform->skeleton_id = skeleton->get_instance_id();
|
||||
NodePath path = animation->track_get_path(i);
|
||||
|
||||
double animation_time = Math::fposmod(current_time, animation->get_length());
|
||||
if (path.get_subname_count() == 1) {
|
||||
int bone_idx = skeleton->find_bone(path.get_subname(0));
|
||||
if (bone_idx != -1) {
|
||||
track_xform->bone_idx = bone_idx;
|
||||
Quaternion rot;
|
||||
animation->try_rotation_track_interpolate(i, animation_time, &rot);
|
||||
skeleton->set_bone_pose_rotation(bone_idx, rot);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// skeleton->set_bone_pose_position(3, Vector3(sin(current_time) * 10., 0., 0.));
|
||||
skeleton->force_update_all_bone_transforms();
|
||||
|
||||
|
||||
// TrackCache *track = track_num_to_track_cache[i];
|
||||
// if (track == nullptr) {
|
||||
// continue; // No path, but avoid error spamming.
|
||||
// }
|
||||
}
|
||||
|
||||
// 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));
|
||||
// }
|
||||
|
||||
|
||||
void SyncedAnimationGraph::_set_process(bool p_process, bool p_force) {
|
||||
if (processing == p_process && !p_force) {
|
||||
return;
|
||||
|
||||
@ -7,19 +7,44 @@ class SyncedAnimationGraph : public Node {
|
||||
GDCLASS(SyncedAnimationGraph, Node);
|
||||
|
||||
private:
|
||||
NodePath animation_tree;
|
||||
bool processing = false;
|
||||
NodePath animation_tree_path;
|
||||
NodePath skeleton_path;
|
||||
|
||||
void set_animation_tree(const NodePath &p_path);
|
||||
NodePath get_animation_tree() const;
|
||||
|
||||
void set_skeleton(const NodePath &p_path);
|
||||
NodePath get_skeleton() const;
|
||||
|
||||
// AnimationMixer::TrackCache
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
/* ---- General settings for animation ---- */
|
||||
AnimationMixer::AnimationCallbackModeProcess callback_mode_process = AnimationMixer::ANIMATION_CALLBACK_MODE_PROCESS_IDLE;
|
||||
AnimationMixer::AnimationCallbackModeMethod callback_mode_method = AnimationMixer::ANIMATION_CALLBACK_MODE_METHOD_DEFERRED;
|
||||
AnimationMixer::AnimationCallbackModeDiscrete callback_mode_discrete = AnimationMixer::ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE;
|
||||
|
||||
bool processing = false;
|
||||
bool active = true;
|
||||
|
||||
public:
|
||||
void _ready(const NodePath &p_path);
|
||||
void _process_animation(double p_delta, bool p_update_only = false);
|
||||
void _process_graph(double p_delta, bool p_update_only = false);
|
||||
|
||||
void set_active(bool p_active);
|
||||
bool is_active() const;
|
||||
|
||||
void set_callback_mode_process(AnimationMixer::AnimationCallbackModeProcess p_mode);
|
||||
AnimationMixer::AnimationCallbackModeProcess get_callback_mode_process() const;
|
||||
|
||||
void set_callback_mode_method(AnimationMixer::AnimationCallbackModeMethod p_mode);
|
||||
AnimationMixer::AnimationCallbackModeMethod get_callback_mode_method() const;
|
||||
|
||||
void set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode);
|
||||
AnimationMixer::AnimationCallbackModeDiscrete get_callback_mode_discrete() const;
|
||||
|
||||
SyncedAnimationGraph();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user