diff options
Diffstat (limited to 'editor/plugins/animation_player_editor_plugin.cpp')
-rw-r--r-- | editor/plugins/animation_player_editor_plugin.cpp | 349 |
1 files changed, 276 insertions, 73 deletions
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 51d195e0e1..f2225fabad 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -44,6 +44,7 @@ #include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning. #include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning. #include "editor/scene_tree_dock.h" +#include "scene/animation/animation_tree.h" #include "scene/gui/separator.h" #include "scene/main/window.h" #include "scene/resources/animation.h" @@ -54,7 +55,11 @@ /////////////////////////////////// void AnimationPlayerEditor::_node_removed(Node *p_node) { - if (player && player == p_node) { + if (player && original_node == p_node) { + if (is_dummy) { + plugin->_clear_dummy_player(); + } + player = nullptr; set_process(false); @@ -63,12 +68,20 @@ void AnimationPlayerEditor::_node_removed(Node *p_node) { track_editor->set_root(nullptr); track_editor->show_select_node_warning(true); _update_player(); + + _ensure_dummy_player(); } } void AnimationPlayerEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PROCESS: { + if (!player || is_dummy) { + track_editor->show_inactive_player_warning(false); + } else { + track_editor->show_inactive_player_warning(!player->is_active()); + } + if (!player) { return; } @@ -103,13 +116,13 @@ void AnimationPlayerEditor::_notification(int p_what) { } break; case NOTIFICATION_ENTER_TREE: { - tool_anim->get_popup()->connect("id_pressed", callable_mp(this, &AnimationPlayerEditor::_animation_tool_menu)); + tool_anim->get_popup()->connect(SNAME("id_pressed"), callable_mp(this, &AnimationPlayerEditor::_animation_tool_menu)); - onion_skinning->get_popup()->connect("id_pressed", callable_mp(this, &AnimationPlayerEditor::_onion_skinning_menu)); + onion_skinning->get_popup()->connect(SNAME("id_pressed"), callable_mp(this, &AnimationPlayerEditor::_onion_skinning_menu)); - blend_editor.next->connect("item_selected", callable_mp(this, &AnimationPlayerEditor::_blend_editor_next_changed)); + blend_editor.next->connect(SNAME("item_selected"), callable_mp(this, &AnimationPlayerEditor::_blend_editor_next_changed)); - get_tree()->connect("node_removed", callable_mp(this, &AnimationPlayerEditor::_node_removed)); + get_tree()->connect(SNAME("node_removed"), callable_mp(this, &AnimationPlayerEditor::_node_removed)); add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("panel"), SNAME("Panel"))); } break; @@ -167,6 +180,10 @@ void AnimationPlayerEditor::_notification(int p_what) { _update_animation_list_icons(); } break; + + case NOTIFICATION_VISIBILITY_CHANGED: { + _ensure_dummy_player(); + } break; } } @@ -303,20 +320,19 @@ void AnimationPlayerEditor::_animation_selected(int p_which) { bool animation_library_is_foreign = EditorNode::get_singleton()->is_resource_read_only(anim); track_editor->set_animation(anim, animation_library_is_foreign); - Node *root = player->get_node(player->get_root()); + Node *root = player->get_node_or_null(player->get_root_node()); if (root) { track_editor->set_root(root); } } frame->set_max((double)anim->get_length()); - + autoplay->set_pressed(current == player->get_autoplay()); } else { track_editor->set_animation(Ref<Animation>(), true); track_editor->set_root(nullptr); + autoplay->set_pressed(false); } - autoplay->set_pressed(current == player->get_autoplay()); - AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying(); _animation_key_editor_seek(timeline_position, false); @@ -506,6 +522,9 @@ void AnimationPlayerEditor::_animation_name_edited() { undo_redo->add_undo_method(this, "_animation_player_changed", player); undo_redo->commit_action(); + if (is_dummy) { + plugin->_update_dummy_player(original_node); + } _select_anim_by_name(new_library_prefix + new_name); } break; @@ -534,7 +553,7 @@ void AnimationPlayerEditor::_animation_name_edited() { if (al.is_null()) { al.instantiate(); lib_added = true; - undo_redo->add_do_method(player, "add_animation_library", "", al); + undo_redo->add_do_method(fetch_mixer_for_library(), "add_animation_library", "", al); library_name = ""; } @@ -547,13 +566,17 @@ void AnimationPlayerEditor::_animation_name_edited() { undo_redo->add_undo_method(this, "_stop_onion_skinning"); } if (lib_added) { - undo_redo->add_undo_method(player, "remove_animation_library", ""); + undo_redo->add_undo_method(fetch_mixer_for_library(), "remove_animation_library", ""); } undo_redo->commit_action(); if (library_name != "") { library_name = library_name + "/"; } + + if (is_dummy) { + plugin->_update_dummy_player(original_node); + } _select_anim_by_name(library_name + new_name); } break; @@ -602,6 +625,10 @@ void AnimationPlayerEditor::_animation_name_edited() { if (library_name != "") { library_name = library_name + "/"; } + + if (is_dummy) { + plugin->_update_dummy_player(original_node); + } _select_anim_by_name(library_name + new_name); } break; } @@ -714,7 +741,7 @@ void AnimationPlayerEditor::_blend_edited() { } void AnimationPlayerEditor::ensure_visibility() { - if (player && pin->is_pressed()) { + if (player) { return; // another player is pinned, don't reset } @@ -724,11 +751,13 @@ void AnimationPlayerEditor::ensure_visibility() { Dictionary AnimationPlayerEditor::get_state() const { Dictionary d; - d["visible"] = is_visible_in_tree(); - if (EditorNode::get_singleton()->get_edited_scene() && is_visible_in_tree() && player) { - d["player"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(player); - d["animation"] = player->get_assigned_animation(); - d["track_editor_state"] = track_editor->get_state(); + if (!is_dummy) { + d["visible"] = is_visible_in_tree(); + if (EditorNode::get_singleton()->get_edited_scene() && is_visible_in_tree() && player) { + d["player"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(player); + d["animation"] = player->get_assigned_animation(); + d["track_editor_state"] = track_editor->get_state(); + } } return d; @@ -746,14 +775,20 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]); if (Object::cast_to<AnimationPlayer>(n) && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) { if (player) { - if (player->is_connected("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { - player->disconnect("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated)); + if (player->is_connected(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { + player->disconnect(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated)); + } + if (player->is_connected(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed))) { + player->disconnect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed)); } } player = Object::cast_to<AnimationPlayer>(n); if (player) { - if (!player->is_connected("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { - player->connect("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated)); + if (!player->is_connected(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { + player->connect(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated), CONNECT_DEFERRED); + } + if (!player->is_connected(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed))) { + player->connect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed), CONNECT_DEFERRED); } } @@ -794,7 +829,7 @@ void AnimationPlayerEditor::_animation_edit() { track_editor->set_animation(anim, animation_library_is_foreign); - Node *root = player->get_node(player->get_root()); + Node *root = player->get_node_or_null(player->get_root_node()); if (root) { track_editor->set_root(root); } @@ -935,7 +970,7 @@ void AnimationPlayerEditor::_update_player() { bool animation_library_is_foreign = EditorNode::get_singleton()->is_resource_read_only(anim); track_editor->set_animation(anim, animation_library_is_foreign); - Node *root = player->get_node(player->get_root()); + Node *root = player->get_node_or_null(player->get_root_node()); if (root) { track_editor->set_root(root); } @@ -1007,21 +1042,69 @@ void AnimationPlayerEditor::_update_name_dialog_library_dropdown() { } } -void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { +void AnimationPlayerEditor::_ensure_dummy_player() { + bool dummy_exists = is_dummy && player && original_node; + if (dummy_exists) { + if (is_visible()) { + player->set_active(true); + original_node->set_editing(true); + } else { + player->set_active(false); + original_node->set_editing(false); + } + } + + // Make some options disabled. + tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(TOOL_EDIT_TRANSITIONS), dummy_exists); + onion_toggle->set_disabled(dummy_exists); + onion_skinning->set_disabled(dummy_exists); + int selected = animation->get_selected(); + autoplay->set_disabled(selected != -1 ? (animation->get_item_text(selected).is_empty() ? true : dummy_exists) : true); + + // Show warning. + if (track_editor) { + track_editor->show_dummy_player_warning(dummy_exists); + } +} + +void AnimationPlayerEditor::edit(AnimationMixer *p_node, AnimationPlayer *p_player, bool p_is_dummy) { if (player && pin->is_pressed()) { return; // Ignore, pinned. } if (player) { - if (player->is_connected("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { - player->disconnect("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated)); + if (player->is_connected(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { + player->disconnect(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated)); + } + if (player->is_connected(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed))) { + player->disconnect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed)); } } + + AnimationTree *tree = Object::cast_to<AnimationTree>(p_node); + + if (tree) { + if (tree->is_connected(SNAME("animation_player_changed"), callable_mp(this, &AnimationPlayerEditor::unpin))) { + tree->disconnect(SNAME("animation_player_changed"), callable_mp(this, &AnimationPlayerEditor::unpin)); + } + } + + original_node = p_node; player = p_player; + is_dummy = p_is_dummy; + + if (tree) { + if (!tree->is_connected(SNAME("animation_player_changed"), callable_mp(this, &AnimationPlayerEditor::unpin))) { + tree->connect(SNAME("animation_player_changed"), callable_mp(this, &AnimationPlayerEditor::unpin)); + } + } if (player) { - if (!player->is_connected("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { - player->connect("animation_libraries_updated", callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated)); + if (!player->is_connected(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated))) { + player->connect(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated), CONNECT_DEFERRED); + } + if (!player->is_connected(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed))) { + player->connect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed)); } _update_player(); @@ -1042,7 +1125,9 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { track_editor->show_select_node_warning(true); } - library_editor->set_animation_player(player); + library_editor->set_animation_mixer(fetch_mixer_for_library()); + + _ensure_dummy_player(); } void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) { @@ -1181,12 +1266,12 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool if (!p_timeline_only) { if (player->is_valid() && !p_set) { - float cpos = player->get_current_animation_position(); - - player->seek_delta(pos, pos - cpos); + double delta = pos - player->get_current_animation_position(); + player->seek(pos, true, true); + player->seek(pos + delta, true, true); } else { player->stop(); - player->seek(pos, true); + player->seek(pos, true, true); } } @@ -1194,14 +1279,12 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool }; void AnimationPlayerEditor::_animation_player_changed(Object *p_pl) { - if (player == p_pl && is_visible_in_tree()) { - _update_player(); - if (blend_editor.dialog->is_visible()) { - _animation_blend(); // Update. - } - if (library_editor->is_visible()) { - library_editor->update_tree(); - } + _update_player(); + if (blend_editor.dialog->is_visible()) { + _animation_blend(); // Update. + } + if (library_editor->is_visible()) { + library_editor->update_tree(); } } @@ -1215,6 +1298,25 @@ void AnimationPlayerEditor::_list_changed() { } } +void AnimationPlayerEditor::_current_animation_changed(const String &p_name) { + if (is_visible_in_tree()) { + if (p_name.is_empty()) { + // Means [stop]. + frame->set_value(0); + track_editor->set_anim_pos(0); + _update_animation(); + return; + } + Ref<Animation> anim = player->get_animation(p_name); + if (anim.is_null()) { + return; + } + bool animation_library_is_foreign = EditorNode::get_singleton()->is_resource_read_only(anim); + track_editor->set_animation(anim, animation_library_is_foreign); + _update_animation(); + } +} + void AnimationPlayerEditor::_animation_key_editor_anim_len_changed(float p_len) { frame->set_max(p_len); } @@ -1257,7 +1359,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { _animation_new(); } break; case TOOL_ANIM_LIBRARY: { - library_editor->set_animation_player(player); + library_editor->set_animation_mixer(fetch_mixer_for_library()); library_editor->show_dialog(); } break; case TOOL_DUPLICATE_ANIM: { @@ -1267,6 +1369,9 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { _animation_rename(); } break; case TOOL_EDIT_TRANSITIONS: { + if (is_dummy) { + break; + } _animation_blend(); } break; case TOOL_REMOVE_ANIM: { @@ -1506,7 +1611,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { } // Backup current animation state. - Ref<AnimatedValuesBackup> values_backup = player->backup_animated_values(); + Ref<AnimatedValuesBackup> backup_current = player->make_backup(); float cpos = player->get_current_animation_position(); // Render every past/future step with the capture shader. @@ -1536,7 +1641,6 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { if (valid) { player->seek(pos, true); get_tree()->flush_transform_notifications(); // Needed for transforms of Node3Ds. - values_backup->update_skeletons(); // Needed for Skeletons (2D & 3D). RS::get_singleton()->viewport_set_active(onion.captures[cidx], true); RS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]); @@ -1556,7 +1660,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { // (Seeking with update=true wouldn't do the trick because the current value of the properties // may not match their value for the current point in the animation). player->seek(cpos, false); - values_backup->restore(); + player->restore(backup_current); // Restore state of main editors. if (Node3DEditor::get_singleton()->is_visible()) { @@ -1574,14 +1678,14 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { void AnimationPlayerEditor::_start_onion_skinning() { // FIXME: Using "process_frame" makes onion layers update one frame behind the current. - if (!get_tree()->is_connected("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) { - get_tree()->connect("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred)); + if (!get_tree()->is_connected(SNAME("process_frame"), callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) { + get_tree()->connect(SNAME("process_frame"), callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred)); } } void AnimationPlayerEditor::_stop_onion_skinning() { - if (get_tree()->is_connected("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) { - get_tree()->disconnect("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred)); + if (get_tree()->is_connected(SNAME("process_frame"), callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) { + get_tree()->disconnect(SNAME("process_frame"), callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred)); _free_onion_layers(); @@ -1595,6 +1699,21 @@ void AnimationPlayerEditor::_pin_pressed() { SceneTreeDock::get_singleton()->get_tree_editor()->update_tree(); } +AnimationMixer *AnimationPlayerEditor::fetch_mixer_for_library() const { + if (!original_node) { + return nullptr; + } + // Does AnimationTree have AnimationPlayer? + if (original_node->is_class("AnimationTree")) { + AnimationTree *src_tree = Object::cast_to<AnimationTree>(original_node); + Node *src_player = src_tree->get_node_or_null(src_tree->get_animation_player()); + if (src_player) { + return Object::cast_to<AnimationMixer>(src_player); + } + } + return original_node; +} + bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) { bool is_valid = true; if (!p_anim.is_valid()) { @@ -1668,6 +1787,10 @@ AnimationPlayer *AnimationPlayerEditor::get_player() const { return player; } +AnimationMixer *AnimationPlayerEditor::get_editing_node() const { + return original_node; +} + AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plugin) { plugin = p_plugin; singleton = this; @@ -1724,7 +1847,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug delete_dialog = memnew(ConfirmationDialog); add_child(delete_dialog); - delete_dialog->connect("confirmed", callable_mp(this, &AnimationPlayerEditor::_animation_remove_confirmed)); + delete_dialog->connect(SNAME("confirmed"), callable_mp(this, &AnimationPlayerEditor::_animation_remove_confirmed)); tool_anim = memnew(MenuButton); tool_anim->set_shortcut_context(this); @@ -1769,7 +1892,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug onion_toggle->set_theme_type_variation("FlatButton"); onion_toggle->set_toggle_mode(true); onion_toggle->set_tooltip_text(TTR("Enable Onion Skinning")); - onion_toggle->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_onion_skinning_menu).bind(ONION_SKINNING_ENABLE)); + onion_toggle->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_onion_skinning_menu).bind(ONION_SKINNING_ENABLE)); hb->add_child(onion_toggle); onion_skinning = memnew(MenuButton); @@ -1808,7 +1931,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug pin->set_toggle_mode(true); pin->set_tooltip_text(TTR("Pin AnimationPlayer")); hb->add_child(pin); - pin->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_pin_pressed)); + pin->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_pin_pressed)); file = memnew(EditorFileDialog); add_child(file); @@ -1838,7 +1961,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug error_dialog->set_title(TTR("Error!")); add_child(error_dialog); - name_dialog->connect("confirmed", callable_mp(this, &AnimationPlayerEditor::_animation_name_edited)); + name_dialog->connect(SNAME("confirmed"), callable_mp(this, &AnimationPlayerEditor::_animation_name_edited)); blend_editor.dialog = memnew(AcceptDialog); add_child(blend_editor.dialog); @@ -1855,20 +1978,20 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug blend_editor.dialog->set_title(TTR("Cross-Animation Blend Times")); updating_blends = false; - blend_editor.tree->connect("item_edited", callable_mp(this, &AnimationPlayerEditor::_blend_edited)); + blend_editor.tree->connect(SNAME("item_edited"), callable_mp(this, &AnimationPlayerEditor::_blend_edited)); - autoplay->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_autoplay_pressed)); + autoplay->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_autoplay_pressed)); autoplay->set_toggle_mode(true); - play->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_play_pressed)); - play_from->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_play_from_pressed)); - play_bw->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_play_bw_pressed)); - play_bw_from->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_play_bw_from_pressed)); - stop->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_stop_pressed)); + play->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_play_pressed)); + play_from->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_play_from_pressed)); + play_bw->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_play_bw_pressed)); + play_bw_from->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_play_bw_from_pressed)); + stop->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_stop_pressed)); - animation->connect("item_selected", callable_mp(this, &AnimationPlayerEditor::_animation_selected)); + animation->connect(SNAME("item_selected"), callable_mp(this, &AnimationPlayerEditor::_animation_selected)); - frame->connect("value_changed", callable_mp(this, &AnimationPlayerEditor::_seek_value_changed).bind(true, false)); - scale->connect("text_submitted", callable_mp(this, &AnimationPlayerEditor::_scale_changed)); + frame->connect(SNAME("value_changed"), callable_mp(this, &AnimationPlayerEditor::_seek_value_changed).bind(true, false)); + scale->connect(SNAME("text_submitted"), callable_mp(this, &AnimationPlayerEditor::_scale_changed)); last_active = false; timeline_position = 0; @@ -1877,18 +2000,18 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug add_child(track_editor); track_editor->set_v_size_flags(SIZE_EXPAND_FILL); - track_editor->connect("timeline_changed", callable_mp(this, &AnimationPlayerEditor::_animation_key_editor_seek)); - track_editor->connect("animation_len_changed", callable_mp(this, &AnimationPlayerEditor::_animation_key_editor_anim_len_changed)); + track_editor->connect(SNAME("timeline_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_key_editor_seek)); + track_editor->connect(SNAME("animation_len_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_key_editor_anim_len_changed)); _update_player(); library_editor = memnew(AnimationLibraryEditor); add_child(library_editor); - library_editor->connect("update_editor", callable_mp(this, &AnimationPlayerEditor::_animation_player_changed)); + library_editor->connect(SNAME("update_editor"), callable_mp(this, &AnimationPlayerEditor::_animation_player_changed)); // Onion skinning. - track_editor->connect("visibility_changed", callable_mp(this, &AnimationPlayerEditor::_editor_visibility_changed)); + track_editor->connect(SNAME("visibility_changed"), callable_mp(this, &AnimationPlayerEditor::_editor_visibility_changed)); onion.enabled = false; onion.past = true; @@ -1943,10 +2066,10 @@ AnimationPlayerEditor::~AnimationPlayerEditor() { void AnimationPlayerEditorPlugin::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - Node3DEditor::get_singleton()->connect("transform_key_request", callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request)); - InspectorDock::get_inspector_singleton()->connect("property_keyed", callable_mp(this, &AnimationPlayerEditorPlugin::_property_keyed)); - anim_editor->get_track_editor()->connect("keying_changed", callable_mp(this, &AnimationPlayerEditorPlugin::_update_keying)); - InspectorDock::get_inspector_singleton()->connect("edited_object_changed", callable_mp(anim_editor->get_track_editor(), &AnimationTrackEditor::update_keying)); + Node3DEditor::get_singleton()->connect(SNAME("transform_key_request"), callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request)); + InspectorDock::get_inspector_singleton()->connect(SNAME("property_keyed"), callable_mp(this, &AnimationPlayerEditorPlugin::_property_keyed)); + anim_editor->get_track_editor()->connect(SNAME("keying_changed"), callable_mp(this, &AnimationPlayerEditorPlugin::_update_keying)); + InspectorDock::get_inspector_singleton()->connect(SNAME("edited_object_changed"), callable_mp(anim_editor->get_track_editor(), &AnimationTrackEditor::update_keying)); set_force_draw_over_forwarding_enabled(); } break; } @@ -1979,14 +2102,88 @@ void AnimationPlayerEditorPlugin::_update_keying() { } void AnimationPlayerEditorPlugin::edit(Object *p_object) { + if (player && anim_editor && anim_editor->is_pinned()) { + return; // Ignore, pinned. + } + + player = nullptr; if (!p_object) { return; } - anim_editor->edit(Object::cast_to<AnimationPlayer>(p_object)); + last_mixer = p_object->get_instance_id(); + + AnimationMixer *src_node = Object::cast_to<AnimationMixer>(p_object); + bool is_dummy = false; + if (!p_object->is_class("AnimationPlayer")) { + // If it needs dummy AnimationPlayer, assign original AnimationMixer to LibraryEditor. + _update_dummy_player(src_node); + + is_dummy = true; + + if (!src_node->is_connected(SNAME("mixer_updated"), callable_mp(this, &AnimationPlayerEditorPlugin::_update_dummy_player))) { + src_node->connect(SNAME("mixer_updated"), callable_mp(this, &AnimationPlayerEditorPlugin::_update_dummy_player).bind(src_node), CONNECT_DEFERRED); + } + if (!src_node->is_connected(SNAME("animation_libraries_updated"), callable_mp(this, &AnimationPlayerEditorPlugin::_update_dummy_player))) { + src_node->connect(SNAME("animation_libraries_updated"), callable_mp(this, &AnimationPlayerEditorPlugin::_update_dummy_player).bind(src_node), CONNECT_DEFERRED); + } + } else { + _clear_dummy_player(); + player = Object::cast_to<AnimationPlayer>(p_object); + } + player->set_dummy(is_dummy); + + anim_editor->edit(src_node, player, is_dummy); +} + +void AnimationPlayerEditorPlugin::_clear_dummy_player() { + if (!dummy_player) { + return; + } + Node *parent = dummy_player->get_parent(); + if (parent) { + parent->call_deferred("remove_child", dummy_player); + } + dummy_player->queue_free(); + dummy_player = nullptr; +} + +void AnimationPlayerEditorPlugin::_update_dummy_player(AnimationMixer *p_mixer) { + // Check current editing object. + if (p_mixer->get_instance_id() != last_mixer && p_mixer->is_connected(SNAME("mixer_updated"), callable_mp(this, &AnimationPlayerEditorPlugin::_update_dummy_player))) { + p_mixer->disconnect(SNAME("mixer_updated"), callable_mp(this, &AnimationPlayerEditorPlugin::_update_dummy_player)); + return; + } + + // Add dummy player to scene. + if (!dummy_player) { + Node *parent = p_mixer->get_parent(); + ERR_FAIL_NULL(parent); + dummy_player = memnew(AnimationPlayer); + parent->add_child(dummy_player); + } + player = dummy_player; + + // Convert AnimationTree (AnimationMixer) to AnimationPlayer. + AnimationMixer *default_node = memnew(AnimationMixer); + List<PropertyInfo> pinfo; + default_node->get_property_list(&pinfo); + for (const PropertyInfo &E : pinfo) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + if (E.name != "script" && E.name != "active" && E.name != "deterministic" && E.name != "root_motion_track") { + dummy_player->set(E.name, p_mixer->get(E.name)); + } + } + memdelete(default_node); + + if (anim_editor) { + anim_editor->_update_player(); + } } bool AnimationPlayerEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationPlayer"); + return p_object->is_class("AnimationPlayer") || p_object->is_class("AnimationTree") || p_object->is_class("AnimationMixer"); } void AnimationPlayerEditorPlugin::make_visible(bool p_visible) { @@ -2003,6 +2200,12 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin() { } AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() { + if (dummy_player) { + memdelete(dummy_player); + } + if (player) { + memdelete(player); + } } // AnimationTrackKeyEditEditorPlugin |