2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* animation_player_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "animation_player_editor_plugin.h"
2017-01-16 07:04:19 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/project_settings.h"
2020-04-28 13:19:37 +00:00
# include "core/input/input.h"
2018-09-11 16:13:45 +00:00
# include "core/io/resource_loader.h"
# include "core/io/resource_saver.h"
# include "core/os/keyboard.h"
2022-02-12 01:46:22 +00:00
# include "editor/editor_node.h"
2017-03-05 13:21:25 +00:00
# include "editor/editor_settings.h"
2022-08-29 10:10:32 +00:00
# include "editor/editor_undo_redo_manager.h"
2023-04-07 16:59:49 +00:00
# include "editor/gui/editor_file_dialog.h"
2022-11-19 11:45:49 +00:00
# include "editor/inspector_dock.h"
2020-02-23 21:52:27 +00:00
# include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning.
2020-03-27 07:44:44 +00:00
# include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning.
2022-02-12 01:46:22 +00:00
# include "editor/scene_tree_dock.h"
2024-01-15 12:14:55 +00:00
# include "editor/themes/editor_scale.h"
2022-11-22 23:14:08 +00:00
# include "editor/themes/editor_theme_manager.h"
2023-07-20 15:34:06 +00:00
# include "scene/animation/animation_tree.h"
2022-11-19 11:45:49 +00:00
# include "scene/gui/separator.h"
2020-03-04 01:51:12 +00:00
# include "scene/main/window.h"
2021-10-15 13:25:00 +00:00
# include "scene/resources/animation.h"
2023-07-11 20:29:09 +00:00
# include "scene/resources/image_texture.h"
2021-12-06 00:46:03 +00:00
# include "scene/scene_string_names.h"
2020-03-27 18:21:27 +00:00
# include "servers/rendering_server.h"
2017-10-30 18:43:19 +00:00
2022-04-07 11:49:28 +00:00
///////////////////////////////////
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _node_removed ( Node * p_node ) {
2023-07-20 15:34:06 +00:00
if ( player & & original_node = = p_node ) {
if ( is_dummy ) {
plugin - > _clear_dummy_player ( ) ;
}
2020-04-01 23:20:12 +00:00
player = nullptr ;
2016-01-17 23:03:57 +00:00
2014-02-10 01:10:30 +00:00
set_process ( false ) ;
2022-05-04 00:17:08 +00:00
track_editor - > set_animation ( Ref < Animation > ( ) , true ) ;
2020-04-01 23:20:12 +00:00
track_editor - > set_root ( nullptr ) ;
2018-06-07 15:46:14 +00:00
track_editor - > show_select_node_warning ( true ) ;
2016-01-17 23:03:57 +00:00
_update_player ( ) ;
2023-07-20 15:34:06 +00:00
_ensure_dummy_player ( ) ;
2014-02-10 01:10:30 +00:00
}
}
void AnimationPlayerEditor : : _notification ( int p_what ) {
2017-09-09 02:19:43 +00:00
switch ( p_what ) {
case NOTIFICATION_PROCESS : {
2023-07-20 15:34:06 +00:00
if ( ! player | | is_dummy ) {
track_editor - > show_inactive_player_warning ( false ) ;
} else {
track_editor - > show_inactive_player_warning ( ! player - > is_active ( ) ) ;
}
2020-05-14 14:41:43 +00:00
if ( ! player ) {
2017-09-09 02:19:43 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2017-09-09 02:19:43 +00:00
updating = true ;
2014-02-10 01:10:30 +00:00
2017-09-09 02:19:43 +00:00
if ( player - > is_playing ( ) ) {
{
2018-01-14 10:28:57 +00:00
String animname = player - > get_assigned_animation ( ) ;
2014-02-10 01:10:30 +00:00
2017-09-09 02:19:43 +00:00
if ( player - > has_animation ( animname ) ) {
Ref < Animation > anim = player - > get_animation ( animname ) ;
if ( ! anim . is_null ( ) ) {
2021-10-15 13:25:00 +00:00
frame - > set_max ( ( double ) anim - > get_length ( ) ) ;
2017-09-09 02:19:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
}
2017-09-10 13:37:49 +00:00
frame - > set_value ( player - > get_current_animation_position ( ) ) ;
2018-06-07 15:46:14 +00:00
track_editor - > set_anim_pos ( player - > get_current_animation_position ( ) ) ;
2019-11-30 16:47:09 +00:00
} else if ( ! player - > is_valid ( ) ) {
// Reset timeline when the player has been stopped externally
frame - > set_value ( 0 ) ;
2017-09-09 02:19:43 +00:00
} else if ( last_active ) {
2019-06-19 03:51:06 +00:00
// Need the last frame after it stopped.
2017-09-10 13:37:49 +00:00
frame - > set_value ( player - > get_current_animation_position ( ) ) ;
2022-12-08 12:38:01 +00:00
track_editor - > set_anim_pos ( player - > get_current_animation_position ( ) ) ;
2019-11-19 13:39:10 +00:00
stop - > set_icon ( stop_icon ) ;
2014-02-10 01:10:30 +00:00
}
2015-01-03 18:39:01 +00:00
2017-09-09 02:19:43 +00:00
last_active = player - > is_playing ( ) ;
2023-09-27 14:37:26 +00:00
2017-09-09 02:19:43 +00:00
updating = false ;
} break ;
2022-02-16 02:44:22 +00:00
2017-09-09 02:19:43 +00:00
case NOTIFICATION_ENTER_TREE : {
2023-07-20 15:34:06 +00:00
tool_anim - > get_popup ( ) - > connect ( SNAME ( " id_pressed " ) , callable_mp ( this , & AnimationPlayerEditor : : _animation_tool_menu ) ) ;
2017-09-09 02:19:43 +00:00
2023-07-20 15:34:06 +00:00
onion_skinning - > get_popup ( ) - > connect ( SNAME ( " id_pressed " ) , callable_mp ( this , & AnimationPlayerEditor : : _onion_skinning_menu ) ) ;
2017-10-30 18:43:19 +00:00
2023-07-20 15:34:06 +00:00
blend_editor . next - > connect ( SNAME ( " item_selected " ) , callable_mp ( this , & AnimationPlayerEditor : : _blend_editor_next_changed ) ) ;
2014-02-10 01:10:30 +00:00
2023-07-20 15:34:06 +00:00
get_tree ( ) - > connect ( SNAME ( " node_removed " ) , callable_mp ( this , & AnimationPlayerEditor : : _node_removed ) ) ;
2017-09-09 02:19:43 +00:00
2023-09-13 11:14:07 +00:00
add_theme_style_override ( " panel " , EditorNode : : get_singleton ( ) - > get_editor_theme ( ) - > get_stylebox ( SNAME ( " panel " ) , SNAME ( " Panel " ) ) ) ;
2017-09-09 02:19:43 +00:00
} break ;
2022-02-16 02:44:22 +00:00
2017-09-09 02:19:43 +00:00
case EditorSettings : : NOTIFICATION_EDITOR_SETTINGS_CHANGED : {
2022-11-22 23:14:08 +00:00
if ( EditorThemeManager : : is_generated_theme_outdated ( ) ) {
add_theme_style_override ( " panel " , EditorNode : : get_singleton ( ) - > get_editor_theme ( ) - > get_stylebox ( SNAME ( " panel " ) , SNAME ( " Panel " ) ) ) ;
}
2017-09-09 02:19:43 +00:00
} break ;
2022-02-16 02:44:22 +00:00
2020-09-03 11:22:16 +00:00
case NOTIFICATION_TRANSLATION_CHANGED :
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
2017-09-09 02:19:43 +00:00
case NOTIFICATION_THEME_CHANGED : {
2023-08-13 00:33:39 +00:00
stop_icon = get_editor_theme_icon ( SNAME ( " Stop " ) ) ;
pause_icon = get_editor_theme_icon ( SNAME ( " Pause " ) ) ;
2019-11-19 13:39:10 +00:00
if ( player & & player - > is_playing ( ) ) {
stop - > set_icon ( pause_icon ) ;
} else {
stop - > set_icon ( stop_icon ) ;
}
2017-09-09 02:19:43 +00:00
2023-08-13 00:33:39 +00:00
autoplay - > set_icon ( get_editor_theme_icon ( SNAME ( " AutoPlay " ) ) ) ;
play - > set_icon ( get_editor_theme_icon ( SNAME ( " PlayStart " ) ) ) ;
play_from - > set_icon ( get_editor_theme_icon ( SNAME ( " Play " ) ) ) ;
play_bw - > set_icon ( get_editor_theme_icon ( SNAME ( " PlayStartBackwards " ) ) ) ;
play_bw_from - > set_icon ( get_editor_theme_icon ( SNAME ( " PlayBackwards " ) ) ) ;
2017-09-09 02:19:43 +00:00
2023-08-13 00:33:39 +00:00
autoplay_icon = get_editor_theme_icon ( SNAME ( " AutoPlay " ) ) ;
reset_icon = get_editor_theme_icon ( SNAME ( " Reload " ) ) ;
2020-12-20 10:46:44 +00:00
{
2021-03-28 11:32:17 +00:00
Ref < Image > autoplay_img = autoplay_icon - > get_image ( ) ;
Ref < Image > reset_img = reset_icon - > get_image ( ) ;
2021-09-23 14:58:43 +00:00
Size2 icon_size = autoplay_img - > get_size ( ) ;
2022-07-22 18:06:19 +00:00
Ref < Image > autoplay_reset_img = Image : : create_empty ( icon_size . x * 2 , icon_size . y , false , autoplay_img - > get_format ( ) ) ;
2022-07-09 20:43:34 +00:00
autoplay_reset_img - > blit_rect ( autoplay_img , Rect2i ( Point2i ( ) , icon_size ) , Point2i ( ) ) ;
autoplay_reset_img - > blit_rect ( reset_img , Rect2i ( Point2i ( ) , icon_size ) , Point2i ( icon_size . x , 0 ) ) ;
2022-07-22 18:06:19 +00:00
autoplay_reset_icon = ImageTexture : : create_from_image ( autoplay_reset_img ) ;
2020-12-20 10:46:44 +00:00
}
2018-06-07 15:46:14 +00:00
2023-08-13 00:33:39 +00:00
onion_toggle - > set_icon ( get_editor_theme_icon ( SNAME ( " Onion " ) ) ) ;
onion_skinning - > set_icon ( get_editor_theme_icon ( SNAME ( " GuiTabMenuHl " ) ) ) ;
2019-05-14 03:42:05 +00:00
2023-08-13 00:33:39 +00:00
pin - > set_icon ( get_editor_theme_icon ( SNAME ( " Pin " ) ) ) ;
2017-09-09 02:19:43 +00:00
2022-02-08 09:14:58 +00:00
tool_anim - > add_theme_style_override ( " normal " , get_theme_stylebox ( SNAME ( " normal " ) , SNAME ( " Button " ) ) ) ;
track_editor - > get_edit_menu ( ) - > add_theme_style_override ( " normal " , get_theme_stylebox ( SNAME ( " normal " ) , SNAME ( " Button " ) ) ) ;
2018-06-07 15:46:14 +00:00
2023-08-13 00:33:39 +00:00
# define ITEM_ICON(m_item, m_icon) tool_anim->get_popup()->set_item_icon(tool_anim->get_popup()->get_item_index(m_item), get_editor_theme_icon(SNAME(m_icon)))
2018-06-07 15:46:14 +00:00
ITEM_ICON ( TOOL_NEW_ANIM , " New " ) ;
2022-04-07 11:49:28 +00:00
ITEM_ICON ( TOOL_ANIM_LIBRARY , " AnimationLibrary " ) ;
2018-06-07 15:46:14 +00:00
ITEM_ICON ( TOOL_DUPLICATE_ANIM , " Duplicate " ) ;
ITEM_ICON ( TOOL_RENAME_ANIM , " Rename " ) ;
ITEM_ICON ( TOOL_EDIT_TRANSITIONS , " Blend " ) ;
2018-07-01 20:44:15 +00:00
ITEM_ICON ( TOOL_EDIT_RESOURCE , " Edit " ) ;
2018-06-07 15:46:14 +00:00
ITEM_ICON ( TOOL_REMOVE_ANIM , " Remove " ) ;
2022-03-10 05:46:05 +00:00
_update_animation_list_icons ( ) ;
2017-09-09 02:19:43 +00:00
} break ;
2023-07-20 15:34:06 +00:00
case NOTIFICATION_VISIBILITY_CHANGED : {
_ensure_dummy_player ( ) ;
} break ;
2014-02-10 01:10:30 +00:00
}
}
void AnimationPlayerEditor : : _autoplay_pressed ( ) {
2020-05-14 14:41:43 +00:00
if ( updating ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2022-04-07 11:49:28 +00:00
if ( animation - > has_selectable_items ( ) = = 0 ) {
2014-02-10 01:10:30 +00:00
return ;
}
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2014-02-10 01:10:30 +00:00
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
if ( player - > get_autoplay ( ) = = current ) {
//unset
2016-05-04 01:25:37 +00:00
undo_redo - > create_action ( TTR ( " Toggle Autoplay " ) ) ;
2014-02-10 01:10:30 +00:00
undo_redo - > add_do_method ( player , " set_autoplay " , " " ) ;
undo_redo - > add_undo_method ( player , " set_autoplay " , player - > get_autoplay ( ) ) ;
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > commit_action ( ) ;
} else {
//set
2016-05-04 01:25:37 +00:00
undo_redo - > create_action ( TTR ( " Toggle Autoplay " ) ) ;
2014-02-10 01:10:30 +00:00
undo_redo - > add_do_method ( player , " set_autoplay " , current ) ;
undo_redo - > add_undo_method ( player , " set_autoplay " , player - > get_autoplay ( ) ) ;
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > commit_action ( ) ;
}
}
void AnimationPlayerEditor : : _play_pressed ( ) {
2022-04-07 11:49:28 +00:00
String current = _get_current ( ) ;
2014-02-10 01:10:30 +00:00
2021-12-09 09:42:46 +00:00
if ( ! current . is_empty ( ) ) {
2020-05-14 14:41:43 +00:00
if ( current = = player - > get_assigned_animation ( ) ) {
2018-01-18 20:37:17 +00:00
player - > stop ( ) ; //so it won't blend with itself
2020-05-14 14:41:43 +00:00
}
2022-11-17 06:24:00 +00:00
ERR_FAIL_COND_EDMSG ( ! _validate_tracks ( player - > get_animation ( current ) ) , " Animation tracks may have any invalid key, abort playing. " ) ;
2014-02-10 01:10:30 +00:00
player - > play ( current ) ;
}
//unstop
2019-11-19 13:39:10 +00:00
stop - > set_icon ( pause_icon ) ;
2014-02-10 01:10:30 +00:00
}
2015-05-25 04:46:45 +00:00
void AnimationPlayerEditor : : _play_from_pressed ( ) {
2022-04-07 11:49:28 +00:00
String current = _get_current ( ) ;
2015-05-25 04:46:45 +00:00
2021-12-09 09:42:46 +00:00
if ( ! current . is_empty ( ) ) {
2017-09-10 13:37:49 +00:00
float time = player - > get_current_animation_position ( ) ;
2018-01-14 10:28:57 +00:00
if ( current = = player - > get_assigned_animation ( ) & & player - > is_playing ( ) ) {
2018-01-18 20:37:17 +00:00
player - > stop ( ) ; //so it won't blend with itself
2015-05-25 04:46:45 +00:00
}
2022-11-17 06:24:00 +00:00
ERR_FAIL_COND_EDMSG ( ! _validate_tracks ( player - > get_animation ( current ) ) , " Animation tracks may have any invalid key, abort playing. " ) ;
2015-05-25 04:46:45 +00:00
player - > seek ( time ) ;
2022-12-05 00:30:47 +00:00
player - > play ( current ) ;
2015-05-25 04:46:45 +00:00
}
//unstop
2019-11-19 13:39:10 +00:00
stop - > set_icon ( pause_icon ) ;
2015-05-25 04:46:45 +00:00
}
2022-04-07 11:49:28 +00:00
String AnimationPlayerEditor : : _get_current ( ) const {
2015-05-25 04:46:45 +00:00
String current ;
2022-04-07 11:49:28 +00:00
if ( animation - > get_selected ( ) > = 0 & & animation - > get_selected ( ) < animation - > get_item_count ( ) & & ! animation - > is_item_separator ( animation - > get_selected ( ) ) ) {
2015-05-25 04:46:45 +00:00
current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
}
2022-04-07 11:49:28 +00:00
return current ;
}
void AnimationPlayerEditor : : _play_bw_pressed ( ) {
String current = _get_current ( ) ;
2021-12-09 09:42:46 +00:00
if ( ! current . is_empty ( ) ) {
2020-05-14 14:41:43 +00:00
if ( current = = player - > get_assigned_animation ( ) ) {
2018-01-18 20:37:17 +00:00
player - > stop ( ) ; //so it won't blend with itself
2020-05-14 14:41:43 +00:00
}
2022-11-17 06:24:00 +00:00
ERR_FAIL_COND_EDMSG ( ! _validate_tracks ( player - > get_animation ( current ) ) , " Animation tracks may have any invalid key, abort playing. " ) ;
2023-01-21 05:51:03 +00:00
player - > play_backwards ( current ) ;
2015-05-25 04:46:45 +00:00
}
//unstop
2019-11-19 13:39:10 +00:00
stop - > set_icon ( pause_icon ) ;
2015-05-25 04:46:45 +00:00
}
void AnimationPlayerEditor : : _play_bw_from_pressed ( ) {
2022-04-07 11:49:28 +00:00
String current = _get_current ( ) ;
2015-05-25 04:46:45 +00:00
2021-12-09 09:42:46 +00:00
if ( ! current . is_empty ( ) ) {
2017-09-10 13:37:49 +00:00
float time = player - > get_current_animation_position ( ) ;
2020-05-14 14:41:43 +00:00
if ( current = = player - > get_assigned_animation ( ) ) {
2018-01-18 20:37:17 +00:00
player - > stop ( ) ; //so it won't blend with itself
2020-05-14 14:41:43 +00:00
}
2022-11-17 06:24:00 +00:00
ERR_FAIL_COND_EDMSG ( ! _validate_tracks ( player - > get_animation ( current ) ) , " Animation tracks may have any invalid key, abort playing. " ) ;
2015-05-25 04:46:45 +00:00
player - > seek ( time ) ;
2023-01-21 05:51:03 +00:00
player - > play_backwards ( current ) ;
2015-05-25 04:46:45 +00:00
}
//unstop
2019-11-19 13:39:10 +00:00
stop - > set_icon ( pause_icon ) ;
2015-05-25 04:46:45 +00:00
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _stop_pressed ( ) {
2017-07-01 15:49:43 +00:00
if ( ! player ) {
return ;
}
2019-11-19 13:39:10 +00:00
if ( player - > is_playing ( ) ) {
player - > pause ( ) ;
} else {
String current = _get_current ( ) ;
player - > stop ( ) ;
player - > set_assigned_animation ( current ) ;
frame - > set_value ( 0 ) ;
track_editor - > set_anim_pos ( 0 ) ;
}
stop - > set_icon ( stop_icon ) ;
2014-02-10 01:10:30 +00:00
}
2019-05-30 19:43:46 +00:00
void AnimationPlayerEditor : : _animation_selected ( int p_which ) {
2020-05-14 14:41:43 +00:00
if ( updating ) {
2019-05-30 19:43:46 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
// when selecting an animation, the idea is that the only interesting behavior
// ui-wise is that it should play/blend the next one if currently playing
2022-04-07 11:49:28 +00:00
String current = _get_current ( ) ;
2014-02-10 01:10:30 +00:00
2021-12-09 09:42:46 +00:00
if ( ! current . is_empty ( ) ) {
2019-05-06 19:39:24 +00:00
player - > set_assigned_animation ( current ) ;
2016-01-17 23:03:57 +00:00
2019-05-30 19:43:46 +00:00
Ref < Animation > anim = player - > get_animation ( current ) ;
{
2022-09-07 09:17:22 +00:00
bool animation_library_is_foreign = EditorNode : : get_singleton ( ) - > is_resource_read_only ( anim ) ;
2022-05-04 00:17:08 +00:00
track_editor - > set_animation ( anim , animation_library_is_foreign ) ;
2023-07-20 15:34:06 +00:00
Node * root = player - > get_node_or_null ( player - > get_root_node ( ) ) ;
2014-02-10 01:10:30 +00:00
if ( root ) {
2018-06-07 15:46:14 +00:00
track_editor - > set_root ( root ) ;
2014-02-10 01:10:30 +00:00
}
}
2021-10-15 13:25:00 +00:00
frame - > set_max ( ( double ) anim - > get_length ( ) ) ;
2023-07-20 15:34:06 +00:00
autoplay - > set_pressed ( current = = player - > get_autoplay ( ) ) ;
2014-02-10 01:10:30 +00:00
} else {
2022-05-04 00:17:08 +00:00
track_editor - > set_animation ( Ref < Animation > ( ) , true ) ;
2020-04-01 23:20:12 +00:00
track_editor - > set_root ( nullptr ) ;
2023-07-20 15:34:06 +00:00
autoplay - > set_pressed ( false ) ;
2014-02-10 01:10:30 +00:00
}
2021-02-04 08:20:26 +00:00
AnimationPlayerEditor : : get_singleton ( ) - > get_track_editor ( ) - > update_keying ( ) ;
2023-11-21 21:14:32 +00:00
_animation_key_editor_seek ( timeline_position ) ;
2022-10-10 07:45:52 +00:00
emit_signal ( " animation_selected " , current ) ;
2014-02-10 01:10:30 +00:00
}
void AnimationPlayerEditor : : _animation_new ( ) {
int count = 1 ;
2021-05-09 07:06:37 +00:00
String base = " new_animation " ;
2022-05-14 16:54:14 +00:00
String current_library_name = " " ;
if ( animation - > has_selectable_items ( ) ) {
String current_animation_name = animation - > get_item_text ( animation - > get_selected ( ) ) ;
Ref < Animation > current_animation = player - > get_animation ( current_animation_name ) ;
if ( current_animation . is_valid ( ) ) {
current_library_name = player - > find_animation_library ( current_animation ) ;
}
}
String attempt_prefix = ( current_library_name = = " " ) ? " " : current_library_name + " / " ;
2014-02-10 01:10:30 +00:00
while ( true ) {
String attempt = base ;
2020-05-14 14:41:43 +00:00
if ( count > 1 ) {
2021-05-09 07:06:37 +00:00
attempt + = vformat ( " _%d " , count ) ;
2020-05-14 14:41:43 +00:00
}
2022-05-14 16:54:14 +00:00
if ( player - > has_animation ( attempt_prefix + attempt ) ) {
2014-02-10 01:10:30 +00:00
count + + ;
continue ;
}
base = attempt ;
break ;
}
2022-05-14 16:54:14 +00:00
_update_name_dialog_library_dropdown ( ) ;
2022-04-07 11:49:28 +00:00
2022-05-14 16:54:14 +00:00
name_dialog_op = TOOL_NEW_ANIM ;
name_dialog - > set_title ( TTR ( " Create New Animation " ) ) ;
2014-02-10 01:10:30 +00:00
name_dialog - > popup_centered ( Size2 ( 300 , 90 ) ) ;
2022-05-14 16:54:14 +00:00
name_title - > set_text ( TTR ( " New Animation Name: " ) ) ;
name - > set_text ( base ) ;
2014-02-10 01:10:30 +00:00
name - > select_all ( ) ;
name - > grab_focus ( ) ;
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _animation_rename ( ) {
2022-04-07 11:49:28 +00:00
if ( ! animation - > has_selectable_items ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
int selected = animation - > get_selected ( ) ;
String selected_name = animation - > get_item_text ( selected ) ;
2022-05-14 16:54:14 +00:00
// Remove library prefix if present.
if ( selected_name . contains ( " / " ) ) {
selected_name = selected_name . get_slice ( " / " , 1 ) ;
}
name_dialog - > set_title ( TTR ( " Rename Animation " ) ) ;
2016-05-04 01:25:37 +00:00
name_title - > set_text ( TTR ( " Change Animation Name: " ) ) ;
2014-02-10 01:10:30 +00:00
name - > set_text ( selected_name ) ;
2022-02-14 06:28:42 +00:00
name_dialog_op = TOOL_RENAME_ANIM ;
2014-02-10 01:10:30 +00:00
name_dialog - > popup_centered ( Size2 ( 300 , 90 ) ) ;
name - > select_all ( ) ;
name - > grab_focus ( ) ;
2022-04-07 11:49:28 +00:00
library - > hide ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-01-25 20:22:16 +00:00
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _animation_remove ( ) {
2022-04-07 11:49:28 +00:00
if ( ! animation - > has_selectable_items ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-04-08 22:42:19 +00:00
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
2020-07-24 13:14:56 +00:00
delete_dialog - > set_text ( vformat ( TTR ( " Delete Animation '%s'? " ) , current ) ) ;
2020-03-06 17:00:16 +00:00
delete_dialog - > popup_centered ( ) ;
2017-01-25 20:22:16 +00:00
}
void AnimationPlayerEditor : : _animation_remove_confirmed ( ) {
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
Ref < Animation > anim = player - > get_animation ( current ) ;
2014-02-10 01:10:30 +00:00
2022-04-07 11:49:28 +00:00
Ref < AnimationLibrary > al = player - > get_animation_library ( player - > find_animation_library ( anim ) ) ;
ERR_FAIL_COND ( al . is_null ( ) ) ;
2022-05-14 16:54:14 +00:00
// For names of form lib_name/anim_name, remove library name prefix.
if ( current . contains ( " / " ) ) {
current = current . get_slice ( " / " , 1 ) ;
}
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2016-05-04 01:25:37 +00:00
undo_redo - > create_action ( TTR ( " Remove Animation " ) ) ;
2018-04-26 01:09:41 +00:00
if ( player - > get_autoplay ( ) = = current ) {
undo_redo - > add_do_method ( player , " set_autoplay " , " " ) ;
undo_redo - > add_undo_method ( player , " set_autoplay " , current ) ;
2019-06-19 03:51:06 +00:00
// Avoid having the autoplay icon linger around if there is only one animation in the player.
2018-04-26 01:09:41 +00:00
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
}
2022-04-07 11:49:28 +00:00
undo_redo - > add_do_method ( al . ptr ( ) , " remove_animation " , current ) ;
undo_redo - > add_undo_method ( al . ptr ( ) , " add_animation " , current , anim ) ;
2017-01-25 20:22:16 +00:00
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
2022-04-07 11:49:28 +00:00
if ( animation - > has_selectable_items ( ) & & animation - > get_selectable_item ( false ) = = animation - > get_selectable_item ( true ) ) { // Last item remaining.
2019-06-19 03:51:06 +00:00
undo_redo - > add_do_method ( this , " _stop_onion_skinning " ) ;
undo_redo - > add_undo_method ( this , " _start_onion_skinning " ) ;
}
2014-02-10 01:10:30 +00:00
undo_redo - > commit_action ( ) ;
}
void AnimationPlayerEditor : : _select_anim_by_name ( const String & p_anim ) {
int idx = - 1 ;
for ( int i = 0 ; i < animation - > get_item_count ( ) ; i + + ) {
if ( animation - > get_item_text ( i ) = = p_anim ) {
idx = i ;
break ;
}
}
ERR_FAIL_COND ( idx = = - 1 ) ;
animation - > select ( idx ) ;
_animation_selected ( idx ) ;
}
2022-12-08 12:38:01 +00:00
float AnimationPlayerEditor : : _get_editor_step ( ) const {
2019-09-02 13:49:55 +00:00
// Returns the effective snapping value depending on snapping modifiers, or 0 if snapping is disabled.
if ( track_editor - > is_snap_enabled ( ) ) {
const String current = player - > get_assigned_animation ( ) ;
const Ref < Animation > anim = player - > get_animation ( current ) ;
2019-10-03 18:42:22 +00:00
ERR_FAIL_COND_V ( ! anim . is_valid ( ) , 0.0 ) ;
2019-09-02 13:49:55 +00:00
// Use more precise snapping when holding Shift
2021-08-13 21:31:57 +00:00
return Input : : get_singleton ( ) - > is_key_pressed ( Key : : SHIFT ) ? anim - > get_step ( ) * 0.25 : anim - > get_step ( ) ;
2019-09-02 13:49:55 +00:00
}
2022-12-08 12:38:01 +00:00
return 0.0f ;
2019-09-02 13:49:55 +00:00
}
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _animation_name_edited ( ) {
2023-01-21 05:51:03 +00:00
if ( player - > is_playing ( ) ) {
player - > stop ( ) ;
}
2014-02-10 01:10:30 +00:00
String new_name = name - > get_text ( ) ;
2022-05-05 03:19:54 +00:00
if ( ! AnimationLibrary : : is_valid_animation_name ( new_name ) ) {
2018-04-27 01:58:30 +00:00
error_dialog - > set_text ( TTR ( " Invalid animation name! " ) ) ;
2020-03-06 17:00:16 +00:00
error_dialog - > popup_centered ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2022-04-07 11:49:28 +00:00
if ( name_dialog_op = = TOOL_RENAME_ANIM & & animation - > has_selectable_items ( ) & & animation - > get_item_text ( animation - > get_selected ( ) ) = = new_name ) {
2014-02-10 01:10:30 +00:00
name_dialog - > hide ( ) ;
return ;
}
2022-05-14 16:54:14 +00:00
String test_name_prefix = " " ;
if ( library - > is_visible ( ) & & library - > get_selected_id ( ) ! = - 1 ) {
test_name_prefix = library - > get_item_metadata ( library - > get_selected_id ( ) ) ;
test_name_prefix + = ( test_name_prefix ! = " " ) ? " / " : " " ;
}
if ( player - > has_animation ( test_name_prefix + new_name ) ) {
2022-05-24 12:22:16 +00:00
error_dialog - > set_text ( vformat ( TTR ( " Animation '%s' already exists! " ) , test_name_prefix + new_name ) ) ;
2020-03-06 17:00:16 +00:00
error_dialog - > popup_centered ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2022-02-14 06:28:42 +00:00
switch ( name_dialog_op ) {
case TOOL_RENAME_ANIM : {
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
Ref < Animation > anim = player - > get_animation ( current ) ;
2022-04-07 11:49:28 +00:00
Ref < AnimationLibrary > al = player - > get_animation_library ( player - > find_animation_library ( anim ) ) ;
ERR_FAIL_COND ( al . is_null ( ) ) ;
2022-05-14 16:54:14 +00:00
// Extract library prefix if present.
String new_library_prefix = " " ;
if ( current . contains ( " / " ) ) {
new_library_prefix = current . get_slice ( " / " , 0 ) + " / " ;
current = current . get_slice ( " / " , 1 ) ;
}
2022-02-14 06:28:42 +00:00
undo_redo - > create_action ( TTR ( " Rename Animation " ) ) ;
2022-04-07 11:49:28 +00:00
undo_redo - > add_do_method ( al . ptr ( ) , " rename_animation " , current , new_name ) ;
2022-02-14 06:28:42 +00:00
undo_redo - > add_do_method ( anim . ptr ( ) , " set_name " , new_name ) ;
2022-04-07 11:49:28 +00:00
undo_redo - > add_undo_method ( al . ptr ( ) , " rename_animation " , new_name , current ) ;
2022-02-14 06:28:42 +00:00
undo_redo - > add_undo_method ( anim . ptr ( ) , " set_name " , current ) ;
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > commit_action ( ) ;
2023-07-20 15:34:06 +00:00
if ( is_dummy ) {
plugin - > _update_dummy_player ( original_node ) ;
}
2022-05-14 16:54:14 +00:00
_select_anim_by_name ( new_library_prefix + new_name ) ;
2022-02-14 06:28:42 +00:00
} break ;
2014-02-10 01:10:30 +00:00
2022-02-14 06:28:42 +00:00
case TOOL_NEW_ANIM : {
Ref < Animation > new_anim = Ref < Animation > ( memnew ( Animation ) ) ;
new_anim - > set_name ( new_name ) ;
2022-05-14 16:54:14 +00:00
String library_name ;
2022-04-07 11:49:28 +00:00
Ref < AnimationLibrary > al ;
if ( library - > is_visible ( ) ) {
2022-05-14 16:54:14 +00:00
library_name = library - > get_item_metadata ( library - > get_selected ( ) ) ;
// It's possible that [Global] was selected, but doesn't exist yet.
if ( player - > has_animation_library ( library_name ) ) {
al = player - > get_animation_library ( library_name ) ;
}
2022-04-07 11:49:28 +00:00
} else {
if ( player - > has_animation_library ( " " ) ) {
al = player - > get_animation_library ( " " ) ;
2022-05-14 16:54:14 +00:00
library_name = " " ;
2022-04-07 11:49:28 +00:00
}
}
2022-02-14 06:28:42 +00:00
undo_redo - > create_action ( TTR ( " Add Animation " ) ) ;
2022-04-07 11:49:28 +00:00
bool lib_added = false ;
if ( al . is_null ( ) ) {
al . instantiate ( ) ;
lib_added = true ;
2023-07-20 15:34:06 +00:00
undo_redo - > add_do_method ( fetch_mixer_for_library ( ) , " add_animation_library " , " " , al ) ;
2022-05-14 16:54:14 +00:00
library_name = " " ;
2022-04-07 11:49:28 +00:00
}
undo_redo - > add_do_method ( al . ptr ( ) , " add_animation " , new_name , new_anim ) ;
undo_redo - > add_undo_method ( al . ptr ( ) , " remove_animation " , new_name ) ;
2022-02-14 06:28:42 +00:00
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
2022-04-07 11:49:28 +00:00
if ( ! animation - > has_selectable_items ( ) ) {
2022-02-14 06:28:42 +00:00
undo_redo - > add_do_method ( this , " _start_onion_skinning " ) ;
undo_redo - > add_undo_method ( this , " _stop_onion_skinning " ) ;
}
2022-04-07 11:49:28 +00:00
if ( lib_added ) {
2023-07-20 15:34:06 +00:00
undo_redo - > add_undo_method ( fetch_mixer_for_library ( ) , " remove_animation_library " , " " ) ;
2022-04-07 11:49:28 +00:00
}
2022-02-14 06:28:42 +00:00
undo_redo - > commit_action ( ) ;
2014-02-10 01:10:30 +00:00
2022-05-14 16:54:14 +00:00
if ( library_name ! = " " ) {
library_name = library_name + " / " ;
}
2023-07-20 15:34:06 +00:00
if ( is_dummy ) {
plugin - > _update_dummy_player ( original_node ) ;
}
2022-05-14 16:54:14 +00:00
_select_anim_by_name ( library_name + new_name ) ;
2022-02-14 06:28:42 +00:00
} break ;
2014-02-10 01:10:30 +00:00
2022-02-14 06:28:42 +00:00
case TOOL_DUPLICATE_ANIM : {
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
Ref < Animation > anim = player - > get_animation ( current ) ;
2014-02-10 01:10:30 +00:00
2022-02-14 06:28:42 +00:00
Ref < Animation > new_anim = _animation_clone ( anim ) ;
2022-03-05 13:00:34 +00:00
new_anim - > set_name ( new_name ) ;
2022-02-14 06:28:42 +00:00
2022-05-14 16:54:14 +00:00
String library_name ;
Ref < AnimationLibrary > al ;
if ( library - > is_visible ( ) ) {
library_name = library - > get_item_metadata ( library - > get_selected ( ) ) ;
// It's possible that [Global] was selected, but doesn't exist yet.
if ( player - > has_animation_library ( library_name ) ) {
al = player - > get_animation_library ( library_name ) ;
}
} else {
if ( player - > has_animation_library ( " " ) ) {
al = player - > get_animation_library ( " " ) ;
library_name = " " ;
}
}
2022-04-07 11:49:28 +00:00
2022-02-14 06:28:42 +00:00
undo_redo - > create_action ( TTR ( " Duplicate Animation " ) ) ;
2022-05-14 16:54:14 +00:00
bool lib_added = false ;
if ( al . is_null ( ) ) {
al . instantiate ( ) ;
lib_added = true ;
undo_redo - > add_do_method ( player , " add_animation_library " , " " , al ) ;
library_name = " " ;
}
undo_redo - > add_do_method ( al . ptr ( ) , " add_animation " , new_name , new_anim ) ;
undo_redo - > add_undo_method ( al . ptr ( ) , " remove_animation " , new_name ) ;
2022-02-14 06:28:42 +00:00
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
2022-05-14 16:54:14 +00:00
if ( lib_added ) {
undo_redo - > add_undo_method ( player , " remove_animation_library " , " " ) ;
}
2022-02-14 06:28:42 +00:00
undo_redo - > commit_action ( ) ;
2014-02-10 01:10:30 +00:00
2022-05-14 16:54:14 +00:00
if ( library_name ! = " " ) {
library_name = library_name + " / " ;
}
2023-07-20 15:34:06 +00:00
if ( is_dummy ) {
plugin - > _update_dummy_player ( original_node ) ;
}
2022-05-14 16:54:14 +00:00
_select_anim_by_name ( library_name + new_name ) ;
2022-02-14 06:28:42 +00:00
} break ;
2014-02-10 01:10:30 +00:00
}
name_dialog - > hide ( ) ;
}
2016-05-07 16:27:52 +00:00
void AnimationPlayerEditor : : _blend_editor_next_changed ( const int p_idx ) {
2022-04-07 11:49:28 +00:00
if ( ! animation - > has_selectable_items ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2016-05-07 16:27:52 +00:00
undo_redo - > create_action ( TTR ( " Blend Next Changed " ) ) ;
undo_redo - > add_do_method ( player , " animation_set_next " , current , blend_editor . next - > get_item_text ( p_idx ) ) ;
undo_redo - > add_undo_method ( player , " animation_set_next " , current , player - > animation_get_next ( current ) ) ;
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > commit_action ( ) ;
2014-02-10 01:10:30 +00:00
}
2023-10-30 20:51:36 +00:00
void AnimationPlayerEditor : : _edit_animation_blend ( ) {
if ( updating_blends | | ! animation - > has_selectable_items ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-10-30 20:51:36 +00:00
blend_editor . dialog - > popup_centered ( Size2 ( 400 , 400 ) * EDSCALE ) ;
_update_animation_blend ( ) ;
}
2014-02-10 01:10:30 +00:00
2023-10-30 20:51:36 +00:00
void AnimationPlayerEditor : : _update_animation_blend ( ) {
if ( updating_blends | | ! animation - > has_selectable_items ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-10-30 20:51:36 +00:00
blend_editor . tree - > clear ( ) ;
2014-02-10 01:10:30 +00:00
2023-10-30 20:51:36 +00:00
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
2014-02-10 01:10:30 +00:00
List < StringName > anims ;
player - > get_animation_list ( & anims ) ;
TreeItem * root = blend_editor . tree - > create_item ( ) ;
updating_blends = true ;
2016-05-07 16:27:52 +00:00
int i = 0 ;
bool anim_found = false ;
blend_editor . next - > clear ( ) ;
blend_editor . next - > add_item ( " " , i ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & to : anims ) {
2014-02-10 01:10:30 +00:00
TreeItem * blend = blend_editor . tree - > create_item ( root ) ;
blend - > set_editable ( 0 , false ) ;
blend - > set_editable ( 1 , true ) ;
blend - > set_text ( 0 , to ) ;
blend - > set_cell_mode ( 1 , TreeItem : : CELL_MODE_RANGE ) ;
blend - > set_range_config ( 1 , 0 , 3600 , 0.001 ) ;
blend - > set_range ( 1 , player - > get_blend_time ( current , to ) ) ;
2016-05-07 16:27:52 +00:00
i + + ;
blend_editor . next - > add_item ( to , i ) ;
if ( to = = player - > animation_get_next ( current ) ) {
blend_editor . next - > select ( i ) ;
anim_found = true ;
}
2014-02-10 01:10:30 +00:00
}
2016-05-07 16:27:52 +00:00
// make sure we reset it else it becomes out of sync and could contain a deleted animation
if ( ! anim_found ) {
blend_editor . next - > select ( 0 ) ;
player - > animation_set_next ( current , blend_editor . next - > get_item_text ( 0 ) ) ;
}
2014-02-10 01:10:30 +00:00
updating_blends = false ;
}
void AnimationPlayerEditor : : _blend_edited ( ) {
2023-10-30 20:51:36 +00:00
if ( updating_blends | | ! animation - > has_selectable_items ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
TreeItem * selected = blend_editor . tree - > get_edited ( ) ;
2020-05-14 14:41:43 +00:00
if ( ! selected ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-10-30 20:51:36 +00:00
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
2014-02-10 01:10:30 +00:00
updating_blends = true ;
String to = selected - > get_text ( 0 ) ;
float blend_time = selected - > get_range ( 1 ) ;
float prev_blend_time = player - > get_blend_time ( current , to ) ;
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2016-05-04 01:25:37 +00:00
undo_redo - > create_action ( TTR ( " Change Blend Time " ) ) ;
2014-02-10 01:10:30 +00:00
undo_redo - > add_do_method ( player , " set_blend_time " , current , to , blend_time ) ;
undo_redo - > add_undo_method ( player , " set_blend_time " , current , to , prev_blend_time ) ;
undo_redo - > add_do_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > add_undo_method ( this , " _animation_player_changed " , player ) ;
undo_redo - > commit_action ( ) ;
updating_blends = false ;
}
void AnimationPlayerEditor : : ensure_visibility ( ) {
2023-07-20 15:34:06 +00:00
if ( player ) {
2016-05-01 09:37:58 +00:00
return ; // another player is pinned, don't reset
2020-05-14 14:41:43 +00:00
}
2016-05-01 09:37:58 +00:00
2014-02-10 01:10:30 +00:00
_animation_edit ( ) ;
}
2015-08-30 01:59:25 +00:00
Dictionary AnimationPlayerEditor : : get_state ( ) const {
Dictionary d ;
2023-07-20 15:34:06 +00:00
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 ( ) ;
}
2015-08-30 01:59:25 +00:00
}
return d ;
}
2020-05-14 12:29:06 +00:00
2015-08-30 01:59:25 +00:00
void AnimationPlayerEditor : : set_state ( const Dictionary & p_state ) {
2019-05-25 01:47:16 +00:00
if ( ! p_state . has ( " visible " ) | | ! p_state [ " visible " ] ) {
return ;
}
if ( ! EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ) {
return ;
}
2015-08-30 01:59:25 +00:00
2019-05-25 01:47:16 +00:00
if ( p_state . has ( " player " ) ) {
2015-08-30 01:59:25 +00:00
Node * n = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) - > get_node ( p_state [ " player " ] ) ;
2017-08-24 20:58:51 +00:00
if ( Object : : cast_to < AnimationPlayer > ( n ) & & EditorNode : : get_singleton ( ) - > get_editor_selection ( ) - > is_selected ( n ) ) {
2022-05-05 03:53:47 +00:00
if ( player ) {
2023-07-20 15:34:06 +00:00
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 ) ) ;
2022-05-05 03:53:47 +00:00
}
}
2017-08-24 20:58:51 +00:00
player = Object : : cast_to < AnimationPlayer > ( n ) ;
2022-05-05 03:53:47 +00:00
if ( player ) {
2023-07-20 15:34:06 +00:00
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 ) ;
2022-05-05 03:53:47 +00:00
}
}
2015-08-30 01:59:25 +00:00
_update_player ( ) ;
2022-01-27 09:36:51 +00:00
EditorNode : : get_singleton ( ) - > make_bottom_panel_item_visible ( this ) ;
2015-08-30 01:59:25 +00:00
set_process ( true ) ;
ensure_visibility ( ) ;
if ( p_state . has ( " animation " ) ) {
String anim = p_state [ " animation " ] ;
2020-12-15 12:04:21 +00:00
if ( ! anim . is_empty ( ) & & player - > has_animation ( anim ) ) {
2019-09-04 16:18:25 +00:00
_select_anim_by_name ( anim ) ;
_animation_edit ( ) ;
}
2015-08-30 01:59:25 +00:00
}
}
2019-05-25 01:47:16 +00:00
}
2019-04-14 19:43:38 +00:00
2019-05-25 01:47:16 +00:00
if ( p_state . has ( " track_editor_state " ) ) {
track_editor - > set_state ( p_state [ " track_editor_state " ] ) ;
2015-08-30 01:59:25 +00:00
}
}
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _animation_resource_edit ( ) {
2022-04-07 11:49:28 +00:00
String current = _get_current ( ) ;
if ( current ! = String ( ) ) {
2014-02-10 01:10:30 +00:00
Ref < Animation > anim = player - > get_animation ( current ) ;
2022-01-27 09:36:51 +00:00
EditorNode : : get_singleton ( ) - > edit_resource ( anim ) ;
2014-02-10 01:10:30 +00:00
}
}
void AnimationPlayerEditor : : _animation_edit ( ) {
2022-04-07 11:49:28 +00:00
String current = _get_current ( ) ;
if ( current ! = String ( ) ) {
2016-01-17 23:03:57 +00:00
Ref < Animation > anim = player - > get_animation ( current ) ;
2022-05-04 00:17:08 +00:00
2022-09-07 09:17:22 +00:00
bool animation_library_is_foreign = EditorNode : : get_singleton ( ) - > is_resource_read_only ( anim ) ;
2022-05-04 00:17:08 +00:00
track_editor - > set_animation ( anim , animation_library_is_foreign ) ;
2019-05-25 01:47:16 +00:00
2023-07-20 15:34:06 +00:00
Node * root = player - > get_node_or_null ( player - > get_root_node ( ) ) ;
2016-01-17 23:03:57 +00:00
if ( root ) {
2018-06-07 15:46:14 +00:00
track_editor - > set_root ( root ) ;
2016-01-17 23:03:57 +00:00
}
} else {
2022-05-04 00:17:08 +00:00
track_editor - > set_animation ( Ref < Animation > ( ) , true ) ;
2020-04-01 23:20:12 +00:00
track_editor - > set_root ( nullptr ) ;
2014-02-10 01:10:30 +00:00
}
}
2019-05-25 01:47:16 +00:00
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _scale_changed ( const String & p_scale ) {
2020-07-24 18:07:57 +00:00
player - > set_speed_scale ( p_scale . to_float ( ) ) ;
2014-02-10 01:10:30 +00:00
}
void AnimationPlayerEditor : : _update_animation ( ) {
// the purpose of _update_animation is to reflect the current state
// of the animation player in the current editor..
updating = true ;
if ( player - > is_playing ( ) ) {
2019-11-19 13:39:10 +00:00
stop - > set_icon ( pause_icon ) ;
2014-02-10 01:10:30 +00:00
} else {
2019-11-19 13:39:10 +00:00
stop - > set_icon ( stop_icon ) ;
2014-02-10 01:10:30 +00:00
}
2017-01-13 22:36:04 +00:00
scale - > set_text ( String : : num ( player - > get_speed_scale ( ) , 2 ) ) ;
2018-01-14 10:28:57 +00:00
String current = player - > get_assigned_animation ( ) ;
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < animation - > get_item_count ( ) ; i + + ) {
if ( animation - > get_item_text ( i ) = = current ) {
animation - > select ( i ) ;
break ;
}
}
updating = false ;
}
void AnimationPlayerEditor : : _update_player ( ) {
updating = true ;
animation - > clear ( ) ;
2022-08-14 10:50:42 +00:00
tool_anim - > set_disabled ( player = = nullptr ) ;
pin - > set_disabled ( player = = nullptr ) ;
2019-04-04 20:00:16 +00:00
if ( ! player ) {
2021-02-04 08:20:26 +00:00
AnimationPlayerEditor : : get_singleton ( ) - > get_track_editor ( ) - > update_keying ( ) ;
2019-04-04 20:00:16 +00:00
return ;
}
2022-04-07 11:49:28 +00:00
List < StringName > libraries ;
2022-08-14 10:50:42 +00:00
player - > get_animation_library_list ( & libraries ) ;
2022-04-07 11:49:28 +00:00
2014-02-10 01:10:30 +00:00
int active_idx = - 1 ;
2022-04-07 11:49:28 +00:00
bool no_anims_found = true ;
2022-05-04 00:17:08 +00:00
bool foreign_global_anim_lib = false ;
2014-02-10 01:10:30 +00:00
2022-04-07 11:49:28 +00:00
for ( const StringName & K : libraries ) {
if ( K ! = StringName ( ) ) {
animation - > add_separator ( K ) ;
}
2022-05-04 00:17:08 +00:00
// Check if the global library is foreign since we want to disable options for adding/remove/renaming animations if it is.
2022-09-29 09:53:28 +00:00
Ref < AnimationLibrary > anim_library = player - > get_animation_library ( K ) ;
2022-05-04 00:17:08 +00:00
if ( K = = " " ) {
2022-09-29 09:53:28 +00:00
foreign_global_anim_lib = EditorNode : : get_singleton ( ) - > is_resource_read_only ( anim_library ) ;
2022-05-04 00:17:08 +00:00
}
2022-04-07 11:49:28 +00:00
List < StringName > animlist ;
2022-09-29 09:53:28 +00:00
anim_library - > get_animation_list ( & animlist ) ;
2022-04-07 11:49:28 +00:00
for ( const StringName & E : animlist ) {
String path = K ;
if ( path ! = " " ) {
path + = " / " ;
}
path + = E ;
animation - > add_item ( path ) ;
if ( player - > get_assigned_animation ( ) = = path ) {
active_idx = animation - > get_selectable_item ( true ) ;
}
no_anims_found = false ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2022-05-04 00:17:08 +00:00
# define ITEM_CHECK_DISABLED(m_item) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), foreign_global_anim_lib)
ITEM_CHECK_DISABLED ( TOOL_NEW_ANIM ) ;
# undef ITEM_CHECK_DISABLED
# define ITEM_CHECK_DISABLED(m_item) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), no_anims_found || foreign_global_anim_lib)
2022-04-07 11:49:28 +00:00
ITEM_CHECK_DISABLED ( TOOL_DUPLICATE_ANIM ) ;
ITEM_CHECK_DISABLED ( TOOL_RENAME_ANIM ) ;
ITEM_CHECK_DISABLED ( TOOL_EDIT_TRANSITIONS ) ;
ITEM_CHECK_DISABLED ( TOOL_REMOVE_ANIM ) ;
2022-04-08 01:31:19 +00:00
ITEM_CHECK_DISABLED ( TOOL_EDIT_RESOURCE ) ;
2022-04-07 11:49:28 +00:00
# undef ITEM_CHECK_DISABLED
stop - > set_disabled ( no_anims_found ) ;
play - > set_disabled ( no_anims_found ) ;
play_bw - > set_disabled ( no_anims_found ) ;
play_bw_from - > set_disabled ( no_anims_found ) ;
play_from - > set_disabled ( no_anims_found ) ;
frame - > set_editable ( ! no_anims_found ) ;
animation - > set_disabled ( no_anims_found ) ;
autoplay - > set_disabled ( no_anims_found ) ;
onion_toggle - > set_disabled ( no_anims_found ) ;
onion_skinning - > set_disabled ( no_anims_found ) ;
2022-03-10 05:46:05 +00:00
_update_animation_list_icons ( ) ;
2014-02-10 01:10:30 +00:00
updating = false ;
if ( active_idx ! = - 1 ) {
animation - > select ( active_idx ) ;
autoplay - > set_pressed ( animation - > get_item_text ( active_idx ) = = player - > get_autoplay ( ) ) ;
_animation_selected ( active_idx ) ;
2022-04-07 11:49:28 +00:00
} else if ( animation - > has_selectable_items ( ) ) {
int item = animation - > get_selectable_item ( ) ;
animation - > select ( item ) ;
autoplay - > set_pressed ( animation - > get_item_text ( item ) = = player - > get_autoplay ( ) ) ;
_animation_selected ( item ) ;
2018-07-03 18:57:54 +00:00
} else {
_animation_selected ( 0 ) ;
2014-02-10 01:10:30 +00:00
}
2022-04-07 11:49:28 +00:00
if ( ! no_anims_found ) {
2016-01-17 23:03:57 +00:00
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
Ref < Animation > anim = player - > get_animation ( current ) ;
2022-05-04 00:17:08 +00:00
2022-09-07 09:17:22 +00:00
bool animation_library_is_foreign = EditorNode : : get_singleton ( ) - > is_resource_read_only ( anim ) ;
2022-05-04 00:17:08 +00:00
track_editor - > set_animation ( anim , animation_library_is_foreign ) ;
2023-07-20 15:34:06 +00:00
Node * root = player - > get_node_or_null ( player - > get_root_node ( ) ) ;
2016-01-17 23:03:57 +00:00
if ( root ) {
2018-06-07 15:46:14 +00:00
track_editor - > set_root ( root ) ;
2014-02-10 01:10:30 +00:00
}
}
_update_animation ( ) ;
}
2022-03-10 05:46:05 +00:00
void AnimationPlayerEditor : : _update_animation_list_icons ( ) {
for ( int i = 0 ; i < animation - > get_item_count ( ) ; i + + ) {
2022-09-29 09:53:28 +00:00
String anim_name = animation - > get_item_text ( i ) ;
2022-04-07 11:49:28 +00:00
if ( animation - > is_item_disabled ( i ) | | animation - > is_item_separator ( i ) ) {
continue ;
}
2022-03-10 05:46:05 +00:00
Ref < Texture2D > icon ;
2022-09-29 09:53:28 +00:00
if ( anim_name = = player - > get_autoplay ( ) ) {
if ( anim_name = = SceneStringNames : : get_singleton ( ) - > RESET ) {
2022-03-10 05:46:05 +00:00
icon = autoplay_reset_icon ;
} else {
icon = autoplay_icon ;
}
2022-09-29 09:53:28 +00:00
} else if ( anim_name = = SceneStringNames : : get_singleton ( ) - > RESET ) {
2022-03-10 05:46:05 +00:00
icon = reset_icon ;
}
animation - > set_item_icon ( i , icon ) ;
}
}
2022-05-14 16:54:14 +00:00
void AnimationPlayerEditor : : _update_name_dialog_library_dropdown ( ) {
2022-11-14 17:21:06 +00:00
StringName current_library_name ;
2022-05-14 16:54:14 +00:00
if ( animation - > has_selectable_items ( ) ) {
String current_animation_name = animation - > get_item_text ( animation - > get_selected ( ) ) ;
Ref < Animation > current_animation = player - > get_animation ( current_animation_name ) ;
if ( current_animation . is_valid ( ) ) {
current_library_name = player - > find_animation_library ( current_animation ) ;
}
}
List < StringName > libraries ;
player - > get_animation_library_list ( & libraries ) ;
library - > clear ( ) ;
// When [Global] isn't present, but other libraries are, add option of creating [Global].
int index_offset = 0 ;
if ( ! player - > has_animation_library ( StringName ( ) ) ) {
library - > add_item ( String ( TTR ( " [Global] (create) " ) ) ) ;
library - > set_item_metadata ( 0 , " " ) ;
index_offset = 1 ;
}
int current_lib_id = index_offset ; // Don't default to [Global] if it doesn't exist yet.
for ( int i = 0 ; i < libraries . size ( ) ; i + + ) {
StringName library_name = libraries [ i ] ;
library - > add_item ( ( library_name = = StringName ( ) ) ? String ( TTR ( " [Global] " ) ) : String ( library_name ) ) ;
library - > set_item_metadata ( i + index_offset , String ( library_name ) ) ;
// Default to duplicating into same library.
if ( library_name = = current_library_name ) {
current_lib_id = i + index_offset ;
}
}
if ( library - > get_item_count ( ) > 1 ) {
library - > select ( current_lib_id ) ;
library - > show ( ) ;
} else {
library - > hide ( ) ;
}
}
2023-07-20 15:34:06 +00:00
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.
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 ) {
2020-05-14 14:41:43 +00:00
if ( player & & pin - > is_pressed ( ) ) {
2019-06-19 03:51:06 +00:00
return ; // Ignore, pinned.
2020-05-14 14:41:43 +00:00
}
2022-05-05 03:53:47 +00:00
if ( player ) {
2023-07-20 15:34:06 +00:00
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 ) ) ;
2022-05-05 03:53:47 +00:00
}
}
2023-07-20 15:34:06 +00:00
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 ;
2014-02-10 01:10:30 +00:00
player = p_player ;
2023-07-20 15:34:06 +00:00
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 ) ) ;
}
}
2014-02-10 01:10:30 +00:00
2016-07-22 12:42:12 +00:00
if ( player ) {
2023-07-20 15:34:06 +00:00
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 ) ) ;
2022-05-05 03:53:47 +00:00
}
2014-02-10 01:10:30 +00:00
_update_player ( ) ;
2019-06-19 03:51:06 +00:00
if ( onion . enabled ) {
2022-04-07 11:49:28 +00:00
if ( animation - > has_selectable_items ( ) ) {
2019-06-19 03:51:06 +00:00
_start_onion_skinning ( ) ;
2020-05-14 14:41:43 +00:00
} else {
2019-06-19 03:51:06 +00:00
_stop_onion_skinning ( ) ;
2020-05-14 14:41:43 +00:00
}
2019-06-19 03:51:06 +00:00
}
2018-06-07 15:46:14 +00:00
track_editor - > show_select_node_warning ( false ) ;
2016-07-22 12:42:12 +00:00
} else {
2020-05-14 14:41:43 +00:00
if ( onion . enabled ) {
2019-06-19 03:51:06 +00:00
_stop_onion_skinning ( ) ;
2020-05-14 14:41:43 +00:00
}
2019-06-19 03:51:06 +00:00
2018-06-07 15:46:14 +00:00
track_editor - > show_select_node_warning ( true ) ;
2014-02-10 01:10:30 +00:00
}
2022-04-07 11:49:28 +00:00
2023-07-20 15:34:06 +00:00
library_editor - > set_animation_mixer ( fetch_mixer_for_library ( ) ) ;
_ensure_dummy_player ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-09-14 12:51:50 +00:00
void AnimationPlayerEditor : : forward_force_draw_over_viewport ( Control * p_overlay ) {
2020-05-14 14:41:43 +00:00
if ( ! onion . can_overlay ) {
2017-10-30 18:43:19 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-10-30 18:43:19 +00:00
2019-06-19 03:51:06 +00:00
// Can happen on viewport resize, at least.
2020-05-14 14:41:43 +00:00
if ( ! _are_onion_layers_valid ( ) ) {
2017-10-30 18:43:19 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-10-30 18:43:19 +00:00
RID ci = p_overlay - > get_canvas_item ( ) ;
Rect2 src_rect = p_overlay - > get_global_rect ( ) ;
2019-06-19 03:51:06 +00:00
// Re-flip since captures are already flipped.
2017-10-30 18:43:19 +00:00
src_rect . position . y = onion . capture_size . y - ( src_rect . position . y + src_rect . size . y ) ;
src_rect . size . y * = - 1 ;
Rect2 dst_rect = Rect2 ( Point2 ( ) , p_overlay - > get_size ( ) ) ;
float alpha_step = 1.0 / ( onion . steps + 1 ) ;
2023-09-27 14:37:26 +00:00
uint32_t capture_idx = 0 ;
2017-10-30 18:43:19 +00:00
if ( onion . past ) {
2023-09-27 14:37:26 +00:00
float alpha = 0.0f ;
2017-10-30 18:43:19 +00:00
do {
alpha + = alpha_step ;
2023-09-27 14:37:26 +00:00
if ( onion . captures_valid [ capture_idx ] ) {
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > canvas_item_add_texture_rect_region (
2023-09-27 14:37:26 +00:00
ci , dst_rect , RS : : get_singleton ( ) - > viewport_get_texture ( onion . captures [ capture_idx ] ) , src_rect , Color ( 1 , 1 , 1 , alpha ) ) ;
2017-10-30 18:43:19 +00:00
}
2023-09-27 14:37:26 +00:00
capture_idx + + ;
} while ( capture_idx < onion . steps ) ;
2017-10-30 18:43:19 +00:00
}
if ( onion . future ) {
2023-09-27 14:37:26 +00:00
float alpha = 1.0f ;
uint32_t base_cidx = capture_idx ;
2017-10-30 18:43:19 +00:00
do {
alpha - = alpha_step ;
2023-09-27 14:37:26 +00:00
if ( onion . captures_valid [ capture_idx ] ) {
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > canvas_item_add_texture_rect_region (
2023-09-27 14:37:26 +00:00
ci , dst_rect , RS : : get_singleton ( ) - > viewport_get_texture ( onion . captures [ capture_idx ] ) , src_rect , Color ( 1 , 1 , 1 , alpha ) ) ;
2017-10-30 18:43:19 +00:00
}
2023-09-27 14:37:26 +00:00
capture_idx + + ;
} while ( capture_idx < base_cidx + onion . steps ) ; // In case there's the present capture at the end, skip it.
2017-10-30 18:43:19 +00:00
}
}
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _animation_duplicate ( ) {
2022-04-07 11:49:28 +00:00
if ( ! animation - > has_selectable_items ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
String current = animation - > get_item_text ( animation - > get_selected ( ) ) ;
Ref < Animation > anim = player - > get_animation ( current ) ;
2020-05-14 14:41:43 +00:00
if ( ! anim . is_valid ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-01-29 20:13:43 +00:00
int count = 2 ;
2014-02-10 01:10:30 +00:00
String new_name = current ;
2023-01-29 20:13:43 +00:00
PackedStringArray split = new_name . split ( " _ " ) ;
int last_index = split . size ( ) - 1 ;
if ( last_index > 0 & & split [ last_index ] . is_valid_int ( ) & & split [ last_index ] . to_int ( ) > = 0 ) {
count = split [ last_index ] . to_int ( ) ;
split . remove_at ( last_index ) ;
new_name = String ( " _ " ) . join ( split ) ;
}
while ( true ) {
String attempt = new_name ;
attempt + = vformat ( " _%d " , count ) ;
if ( player - > has_animation ( attempt ) ) {
count + + ;
continue ;
}
new_name = attempt ;
break ;
2014-02-10 01:10:30 +00:00
}
2022-05-14 16:54:14 +00:00
if ( new_name . contains ( " / " ) ) {
// Discard library prefix.
new_name = new_name . get_slice ( " / " , 1 ) ;
}
_update_name_dialog_library_dropdown ( ) ;
2022-02-14 06:28:42 +00:00
name_dialog_op = TOOL_DUPLICATE_ANIM ;
2022-06-23 05:13:42 +00:00
name_dialog - > set_title ( TTR ( " Duplicate Animation " ) ) ;
2023-02-13 03:27:12 +00:00
// TRANSLATORS: This is a label for the new name field in the "Duplicate Animation" dialog.
2022-05-14 16:54:14 +00:00
name_title - > set_text ( TTR ( " Duplicated Animation Name: " ) ) ;
name - > set_text ( new_name ) ;
2022-02-14 06:28:42 +00:00
name_dialog - > popup_centered ( Size2 ( 300 , 90 ) ) ;
name - > select_all ( ) ;
name - > grab_focus ( ) ;
2014-02-10 01:10:30 +00:00
}
2019-11-02 00:52:54 +00:00
Ref < Animation > AnimationPlayerEditor : : _animation_clone ( Ref < Animation > p_anim ) {
Ref < Animation > new_anim = memnew ( Animation ) ;
List < PropertyInfo > plist ;
p_anim - > get_property_list ( & plist ) ;
for ( const PropertyInfo & E : plist ) {
if ( E . usage & PROPERTY_USAGE_STORAGE ) {
new_anim - > set ( E . name , p_anim - > get ( E . name ) ) ;
}
}
new_anim - > set_path ( " " ) ;
return new_anim ;
}
2023-11-21 21:14:32 +00:00
void AnimationPlayerEditor : : _seek_value_changed ( float p_value , bool p_timeline_only ) {
2014-02-10 01:10:30 +00:00
if ( updating | | ! player | | player - > is_playing ( ) ) {
return ;
} ;
updating = true ;
2019-08-05 23:15:20 +00:00
String current = player - > get_assigned_animation ( ) ;
2021-12-09 09:42:46 +00:00
if ( current . is_empty ( ) | | ! player - > has_animation ( current ) ) {
2014-02-10 01:10:30 +00:00
updating = false ;
current = " " ;
return ;
} ;
Ref < Animation > anim ;
anim = player - > get_animation ( current ) ;
2023-11-21 21:14:32 +00:00
double pos = CLAMP ( ( double ) anim - > get_length ( ) * ( p_value / frame - > get_max ( ) ) , 0 , ( double ) anim - > get_length ( ) ) ;
2019-08-05 23:15:20 +00:00
if ( track_editor - > is_snap_enabled ( ) ) {
2020-12-21 18:02:57 +00:00
pos = Math : : snapped ( pos , _get_editor_step ( ) ) ;
2016-02-09 20:02:57 +00:00
}
2023-11-21 21:14:32 +00:00
pos = CLAMP ( pos , 0 , ( double ) anim - > get_length ( ) - CMP_EPSILON2 ) ; // Hack: Avoid fposmod with LOOP_LINEAR.
2014-02-10 01:10:30 +00:00
2023-11-21 21:14:32 +00:00
if ( ! p_timeline_only & & anim . is_valid ( ) ) {
player - > seek ( pos , true , true ) ;
2014-02-10 01:10:30 +00:00
}
2018-06-07 15:46:14 +00:00
track_editor - > set_anim_pos ( pos ) ;
2014-02-10 01:10:30 +00:00
} ;
void AnimationPlayerEditor : : _animation_player_changed ( Object * p_pl ) {
2023-07-20 15:34:06 +00:00
_update_player ( ) ;
2023-10-30 20:51:36 +00:00
2023-07-20 15:34:06 +00:00
if ( blend_editor . dialog - > is_visible ( ) ) {
2023-10-30 20:51:36 +00:00
_update_animation_blend ( ) ; // Update.
2023-07-20 15:34:06 +00:00
}
2023-10-30 20:51:36 +00:00
2023-07-20 15:34:06 +00:00
if ( library_editor - > is_visible ( ) ) {
library_editor - > update_tree ( ) ;
2014-02-10 01:10:30 +00:00
}
}
2022-05-05 03:53:47 +00:00
void AnimationPlayerEditor : : _animation_libraries_updated ( ) {
_animation_player_changed ( player ) ;
}
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _list_changed ( ) {
2020-05-14 14:41:43 +00:00
if ( is_visible_in_tree ( ) ) {
2014-02-10 01:10:30 +00:00
_update_player ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2023-07-20 15:34:06 +00:00
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 ( ) ;
}
}
2019-05-30 19:43:46 +00:00
void AnimationPlayerEditor : : _animation_key_editor_anim_len_changed ( float p_len ) {
frame - > set_max ( p_len ) ;
}
2023-11-21 21:14:32 +00:00
void AnimationPlayerEditor : : _animation_key_editor_seek ( float p_pos , bool p_timeline_only ) {
2019-10-01 16:38:03 +00:00
timeline_position = p_pos ;
2020-05-14 14:41:43 +00:00
if ( ! is_visible_in_tree ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2019-10-02 20:49:34 +00:00
2020-05-14 14:41:43 +00:00
if ( ! player ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( player - > is_playing ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( ! player - > has_animation ( player - > get_assigned_animation ( ) ) ) {
2019-10-02 20:49:34 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2019-10-02 20:49:34 +00:00
2016-06-19 04:43:02 +00:00
updating = true ;
2020-12-21 18:02:57 +00:00
frame - > set_value ( Math : : snapped ( p_pos , _get_editor_step ( ) ) ) ;
2016-06-19 04:43:02 +00:00
updating = false ;
2023-11-21 21:14:32 +00:00
_seek_value_changed ( p_pos , p_timeline_only ) ;
2014-02-10 01:10:30 +00:00
}
2015-05-25 04:46:45 +00:00
void AnimationPlayerEditor : : _animation_tool_menu ( int p_option ) {
2022-04-07 11:49:28 +00:00
String current = _get_current ( ) ;
2018-07-01 08:26:29 +00:00
2018-06-07 15:46:14 +00:00
Ref < Animation > anim ;
2021-12-09 09:42:46 +00:00
if ( ! current . is_empty ( ) ) {
2018-06-07 15:46:14 +00:00
anim = player - > get_animation ( current ) ;
}
2015-05-25 04:46:45 +00:00
switch ( p_option ) {
2018-06-07 15:46:14 +00:00
case TOOL_NEW_ANIM : {
_animation_new ( ) ;
} break ;
2022-04-07 11:49:28 +00:00
case TOOL_ANIM_LIBRARY : {
2023-07-20 15:34:06 +00:00
library_editor - > set_animation_mixer ( fetch_mixer_for_library ( ) ) ;
2022-04-07 11:49:28 +00:00
library_editor - > show_dialog ( ) ;
2018-06-07 15:46:14 +00:00
} break ;
case TOOL_DUPLICATE_ANIM : {
_animation_duplicate ( ) ;
2022-02-14 06:28:42 +00:00
} break ;
2018-06-07 15:46:14 +00:00
case TOOL_RENAME_ANIM : {
_animation_rename ( ) ;
} break ;
case TOOL_EDIT_TRANSITIONS : {
2023-10-30 20:51:36 +00:00
_edit_animation_blend ( ) ;
2018-06-07 15:46:14 +00:00
} break ;
case TOOL_REMOVE_ANIM : {
_animation_remove ( ) ;
} break ;
2015-05-25 04:46:45 +00:00
case TOOL_EDIT_RESOURCE : {
2022-04-08 01:31:19 +00:00
if ( anim . is_valid ( ) ) {
EditorNode : : get_singleton ( ) - > edit_resource ( anim ) ;
2015-05-25 04:46:45 +00:00
}
} break ;
}
}
2017-10-30 18:43:19 +00:00
void AnimationPlayerEditor : : _onion_skinning_menu ( int p_option ) {
PopupMenu * menu = onion_skinning - > get_popup ( ) ;
int idx = menu - > get_item_index ( p_option ) ;
switch ( p_option ) {
case ONION_SKINNING_ENABLE : {
onion . enabled = ! onion . enabled ;
2020-05-14 14:41:43 +00:00
if ( onion . enabled ) {
2023-09-27 14:37:26 +00:00
if ( get_player ( ) & & ! get_player ( ) - > has_animation ( SceneStringNames : : get_singleton ( ) - > RESET ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Onion skinning requires a RESET animation. " ) ) ;
}
_start_onion_skinning ( ) ; // It will check for RESET animation anyway.
2020-05-14 14:41:43 +00:00
} else {
2017-10-30 18:43:19 +00:00
_stop_onion_skinning ( ) ;
2020-05-14 14:41:43 +00:00
}
2017-10-30 18:43:19 +00:00
} break ;
case ONION_SKINNING_PAST : {
2019-06-19 03:51:06 +00:00
// Ensure at least one of past/future is checked.
2017-10-30 18:43:19 +00:00
onion . past = onion . future ? ! onion . past : true ;
menu - > set_item_checked ( idx , onion . past ) ;
} break ;
case ONION_SKINNING_FUTURE : {
2019-06-19 03:51:06 +00:00
// Ensure at least one of past/future is checked.
2017-10-30 18:43:19 +00:00
onion . future = onion . past ? ! onion . future : true ;
menu - > set_item_checked ( idx , onion . future ) ;
} break ;
2019-06-19 03:51:06 +00:00
case ONION_SKINNING_1_STEP : // Fall-through.
2017-10-30 18:43:19 +00:00
case ONION_SKINNING_2_STEPS :
case ONION_SKINNING_3_STEPS : {
onion . steps = ( p_option - ONION_SKINNING_1_STEP ) + 1 ;
int one_frame_idx = menu - > get_item_index ( ONION_SKINNING_1_STEP ) ;
for ( int i = 0 ; i < = ONION_SKINNING_LAST_STEPS_OPTION - ONION_SKINNING_1_STEP ; i + + ) {
2023-09-27 14:37:26 +00:00
menu - > set_item_checked ( one_frame_idx + i , ( int ) onion . steps = = i + 1 ) ;
2017-10-30 18:43:19 +00:00
}
} break ;
case ONION_SKINNING_DIFFERENCES_ONLY : {
onion . differences_only = ! onion . differences_only ;
menu - > set_item_checked ( idx , onion . differences_only ) ;
} break ;
case ONION_SKINNING_FORCE_WHITE_MODULATE : {
onion . force_white_modulate = ! onion . force_white_modulate ;
menu - > set_item_checked ( idx , onion . force_white_modulate ) ;
} break ;
case ONION_SKINNING_INCLUDE_GIZMOS : {
onion . include_gizmos = ! onion . include_gizmos ;
menu - > set_item_checked ( idx , onion . include_gizmos ) ;
} break ;
}
}
2022-01-11 13:59:52 +00:00
void AnimationPlayerEditor : : shortcut_input ( const Ref < InputEvent > & p_ev ) {
2021-04-05 06:52:21 +00:00
ERR_FAIL_COND ( p_ev . is_null ( ) ) ;
2017-05-20 15:38:03 +00:00
Ref < InputEventKey > k = p_ev ;
2021-04-24 20:33:50 +00:00
if ( is_visible_in_tree ( ) & & k . is_valid ( ) & & k - > is_pressed ( ) & & ! k - > is_echo ( ) & & ! k - > is_alt_pressed ( ) & & ! k - > is_ctrl_pressed ( ) & & ! k - > is_meta_pressed ( ) ) {
2018-04-05 17:59:35 +00:00
switch ( k - > get_keycode ( ) ) {
2021-08-13 21:31:57 +00:00
case Key : : A : {
2021-04-24 20:33:50 +00:00
if ( ! k - > is_shift_pressed ( ) ) {
2015-05-25 04:46:45 +00:00
_play_bw_from_pressed ( ) ;
2020-05-14 14:41:43 +00:00
} else {
2015-05-25 04:46:45 +00:00
_play_bw_pressed ( ) ;
2020-05-14 14:41:43 +00:00
}
2020-09-17 01:40:00 +00:00
accept_event ( ) ;
2015-05-25 04:46:45 +00:00
} break ;
2021-08-13 21:31:57 +00:00
case Key : : S : {
2015-05-25 04:46:45 +00:00
_stop_pressed ( ) ;
2020-09-17 01:40:00 +00:00
accept_event ( ) ;
2015-05-25 04:46:45 +00:00
} break ;
2021-08-13 21:31:57 +00:00
case Key : : D : {
2021-04-24 20:33:50 +00:00
if ( ! k - > is_shift_pressed ( ) ) {
2015-05-25 04:46:45 +00:00
_play_from_pressed ( ) ;
2020-05-14 14:41:43 +00:00
} else {
2015-05-25 04:46:45 +00:00
_play_pressed ( ) ;
2020-05-14 14:41:43 +00:00
}
2020-09-17 01:40:00 +00:00
accept_event ( ) ;
2015-05-25 04:46:45 +00:00
} break ;
2021-06-20 17:12:33 +00:00
default :
break ;
2015-05-25 04:46:45 +00:00
}
}
}
2017-10-30 18:43:19 +00:00
void AnimationPlayerEditor : : _editor_visibility_changed ( ) {
2022-04-07 11:49:28 +00:00
if ( is_visible ( ) & & animation - > has_selectable_items ( ) ) {
2017-10-30 18:43:19 +00:00
_start_onion_skinning ( ) ;
}
}
bool AnimationPlayerEditor : : _are_onion_layers_valid ( ) {
ERR_FAIL_COND_V ( ! onion . past & & ! onion . future , false ) ;
2023-09-27 14:37:26 +00:00
Size2 capture_size = DisplayServer : : get_singleton ( ) - > window_get_size ( DisplayServer : : MAIN_WINDOW_ID ) ;
return onion . captures . size ( ) = = onion . get_capture_count ( ) & & onion . capture_size = = capture_size ;
2017-10-30 18:43:19 +00:00
}
void AnimationPlayerEditor : : _allocate_onion_layers ( ) {
_free_onion_layers ( ) ;
2023-09-27 14:37:26 +00:00
int captures = onion . get_capture_count ( ) ;
Size2 capture_size = DisplayServer : : get_singleton ( ) - > window_get_size ( DisplayServer : : MAIN_WINDOW_ID ) ;
2017-10-30 18:43:19 +00:00
onion . captures . resize ( captures ) ;
onion . captures_valid . resize ( captures ) ;
for ( int i = 0 ; i < captures ; i + + ) {
bool is_present = onion . differences_only & & i = = captures - 1 ;
2019-06-19 03:51:06 +00:00
// Each capture is a viewport with a canvas item attached that renders a full-size rect with the contents of the main viewport.
2023-09-27 14:37:26 +00:00
onion . captures [ i ] = RS : : get_singleton ( ) - > viewport_create ( ) ;
2019-08-18 22:40:52 +00:00
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > viewport_set_size ( onion . captures [ i ] , capture_size . width , capture_size . height ) ;
RS : : get_singleton ( ) - > viewport_set_update_mode ( onion . captures [ i ] , RS : : VIEWPORT_UPDATE_ALWAYS ) ;
RS : : get_singleton ( ) - > viewport_set_transparent_background ( onion . captures [ i ] , ! is_present ) ;
RS : : get_singleton ( ) - > viewport_attach_canvas ( onion . captures [ i ] , onion . capture . canvas ) ;
2017-10-30 18:43:19 +00:00
}
2019-06-19 03:51:06 +00:00
// Reset the capture canvas item to the current root viewport texture (defensive).
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > canvas_item_clear ( onion . capture . canvas_item ) ;
2023-09-27 14:37:26 +00:00
RS : : get_singleton ( ) - > canvas_item_add_texture_rect ( onion . capture . canvas_item , Rect2 ( Point2 ( ) , Point2 ( capture_size . x , - capture_size . y ) ) , get_tree ( ) - > get_root ( ) - > get_texture ( ) - > get_rid ( ) ) ;
2017-10-30 18:43:19 +00:00
onion . capture_size = capture_size ;
}
void AnimationPlayerEditor : : _free_onion_layers ( ) {
2023-09-27 14:37:26 +00:00
for ( uint32_t i = 0 ; i < onion . captures . size ( ) ; i + + ) {
2017-10-30 18:43:19 +00:00
if ( onion . captures [ i ] . is_valid ( ) ) {
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > free ( onion . captures [ i ] ) ;
2017-10-30 18:43:19 +00:00
}
}
onion . captures . clear ( ) ;
onion . captures_valid . clear ( ) ;
}
void AnimationPlayerEditor : : _prepare_onion_layers_1 ( ) {
2019-06-19 03:51:06 +00:00
// This would be called per viewport and we want to act once only.
2022-09-29 09:53:28 +00:00
int64_t cur_frame = get_tree ( ) - > get_frame ( ) ;
if ( cur_frame = = onion . last_frame ) {
2017-10-30 18:43:19 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-10-30 18:43:19 +00:00
2023-09-27 14:37:26 +00:00
if ( ! onion . enabled | | ! is_visible ( ) | | ! get_player ( ) | | ! get_player ( ) - > has_animation ( SceneStringNames : : get_singleton ( ) - > RESET ) ) {
2017-10-30 18:43:19 +00:00
_stop_onion_skinning ( ) ;
return ;
}
2022-09-29 09:53:28 +00:00
onion . last_frame = cur_frame ;
2017-10-30 18:43:19 +00:00
2019-06-19 03:51:06 +00:00
// Refresh viewports with no onion layers overlaid.
2017-10-30 18:43:19 +00:00
onion . can_overlay = false ;
plugin - > update_overlays ( ) ;
2020-05-14 14:41:43 +00:00
if ( player - > is_playing ( ) ) {
2017-10-30 18:43:19 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-10-30 18:43:19 +00:00
2019-06-19 03:51:06 +00:00
// And go to next step afterwards.
2023-09-27 14:37:26 +00:00
callable_mp ( this , & AnimationPlayerEditor : : _prepare_onion_layers_2_prolog ) . call_deferred ( ) ;
2017-10-30 18:43:19 +00:00
}
2023-09-27 14:37:26 +00:00
void AnimationPlayerEditor : : _prepare_onion_layers_2_prolog ( ) {
2018-01-14 10:28:57 +00:00
Ref < Animation > anim = player - > get_animation ( player - > get_assigned_animation ( ) ) ;
2020-05-14 14:41:43 +00:00
if ( ! anim . is_valid ( ) ) {
2017-10-30 18:43:19 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-10-30 18:43:19 +00:00
2020-05-14 14:41:43 +00:00
if ( ! _are_onion_layers_valid ( ) ) {
2017-10-30 18:43:19 +00:00
_allocate_onion_layers ( ) ;
2020-05-14 14:41:43 +00:00
}
2017-10-30 18:43:19 +00:00
2019-06-19 03:51:06 +00:00
// Hide superfluous elements that would make the overlay unnecessary cluttered.
2020-03-26 21:49:16 +00:00
if ( Node3DEditor : : get_singleton ( ) - > is_visible ( ) ) {
2017-10-30 18:43:19 +00:00
// 3D
2023-09-27 14:37:26 +00:00
onion . temp . spatial_edit_state = Node3DEditor : : get_singleton ( ) - > get_state ( ) ;
Dictionary new_state = onion . temp . spatial_edit_state . duplicate ( ) ;
2017-10-30 18:43:19 +00:00
new_state [ " show_grid " ] = false ;
new_state [ " show_origin " ] = false ;
2023-09-27 14:37:26 +00:00
Array orig_vp = onion . temp . spatial_edit_state [ " viewports " ] ;
2017-10-30 18:43:19 +00:00
Array vp ;
vp . resize ( 4 ) ;
for ( int i = 0 ; i < vp . size ( ) ; i + + ) {
2017-12-17 22:07:15 +00:00
Dictionary d = ( ( Dictionary ) orig_vp [ i ] ) . duplicate ( ) ;
2017-10-30 18:43:19 +00:00
d [ " use_environment " ] = false ;
d [ " doppler " ] = false ;
2023-09-27 14:37:26 +00:00
d [ " listener " ] = false ;
2017-10-30 18:43:19 +00:00
d [ " gizmos " ] = onion . include_gizmos ? d [ " gizmos " ] : Variant ( false ) ;
d [ " information " ] = false ;
vp [ i ] = d ;
}
new_state [ " viewports " ] = vp ;
2019-06-19 03:51:06 +00:00
// TODO: Save/restore only affected entries.
2020-03-26 21:49:16 +00:00
Node3DEditor : : get_singleton ( ) - > set_state ( new_state ) ;
2023-09-27 14:37:26 +00:00
} else {
// CanvasItemEditor.
onion . temp . canvas_edit_state = CanvasItemEditor : : get_singleton ( ) - > get_state ( ) ;
Dictionary new_state = onion . temp . canvas_edit_state . duplicate ( ) ;
new_state [ " show_origin " ] = false ;
2017-10-30 18:43:19 +00:00
new_state [ " show_grid " ] = false ;
new_state [ " show_rulers " ] = false ;
new_state [ " show_guides " ] = false ;
new_state [ " show_helpers " ] = false ;
2019-05-20 20:36:34 +00:00
new_state [ " show_zoom_control " ] = false ;
2023-09-27 14:37:26 +00:00
new_state [ " show_edit_locks " ] = false ;
new_state [ " grid_visibility " ] = 2 ; // TODO: Expose CanvasItemEditor::GRID_VISIBILITY_HIDE somehow and use it.
new_state [ " show_transformation_gizmos " ] = onion . include_gizmos ? new_state [ " gizmos " ] : Variant ( false ) ;
2019-06-19 03:51:06 +00:00
// TODO: Save/restore only affected entries.
2017-10-30 18:43:19 +00:00
CanvasItemEditor : : get_singleton ( ) - > set_state ( new_state ) ;
}
2019-06-19 03:51:06 +00:00
// Tweak the root viewport to ensure it's rendered before our target.
2017-10-30 18:43:19 +00:00
RID root_vp = get_tree ( ) - > get_root ( ) - > get_viewport_rid ( ) ;
2023-09-27 14:37:26 +00:00
onion . temp . screen_rect = Rect2 ( Vector2 ( ) , DisplayServer : : get_singleton ( ) - > window_get_size ( DisplayServer : : MAIN_WINDOW_ID ) ) ;
RS : : get_singleton ( ) - > viewport_attach_to_screen ( root_vp , Rect2 ( ) , DisplayServer : : INVALID_WINDOW_ID ) ;
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > viewport_set_update_mode ( root_vp , RS : : VIEWPORT_UPDATE_ALWAYS ) ;
2017-10-30 18:43:19 +00:00
RID present_rid ;
if ( onion . differences_only ) {
2019-06-19 03:51:06 +00:00
// Capture present scene as it is.
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > canvas_item_set_material ( onion . capture . canvas_item , RID ( ) ) ;
2017-10-30 18:43:19 +00:00
present_rid = onion . captures [ onion . captures . size ( ) - 1 ] ;
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > viewport_set_active ( present_rid , true ) ;
RS : : get_singleton ( ) - > viewport_set_parent_viewport ( root_vp , present_rid ) ;
RS : : get_singleton ( ) - > draw ( false ) ;
RS : : get_singleton ( ) - > viewport_set_active ( present_rid , false ) ;
2017-10-30 18:43:19 +00:00
}
2019-06-19 03:51:06 +00:00
// Backup current animation state.
2023-09-27 14:37:26 +00:00
onion . temp . anim_values_backup = player - > make_backup ( ) ;
onion . temp . anim_player_position = player - > get_current_animation_position ( ) ;
2017-10-30 18:43:19 +00:00
2019-06-19 03:51:06 +00:00
// Render every past/future step with the capture shader.
2017-10-30 18:43:19 +00:00
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > canvas_item_set_material ( onion . capture . canvas_item , onion . capture . material - > get_rid ( ) ) ;
2022-08-27 09:22:43 +00:00
onion . capture . material - > set_shader_parameter ( " bkg_color " , GLOBAL_GET ( " rendering/environment/defaults/default_clear_color " ) ) ;
onion . capture . material - > set_shader_parameter ( " differences_only " , onion . differences_only ) ;
onion . capture . material - > set_shader_parameter ( " present " , onion . differences_only ? RS : : get_singleton ( ) - > viewport_get_texture ( present_rid ) : RID ( ) ) ;
onion . capture . material - > set_shader_parameter ( " dir_color " , onion . force_white_modulate ? Color ( 1 , 1 , 1 ) : Color ( EDITOR_GET ( " editors/animation/onion_layers_past_color " ) ) ) ;
2017-10-30 18:43:19 +00:00
2023-09-27 14:37:26 +00:00
uint32_t p_capture_idx = 0 ;
int first_step_offset = onion . past ? - ( int ) onion . steps : 0 ;
_prepare_onion_layers_2_step_prepare ( first_step_offset , p_capture_idx ) ;
}
void AnimationPlayerEditor : : _prepare_onion_layers_2_step_prepare ( int p_step_offset , uint32_t p_capture_idx ) {
uint32_t next_capture_idx = p_capture_idx ;
if ( p_step_offset = = 0 ) {
// Skip present step and switch to the color of future.
if ( ! onion . force_white_modulate ) {
onion . capture . material - > set_shader_parameter ( " dir_color " , EDITOR_GET ( " editors/animation/onion_layers_future_color " ) ) ;
}
} else {
Ref < Animation > anim = player - > get_animation ( player - > get_assigned_animation ( ) ) ;
double pos = onion . temp . anim_player_position + p_step_offset * anim - > get_step ( ) ;
2017-10-30 18:43:19 +00:00
2022-05-04 18:53:48 +00:00
bool valid = anim - > get_loop_mode ( ) ! = Animation : : LOOP_NONE | | ( pos > = 0 & & pos < = anim - > get_length ( ) ) ;
2023-09-27 14:37:26 +00:00
onion . captures_valid [ p_capture_idx ] = valid ;
2017-10-30 18:43:19 +00:00
if ( valid ) {
2023-10-16 09:47:14 +00:00
player - > seek ( pos , true , true ) ;
2023-09-27 14:37:26 +00:00
OS : : get_singleton ( ) - > get_main_loop ( ) - > process ( 0 ) ;
// This is the key: process the frame and let all callbacks/updates/notifications happen
// so everything (transforms, skeletons, etc.) is up-to-date visually.
callable_mp ( this , & AnimationPlayerEditor : : _prepare_onion_layers_2_step_capture ) . bind ( p_step_offset , p_capture_idx ) . call_deferred ( ) ;
return ;
} else {
next_capture_idx + + ;
2017-10-30 18:43:19 +00:00
}
2023-09-27 14:37:26 +00:00
}
int last_step_offset = onion . future ? onion . steps : 0 ;
if ( p_step_offset < last_step_offset ) {
_prepare_onion_layers_2_step_prepare ( p_step_offset + 1 , next_capture_idx ) ;
} else {
_prepare_onion_layers_2_epilog ( ) ;
}
}
void AnimationPlayerEditor : : _prepare_onion_layers_2_step_capture ( int p_step_offset , uint32_t p_capture_idx ) {
DEV_ASSERT ( p_step_offset ! = 0 ) ;
DEV_ASSERT ( onion . captures_valid [ p_capture_idx ] ) ;
2017-10-30 18:43:19 +00:00
2023-09-27 14:37:26 +00:00
RID root_vp = get_tree ( ) - > get_root ( ) - > get_viewport_rid ( ) ;
RS : : get_singleton ( ) - > viewport_set_active ( onion . captures [ p_capture_idx ] , true ) ;
RS : : get_singleton ( ) - > viewport_set_parent_viewport ( root_vp , onion . captures [ p_capture_idx ] ) ;
RS : : get_singleton ( ) - > draw ( false ) ;
RS : : get_singleton ( ) - > viewport_set_active ( onion . captures [ p_capture_idx ] , false ) ;
int last_step_offset = onion . future ? onion . steps : 0 ;
if ( p_step_offset < last_step_offset ) {
_prepare_onion_layers_2_step_prepare ( p_step_offset + 1 , p_capture_idx + 1 ) ;
} else {
_prepare_onion_layers_2_epilog ( ) ;
2017-10-30 18:43:19 +00:00
}
2023-09-27 14:37:26 +00:00
}
2017-10-30 18:43:19 +00:00
2023-09-27 14:37:26 +00:00
void AnimationPlayerEditor : : _prepare_onion_layers_2_epilog ( ) {
2019-06-19 03:51:06 +00:00
// Restore root viewport.
2023-09-27 14:37:26 +00:00
RID root_vp = get_tree ( ) - > get_root ( ) - > get_viewport_rid ( ) ;
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > viewport_set_parent_viewport ( root_vp , RID ( ) ) ;
2023-09-27 14:37:26 +00:00
RS : : get_singleton ( ) - > viewport_attach_to_screen ( root_vp , onion . temp . screen_rect , DisplayServer : : MAIN_WINDOW_ID ) ;
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > viewport_set_update_mode ( root_vp , RS : : VIEWPORT_UPDATE_WHEN_VISIBLE ) ;
2017-10-30 18:43:19 +00:00
2023-09-27 14:37:26 +00:00
// Restore animation state.
// Here we're combine the power of seeking back to the original position and
// restoring the values backup. In most cases they will bring the same value back,
// but there are cases handled by one that the other can't.
// Namely:
// - Seeking won't restore any values that may have been modified by the user
// in the node after the last time the AnimationPlayer updated it.
// - Restoring the backup won't account for values that are not directly involved
// in the animation but a consequence of them (e.g., SkeletonModification2DLookAt).
// FIXME: Since backup of values is based on the reset animation, only values
// backed by a proper reset animation will work correctly with onion
// skinning and the possibility to restore the values mentioned in the
// first point above is gone. Still good enough.
player - > seek ( onion . temp . anim_player_position , true , true ) ;
player - > restore ( onion . temp . anim_values_backup ) ;
2017-10-30 18:43:19 +00:00
2019-09-04 05:20:57 +00:00
// Restore state of main editors.
2020-03-26 21:49:16 +00:00
if ( Node3DEditor : : get_singleton ( ) - > is_visible ( ) ) {
2017-10-30 18:43:19 +00:00
// 3D
2023-09-27 14:37:26 +00:00
Node3DEditor : : get_singleton ( ) - > set_state ( onion . temp . spatial_edit_state ) ;
2017-10-30 18:43:19 +00:00
} else { // CanvasItemEditor
// 2D
2023-09-27 14:37:26 +00:00
CanvasItemEditor : : get_singleton ( ) - > set_state ( onion . temp . canvas_edit_state ) ;
2017-10-30 18:43:19 +00:00
}
2019-06-19 03:51:06 +00:00
// Update viewports with skin layers overlaid for the actual engine loop render.
2017-10-30 18:43:19 +00:00
onion . can_overlay = true ;
plugin - > update_overlays ( ) ;
}
void AnimationPlayerEditor : : _start_onion_skinning ( ) {
2023-09-27 14:37:26 +00:00
if ( get_player ( ) & & ! get_player ( ) - > has_animation ( SceneStringNames : : get_singleton ( ) - > RESET ) ) {
onion . enabled = false ;
onion_toggle - > set_pressed_no_signal ( false ) ;
return ;
}
if ( ! get_tree ( ) - > is_connected ( SNAME ( " process_frame " ) , callable_mp ( this , & AnimationPlayerEditor : : _prepare_onion_layers_1 ) ) ) {
get_tree ( ) - > connect ( SNAME ( " process_frame " ) , callable_mp ( this , & AnimationPlayerEditor : : _prepare_onion_layers_1 ) ) ;
2017-10-30 18:43:19 +00:00
}
}
void AnimationPlayerEditor : : _stop_onion_skinning ( ) {
2023-09-27 14:37:26 +00:00
if ( get_tree ( ) - > is_connected ( SNAME ( " process_frame " ) , callable_mp ( this , & AnimationPlayerEditor : : _prepare_onion_layers_1 ) ) ) {
get_tree ( ) - > disconnect ( SNAME ( " process_frame " ) , callable_mp ( this , & AnimationPlayerEditor : : _prepare_onion_layers_1 ) ) ;
2017-10-30 18:43:19 +00:00
_free_onion_layers ( ) ;
2023-09-27 14:37:26 +00:00
// Clean up.
2017-10-30 18:43:19 +00:00
onion . can_overlay = false ;
plugin - > update_overlays ( ) ;
2023-09-27 14:37:26 +00:00
onion . temp = { } ;
2017-10-30 18:43:19 +00:00
}
}
2018-06-07 15:46:14 +00:00
void AnimationPlayerEditor : : _pin_pressed ( ) {
2021-11-17 20:08:55 +00:00
SceneTreeDock : : get_singleton ( ) - > get_tree_editor ( ) - > update_tree ( ) ;
2018-06-07 15:46:14 +00:00
}
2023-07-20 15:34:06 +00:00
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 ;
}
2022-11-17 06:24:00 +00:00
bool AnimationPlayerEditor : : _validate_tracks ( const Ref < Animation > p_anim ) {
bool is_valid = true ;
if ( ! p_anim . is_valid ( ) ) {
return true ; // There is a problem outside of the animation track.
}
int len = p_anim - > get_track_count ( ) ;
for ( int i = 0 ; i < len ; i + + ) {
Animation : : TrackType ttype = p_anim - > track_get_type ( i ) ;
if ( ttype = = Animation : : TYPE_ROTATION_3D ) {
int key_len = p_anim - > track_get_key_count ( i ) ;
for ( int j = 0 ; j < key_len ; j + + ) {
Quaternion q ;
p_anim - > rotation_track_get_key ( i , j , & q ) ;
2023-02-21 02:26:23 +00:00
ERR_BREAK_EDMSG ( ! q . is_normalized ( ) , " AnimationPlayer: ' " + player - > get_name ( ) + " ', Animation: ' " + player - > get_current_animation ( ) + " ', 3D Rotation Track: ' " + p_anim - > track_get_path ( i ) + " ' contains unnormalized Quaternion key. " ) ;
2022-11-17 06:24:00 +00:00
}
} else if ( ttype = = Animation : : TYPE_VALUE ) {
int key_len = p_anim - > track_get_key_count ( i ) ;
if ( key_len = = 0 ) {
continue ;
}
switch ( p_anim - > track_get_key_value ( i , 0 ) . get_type ( ) ) {
case Variant : : QUATERNION : {
for ( int j = 0 ; j < key_len ; j + + ) {
Quaternion q = Quaternion ( p_anim - > track_get_key_value ( i , j ) ) ;
if ( ! q . is_normalized ( ) ) {
is_valid = false ;
2023-02-21 02:26:23 +00:00
ERR_BREAK_EDMSG ( true , " AnimationPlayer: ' " + player - > get_name ( ) + " ', Animation: ' " + player - > get_current_animation ( ) + " ', Value Track: ' " + p_anim - > track_get_path ( i ) + " ' contains unnormalized Quaternion key. " ) ;
2022-11-17 06:24:00 +00:00
}
}
} break ;
case Variant : : TRANSFORM3D : {
for ( int j = 0 ; j < key_len ; j + + ) {
Transform3D t = Transform3D ( p_anim - > track_get_key_value ( i , j ) ) ;
if ( ! t . basis . orthonormalized ( ) . is_rotation ( ) ) {
is_valid = false ;
2023-02-21 02:26:23 +00:00
ERR_BREAK_EDMSG ( true , " AnimationPlayer: ' " + player - > get_name ( ) + " ', Animation: ' " + player - > get_current_animation ( ) + " ', Value Track: ' " + p_anim - > track_get_path ( i ) + " ' contains corrupted basis (some axes are too close other axis or scaled by zero) Transform3D key. " ) ;
2022-11-17 06:24:00 +00:00
}
}
} break ;
default : {
} break ;
}
}
}
return is_valid ;
}
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditor : : _bind_methods ( ) {
2023-10-30 20:51:36 +00:00
// Needed for UndoRedo.
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " _animation_player_changed " ) , & AnimationPlayerEditor : : _animation_player_changed ) ;
2019-06-19 03:51:06 +00:00
ClassDB : : bind_method ( D_METHOD ( " _start_onion_skinning " ) , & AnimationPlayerEditor : : _start_onion_skinning ) ;
ClassDB : : bind_method ( D_METHOD ( " _stop_onion_skinning " ) , & AnimationPlayerEditor : : _stop_onion_skinning ) ;
2022-10-10 07:45:52 +00:00
ADD_SIGNAL ( MethodInfo ( " animation_selected " , PropertyInfo ( Variant : : STRING , " name " ) ) ) ;
2014-02-10 01:10:30 +00:00
}
2020-04-01 23:20:12 +00:00
AnimationPlayerEditor * AnimationPlayerEditor : : singleton = nullptr ;
2015-12-05 17:18:22 +00:00
AnimationPlayer * AnimationPlayerEditor : : get_player ( ) const {
return player ;
}
2017-10-30 18:43:19 +00:00
2023-07-20 15:34:06 +00:00
AnimationMixer * AnimationPlayerEditor : : get_editing_node ( ) const {
return original_node ;
}
2022-01-27 09:36:51 +00:00
AnimationPlayerEditor : : AnimationPlayerEditor ( AnimationPlayerEditorPlugin * p_plugin ) {
2017-10-30 18:43:19 +00:00
plugin = p_plugin ;
2015-12-05 17:18:22 +00:00
singleton = this ;
2014-02-10 01:10:30 +00:00
set_focus_mode ( FOCUS_ALL ) ;
2023-10-30 20:51:36 +00:00
set_process_shortcut_input ( true ) ;
2014-02-10 01:10:30 +00:00
HBoxContainer * hb = memnew ( HBoxContainer ) ;
add_child ( hb ) ;
2020-06-19 18:49:04 +00:00
play_bw_from = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
play_bw_from - > set_theme_type_variation ( " FlatButton " ) ;
2022-08-25 10:42:17 +00:00
play_bw_from - > set_tooltip_text ( TTR ( " Play selected animation backwards from current pos. (A) " ) ) ;
2016-01-17 23:03:57 +00:00
hb - > add_child ( play_bw_from ) ;
2020-06-19 18:49:04 +00:00
play_bw = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
play_bw - > set_theme_type_variation ( " FlatButton " ) ;
2022-08-25 10:42:17 +00:00
play_bw - > set_tooltip_text ( TTR ( " Play selected animation backwards from end. (Shift+A) " ) ) ;
2016-01-17 23:03:57 +00:00
hb - > add_child ( play_bw ) ;
2020-06-19 18:49:04 +00:00
stop = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
stop - > set_theme_type_variation ( " FlatButton " ) ;
2016-01-17 23:03:57 +00:00
hb - > add_child ( stop ) ;
2023-01-21 05:51:03 +00:00
stop - > set_tooltip_text ( TTR ( " Pause/stop animation playback. (S) " ) ) ;
2016-01-17 23:03:57 +00:00
2020-06-19 18:49:04 +00:00
play = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
play - > set_theme_type_variation ( " FlatButton " ) ;
2022-08-25 10:42:17 +00:00
play - > set_tooltip_text ( TTR ( " Play selected animation from start. (Shift+D) " ) ) ;
2016-01-17 23:03:57 +00:00
hb - > add_child ( play ) ;
2020-06-19 18:49:04 +00:00
play_from = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
play_from - > set_theme_type_variation ( " FlatButton " ) ;
2022-08-25 10:42:17 +00:00
play_from - > set_tooltip_text ( TTR ( " Play selected animation from current pos. (D) " ) ) ;
2016-01-17 23:03:57 +00:00
hb - > add_child ( play_from ) ;
frame = memnew ( SpinBox ) ;
hb - > add_child ( frame ) ;
2022-05-28 04:25:23 +00:00
frame - > set_custom_minimum_size ( Size2 ( 80 , 0 ) * EDSCALE ) ;
2016-01-17 23:03:57 +00:00
frame - > set_stretch_ratio ( 2 ) ;
2019-08-06 10:24:08 +00:00
frame - > set_step ( 0.0001 ) ;
2022-08-25 10:42:17 +00:00
frame - > set_tooltip_text ( TTR ( " Animation position (in seconds). " ) ) ;
2016-01-17 23:03:57 +00:00
hb - > add_child ( memnew ( VSeparator ) ) ;
scale = memnew ( LineEdit ) ;
hb - > add_child ( scale ) ;
scale - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
scale - > set_stretch_ratio ( 1 ) ;
2022-08-25 10:42:17 +00:00
scale - > set_tooltip_text ( TTR ( " Scale animation playback globally for the node. " ) ) ;
2016-01-17 23:03:57 +00:00
scale - > hide ( ) ;
2017-01-25 20:22:16 +00:00
delete_dialog = memnew ( ConfirmationDialog ) ;
add_child ( delete_dialog ) ;
2023-07-20 15:34:06 +00:00
delete_dialog - > connect ( SNAME ( " confirmed " ) , callable_mp ( this , & AnimationPlayerEditor : : _animation_remove_confirmed ) ) ;
2017-01-25 20:22:16 +00:00
2018-06-07 15:46:14 +00:00
tool_anim = memnew ( MenuButton ) ;
2020-09-17 01:40:00 +00:00
tool_anim - > set_shortcut_context ( this ) ;
2018-06-07 15:46:14 +00:00
tool_anim - > set_flat ( false ) ;
2022-08-25 10:42:17 +00:00
tool_anim - > set_tooltip_text ( TTR ( " Animation Tools " ) ) ;
2018-06-08 17:44:18 +00:00
tool_anim - > set_text ( TTR ( " Animation " ) ) ;
2024-02-17 13:14:53 +00:00
tool_anim - > get_popup ( ) - > add_shortcut ( ED_SHORTCUT ( " animation_player_editor/new_animation " , TTR ( " New... " ) ) , TOOL_NEW_ANIM ) ;
2018-06-07 15:46:14 +00:00
tool_anim - > get_popup ( ) - > add_separator ( ) ;
2022-04-07 11:49:28 +00:00
tool_anim - > get_popup ( ) - > add_shortcut ( ED_SHORTCUT ( " animation_player_editor/animation_libraries " , TTR ( " Manage Animations... " ) ) , TOOL_ANIM_LIBRARY ) ;
2018-06-07 15:46:14 +00:00
tool_anim - > get_popup ( ) - > add_separator ( ) ;
2022-02-14 06:28:42 +00:00
tool_anim - > get_popup ( ) - > add_shortcut ( ED_SHORTCUT ( " animation_player_editor/duplicate_animation " , TTR ( " Duplicate... " ) ) , TOOL_DUPLICATE_ANIM ) ;
2018-06-07 15:46:14 +00:00
tool_anim - > get_popup ( ) - > add_separator ( ) ;
tool_anim - > get_popup ( ) - > add_shortcut ( ED_SHORTCUT ( " animation_player_editor/rename_animation " , TTR ( " Rename... " ) ) , TOOL_RENAME_ANIM ) ;
tool_anim - > get_popup ( ) - > add_shortcut ( ED_SHORTCUT ( " animation_player_editor/edit_transitions " , TTR ( " Edit Transitions... " ) ) , TOOL_EDIT_TRANSITIONS ) ;
2018-07-01 20:44:15 +00:00
tool_anim - > get_popup ( ) - > add_shortcut ( ED_SHORTCUT ( " animation_player_editor/open_animation_in_inspector " , TTR ( " Open in Inspector " ) ) , TOOL_EDIT_RESOURCE ) ;
2018-06-07 15:46:14 +00:00
tool_anim - > get_popup ( ) - > add_separator ( ) ;
tool_anim - > get_popup ( ) - > add_shortcut ( ED_SHORTCUT ( " animation_player_editor/remove_animation " , TTR ( " Remove " ) ) , TOOL_REMOVE_ANIM ) ;
2022-04-07 11:49:28 +00:00
tool_anim - > set_disabled ( true ) ;
2018-06-07 15:46:14 +00:00
hb - > add_child ( tool_anim ) ;
2015-05-25 04:46:45 +00:00
2014-02-10 01:10:30 +00:00
animation = memnew ( OptionButton ) ;
hb - > add_child ( animation ) ;
animation - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
2022-08-25 10:42:17 +00:00
animation - > set_tooltip_text ( TTR ( " Display list of animations in player. " ) ) ;
2016-06-29 23:11:14 +00:00
animation - > set_clip_text ( true ) ;
2024-01-23 21:29:45 +00:00
animation - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2014-02-10 01:10:30 +00:00
2020-06-19 18:49:04 +00:00
autoplay = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
autoplay - > set_theme_type_variation ( " FlatButton " ) ;
2014-02-10 01:10:30 +00:00
hb - > add_child ( autoplay ) ;
2022-08-25 10:42:17 +00:00
autoplay - > set_tooltip_text ( TTR ( " Autoplay on Load " ) ) ;
2014-02-10 01:10:30 +00:00
2018-06-09 18:49:16 +00:00
hb - > add_child ( memnew ( VSeparator ) ) ;
2018-06-07 15:46:14 +00:00
track_editor = memnew ( AnimationTrackEditor ) ;
hb - > add_child ( track_editor - > get_edit_menu ( ) ) ;
2014-02-10 01:10:30 +00:00
2019-05-14 03:42:05 +00:00
hb - > add_child ( memnew ( VSeparator ) ) ;
2020-06-19 18:49:04 +00:00
onion_toggle = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
onion_toggle - > set_theme_type_variation ( " FlatButton " ) ;
2019-05-14 03:42:05 +00:00
onion_toggle - > set_toggle_mode ( true ) ;
2022-08-25 10:42:17 +00:00
onion_toggle - > set_tooltip_text ( TTR ( " Enable Onion Skinning " ) ) ;
2023-07-20 15:34:06 +00:00
onion_toggle - > connect ( SNAME ( " pressed " ) , callable_mp ( this , & AnimationPlayerEditor : : _onion_skinning_menu ) . bind ( ONION_SKINNING_ENABLE ) ) ;
2019-05-14 03:42:05 +00:00
hb - > add_child ( onion_toggle ) ;
2017-10-30 18:43:19 +00:00
onion_skinning = memnew ( MenuButton ) ;
2023-12-21 00:52:20 +00:00
onion_skinning - > set_flat ( false ) ;
onion_skinning - > set_theme_type_variation ( " FlatMenuButton " ) ;
2022-08-25 10:42:17 +00:00
onion_skinning - > set_tooltip_text ( TTR ( " Onion Skinning Options " ) ) ;
2019-05-14 03:42:05 +00:00
onion_skinning - > get_popup ( ) - > add_separator ( TTR ( " Directions " ) ) ;
2022-06-08 09:42:51 +00:00
// TRANSLATORS: Opposite of "Future", refers to a direction in animation onion skinning.
2017-10-30 18:43:19 +00:00
onion_skinning - > get_popup ( ) - > add_check_item ( TTR ( " Past " ) , ONION_SKINNING_PAST ) ;
2022-03-12 00:06:45 +00:00
onion_skinning - > get_popup ( ) - > set_item_checked ( - 1 , true ) ;
2022-06-08 09:42:51 +00:00
// TRANSLATORS: Opposite of "Past", refers to a direction in animation onion skinning.
2017-10-30 18:43:19 +00:00
onion_skinning - > get_popup ( ) - > add_check_item ( TTR ( " Future " ) , ONION_SKINNING_FUTURE ) ;
2019-05-14 03:42:05 +00:00
onion_skinning - > get_popup ( ) - > add_separator ( TTR ( " Depth " ) ) ;
2018-03-24 02:05:20 +00:00
onion_skinning - > get_popup ( ) - > add_radio_check_item ( TTR ( " 1 step " ) , ONION_SKINNING_1_STEP ) ;
2022-03-12 00:06:45 +00:00
onion_skinning - > get_popup ( ) - > set_item_checked ( - 1 , true ) ;
2018-03-24 02:05:20 +00:00
onion_skinning - > get_popup ( ) - > add_radio_check_item ( TTR ( " 2 steps " ) , ONION_SKINNING_2_STEPS ) ;
onion_skinning - > get_popup ( ) - > add_radio_check_item ( TTR ( " 3 steps " ) , ONION_SKINNING_3_STEPS ) ;
2017-10-30 18:43:19 +00:00
onion_skinning - > get_popup ( ) - > add_separator ( ) ;
onion_skinning - > get_popup ( ) - > add_check_item ( TTR ( " Differences Only " ) , ONION_SKINNING_DIFFERENCES_ONLY ) ;
onion_skinning - > get_popup ( ) - > add_check_item ( TTR ( " Force White Modulate " ) , ONION_SKINNING_FORCE_WHITE_MODULATE ) ;
onion_skinning - > get_popup ( ) - > add_check_item ( TTR ( " Include Gizmos (3D) " ) , ONION_SKINNING_INCLUDE_GIZMOS ) ;
hb - > add_child ( onion_skinning ) ;
2019-05-14 03:42:05 +00:00
hb - > add_child ( memnew ( VSeparator ) ) ;
2020-06-19 18:49:04 +00:00
pin = memnew ( Button ) ;
2023-09-19 16:03:10 +00:00
pin - > set_theme_type_variation ( " FlatButton " ) ;
2016-01-17 23:03:57 +00:00
pin - > set_toggle_mode ( true ) ;
2022-08-25 10:42:17 +00:00
pin - > set_tooltip_text ( TTR ( " Pin AnimationPlayer " ) ) ;
2016-01-17 23:03:57 +00:00
hb - > add_child ( pin ) ;
2023-07-20 15:34:06 +00:00
pin - > connect ( SNAME ( " pressed " ) , callable_mp ( this , & AnimationPlayerEditor : : _pin_pressed ) ) ;
2014-02-10 01:10:30 +00:00
2015-06-06 12:44:38 +00:00
file = memnew ( EditorFileDialog ) ;
2014-02-10 01:10:30 +00:00
add_child ( file ) ;
name_dialog = memnew ( ConfirmationDialog ) ;
2016-05-04 01:25:37 +00:00
name_dialog - > set_title ( TTR ( " Create New Animation " ) ) ;
2014-02-10 01:10:30 +00:00
name_dialog - > set_hide_on_ok ( false ) ;
add_child ( name_dialog ) ;
2017-06-13 06:04:15 +00:00
VBoxContainer * vb = memnew ( VBoxContainer ) ;
name_dialog - > add_child ( vb ) ;
2014-02-10 01:10:30 +00:00
2019-05-25 01:47:16 +00:00
name_title = memnew ( Label ( TTR ( " Animation Name: " ) ) ) ;
vb - > add_child ( name_title ) ;
2014-02-10 01:10:30 +00:00
2022-04-07 11:49:28 +00:00
HBoxContainer * name_hb = memnew ( HBoxContainer ) ;
2017-06-13 06:04:15 +00:00
name = memnew ( LineEdit ) ;
2022-04-07 11:49:28 +00:00
name_hb - > add_child ( name ) ;
name - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
library = memnew ( OptionButton ) ;
name_hb - > add_child ( library ) ;
library - > hide ( ) ;
vb - > add_child ( name_hb ) ;
2017-06-13 06:04:15 +00:00
name_dialog - > register_text_enter ( name ) ;
2014-02-10 01:10:30 +00:00
error_dialog = memnew ( ConfirmationDialog ) ;
2022-07-08 00:31:19 +00:00
error_dialog - > set_ok_button_text ( TTR ( " Close " ) ) ;
2018-04-27 01:58:30 +00:00
error_dialog - > set_title ( TTR ( " Error! " ) ) ;
2014-02-10 01:10:30 +00:00
add_child ( error_dialog ) ;
2023-07-20 15:34:06 +00:00
name_dialog - > connect ( SNAME ( " confirmed " ) , callable_mp ( this , & AnimationPlayerEditor : : _animation_name_edited ) ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
blend_editor . dialog = memnew ( AcceptDialog ) ;
2023-10-30 20:51:36 +00:00
blend_editor . dialog - > set_title ( TTR ( " Cross-Animation Blend Times " ) ) ;
2022-07-08 00:31:19 +00:00
blend_editor . dialog - > set_ok_button_text ( TTR ( " Close " ) ) ;
2014-02-10 01:10:30 +00:00
blend_editor . dialog - > set_hide_on_ok ( true ) ;
2023-10-30 20:51:36 +00:00
add_child ( blend_editor . dialog ) ;
2014-02-10 01:10:30 +00:00
VBoxContainer * blend_vb = memnew ( VBoxContainer ) ;
blend_editor . dialog - > add_child ( blend_vb ) ;
2023-10-30 20:51:36 +00:00
2014-02-10 01:10:30 +00:00
blend_editor . tree = memnew ( Tree ) ;
2023-10-30 20:51:36 +00:00
blend_editor . tree - > set_hide_root ( true ) ;
2014-02-10 01:10:30 +00:00
blend_editor . tree - > set_columns ( 2 ) ;
2023-10-30 20:51:36 +00:00
blend_editor . tree - > set_column_expand_ratio ( 0 , 10 ) ;
blend_editor . tree - > set_column_clip_content ( 0 , true ) ;
blend_editor . tree - > set_column_expand_ratio ( 1 , 3 ) ;
blend_editor . tree - > set_column_clip_content ( 1 , true ) ;
2016-05-18 22:08:12 +00:00
blend_vb - > add_margin_child ( TTR ( " Blend Times: " ) , blend_editor . tree , true ) ;
2023-10-30 20:51:36 +00:00
blend_editor . tree - > connect ( SNAME ( " item_edited " ) , callable_mp ( this , & AnimationPlayerEditor : : _blend_edited ) ) ;
2016-05-07 16:27:52 +00:00
blend_editor . next = memnew ( OptionButton ) ;
2024-01-23 21:29:45 +00:00
blend_editor . next - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2016-05-04 01:25:37 +00:00
blend_vb - > add_margin_child ( TTR ( " Next (Auto Queue): " ) , blend_editor . next ) ;
2016-03-08 23:00:52 +00:00
2023-07-20 15:34:06 +00:00
autoplay - > connect ( SNAME ( " pressed " ) , callable_mp ( this , & AnimationPlayerEditor : : _autoplay_pressed ) ) ;
2014-02-10 01:10:30 +00:00
autoplay - > set_toggle_mode ( true ) ;
2023-07-20 15:34:06 +00:00
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 ) ) ;
2016-01-17 23:03:57 +00:00
2023-07-20 15:34:06 +00:00
animation - > connect ( SNAME ( " item_selected " ) , callable_mp ( this , & AnimationPlayerEditor : : _animation_selected ) ) ;
2018-06-07 15:46:14 +00:00
2023-11-21 21:14:32 +00:00
frame - > connect ( SNAME ( " value_changed " ) , callable_mp ( this , & AnimationPlayerEditor : : _seek_value_changed ) . bind ( false ) ) ;
2023-07-20 15:34:06 +00:00
scale - > connect ( SNAME ( " text_submitted " ) , callable_mp ( this , & AnimationPlayerEditor : : _scale_changed ) ) ;
2016-01-17 23:03:57 +00:00
2018-06-07 15:46:14 +00:00
add_child ( track_editor ) ;
track_editor - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
2023-07-20 15:34:06 +00:00
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 ) ) ;
2016-01-17 23:03:57 +00:00
_update_player ( ) ;
2017-10-30 18:43:19 +00:00
2022-04-07 11:49:28 +00:00
library_editor = memnew ( AnimationLibraryEditor ) ;
add_child ( library_editor ) ;
2023-07-20 15:34:06 +00:00
library_editor - > connect ( SNAME ( " update_editor " ) , callable_mp ( this , & AnimationPlayerEditor : : _animation_player_changed ) ) ;
2022-04-07 11:49:28 +00:00
2019-09-04 05:20:57 +00:00
// Onion skinning.
2017-10-30 18:43:19 +00:00
2023-07-20 15:34:06 +00:00
track_editor - > connect ( SNAME ( " visibility_changed " ) , callable_mp ( this , & AnimationPlayerEditor : : _editor_visibility_changed ) ) ;
2017-10-30 18:43:19 +00:00
2020-03-27 18:21:27 +00:00
onion . capture . canvas = RS : : get_singleton ( ) - > canvas_create ( ) ;
onion . capture . canvas_item = RS : : get_singleton ( ) - > canvas_item_create ( ) ;
RS : : get_singleton ( ) - > canvas_item_set_parent ( onion . capture . canvas_item , onion . capture . canvas ) ;
2017-10-30 18:43:19 +00:00
2023-09-27 14:37:26 +00:00
onion . capture . material . instantiate ( ) ;
2017-10-30 18:43:19 +00:00
2023-09-27 14:37:26 +00:00
onion . capture . shader . instantiate ( ) ;
2021-07-19 06:06:51 +00:00
onion . capture . shader - > set_code ( R " (
2021-08-18 01:09:22 +00:00
// Animation editor onion skinning shader.
2021-07-19 06:06:51 +00:00
shader_type canvas_item ;
uniform vec4 bkg_color ;
uniform vec4 dir_color ;
uniform bool differences_only ;
uniform sampler2D present ;
float zero_if_equal ( vec4 a , vec4 b ) {
return smoothstep ( 0.0 , 0.005 , length ( a . rgb - b . rgb ) / sqrt ( 3.0 ) ) ;
}
void fragment ( ) {
vec4 capture_samp = texture ( TEXTURE , UV ) ;
float bkg_mask = zero_if_equal ( capture_samp , bkg_color ) ;
2023-09-27 14:37:26 +00:00
float diff_mask = 1.0 ;
if ( differences_only ) {
// FIXME: If Y-flips across render target, canvas item, etc. was handled correctly,
// this would not be as convoluted in the shader.
vec4 capture_samp2 = texture ( TEXTURE , vec2 ( UV . x , 1.0 - UV . y ) ) ;
vec4 present_samp = texture ( present , vec2 ( UV . x , 1.0 - UV . y ) ) ;
diff_mask = 1.0 - zero_if_equal ( present_samp , bkg_color ) ;
}
2021-07-19 06:06:51 +00:00
COLOR = vec4 ( capture_samp . rgb * dir_color . rgb , bkg_mask * diff_mask ) ;
}
) " );
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > material_set_shader ( onion . capture . material - > get_rid ( ) , onion . capture . shader - > get_rid ( ) ) ;
2017-10-30 18:43:19 +00:00
}
AnimationPlayerEditor : : ~ AnimationPlayerEditor ( ) {
_free_onion_layers ( ) ;
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > free ( onion . capture . canvas ) ;
RS : : get_singleton ( ) - > free ( onion . capture . canvas_item ) ;
2023-09-27 14:37:26 +00:00
onion . capture = { } ;
2017-10-30 18:43:19 +00:00
}
void AnimationPlayerEditorPlugin : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
2023-07-20 15:34:06 +00:00
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 ) ) ;
2017-10-30 18:43:19 +00:00
set_force_draw_over_forwarding_enabled ( ) ;
} break ;
}
2014-02-10 01:10:30 +00:00
}
2021-12-02 11:01:38 +00:00
void AnimationPlayerEditorPlugin : : _property_keyed ( const String & p_keyed , const Variant & p_value , bool p_advance ) {
2022-12-16 21:28:42 +00:00
AnimationTrackEditor * te = anim_editor - > get_track_editor ( ) ;
if ( ! te | | ! te - > has_keying ( ) ) {
2021-12-02 11:01:38 +00:00
return ;
}
2022-12-16 21:28:42 +00:00
te - > _clear_selection ( ) ;
te - > insert_value_key ( p_keyed , p_value , p_advance ) ;
2021-12-02 11:01:38 +00:00
}
void AnimationPlayerEditorPlugin : : _transform_key_request ( Object * sp , const String & p_sub , const Transform3D & p_key ) {
if ( ! anim_editor - > get_track_editor ( ) - > has_keying ( ) ) {
return ;
}
Node3D * s = Object : : cast_to < Node3D > ( sp ) ;
if ( ! s ) {
return ;
}
anim_editor - > get_track_editor ( ) - > insert_transform_key ( s , p_sub , Animation : : TYPE_POSITION_3D , p_key . origin ) ;
anim_editor - > get_track_editor ( ) - > insert_transform_key ( s , p_sub , Animation : : TYPE_ROTATION_3D , p_key . basis . get_rotation_quaternion ( ) ) ;
anim_editor - > get_track_editor ( ) - > insert_transform_key ( s , p_sub , Animation : : TYPE_SCALE_3D , p_key . basis . get_scale ( ) ) ;
}
void AnimationPlayerEditorPlugin : : _update_keying ( ) {
2021-11-17 20:08:55 +00:00
InspectorDock : : get_inspector_singleton ( ) - > set_keying ( anim_editor - > get_track_editor ( ) - > has_keying ( ) ) ;
2021-12-02 11:01:38 +00:00
}
2014-02-10 01:10:30 +00:00
void AnimationPlayerEditorPlugin : : edit ( Object * p_object ) {
2023-07-20 15:34:06 +00:00
if ( player & & anim_editor & & anim_editor - > is_pinned ( ) ) {
return ; // Ignore, pinned.
}
player = nullptr ;
2020-05-14 14:41:43 +00:00
if ( ! p_object ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2023-07-20 15:34:06 +00:00
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 ) {
2023-12-18 14:46:56 +00:00
callable_mp ( parent , & Node : : remove_child ) . call_deferred ( dummy_player ) ;
2023-07-20 15:34:06 +00:00
}
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 ) ;
2023-12-30 16:20:44 +00:00
dummy_player - > set_active ( false ) ; // Inactive as default, it will be activated if the AnimationPlayerEditor visibility is changed.
2023-07-20 15:34:06 +00:00
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 ( ) ;
}
2014-02-10 01:10:30 +00:00
}
bool AnimationPlayerEditorPlugin : : handles ( Object * p_object ) const {
2023-07-20 15:34:06 +00:00
return p_object - > is_class ( " AnimationPlayer " ) | | p_object - > is_class ( " AnimationTree " ) | | p_object - > is_class ( " AnimationMixer " ) ;
2014-02-10 01:10:30 +00:00
}
void AnimationPlayerEditorPlugin : : make_visible ( bool p_visible ) {
if ( p_visible ) {
2022-01-27 09:36:51 +00:00
EditorNode : : get_singleton ( ) - > make_bottom_panel_item_visible ( anim_editor ) ;
2014-02-10 01:10:30 +00:00
anim_editor - > set_process ( true ) ;
anim_editor - > ensure_visibility ( ) ;
}
}
2022-01-27 09:36:51 +00:00
AnimationPlayerEditorPlugin : : AnimationPlayerEditorPlugin ( ) {
anim_editor = memnew ( AnimationPlayerEditor ( this ) ) ;
2023-06-23 13:23:36 +00:00
EditorNode : : get_singleton ( ) - > add_bottom_panel_item ( TTR ( " Animation " ) , anim_editor ) ;
2014-02-10 01:10:30 +00:00
}
AnimationPlayerEditorPlugin : : ~ AnimationPlayerEditorPlugin ( ) {
2023-07-20 15:34:06 +00:00
if ( dummy_player ) {
memdelete ( dummy_player ) ;
}
2014-02-10 01:10:30 +00:00
}
2022-12-08 12:38:01 +00:00
// AnimationTrackKeyEditEditorPlugin
bool EditorInspectorPluginAnimationTrackKeyEdit : : can_handle ( Object * p_object ) {
return Object : : cast_to < AnimationTrackKeyEdit > ( p_object ) ! = nullptr ;
}
void EditorInspectorPluginAnimationTrackKeyEdit : : parse_begin ( Object * p_object ) {
AnimationTrackKeyEdit * atk = Object : : cast_to < AnimationTrackKeyEdit > ( p_object ) ;
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( atk ) ;
2022-12-08 12:38:01 +00:00
atk_editor = memnew ( AnimationTrackKeyEditEditor ( atk - > animation , atk - > track , atk - > key_ofs , atk - > use_fps ) ) ;
add_custom_control ( atk_editor ) ;
}
AnimationTrackKeyEditEditorPlugin : : AnimationTrackKeyEditEditorPlugin ( ) {
atk_plugin = memnew ( EditorInspectorPluginAnimationTrackKeyEdit ) ;
EditorInspector : : add_inspector_plugin ( atk_plugin ) ;
}
bool AnimationTrackKeyEditEditorPlugin : : handles ( Object * p_object ) const {
return p_object - > is_class ( " AnimationTrackKeyEdit " ) ;
}