diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index 44ba38236a01..0bd062874e98 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -117,6 +117,12 @@ bool NodePath::operator==(const NodePath &p_path) const { return false; } + if (data->hash_cache_valid && p_path.data->hash_cache_valid) { + if (data->hash_cache != p_path.data->hash_cache) { + return false; + } + } + if (data->absolute != p_path.data->absolute) { return false; } diff --git a/core/templates/a_hash_map.h b/core/templates/a_hash_map.h index 079cf1b13020..1eb217f81bec 100644 --- a/core/templates/a_hash_map.h +++ b/core/templates/a_hash_map.h @@ -339,6 +339,22 @@ class AHashMap { return nullptr; } + TValue &get_value_ref_or_add_default(const TKey &p_key, bool &r_was_added) { + uint32_t element_idx = 0; + uint32_t meta_idx = 0; + uint32_t hash = _hash(p_key); + bool exists = _lookup_idx_with_hash(p_key, element_idx, meta_idx, hash); + + if (exists) { + r_was_added = false; + return _elements[element_idx].value; + } else { + r_was_added = true; + element_idx = _insert_element(p_key, TValue(), hash); + return _elements[element_idx].value; + } + } + bool has(const TKey &p_key) const { uint32_t _idx = 0; uint32_t meta_idx = 0; @@ -592,17 +608,8 @@ class AHashMap { } TValue &operator[](const TKey &p_key) { - uint32_t element_idx = 0; - uint32_t meta_idx = 0; - uint32_t hash = _hash(p_key); - bool exists = _lookup_idx_with_hash(p_key, element_idx, meta_idx, hash); - - if (exists) { - return _elements[element_idx].value; - } else { - element_idx = _insert_element(p_key, TValue(), hash); - return _elements[element_idx].value; - } + bool dummy; + return get_value_ref_or_add_default(p_key, dummy); } /* Insert */ diff --git a/editor/animation/animation_blend_space_1d_editor.cpp b/editor/animation/animation_blend_space_1d_editor.cpp index ff435babcea3..d4be0428a4ed 100644 --- a/editor/animation/animation_blend_space_1d_editor.cpp +++ b/editor/animation/animation_blend_space_1d_editor.cpp @@ -77,12 +77,12 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref classes; - ClassDB::get_inheriters_from_class("AnimationRootNode", classes); + ClassDB::get_inheriters_from_class(SNAME("AnimationRootNode"), classes); classes.sort_custom(); menu->add_submenu_node_item(TTR("Add Animation"), animations_menu); - List names; + LocalVector names; tree->get_animation_list(&names); for (const StringName &E : names) { diff --git a/editor/animation/animation_blend_space_1d_editor.h b/editor/animation/animation_blend_space_1d_editor.h index 8a4968d43def..146e7cd1a4b9 100644 --- a/editor/animation/animation_blend_space_1d_editor.h +++ b/editor/animation/animation_blend_space_1d_editor.h @@ -94,7 +94,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { PopupMenu *menu = nullptr; PopupMenu *animations_menu = nullptr; - Vector animations_to_add; + Vector animations_to_add; float add_point_pos = 0.0f; Vector points; diff --git a/editor/animation/animation_blend_space_2d_editor.cpp b/editor/animation/animation_blend_space_2d_editor.cpp index b9d86de9b1f5..eca0fd5e006c 100644 --- a/editor/animation/animation_blend_space_2d_editor.cpp +++ b/editor/animation/animation_blend_space_2d_editor.cpp @@ -126,7 +126,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Refadd_submenu_node_item(TTR("Add Animation"), animations_menu); - List names; + LocalVector names; tree->get_animation_list(&names); for (const StringName &E : names) { animations_menu->add_icon_item(get_editor_theme_icon(SNAME("Animation")), E); diff --git a/editor/animation/animation_blend_space_2d_editor.h b/editor/animation/animation_blend_space_2d_editor.h index 008356cfa315..f913008922d2 100644 --- a/editor/animation/animation_blend_space_2d_editor.h +++ b/editor/animation/animation_blend_space_2d_editor.h @@ -99,7 +99,7 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { PopupMenu *menu = nullptr; PopupMenu *animations_menu = nullptr; - Vector animations_to_add; + Vector animations_to_add; Vector2 add_point_pos; Vector points; diff --git a/editor/animation/animation_blend_tree_editor_plugin.cpp b/editor/animation/animation_blend_tree_editor_plugin.cpp index c2940b3f2484..0768e656a5f8 100644 --- a/editor/animation/animation_blend_tree_editor_plugin.cpp +++ b/editor/animation/animation_blend_tree_editor_plugin.cpp @@ -119,6 +119,18 @@ void AnimationNodeBlendTreeEditor::update_graph() { if (updating || blend_tree.is_null()) { return; } + if (graph_update_queued) { + return; + } + graph_update_queued = true; + // Defer to idle time, so multiple requests can be merged. + callable_mp(this, &AnimationNodeBlendTreeEditor::update_graph_immediately).call_deferred(); +} + +void AnimationNodeBlendTreeEditor::update_graph_immediately() { + if (updating || blend_tree.is_null()) { + return; + } AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); if (!tree) { @@ -174,8 +186,8 @@ void AnimationNodeBlendTreeEditor::update_graph() { name->set_custom_minimum_size(Vector2(100, 0) * EDSCALE); node->add_child(name); node->set_slot(0, false, 0, Color(), true, read_only ? -1 : 0, get_theme_color(SceneStringName(font_color), SNAME("Label"))); - name->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed).bind(agnode), CONNECT_DEFERRED); - name->connect(SceneStringName(focus_exited), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed_focus_out).bind(agnode), CONNECT_DEFERRED); + name->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed).bind(agnode, E), CONNECT_DEFERRED); + name->connect(SceneStringName(focus_exited), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed_focus_out).bind(agnode, E), CONNECT_DEFERRED); name->connect(SceneStringName(text_changed), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_rename_lineedit_changed), CONNECT_DEFERRED); base = 1; agnode->set_deletable(true); @@ -198,7 +210,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { node->set_slot(base + i, true, read_only ? -1 : 0, get_theme_color(SceneStringName(font_color), SNAME("Label")), false, 0, Color()); } - List pinfo; + LocalVector pinfo; agnode->get_parameter_list(&pinfo); for (const PropertyInfo &F : pinfo) { if (!(F.usage & PROPERTY_USAGE_EDITOR)) { @@ -218,7 +230,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { } prop->set_name_split_ratio(ratio); prop->update_property(); - prop->connect("property_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_property_changed)); + prop->connect(SNAME("property_changed"), callable_mp(this, &AnimationNodeBlendTreeEditor::_property_changed)); if (F.hint == PROPERTY_HINT_RESOURCE_TYPE) { // Give the resource editor some more space to make the inside readable. @@ -232,7 +244,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { } } - node->connect("dragged", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_dragged).bind(E)); + node->connect(SNAME("dragged"), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_dragged).bind(E)); if (AnimationTreeEditor::get_singleton()->can_edit(agnode)) { node->add_child(memnew(HSeparator)); @@ -272,7 +284,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { ProgressBar *pb = memnew(ProgressBar); - List anims; + LocalVector anims; tree->get_animation_list(&anims); for (const StringName &F : anims) { @@ -285,25 +297,25 @@ void AnimationNodeBlendTreeEditor::update_graph() { animations[E] = pb; node->add_child(pb); - mb->get_popup()->connect("index_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_anim_selected).bind(options, E), CONNECT_DEFERRED); + mb->get_popup()->connect(SNAME("index_pressed"), callable_mp(this, &AnimationNodeBlendTreeEditor::_anim_selected).bind(options, E), CONNECT_DEFERRED); } - Ref sb_panel = node->get_theme_stylebox(SceneStringName(panel), "GraphNode")->duplicate(); + Ref sb_panel = node->get_theme_stylebox(SceneStringName(panel), SNAME("GraphNode"))->duplicate(); if (sb_panel.is_valid()) { sb_panel->set_content_margin(SIDE_TOP, 12 * EDSCALE); sb_panel->set_content_margin(SIDE_BOTTOM, 12 * EDSCALE); node->add_theme_style_override(SceneStringName(panel), sb_panel); } - node->add_theme_constant_override("separation", 4 * EDSCALE); + node->add_theme_constant_override(SNAME("separation"), 4 * EDSCALE); } List node_connections; blend_tree->get_node_connections(&node_connections); for (const AnimationNodeBlendTree::NodeConnection &E : node_connections) { - StringName from = E.output_node; - StringName to = E.input_node; + const StringName &from = E.output_node; + const StringName &to = E.input_node; int to_idx = E.input_index; graph->connect_node(from, 0, to, to_idx); @@ -324,6 +336,8 @@ void AnimationNodeBlendTreeEditor::update_graph() { } } } + + graph_update_queued = false; } void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) { @@ -795,16 +809,16 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref &ano updating = true; - HashSet paths; - HashMap> types; + HashSet paths; + HashMap> types; { - List animation_list; + LocalVector animation_list; tree->get_animation_list(&animation_list); for (const StringName &E : animation_list) { Ref anim = tree->get_animation(E); for (int i = 0; i < anim->get_track_count(); i++) { - String track_path = String(anim->track_get_path(i)); + NodePath track_path = anim->track_get_path(i); paths.insert(track_path); String track_type_name; @@ -835,8 +849,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref &ano HashMap parenthood; - for (const String &E : paths) { - NodePath path = E; + for (const NodePath &path : paths) { TreeItem *ti = nullptr; String accum; for (int i = 0; i < path.get_name_count(); i++) { @@ -856,8 +869,8 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref &ano ti->set_selectable(0, false); ti->set_editable(0, false); - if (base->has_node(accum)) { - Node *node = base->get_node(accum); + Node *node = base->get_node_or_null(accum); + if (node) { ti->set_icon(0, EditorNode::get_singleton()->get_object_icon(node)); } @@ -866,10 +879,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref &ano } } - Node *node = nullptr; - if (base->has_node(accum)) { - node = base->get_node(accum); - } + Node *node = base->get_node_or_null(accum); if (!node) { continue; //no node, can't edit } @@ -1007,6 +1017,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { return; // Node has been changed. } + if (graph_update_queued) { + return; + } + String error; error = tree->get_editor_error_message(); @@ -1094,7 +1108,7 @@ void AnimationNodeBlendTreeEditor::_node_changed(const StringName &p_node_name) update_graph(); } -void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref p_node) { +void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref p_node, const StringName p_name) { if (blend_tree.is_null()) { return; } @@ -1104,7 +1118,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Refget_node_name(p_node); + String prev_name = p_name; ERR_FAIL_COND(prev_name.is_empty()); GraphNode *gn = Object::cast_to(graph->get_node(prev_name)); ERR_FAIL_NULL(gn); @@ -1175,11 +1189,11 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref p_node) { +void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Ref p_node, const StringName p_name) { if (current_node_rename_text.is_empty()) { return; // The text_submitted signal triggered the graph update and freed the LineEdit. } - _node_renamed(current_node_rename_text, p_node); + _node_renamed(current_node_rename_text, p_node, p_name); } void AnimationNodeBlendTreeEditor::_node_rename_lineedit_changed(const String &p_text) { diff --git a/editor/animation/animation_blend_tree_editor_plugin.h b/editor/animation/animation_blend_tree_editor_plugin.h index a74e0eb50cd2..9ea1394f488f 100644 --- a/editor/animation/animation_blend_tree_editor_plugin.h +++ b/editor/animation/animation_blend_tree_editor_plugin.h @@ -98,12 +98,13 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { static AnimationNodeBlendTreeEditor *singleton; void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which); - void _node_renamed(const String &p_text, Ref p_node); - void _node_renamed_focus_out(Ref p_node); + void _node_renamed(const String &p_text, Ref p_node, StringName p_name); + void _node_renamed_focus_out(Ref p_node, StringName p_name); void _node_rename_lineedit_changed(const String &p_text); void _node_changed(const StringName &p_node_name); String current_node_rename_text; + bool graph_update_queued; bool updating; void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index); @@ -167,6 +168,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { virtual void edit(const Ref &p_node) override; void update_graph(); + void update_graph_immediately(); AnimationNodeBlendTreeEditor(); }; diff --git a/editor/animation/animation_library_editor.cpp b/editor/animation/animation_library_editor.cpp index b36723b77da0..f96cdf84a0c5 100644 --- a/editor/animation/animation_library_editor.cpp +++ b/editor/animation/animation_library_editor.cpp @@ -374,7 +374,7 @@ void AnimationLibraryEditor::_load_files(const PackedStringArray &p_paths) { continue; } - List libs; + LocalVector libs; mixer->get_animation_library_list(&libs); bool is_already_added = false; for (const StringName &K : libs) { @@ -684,7 +684,7 @@ void AnimationLibraryEditor::update_tree() { Color ss_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)); TreeItem *root = tree->create_item(); - List libs; + LocalVector libs; Vector collapsed_lib_ids = _load_mixer_libs_folding(); mixer->get_animation_library_list(&libs); @@ -951,7 +951,7 @@ String AnimationLibraryEditor::_get_mixer_signature() const { String signature = String(); // Get all libraries sorted for consistency - List libs; + LocalVector libs; mixer->get_animation_library_list(&libs); libs.sort_custom(); diff --git a/editor/animation/animation_player_editor_plugin.cpp b/editor/animation/animation_player_editor_plugin.cpp index 68f883481d81..34b3bf3f69a9 100644 --- a/editor/animation/animation_player_editor_plugin.cpp +++ b/editor/animation/animation_player_editor_plugin.cpp @@ -811,9 +811,9 @@ void AnimationPlayerEditor::_update_animation_blend() { blend_editor.tree->clear(); - String current = animation->get_item_text(animation->get_selected()); + StringName current = animation->get_item_text(animation->get_selected()); - List anims; + LocalVector anims; player->get_animation_list(&anims); TreeItem *root = blend_editor.tree->create_item(); updating_blends = true; @@ -1020,7 +1020,7 @@ void AnimationPlayerEditor::_update_player() { return; } - List libraries; + LocalVector libraries; player->get_animation_library_list(&libraries); int active_idx = -1; @@ -1144,7 +1144,7 @@ void AnimationPlayerEditor::_update_name_dialog_library_dropdown() { } } - List libraries; + LocalVector libraries; player->get_animation_library_list(&libraries); library->clear(); @@ -1478,7 +1478,7 @@ void AnimationPlayerEditor::_current_animation_changed(const StringName &p_name) } // Determine the read-only status of the animation's library and the libraries as a whole. - List libraries; + LocalVector libraries; player->get_animation_library_list(&libraries); bool current_animation_library_is_readonly = false; diff --git a/editor/animation/animation_state_machine_editor.cpp b/editor/animation/animation_state_machine_editor.cpp index 3aa55f02ba8a..10d3cc07a6e5 100644 --- a/editor/animation/animation_state_machine_editor.cpp +++ b/editor/animation/animation_state_machine_editor.cpp @@ -821,7 +821,7 @@ void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) { animations_menu->clear(); animations_to_add.clear(); - List animation_names; + LocalVector animation_names; tree->get_animation_list(&animation_names); menu->add_submenu_node_item(TTR("Add Animation"), animations_menu); if (animation_names.is_empty()) { @@ -982,7 +982,7 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { anim->set_animation(animations_to_add[p_index]); - String base_name = animations_to_add[p_index].validate_node_name(); + String base_name = String(animations_to_add[p_index]).validate_node_name(); int base = 1; String name = base_name; while (state_machine->has_node(name)) { diff --git a/editor/animation/animation_state_machine_editor.h b/editor/animation/animation_state_machine_editor.h index d2008b40d826..13e1c5ab40a1 100644 --- a/editor/animation/animation_state_machine_editor.h +++ b/editor/animation/animation_state_machine_editor.h @@ -144,7 +144,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { PopupMenu *state_machine_menu = nullptr; PopupMenu *end_menu = nullptr; PopupMenu *animations_menu = nullptr; - Vector animations_to_add; + Vector animations_to_add; Vector nodes_to_connect; Vector2 add_node_pos; diff --git a/editor/animation/animation_track_editor.cpp b/editor/animation/animation_track_editor.cpp index fde08fc7901d..660c221a0a73 100644 --- a/editor/animation/animation_track_editor.cpp +++ b/editor/animation/animation_track_editor.cpp @@ -645,7 +645,7 @@ void AnimationTrackKeyEdit::_get_property_list(List *p_list) const if (root_path) { AnimationPlayer *ap = Object::cast_to(root_path->get_node_or_null(animation->track_get_path(track))); if (ap) { - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &E : anims) { if (!animations.is_empty()) { @@ -1251,7 +1251,7 @@ void AnimationMultiTrackKeyEdit::_get_property_list(List *p_list) if (root_path) { AnimationPlayer *ap = Object::cast_to(root_path->get_node_or_null(animation->track_get_path(first_track))); if (ap) { - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &anim : anims) { if (!animations.is_empty()) { @@ -4130,7 +4130,7 @@ void AnimationTrackEditor::_animation_track_remove_request(int p_track, Reftrack_get_path(i) == p_from_animation->track_get_path(p_track)) { // Check if the reset track isn't used by other animations. bool used = false; - List animation_list; + LocalVector animation_list; player->get_animation_list(&animation_list); for (const StringName &anim_name : animation_list) { @@ -7423,7 +7423,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { } break; case EDIT_CLEAN_UP_ANIMATION_CONFIRM: { if (cleanup_all->is_pressed()) { - List names; + LocalVector names; AnimationPlayerEditor::get_singleton()->get_player()->get_animation_list(&names); for (const StringName &E : names) { _cleanup_animation(AnimationPlayerEditor::get_singleton()->get_player()->get_animation(E)); diff --git a/editor/animation/animation_tree_editor_plugin.cpp b/editor/animation/animation_tree_editor_plugin.cpp index f4e9d7d46ada..34c5fe350915 100644 --- a/editor/animation/animation_tree_editor_plugin.cpp +++ b/editor/animation/animation_tree_editor_plugin.cpp @@ -245,7 +245,7 @@ Vector AnimationTreeEditor::get_animation_list() { return Vector(); } - List anims; + LocalVector anims; tree->get_animation_list(&anims); Vector ret; for (const StringName &E : anims) { diff --git a/editor/docks/scene_tree_dock.cpp b/editor/docks/scene_tree_dock.cpp index 74e4049e6fad..4d05ac5940a3 100644 --- a/editor/docks/scene_tree_dock.cpp +++ b/editor/docks/scene_tree_dock.cpp @@ -1931,7 +1931,7 @@ bool SceneTreeDock::_has_tracks_to_delete(Node *p_node, List &p_to_delet if (ap) { Node *root = ap->get_node(ap->get_root_node()); if (root && !p_to_delete.find(root)) { - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &E : anims) { @@ -2186,7 +2186,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap } if (!points_to_other_animation_player) { - List anims; + LocalVector anims; mixer->get_animation_list(&anims); Node *root = mixer->get_node(mixer->get_root_node()); diff --git a/editor/import/3d/post_import_plugin_skeleton_renamer.cpp b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp index 938c924f2922..399d96965447 100644 --- a/editor/import/3d/post_import_plugin_skeleton_renamer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp @@ -95,7 +95,7 @@ void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { Ref anim = ap->get_animation(name); @@ -208,7 +208,7 @@ void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_ TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { Ref anim = ap->get_animation(name); diff --git a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp index 0e995af029f6..40b8791c7d9d 100644 --- a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp @@ -167,7 +167,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { Ref anim = ap->get_animation(name); @@ -235,7 +235,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { if (String(name).contains_char('/')) { @@ -398,7 +398,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { Ref anim = ap->get_animation(name); @@ -569,7 +569,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory String general_skeleton_pathname = UNIQUE_NODE_PREFIX + profile_skeleton->get_name(); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { Ref anim = ap->get_animation(name); @@ -704,7 +704,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); ERR_CONTINUE(!ap); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { Ref anim = ap->get_animation(name); @@ -836,7 +836,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { Ref anim = ap->get_animation(name); diff --git a/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp index c6befa13f1a9..8d7a2ecf91bc 100644 --- a/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp @@ -69,7 +69,7 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); - List anims; + LocalVector anims; ap->get_animation_list(&anims); Ref unmapped_al; diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index fc686d01e91c..297867d6f159 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -615,7 +615,7 @@ void _populate_scalable_nodes_collection(Node *p_node, ScalableNodeCollection &p } AnimationPlayer *animation_player = Object::cast_to(p_node); if (animation_player) { - List animation_list; + LocalVector animation_list; animation_player->get_animation_list(&animation_list); for (const StringName &E : animation_list) { @@ -701,7 +701,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &E : anims) { Ref anim = ap->get_animation(E); @@ -1078,7 +1078,7 @@ Node *ResourceImporterScene::_pre_fix_animations(Node *p_node, Node *p_root, con if (Object::cast_to(p_node)) { AnimationPlayer *ap = Object::cast_to(p_node); - List anims; + LocalVector anims; ap->get_animation_list(&anims); AnimationImportTracks import_tracks_mode[TRACK_CHANNEL_MAX] = { @@ -1126,7 +1126,7 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co if (Object::cast_to(p_node)) { AnimationPlayer *ap = Object::cast_to(p_node); - List anims; + LocalVector anims; ap->get_animation_list(&anims); if (p_remove_immutable_tracks) { @@ -1503,10 +1503,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< for (int node_i = 0; node_i < children.size(); node_i++) { AnimationPlayer *anim_player = cast_to(children[node_i]); ERR_CONTINUE(anim_player == nullptr); - List anim_list; + LocalVector anim_list; anim_player->get_animation_list(&anim_list); if (anim_list.size() == 1) { - selected_animation_name = anim_list.front()->get(); + selected_animation_name = anim_list[0]; } rest_animation = anim_player->get_animation(selected_animation_name); if (rest_animation.is_valid()) { @@ -1872,7 +1872,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< } if (post_importer_plugins.size()) { - List anims; + LocalVector anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { if (p_animation_data.has(name)) { @@ -2097,7 +2097,7 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref a } void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_vel_error, float p_max_ang_error, int p_prc_error) { - List anim_names; + LocalVector anim_names; anim->get_animation_list(&anim_names); for (const StringName &E : anim_names) { Ref a = anim->get_animation(E); @@ -2106,7 +2106,7 @@ void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_ } void ResourceImporterScene::_compress_animations(AnimationPlayer *anim, int p_page_size_kb) { - List anim_names; + LocalVector anim_names; anim->get_animation_list(&anim_names); for (const StringName &E : anim_names) { Ref a = anim->get_animation(E); @@ -2817,7 +2817,7 @@ void ResourceImporterScene::_copy_meta(Object *p_src_object, Object *p_dst_objec } void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions) { - List anims; + LocalVector anims; p_player->get_animation_list(&anims); Node *parent = p_player->get_parent(); ERR_FAIL_NULL(parent); @@ -3332,10 +3332,10 @@ Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p for (int i = 0; i < scene->get_child_count(); i++) { AnimationPlayer *ap = Object::cast_to(scene->get_child(i)); if (ap) { - List libs; + LocalVector libs; ap->get_animation_library_list(&libs); if (libs.size()) { - library = ap->get_animation_library(libs.front()->get()); + library = ap->get_animation_library(libs[0]); break; } } diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index e976365a18c2..7e8aa49bf14e 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -454,7 +454,7 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite AnimationPlayer *anim_node = Object::cast_to(p_node); if (anim_node) { Vector animation_list; - List animations; + LocalVector animations; anim_node->get_animation_list(&animations); for (const StringName &E : animations) { _fill_animation(scene_tree, anim_node->get_animation(E), E, item); diff --git a/editor/scene/3d/root_motion_editor_plugin.cpp b/editor/scene/3d/root_motion_editor_plugin.cpp index c6f1f45ac3c5..d4e095cc13d5 100644 --- a/editor/scene/3d/root_motion_editor_plugin.cpp +++ b/editor/scene/3d/root_motion_editor_plugin.cpp @@ -66,7 +66,7 @@ void EditorPropertyRootMotion::_node_assign() { HashSet paths; { - List animations; + LocalVector animations; mixer->get_animation_list(&animations); for (const StringName &E : animations) { diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index adeebde52111..8fece1405f3f 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5254,7 +5254,7 @@ Error GLTFDocument::_serialize_animations(Ref p_state) { } for (int32_t player_i = 0; player_i < p_state->animation_players.size(); player_i++) { AnimationPlayer *animation_player = p_state->animation_players[player_i]; - List animations; + LocalVector animations; animation_player->get_animation_list(&animations); for (const StringName &animation_name : animations) { _convert_animation(p_state, animation_player, animation_name); diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index a57ea2e3fde4..432edcfcdf75 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -32,7 +32,7 @@ #include "animation_blend_tree.h" -void AnimationNodeBlendSpace1D::get_parameter_list(List *r_list) const { +void AnimationNodeBlendSpace1D::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position)); r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); @@ -123,7 +123,7 @@ void AnimationNodeBlendSpace1D::_bind_methods() { BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY); } -void AnimationNodeBlendSpace1D::get_child_nodes(List *r_child_nodes) { +void AnimationNodeBlendSpace1D::get_child_nodes(LocalVector *r_child_nodes) { for (int i = 0; i < blend_points_used; i++) { ChildNode cn; cn.name = itos(i); @@ -277,7 +277,7 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref max_weight) { max_weight = pi.weight; mind = t; @@ -358,7 +360,8 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM } } else if (sync) { pi.weight = 0; - blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); + AnimationNodeInstance &other_instance = p_instance.get_child_instance_by_path(blend_points[i].name); + blend_node(p_process_state, p_instance, &other_instance, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); } } } else { @@ -373,34 +376,33 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM } } + AnimationNodeInstance *instance_current_closest = p_instance.get_child_instance_by_path_or_null(blend_points[cur_closest].name); if (new_closest != cur_closest && new_closest != -1) { + AnimationNodeInstance *instance_new_closest = p_instance.get_child_instance_by_path_or_null(blend_points[new_closest].name); + if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) { NodeTimeInfo from; // For ping-pong loop. Ref na_c = static_cast>(blend_points[cur_closest].node); Ref na_n = static_cast>(blend_points[new_closest].node); - if (na_c.is_valid() && na_n.is_valid()) { - na_n->process_state = process_state; - na_c->process_state = process_state; - - na_n->set_backward(na_c->is_backward()); - + if (na_c.is_valid() && na_n.is_valid() && instance_current_closest && instance_new_closest) { + na_n->set_backward(*instance_new_closest, p_process_state, na_c->is_backward(*instance_current_closest, p_process_state)); na_n = nullptr; na_c = nullptr; } // See how much animation remains. pi.seeked = false; pi.weight = 0; - from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true); + from = blend_node(p_process_state, p_instance, instance_current_closest, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true); pi.time = from.position; } pi.seeked = true; pi.weight = 1.0; - mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only); + mind = blend_node(p_process_state, p_instance, instance_new_closest, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only); cur_closest = new_closest; } else { pi.weight = 1.0; - mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only); + mind = blend_node(p_process_state, p_instance, instance_current_closest, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only); } if (sync) { @@ -408,13 +410,14 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM pi.weight = 0; for (int i = 0; i < blend_points_used; i++) { if (i != cur_closest) { - blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); + AnimationNodeInstance &other_instance = p_instance.get_child_instance_by_path(blend_points[i].name); + blend_node(p_process_state, p_instance, &other_instance, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); } } } } - set_parameter(closest, cur_closest); + p_instance.set_parameter(closest, cur_closest, p_process_state.is_testing); return mind; } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index 0bd29cd97e9f..8ac4b26c9eb7 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -80,10 +80,10 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override; public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; - virtual void get_child_nodes(List *r_child_nodes) override; + virtual void get_child_nodes(LocalVector *r_child_nodes) override; void add_blend_point(const Ref &p_node, float p_position, int p_at_index = -1); void set_blend_point_position(int p_point, float p_position); @@ -112,7 +112,7 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { void set_use_sync(bool p_sync); bool is_using_sync() const; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; String get_caption() const override; Ref get_child_by_name(const StringName &p_name) const override; diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 62ba7edd4e0c..36b103418513 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -34,7 +34,7 @@ #include "core/math/geometry_2d.h" #include "scene/resources/material.h" -void AnimationNodeBlendSpace2D::get_parameter_list(List *r_list) const { +void AnimationNodeBlendSpace2D::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position)); r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); @@ -53,7 +53,7 @@ Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName } } -void AnimationNodeBlendSpace2D::get_child_nodes(List *r_child_nodes) { +void AnimationNodeBlendSpace2D::get_child_nodes(LocalVector *r_child_nodes) { for (int i = 0; i < blend_points_used; i++) { ChildNode cn; cn.name = itos(i); @@ -444,15 +444,15 @@ void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vect r_weights[2] = w; } -AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { +AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { _update_triangles(); if (!blend_points_used) { return NodeTimeInfo(); } - Vector2 blend_pos = get_parameter(blend_position); - int cur_closest = get_parameter(closest); + Vector2 blend_pos = p_instance.get_parameter(blend_position); + int cur_closest = p_instance.get_parameter(closest); NodeTimeInfo mind; //time of min distance point AnimationMixer::PlaybackInfo pi = p_playback_info; @@ -519,7 +519,8 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM if (i == triangle_points[j]) { //blend with the given weight pi.weight = blend_weights[j]; - NodeTimeInfo t = blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); + AnimationNodeInstance *other_instance = p_instance.get_child_instance_by_path_or_null(blend_points[i].name); + NodeTimeInfo t = blend_node(p_process_state, p_instance, other_instance, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); if (first || pi.weight > max_weight) { mind = t; max_weight = pi.weight; @@ -532,7 +533,8 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM if (sync && !found) { pi.weight = 0; - blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); + AnimationNodeInstance *other_instance = p_instance.get_child_instance_by_path_or_null(blend_points[i].name); + blend_node(p_process_state, p_instance, other_instance, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); } } } else { @@ -547,34 +549,33 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM } } + AnimationNodeInstance *instance_current_closest = p_instance.get_child_instance_by_path_or_null(blend_points[cur_closest].name); if (new_closest != cur_closest && new_closest != -1) { + AnimationNodeInstance *instance_new_closest = p_instance.get_child_instance_by_path_or_null(blend_points[new_closest].name); + if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) { NodeTimeInfo from; // For ping-pong loop. Ref na_c = static_cast>(blend_points[cur_closest].node); Ref na_n = static_cast>(blend_points[new_closest].node); - if (na_c.is_valid() && na_n.is_valid()) { - na_n->process_state = process_state; - na_c->process_state = process_state; - - na_n->set_backward(na_c->is_backward()); - + if (na_c.is_valid() && na_n.is_valid() && instance_current_closest && instance_new_closest) { + na_n->set_backward(*instance_new_closest, p_process_state, na_c->is_backward(*instance_current_closest, p_process_state)); na_n = nullptr; na_c = nullptr; } // See how much animation remains. pi.seeked = false; pi.weight = 0; - from = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true); + from = blend_node(p_process_state, p_instance, instance_current_closest, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, true); pi.time = from.position; } pi.seeked = true; pi.weight = 1.0; - mind = blend_node(blend_points[new_closest].node, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only); + mind = blend_node(p_process_state, p_instance, instance_new_closest, blend_points[new_closest].name, pi, FILTER_IGNORE, true, p_test_only); cur_closest = new_closest; } else { pi.weight = 1.0; - mind = blend_node(blend_points[cur_closest].node, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only); + mind = blend_node(p_process_state, p_instance, instance_current_closest, blend_points[cur_closest].name, pi, FILTER_IGNORE, true, p_test_only); } if (sync) { @@ -582,13 +583,14 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM pi.weight = 0; for (int i = 0; i < blend_points_used; i++) { if (i != cur_closest) { - blend_node(blend_points[i].node, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); + AnimationNodeInstance &other_instance = p_instance.get_child_instance_by_path(blend_points[i].name); + blend_node(p_process_state, p_instance, &other_instance, blend_points[i].name, pi, FILTER_IGNORE, true, p_test_only); } } } } - set_parameter(closest, cur_closest); + p_instance.set_parameter(closest, cur_closest, p_process_state.is_testing); return mind; } diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 4c4791c26015..1e5bf577fb08 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -93,10 +93,10 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override; public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; - virtual void get_child_nodes(List *r_child_nodes) override; + virtual void get_child_nodes(LocalVector *r_child_nodes) override; void add_blend_point(const Ref &p_node, const Vector2 &p_position, int p_at_index = -1); void set_blend_point_position(int p_point, const Vector2 &p_position); @@ -127,7 +127,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { void set_y_label(const String &p_label); String get_y_label() const; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; virtual String get_caption() const override; Vector2 get_closest_point(const Vector2 &p_point); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index dc1aa67ac5ba..9cf318ba5d8b 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -42,7 +42,7 @@ StringName AnimationNodeAnimation::get_animation() const { Vector (*AnimationNodeAnimation::get_editable_animation_list)() = nullptr; -void AnimationNodeAnimation::get_parameter_list(List *r_list) const { +void AnimationNodeAnimation::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::BOOL, backward, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } @@ -58,9 +58,10 @@ Variant AnimationNodeAnimation::get_parameter_default_value(const StringName &p_ return 0.0; } -AnimationNode::NodeTimeInfo AnimationNodeAnimation::get_node_time_info() const { +AnimationNode::NodeTimeInfo AnimationNodeAnimation::get_node_time_info(AnimationNodeInstance &instance, ProcessState &p_process_state) const { NodeTimeInfo nti; - if (!process_state->tree->has_animation(animation)) { + const Ref &anim = p_process_state.tree->get_animation_or_null(animation); + if (anim.is_null()) { return nti; } @@ -68,11 +69,10 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::get_node_time_info() const { nti.length = timeline_length; nti.loop_mode = loop_mode; } else { - Ref anim = process_state->tree->get_animation(animation); nti.length = (double)anim->get_length(); nti.loop_mode = anim->get_loop_mode(); } - nti.position = get_parameter(current_position); + nti.position = instance.get_parameter(current_position); return nti; } @@ -100,49 +100,47 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const } } -AnimationNode::NodeTimeInfo AnimationNodeAnimation::process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - process_state->is_testing = p_test_only; +AnimationNode::NodeTimeInfo AnimationNodeAnimation::process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + p_process_state.is_testing = p_test_only; AnimationMixer::PlaybackInfo pi = p_playback_info; if (p_playback_info.seeked) { if (p_playback_info.is_external_seeking) { - pi.delta = get_node_time_info().position - p_playback_info.time; + pi.delta = get_node_time_info(p_instance, p_process_state).position - p_playback_info.time; } } else { - pi.time = get_node_time_info().position + (get_parameter(backward) ? -p_playback_info.delta : p_playback_info.delta); + pi.time = get_node_time_info(p_instance, p_process_state).position + (p_instance.get_parameter(backward) ? -p_playback_info.delta : p_playback_info.delta); } - NodeTimeInfo nti = _process(pi, p_test_only); + NodeTimeInfo nti = _process(p_process_state, p_instance, pi, p_test_only); if (!p_test_only) { - set_node_time_info(nti); + set_node_time_info(p_instance, p_process_state, nti); } return nti; } -AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - if (!process_state->tree->has_animation(animation)) { - AnimationNodeBlendTree *tree = Object::cast_to(node_state.parent); - if (tree) { - String node_name = tree->get_node_name(Ref(this)); - make_invalid(vformat(RTR("On BlendTree node '%s', animation not found: '%s'"), node_name, animation)); - +AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + const Ref &anim = p_process_state.tree->get_animation_or_null(animation); + if (anim.is_null()) { + if (AnimationNodeBlendTree *tree = Object::cast_to(p_instance.parent_node)) { + const StringName &node_name = tree->get_node_name(*this); + make_invalid(p_process_state, vformat(RTR("On BlendTree node '%s', animation not found: '%s'"), node_name, animation)); } else { - make_invalid(vformat(RTR("Animation not found: '%s'"), animation)); + make_invalid(p_process_state, vformat(RTR("Animation not found: '%s'"), animation)); } return NodeTimeInfo(); } - Ref anim = process_state->tree->get_animation(animation); double anim_size = (double)anim->get_length(); - NodeTimeInfo cur_nti = get_node_time_info(); + NodeTimeInfo cur_nti = get_node_time_info(p_instance, p_process_state); double cur_len = cur_nti.length; double cur_time = p_playback_info.time; double cur_delta = p_playback_info.delta; - bool cur_backward = get_parameter(backward); + bool cur_backward = p_instance.get_parameter(backward); Animation::LoopMode cur_loop_mode = cur_nti.loop_mode; double prev_time = cur_nti.position; @@ -251,15 +249,15 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe // Emit start & finish signal. Internally, the detections are the same for backward. // We should use call_deferred since the track keys are still being processed. - if (process_state->tree && !p_test_only) { + if (p_process_state.tree && !p_test_only) { // AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection. if (is_started) { - process_state->tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_started), animation); + p_process_state.tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_started), animation); } // Finished. if (Animation::is_less_approx(prev_playback_time, anim_size) && Animation::is_greater_or_equal_approx(cur_playback_time, anim_size)) { cur_playback_time = anim_size; - process_state->tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_finished), animation); + p_process_state.tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_finished), animation); } } } @@ -280,10 +278,10 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe } pi.weight = 1.0; pi.looped_flag = looped_flag; - blend_animation(animation, pi); + blend_animation(p_process_state, p_instance, animation, pi); } - set_parameter(backward, cur_backward); + p_instance.set_parameter(backward, cur_backward, p_process_state.is_testing); return nti; } @@ -300,12 +298,12 @@ AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const { return play_mode; } -void AnimationNodeAnimation::set_backward(bool p_backward) { - set_parameter(backward, p_backward); +void AnimationNodeAnimation::set_backward(AnimationNodeInstance &p_instance, ProcessState &p_process_state, bool p_backward) { + p_instance.set_parameter(backward, p_backward, p_process_state.is_testing); } -bool AnimationNodeAnimation::is_backward() const { - return get_parameter(backward); +bool AnimationNodeAnimation::is_backward(AnimationNodeInstance &p_instance, ProcessState &p_process_state) const { + return p_instance.get_parameter(backward); } void AnimationNodeAnimation::set_advance_on_start(bool p_advance_on_start) { @@ -420,7 +418,7 @@ AnimationNodeSync::AnimationNodeSync() { } //////////////////////////////////////////////////////// -void AnimationNodeOneShot::get_parameter_list(List *r_list) const { +void AnimationNodeOneShot::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); r_list->push_back(PropertyInfo(Variant::BOOL, internal_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); @@ -538,16 +536,16 @@ bool AnimationNodeOneShot::has_filter() const { return true; } -AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - OneShotRequest cur_request = static_cast((int)get_parameter(request)); - bool cur_active = get_parameter(active); - bool cur_internal_active = get_parameter(internal_active); - NodeTimeInfo cur_nti = get_node_time_info(); - double cur_time_to_restart = get_parameter(time_to_restart); - double cur_fade_in_remaining = get_parameter(fade_in_remaining); - double cur_fade_out_remaining = get_parameter(fade_out_remaining); +AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + OneShotRequest cur_request = static_cast((int)p_instance.get_parameter(request)); + bool cur_active = p_instance.get_parameter(active); + bool cur_internal_active = p_instance.get_parameter(internal_active); + NodeTimeInfo cur_nti = get_node_time_info(p_instance, p_process_state); + double cur_time_to_restart = p_instance.get_parameter(time_to_restart); + double cur_fade_in_remaining = p_instance.get_parameter(fade_in_remaining); + double cur_fade_out_remaining = p_instance.get_parameter(fade_out_remaining); - set_parameter(request, ONE_SHOT_REQUEST_NONE); + p_instance.set_parameter(request, ONE_SHOT_REQUEST_NONE, p_process_state.is_testing); bool is_shooting = true; bool clear_remaining_fade = false; @@ -565,9 +563,9 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE; if (cur_request == ONE_SHOT_REQUEST_ABORT) { - set_parameter(internal_active, false); - set_parameter(active, false); - set_parameter(time_to_restart, -1); + p_instance.set_parameter(internal_active, false, p_process_state.is_testing); + p_instance.set_parameter(active, false, p_process_state.is_testing); + p_instance.set_parameter(time_to_restart, -1, p_process_state.is_testing); is_shooting = false; } else if (cur_request == ONE_SHOT_REQUEST_FADE_OUT && !is_fading_out) { // If fading, keep current fade. if (cur_active) { @@ -579,15 +577,15 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: // Shot is ended, do nothing. is_shooting = false; } - set_parameter(internal_active, false); - set_parameter(time_to_restart, -1); + p_instance.set_parameter(internal_active, false, p_process_state.is_testing); + p_instance.set_parameter(time_to_restart, -1, p_process_state.is_testing); } else if (!do_start && !cur_active) { if (Animation::is_greater_or_equal_approx(cur_time_to_restart, 0) && !p_seek) { cur_time_to_restart -= abs_delta; if (Animation::is_less_approx(cur_time_to_restart, 0)) { do_start = true; // Restart. } - set_parameter(time_to_restart, cur_time_to_restart); + p_instance.set_parameter(time_to_restart, cur_time_to_restart, p_process_state.is_testing); } if (!do_start) { is_shooting = false; @@ -599,18 +597,18 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: if (clear_remaining_fade) { os_seek = false; cur_fade_out_remaining = 0; - set_parameter(fade_out_remaining, 0); + p_instance.set_parameter(fade_out_remaining, 0, p_process_state.is_testing); if (is_fading_out) { is_fading_out = false; - set_parameter(internal_active, false); - set_parameter(active, false); + p_instance.set_parameter(internal_active, false, p_process_state.is_testing); + p_instance.set_parameter(active, false, p_process_state.is_testing); } } if (!is_shooting) { AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0; - return blend_input(0, pi, FILTER_IGNORE, sync, p_test_only); + return blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, sync, p_test_only); } if (do_start) { @@ -619,9 +617,9 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: cur_fade_in_remaining = fade_in; // If already active, don't fade-in again. } cur_internal_active = true; - set_parameter(request, ONE_SHOT_REQUEST_NONE); - set_parameter(internal_active, true); - set_parameter(active, true); + p_instance.set_parameter(request, ONE_SHOT_REQUEST_NONE, p_process_state.is_testing); + p_instance.set_parameter(internal_active, true, p_process_state.is_testing); + p_instance.set_parameter(active, true, p_process_state.is_testing); } real_t blend = 1.0; @@ -655,11 +653,11 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: NodeTimeInfo main_nti; if (mix == MIX_MODE_ADD) { pi.weight = 1.0; - main_nti = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only); + main_nti = blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, sync, p_test_only); } else { pi.seeked &= use_blend; pi.weight = 1.0 - blend; - main_nti = blend_input(0, pi, FILTER_BLEND, sync, p_test_only); // Unlike below, processing this edge is a corner case. + main_nti = blend_input(p_process_state, p_instance, 0, pi, FILTER_BLEND, sync, p_test_only); // Unlike below, processing this edge is a corner case. } pi = p_playback_info; @@ -671,7 +669,7 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: pi.seeked = os_seek; pi.weight = Math::is_zero_approx(blend) ? CMP_EPSILON : blend; - NodeTimeInfo os_nti = blend_input(1, pi, FILTER_PASS, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + NodeTimeInfo os_nti = blend_input(p_process_state, p_instance, 1, pi, FILTER_PASS, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (Animation::is_less_or_equal_approx(cur_fade_in_remaining, 0) && !do_start && !is_fading_out) { // Predict time scale by difference of delta times to estimate input animation's remain time in self time scale. @@ -683,17 +681,17 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: is_fading_out = true; cur_fade_out_remaining = os_rem + abs_delta; cur_fade_in_remaining = 0; - set_parameter(internal_active, false); + p_instance.set_parameter(internal_active, false, p_process_state.is_testing); } } if (!p_seek) { if (Animation::is_less_or_equal_approx(os_nti.get_remain(break_loop_at_end), 0) || (is_fading_out && Animation::is_less_or_equal_approx(cur_fade_out_remaining, 0))) { - set_parameter(internal_active, false); - set_parameter(active, false); + p_instance.set_parameter(internal_active, false, p_process_state.is_testing); + p_instance.set_parameter(active, false, p_process_state.is_testing); if (auto_restart) { double restart_sec = auto_restart_delay + Math::randd() * auto_restart_random_delay; - set_parameter(time_to_restart, restart_sec); + p_instance.set_parameter(time_to_restart, restart_sec, p_process_state.is_testing); } } if (!do_start) { @@ -702,8 +700,8 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: cur_fade_out_remaining = MAX(0, cur_fade_out_remaining - abs_delta); } - set_parameter(fade_in_remaining, cur_fade_in_remaining); - set_parameter(fade_out_remaining, cur_fade_out_remaining); + p_instance.set_parameter(fade_in_remaining, cur_fade_in_remaining, p_process_state.is_testing); + p_instance.set_parameter(fade_out_remaining, cur_fade_out_remaining, p_process_state.is_testing); return cur_internal_active ? os_nti : main_nti; } @@ -766,7 +764,7 @@ AnimationNodeOneShot::AnimationNodeOneShot() { //////////////////////////////////////////////// -void AnimationNodeAdd2::get_parameter_list(List *r_list) const { +void AnimationNodeAdd2::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater")); } @@ -788,14 +786,14 @@ bool AnimationNodeAdd2::has_filter() const { return true; } -AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - double amount = get_parameter(add_amount); +AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + double amount = p_instance.get_parameter(add_amount); AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0; - NodeTimeInfo nti = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only); + NodeTimeInfo nti = blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, sync, p_test_only); pi.weight = amount; - blend_input(1, pi, FILTER_PASS, sync, p_test_only); + blend_input(p_process_state, p_instance, 1, pi, FILTER_PASS, sync, p_test_only); return nti; } @@ -807,7 +805,7 @@ AnimationNodeAdd2::AnimationNodeAdd2() { //////////////////////////////////////////////// -void AnimationNodeAdd3::get_parameter_list(List *r_list) const { +void AnimationNodeAdd3::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, add_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater")); } @@ -829,16 +827,16 @@ bool AnimationNodeAdd3::has_filter() const { return true; } -AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - double amount = get_parameter(add_amount); +AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + double amount = p_instance.get_parameter(add_amount); AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = MAX(0, -amount); - blend_input(0, pi, FILTER_PASS, sync, p_test_only); + blend_input(p_process_state, p_instance, 0, pi, FILTER_PASS, sync, p_test_only); pi.weight = 1.0; - NodeTimeInfo nti = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only); + NodeTimeInfo nti = blend_input(p_process_state, p_instance, 1, pi, FILTER_IGNORE, sync, p_test_only); pi.weight = MAX(0, amount); - blend_input(2, pi, FILTER_PASS, sync, p_test_only); + blend_input(p_process_state, p_instance, 2, pi, FILTER_PASS, sync, p_test_only); return nti; } @@ -851,7 +849,7 @@ AnimationNodeAdd3::AnimationNodeAdd3() { ///////////////////////////////////////////// -void AnimationNodeBlend2::get_parameter_list(List *r_list) const { +void AnimationNodeBlend2::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater")); } @@ -869,14 +867,14 @@ String AnimationNodeBlend2::get_caption() const { return "Blend2"; } -AnimationNode::NodeTimeInfo AnimationNodeBlend2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - double amount = get_parameter(blend_amount); +AnimationNode::NodeTimeInfo AnimationNodeBlend2::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + double amount = p_instance.get_parameter(blend_amount); AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0 - amount; - NodeTimeInfo nti0 = blend_input(0, pi, FILTER_BLEND, sync, p_test_only); + NodeTimeInfo nti0 = blend_input(p_process_state, p_instance, 0, pi, FILTER_BLEND, sync, p_test_only); pi.weight = amount; - NodeTimeInfo nti1 = blend_input(1, pi, FILTER_PASS, sync, p_test_only); + NodeTimeInfo nti1 = blend_input(p_process_state, p_instance, 1, pi, FILTER_PASS, sync, p_test_only); return amount > 0.5 ? nti1 : nti0; // Hacky but good enough. } @@ -892,7 +890,7 @@ AnimationNodeBlend2::AnimationNodeBlend2() { ////////////////////////////////////// -void AnimationNodeBlend3::get_parameter_list(List *r_list) const { +void AnimationNodeBlend3::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, blend_amount, PROPERTY_HINT_RANGE, "-1,1,0.01,or_less,or_greater")); } @@ -910,16 +908,16 @@ String AnimationNodeBlend3::get_caption() const { return "Blend3"; } -AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - double amount = get_parameter(blend_amount); +AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + double amount = p_instance.get_parameter(blend_amount); AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = MAX(0, -amount); - NodeTimeInfo nti0 = blend_input(0, pi, FILTER_IGNORE, sync, p_test_only); + NodeTimeInfo nti0 = blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, sync, p_test_only); pi.weight = 1.0 - Math::abs(amount); - NodeTimeInfo nti1 = blend_input(1, pi, FILTER_IGNORE, sync, p_test_only); + NodeTimeInfo nti1 = blend_input(p_process_state, p_instance, 1, pi, FILTER_IGNORE, sync, p_test_only); pi.weight = MAX(0, amount); - NodeTimeInfo nti2 = blend_input(2, pi, FILTER_IGNORE, sync, p_test_only); + NodeTimeInfo nti2 = blend_input(p_process_state, p_instance, 2, pi, FILTER_IGNORE, sync, p_test_only); return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough. } @@ -932,7 +930,7 @@ AnimationNodeBlend3::AnimationNodeBlend3() { //////////////////////////////////////////////// -void AnimationNodeSub2::get_parameter_list(List *r_list) const { +void AnimationNodeSub2::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, sub_amount, PROPERTY_HINT_RANGE, "0,1,0.01,or_less,or_greater")); } @@ -954,16 +952,16 @@ bool AnimationNodeSub2::has_filter() const { return true; } -AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - double amount = get_parameter(sub_amount); +AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + double amount = p_instance.get_parameter(sub_amount); AnimationMixer::PlaybackInfo pi = p_playback_info; // Out = Sub.Transform3D^(-1) * In.Transform3D pi.weight = -amount; - blend_input(1, pi, FILTER_PASS, sync, p_test_only); + blend_input(p_process_state, p_instance, 1, pi, FILTER_PASS, sync, p_test_only); pi.weight = 1.0; - return blend_input(0, pi, FILTER_IGNORE, sync, p_test_only); + return blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, sync, p_test_only); } AnimationNodeSub2::AnimationNodeSub2() { @@ -973,7 +971,7 @@ AnimationNodeSub2::AnimationNodeSub2() { ///////////////////////////////// -void AnimationNodeTimeScale::get_parameter_list(List *r_list) const { +void AnimationNodeTimeScale::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_less,or_greater")); } @@ -991,8 +989,8 @@ String AnimationNodeTimeScale::get_caption() const { return "TimeScale"; } -AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - double cur_scale = get_parameter(scale); +AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + double cur_scale = p_instance.get_parameter(scale); AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0; @@ -1000,7 +998,7 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixe pi.delta *= cur_scale; } - return blend_input(0, pi, FILTER_IGNORE, true, p_test_only); + return blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, true, p_test_only); } AnimationNodeTimeScale::AnimationNodeTimeScale() { @@ -1009,7 +1007,7 @@ AnimationNodeTimeScale::AnimationNodeTimeScale() { //////////////////////////////////// -void AnimationNodeTimeSeek::get_parameter_list(List *r_list) const { +void AnimationNodeTimeSeek::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos_request, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); // It will be reset to -1 after seeking the position immediately. } @@ -1035,8 +1033,8 @@ bool AnimationNodeTimeSeek::is_explicit_elapse() const { return explicit_elapse; } -AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - double cur_seek_pos = get_parameter(seek_pos_request); +AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + double cur_seek_pos = p_instance.get_parameter(seek_pos_request); AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0; @@ -1044,10 +1042,10 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer pi.time = cur_seek_pos; pi.seeked = true; pi.is_external_seeking = explicit_elapse; - set_parameter(seek_pos_request, -1.0); // Reset. + p_instance.set_parameter(seek_pos_request, -1.0, p_process_state.is_testing); // Reset. } - return blend_input(0, pi, FILTER_IGNORE, true, p_test_only); + return blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, true, p_test_only); } AnimationNodeTimeSeek::AnimationNodeTimeSeek() { @@ -1123,7 +1121,7 @@ bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) con return true; } -void AnimationNodeTransition::get_parameter_list(List *r_list) const { +void AnimationNodeTransition::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); String anims; for (int i = 0; i < get_input_count(); i++) { @@ -1260,13 +1258,13 @@ bool AnimationNodeTransition::is_allow_transition_to_self() const { return allow_transition_to_self; } -AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - String cur_transition_request = get_parameter(transition_request); - int cur_current_index = get_parameter(current_index); - int cur_prev_index = get_parameter(prev_index); +AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + const String &cur_transition_request = p_instance.get_parameter(transition_request); + int cur_current_index = p_instance.get_parameter(current_index); + int cur_prev_index = p_instance.get_parameter(prev_index); - NodeTimeInfo cur_nti = get_node_time_info(); - double cur_prev_xfading = get_parameter(prev_xfading); + NodeTimeInfo cur_nti = get_node_time_info(p_instance, p_process_state); + double cur_prev_xfading = p_instance.get_parameter(prev_xfading); bool switched = false; bool restart = false; @@ -1274,16 +1272,16 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMix if (pending_update) { if (cur_current_index < 0 || cur_current_index >= get_input_count()) { - set_parameter(prev_index, -1); + p_instance.set_parameter(prev_index, -1, p_process_state.is_testing); if (get_input_count() > 0) { - set_parameter(current_index, 0); - set_parameter(current_state, get_input_name(0)); + p_instance.set_parameter(current_index, 0, p_process_state.is_testing); + p_instance.set_parameter(current_state, get_input_name(0), p_process_state.is_testing); } else { - set_parameter(current_index, -1); - set_parameter(current_state, StringName()); + p_instance.set_parameter(current_index, -1, p_process_state.is_testing); + p_instance.set_parameter(current_state, StringName(), p_process_state.is_testing); } } else { - set_parameter(current_state, get_input_name(cur_current_index)); + p_instance.set_parameter(current_state, get_input_name(cur_current_index), p_process_state.is_testing); } pending_update = false; } @@ -1308,23 +1306,22 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMix } else { switched = true; cur_prev_index = cur_current_index; - set_parameter(prev_index, cur_current_index); + p_instance.set_parameter(prev_index, cur_current_index, p_process_state.is_testing); cur_current_index = new_idx; - set_parameter(current_index, cur_current_index); - set_parameter(current_state, cur_transition_request); + p_instance.set_parameter(current_index, cur_current_index, p_process_state.is_testing); + p_instance.set_parameter(current_state, cur_transition_request, p_process_state.is_testing); } } else { ERR_PRINT("No such input: '" + cur_transition_request + "'"); } - cur_transition_request = String(); - set_parameter(transition_request, cur_transition_request); + p_instance.set_parameter(transition_request, String(), p_process_state.is_testing); } if (clear_remaining_fade) { cur_prev_xfading = 0; - set_parameter(prev_xfading, 0); + p_instance.set_parameter(prev_xfading, 0, p_process_state.is_testing); cur_prev_index = -1; - set_parameter(prev_index, -1); + p_instance.set_parameter(prev_index, -1, p_process_state.is_testing); } AnimationMixer::PlaybackInfo pi = p_playback_info; @@ -1334,7 +1331,7 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMix pi.time = 0; pi.seeked = true; pi.weight = 1.0; - return blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only); + return blend_input(p_process_state, p_instance, cur_current_index, pi, FILTER_IGNORE, true, p_test_only); } if (switched) { @@ -1349,16 +1346,16 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMix pi.weight = 0; for (int i = 0; i < get_input_count(); i++) { if (i != cur_current_index && i != cur_prev_index) { - blend_input(i, pi, FILTER_IGNORE, true, p_test_only); + blend_input(p_process_state, p_instance, i, pi, FILTER_IGNORE, true, p_test_only); } } } if (cur_prev_index < 0) { // Process current animation, check for transition. pi.weight = 1.0; - cur_nti = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only); + cur_nti = blend_input(p_process_state, p_instance, cur_current_index, pi, FILTER_IGNORE, true, p_test_only); if (input_data[cur_current_index].auto_advance && Animation::is_less_or_equal_approx(cur_nti.get_remain(input_data[cur_current_index].break_loop_at_end), xfade_time)) { - set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count())); + p_instance.set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count()), p_process_state.is_testing); } } else { // Cross-fading from prev to current. @@ -1382,21 +1379,21 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(const AnimationMix pi.time = 0; pi.seeked = true; } - cur_nti = blend_input(cur_current_index, pi, FILTER_IGNORE, true, p_test_only); + cur_nti = blend_input(p_process_state, p_instance, cur_current_index, pi, FILTER_IGNORE, true, p_test_only); pi = p_playback_info; pi.seeked &= use_blend; pi.weight = blend; - blend_input(cur_prev_index, pi, FILTER_IGNORE, true, p_test_only); + blend_input(p_process_state, p_instance, cur_prev_index, pi, FILTER_IGNORE, true, p_test_only); if (!p_seek) { if (Animation::is_less_or_equal_approx(cur_prev_xfading, 0)) { - set_parameter(prev_index, -1); + p_instance.set_parameter(prev_index, -1, p_process_state.is_testing); } cur_prev_xfading -= Math::abs(p_playback_info.delta); } } - set_parameter(prev_xfading, cur_prev_xfading); + p_instance.set_parameter(prev_xfading, cur_prev_xfading, p_process_state.is_testing); return cur_nti; } @@ -1446,10 +1443,10 @@ String AnimationNodeOutput::get_caption() const { return "Output"; } -AnimationNode::NodeTimeInfo AnimationNodeOutput::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { +AnimationNode::NodeTimeInfo AnimationNodeOutput::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0; - return blend_input(0, pi, FILTER_IGNORE, true, p_test_only); + return blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, true, p_test_only); } AnimationNodeOutput::AnimationNodeOutput() { @@ -1457,7 +1454,7 @@ AnimationNodeOutput::AnimationNodeOutput() { } /////////////////////////////////////////////////////// -void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref p_node, const Vector2 &p_position) { +void AnimationNodeBlendTree::add_node(const StringName &p_name, const Ref &p_node, const Vector2 &p_position) { ERR_FAIL_COND(nodes.has(p_name)); ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_name == SceneStringName(output)); @@ -1479,19 +1476,30 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref AnimationNodeBlendTree::get_node(const StringName &p_name) const { - ERR_FAIL_COND_V(!nodes.has(p_name), Ref()); + const Node *node = nodes.getptr(p_name); + ERR_FAIL_NULL_V(node, Ref()); + return node->node; +} - return nodes[p_name].node; +const Ref &AnimationNodeBlendTree::get_node_or_null(const StringName &p_name) const { + const Node *node = nodes.getptr(p_name); + if (!node) { + const static Ref empty = Ref(); + return empty; + } + return node->node; } -StringName AnimationNodeBlendTree::get_node_name(const Ref &p_node) const { +// Keep in mind multiple Nodes can use the same AnimationNode. +const StringName &AnimationNodeBlendTree::get_node_name(const AnimationNode &p_node) const { for (const KeyValue &E : nodes) { - if (E.value.node == p_node) { + if (E.value.node.ptr() == &p_node) { return E.key; } } - ERR_FAIL_V(StringName()); + static const StringName empty = StringName(); + ERR_FAIL_V(empty); } void AnimationNodeBlendTree::set_node_position(const StringName &p_node, const Vector2 &p_position) { @@ -1504,7 +1512,7 @@ Vector2 AnimationNodeBlendTree::get_node_position(const StringName &p_node) cons return nodes[p_node].position; } -void AnimationNodeBlendTree::get_child_nodes(List *r_child_nodes) { +void AnimationNodeBlendTree::get_child_nodes(LocalVector *r_child_nodes) { Vector ns; for (const KeyValue &E : nodes) { @@ -1524,8 +1532,9 @@ bool AnimationNodeBlendTree::has_node(const StringName &p_name) const { } Vector AnimationNodeBlendTree::get_node_connection_array(const StringName &p_name) const { - ERR_FAIL_COND_V(!nodes.has(p_name), Vector()); - return nodes[p_name].connections; + const Node *node = nodes.getptr(p_name); + ERR_FAIL_NULL_V(node, Vector()); + return node->connections; } void AnimationNodeBlendTree::remove_node(const StringName &p_name) { @@ -1665,15 +1674,18 @@ String AnimationNodeBlendTree::get_caption() const { return "BlendTree"; } -AnimationNode::NodeTimeInfo AnimationNodeBlendTree::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - Ref output = nodes[SceneStringName(output)].node; - node_state.connections = nodes[SceneStringName(output)].connections; +AnimationNode::NodeTimeInfo AnimationNodeBlendTree::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + const Ref output = nodes[SceneStringName(output)].node; ERR_FAIL_COND_V(output.is_null(), NodeTimeInfo()); + p_instance.connections = nodes[SceneStringName(output)].connections; + p_instance.parent_node = this; + AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0; - return _blend_node(output, "output", this, pi, FILTER_IGNORE, true, p_test_only, nullptr); + AnimationNodeInstance &output_instance = p_instance.get_child_instance_by_path(SceneStringName(output)); + return _blend_node(p_process_state, p_instance, output_instance, SNAME("output"), this, pi, FILTER_IGNORE, true, p_test_only, nullptr); } LocalVector AnimationNodeBlendTree::get_node_list() const { diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 59edf47657da..7295c3c568e1 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -57,16 +57,16 @@ class AnimationNodeAnimation : public AnimationRootNode { PLAY_MODE_BACKWARD }; - void get_parameter_list(List *r_list) const override; + void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; - virtual NodeTimeInfo get_node_time_info() const override; // Wrapper of get_parameter(). + virtual NodeTimeInfo get_node_time_info(AnimationNodeInstance &instance, ProcessState &p_process_state) const override; // Wrapper of get_parameter(). static Vector (*get_editable_animation_list)(); virtual String get_caption() const override; - virtual NodeTimeInfo process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; void set_animation(const StringName &p_name); StringName get_animation() const; @@ -74,8 +74,8 @@ class AnimationNodeAnimation : public AnimationRootNode { void set_play_mode(PlayMode p_play_mode); PlayMode get_play_mode() const; - void set_backward(bool p_backward); - bool is_backward() const; + void set_backward(AnimationNodeInstance &p_instance, ProcessState &p_process_state, bool p_backward); + bool is_backward(AnimationNodeInstance &p_instance, ProcessState &p_process_state) const; void set_advance_on_start(bool p_advance_on_start); bool is_advance_on_start() const; @@ -161,7 +161,7 @@ class AnimationNodeOneShot : public AnimationNodeSync { static void _bind_methods(); public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual bool is_parameter_read_only(const StringName &p_parameter) const override; @@ -194,7 +194,7 @@ class AnimationNodeOneShot : public AnimationNodeSync { bool is_loop_broken_at_end() const; virtual bool has_filter() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeOneShot(); }; @@ -208,13 +208,13 @@ class AnimationNodeAdd2 : public AnimationNodeSync { StringName add_amount = PNAME("add_amount"); public: - void get_parameter_list(List *r_list) const override; + void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; virtual bool has_filter() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeAdd2(); }; @@ -225,13 +225,13 @@ class AnimationNodeAdd3 : public AnimationNodeSync { StringName add_amount = PNAME("add_amount"); public: - void get_parameter_list(List *r_list) const override; + void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; virtual bool has_filter() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeAdd3(); }; @@ -242,11 +242,11 @@ class AnimationNodeBlend2 : public AnimationNodeSync { StringName blend_amount = PNAME("blend_amount"); public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; virtual bool has_filter() const override; AnimationNodeBlend2(); @@ -258,12 +258,12 @@ class AnimationNodeBlend3 : public AnimationNodeSync { StringName blend_amount = PNAME("blend_amount"); public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeBlend3(); }; @@ -273,13 +273,13 @@ class AnimationNodeSub2 : public AnimationNodeSync { StringName sub_amount = PNAME("sub_amount"); public: - void get_parameter_list(List *r_list) const override; + void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; virtual bool has_filter() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeSub2(); }; @@ -290,12 +290,12 @@ class AnimationNodeTimeScale : public AnimationNode { StringName scale = PNAME("scale"); public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeTimeScale(); }; @@ -310,12 +310,12 @@ class AnimationNodeTimeSeek : public AnimationNode { static void _bind_methods(); public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; void set_explicit_elapse(bool p_enable); bool is_explicit_elapse() const; @@ -355,7 +355,7 @@ class AnimationNodeTransition : public AnimationNodeSync { void _get_property_list(List *p_list) const; public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual bool is_parameter_read_only(const StringName &p_parameter) const override; @@ -385,7 +385,7 @@ class AnimationNodeTransition : public AnimationNodeSync { void set_allow_transition_to_self(bool p_enable); bool is_allow_transition_to_self() const; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeTransition(); }; @@ -395,7 +395,7 @@ class AnimationNodeOutput : public AnimationNode { public: virtual String get_caption() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; AnimationNodeOutput(); }; @@ -441,18 +441,19 @@ class AnimationNodeBlendTree : public AnimationRootNode { //no need to check for cycles due to tree topology }; - void add_node(const StringName &p_name, Ref p_node, const Vector2 &p_position = Vector2()); + void add_node(const StringName &p_name, const Ref &p_node, const Vector2 &p_position = Vector2()); Ref get_node(const StringName &p_name) const; + const Ref &get_node_or_null(const StringName &p_name) const; void remove_node(const StringName &p_name); void rename_node(const StringName &p_name, const StringName &p_new_name); bool has_node(const StringName &p_name) const; - StringName get_node_name(const Ref &p_node) const; + const StringName &get_node_name(const AnimationNode &p_node) const; Vector get_node_connection_array(const StringName &p_name) const; void set_node_position(const StringName &p_node, const Vector2 &p_position); Vector2 get_node_position(const StringName &p_node) const; - virtual void get_child_nodes(List *r_child_nodes) override; + virtual void get_child_nodes(LocalVector *r_child_nodes) override; void connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node); void disconnect_node(const StringName &p_node, int p_input_index); @@ -467,7 +468,7 @@ class AnimationNodeBlendTree : public AnimationRootNode { void get_node_connections(List *r_connections) const; virtual String get_caption() const override; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; LocalVector get_node_list() const; TypedArray get_node_list_as_typed_array() const; diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 27fb0f4ccea0..78bae9ef4ef0 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -138,16 +138,19 @@ void AnimationMixer::_animation_set_cache_update() { for (const AnimationLibraryData &lib : animation_libraries) { for (const KeyValue> &K : lib.library->animations) { StringName key = lib.name == StringName() ? K.key : StringName(String(lib.name) + "/" + String(K.key)); - if (!animation_set.has(key)) { - AnimationData ad; + + bool was_added = false; + AnimationData &ad = animation_set.get_value_ref_or_add_default(key, was_added); + + if (was_added) { ad.animation = K.value; ad.animation_library = lib.name; ad.name = key; ad.last_update = animation_set_update_pass; - animation_set.insert(ad.name, ad); + //todo: possible issue here, not using key, but instead ad.name? + //animation_set.insert(ad.name, ad); cache_valid = false; // No need to delete the cache, but it must be updated to add track caches. } else { - AnimationData &ad = animation_set[key]; if (ad.last_update != animation_set_update_pass) { // Was not updated, update. If the animation is duplicated, the second one will be ignored. if (ad.animation != K.value || ad.animation_library != lib.name) { @@ -164,7 +167,7 @@ void AnimationMixer::_animation_set_cache_update() { } // Check removed. - List to_erase; + LocalVector to_erase; for (const KeyValue &E : animation_set) { if (E.value.last_update != animation_set_update_pass) { // Was not updated, must be erased. @@ -173,9 +176,8 @@ void AnimationMixer::_animation_set_cache_update() { } } - while (to_erase.size()) { - animation_set.erase(to_erase.front()->get()); - to_erase.pop_front(); + for (const StringName &E : to_erase) { + animation_set.erase(E); } if (clear_cache_needed) { @@ -191,7 +193,7 @@ void AnimationMixer::_animation_added(const StringName &p_name, const StringName } void AnimationMixer::_animation_removed(const StringName &p_name, const StringName &p_library) { - StringName name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); + const StringName name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); if (!animation_set.has(name)) { return; // No need to update because not the one from the library being used. @@ -203,8 +205,8 @@ void AnimationMixer::_animation_removed(const StringName &p_name, const StringNa } void AnimationMixer::_animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library) { - StringName from_name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); - StringName to_name = p_library == StringName() ? p_to_name : StringName(String(p_library) + "/" + String(p_to_name)); + const StringName from_name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); + const StringName to_name = p_library == StringName() ? p_to_name : StringName(String(p_library) + "/" + String(p_to_name)); if (!animation_set.has(from_name)) { return; // No need to update because not the one from the library being used. @@ -238,7 +240,7 @@ TypedArray AnimationMixer::_get_animation_library_list() const { return ret; } -void AnimationMixer::get_animation_library_list(List *p_libraries) const { +void AnimationMixer::get_animation_library_list(LocalVector *p_libraries) const { for (const AnimationLibraryData &lib : animation_libraries) { p_libraries->push_back(lib.name); } @@ -273,13 +275,14 @@ StringName AnimationMixer::get_animation_library_name(const Ref &p_animation) const { +const StringName &AnimationMixer::find_animation_library(const Ref &p_animation) const { for (const KeyValue &E : animation_set) { if (E.value.animation == p_animation) { return E.value.animation_library; } } - return StringName(); + const static StringName empty = StringName(); + return empty; } Error AnimationMixer::add_animation_library(const StringName &p_name, const Ref &p_animation_library) { @@ -382,21 +385,38 @@ void AnimationMixer::rename_animation_library(const StringName &p_name, const St notify_property_list_changed(); } -void AnimationMixer::get_animation_list(List *p_animations) const { - List anims; - for (const KeyValue &E : animation_set) { - anims.push_back(E.key); - } - anims.sort(); - for (const String &E : anims) { - p_animations->push_back(E); +void AnimationMixer::get_animation_list(LocalVector *p_animations, const bool p_sorted) const { + // Sorting is wasteful (both on sorting, and converting to and from String and to StringName). + if (p_sorted) { + LocalVector anims; + for (const KeyValue &E : animation_set) { + anims.push_back(E.key); + } + anims.sort(); + + for (const String &E : anims) { + p_animations->push_back(E); + } + } else { + for (const KeyValue &E : animation_set) { + p_animations->push_back(E.key); + } } } -Ref AnimationMixer::get_animation(const StringName &p_name) const { - ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref(), vformat("Animation not found: \"%s\".", p_name)); - const AnimationData &anim_data = animation_set[p_name]; - return anim_data.animation; +const Ref &AnimationMixer::get_animation(const StringName &p_name) const { + const Ref &animation = get_animation_or_null(p_name); + ERR_FAIL_COND_V_MSG(animation.is_null(), animation, vformat("Animation not found: \"%s\".", p_name)); + return animation; +} + +const Ref &AnimationMixer::get_animation_or_null(const StringName &p_name) const { + const AnimationData *ad = animation_set.getptr(p_name); + if (!ad) { + const static Ref empty = Ref(); + return empty; + } + return ad->animation; } bool AnimationMixer::has_animation(const StringName &p_name) const { @@ -606,7 +626,7 @@ void AnimationMixer::_init_root_motion_cache() { root_motion_scale_accumulator = Vector3(1, 1, 1); } -void AnimationMixer::_create_track_num_to_track_cache_for_animation(Ref &p_animation) { +void AnimationMixer::_create_track_num_to_track_cache_for_animation(const Ref &p_animation) { if (animation_track_num_to_track_cache.has(p_animation)) { // In AnimationMixer::_update_caches, it retrieves all animations via AnimationMixer::get_animation_list // Since multiple AnimationLibraries can share the same Animation, it is possible that the cache is already created. @@ -628,13 +648,16 @@ void AnimationMixer::_create_track_num_to_track_cache_for_animation(Ref sname_list; - get_animation_list(&sname_list); + LocalVector sname_list; + get_animation_list(&sname_list, false); bool check_path = GLOBAL_GET_CACHED(bool, "animation/warnings/check_invalid_track_paths"); bool check_angle_interpolation = GLOBAL_GET_CACHED(bool, "animation/warnings/check_angle_interpolation_type_conflicting"); @@ -664,16 +687,18 @@ bool AnimationMixer::_update_caches() { reset_anim = get_animation(SceneStringName(RESET)); } for (const StringName &E : sname_list) { - Ref anim = get_animation(E); + const Ref &anim = get_animation(E); for (int i = 0; i < anim->get_track_count(); i++) { NodePath path = anim->track_get_path(i); + (void)path.hash(); // Make sure the cache is valid for faster comparison. + Animation::TypeHash thash = anim->track_get_type_hash(i); Animation::TrackType track_src_type = anim->track_get_type(i); Animation::TrackType track_cache_type = Animation::get_cache_type(track_src_type); TrackCache *track = nullptr; - if (track_cache.has(thash)) { - track = track_cache.get(thash); + if (TrackCache **p = track_cache.getptr(thash)) { + track = *p; } // If not valid, delete track. @@ -935,7 +960,7 @@ bool AnimationMixer::_update_caches() { } } - List to_delete; + LocalVector to_delete; for (const KeyValue &K : track_cache) { if (K.value->setup_pass != setup_pass) { @@ -943,11 +968,9 @@ bool AnimationMixer::_update_caches() { } } - while (to_delete.front()) { - Animation::TypeHash thash = to_delete.front()->get(); + for (const Animation::TypeHash &thash : to_delete) { memdelete(track_cache[thash]); track_cache.erase(thash); - to_delete.pop_front(); } track_map.clear(); @@ -962,9 +985,14 @@ bool AnimationMixer::_update_caches() { K.value->blend_idx = track_map[K.value->path]; } + track_map_version++; + if (track_map_version == 0) { + track_map_version = 1; + } + animation_track_num_to_track_cache.clear(); for (const StringName &E : sname_list) { - Ref anim = get_animation(E); + const Ref &anim = get_animation(E); _create_track_num_to_track_cache_for_animation(anim); } @@ -1129,14 +1157,30 @@ void AnimationMixer::blend_capture(double p_delta) { void AnimationMixer::_blend_calc_total_weight() { for (const AnimationInstance &ai : animation_instances) { - Ref a = ai.animation_data.animation; + const Ref &a = ai.animation_data.animation; real_t weight = ai.playback_info.weight; - const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr(); - int track_weights_count = ai.playback_info.track_weights.size(); - ERR_CONTINUE_EDMSG(!animation_track_num_to_track_cache.has(a), "No animation in cache."); - LocalVector &track_num_to_track_cache = animation_track_num_to_track_cache[a]; - thread_local HashSet processed_hashes; - processed_hashes.clear(); + if (Math::is_zero_approx(weight)) { + continue; + } + + LocalVector *track_weights = ai.playback_info.track_weights; + + LocalVector *t_cache = animation_track_num_to_track_cache.getptr(a); + ERR_CONTINUE_EDMSG(!t_cache, "No animation in cache."); + LocalVector &track_num_to_track_cache = *t_cache; + + uint64_t pass_id = ++animation_instance_weight_pass_counter; + // Handle wrap (slower but rare). + if (unlikely(pass_id == 0)) { + for (KeyValue &kv : track_cache) { + if (kv.value) { + kv.value->animation_instance_weight_applied_at = 0; + } + } + animation_instance_weight_pass_counter = 1; + pass_id = 1; + } + const LocalVector &tracks = a->get_tracks(); Animation::Track *const *tracks_ptr = tracks.ptr(); int count = tracks.size(); @@ -1145,18 +1189,30 @@ void AnimationMixer::_blend_calc_total_weight() { if (!animation_track->enabled) { continue; } - Animation::TypeHash thash = animation_track->thash; TrackCache *track = track_num_to_track_cache[i]; - if (track == nullptr || processed_hashes.has(thash)) { + if (track == nullptr) { // No path, but avoid error spamming. - // Or, there is the case different track type with same path; These can be distinguished by hash. So don't add the weight doubly. continue; } + + // In some cases (e.g. TrackCacheTransform), + // multiple Animation::Tracks (e.g. TYPE_POSITION_3D, TYPE_ROTATION_3D and TYPE_SCALE_3D) + // can point to the same TrackCache instance. + // So we need to make sure that the weight is added only once per AnimationInstance. + if (track->animation_instance_weight_applied_at == pass_id) { + continue; + } + int blend_idx = track->blend_idx; ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count); - real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight; + real_t blend; + if (track_weights && blend_idx < static_cast(track_weights->size())) { + blend = track_weights->operator[](blend_idx) * weight; + } else { + blend = weight; + } track->total_weight += blend; - processed_hashes.insert(thash); + track->animation_instance_weight_applied_at = pass_id; } } } @@ -1167,7 +1223,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); #endif // TOOLS_ENABLED for (const AnimationInstance &ai : animation_instances) { - Ref a = ai.animation_data.animation; + const Ref &a = ai.animation_data.animation; double time = ai.playback_info.time; double delta = ai.playback_info.delta; double start = ai.playback_info.start; @@ -1176,8 +1232,6 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { Animation::LoopedFlag looped_flag = ai.playback_info.looped_flag; bool is_external_seeking = ai.playback_info.is_external_seeking; real_t weight = ai.playback_info.weight; - const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr(); - int track_weights_count = ai.playback_info.track_weights.size(); bool backward = std::signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream. bool seeked_backward = std::signbit(p_delta); #ifndef _3D_DISABLED @@ -1200,7 +1254,14 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } int blend_idx = track->blend_idx; ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count); - real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight; + real_t blend; + LocalVector *track_weights = ai.playback_info.track_weights; + if (track_weights && blend_idx < static_cast(track_weights->size())) { + blend = track_weights->operator[](blend_idx) * weight; + } else { + blend = weight; + } + if (!deterministic) { // If non-deterministic, do normalization. // It would be better to make this if statement outside the for loop, but come here since too much code... @@ -1625,7 +1686,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { t_obj->set_indexed(t->subpath, value); } } else { - List indices; + LocalVector indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { t->use_discrete = true; @@ -1658,7 +1719,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { Vector params = a->method_track_get_params(i, idx); _call_object(t->object_id, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED); } else { - List indices; + LocalVector indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { StringName method = a->method_track_get_name(i, F); @@ -1706,10 +1767,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { map.erase(idx); } } else { - List to_play; + LocalVector to_play; a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag); if (to_play.size()) { - idx = to_play.back()->get(); + idx = to_play[to_play.size() - 1]; } } if (idx < 0) { @@ -1789,10 +1850,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } double pos = a->track_get_key_time(i, idx); StringName anim_name = a->animation_track_get_key_animation(i, idx); - if (String(anim_name) == "[stop]" || !player2->has_animation(anim_name)) { + const Ref &anim = player2->get_animation_or_null(anim_name); + if (anim_name == SNAME("[stop]") || anim.is_null()) { continue; } - Ref anim = player2->get_animation(anim_name); double at_anim_pos = start; switch (anim->get_loop_mode()) { case Animation::LOOP_NONE: { @@ -1821,12 +1882,12 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } } else { // Find stuff to play. - List to_play; + LocalVector to_play; a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag); if (to_play.size()) { - int idx = to_play.back()->get(); + int idx = to_play[to_play.size() - 1]; StringName anim_name = a->animation_track_get_key_animation(i, idx); - if (String(anim_name) == "[stop]" || !player2->has_animation(anim_name)) { + if (anim_name == SNAME("[stop]") || !player2->has_animation(anim_name)) { if (playing_caches.has(t)) { playing_caches.erase(t); player2->stop(); @@ -1871,29 +1932,40 @@ void AnimationMixer::_blend_apply() { if (!t_skeleton) { return; } - if (t->loc_used) { - t_skeleton->set_bone_pose_position(t->bone_idx, t->loc); - } - if (t->rot_used) { - t_skeleton->set_bone_pose_rotation(t->bone_idx, t->rot); - } - if (t->scale_used) { - t_skeleton->set_bone_pose_scale(t->bone_idx, t->scale); - } + if (t->loc_used && t->rot_used && t->scale_used) { + Transform3D transform = Transform3D(Basis(t->rot).scaled(t->scale), t->loc); + t_skeleton->set_bone_pose(t->bone_idx, transform); + } else { + if (t->loc_used) { + t_skeleton->set_bone_pose_position(t->bone_idx, t->loc); + } + if (t->rot_used) { + t_skeleton->set_bone_pose_rotation(t->bone_idx, t->rot); + } + if (t->scale_used) { + t_skeleton->set_bone_pose_scale(t->bone_idx, t->scale); + } + } } else if (!t->skeleton_id.is_valid()) { Node3D *t_node_3d = ObjectDB::get_instance(t->object_id); if (!t_node_3d) { return; } - if (t->loc_used) { - t_node_3d->set_position(t->loc); - } - if (t->rot_used) { - t_node_3d->set_rotation(t->rot.get_euler()); - } - if (t->scale_used) { - t_node_3d->set_scale(t->scale); + + if (t->loc_used && t->rot_used && t->scale_used) { + Transform3D transform = Transform3D(Basis(t->rot).scaled(t->scale), t->loc); + t_node_3d->set_transform(transform); + } else { + if (t->loc_used) { + t_node_3d->set_position(t->loc); + } + if (t->rot_used) { + t_node_3d->set_rotation(t->rot.get_euler()); + } + if (t->scale_used) { + t_node_3d->set_scale(t->scale); + } } } #endif // _3D_DISABLED @@ -2032,19 +2104,19 @@ void AnimationMixer::_call_object(ObjectID p_object_id, const StringName &p_meth } } -void AnimationMixer::make_animation_instance(const StringName &p_name, const PlaybackInfo p_playback_info) { - ERR_FAIL_COND(!has_animation(p_name)); - +void AnimationMixer::make_animation_instance(const StringName &p_name, const PlaybackInfo &p_playback_info) { + const Ref &animation = get_animation_or_null(p_name); + ERR_FAIL_COND(animation.is_null()); AnimationData ad; ad.name = p_name; ad.animation = get_animation(p_name); ad.animation_library = find_animation_library(ad.animation); AnimationInstance ai; - ai.animation_data = ad; + ai.animation_data = std::move(ad); ai.playback_info = p_playback_info; - animation_instances.push_back(ai); + animation_instances.push_back(std::move(ai)); } void AnimationMixer::clear_animation_instances() { @@ -2285,9 +2357,9 @@ Ref AnimationMixer::apply_reset(bool p_user_initiated) { void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) { ERR_FAIL_COND(!active); - ERR_FAIL_COND(!has_animation(p_name)); + const Ref &reference_animation = get_animation_or_null(p_name); + ERR_FAIL_COND(reference_animation.is_null()); ERR_FAIL_COND(p_duration <= 0); - Ref reference_animation = get_animation(p_name); if (!cache_valid) { _update_caches(); // Need to retrieve object id. @@ -2369,13 +2441,13 @@ void AnimationMixer::get_argument_options(const StringName &p_function, int p_id const String pf = p_function; if (p_idx == 0) { if (pf == "get_animation" || pf == "has_animation") { - List al; + LocalVector al; get_animation_list(&al); for (const StringName &name : al) { r_options->push_back(String(name).quote()); } } else if (pf == "get_animation_library" || pf == "has_animation_library" || pf == "remove_animation_library" || pf == "rename_animation_library") { - List al; + LocalVector al; get_animation_library_list(&al); for (const StringName &name : al) { r_options->push_back(String(name).quote()); @@ -2494,7 +2566,7 @@ AnimationMixer::AnimationMixer() { AnimationMixer::~AnimationMixer() { } -void AnimatedValuesBackup::set_data(const AHashMap p_data) { +void AnimatedValuesBackup::set_data(const AHashMap &p_data) { clear_data(); for (const KeyValue &E : p_data) { diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h index 7b918995f1ef..952e25244739 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -76,7 +76,7 @@ class AnimationMixer : public Node { }; struct AnimationData { - String name; + StringName name; Ref animation; StringName animation_library; uint64_t last_update = 0; @@ -91,7 +91,7 @@ class AnimationMixer : public Node { bool is_external_seeking = false; Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE; real_t weight = 0.0; - Vector track_weights; + LocalVector *track_weights = nullptr; }; struct AnimationInstance { @@ -106,12 +106,12 @@ class AnimationMixer : public Node { TypedArray _get_animation_library_list() const; Vector _get_animation_list() const { - List animations; + LocalVector animations; get_animation_list(&animations); Vector ret; - while (animations.size()) { - ret.push_back(animations.front()->get()); - animations.pop_front(); + ret.resize(animations.size()); + for (uint32_t i = 0; i < animations.size(); ++i) { + ret.write[i] = animations[i]; } return ret; } @@ -141,7 +141,6 @@ class AnimationMixer : public Node { /* ---- Caches for blending ---- */ bool cache_valid = false; uint64_t setup_pass = 1; - uint64_t process_pass = 1; struct TrackCache { bool root_motion = false; @@ -151,6 +150,7 @@ class AnimationMixer : public Node { int blend_idx = -1; ObjectID object_id; real_t total_weight = 0.0; + uint64_t animation_instance_weight_applied_at = 0; TrackCache() = default; TrackCache(const TrackCache &p_other) : @@ -158,7 +158,8 @@ class AnimationMixer : public Node { setup_pass(p_other.setup_pass), type(p_other.type), object_id(p_other.object_id), - total_weight(p_other.total_weight) {} + total_weight(p_other.total_weight), + animation_instance_weight_applied_at(p_other.animation_instance_weight_applied_at) {} virtual ~TrackCache() {} }; @@ -315,14 +316,16 @@ class AnimationMixer : public Node { void _clear_playing_caches(); void _init_root_motion_cache(); bool _update_caches(); - void _create_track_num_to_track_cache_for_animation(Ref &p_animation); + void _create_track_num_to_track_cache_for_animation(const Ref &p_animation); /* ---- Audio ---- */ AudioServer::PlaybackType playback_type; /* ---- Blending processor ---- */ LocalVector animation_instances; + uint64_t animation_instance_weight_pass_counter = 0; AHashMap track_map; + uint64_t track_map_version = 1; int track_count = 0; bool deterministic = false; @@ -401,17 +404,18 @@ class AnimationMixer : public Node { /* ---- Data lists ---- */ Dictionary *get_animation_libraries(); - void get_animation_library_list(List *p_animations) const; + void get_animation_library_list(LocalVector *p_animations) const; Ref get_animation_library(const StringName &p_name) const; bool has_animation_library(const StringName &p_name) const; StringName get_animation_library_name(const Ref &p_animation_library) const; - StringName find_animation_library(const Ref &p_animation) const; + const StringName &find_animation_library(const Ref &p_animation) const; Error add_animation_library(const StringName &p_name, const Ref &p_animation_library); void remove_animation_library(const StringName &p_name); void rename_animation_library(const StringName &p_name, const StringName &p_new_name); - void get_animation_list(List *p_animations) const; - Ref get_animation(const StringName &p_name) const; + void get_animation_list(LocalVector *p_animations, const bool p_sorted = true) const; + const Ref &get_animation(const StringName &p_name) const; + const Ref &get_animation_or_null(const StringName &p_name) const; bool has_animation(const StringName &p_name) const; StringName find_animation(const Ref &p_animation) const; @@ -454,7 +458,7 @@ class AnimationMixer : public Node { Vector3 get_root_motion_scale_accumulator() const; /* ---- Blending processor ---- */ - void make_animation_instance(const StringName &p_name, const PlaybackInfo p_playback_info); + void make_animation_instance(const StringName &p_name, const PlaybackInfo &p_playback_info); void clear_animation_instances(); virtual void advance(double p_time); virtual void clear_caches(); // Must be called by hand if an animation was modified after added. @@ -491,7 +495,7 @@ class AnimatedValuesBackup : public RefCounted { AHashMap data; public: - void set_data(const AHashMap p_data); + void set_data(const AHashMap &p_data); AHashMap get_data() const; void clear_data(); diff --git a/scene/animation/animation_node_extension.cpp b/scene/animation/animation_node_extension.cpp index 3d5a63678722..7460ff0994ab 100644 --- a/scene/animation/animation_node_extension.cpp +++ b/scene/animation/animation_node_extension.cpp @@ -30,7 +30,7 @@ #include "animation_node_extension.h" -AnimationNode::NodeTimeInfo AnimationNodeExtension::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { +AnimationNode::NodeTimeInfo AnimationNodeExtension::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { PackedFloat32Array r_ret; GDVIRTUAL_CALL( diff --git a/scene/animation/animation_node_extension.h b/scene/animation/animation_node_extension.h index 71adcc5ba425..82690d7b9c35 100644 --- a/scene/animation/animation_node_extension.h +++ b/scene/animation/animation_node_extension.h @@ -36,7 +36,7 @@ class AnimationNodeExtension : public AnimationNode { GDCLASS(AnimationNodeExtension, AnimationNode); public: - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; static bool is_looping(const PackedFloat32Array &p_node_info); static double get_remaining_time(const PackedFloat32Array &p_node_info, bool p_break_loop = false); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index e5df4b0b3d10..1216999ba91c 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -192,7 +192,7 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { //////////////////////////////////////////////////////// -void AnimationNodeStateMachinePlayback::_set_current(AnimationNodeStateMachine *p_state_machine, const StringName &p_state) { +void AnimationNodeStateMachinePlayback::_set_current(AnimationNode::ProcessState &p_process_state, AnimationNodeStateMachine *p_state_machine, const StringName &p_state) { current = p_state; if (current == StringName()) { group_start_transition = Ref(); @@ -200,7 +200,7 @@ void AnimationNodeStateMachinePlayback::_set_current(AnimationNodeStateMachine * return; } - AnimationTree *tree = p_state_machine->process_state ? p_state_machine->process_state->tree : nullptr; + AnimationTree *tree = p_process_state.tree; Ref anodesm = p_state_machine->find_node_by_path(current); if (anodesm.is_null()) { group_start_transition = Ref(); @@ -354,9 +354,9 @@ bool _is_grouped_state_machine(const Ref p_node) { return p_node.is_valid() && p_node->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED; } -void AnimationNodeStateMachinePlayback::_clear_fading(AnimationNodeStateMachine *p_state_machine, const StringName &p_state) { +void AnimationNodeStateMachinePlayback::_clear_fading(AnimationNode::ProcessState &p_process_state, AnimationNodeStateMachine *p_state_machine, const StringName &p_state) { if (!p_state.is_empty() && !_is_grouped_state_machine(p_state_machine->get_node(p_state))) { - _signal_state_change(p_state_machine->get_animation_tree(), p_state, false); + _signal_state_change(p_process_state.tree, p_state, false); } fading_from = StringName(); fadeing_from_nti = AnimationNode::NodeTimeInfo(); @@ -373,8 +373,8 @@ void AnimationNodeStateMachinePlayback::_signal_state_change(AnimationTree *p_an emit_signal(p_started ? SceneStringName(state_started) : SceneStringName(state_finished), p_state); } -void AnimationNodeStateMachinePlayback::_clear_path_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only) { - List child_nodes; +void AnimationNodeStateMachinePlayback::_clear_path_children(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only) { + LocalVector child_nodes; p_state_machine->get_child_nodes(&child_nodes); for (const AnimationNode::ChildNode &child_node : child_nodes) { Ref anodesm = child_node.node; @@ -386,9 +386,9 @@ void AnimationNodeStateMachinePlayback::_clear_path_children(AnimationTree *p_tr playback = playback->duplicate(); } playback->path.clear(); - playback->_clear_path_children(p_tree, anodesm.ptr(), p_test_only); + playback->_clear_path_children(p_process_state, p_tree, anodesm.ptr(), p_test_only); if (current != child_node.name) { - playback->_start(anodesm.ptr()); // Can restart. + playback->_start(p_process_state, anodesm.ptr()); // Can restart. } } } @@ -421,7 +421,7 @@ void AnimationNodeStateMachinePlayback::_start_children(AnimationTree *p_tree, A } } -bool AnimationNodeStateMachinePlayback::_travel_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only) { +bool AnimationNodeStateMachinePlayback::_travel_children(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only) { if (p_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) { return false; // This function must be fired only by the top state machine, do nothing in child state machine. } @@ -448,7 +448,7 @@ bool AnimationNodeStateMachinePlayback::_travel_children(AnimationTree *p_tree, playback = playback->duplicate(); } if (!playback->is_playing()) { - playback->_start(anodesm.ptr()); + playback->_start(p_process_state, anodesm.ptr()); } ChildStateMachineInfo child_info; child_info.playback = playback; @@ -471,18 +471,18 @@ bool AnimationNodeStateMachinePlayback::_travel_children(AnimationTree *p_tree, child_playback = child_playback->duplicate(); } child_playback->_travel_main(SceneStringName(End)); - child_found_route &= child_playback->_travel(p_tree, child_anodesm.ptr(), false, p_test_only); + child_found_route &= child_playback->_travel(p_process_state, p_tree, child_anodesm.ptr(), false, p_test_only); child_path += "/" + child_playback->get_current_node(); } // Force restart target state machine. - playback->_start(anodesm.ptr()); + playback->_start(p_process_state, anodesm.ptr()); } is_parent_same_state = is_current_same_state; bool is_deepest_state = i == temp_path.size() - 1; child_info.is_reset = is_deepest_state ? reset_request_on_teleport : false; playback->_travel_main(temp_path[i], child_info.is_reset); - if (playback->_make_travel_path(p_tree, anodesm.ptr(), is_deepest_state ? p_is_allow_transition_to_self : false, child_info.path, p_test_only)) { + if (playback->_make_travel_path(p_process_state, p_tree, anodesm.ptr(), is_deepest_state ? p_is_allow_transition_to_self : false, child_info.path, p_test_only)) { found_route &= child_found_route; } else { child_info.path.push_back(temp_path[i]); @@ -511,16 +511,16 @@ bool AnimationNodeStateMachinePlayback::_travel_children(AnimationTree *p_tree, return found_route; } -void AnimationNodeStateMachinePlayback::_start(AnimationNodeStateMachine *p_state_machine) { +void AnimationNodeStateMachinePlayback::_start(AnimationNode::ProcessState &p_process_state, AnimationNodeStateMachine *p_state_machine) { playing = true; - _set_current(p_state_machine, start_request != StringName() ? start_request : SceneStringName(Start)); + _set_current(p_process_state, p_state_machine, start_request != StringName() ? start_request : SceneStringName(Start)); teleport_request = true; stop_request = false; start_request = StringName(); } -bool AnimationNodeStateMachinePlayback::_travel(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, bool p_test_only) { - return _make_travel_path(p_tree, p_state_machine, p_is_allow_transition_to_self, path, p_test_only); +bool AnimationNodeStateMachinePlayback::_travel(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, bool p_test_only) { + return _make_travel_path(p_process_state, p_tree, p_state_machine, p_is_allow_transition_to_self, path, p_test_only); } String AnimationNodeStateMachinePlayback::_validate_path(AnimationNodeStateMachine *p_state_machine, const String &p_path) { @@ -541,12 +541,12 @@ String AnimationNodeStateMachinePlayback::_validate_path(AnimationNodeStateMachi return target; } -bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector &r_path, bool p_test_only) { +bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector &r_path, bool p_test_only) { StringName travel = travel_request; travel_request = StringName(); if (!playing) { - _start(p_state_machine); + _start(p_process_state, p_state_machine); } ERR_FAIL_COND_V(!p_state_machine->states.has(travel), false); @@ -678,13 +678,13 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, playback = playback->duplicate(); } if (i > 0) { - playback->_start(anodesm.ptr()); + playback->_start(p_process_state, anodesm.ptr()); } if (i >= new_path.size()) { break; // Tracing has been finished, needs to break. } playback->_travel_main(SceneStringName(End)); - if (!playback->_travel(p_tree, anodesm.ptr(), false, p_test_only)) { + if (!playback->_travel(p_process_state, p_tree, anodesm.ptr(), false, p_test_only)) { found_route = false; break; } @@ -705,8 +705,8 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree, } } -AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - AnimationNode::NodeTimeInfo nti = _process(p_state_machine, p_playback_info, p_test_only); +AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::process(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { + AnimationNode::NodeTimeInfo nti = _process(p_process_state, p_instance, p_state_machine, p_playback_info, p_test_only); start_request = StringName(); next_request = false; stop_request = false; @@ -714,8 +714,8 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::process(Animation return nti; } -AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - AnimationTree *tree = p_state_machine->process_state->tree; +AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { + AnimationTree *tree = p_process_state.tree; double p_time = p_playback_info.time; double p_delta = p_playback_info.delta; @@ -728,9 +728,9 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio // Restart state machine. if (p_state_machine->get_state_machine_type() != AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) { path.clear(); - _clear_path_children(tree, p_state_machine, p_test_only); + _clear_path_children(p_process_state, tree, p_state_machine, p_test_only); } - _start(p_state_machine); + _start(p_process_state, p_state_machine); reset_request = true; } else { // Reset current state. @@ -754,7 +754,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio // Process start/travel request. if (start_request != StringName() || travel_request != StringName()) { if (p_state_machine->get_state_machine_type() != AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) { - _clear_path_children(tree, p_state_machine, p_test_only); + _clear_path_children(p_process_state, tree, p_state_machine, p_test_only); } } @@ -768,7 +768,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio } // Teleport to start. if (p_state_machine->states.has(start_request)) { - _start(p_state_machine); + _start(p_process_state, p_state_machine); } else { StringName node = start_request; ERR_FAIL_V_MSG(AnimationNode::NodeTimeInfo(), "No such node: '" + node + "'"); @@ -783,10 +783,10 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio StringName temp_travel_request = travel_request; // For the case that can't travel. // Process children. Vector new_path; - bool can_travel = _make_travel_path(tree, p_state_machine, travel_path.size() <= 1 ? p_state_machine->is_allow_transition_to_self() : false, new_path, p_test_only); + bool can_travel = _make_travel_path(p_process_state, tree, p_state_machine, travel_path.size() <= 1 ? p_state_machine->is_allow_transition_to_self() : false, new_path, p_test_only); if (travel_path.size()) { if (can_travel) { - can_travel = _travel_children(tree, p_state_machine, travel_target, p_state_machine->is_allow_transition_to_self(), travel_path[0] == current, p_test_only); + can_travel = _travel_children(p_process_state, tree, p_state_machine, travel_target, p_state_machine->is_allow_transition_to_self(), travel_path[0] == current, p_test_only); } else { _start_children(tree, p_state_machine, travel_target, p_test_only); } @@ -800,7 +800,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio if (p_state_machine->states.has(temp_travel_request)) { path.clear(); if (current != temp_travel_request || reset_request_on_teleport) { - _set_current(p_state_machine, temp_travel_request); + _set_current(p_process_state, p_state_machine, temp_travel_request); reset_request = reset_request_on_teleport; teleport_request = true; } @@ -823,15 +823,16 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio pi.seeked = true; pi.is_external_seeking = false; pi.weight = 0; - current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true); + AnimationNodeInstance *other_instance = p_instance.get_child_instance_by_path_or_null(current); + current_nti = p_state_machine->blend_node(p_process_state, p_instance, other_instance, current, pi, AnimationNode::FILTER_IGNORE, true, true); // Don't process first node if not necessary, instead process next node. - _transition_to_next_recursive(tree, p_state_machine, p_delta, p_test_only); + _transition_to_next_recursive(p_process_state, p_instance, tree, p_state_machine, p_delta, p_test_only); } // Check current node existence. if (!p_state_machine->states.has(current)) { playing = false; // Current does not exist. - _set_current(p_state_machine, StringName()); + _set_current(p_process_state, p_state_machine, StringName()); return AnimationNode::NodeTimeInfo(); } @@ -873,7 +874,8 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio pi.time = 0; pi.seeked = true; } - current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + AnimationNodeInstance *other_instance = p_instance.get_child_instance_by_path_or_null(current); + current_nti = p_state_machine->blend_node(p_process_state, p_instance, other_instance, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. // Cross-fade process. if (fading_from != StringName()) { @@ -892,16 +894,17 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio pi.time = 0; pi.seeked = true; } - fadeing_from_nti = p_state_machine->blend_node(p_state_machine->states[fading_from].node, fading_from, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + AnimationNodeInstance *fading_from_instance = p_instance.get_child_instance_by_path_or_null(fading_from); + fadeing_from_nti = p_state_machine->blend_node(p_process_state, p_instance, fading_from_instance, fading_from, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (Animation::is_greater_or_equal_approx(fading_pos, fading_time)) { // Finish fading. - _clear_fading(p_state_machine, fading_from); + _clear_fading(p_process_state, p_state_machine, fading_from); } } // Find next and see when to transition. - bool will_end = _transition_to_next_recursive(tree, p_state_machine, p_delta, p_test_only) || current == SceneStringName(End); + bool will_end = _transition_to_next_recursive(p_process_state, p_instance, tree, p_state_machine, p_delta, p_test_only) || current == SceneStringName(End); // Predict remaining time. if (will_end || ((p_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_NESTED) && !p_state_machine->has_transition_from(current))) { @@ -919,7 +922,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(Animatio return current_nti; } -bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, double p_delta, bool p_test_only) { +bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, double p_delta, bool p_test_only) { _reset_request_for_fading_from = false; AnimationMixer::PlaybackInfo pi; @@ -928,9 +931,9 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT Vector transition_path; transition_path.push_back(current); while (true) { - next = _find_next(p_tree, p_state_machine); + next = _find_next(p_process_state, p_instance, p_tree, p_state_machine); - if (!_can_transition_to_next(p_tree, p_state_machine, next, p_test_only)) { + if (!_can_transition_to_next(p_process_state, p_tree, p_state_machine, next, p_test_only)) { break; // Finish transition. } @@ -954,9 +957,10 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT pi.seeked = true; pi.is_external_seeking = false; pi.weight = 0; - p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); + AnimationNodeInstance *other_instance = p_instance.get_child_instance_by_path_or_null(current); + p_state_machine->blend_node(p_process_state, p_instance, other_instance, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); } - _clear_fading(p_state_machine, current); + _clear_fading(p_process_state, p_state_machine, current); fading_time = 0; fading_pos = 0; } @@ -967,7 +971,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT } // Update current status. - _set_current(p_state_machine, next.node); + _set_current(p_process_state, p_state_machine, next.node); current_curve = next.curve; if (current == SceneStringName(End)) { @@ -979,12 +983,13 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT fadeing_from_nti = current_nti; + AnimationNodeInstance *other_instance = p_instance.get_child_instance_by_path_or_null(current); if (next.switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { pi.time = current_nti.position; pi.seeked = true; pi.is_external_seeking = false; pi.weight = 0; - p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); + p_state_machine->blend_node(p_process_state, p_instance, other_instance, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); } // Just get length to find next recursive. @@ -992,7 +997,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT pi.is_external_seeking = false; pi.weight = 0; pi.seeked = next.is_reset; - current_nti = p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, true); // Just retrieve remain length, don't process. + current_nti = p_state_machine->blend_node(p_process_state, p_instance, other_instance, current, pi, AnimationNode::FILTER_IGNORE, true, true); // Just retrieve remain length, don't process. // Fading must be processed. if (fading_time) { @@ -1003,7 +1008,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT return next.node == SceneStringName(End); } -bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, NextInfo p_next, bool p_test_only) { +bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, NextInfo p_next, bool p_test_only) { if (p_next.node == StringName()) { return false; } @@ -1022,7 +1027,7 @@ bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p } playback->_next_main(); // Then, fading should end. - _clear_fading(p_state_machine, fading_from); + _clear_fading(p_process_state, p_state_machine, fading_from); fading_pos = 0; } else { return true; @@ -1063,7 +1068,7 @@ Ref AnimationNodeStateMachinePlayback::_che return p_transition.transition; } -AnimationNodeStateMachinePlayback::NextInfo AnimationNodeStateMachinePlayback::_find_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine) const { +AnimationNodeStateMachinePlayback::NextInfo AnimationNodeStateMachinePlayback::_find_next(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine) const { NextInfo next; if (path.size()) { for (int i = 0; i < p_state_machine->transitions.size(); i++) { @@ -1092,7 +1097,7 @@ AnimationNodeStateMachinePlayback::NextInfo AnimationNodeStateMachinePlayback::_ if (ref_transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) { continue; } - if (p_state_machine->transitions[i].from == current && (_check_advance_condition(anodesm, ref_transition) || bypass)) { + if (p_state_machine->transitions[i].from == current && (_check_advance_condition(p_process_state, p_instance, anodesm, ref_transition) || bypass)) { if (ref_transition->get_priority() <= priority_best) { priority_best = ref_transition->get_priority(); auto_advance_to = i; @@ -1116,19 +1121,21 @@ AnimationNodeStateMachinePlayback::NextInfo AnimationNodeStateMachinePlayback::_ return next; } -bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref state_machine, const Ref transition) const { +bool AnimationNodeStateMachinePlayback::_check_advance_condition(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, const Ref state_machine, const Ref transition) const { if (transition->get_advance_mode() != AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO) { return false; } StringName advance_condition_name = transition->get_advance_condition_name(); - if (advance_condition_name != StringName() && !bool(state_machine->get_parameter(advance_condition_name))) { + ERR_FAIL_COND_V(p_instance.node != state_machine.ptr(), false); + + if (advance_condition_name != StringName() && !bool(p_instance.get_parameter(advance_condition_name))) { return false; } if (transition->expression.is_valid()) { - AnimationTree *tree_base = state_machine->get_animation_tree(); + AnimationTree *tree_base = p_process_state.tree; ERR_FAIL_NULL_V(tree_base, false); NodePath advance_expression_base_node_path = tree_base->get_advance_expression_base_node(); @@ -1236,7 +1243,7 @@ AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { /////////////////////////////////////////////////////// -void AnimationNodeStateMachine::get_parameter_list(List *r_list) const { +void AnimationNodeStateMachine::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ALWAYS_DUPLICATE)); // Don't store this object in .tres, it always needs to be made as unique object. List advance_conditions; @@ -1374,7 +1381,7 @@ StringName AnimationNodeStateMachine::get_node_name(const Ref &p_ ERR_FAIL_V(StringName()); } -void AnimationNodeStateMachine::get_child_nodes(List *r_child_nodes) { +void AnimationNodeStateMachine::get_child_nodes(LocalVector *r_child_nodes) { Vector nodes; for (const KeyValue &E : states) { @@ -1635,16 +1642,17 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const { return graph_offset; } -AnimationNode::NodeTimeInfo AnimationNodeStateMachine::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - Ref playback_new = get_parameter(playback); +AnimationNode::NodeTimeInfo AnimationNodeStateMachine::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + Ref playback_new = p_instance.get_parameter(playback); ERR_FAIL_COND_V(playback_new.is_null(), AnimationNode::NodeTimeInfo()); - playback_new->_set_base_path(node_state.get_base_path()); + + playback_new->_set_base_path(p_instance.base_path); playback_new->_set_grouped(state_machine_type == STATE_MACHINE_TYPE_GROUPED); if (p_test_only) { playback_new = playback_new->duplicate(); // Don't process original when testing. } - return playback_new->process(this, p_playback_info, p_test_only); + return playback_new->process(p_process_state, p_instance, this, p_playback_info, p_test_only); } String AnimationNodeStateMachine::get_caption() const { diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index d62f1ce8947d..c8ecb56984e5 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -164,7 +164,7 @@ class AnimationNodeStateMachine : public AnimationRootNode { virtual void reset_state() override; public: - virtual void get_parameter_list(List *r_list) const override; + virtual void get_parameter_list(LocalVector *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual bool is_parameter_read_only(const StringName &p_parameter) const override; @@ -181,7 +181,7 @@ class AnimationNodeStateMachine : public AnimationRootNode { void set_node_position(const StringName &p_name, const Vector2 &p_position); Vector2 get_node_position(const StringName &p_name) const; - virtual void get_child_nodes(List *r_child_nodes) override; + virtual void get_child_nodes(LocalVector *r_child_nodes) override; bool has_transition(const StringName &p_from, const StringName &p_to) const; bool has_transition_from(const StringName &p_from) const; @@ -212,7 +212,7 @@ class AnimationNodeStateMachine : public AnimationRootNode { void set_graph_offset(const Vector2 &p_offset); Vector2 get_graph_offset() const; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false) override; virtual String get_caption() const override; virtual Ref get_child_by_name(const StringName &p_name) const override; @@ -289,32 +289,32 @@ class AnimationNodeStateMachinePlayback : public Resource { bool is_grouped = false; - void _clear_fading(AnimationNodeStateMachine *p_state_machine, const StringName &p_state); + void _clear_fading(AnimationNode::ProcessState &p_process_state, AnimationNodeStateMachine *p_state_machine, const StringName &p_state); void _signal_state_change(AnimationTree *p_animation_tree, const StringName &p_state, bool p_started); void _travel_main(const StringName &p_state, bool p_reset_on_teleport = true); void _start_main(const StringName &p_state, bool p_reset = true); void _next_main(); void _stop_main(); - bool _make_travel_path(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector &r_path, bool p_test_only); + bool _make_travel_path(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector &r_path, bool p_test_only); String _validate_path(AnimationNodeStateMachine *p_state_machine, const String &p_path); - bool _travel(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, bool p_test_only); - void _start(AnimationNodeStateMachine *p_state_machine); + bool _travel(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, bool p_test_only); + void _start(AnimationNode::ProcessState &p_process_state, AnimationNodeStateMachine *p_state_machine); - void _clear_path_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only); - bool _travel_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only); + void _clear_path_children(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only); + bool _travel_children(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only); void _start_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_test_only); - AnimationNode::NodeTimeInfo process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only); - AnimationNode::NodeTimeInfo _process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only); + AnimationNode::NodeTimeInfo process(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only); + AnimationNode::NodeTimeInfo _process(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only); - bool _check_advance_condition(const Ref p_state_machine, const Ref p_transition) const; - bool _transition_to_next_recursive(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, double p_delta, bool p_test_only); - NextInfo _find_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine) const; + bool _check_advance_condition(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, const Ref p_state_machine, const Ref p_transition) const; + bool _transition_to_next_recursive(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, double p_delta, bool p_test_only); + NextInfo _find_next(AnimationNode::ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine) const; Ref _check_group_transition(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const AnimationNodeStateMachine::Transition &p_transition, Ref &r_state_machine, bool &r_bypass) const; - bool _can_transition_to_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, NextInfo p_next, bool p_test_only); + bool _can_transition_to_next(AnimationNode::ProcessState &p_process_state, AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, NextInfo p_next, bool p_test_only); - void _set_current(AnimationNodeStateMachine *p_state_machine, const StringName &p_state); + void _set_current(AnimationNode::ProcessState &p_process_state, AnimationNodeStateMachine *p_state_machine, const StringName &p_state); void _set_grouped(bool p_is_grouped); void _set_base_path(const String &p_base_path); Ref _get_parent_playback(AnimationTree *p_tree) const; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index eea2ed3a948e..074d505a9ed4 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -282,20 +282,20 @@ void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) { } return; } - List::Element *> to_erase; - for (List::Element *E = c.blend.front(); E; E = E->next()) { - Blend &b = E->get(); + LocalVector to_erase; + for (uint32_t i = 0; i < c.blend.size(); i++) { + Blend &b = c.blend[i]; b.blend_left = MAX(0, b.blend_left - Math::abs(speed_scale * p_delta) / b.blend_time); if (Animation::is_less_or_equal_approx(b.blend_left, 0)) { - to_erase.push_back(E); + to_erase.push_back(i); b.blend_left = CMP_EPSILON; // May want to play last frame. } // Note: There may be issues if an animation event triggers an animation change while this blend is active, // so it is best to use "deferred" calls instead of "immediate" for animation events that can trigger new animations. _process_playback_data(b.data, p_delta, b.blend_left, false, false, false); } - for (List::Element *&E : to_erase) { - c.blend.erase(E); + for (int i = to_erase.size() - 1; i >= 0; i--) { + c.blend.remove_at(to_erase[i]); } } @@ -339,9 +339,9 @@ void AnimationPlayer::_blend_post_process() { // If the method track changes current animation, the animation is not finished. if (tmp_from == playback.current.from->animation->get_instance_id()) { if (playback_queue.size()) { - String old = playback.assigned; + const StringName &old = playback.assigned; play(playback_queue.front()->get()); - String new_name = playback.assigned; + const StringName &new_name = playback.assigned; playback_queue.pop_front(); if (end_notify) { emit_signal(SceneStringName(animation_changed), old, new_name); @@ -417,9 +417,10 @@ void AnimationPlayer::play_section_with_markers(const StringName &p_name, const name = playback.assigned; } - ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name)); + AnimationData *ad = animation_set.getptr(name); + ERR_FAIL_NULL_MSG(ad, vformat("Animation not found: %s.", name)); - Ref animation = animation_set[name].animation; + const Ref &animation = ad->animation; ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker)); ERR_FAIL_COND_MSG(p_start_marker && !animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, name)); @@ -465,12 +466,12 @@ void AnimationPlayer::play_section(const StringName &p_name, double p_start_time } else if (blend_times.has(bk)) { blend_time = blend_times[bk]; } else { - bk.from = "*"; + bk.from = SNAME("*"); if (blend_times.has(bk)) { blend_time = blend_times[bk]; } else { bk.from = c.current.from->name; - bk.to = "*"; + bk.to = SNAME("*"); if (blend_times.has(bk)) { blend_time = blend_times[bk]; @@ -658,8 +659,9 @@ void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_ playback.current.pos = p_time; if (!playback.current.from) { if (playback.assigned) { - ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned)); - playback.current.from = &animation_set[playback.assigned]; + AnimationData *ad = animation_set.getptr(playback.assigned); + ERR_FAIL_NULL_MSG(ad, vformat("Animation not found: %s.", playback.assigned)); + playback.current.from = ad; } if (!playback.current.from) { return; // There is no animation. @@ -802,10 +804,11 @@ void AnimationPlayer::animation_set_next(const StringName &p_animation, const St } StringName AnimationPlayer::animation_get_next(const StringName &p_animation) const { - if (!animation_next_set.has(p_animation)) { + const StringName *next = animation_next_set.getptr(p_animation); + if (!next) { return StringName(); } - return animation_next_set[p_animation]; + return *next; } void AnimationPlayer::set_default_blend_time(double p_default) { @@ -836,8 +839,8 @@ double AnimationPlayer::get_blend_time(const StringName &p_animation1, const Str bk.from = p_animation1; bk.to = p_animation2; - if (blend_times.has(bk)) { - return blend_times[bk]; + if (const double *blend_time = blend_times.getptr(bk)) { + return *blend_time; } else { return 0; } @@ -880,7 +883,7 @@ Tween::EaseType AnimationPlayer::get_auto_capture_ease_type() const { void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List *r_options) const { const String pf = p_function; if (p_idx == 0 && (pf == "play" || pf == "play_backwards" || pf == "has_animation" || pf == "queue")) { - List al; + LocalVector al; get_animation_list(&al); for (const StringName &name : al) { r_options->push_back(String(name).quote()); @@ -893,7 +896,7 @@ void AnimationPlayer::get_argument_options(const StringName &p_function, int p_i void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) { AnimationMixer::_animation_removed(p_name, p_library); - StringName name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); + const StringName &name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); if (!animation_set.has(name)) { return; // No need to update because not the one from the library being used. @@ -902,17 +905,16 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN _animation_set_cache_update(); // Erase blends if needed - List to_erase; + LocalVector to_erase; for (const KeyValue &E : blend_times) { - BlendKey bk = E.key; + const BlendKey &bk = E.key; if (bk.from == name || bk.to == name) { to_erase.push_back(bk); } } - while (to_erase.size()) { - blend_times.erase(to_erase.front()->get()); - to_erase.pop_front(); + for (const BlendKey &bk : to_erase) { + blend_times.erase(bk); } } @@ -920,7 +922,7 @@ void AnimationPlayer::_rename_animation(const StringName &p_from_name, const Str AnimationMixer::_rename_animation(p_from_name, p_to_name); // Rename autoplay or blends if needed. - List to_erase; + LocalVector to_erase; HashMap to_insert; for (const KeyValue &E : blend_times) { BlendKey bk = E.key; @@ -941,9 +943,8 @@ void AnimationPlayer::_rename_animation(const StringName &p_from_name, const Str } } - while (to_erase.size()) { - blend_times.erase(to_erase.front()->get()); - to_erase.pop_front(); + for (const BlendKey &bk : to_erase) { + blend_times.erase(bk); } while (to_insert.size()) { diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 4aa2411d0a29..1ad7b1ec3227 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -96,7 +96,7 @@ class AnimationPlayer : public AnimationMixer { bool seeked = false; bool internal_seeked = false; bool started = false; - List blend; + LocalVector blend; } playback; struct BlendKey { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index b1ba39e9782c..4fea35af796f 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -34,7 +34,10 @@ #include "animation_blend_tree.h" #include "scene/animation/animation_player.h" -void AnimationNode::get_parameter_list(List *r_list) const { +thread_local AnimationNode::ProcessState *AnimationNode::tls_process_state = nullptr; +thread_local AnimationNodeInstance *AnimationNode::current_instance = nullptr; + +void AnimationNode::get_parameter_list(LocalVector *r_list) const { Array parameters; if (GDVIRTUAL_CALL(_get_parameter_list, parameters)) { @@ -72,60 +75,36 @@ bool AnimationNode::is_parameter_read_only(const StringName &p_parameter) const return false; } -void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) { - ERR_FAIL_NULL(process_state); - if (process_state->is_testing) { - return; - } - - const AHashMap::Iterator it = property_cache.find(p_name); - if (it) { - Pair &prop = process_state->tree->property_map.get_by_index(it->value).value; - Variant value = p_value; - if (Animation::validate_type_match(prop.first, value)) { - prop.first = value; - } - return; - } - - ERR_FAIL_COND(!process_state->tree->property_parent_map.has(node_state.base_path)); - ERR_FAIL_COND(!process_state->tree->property_parent_map[node_state.base_path].has(p_name)); - StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name]; - int idx = process_state->tree->property_map.get_index(path); - property_cache.insert_new(p_name, idx); - process_state->tree->property_map.get_by_index(idx).value.first = p_value; +void AnimationNode::set_parameter_ex(const StringName &p_name, const Variant &p_value) { + ERR_FAIL_NULL(tls_process_state); + ERR_FAIL_NULL(current_instance); + current_instance->set_parameter(p_name, p_value, tls_process_state->is_testing); } -Variant AnimationNode::get_parameter(const StringName &p_name) const { - ERR_FAIL_NULL_V(process_state, Variant()); - const AHashMap::ConstIterator it = property_cache.find(p_name); - if (it) { - return process_state->tree->property_map.get_by_index(it->value).value.first; - } - ERR_FAIL_COND_V(!process_state->tree->property_parent_map.has(node_state.base_path), Variant()); - ERR_FAIL_COND_V(!process_state->tree->property_parent_map[node_state.base_path].has(p_name), Variant()); - - StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name]; - int idx = process_state->tree->property_map.get_index(path); - property_cache.insert_new(p_name, idx); - return process_state->tree->property_map.get_by_index(idx).value.first; +Variant AnimationNode::get_parameter_ex(const StringName &p_name) const { + ERR_FAIL_NULL_V(tls_process_state, Variant()); + ERR_FAIL_NULL_V(current_instance, Variant()); + return current_instance->get_parameter(p_name); } -void AnimationNode::set_node_time_info(const NodeTimeInfo &p_node_time_info) { - set_parameter(current_length, p_node_time_info.length); - set_parameter(current_position, p_node_time_info.position); - set_parameter(current_delta, p_node_time_info.delta); +void AnimationNode::set_node_time_info(AnimationNodeInstance &instance, ProcessState &p_process_state, const NodeTimeInfo &p_node_time_info) { + if (p_process_state.is_testing) { + return; + } + instance.set_parameter(current_length, p_node_time_info.length, p_process_state.is_testing); + instance.set_parameter(current_position, p_node_time_info.position, p_process_state.is_testing); + instance.set_parameter(current_delta, p_node_time_info.delta, p_process_state.is_testing); } -AnimationNode::NodeTimeInfo AnimationNode::get_node_time_info() const { +AnimationNode::NodeTimeInfo AnimationNode::get_node_time_info(AnimationNodeInstance &instance, ProcessState &p_process_state) const { NodeTimeInfo nti; - nti.length = get_parameter(current_length); - nti.position = get_parameter(current_position); - nti.delta = get_parameter(current_delta); + nti.length = instance.get_parameter(current_length); + nti.position = instance.get_parameter(current_position); + nti.delta = instance.get_parameter(current_delta); return nti; } -void AnimationNode::get_child_nodes(List *r_child_nodes) { +void AnimationNode::get_child_nodes(LocalVector *r_child_nodes) { Dictionary cn; if (GDVIRTUAL_CALL(_get_child_nodes, cn)) { for (const KeyValue &kv : cn) { @@ -137,95 +116,89 @@ void AnimationNode::get_child_nodes(List *r_child_nodes) { } } -void AnimationNode::blend_animation(const StringName &p_animation, AnimationMixer::PlaybackInfo p_playback_info) { - ERR_FAIL_NULL(process_state); - p_playback_info.track_weights = Vector(node_state.track_weights); - process_state->tree->make_animation_instance(p_animation, p_playback_info); +void AnimationNode::blend_animation(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const StringName &p_animation, AnimationMixer::PlaybackInfo &p_playback_info) { + p_playback_info.track_weights = &p_instance.track_weights; + p_process_state.tree->make_animation_instance(p_animation, p_playback_info); } -AnimationNode::NodeTimeInfo AnimationNode::_pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - process_state = p_process_state; - NodeTimeInfo nti = process(p_playback_info, p_test_only); - process_state = nullptr; +AnimationNode::NodeTimeInfo AnimationNode::_pre_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + ERR_FAIL_NULL_V(tls_process_state, NodeTimeInfo()); // Should not ever happen. + ERR_FAIL_COND_V_MSG(tls_process_state != &p_process_state, NodeTimeInfo(), "AnimationNodes can only be processed from within their own AnimationTree."); + + AnimationNodeInstance *prev_instance = current_instance; + + current_instance = &p_instance; + NodeTimeInfo nti = process(p_process_state, p_instance, p_playback_info, p_test_only); + current_instance = prev_instance; + return nti; } -void AnimationNode::make_invalid(const String &p_reason) { - ERR_FAIL_NULL(process_state); - process_state->valid = false; - if (!process_state->invalid_reasons.is_empty()) { - process_state->invalid_reasons += "\n"; +void AnimationNode::make_invalid(ProcessState &p_process_state, const String &p_reason) { + p_process_state.valid = false; + if (!p_process_state.invalid_reasons.is_empty()) { + p_process_state.invalid_reasons += "\n"; } - process_state->invalid_reasons += String::utf8("• ") + p_reason; -} - -AnimationTree *AnimationNode::get_animation_tree() const { - ERR_FAIL_NULL_V(process_state, nullptr); - return process_state->tree; + p_process_state.invalid_reasons += String::utf8("• ") + p_reason; } -AnimationNode::NodeTimeInfo AnimationNode::blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) { +AnimationNode::NodeTimeInfo AnimationNode::blend_input(ProcessState &p_process_state, AnimationNodeInstance &p_instance, int p_input, const AnimationMixer::PlaybackInfo &p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) { ERR_FAIL_INDEX_V(p_input, (int64_t)inputs.size(), NodeTimeInfo()); - AnimationNodeBlendTree *blend_tree = Object::cast_to(node_state.parent); + AnimationNodeInstance *blend_tree_instance = p_instance.parent; + ERR_FAIL_NULL_V(blend_tree_instance, NodeTimeInfo()); + + AnimationNodeBlendTree *blend_tree = Object::cast_to(blend_tree_instance->node.ptr()); ERR_FAIL_NULL_V(blend_tree, NodeTimeInfo()); // Update connections. - StringName current_name = blend_tree->get_node_name(Ref(this)); - node_state.connections = blend_tree->get_node_connection_array(current_name); + const StringName ¤t_name = blend_tree->get_node_name(*this); + p_instance.connections = blend_tree->get_node_connection_array(current_name); // Get node which is connected input port. - StringName node_name = node_state.connections[p_input]; - if (!blend_tree->has_node(node_name)) { - make_invalid(vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), current_name)); + const StringName &node_name = p_instance.connections[p_input]; + AnimationNodeInstance *node_instance = blend_tree_instance->get_child_instance_by_path_or_null(node_name); + + if (!node_instance) { + make_invalid(p_process_state, vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), current_name)); return NodeTimeInfo(); } - Ref node = blend_tree->get_node(node_name); - ERR_FAIL_COND_V(node.is_null(), NodeTimeInfo()); - real_t activity = 0.0; - LocalVector *activity_ptr = process_state->tree->input_activity_map.getptr(node_state.base_path); - NodeTimeInfo nti = _blend_node(node, node_name, nullptr, p_playback_info, p_filter, p_sync, p_test_only, &activity); + LocalVector *activity_ptr = p_process_state.tree->input_activity_map.getptr(p_instance.base_path); + NodeTimeInfo nti = _blend_node(p_process_state, p_instance, *node_instance, node_name, nullptr, p_playback_info, p_filter, p_sync, p_test_only, &activity); if (activity_ptr && p_input < (int64_t)activity_ptr->size()) { - (*activity_ptr)[p_input].last_pass = process_state->last_pass; + (*activity_ptr)[p_input].last_pass = p_process_state.last_pass; (*activity_ptr)[p_input].activity = activity; } return nti; } -AnimationNode::NodeTimeInfo AnimationNode::blend_node(Ref p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) { - ERR_FAIL_COND_V(p_node.is_null(), NodeTimeInfo()); - p_node->node_state.connections.clear(); - return _blend_node(p_node, p_subpath, this, p_playback_info, p_filter, p_sync, p_test_only, nullptr); +AnimationNode::NodeTimeInfo AnimationNode::blend_node(ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeInstance *p_other, const StringName &p_subpath, const AnimationMixer::PlaybackInfo &p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) { + ERR_FAIL_NULL_V(p_other, NodeTimeInfo()); + p_instance.connections.clear(); + return _blend_node(p_process_state, p_instance, *p_other, p_subpath, this, p_playback_info, p_filter, p_sync, p_test_only, nullptr); } -AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only, real_t *r_activity) { - ERR_FAIL_NULL_V(process_state, NodeTimeInfo()); +AnimationNode::NodeTimeInfo AnimationNode::_blend_node(ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeInstance &p_other, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only, real_t *r_activity) { + int blend_count = p_instance.track_weights.size(); - int blend_count = node_state.track_weights.size(); - - if ((int64_t)p_node->node_state.track_weights.size() != blend_count) { - p_node->node_state.track_weights.resize(blend_count); + if ((int64_t)p_other.track_weights.size() != blend_count) { + p_other.track_weights.resize(blend_count); } - real_t *blendw = p_node->node_state.track_weights.ptr(); - const real_t *blendr = node_state.track_weights.ptr(); + real_t *blendw = p_other.track_weights.ptr(); + const real_t *blendr = p_instance.track_weights.ptr(); bool any_valid = false; if (has_filter() && is_filter_enabled() && p_filter != FILTER_IGNORE) { - for (int i = 0; i < blend_count; i++) { - blendw[i] = 0.0; // All to zero by default. - } + _update_filter_cache(p_process_state, p_instance); + // All to zero by default. + memset(blendw, 0, sizeof(real_t) * blend_count); - for (const KeyValue &E : filter) { - const AHashMap &map = *process_state->track_map; - if (!map.has(E.key)) { - continue; - } - int idx = map[E.key]; + for (const int idx : p_instance.filtered_track_indices_cache) { blendw[idx] = 1.0; // Filtered goes to one. } @@ -295,28 +268,34 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref p_node } } - String new_path; + StringName *new_path; AnimationNode *new_parent; - - // This is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations. + AHashMap *childmap = nullptr; if (p_new_parent) { new_parent = p_new_parent; - new_path = String(node_state.base_path) + String(p_subpath) + "/"; + childmap = &p_instance.child_base_cache; } else { - ERR_FAIL_NULL_V(node_state.parent, NodeTimeInfo()); - new_parent = node_state.parent; - new_path = String(new_parent->node_state.base_path) + String(p_subpath) + "/"; + ERR_FAIL_NULL_V(p_instance.parent, NodeTimeInfo()); + childmap = &p_instance.parent->child_base_cache; + new_parent = p_instance.parent_node; + } + + ERR_FAIL_NULL_V(childmap, NodeTimeInfo()); + if (StringName *found = childmap->getptr(p_subpath)) { + new_path = found; + } else { + ERR_FAIL_V(NodeTimeInfo()); } // This process, which depends on p_sync is needed to process sync correctly in the case of // that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync. - p_node->set_node_state_base_path(new_path); - p_node->node_state.parent = new_parent; + p_other.base_path = *new_path; + p_other.parent_node = new_parent; + if (!p_playback_info.seeked && !p_sync && !any_valid) { p_playback_info.delta = 0.0; - return p_node->_pre_process(process_state, p_playback_info, p_test_only); } - return p_node->_pre_process(process_state, p_playback_info, p_test_only); + return p_other.node->_pre_process(p_process_state, p_other, p_playback_info, p_test_only); } String AnimationNode::get_caption() const { @@ -370,28 +349,28 @@ int AnimationNode::find_input(const String &p_name) const { return idx; } -AnimationNode::NodeTimeInfo AnimationNode::process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { - process_state->is_testing = p_test_only; +AnimationNode::NodeTimeInfo AnimationNode::process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { + p_process_state.is_testing = p_test_only; AnimationMixer::PlaybackInfo pi = p_playback_info; if (p_playback_info.seeked) { if (p_playback_info.is_external_seeking) { - pi.delta = get_node_time_info().position - p_playback_info.time; + pi.delta = get_node_time_info(p_instance, p_process_state).position - p_playback_info.time; } } else { - pi.time = get_node_time_info().position + p_playback_info.delta; + pi.time = get_node_time_info(p_instance, p_process_state).position + p_playback_info.delta; } - NodeTimeInfo nti = _process(pi, p_test_only); + NodeTimeInfo nti = _process(p_process_state, p_instance, pi, p_test_only); if (!p_test_only) { - set_node_time_info(nti); + set_node_time_info(p_instance, p_process_state, nti); } return nti; } -AnimationNode::NodeTimeInfo AnimationNode::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { +AnimationNode::NodeTimeInfo AnimationNode::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { double r_ret = 0.0; GDVIRTUAL_CALL(_process, p_playback_info.time, p_playback_info.seeked, p_playback_info.is_external_seeking, p_test_only, r_ret); NodeTimeInfo nti; @@ -401,14 +380,17 @@ AnimationNode::NodeTimeInfo AnimationNode::_process(const AnimationMixer::Playba void AnimationNode::set_filter_path(const NodePath &p_path, bool p_enable) { if (p_enable) { - filter[p_path] = true; + (void)p_path.hash(); // Make sure the cache is valid. + filter.insert(p_path); } else { filter.erase(p_path); } + filters_dirty = true; } void AnimationNode::set_filter_enabled(bool p_enable) { filter_enabled = p_enable; + filters_dirty = true; } bool AnimationNode::is_filter_enabled() const { @@ -424,13 +406,13 @@ bool AnimationNode::is_deletable() const { } ObjectID AnimationNode::get_processing_animation_tree_instance_id() const { - ERR_FAIL_NULL_V(process_state, ObjectID()); - return process_state->tree->get_instance_id(); + ERR_FAIL_NULL_V(tls_process_state, ObjectID()); + return tls_process_state->tree->get_instance_id(); } bool AnimationNode::is_process_testing() const { - ERR_FAIL_NULL_V(process_state, false); - return process_state->is_testing; + ERR_FAIL_NULL_V(tls_process_state, false); + return tls_process_state->is_testing; } bool AnimationNode::is_path_filtered(const NodePath &p_path) const { @@ -446,8 +428,8 @@ bool AnimationNode::has_filter() const { Array AnimationNode::_get_filters() const { Array paths; - for (const KeyValue &E : filter) { - paths.push_back(String(E.key)); // Use strings, so sorting is possible. + for (const NodePath &E : filter) { + paths.push_back(String(E)); // Use strings, so sorting is possible. } paths.sort(); // Done so every time the scene is saved, it does not change. @@ -461,6 +443,24 @@ void AnimationNode::_set_filters(const Array &p_filters) { } } +void AnimationNode::_update_filter_cache(const ProcessState &p_process_state, const AnimationNodeInstance &p_instance) { + if (!p_process_state.track_map_updated && !filters_dirty) { + return; // Cache is valid. + } + + p_instance.filtered_track_indices_cache.clear(); + if (p_instance.filtered_track_indices_cache.size() < filter.size()) { + p_instance.filtered_track_indices_cache.reserve(filter.size()); + } + + for (const NodePath &path : filter) { + if (const int *p = p_process_state.track_map->getptr(path)) { + p_instance.filtered_track_indices_cache.push_back(*p); + } + } + filters_dirty = false; +} + void AnimationNode::_validate_property(PropertyInfo &p_property) const { if (!has_filter() && (p_property.name == "filter_enabled" || p_property.name == "filters")) { p_property.usage = PROPERTY_USAGE_NONE; @@ -486,6 +486,9 @@ Ref AnimationNode::find_node_by_path(const String &p_name) const } void AnimationNode::blend_animation_ex(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag) { + ERR_FAIL_NULL(tls_process_state); + ERR_FAIL_NULL(current_instance); + AnimationMixer::PlaybackInfo info; info.time = p_time; info.delta = p_delta; @@ -493,26 +496,39 @@ void AnimationNode::blend_animation_ex(const StringName &p_animation, double p_t info.is_external_seeking = p_is_external_seeking; info.weight = p_blend; info.looped_flag = p_looped_flag; - blend_animation(p_animation, info); + + blend_animation(*tls_process_state, *current_instance, p_animation, info); } -double AnimationNode::blend_node_ex(const StringName &p_sub_path, Ref p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync, bool p_test_only) { +double AnimationNode::blend_node_ex(const StringName &p_sub_path, const Ref &p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync, bool p_test_only) { + ERR_FAIL_NULL_V(tls_process_state, 0.0); + ERR_FAIL_NULL_V(current_instance, 0.0); + AnimationMixer::PlaybackInfo info; info.time = p_time; info.seeked = p_seek; info.is_external_seeking = p_is_external_seeking; info.weight = p_blend; - NodeTimeInfo nti = blend_node(p_node, p_sub_path, info, p_filter, p_sync, p_test_only); + + // There can be multiple AnimationNodeInstances sharing the same AnimationNode. + // This sucks because we may not find the correct AnimationNodeInstance here. + // The method needs to be changed, so that it either passes in the name/subpath, or the index. + AnimationNodeInstance *other_instance = tls_process_state->tree->get_first_node_instance_or_null(p_node->get_instance_id(), get_instance_id()); + NodeTimeInfo nti = blend_node(*tls_process_state, *current_instance, other_instance, p_sub_path, info, p_filter, p_sync, p_test_only); return nti.length - nti.position; } double AnimationNode::blend_input_ex(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync, bool p_test_only) { + ERR_FAIL_NULL_V(tls_process_state, 0.0); + ERR_FAIL_NULL_V(current_instance, 0.0); + AnimationMixer::PlaybackInfo info; info.time = p_time; info.seeked = p_seek; info.is_external_seeking = p_is_external_seeking; info.weight = p_blend; - NodeTimeInfo nti = blend_input(p_input, info, p_filter, p_sync, p_test_only); + + NodeTimeInfo nti = blend_input(*tls_process_state, *current_instance, p_input, info, p_filter, p_sync, p_test_only); return nti.length - nti.position; } @@ -526,7 +542,7 @@ void AnimationNode::get_argument_options(const StringName &p_function, int p_idx } } else if (pf == "get_parameter" || pf == "set_parameter") { bool is_setter = pf == "set_parameter"; - List parameters; + LocalVector parameters; get_parameter_list(¶meters); for (const PropertyInfo &E : parameters) { if (is_setter && is_parameter_read_only(E.name)) { @@ -535,8 +551,8 @@ void AnimationNode::get_argument_options(const StringName &p_function, int p_idx r_options->push_back(E.name.quote()); } } else if (pf == "set_filter_path" || pf == "is_path_filtered") { - for (const KeyValue &E : filter) { - r_options->push_back(String(E.key).quote()); + for (const NodePath &E : filter) { + r_options->push_back(String(E).quote()); } } } @@ -569,8 +585,8 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "is_external_seeking", "blend", "filter", "sync", "test_only"), &AnimationNode::blend_node_ex, DEFVAL(FILTER_IGNORE), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "is_external_seeking", "blend", "filter", "sync", "test_only"), &AnimationNode::blend_input_ex, DEFVAL(FILTER_IGNORE), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter); - ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter); + ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter_ex); + ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter_ex); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_filter_enabled", "is_filter_enabled"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters"); @@ -643,9 +659,13 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const if (root_animation_node.is_null()) { return false; } + AnimationNodeInstance &instance = get_node_instance_by_path(SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data())); { // Setup. process_pass++; + if (unlikely(process_pass == 0)) { + process_pass = 1; + } // Init process state. process_state = AnimationNode::ProcessState(); @@ -654,32 +674,37 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const process_state.invalid_reasons = ""; process_state.last_pass = process_pass; process_state.track_map = &p_track_map; + process_state.track_map_updated = track_map_version != last_track_map_version; + process_state.is_testing = false; + + last_track_map_version = track_map_version; // Init node state for root AnimationNode. - root_animation_node->node_state.track_weights.resize(p_track_count); - real_t *src_blendsw = root_animation_node->node_state.track_weights.ptr(); + instance.track_weights.resize(p_track_count); + real_t *src_blendsw = instance.track_weights.ptr(); for (int i = 0; i < p_track_count; i++) { src_blendsw[i] = 1.0; // By default all go to 1 for the root input. } - root_animation_node->set_node_state_base_path(SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data())); - root_animation_node->node_state.parent = nullptr; + instance.base_path = SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data()); + instance.parent_node = nullptr; } // Process. { PlaybackInfo pi; + pi.delta = p_delta; if (started) { + started = false; // If started, seek. pi.seeked = true; - pi.delta = p_delta; - root_animation_node->_pre_process(&process_state, pi, false); - started = false; } else { pi.seeked = false; - pi.delta = p_delta; - root_animation_node->_pre_process(&process_state, pi, false); } + + AnimationNode::tls_process_state = &process_state; + root_animation_node->_pre_process(process_state, instance, pi, false); + AnimationNode::tls_process_state = nullptr; } if (!process_state.valid) { @@ -710,10 +735,6 @@ String AnimationTree::get_invalid_state_reason() const { return process_state.invalid_reasons; } -uint64_t AnimationTree::get_last_process_pass() const { - return process_pass; -} - PackedStringArray AnimationTree::get_configuration_warnings() const { PackedStringArray warnings = AnimationMixer::get_configuration_warnings(); if (root_animation_node.is_null()) { @@ -732,15 +753,20 @@ void AnimationTree::_tree_changed() { } void AnimationTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) { - ERR_FAIL_COND(!property_reference_map.has(p_oid)); - String base_path = property_reference_map[p_oid]; - String old_base = base_path + p_old_name; - String new_base = base_path + p_new_name; - for (const PropertyInfo &E : properties) { - if (E.name.begins_with(old_base)) { - String new_name = E.name.replace_first(old_base, new_base); - property_map[new_name] = property_map[E.name]; - property_map.erase(E.name); + //print_line("Node: " + ObjectDB::get_instance(p_oid)->get_class() + " (" + itos(p_oid) + ") renamed: " + p_old_name + " -> " + p_new_name); + for (auto &pp : instance_paths[p_oid]) { + String parent_path = pp; + String old_base = parent_path + p_old_name; + String new_base = parent_path + p_new_name; + //print_line(" - Updating " + pp + ": " + old_base + " -> " + new_base); + for (const PropertyInfo &E : properties) { + if (E.name.begins_with(old_base)) { + StringName old_name = E.name; + StringName new_name = E.name.replace_first(old_base, new_base); + //print_line(" - Property: " + String(old_name) + " -> " + String(new_name)); + property_map[new_name] = property_map[old_name]; + property_map.erase(old_name); + } } } @@ -750,11 +776,13 @@ void AnimationTree::_animation_node_renamed(const ObjectID &p_oid, const String } void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) { - ERR_FAIL_COND(!property_reference_map.has(p_oid)); - String base_path = String(property_reference_map[p_oid]) + String(p_node); - for (const PropertyInfo &E : properties) { - if (E.name.begins_with(base_path)) { - property_map.erase(E.name); + for (auto &parent_path : instance_paths[p_oid]) { + String base_path = String(parent_path) + String(p_node); + + for (const PropertyInfo &E : properties) { + if (E.name.begins_with(base_path)) { + property_map.erase(E.name); + } } } @@ -763,14 +791,18 @@ void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringN _update_properties(); } -void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref p_node) const { +void AnimationTree::_update_properties_for_node(const StringName &p_base_path, const Ref &p_node) const { ERR_FAIL_COND(p_node.is_null()); - if (!property_parent_map.has(p_base_path)) { - property_parent_map[p_base_path] = AHashMap(); - } - if (!property_reference_map.has(p_node->get_instance_id())) { - property_reference_map[p_node->get_instance_id()] = p_base_path; - } + + //print_line("Inserting Node: " + p_node->get_class() + " (" + itos(p_node->get_instance_id()) + ") base_path: " + String(p_base_path)); + instance_paths[p_node->get_instance_id()].insert(p_base_path); + //print_line(" - instance_path_count: " + itos(instance_paths[p_node->get_instance_id()].size())); + + const String base_path_str = String(p_base_path); + + AnimationNodeInstance &instance = instance_map[p_base_path]; + instance.base_path = p_base_path; + instance.node = p_node; if (p_node->get_input_count() && !input_activity_map.has(p_base_path)) { LocalVector activity; @@ -781,32 +813,38 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref plist; + LocalVector plist; p_node->get_parameter_list(&plist); for (PropertyInfo &pinfo : plist) { - StringName key = pinfo.name; - - if (!property_map.has(p_base_path + key)) { - Pair param; - param.first = p_node->get_parameter_default_value(key); - param.second = p_node->is_parameter_read_only(key); - property_map[p_base_path + key] = param; + StringName pname = pinfo.name; + StringName key = base_path_str + pname; + + bool property_was_added = false; + Pair ¶m = property_map.get_value_ref_or_add_default(key, property_was_added); + if (property_was_added) { + param.first = p_node->get_parameter_default_value(pname); + param.second = p_node->is_parameter_read_only(pname); } - property_parent_map[p_base_path][key] = p_base_path + key; + instance.property_parent_map[pname] = key; - pinfo.name = p_base_path + key; + pinfo.name = key; properties.push_back(pinfo); } - p_node->make_cache_dirty(); - List children; + + LocalVector children; p_node->get_child_nodes(&children); for (const AnimationNode::ChildNode &E : children) { - _update_properties_for_node(p_base_path + E.name + "/", E.node); + const String child_base = base_path_str + E.name + "/"; + instance.child_base_cache[E.name] = child_base; + } + + for (const AnimationNode::ChildNode &E : children) { + _update_properties_for_node(base_path_str + E.name + "/", E.node); } } @@ -816,13 +854,58 @@ void AnimationTree::_update_properties() const { } properties.clear(); - property_reference_map.clear(); - property_parent_map.clear(); + instance_map.clear(); input_activity_map.clear(); input_activity_map_get.clear(); + instance_paths.clear(); if (root_animation_node.is_valid()) { _update_properties_for_node(Animation::PARAMETERS_BASE_PATH, root_animation_node); + + // Now that the properties are stable, we can update each instance. + for (KeyValue &E : instance_map) { + AnimationNodeInstance &instance = E.value; + + // Update AnimationNodeInstance::children_instances and set parents. + for (KeyValue &child_name_to_full_path : instance.child_base_cache) { + AnimationNodeInstance *child_instance = instance_map.getptr(child_name_to_full_path.value); + ERR_CONTINUE(!child_instance); + + instance.children_instances.insert(child_name_to_full_path.key, child_instance); + child_instance->parent = &instance; + } + + // Now properties. + const Ref &node = E.value.node; + ERR_FAIL_COND(node.is_null()); + + LocalVector plist; + instance.parameter_ptrs_by_slot.reserve(plist.size()); + node->get_parameter_list(&plist); + for (const PropertyInfo &pinfo : plist) { + StringName pname = pinfo.name; + + const StringName *k = instance.property_parent_map.getptr(pname); + ERR_CONTINUE(!k); + const StringName &key = *k; + + Pair *p = property_map.getptr(key); + ERR_CONTINUE(!p); + Pair *pair = p; + + instance.property_ptrs[pname] = pair; + const uint32_t slot_index = instance.parameter_ptrs_by_slot.size(); + instance.parameter_ptrs_by_slot.push_back(pair); + + if (pname == node->current_length) { + instance.slot_current_length = slot_index; + } else if (pname == node->current_position) { + instance.slot_current_position = slot_index; + } else if (pname == node->current_delta) { + instance.slot_current_delta = slot_index; + } + } + } } properties_dirty = false; @@ -886,7 +969,7 @@ void AnimationTree::_setup_animation_player() { while (animation_libraries.size()) { remove_animation_library(animation_libraries[0].name); } - List list; + LocalVector list; player->get_animation_library_list(&list); for (const StringName &E : list) { Ref lib = player->get_animation_library(E); @@ -957,8 +1040,8 @@ bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const { _update_properties(); } - if (property_map.has(p_name)) { - r_ret = property_map[p_name].first; + if (const Pair *p = property_map.getptr(p_name)) { + r_ret = p->first; return true; } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 5a612f80e9f0..d438ec3bf463 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -39,6 +39,7 @@ class AnimationNodeBlendTree; class AnimationNodeStartState; class AnimationNodeEndState; class AnimationTree; +struct AnimationNodeInstance; class AnimationNode : public Resource { GDCLASS(AnimationNode, Resource); @@ -59,8 +60,9 @@ class AnimationNode : public Resource { bool closable = false; LocalVector inputs; - AHashMap filter; + HashSet filter; bool filter_enabled = false; + bool filters_dirty = true; // To propagate information from upstream for use in estimation of playback progress. // These values must be taken from the result of blend_node() or blend_input() and must be essentially read-only. @@ -94,87 +96,55 @@ class AnimationNode : public Resource { } }; - // Temporary state for blending process which needs to be stored in each AnimationNodes. - struct NodeState { - friend AnimationNode; - - private: - StringName base_path; - - public: - AnimationNode *parent = nullptr; - Vector connections; - LocalVector track_weights; - - const StringName get_base_path() const { - return base_path; - } - - } node_state; - // Temporary state for blending process which needs to be started in the AnimationTree, pass through the AnimationNodes, and then return to the AnimationTree. struct ProcessState { AnimationTree *tree = nullptr; const AHashMap *track_map; // TODO: Is there a better way to manage filter/tracks? + bool track_map_updated = false; bool is_testing = false; bool valid = false; String invalid_reasons; uint64_t last_pass = 0; - } *process_state = nullptr; + }; -private: - mutable AHashMap property_cache; + // For performance ProcessState needs to be passed down, + // but the scripting api was already exposed before this optimization was made. + // So to keep compatibility, we need this internal state, so that the scripting api can continue working as before. + // It also must be thread_local, because multiple AnimationTrees can be processed in different threads. + static thread_local ProcessState *tls_process_state; + static thread_local AnimationNodeInstance *current_instance; public: - void set_node_state_base_path(const StringName p_base_path) { - if (p_base_path != node_state.base_path) { - node_state.base_path = p_base_path; - make_cache_dirty(); - } - } - - void set_node_state_base_path(const String p_base_path) { - if (p_base_path != node_state.base_path) { - node_state.base_path = p_base_path; - make_cache_dirty(); - } - } - - const StringName get_node_state_base_path() const { - return node_state.get_base_path(); - } - - void make_cache_dirty() { - property_cache.clear(); - } Array _get_filters() const; void _set_filters(const Array &p_filters); + + void _update_filter_cache(const ProcessState &p_process_state, const AnimationNodeInstance &p_instance); + friend class AnimationNodeBlendTree; // The time information is passed from upstream to downstream by AnimationMixer::PlaybackInfo::p_playback_info until AnimationNodeAnimation processes it. // Conversely, AnimationNodeAnimation returns the processed result as NodeTimeInfo from downstream to upstream. - NodeTimeInfo _blend_node(Ref p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false, real_t *r_activity = nullptr); - NodeTimeInfo _pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false); + NodeTimeInfo _blend_node(ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeInstance &p_other, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false, real_t *r_activity = nullptr); + NodeTimeInfo _pre_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false); protected: StringName current_length = "current_length"; StringName current_position = "current_position"; StringName current_delta = "current_delta"; - virtual NodeTimeInfo process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false); // To organize time information. Virtualizing for especially AnimationNodeAnimation needs to take "backward" into account. - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false); // Main process. + virtual NodeTimeInfo process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false); // To organize time information. Virtualizing for especially AnimationNodeAnimation needs to take "backward" into account. + virtual NodeTimeInfo _process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false); // Main process. - void blend_animation(const StringName &p_animation, AnimationMixer::PlaybackInfo p_playback_info); - NodeTimeInfo blend_node(Ref p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false); - NodeTimeInfo blend_input(int p_input, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false); + void blend_animation(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const StringName &p_animation, AnimationMixer::PlaybackInfo &p_playback_info); + NodeTimeInfo blend_node(ProcessState &p_process_state, AnimationNodeInstance &p_instance, AnimationNodeInstance *p_other, const StringName &p_subpath, const AnimationMixer::PlaybackInfo &p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false); + NodeTimeInfo blend_input(ProcessState &p_process_state, AnimationNodeInstance &p_instance, int p_input, const AnimationMixer::PlaybackInfo &p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false); // Bind-able methods to expose for compatibility, moreover AnimationMixer::PlaybackInfo is not exposed. void blend_animation_ex(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE); - double blend_node_ex(const StringName &p_sub_path, Ref p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false); + double blend_node_ex(const StringName &p_sub_path, const Ref &p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false); double blend_input_ex(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false); - void make_invalid(const String &p_reason); - AnimationTree *get_animation_tree() const; + void make_invalid(ProcessState &p_process_state, const String &p_reason); static void _bind_methods(); @@ -190,22 +160,22 @@ class AnimationNode : public Resource { GDVIRTUAL0RC(bool, _has_filter) public: - virtual void get_parameter_list(List *r_list) const; + virtual void get_parameter_list(LocalVector *r_list) const; virtual Variant get_parameter_default_value(const StringName &p_parameter) const; virtual bool is_parameter_read_only(const StringName &p_parameter) const; - void set_parameter(const StringName &p_name, const Variant &p_value); - Variant get_parameter(const StringName &p_name) const; + void set_parameter_ex(const StringName &p_name, const Variant &p_value); + Variant get_parameter_ex(const StringName &p_name) const; - void set_node_time_info(const NodeTimeInfo &p_node_time_info); // Wrapper of set_parameter(). - virtual NodeTimeInfo get_node_time_info() const; // Wrapper of get_parameter(). + void set_node_time_info(AnimationNodeInstance &instance, ProcessState &p_process_state, const NodeTimeInfo &p_node_time_info); // Wrapper of set_parameter(). + virtual NodeTimeInfo get_node_time_info(AnimationNodeInstance &instance, ProcessState &p_process_state) const; // Wrapper of get_parameter(). struct ChildNode { StringName name; Ref node; }; - virtual void get_child_nodes(List *r_child_nodes); + virtual void get_child_nodes(LocalVector *r_child_nodes); virtual String get_caption() const; @@ -261,6 +231,116 @@ class AnimationNodeEndState : public AnimationRootNode { GDCLASS(AnimationNodeEndState, AnimationRootNode); }; +// Per instance data, for a node. +struct AnimationNodeInstance { + AnimationNodeInstance *parent = nullptr; + // TODO: Maybe ptr to parent AnimationNodeInstance for faster access??? + AnimationNode *parent_node = nullptr; + // Multiple AnimationNodeInstance's can share the same node btw + Ref node = Ref(); + Vector connections; // TODO: Can we use LocalVector instead? + LocalVector track_weights; + mutable LocalVector filtered_track_indices_cache; // filtered track indices + StringName base_path; + mutable AHashMap children_instances; + mutable AHashMap property_cache; + // todo: should be renamed to child_name_to_full_path_cache? + mutable AHashMap child_base_cache; // child_name -> child_base + mutable AHashMap property_parent_map; // local property name -> full property path + mutable AHashMap *> property_ptrs; + + // This makes it faster to access the most commonly used parameters, since we just index an array instead of doing a hash lookup. + // But unfortunately, these are still Variants, which are quite slow + // (ideally, get_parameter and set_parameter are removed, and everything is made strongly typed without variant). + LocalVector *> parameter_ptrs_by_slot; + + // We can't put the members in here directly, because when AnimationNodeInstances are destroyed they do not persist data. + // In an ideal world, we would create a structure like `AnimationNodeInstanceParameters` + // It would contain the strongly typed members, and it would be persisted in AnimationTree. + // But that is a ton of work, and this is a good enough optimization for now. + uint32_t slot_current_length = static_cast(-1); + uint32_t slot_current_position = static_cast(-1); + uint32_t slot_current_delta = static_cast(-1); + + _FORCE_INLINE_ void set_parameter(const StringName &p_name, const Variant &p_value, const bool p_test_only) { + if (p_test_only) { + return; + } + + if (p_name == SNAME("current_length")) { + ERR_FAIL_COND(p_value.get_type() != Variant::FLOAT); + Pair *prop = parameter_ptrs_by_slot[slot_current_length]; + prop->first = p_value; + return; + } + if (p_name == SNAME("current_position")) { + ERR_FAIL_COND(p_value.get_type() != Variant::FLOAT); + Pair *prop = parameter_ptrs_by_slot[slot_current_position]; + prop->first = p_value; + return; + } + if (p_name == SNAME("current_delta")) { + ERR_FAIL_COND(p_value.get_type() != Variant::FLOAT); + Pair *prop = parameter_ptrs_by_slot[slot_current_delta]; + prop->first = p_value; + return; + } + + Pair **p = property_ptrs.getptr(p_name); + ERR_FAIL_NULL(p); + Pair &prop = **p; + + // Only copy variant if needed. + if (Animation::needs_type_cast(prop.first, p_value)) { + Variant value = p_value; + if (Animation::validate_type_match(prop.first, value)) { + prop.first = value; + } + } else { + prop.first = p_value; + } + } + + _FORCE_INLINE_ Variant &get_parameter(const StringName &p_name) { + static Variant dummy = Variant(); + + if (p_name == SNAME("current_length")) { + Pair *prop = parameter_ptrs_by_slot[slot_current_length]; + return prop->first; + } + if (p_name == SNAME("current_position")) { + Pair *prop = parameter_ptrs_by_slot[slot_current_position]; + return prop->first; + } + if (p_name == SNAME("current_delta")) { + Pair *prop = parameter_ptrs_by_slot[slot_current_delta]; + return prop->first; + } + + Pair **p = property_ptrs.getptr(p_name); + ERR_FAIL_NULL_V(p, dummy); + Pair &prop = **p; + return prop.first; + } + + _FORCE_INLINE_ AnimationNodeInstance *get_child_instance_by_path_or_null(const StringName &p_path) { + AnimationNodeInstance **instance_ptr = children_instances.getptr(p_path); + if (!instance_ptr) { + return nullptr; + } + AnimationNodeInstance *instance = *instance_ptr; + return instance; + } + + _FORCE_INLINE_ AnimationNodeInstance &get_child_instance_by_path(const StringName &p_path) { + AnimationNodeInstance **instance_ptr = children_instances.getptr(p_path); + CRASH_COND_MSG(!instance_ptr, "No child instance found for path: \"" + String(p_path) + "\""); + AnimationNodeInstance *instance = *instance_ptr; + CRASH_COND_MSG(!instance, "Child instance pointer is null for path : \"" + String(p_path) + "\""); + return *instance; + } +}; + class AnimationTree : public AnimationMixer { GDCLASS(AnimationTree, AnimationMixer); @@ -279,20 +359,23 @@ class AnimationTree : public AnimationMixer { AnimationNode::ProcessState process_state; uint64_t process_pass = 1; + uint64_t last_track_map_version = 0; bool started = true; friend class AnimationNode; - mutable List properties; - mutable AHashMap> property_parent_map; - mutable AHashMap property_reference_map; + StringName current_edited_node_path = StringName(); + mutable LocalVector properties; mutable AHashMap> property_map; // Property value and read-only flag. + //mutable AHashMap instance_map; + mutable AHashMap instance_map; + mutable AHashMap> instance_paths; mutable bool properties_dirty = true; void _update_properties() const; - void _update_properties_for_node(const String &p_base_path, Ref p_node) const; + void _update_properties_for_node(const StringName &p_base_path, const Ref &p_node) const; void _tree_changed(); void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name); @@ -334,6 +417,36 @@ class AnimationTree : public AnimationMixer { #endif // DISABLE_DEPRECATED public: + void set_edited_node_path(const ObjectID &p_oid, const StringName &p_path) { + current_edited_node_path = p_path; + } + + AnimationNodeInstance *get_first_node_instance_or_null(const ObjectID p_id, const ObjectID p_prefer_parent) { + const HashSet *paths = instance_paths.getptr(p_id); + if (!paths || paths->size() == 0) { + return nullptr; + } + + AnimationNodeInstance *instance = nullptr; + for (const StringName &path : *paths) { + instance = instance_map.getptr(path); + if (!instance) { + continue; + } + if (instance->parent && instance->parent->node->get_instance_id() == p_prefer_parent) { + break; // Prefer parent match. + } + } + + return instance; + } + + AnimationNodeInstance &get_node_instance_by_path(const StringName &p_path) { + AnimationNodeInstance *instance = instance_map.getptr(p_path); + CRASH_COND_MSG(instance == nullptr, "No instance found for path %s" + String(p_path)); + return *instance; + } + void set_animation_player(const NodePath &p_path); NodePath get_animation_player() const; @@ -350,8 +463,6 @@ class AnimationTree : public AnimationMixer { real_t get_connection_activity(const StringName &p_path, int p_connection) const; - uint64_t get_last_process_pass() const; - #ifdef TOOLS_ENABLED String get_editor_error_message() const; #endif diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index f29ce142fddd..e3a22ca35cf7 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1090,7 +1090,7 @@ void Animation::track_set_interpolation_loop_wrap(int p_track, bool p_enable) { } bool Animation::track_get_interpolation_loop_wrap(int p_track) const { - ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_track, tracks.size(), INTERPOLATION_NEAREST); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_track, tracks.size(), false); return tracks[p_track]->loop_wrap; } @@ -1294,8 +1294,8 @@ Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quat Quaternion Animation::rotation_track_interpolate(int p_track, double p_time, bool p_backward) const { Quaternion ret = Quaternion(0, 0, 0, 1); ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_track, tracks.size(), ret); - bool err = try_rotation_track_interpolate(p_track, p_time, &ret, p_backward); - ERR_FAIL_COND_V_MSG(err, ret, "3D Rotation Track: '" + String(tracks[p_track]->path) + "' is unavailable."); + Error err = try_rotation_track_interpolate(p_track, p_time, &ret, p_backward); + ERR_FAIL_COND_V_MSG(err != OK, ret, "3D Rotation Track: '" + String(tracks[p_track]->path) + "' is unavailable."); return ret; } @@ -2765,7 +2765,7 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const } template -void Animation::_track_get_key_indices_in_range(const LocalVector &p_array, double from_time, double to_time, List *p_indices, bool p_is_backward) const { +void Animation::_track_get_key_indices_in_range(const LocalVector &p_array, double from_time, double to_time, LocalVector *r_indices, bool p_is_backward) const { int len = p_array.size(); if (len == 0) { return; @@ -2803,22 +2803,22 @@ void Animation::_track_get_key_indices_in_range(const LocalVector &p_array, d } if (from == to) { - p_indices->push_back(from); + r_indices->push_back(from); return; } if (!p_is_backward) { for (int i = from; i <= to; i++) { - p_indices->push_back(i); + r_indices->push_back(i); } } else { for (int i = to; i >= from; i--) { - p_indices->push_back(i); + r_indices->push_back(i); } } } -void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List *p_indices, Animation::LoopedFlag p_looped_flag) const { +void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, LocalVector *r_indices, Animation::LoopedFlag p_looped_flag) const { ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_track, tracks.size()); if (p_delta == 0) { @@ -2869,111 +2869,111 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast(t); if (tt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, r_indices); } else { if (!is_backward) { - _track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(tt->positions, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(tt->positions, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(tt->positions, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(tt->positions, from_time, anim_end, r_indices, is_backward); } } } break; case TYPE_ROTATION_3D: { const RotationTrack *rt = static_cast(t); if (rt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, r_indices); } else { if (!is_backward) { - _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, r_indices, is_backward); } } } break; case TYPE_SCALE_3D: { const ScaleTrack *st = static_cast(t); if (st->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, r_indices); } else { if (!is_backward) { - _track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(st->scales, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(st->scales, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(st->scales, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(st->scales, from_time, anim_end, r_indices, is_backward); } } } break; case TYPE_BLEND_SHAPE: { const BlendShapeTrack *bst = static_cast(t); if (bst->compressed_track >= 0) { - _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, r_indices); } else { if (!is_backward) { - _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, r_indices, is_backward); } } } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast(t); if (!is_backward) { - _track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(vt->values, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(vt->values, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(vt->values, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(vt->values, from_time, anim_end, r_indices, is_backward); } } break; case TYPE_METHOD: { const MethodTrack *mt = static_cast(t); if (!is_backward) { - _track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(mt->methods, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(mt->methods, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(mt->methods, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(mt->methods, from_time, anim_end, r_indices, is_backward); } } break; case TYPE_BEZIER: { const BezierTrack *bz = static_cast(t); if (!is_backward) { - _track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(bz->values, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(bz->values, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(bz->values, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(bz->values, from_time, anim_end, r_indices, is_backward); } } break; case TYPE_AUDIO: { const AudioTrack *ad = static_cast(t); if (!is_backward) { - _track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(ad->values, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(ad->values, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(ad->values, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(ad->values, from_time, anim_end, r_indices, is_backward); } } break; case TYPE_ANIMATION: { const AnimationTrack *an = static_cast(t); if (!is_backward) { - _track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward); - _track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(an->values, from_time, anim_end, r_indices, is_backward); + _track_get_key_indices_in_range(an->values, anim_start, to_time, r_indices, is_backward); } else { - _track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward); - _track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(an->values, anim_start, to_time, r_indices, is_backward); + _track_get_key_indices_in_range(an->values, from_time, anim_end, r_indices, is_backward); } } break; } @@ -2985,12 +2985,12 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl if (!is_backward && Math::is_equal_approx(from_time, 0)) { int edge = track_find_key(p_track, 0, FIND_MODE_EXACT); if (edge >= 0) { - p_indices->push_back(edge); + r_indices->push_back(edge); } } else if (is_backward && Math::is_equal_approx(to_time, length)) { int edge = track_find_key(p_track, length, FIND_MODE_EXACT); if (edge >= 0) { - p_indices->push_back(edge); + r_indices->push_back(edge); } } } @@ -3009,67 +3009,67 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast(t); if (tt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, r_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, r_indices); } else { - _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(tt->positions, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(tt->positions, 0, to_time, r_indices, false); } } break; case TYPE_ROTATION_3D: { const RotationTrack *rt = static_cast(t); if (rt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, r_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, r_indices); } else { - _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(rt->rotations, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(rt->rotations, 0, to_time, r_indices, false); } } break; case TYPE_SCALE_3D: { const ScaleTrack *st = static_cast(t); if (st->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, r_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, r_indices); } else { - _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(st->scales, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(st->scales, 0, to_time, r_indices, false); } } break; case TYPE_BLEND_SHAPE: { const BlendShapeTrack *bst = static_cast(t); if (bst->compressed_track >= 0) { - _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, r_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, r_indices); } else { - _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, r_indices, false); } } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast(t); - _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(vt->values, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(vt->values, 0, to_time, r_indices, false); } break; case TYPE_METHOD: { const MethodTrack *mt = static_cast(t); - _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(mt->methods, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(mt->methods, 0, to_time, r_indices, false); } break; case TYPE_BEZIER: { const BezierTrack *bz = static_cast(t); - _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(bz->values, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(bz->values, 0, to_time, r_indices, false); } break; case TYPE_AUDIO: { const AudioTrack *ad = static_cast(t); - _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(ad->values, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(ad->values, 0, to_time, r_indices, false); } break; case TYPE_ANIMATION: { const AnimationTrack *an = static_cast(t); - _track_get_key_indices_in_range(an->values, 0, from_time, p_indices, true); - _track_get_key_indices_in_range(an->values, 0, to_time, p_indices, false); + _track_get_key_indices_in_range(an->values, 0, from_time, r_indices, true); + _track_get_key_indices_in_range(an->values, 0, to_time, r_indices, false); } break; } return; @@ -3080,67 +3080,67 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast(t); if (tt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, r_indices); } else { - _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices, false); - _track_get_key_indices_in_range(tt->positions, to_time, length, p_indices, true); + _track_get_key_indices_in_range(tt->positions, from_time, length, r_indices, false); + _track_get_key_indices_in_range(tt->positions, to_time, length, r_indices, true); } } break; case TYPE_ROTATION_3D: { const RotationTrack *rt = static_cast(t); if (rt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, r_indices); } else { - _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices, false); - _track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices, true); + _track_get_key_indices_in_range(rt->rotations, from_time, length, r_indices, false); + _track_get_key_indices_in_range(rt->rotations, to_time, length, r_indices, true); } } break; case TYPE_SCALE_3D: { const ScaleTrack *st = static_cast(t); if (st->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, r_indices); } else { - _track_get_key_indices_in_range(st->scales, from_time, length, p_indices, false); - _track_get_key_indices_in_range(st->scales, to_time, length, p_indices, true); + _track_get_key_indices_in_range(st->scales, from_time, length, r_indices, false); + _track_get_key_indices_in_range(st->scales, to_time, length, r_indices, true); } } break; case TYPE_BLEND_SHAPE: { const BlendShapeTrack *bst = static_cast(t); if (bst->compressed_track >= 0) { - _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, r_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length, r_indices); } else { - _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices, false); - _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices, true); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, r_indices, false); + _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, r_indices, true); } } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast(t); - _track_get_key_indices_in_range(vt->values, from_time, length, p_indices, false); - _track_get_key_indices_in_range(vt->values, to_time, length, p_indices, true); + _track_get_key_indices_in_range(vt->values, from_time, length, r_indices, false); + _track_get_key_indices_in_range(vt->values, to_time, length, r_indices, true); } break; case TYPE_METHOD: { const MethodTrack *mt = static_cast(t); - _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices, false); - _track_get_key_indices_in_range(mt->methods, to_time, length, p_indices, true); + _track_get_key_indices_in_range(mt->methods, from_time, length, r_indices, false); + _track_get_key_indices_in_range(mt->methods, to_time, length, r_indices, true); } break; case TYPE_BEZIER: { const BezierTrack *bz = static_cast(t); - _track_get_key_indices_in_range(bz->values, from_time, length, p_indices, false); - _track_get_key_indices_in_range(bz->values, to_time, length, p_indices, true); + _track_get_key_indices_in_range(bz->values, from_time, length, r_indices, false); + _track_get_key_indices_in_range(bz->values, to_time, length, r_indices, true); } break; case TYPE_AUDIO: { const AudioTrack *ad = static_cast(t); - _track_get_key_indices_in_range(ad->values, from_time, length, p_indices, false); - _track_get_key_indices_in_range(ad->values, to_time, length, p_indices, true); + _track_get_key_indices_in_range(ad->values, from_time, length, r_indices, false); + _track_get_key_indices_in_range(ad->values, to_time, length, r_indices, true); } break; case TYPE_ANIMATION: { const AnimationTrack *an = static_cast(t); - _track_get_key_indices_in_range(an->values, from_time, length, p_indices, false); - _track_get_key_indices_in_range(an->values, to_time, length, p_indices, true); + _track_get_key_indices_in_range(an->values, from_time, length, r_indices, false); + _track_get_key_indices_in_range(an->values, to_time, length, r_indices, true); } break; } return; @@ -3158,54 +3158,54 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast(t); if (tt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, r_indices); } else { - _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(tt->positions, from_time, to_time, r_indices, is_backward); } } break; case TYPE_ROTATION_3D: { const RotationTrack *rt = static_cast(t); if (rt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, r_indices); } else { - _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(rt->rotations, from_time, to_time, r_indices, is_backward); } } break; case TYPE_SCALE_3D: { const ScaleTrack *st = static_cast(t); if (st->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, r_indices); } else { - _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(st->scales, from_time, to_time, r_indices, is_backward); } } break; case TYPE_BLEND_SHAPE: { const BlendShapeTrack *bst = static_cast(t); if (bst->compressed_track >= 0) { - _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, r_indices); } else { - _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, r_indices, is_backward); } } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast(t); - _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(vt->values, from_time, to_time, r_indices, is_backward); } break; case TYPE_METHOD: { const MethodTrack *mt = static_cast(t); - _track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(mt->methods, from_time, to_time, r_indices, is_backward); } break; case TYPE_BEZIER: { const BezierTrack *bz = static_cast(t); - _track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(bz->values, from_time, to_time, r_indices, is_backward); } break; case TYPE_AUDIO: { const AudioTrack *ad = static_cast(t); - _track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(ad->values, from_time, to_time, r_indices, is_backward); } break; case TYPE_ANIMATION: { const AnimationTrack *an = static_cast(t); - _track_get_key_indices_in_range(an->values, from_time, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(an->values, from_time, to_time, r_indices, is_backward); } break; } } @@ -3852,10 +3852,6 @@ void Animation::set_length(real_t p_length) { emit_changed(); } -real_t Animation::get_length() const { - return length; -} - void Animation::set_loop_mode(Animation::LoopMode p_loop_mode) { loop_mode = p_loop_mode; emit_changed(); @@ -5483,7 +5479,7 @@ bool Animation::_fetch_compressed(uint32_t p_compressed_track, double p_time, Ve } template -void Animation::_get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List *r_indices) const { +void Animation::_get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, LocalVector *r_indices) const { ERR_FAIL_COND(!compression.enabled); ERR_FAIL_UNSIGNED_INDEX(p_compressed_track, compression.bounds.size()); @@ -5677,6 +5673,24 @@ bool Animation::is_variant_interpolatable(const Variant p_value) { return (type >= Variant::BOOL && type <= Variant::STRING_NAME) || type == Variant::ARRAY || type >= Variant::PACKED_INT32_ARRAY; // PackedByteArray is unsigned, so it would be better to ignore since blending uses float. } +bool Animation::needs_type_cast(const Variant &p_from, const Variant &p_to) { + Variant::Type from_type = p_from.get_type(); + Variant::Type to_type = p_to.get_type(); + + if (from_type == to_type) { + return false; + } + + // Cast between double and int to avoid minor annoyances. + if (from_type == Variant::FLOAT && to_type == Variant::INT) { + return true; + } else if (from_type == Variant::INT && to_type == Variant::FLOAT) { + return true; + } + + return false; +} + bool Animation::validate_type_match(const Variant &p_from, Variant &r_to) { if (p_from.get_type() != r_to.get_type()) { // Cast r_to between double and int to avoid minor annoyances. diff --git a/scene/resources/animation.h b/scene/resources/animation.h index bbaac1bd6edf..f7b1f0047ea2 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -277,7 +277,7 @@ class Animation : public Resource { _FORCE_INLINE_ T _interpolate(const LocalVector> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const; template - _FORCE_INLINE_ void _track_get_key_indices_in_range(const LocalVector &p_array, double from_time, double to_time, List *p_indices, bool p_is_backward) const; + _FORCE_INLINE_ void _track_get_key_indices_in_range(const LocalVector &p_array, double from_time, double to_time, LocalVector *r_indices, bool p_is_backward) const; double length = 1.0; real_t step = DEFAULT_STEP; @@ -365,7 +365,7 @@ class Animation : public Resource { bool _fetch_compressed_by_index(uint32_t p_compressed_track, int p_index, Vector3i &r_value, double &r_time) const; int _get_compressed_key_count(uint32_t p_compressed_track) const; template - void _get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List *r_indices) const; + void _get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, LocalVector *r_indices) const; _FORCE_INLINE_ Quaternion _uncompress_quaternion(const Vector3i &p_value) const; _FORCE_INLINE_ Vector3 _uncompress_pos_scale(uint32_t p_compressed_track, const Vector3i &p_value) const; _FORCE_INLINE_ float _uncompress_blend_shape(const Vector3i &p_value) const; @@ -513,7 +513,7 @@ class Animation : public Resource { void copy_track(int p_track, Ref p_to_animation); - void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List *p_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const; + void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, LocalVector *r_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const; void add_marker(const StringName &p_name, double p_time); void remove_marker(const StringName &p_name); @@ -527,7 +527,7 @@ class Animation : public Resource { void set_marker_color(const StringName &p_name, const Color &p_color); void set_length(real_t p_length); - real_t get_length() const; + _FORCE_INLINE_ real_t get_length() const { return length; } void set_loop_mode(LoopMode p_loop_mode); LoopMode get_loop_mode() const; @@ -542,6 +542,7 @@ class Animation : public Resource { // Helper functions for Variant. static bool is_variant_interpolatable(const Variant p_value); + static bool needs_type_cast(const Variant &p_from, const Variant &p_to); static bool validate_type_match(const Variant &p_from, Variant &r_to); static Variant cast_to_blendwise(const Variant p_value);