2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* animation_player.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-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "animation_player.h"
2016-03-09 00:00:52 +01:00
2020-11-07 23:33:38 +01:00
# include "core/config/engine.h"
# include "core/object/message_queue.h"
2014-02-10 02:10:30 +01:00
# include "scene/scene_string_names.h"
2018-06-07 17:46:14 +02:00
# include "servers/audio/audio_stream.h"
2018-12-18 02:53:54 +01:00
2017-10-30 19:43:19 +01:00
# ifdef TOOLS_ENABLED
2020-12-20 11:46:44 +01:00
# include "editor/editor_node.h"
2022-03-25 18:06:46 +01:00
# include "editor/editor_undo_redo_manager.h"
2019-05-22 20:40:19 +02:00
# include "scene/2d/skeleton_2d.h"
2018-12-18 02:53:54 +01:00
2017-10-30 19:43:19 +01:00
void AnimatedValuesBackup : : update_skeletons ( ) {
for ( int i = 0 ; i < entries . size ( ) ; i + + ) {
if ( entries [ i ] . bone_idx ! = - 1 ) {
2019-05-22 20:40:19 +02:00
// 3D bone
2020-03-26 22:49:16 +01:00
Object : : cast_to < Skeleton3D > ( entries [ i ] . object ) - > notification ( Skeleton3D : : NOTIFICATION_UPDATE_SKELETON ) ;
2019-05-22 20:40:19 +02:00
} else {
Bone2D * bone = Object : : cast_to < Bone2D > ( entries [ i ] . object ) ;
if ( bone & & bone - > skeleton ) {
// 2D bone
bone - > skeleton - > _update_transform ( ) ;
}
2017-10-30 19:43:19 +01:00
}
}
}
2020-12-20 11:46:44 +01:00
void AnimatedValuesBackup : : restore ( ) const {
for ( int i = 0 ; i < entries . size ( ) ; i + + ) {
const AnimatedValuesBackup : : Entry * entry = & entries [ i ] ;
if ( entry - > bone_idx = = - 1 ) {
entry - > object - > set_indexed ( entry - > subpath , entry - > value ) ;
} else {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
Array arr = entry - > value ;
if ( arr . size ( ) = = 3 ) {
Object : : cast_to < Skeleton3D > ( entry - > object ) - > set_bone_pose_position ( entry - > bone_idx , arr [ 0 ] ) ;
Object : : cast_to < Skeleton3D > ( entry - > object ) - > set_bone_pose_rotation ( entry - > bone_idx , arr [ 1 ] ) ;
2022-07-25 22:32:26 +02:00
Object : : cast_to < Skeleton3D > ( entry - > object ) - > set_bone_pose_scale ( entry - > bone_idx , arr [ 2 ] ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
}
2020-12-20 11:46:44 +01:00
}
}
}
void AnimatedValuesBackup : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " restore " ) , & AnimatedValuesBackup : : restore ) ;
}
2017-10-30 19:43:19 +01:00
# endif
2014-02-10 02:10:30 +01:00
bool AnimationPlayer : : _set ( const StringName & p_name , const Variant & p_value ) {
String name = p_name ;
2018-01-11 23:35:12 +01:00
if ( name . begins_with ( " playback/play " ) ) { // bw compatibility
2014-02-10 02:10:30 +01:00
2018-01-11 23:35:12 +01:00
set_current_animation ( p_value ) ;
2014-02-10 02:10:30 +01:00
} else if ( name . begins_with ( " anims/ " ) ) {
2022-04-07 13:49:28 +02:00
// Backwards compatibility with 3.x, add them to "default" library.
2015-06-29 05:29:49 +02:00
String which = name . get_slicec ( ' / ' , 1 ) ;
2022-04-07 13:49:28 +02:00
Ref < Animation > anim = p_value ;
Ref < AnimationLibrary > al ;
if ( ! has_animation_library ( StringName ( ) ) ) {
al . instantiate ( ) ;
add_animation_library ( StringName ( ) , al ) ;
} else {
al = get_animation_library ( StringName ( ) ) ;
}
al - > add_animation ( which , anim ) ;
} else if ( name . begins_with ( " libraries " ) ) {
Dictionary d = p_value ;
while ( animation_libraries . size ( ) ) {
remove_animation_library ( animation_libraries [ 0 ] . name ) ;
}
List < Variant > keys ;
d . get_key_list ( & keys ) ;
for ( const Variant & K : keys ) {
StringName lib_name = K ;
Ref < AnimationLibrary > lib = d [ lib_name ] ;
add_animation_library ( lib_name , lib ) ;
}
2022-05-05 05:53:47 +02:00
emit_signal ( " animation_libraries_updated " ) ;
2014-02-10 02:10:30 +01:00
} else if ( name . begins_with ( " next/ " ) ) {
2015-06-29 05:29:49 +02:00
String which = name . get_slicec ( ' / ' , 1 ) ;
2014-02-10 02:10:30 +01:00
animation_set_next ( which , p_value ) ;
2015-06-29 05:29:49 +02:00
} else if ( p_name = = SceneStringNames : : get_singleton ( ) - > blend_times ) {
2014-02-10 02:10:30 +01:00
Array array = p_value ;
int len = array . size ( ) ;
ERR_FAIL_COND_V ( len % 3 , false ) ;
for ( int i = 0 ; i < len / 3 ; i + + ) {
StringName from = array [ i * 3 + 0 ] ;
StringName to = array [ i * 3 + 1 ] ;
float time = array [ i * 3 + 2 ] ;
set_blend_time ( from , to , time ) ;
}
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return true ;
}
bool AnimationPlayer : : _get ( const StringName & p_name , Variant & r_ret ) const {
String name = p_name ;
2018-01-11 23:35:12 +01:00
if ( name = = " playback/play " ) { // bw compatibility
2016-03-09 00:00:52 +01:00
2018-01-11 23:35:12 +01:00
r_ret = get_current_animation ( ) ;
2014-02-10 02:10:30 +01:00
2022-04-07 13:49:28 +02:00
} else if ( name . begins_with ( " libraries " ) ) {
Dictionary d ;
2022-12-29 01:24:45 +01:00
for ( const AnimationLibraryData & lib : animation_libraries ) {
d [ lib . name ] = lib . library ;
2022-04-07 13:49:28 +02:00
}
r_ret = d ;
2018-01-11 23:35:12 +01:00
2014-02-10 02:10:30 +01:00
} else if ( name . begins_with ( " next/ " ) ) {
2015-06-29 05:29:49 +02:00
String which = name . get_slicec ( ' / ' , 1 ) ;
2014-02-10 02:10:30 +01:00
r_ret = animation_get_next ( which ) ;
} else if ( name = = " blend_times " ) {
2017-01-16 18:03:38 +01:00
Vector < BlendKey > keys ;
2022-09-22 15:54:15 +02:00
for ( const KeyValue < BlendKey , double > & E : blend_times ) {
2021-08-09 22:13:42 +02:00
keys . ordered_insert ( E . key ) ;
2014-02-10 02:10:30 +01:00
}
2017-01-16 18:03:38 +01:00
Array array ;
for ( int i = 0 ; i < keys . size ( ) ; i + + ) {
array . push_back ( keys [ i ] . from ) ;
array . push_back ( keys [ i ] . to ) ;
2022-05-13 15:04:37 +02:00
array . push_back ( blend_times . get ( keys [ i ] ) ) ;
2017-01-16 18:03:38 +01:00
}
2014-02-10 02:10:30 +01:00
r_ret = array ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return true ;
}
2022-08-12 22:57:11 +02:00
void AnimationPlayer : : _validate_property ( PropertyInfo & p_property ) const {
if ( p_property . name = = " current_animation " ) {
2018-01-11 23:35:12 +01:00
List < String > names ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , AnimationData > & E : animation_set ) {
names . push_back ( E . key ) ;
2018-01-11 23:35:12 +01:00
}
names . sort ( ) ;
names . push_front ( " [stop] " ) ;
String hint ;
for ( List < String > : : Element * E = names . front ( ) ; E ; E = E - > next ( ) ) {
2020-05-14 16:41:43 +02:00
if ( E ! = names . front ( ) ) {
2018-01-11 23:35:12 +01:00
hint + = " , " ;
2020-05-14 16:41:43 +02:00
}
2018-01-11 23:35:12 +01:00
hint + = E - > get ( ) ;
}
2022-08-12 22:57:11 +02:00
p_property . hint_string = hint ;
2018-01-11 23:35:12 +01:00
}
}
void AnimationPlayer : : _get_property_list ( List < PropertyInfo > * p_list ) const {
2016-02-01 04:19:45 +01:00
List < PropertyInfo > anim_names ;
2023-02-09 17:24:52 +01:00
anim_names . push_back ( PropertyInfo ( Variant : : DICTIONARY , PNAME ( " libraries " ) ) ) ;
2022-04-07 13:49:28 +02:00
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , AnimationData > & E : animation_set ) {
if ( E . value . next ! = StringName ( ) ) {
2021-11-03 23:06:17 +01:00
anim_names . push_back ( PropertyInfo ( Variant : : STRING , " next/ " + String ( E . key ) , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-02-01 04:19:45 +01:00
anim_names . sort ( ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : anim_names ) {
2021-07-16 05:45:57 +02:00
p_list - > push_back ( E ) ;
2016-02-01 04:19:45 +01:00
}
2014-02-10 02:10:30 +01:00
2021-11-03 23:06:17 +01:00
p_list - > push_back ( PropertyInfo ( Variant : : ARRAY , " blend_times " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
2014-02-10 02:10:30 +01:00
}
2022-09-22 15:54:15 +02:00
void AnimationPlayer : : advance ( double p_time ) {
2014-10-03 05:10:51 +02:00
_animation_process ( p_time ) ;
}
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : _notification ( int p_what ) {
switch ( p_what ) {
2014-11-06 01:20:42 +01:00
case NOTIFICATION_ENTER_TREE : {
2014-02-10 02:10:30 +01:00
if ( ! processing ) {
//make sure that a previous process state was not saved
//only process if "processing" is set
2018-04-11 09:28:14 +02:00
set_physics_process_internal ( false ) ;
set_process_internal ( false ) ;
2014-02-10 02:10:30 +01:00
}
clear_caches ( ) ;
} break ;
2022-02-15 18:06:48 +01:00
2014-02-10 02:10:30 +01:00
case NOTIFICATION_READY : {
2017-08-19 01:02:56 +02:00
if ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) & & animation_set . has ( autoplay ) ) {
2014-02-10 02:10:30 +01:00
play ( autoplay ) ;
2017-05-29 13:56:35 +02:00
_animation_process ( 0 ) ;
2014-02-10 02:10:30 +01:00
}
} break ;
2022-02-15 18:06:48 +01:00
2017-01-10 22:02:19 +01:00
case NOTIFICATION_INTERNAL_PROCESS : {
2021-02-18 19:52:29 +01:00
if ( process_callback = = ANIMATION_PROCESS_PHYSICS ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( processing ) {
2014-02-10 02:10:30 +01:00
_animation_process ( get_process_delta_time ( ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} break ;
2022-02-15 18:06:48 +01:00
2017-09-30 16:19:07 +02:00
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
2021-02-18 19:52:29 +01:00
if ( process_callback = = ANIMATION_PROCESS_IDLE ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( processing ) {
2017-09-30 16:19:07 +02:00
_animation_process ( get_physics_process_delta_time ( ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} break ;
2022-02-15 18:06:48 +01:00
2014-11-06 01:20:42 +01:00
case NOTIFICATION_EXIT_TREE : {
2014-02-10 02:10:30 +01:00
clear_caches ( ) ;
} break ;
}
}
2021-02-09 21:41:48 +01:00
void AnimationPlayer : : _ensure_node_caches ( AnimationData * p_anim , Node * p_root_override ) {
2017-11-01 21:32:39 +01:00
// Already cached?
2020-05-14 16:41:43 +02:00
if ( p_anim - > node_cache . size ( ) = = p_anim - > animation - > get_track_count ( ) ) {
2017-11-01 21:32:39 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2023-06-05 14:56:54 +02:00
Node * parent = p_root_override ? p_root_override : get_node_or_null ( root ) ;
2016-03-09 00:00:52 +01:00
2023-06-06 14:59:54 +02:00
ERR_FAIL_NULL ( parent ) ;
2014-02-10 02:10:30 +01:00
Animation * a = p_anim - > animation . operator - > ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
p_anim - > node_cache . resize ( a - > get_track_count ( ) ) ;
2016-03-09 00:00:52 +01:00
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
setup_pass + + ;
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < a - > get_track_count ( ) ; i + + ) {
2022-06-29 06:18:45 +02:00
p_anim - > node_cache . write [ i ] = nullptr ;
2022-06-23 08:35:34 +02:00
if ( ! a - > track_is_enabled ( i ) ) {
continue ;
}
2022-06-29 06:18:45 +02:00
2022-05-03 01:43:50 +02:00
Ref < Resource > resource ;
2017-05-30 22:20:15 +02:00
Vector < StringName > leftover_path ;
Node * child = parent - > get_node_and_resource ( a - > track_get_path ( i ) , resource , leftover_path ) ;
2019-08-08 22:11:48 +02:00
ERR_CONTINUE_MSG ( ! child , " On Animation: ' " + p_anim - > name + " ', couldn't resolve track: ' " + String ( a - > track_get_path ( i ) ) + " '. " ) ; // couldn't find the child node
2020-02-12 18:24:06 +01:00
ObjectID id = resource . is_valid ( ) ? resource - > get_instance_id ( ) : child - > get_instance_id ( ) ;
2014-02-10 02:10:30 +01:00
int bone_idx = - 1 ;
2021-10-16 00:04:35 +02:00
int blend_shape_idx = - 1 ;
2014-02-10 02:10:30 +01:00
2021-03-18 00:35:42 +01:00
# ifndef _3D_DISABLED
2020-03-26 22:49:16 +01:00
if ( a - > track_get_path ( i ) . get_subname_count ( ) = = 1 & & Object : : cast_to < Skeleton3D > ( child ) ) {
Skeleton3D * sk = Object : : cast_to < Skeleton3D > ( child ) ;
2017-10-03 18:49:32 +02:00
bone_idx = sk - > find_bone ( a - > track_get_path ( i ) . get_subname ( 0 ) ) ;
2019-09-19 00:46:32 +02:00
if ( bone_idx = = - 1 ) {
2014-02-10 02:10:30 +01:00
continue ;
}
}
2021-10-16 00:04:35 +02:00
if ( a - > track_get_type ( i ) = = Animation : : TYPE_BLEND_SHAPE ) {
MeshInstance3D * mi_3d = Object : : cast_to < MeshInstance3D > ( child ) ;
if ( ! mi_3d ) {
continue ;
}
if ( a - > track_get_path ( i ) . get_subname_count ( ) ! = 1 ) {
continue ;
}
blend_shape_idx = mi_3d - > find_blend_shape_by_name ( a - > track_get_path ( i ) . get_subname ( 0 ) ) ;
if ( blend_shape_idx = = - 1 ) {
continue ;
}
}
2021-03-18 00:35:42 +01:00
# endif // _3D_DISABLED
2016-03-09 00:00:52 +01:00
2022-08-20 02:15:46 +02:00
if ( ! child - > is_connected ( " tree_exiting " , callable_mp ( this , & AnimationPlayer : : _node_removed ) ) ) {
2022-09-01 15:44:42 +02:00
child - > connect ( " tree_exiting " , callable_mp ( this , & AnimationPlayer : : _node_removed ) . bind ( child ) , CONNECT_ONE_SHOT ) ;
2014-02-10 02:10:30 +01:00
}
TrackNodeCacheKey key ;
key . id = id ;
key . bone_idx = bone_idx ;
2021-10-16 00:04:35 +02:00
key . blend_shape_idx = blend_shape_idx ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( ! node_cache_map . has ( key ) ) {
2014-02-10 02:10:30 +01:00
node_cache_map [ key ] = TrackNodeCache ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
TrackNodeCache * node_cache = & node_cache_map [ key ] ;
p_anim - > node_cache . write [ i ] = node_cache ;
node_cache - > path = a - > track_get_path ( i ) ;
node_cache - > node = child ;
node_cache - > resource = resource ;
node_cache - > node_2d = Object : : cast_to < Node2D > ( child ) ;
2021-03-18 00:35:42 +01:00
# ifndef _3D_DISABLED
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( a - > track_get_type ( i ) = = Animation : : TYPE_POSITION_3D | | a - > track_get_type ( i ) = = Animation : : TYPE_ROTATION_3D | | a - > track_get_type ( i ) = = Animation : : TYPE_SCALE_3D ) {
2018-09-09 00:32:57 +02:00
// special cases and caches for transform tracks
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( node_cache - > last_setup_pass ! = setup_pass ) {
node_cache - > loc_used = false ;
node_cache - > rot_used = false ;
node_cache - > scale_used = false ;
}
2021-03-18 00:35:42 +01:00
// cache node_3d
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > node_3d = Object : : cast_to < Node3D > ( child ) ;
2018-09-09 00:32:57 +02:00
// cache skeleton
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > skeleton = Object : : cast_to < Skeleton3D > ( child ) ;
if ( node_cache - > skeleton ) {
2018-09-09 00:32:57 +02:00
if ( a - > track_get_path ( i ) . get_subname_count ( ) = = 1 ) {
StringName bone_name = a - > track_get_path ( i ) . get_subname ( 0 ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > bone_idx = node_cache - > skeleton - > find_bone ( bone_name ) ;
if ( node_cache - > bone_idx < 0 ) {
2018-09-09 00:32:57 +02:00
// broken track (nonexistent bone)
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > skeleton = nullptr ;
node_cache - > node_3d = nullptr ;
ERR_CONTINUE ( node_cache - > bone_idx < 0 ) ;
2014-02-10 02:10:30 +01:00
}
2022-07-25 22:32:26 +02:00
Transform3D rest = node_cache - > skeleton - > get_bone_rest ( bone_idx ) ;
node_cache - > init_loc = rest . origin ;
node_cache - > init_rot = rest . basis . get_rotation_quaternion ( ) ;
node_cache - > init_scale = rest . basis . get_scale ( ) ;
2018-09-09 00:32:57 +02:00
} else {
2022-08-20 02:15:46 +02:00
// Not a skeleton, the node can be accessed with the node_3d member.
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > skeleton = nullptr ;
}
}
switch ( a - > track_get_type ( i ) ) {
case Animation : : TYPE_POSITION_3D : {
node_cache - > loc_used = true ;
} break ;
case Animation : : TYPE_ROTATION_3D : {
node_cache - > rot_used = true ;
} break ;
case Animation : : TYPE_SCALE_3D : {
node_cache - > scale_used = true ;
} break ;
default : {
2014-02-10 02:10:30 +01:00
}
}
}
2021-10-16 00:04:35 +02:00
if ( a - > track_get_type ( i ) = = Animation : : TYPE_BLEND_SHAPE ) {
// special cases and caches for transform tracks
node_cache - > node_blend_shape = Object : : cast_to < MeshInstance3D > ( child ) ;
node_cache - > blend_shape_idx = blend_shape_idx ;
}
2021-03-18 00:35:42 +01:00
# endif // _3D_DISABLED
2014-02-10 02:10:30 +01:00
if ( a - > track_get_type ( i ) = = Animation : : TYPE_VALUE ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( ! node_cache - > property_anim . has ( a - > track_get_path ( i ) . get_concatenated_subnames ( ) ) ) {
2014-02-10 02:10:30 +01:00
TrackNodeCache : : PropertyAnim pa ;
2017-05-30 22:20:15 +02:00
pa . subpath = leftover_path ;
2014-02-10 02:10:30 +01:00
pa . object = resource . is_valid ( ) ? ( Object * ) resource . ptr ( ) : ( Object * ) child ;
pa . special = SP_NONE ;
2015-12-05 18:18:22 +01:00
pa . owner = p_anim - > node_cache [ i ] ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( false & & node_cache - > node_2d ) {
2020-05-14 16:41:43 +02:00
if ( leftover_path . size ( ) = = 1 & & leftover_path [ 0 ] = = SceneStringNames : : get_singleton ( ) - > transform_pos ) {
2014-02-10 02:10:30 +01:00
pa . special = SP_NODE2D_POS ;
2020-05-14 16:41:43 +02:00
} else if ( leftover_path . size ( ) = = 1 & & leftover_path [ 0 ] = = SceneStringNames : : get_singleton ( ) - > transform_rot ) {
2014-02-10 02:10:30 +01:00
pa . special = SP_NODE2D_ROT ;
2020-05-14 16:41:43 +02:00
} else if ( leftover_path . size ( ) = = 1 & & leftover_path [ 0 ] = = SceneStringNames : : get_singleton ( ) - > transform_scale ) {
2014-02-10 02:10:30 +01:00
pa . special = SP_NODE2D_SCALE ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > property_anim [ a - > track_get_path ( i ) . get_concatenated_subnames ( ) ] = pa ;
2014-02-10 02:10:30 +01:00
}
}
2018-06-07 17:46:14 +02:00
if ( a - > track_get_type ( i ) = = Animation : : TYPE_BEZIER & & leftover_path . size ( ) ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( ! node_cache - > bezier_anim . has ( a - > track_get_path ( i ) . get_concatenated_subnames ( ) ) ) {
2018-06-07 17:46:14 +02:00
TrackNodeCache : : BezierAnim ba ;
2018-06-18 14:59:13 +02:00
ba . bezier_property = leftover_path ;
2018-06-07 17:46:14 +02:00
ba . object = resource . is_valid ( ) ? ( Object * ) resource . ptr ( ) : ( Object * ) child ;
ba . owner = p_anim - > node_cache [ i ] ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > bezier_anim [ a - > track_get_path ( i ) . get_concatenated_subnames ( ) ] = ba ;
2018-06-07 17:46:14 +02:00
}
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
2023-01-27 19:25:49 +01:00
if ( a - > track_get_type ( i ) = = Animation : : TYPE_AUDIO ) {
if ( ! node_cache - > audio_anim . has ( a - > track_get_path ( i ) . get_concatenated_names ( ) ) ) {
TrackNodeCache : : AudioAnim aa ;
aa . object = ( Object * ) child ;
aa . audio_stream . instantiate ( ) ;
aa . audio_stream - > set_polyphony ( audio_max_polyphony ) ;
node_cache - > audio_anim [ a - > track_get_path ( i ) . get_concatenated_names ( ) ] = aa ;
}
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
node_cache - > last_setup_pass = setup_pass ;
2014-02-10 02:10:30 +01:00
}
}
2022-03-09 14:58:40 +01:00
static void _call_object ( Object * p_object , const StringName & p_method , const Vector < Variant > & p_params , bool p_deferred ) {
// Separate function to use alloca() more efficiently
const Variant * * argptrs = ( const Variant * * ) alloca ( sizeof ( const Variant * * ) * p_params . size ( ) ) ;
const Variant * args = p_params . ptr ( ) ;
uint32_t argcount = p_params . size ( ) ;
for ( uint32_t i = 0 ; i < argcount ; i + + ) {
argptrs [ i ] = & args [ i ] ;
}
if ( p_deferred ) {
MessageQueue : : get_singleton ( ) - > push_callp ( p_object , p_method , argptrs , argcount ) ;
} else {
Callable : : CallError ce ;
p_object - > callp ( p_method , argptrs , argcount , ce ) ;
}
}
2023-01-19 16:43:37 +01:00
Variant AnimationPlayer : : post_process_key_value ( const Ref < Animation > & p_anim , int p_track , Variant p_value , const Object * p_object , int p_object_idx ) {
Variant res ;
if ( GDVIRTUAL_CALL ( _post_process_key_value , p_anim , p_track , p_value , const_cast < Object * > ( p_object ) , p_object_idx , res ) ) {
return res ;
}
return _post_process_key_value ( p_anim , p_track , p_value , p_object , p_object_idx ) ;
}
2022-07-26 11:48:08 +02:00
Variant AnimationPlayer : : _post_process_key_value ( const Ref < Animation > & p_anim , int p_track , Variant p_value , const Object * p_object , int p_object_idx ) {
switch ( p_anim - > track_get_type ( p_track ) ) {
# ifndef _3D_DISABLED
case Animation : : TYPE_POSITION_3D : {
if ( p_object_idx > = 0 ) {
const Skeleton3D * skel = Object : : cast_to < Skeleton3D > ( p_object ) ;
return Vector3 ( p_value ) * skel - > get_motion_scale ( ) ;
}
return p_value ;
} break ;
# endif // _3D_DISABLED
default : {
} break ;
}
return p_value ;
}
2022-11-29 10:51:45 +01:00
void AnimationPlayer : : _animation_process_animation ( AnimationData * p_anim , double p_prev_time , double p_time , double p_delta , float p_interp , bool p_is_current , bool p_seeked , bool p_started , Animation : : LoopedFlag p_looped_flag ) {
2017-11-01 21:32:39 +01:00
_ensure_node_caches ( p_anim ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( p_anim - > node_cache . size ( ) ! = p_anim - > animation - > get_track_count ( ) ) ;
Animation * a = p_anim - > animation . operator - > ( ) ;
2023-01-26 05:01:26 +01:00
# ifdef TOOLS_ENABLED
2017-08-19 01:02:56 +02:00
bool can_call = is_inside_tree ( ) & & ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
2023-01-26 05:01:26 +01:00
# endif // TOOLS_ENABLED
2021-10-15 15:25:00 +02:00
bool backward = signbit ( p_delta ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < a - > get_track_count ( ) ; i + + ) {
2019-02-01 16:15:39 +01:00
// If an animation changes this animation (or it animates itself)
// we need to recreate our animation cache
if ( p_anim - > node_cache . size ( ) ! = a - > get_track_count ( ) ) {
_ensure_node_caches ( p_anim ) ;
}
2014-02-10 02:10:30 +01:00
TrackNodeCache * nc = p_anim - > node_cache [ i ] ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( ! nc ) {
2019-02-01 16:15:39 +01:00
continue ; // no node cache for this track, skip it
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( ! a - > track_is_enabled ( i ) ) {
2017-11-28 16:46:37 +01:00
continue ; // do nothing if the track is disabled
2020-05-14 16:41:43 +02:00
}
2017-11-28 16:46:37 +01:00
2020-05-14 16:41:43 +02:00
if ( a - > track_get_key_count ( i ) = = 0 ) {
2014-02-10 02:10:30 +01:00
continue ; // do nothing if track is empty
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
switch ( a - > track_get_type ( i ) ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
case Animation : : TYPE_POSITION_3D : {
2021-03-18 00:35:42 +01:00
# ifndef _3D_DISABLED
if ( ! nc - > node_3d ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
Vector3 loc ;
2023-02-21 03:26:23 +01:00
Error err = a - > try_position_track_interpolate ( i , p_time , & loc ) ;
2014-12-02 18:02:41 +01:00
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2023-01-19 16:43:37 +01:00
loc = post_process_key_value ( a , i , loc , nc - > node_3d , nc - > bone_idx ) ;
2014-02-10 02:10:30 +01:00
if ( nc - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_size > = NODE_CACHE_UPDATE_MAX ) ;
cache_update [ cache_update_size + + ] = nc ;
nc - > accum_pass = accum_pass ;
nc - > loc_accum = loc ;
2022-07-25 22:32:26 +02:00
nc - > rot_accum = nc - > init_rot ;
nc - > scale_accum = nc - > init_scale ;
2014-02-10 02:10:30 +01:00
} else {
2020-03-16 10:07:33 +01:00
nc - > loc_accum = nc - > loc_accum . lerp ( loc , p_interp ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
}
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_ROTATION_3D : {
# ifndef _3D_DISABLED
if ( ! nc - > node_3d ) {
continue ;
}
Quaternion rot ;
2023-02-21 03:26:23 +01:00
Error err = a - > try_rotation_track_interpolate ( i , p_time , & rot ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
rot = post_process_key_value ( a , i , rot , nc - > node_3d , nc - > bone_idx ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( nc - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_size > = NODE_CACHE_UPDATE_MAX ) ;
cache_update [ cache_update_size + + ] = nc ;
nc - > accum_pass = accum_pass ;
2022-07-25 22:32:26 +02:00
nc - > loc_accum = nc - > init_loc ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
nc - > rot_accum = rot ;
2022-07-25 22:32:26 +02:00
nc - > scale_accum = nc - > init_scale ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
} else {
2014-02-10 02:10:30 +01:00
nc - > rot_accum = nc - > rot_accum . slerp ( rot , p_interp ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
}
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_SCALE_3D : {
# ifndef _3D_DISABLED
if ( ! nc - > node_3d ) {
continue ;
}
Vector3 scale ;
2023-02-21 03:26:23 +01:00
Error err = a - > try_scale_track_interpolate ( i , p_time , & scale ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
scale = post_process_key_value ( a , i , scale , nc - > node_3d , nc - > bone_idx ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( nc - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_size > = NODE_CACHE_UPDATE_MAX ) ;
cache_update [ cache_update_size + + ] = nc ;
nc - > accum_pass = accum_pass ;
2022-07-25 22:32:26 +02:00
nc - > loc_accum = nc - > init_loc ;
nc - > rot_accum = nc - > init_rot ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
nc - > scale_accum = scale ;
} else {
2020-03-16 10:07:33 +01:00
nc - > scale_accum = nc - > scale_accum . lerp ( scale , p_interp ) ;
2014-02-10 02:10:30 +01:00
}
2021-10-16 00:04:35 +02:00
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_BLEND_SHAPE : {
# ifndef _3D_DISABLED
if ( ! nc - > node_blend_shape ) {
continue ;
}
float blend ;
2023-02-21 03:26:23 +01:00
Error err = a - > try_blend_shape_track_interpolate ( i , p_time , & blend ) ;
2021-10-16 00:04:35 +02:00
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
blend = post_process_key_value ( a , i , blend , nc - > node_blend_shape , nc - > blend_shape_idx ) ;
2021-10-16 00:04:35 +02:00
if ( nc - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_size > = NODE_CACHE_UPDATE_MAX ) ;
nc - > accum_pass = accum_pass ;
cache_update [ cache_update_size + + ] = nc ;
nc - > blend_shape_accum = blend ;
} else {
nc - > blend_shape_accum = Math : : lerp ( nc - > blend_shape_accum , blend , p_interp ) ;
}
2021-03-18 00:35:42 +01:00
# endif // _3D_DISABLED
2014-02-10 02:10:30 +01:00
} break ;
case Animation : : TYPE_VALUE : {
2020-05-14 16:41:43 +02:00
if ( ! nc - > node ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
//StringName property=a->track_get_path(i).get_property();
2022-05-13 15:04:37 +02:00
HashMap < StringName , TrackNodeCache : : PropertyAnim > : : Iterator E = nc - > property_anim . find ( a - > track_get_path ( i ) . get_concatenated_subnames ( ) ) ;
2014-02-10 02:10:30 +01:00
ERR_CONTINUE ( ! E ) ; //should it continue, or create a new one?
2022-05-13 15:04:37 +02:00
TrackNodeCache : : PropertyAnim * pa = & E - > value ;
2014-02-10 02:10:30 +01:00
2018-06-07 17:46:14 +02:00
Animation : : UpdateMode update_mode = a - > value_track_get_update_mode ( i ) ;
if ( update_mode = = Animation : : UPDATE_CAPTURE ) {
2020-06-05 23:26:25 +02:00
if ( p_started | | pa - > capture = = Variant ( ) ) {
2018-06-07 17:46:14 +02:00
pa - > capture = pa - > object - > get_indexed ( pa - > subpath ) ;
}
2018-06-29 14:13:20 +02:00
int key_count = a - > track_get_key_count ( i ) ;
2020-05-14 16:41:43 +02:00
if ( key_count = = 0 ) {
2018-06-07 17:46:14 +02:00
continue ; //eeh not worth it
2020-05-14 16:41:43 +02:00
}
2018-06-07 17:46:14 +02:00
2021-10-15 15:25:00 +02:00
double first_key_time = a - > track_get_key_time ( i , 0 ) ;
double transition = 1.0 ;
2018-06-29 14:13:20 +02:00
int first_key = 0 ;
if ( first_key_time = = 0.0 ) {
//ignore, use for transition
2020-05-14 16:41:43 +02:00
if ( key_count = = 1 ) {
2018-09-13 03:38:39 +02:00
continue ; //with one key we can't do anything
2020-05-14 16:41:43 +02:00
}
2021-10-15 15:25:00 +02:00
transition = ( double ) a - > track_get_key_transition ( i , 0 ) ;
2018-06-29 14:13:20 +02:00
first_key_time = a - > track_get_key_time ( i , 1 ) ;
first_key = 1 ;
}
2018-06-07 17:46:14 +02:00
if ( p_time < first_key_time ) {
2021-10-15 15:25:00 +02:00
double c = Math : : ease ( p_time / first_key_time , transition ) ;
2018-06-29 14:13:20 +02:00
Variant first_value = a - > track_get_key_value ( i , first_key ) ;
2023-01-19 16:43:37 +01:00
first_value = post_process_key_value ( a , i , first_value , nc - > node ) ;
2022-09-06 05:24:06 +02:00
Variant interp_value = Animation : : interpolate_variant ( pa - > capture , first_value , c ) ;
2018-06-07 17:46:14 +02:00
if ( pa - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_prop_size > = NODE_CACHE_UPDATE_MAX ) ;
cache_update_prop [ cache_update_prop_size + + ] = pa ;
pa - > value_accum = interp_value ;
pa - > accum_pass = accum_pass ;
} else {
2022-09-06 05:24:06 +02:00
pa - > value_accum = Animation : : interpolate_variant ( pa - > value_accum , interp_value , p_interp ) ;
2018-06-07 17:46:14 +02:00
}
continue ; //handled
}
}
2022-11-29 10:51:45 +01:00
if ( update_mode = = Animation : : UPDATE_CONTINUOUS | | update_mode = = Animation : : UPDATE_CAPTURE ) {
2014-02-10 02:10:30 +01:00
Variant value = a - > value_track_interpolate ( i , p_time ) ;
2017-10-15 01:28:08 +02:00
2020-05-14 16:41:43 +02:00
if ( value = = Variant ( ) ) {
2017-10-15 01:28:08 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2023-01-19 16:43:37 +01:00
value = post_process_key_value ( a , i , value , nc - > node ) ;
2017-10-15 01:28:08 +02:00
2014-02-10 02:10:30 +01:00
if ( pa - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_prop_size > = NODE_CACHE_UPDATE_MAX ) ;
cache_update_prop [ cache_update_prop_size + + ] = pa ;
pa - > value_accum = value ;
pa - > accum_pass = accum_pass ;
} else {
2022-09-06 05:24:06 +02:00
pa - > value_accum = Animation : : interpolate_variant ( pa - > value_accum , value , p_interp ) ;
2014-02-10 02:10:30 +01:00
}
2022-11-29 10:51:45 +01:00
} else {
2014-02-10 02:10:30 +01:00
List < int > indices ;
2022-11-29 10:51:45 +01:00
if ( p_seeked ) {
int found_key = a - > track_find_key ( i , p_time ) ;
if ( found_key > = 0 ) {
indices . push_back ( found_key ) ;
2022-11-29 10:51:45 +01:00
}
2022-11-29 10:51:45 +01:00
} else {
if ( p_started ) {
2022-12-08 13:38:01 +01:00
int first_key = a - > track_find_key ( i , p_prev_time , Animation : : FIND_MODE_EXACT ) ;
2022-11-29 10:51:45 +01:00
if ( first_key > = 0 ) {
indices . push_back ( first_key ) ;
}
}
a - > track_get_key_indices_in_range ( i , p_time , p_delta , & indices , p_looped_flag ) ;
2022-11-29 10:51:45 +01:00
}
2016-06-19 06:43:02 +02:00
2021-07-16 05:45:57 +02:00
for ( int & F : indices ) {
Variant value = a - > track_get_key_value ( i , F ) ;
2023-01-19 16:43:37 +01:00
value = post_process_key_value ( a , i , value , nc - > node ) ;
2014-02-10 02:10:30 +01:00
switch ( pa - > special ) {
2015-12-05 18:18:22 +01:00
case SP_NONE : {
bool valid ;
2017-05-30 22:20:15 +02:00
pa - > object - > set_indexed ( pa - > subpath , value , & valid ) ; //you are not speshul
2015-12-05 18:18:22 +01:00
# ifdef DEBUG_ENABLED
if ( ! valid ) {
2021-12-23 15:58:03 +01:00
ERR_PRINT ( " Failed setting track value ' " + String ( pa - > owner - > path ) + " '. Check if the property exists or the type of key is valid. Animation ' " + a - > get_name ( ) + " ' at node ' " + get_path ( ) + " '. " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
} break ;
case SP_NODE2D_POS : {
# ifdef DEBUG_ENABLED
if ( value . get_type ( ) ! = Variant : : VECTOR2 ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Position key at time " + rtos ( p_time ) + " in Animation Track ' " + String ( pa - > owner - > path ) + " ' not of type Vector2(). Animation ' " + a - > get_name ( ) + " ' at node ' " + get_path ( ) + " '. " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
2017-01-04 05:16:14 +01:00
static_cast < Node2D * > ( pa - > object ) - > set_position ( value ) ;
2015-12-05 18:18:22 +01:00
} break ;
case SP_NODE2D_ROT : {
# ifdef DEBUG_ENABLED
if ( value . is_num ( ) ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Rotation key at time " + rtos ( p_time ) + " in Animation Track ' " + String ( pa - > owner - > path ) + " ' not numerical. Animation ' " + a - > get_name ( ) + " ' at node ' " + get_path ( ) + " '. " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
Fix editor suffixes and degrees conversion
* Functions to convert to/from degrees are all gone. Conversion is done by the editor.
* Use PROPERTY_HINT_ANGLE instead of PROPERTY_HINT_RANGE to edit radian angles in degrees.
* Added possibility to add suffixes to range properties, use "min,max[,step][,suffix:<something>]" example "0,100,1,suffix:m"
* In general, can add suffixes for EditorSpinSlider
Not covered by this PR, will have to be addressed by future ones:
* Ability to switch radians/degrees in the inspector for angle properties (if actually wanted).
* Animations previously made will most likely break, need to add a way to make old ones compatible.
* Only added a "px" suffix to 2D position and a "m" one to 3D position, someone needs to go through the rest of the engine and add all remaining suffixes.
* Likely also need to track down usage of EditorSpinSlider outside properties to add suffixes to it too.
2021-06-29 21:42:12 +02:00
static_cast < Node2D * > ( pa - > object ) - > set_rotation ( ( double ) value ) ;
2015-12-05 18:18:22 +01:00
} break ;
case SP_NODE2D_SCALE : {
# ifdef DEBUG_ENABLED
if ( value . get_type ( ) ! = Variant : : VECTOR2 ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Scale key at time " + rtos ( p_time ) + " in Animation Track ' " + String ( pa - > owner - > path ) + " ' not of type Vector2(). " + a - > get_name ( ) + " ' at node ' " + get_path ( ) + " '. " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
static_cast < Node2D * > ( pa - > object ) - > set_scale ( value ) ;
} break ;
2014-02-10 02:10:30 +01:00
}
}
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
} break ;
case Animation : : TYPE_METHOD : {
2023-01-26 05:01:26 +01:00
# ifdef TOOLS_ENABLED
if ( ! can_call ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2023-01-26 05:01:26 +01:00
# endif // TOOLS_ENABLED
if ( ! p_is_current | | ! nc - > node | | is_stopping ) {
continue ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
List < int > indices ;
2022-11-29 10:51:45 +01:00
if ( p_seeked ) {
int found_key = a - > track_find_key ( i , p_time ) ;
if ( found_key > = 0 ) {
indices . push_back ( found_key ) ;
}
} else {
if ( p_started ) {
2022-12-08 13:38:01 +01:00
int first_key = a - > track_find_key ( i , p_prev_time , Animation : : FIND_MODE_EXACT ) ;
2022-11-29 10:51:45 +01:00
if ( first_key > = 0 ) {
indices . push_back ( first_key ) ;
}
2022-11-29 10:51:45 +01:00
}
2022-11-29 10:51:45 +01:00
a - > track_get_key_indices_in_range ( i , p_time , p_delta , & indices , p_looped_flag ) ;
2022-11-29 10:51:45 +01:00
}
2016-03-09 00:00:52 +01:00
2021-07-16 05:45:57 +02:00
for ( int & E : indices ) {
StringName method = a - > method_track_get_name ( i , E ) ;
Vector < Variant > params = a - > method_track_get_params ( i , E ) ;
2018-07-19 03:26:01 +02:00
# ifdef DEBUG_ENABLED
if ( ! nc - > node - > has_method ( method ) ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Invalid method call ' " + method + " '. ' " + a - > get_name ( ) + " ' at node ' " + get_path ( ) + " '. " ) ;
2018-07-19 03:26:01 +02:00
}
# endif
2023-01-26 05:01:26 +01:00
_call_object ( nc - > node , method , params , method_call_mode = = ANIMATION_METHOD_CALL_DEFERRED ) ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
} break ;
2018-06-07 17:46:14 +02:00
case Animation : : TYPE_BEZIER : {
2020-05-14 16:41:43 +02:00
if ( ! nc - > node ) {
2018-06-07 17:46:14 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-07 17:46:14 +02:00
2022-05-13 15:04:37 +02:00
HashMap < StringName , TrackNodeCache : : BezierAnim > : : Iterator E = nc - > bezier_anim . find ( a - > track_get_path ( i ) . get_concatenated_subnames ( ) ) ;
2018-06-07 17:46:14 +02:00
ERR_CONTINUE ( ! E ) ; //should it continue, or create a new one?
2022-05-13 15:04:37 +02:00
TrackNodeCache : : BezierAnim * ba = & E - > value ;
2018-06-07 17:46:14 +02:00
2021-10-15 15:25:00 +02:00
real_t bezier = a - > bezier_track_interpolate ( i , p_time ) ;
2023-01-19 16:43:37 +01:00
bezier = post_process_key_value ( a , i , bezier , nc - > node ) ;
2018-06-07 17:46:14 +02:00
if ( ba - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_bezier_size > = NODE_CACHE_UPDATE_MAX ) ;
cache_update_bezier [ cache_update_bezier_size + + ] = ba ;
ba - > bezier_accum = bezier ;
ba - > accum_pass = accum_pass ;
} else {
2021-09-16 08:03:50 +02:00
ba - > bezier_accum = Math : : lerp ( ba - > bezier_accum , ( float ) bezier , p_interp ) ;
2018-06-07 17:46:14 +02:00
}
} break ;
case Animation : : TYPE_AUDIO : {
2019-11-19 14:39:10 +01:00
if ( ! nc - > node | | is_stopping ) {
2018-06-07 17:46:14 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2023-01-26 05:01:26 +01:00
# ifdef TOOLS_ENABLED
2023-01-27 19:25:49 +01:00
if ( p_seeked & & ! can_call ) {
continue ; // To avoid spamming the preview in editor.
}
2023-01-26 05:01:26 +01:00
# endif // TOOLS_ENABLED
2023-01-27 19:25:49 +01:00
HashMap < StringName , TrackNodeCache : : AudioAnim > : : Iterator E = nc - > audio_anim . find ( a - > track_get_path ( i ) . get_concatenated_names ( ) ) ;
ERR_CONTINUE ( ! E ) ; //should it continue, or create a new one?
2018-06-07 17:46:14 +02:00
2023-01-27 19:25:49 +01:00
TrackNodeCache : : AudioAnim * aa = & E - > value ;
Node * asp = Object : : cast_to < Node > ( aa - > object ) ;
if ( ! asp ) {
continue ;
}
aa - > length = a - > get_length ( ) ;
aa - > time = p_time ;
aa - > loop = a - > get_loop_mode ( ) ! = Animation : : LOOP_NONE ;
aa - > backward = backward ;
if ( aa - > accum_pass ! = accum_pass ) {
ERR_CONTINUE ( cache_update_audio_size > = NODE_CACHE_UPDATE_MAX ) ;
cache_update_audio [ cache_update_audio_size + + ] = aa ;
aa - > accum_pass = accum_pass ;
}
2018-06-07 17:46:14 +02:00
2023-01-27 19:25:49 +01:00
HashMap < int , TrackNodeCache : : PlayingAudioStreamInfo > & map = aa - > playing_streams ;
// Find stream.
int idx = - 1 ;
2023-02-04 19:50:55 +01:00
if ( p_seeked | | p_started ) {
2023-01-27 19:25:49 +01:00
idx = a - > track_find_key ( i , p_time ) ;
// Discard previous stream when seeking.
if ( map . has ( idx ) ) {
aa - > audio_stream_playback - > stop_stream ( map [ idx ] . index ) ;
map . erase ( idx ) ;
2018-06-07 17:46:14 +02:00
}
} else {
List < int > to_play ;
2023-02-04 19:50:55 +01:00
2022-11-29 10:51:45 +01:00
a - > track_get_key_indices_in_range ( i , p_time , p_delta , & to_play , p_looped_flag ) ;
2018-06-07 17:46:14 +02:00
if ( to_play . size ( ) ) {
2023-01-27 19:25:49 +01:00
idx = to_play . back ( ) - > get ( ) ;
}
}
if ( idx < 0 ) {
continue ;
}
2018-07-01 22:44:15 +02:00
2023-01-27 19:25:49 +01:00
// Play stream.
Ref < AudioStream > stream = a - > audio_track_get_key_stream ( i , idx ) ;
if ( stream . is_valid ( ) ) {
double start_ofs = a - > audio_track_get_key_start_offset ( i , idx ) ;
double end_ofs = a - > audio_track_get_key_end_offset ( i , idx ) ;
double len = stream - > get_length ( ) ;
2023-02-04 19:50:55 +01:00
if ( p_seeked | | p_started ) {
start_ofs + = p_time - a - > track_get_key_time ( i , idx ) ;
}
2023-01-27 19:25:49 +01:00
if ( aa - > object - > call ( SNAME ( " get_stream " ) ) ! = aa - > audio_stream ) {
aa - > object - > call ( SNAME ( " set_stream " ) , aa - > audio_stream ) ;
aa - > audio_stream_playback . unref ( ) ;
if ( ! playing_audio_stream_players . has ( asp ) ) {
playing_audio_stream_players . push_back ( asp ) ;
2018-07-01 22:44:15 +02:00
}
2023-01-27 19:25:49 +01:00
}
if ( ! aa - > object - > call ( SNAME ( " is_playing " ) ) ) {
aa - > object - > call ( SNAME ( " play " ) ) ;
}
if ( ! aa - > object - > call ( SNAME ( " has_stream_playback " ) ) ) {
aa - > audio_stream_playback . unref ( ) ;
continue ;
}
if ( aa - > audio_stream_playback . is_null ( ) ) {
aa - > audio_stream_playback = aa - > object - > call ( SNAME ( " get_stream_playback " ) ) ;
}
2018-07-01 22:44:15 +02:00
2023-01-27 19:25:49 +01:00
TrackNodeCache : : PlayingAudioStreamInfo pasi ;
pasi . index = aa - > audio_stream_playback - > play_stream ( stream , start_ofs ) ;
pasi . start = p_time ;
if ( len & & end_ofs > 0 ) { // Force an end at a time.
pasi . len = len - start_ofs - end_ofs ;
} else {
pasi . len = 0 ;
2018-06-07 17:46:14 +02:00
}
2023-01-27 19:25:49 +01:00
map [ idx ] = pasi ;
2018-06-07 17:46:14 +02:00
}
} break ;
case Animation : : TYPE_ANIMATION : {
2019-11-19 14:39:10 +01:00
if ( is_stopping ) {
continue ;
}
2018-06-07 17:46:14 +02:00
AnimationPlayer * player = Object : : cast_to < AnimationPlayer > ( nc - > node ) ;
2020-05-14 16:41:43 +02:00
if ( ! player ) {
2018-06-07 17:46:14 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-07 17:46:14 +02:00
2022-11-29 10:51:45 +01:00
if ( p_seeked ) {
2018-06-07 17:46:14 +02:00
//seek
int idx = a - > track_find_key ( i , p_time ) ;
2020-05-14 16:41:43 +02:00
if ( idx < 0 ) {
2018-06-07 17:46:14 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-07 17:46:14 +02:00
2021-05-21 08:42:37 +02:00
double pos = a - > track_get_key_time ( i , idx ) ;
2018-06-07 17:46:14 +02:00
StringName anim_name = a - > animation_track_get_key_animation ( i , idx ) ;
2020-05-14 16:41:43 +02:00
if ( String ( anim_name ) = = " [stop] " | | ! player - > has_animation ( anim_name ) ) {
2018-06-07 17:46:14 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-07 17:46:14 +02:00
Ref < Animation > anim = player - > get_animation ( anim_name ) ;
2021-10-15 15:25:00 +02:00
double at_anim_pos = 0.0 ;
2021-04-24 22:47:03 +02:00
2021-10-15 15:25:00 +02:00
switch ( anim - > get_loop_mode ( ) ) {
2022-05-04 20:53:48 +02:00
case Animation : : LOOP_NONE : {
2021-10-15 15:25:00 +02:00
at_anim_pos = MIN ( ( double ) anim - > get_length ( ) , p_time - pos ) ; //seek to end
} break ;
2022-05-04 20:53:48 +02:00
case Animation : : LOOP_LINEAR : {
2021-10-15 15:25:00 +02:00
at_anim_pos = Math : : fposmod ( p_time - pos , ( double ) anim - > get_length ( ) ) ; //seek to loop
} break ;
2022-05-04 20:53:48 +02:00
case Animation : : LOOP_PINGPONG : {
2021-10-15 15:25:00 +02:00
at_anim_pos = Math : : pingpong ( p_time - pos , ( double ) anim - > get_length ( ) ) ;
} break ;
default :
break ;
2018-06-07 17:46:14 +02:00
}
2022-12-05 01:30:47 +01:00
if ( player - > is_playing ( ) ) {
2018-06-07 17:46:14 +02:00
player - > seek ( at_anim_pos ) ;
2022-12-11 22:25:50 +01:00
player - > play ( anim_name ) ;
2018-06-07 17:46:14 +02:00
nc - > animation_playing = true ;
playing_caches . insert ( nc ) ;
} else {
player - > set_assigned_animation ( anim_name ) ;
player - > seek ( at_anim_pos , true ) ;
}
} else {
//find stuff to play
List < int > to_play ;
2022-11-29 10:51:45 +01:00
if ( p_started ) {
2022-12-08 13:38:01 +01:00
int first_key = a - > track_find_key ( i , p_prev_time , Animation : : FIND_MODE_EXACT ) ;
2022-11-29 10:51:45 +01:00
if ( first_key > = 0 ) {
to_play . push_back ( first_key ) ;
}
}
a - > track_get_key_indices_in_range ( i , p_time , p_delta , & to_play , p_looped_flag ) ;
2018-06-07 17:46:14 +02:00
if ( to_play . size ( ) ) {
int idx = to_play . back ( ) - > get ( ) ;
StringName anim_name = a - > animation_track_get_key_animation ( i , idx ) ;
if ( String ( anim_name ) = = " [stop] " | | ! player - > has_animation ( anim_name ) ) {
if ( playing_caches . has ( nc ) ) {
playing_caches . erase ( nc ) ;
player - > stop ( ) ;
nc - > animation_playing = false ;
}
} else {
2022-12-11 22:25:50 +01:00
player - > seek ( 0.0 ) ;
2018-06-07 17:46:14 +02:00
player - > play ( anim_name ) ;
nc - > animation_playing = true ;
playing_caches . insert ( nc ) ;
}
}
}
} break ;
2014-02-10 02:10:30 +01:00
}
}
}
2021-05-21 08:42:37 +02:00
void AnimationPlayer : : _animation_process_data ( PlaybackData & cd , double p_delta , float p_blend , bool p_seeked , bool p_started ) {
double delta = p_delta * speed_scale * cd . speed_scale ;
double next_pos = cd . pos + delta ;
2022-12-07 10:28:36 +01:00
bool backwards = signbit ( delta ) ; // Negative zero means playing backwards too.
2016-03-09 00:00:52 +01:00
2021-08-10 00:15:17 +02:00
real_t len = cd . from - > animation - > get_length ( ) ;
2022-11-29 10:51:45 +01:00
Animation : : LoopedFlag looped_flag = Animation : : LOOPED_FLAG_NONE ;
2021-10-15 15:25:00 +02:00
switch ( cd . from - > animation - > get_loop_mode ( ) ) {
2022-05-04 20:53:48 +02:00
case Animation : : LOOP_NONE : {
2021-10-15 15:25:00 +02:00
if ( next_pos < 0 ) {
next_pos = 0 ;
} else if ( next_pos > len ) {
next_pos = len ;
}
2022-12-07 10:28:36 +01:00
delta = next_pos - cd . pos ; // Fix delta (after determination of backwards because negative zero is lost here).
2021-10-15 15:25:00 +02:00
} break ;
2014-02-10 02:10:30 +01:00
2022-05-04 20:53:48 +02:00
case Animation : : LOOP_LINEAR : {
2022-11-29 10:51:45 +01:00
if ( next_pos < 0 & & cd . pos > = 0 ) {
looped_flag = Animation : : LOOPED_FLAG_START ;
2014-02-10 02:10:30 +01:00
}
2022-11-29 10:51:45 +01:00
if ( next_pos > len & & cd . pos < = len ) {
looped_flag = Animation : : LOOPED_FLAG_END ;
}
next_pos = Math : : fposmod ( next_pos , ( double ) len ) ;
2021-10-15 15:25:00 +02:00
} break ;
2014-02-10 02:10:30 +01:00
2022-05-04 20:53:48 +02:00
case Animation : : LOOP_PINGPONG : {
2022-11-29 10:51:45 +01:00
if ( next_pos < 0 & & cd . pos > = 0 ) {
cd . speed_scale * = - 1.0 ;
looped_flag = Animation : : LOOPED_FLAG_START ;
2021-04-24 22:47:03 +02:00
}
2022-11-29 10:51:45 +01:00
if ( next_pos > len & & cd . pos < = len ) {
cd . speed_scale * = - 1.0 ;
looped_flag = Animation : : LOOPED_FLAG_END ;
2021-10-15 15:25:00 +02:00
}
2022-11-29 10:51:45 +01:00
next_pos = Math : : pingpong ( next_pos , ( double ) len ) ;
2021-10-15 15:25:00 +02:00
} break ;
2014-02-10 02:10:30 +01:00
2021-10-15 15:25:00 +02:00
default :
break ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2022-12-07 10:28:36 +01:00
double prev_pos = cd . pos ; // The animation may be changed during process, so it is safer that the state is changed before process.
2014-02-10 02:10:30 +01:00
cd . pos = next_pos ;
2022-12-07 10:28:36 +01:00
2022-12-07 14:34:41 +01:00
AnimationData * prev_from = cd . from ;
_animation_process_animation ( cd . from , prev_pos , cd . pos , delta , p_blend , & cd = = & playback . current , p_seeked , p_started , looped_flag ) ;
2022-12-07 10:28:36 +01:00
2022-12-07 14:34:41 +01:00
// End detection.
2022-12-07 10:28:36 +01:00
if ( cd . from - > animation - > get_loop_mode ( ) = = Animation : : LOOP_NONE ) {
2022-12-07 14:34:41 +01:00
if ( prev_from ! = playback . current . from ) {
return ; // Animation has been changed in the process (may be caused by method track), abort process.
}
if ( ! backwards & & prev_pos < = len & & next_pos = = len ) {
// Playback finished.
end_reached = true ;
end_notify = prev_pos < len ; // Notify only if not already at the end.
}
if ( backwards & & prev_pos > = 0 & & next_pos = = 0 ) {
// Playback finished.
end_reached = true ;
end_notify = prev_pos > 0 ; // Notify only if not already at the beginning.
2022-12-07 10:28:36 +01:00
}
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2021-05-21 08:42:37 +02:00
void AnimationPlayer : : _animation_process2 ( double p_delta , bool p_started ) {
2014-02-10 02:10:30 +01:00
Playback & c = playback ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
accum_pass + + ;
2016-03-09 00:00:52 +01:00
2022-12-07 10:28:36 +01:00
bool seeked = c . seeked ; // The animation may be changed during process, so it is safer that the state is changed before process.
2019-11-13 04:16:13 +01:00
2018-06-07 17:46:14 +02:00
if ( p_delta ! = 0 ) {
c . seeked = false ;
}
2014-02-10 02:10:30 +01:00
2019-11-13 04:16:13 +01:00
float blend = 1.0 ; // First animation we play at 100% blend
2022-12-07 10:28:36 +01:00
2019-11-13 04:16:13 +01:00
List < Blend > : : Element * next = NULL ;
for ( List < Blend > : : Element * E = c . blend . front ( ) ; E ; E = next ) {
2014-02-10 02:10:30 +01:00
Blend & b = E - > get ( ) ;
2019-11-13 04:16:13 +01:00
// Note: There may be issues if an animation event triggers an animation change while this blend is active,
// so it is best to use "deferred" calls instead of "immediate" for animation events that can trigger new animations.
_animation_process_data ( b . data , p_delta , blend , false , false ) ;
blend = 1.0 - b . blend_left / b . blend_time ; // This is how much to blend the NEXT animation
2014-02-10 02:10:30 +01:00
b . blend_left - = Math : : absf ( speed_scale * p_delta ) ;
2019-11-13 04:16:13 +01:00
next = E - > next ( ) ;
2014-02-10 02:10:30 +01:00
if ( b . blend_left < 0 ) {
2019-11-13 04:16:13 +01:00
// If the blend of this has finished, we need to remove ALL the previous blends
List < Blend > : : Element * prev ;
while ( E ) {
prev = E - > prev ( ) ;
c . blend . erase ( E ) ;
E = prev ;
}
2014-02-10 02:10:30 +01:00
}
}
2019-11-13 04:16:13 +01:00
_animation_process_data ( c . current , p_delta , blend , seeked , p_started ) ;
2014-02-10 02:10:30 +01:00
}
void AnimationPlayer : : _animation_update_transforms ( ) {
2017-10-03 18:49:32 +02:00
{
2020-10-17 07:08:21 +02:00
Transform3D t ;
2017-10-03 18:49:32 +02:00
for ( int i = 0 ; i < cache_update_size ; i + + ) {
TrackNodeCache * nc = cache_update [ i ] ;
2014-02-10 02:10:30 +01:00
2017-10-03 18:49:32 +02:00
ERR_CONTINUE ( nc - > accum_pass ! = accum_pass ) ;
2021-03-18 00:35:42 +01:00
# ifndef _3D_DISABLED
2014-02-10 02:10:30 +01:00
if ( nc - > skeleton & & nc - > bone_idx > = 0 ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( nc - > loc_used ) {
nc - > skeleton - > set_bone_pose_position ( nc - > bone_idx , nc - > loc_accum ) ;
}
if ( nc - > rot_used ) {
nc - > skeleton - > set_bone_pose_rotation ( nc - > bone_idx , nc - > rot_accum ) ;
}
if ( nc - > scale_used ) {
nc - > skeleton - > set_bone_pose_scale ( nc - > bone_idx , nc - > scale_accum ) ;
}
2021-10-16 00:04:35 +02:00
} else if ( nc - > node_blend_shape ) {
nc - > node_blend_shape - > set_blend_shape_value ( nc - > blend_shape_idx , nc - > blend_shape_accum ) ;
2021-03-18 00:35:42 +01:00
} else if ( nc - > node_3d ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
if ( nc - > loc_used ) {
nc - > node_3d - > set_position ( nc - > loc_accum ) ;
}
if ( nc - > rot_used ) {
nc - > node_3d - > set_rotation ( nc - > rot_accum . get_euler ( ) ) ;
}
if ( nc - > scale_used ) {
nc - > node_3d - > set_scale ( nc - > scale_accum ) ;
}
2014-02-10 02:10:30 +01:00
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
2021-03-18 00:35:42 +01:00
# endif // _3D_DISABLED
2014-02-10 02:10:30 +01:00
}
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < cache_update_prop_size ; i + + ) {
TrackNodeCache : : PropertyAnim * pa = cache_update_prop [ i ] ;
ERR_CONTINUE ( pa - > accum_pass ! = accum_pass ) ;
2015-12-05 18:18:22 +01:00
switch ( pa - > special ) {
case SP_NONE : {
bool valid ;
2017-05-30 22:20:15 +02:00
pa - > object - > set_indexed ( pa - > subpath , pa - > value_accum , & valid ) ; //you are not speshul
2015-12-05 18:18:22 +01:00
# ifdef DEBUG_ENABLED
2021-12-23 15:58:03 +01:00
2015-12-05 18:18:22 +01:00
if ( ! valid ) {
2021-12-23 15:58:03 +01:00
// Get subpath as string for printing the error
// Cannot use `String::join(Vector<String>)` because this is a vector of StringName
String key_debug ;
if ( pa - > subpath . size ( ) > 0 ) {
key_debug = pa - > subpath [ 0 ] ;
for ( int subpath_index = 1 ; subpath_index < pa - > subpath . size ( ) ; + + subpath_index ) {
key_debug + = " . " ;
key_debug + = pa - > subpath [ subpath_index ] ;
}
}
ERR_PRINT ( " Failed setting key ' " + key_debug +
" ' at time " + rtos ( playback . current . pos ) +
" in Animation ' " + get_current_animation ( ) +
" ' at Node ' " + get_path ( ) +
" ', Track ' " + String ( pa - > owner - > path ) +
" '. Check if the property exists or the type of key is right for the property. " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
} break ;
case SP_NODE2D_POS : {
# ifdef DEBUG_ENABLED
if ( pa - > value_accum . get_type ( ) ! = Variant : : VECTOR2 ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Position key at time " + rtos ( playback . current . pos ) + " in Animation ' " + get_current_animation ( ) + " ' at Node ' " + get_path ( ) + " ', Track ' " + String ( pa - > owner - > path ) + " ' not of type Vector2() " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
2017-01-04 05:16:14 +01:00
static_cast < Node2D * > ( pa - > object ) - > set_position ( pa - > value_accum ) ;
2015-12-05 18:18:22 +01:00
} break ;
case SP_NODE2D_ROT : {
# ifdef DEBUG_ENABLED
if ( pa - > value_accum . is_num ( ) ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Rotation key at time " + rtos ( playback . current . pos ) + " in Animation ' " + get_current_animation ( ) + " ' at Node ' " + get_path ( ) + " ', Track ' " + String ( pa - > owner - > path ) + " ' not numerical " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
2022-08-13 17:45:42 +02:00
static_cast < Node2D * > ( pa - > object ) - > set_rotation ( Math : : deg_to_rad ( ( double ) pa - > value_accum ) ) ;
2015-12-05 18:18:22 +01:00
} break ;
case SP_NODE2D_SCALE : {
# ifdef DEBUG_ENABLED
if ( pa - > value_accum . get_type ( ) ! = Variant : : VECTOR2 ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Scale key at time " + rtos ( playback . current . pos ) + " in Animation ' " + get_current_animation ( ) + " ' at Node ' " + get_path ( ) + " ', Track ' " + String ( pa - > owner - > path ) + " ' not of type Vector2() " ) ;
2015-12-05 18:18:22 +01:00
}
# endif
static_cast < Node2D * > ( pa - > object ) - > set_scale ( pa - > value_accum ) ;
} break ;
2014-02-10 02:10:30 +01:00
}
}
2018-06-07 17:46:14 +02:00
for ( int i = 0 ; i < cache_update_bezier_size ; i + + ) {
TrackNodeCache : : BezierAnim * ba = cache_update_bezier [ i ] ;
ERR_CONTINUE ( ba - > accum_pass ! = accum_pass ) ;
ba - > object - > set_indexed ( ba - > bezier_property , ba - > bezier_accum ) ;
}
2023-01-27 19:25:49 +01:00
for ( int i = 0 ; i < cache_update_audio_size ; i + + ) {
TrackNodeCache : : AudioAnim * aa = cache_update_audio [ i ] ;
ERR_CONTINUE ( aa - > accum_pass ! = accum_pass ) ;
// Audio ending process.
LocalVector < int > erase_list ;
for ( const KeyValue < int , TrackNodeCache : : PlayingAudioStreamInfo > & K : aa - > playing_streams ) {
TrackNodeCache : : PlayingAudioStreamInfo pasi = K . value ;
bool stop = false ;
if ( ! aa - > audio_stream_playback - > is_stream_playing ( pasi . index ) ) {
stop = true ;
}
if ( ! aa - > loop ) {
if ( ! aa - > backward ) {
if ( aa - > time < pasi . start ) {
stop = true ;
}
2022-11-22 23:09:30 +01:00
} else {
2023-01-27 19:25:49 +01:00
if ( aa - > time > pasi . start ) {
stop = true ;
}
}
}
if ( pasi . len > 0 ) {
double len = 0.0 ;
if ( ! aa - > backward ) {
len = pasi . start > aa - > time ? ( aa - > length - pasi . start ) + aa - > time : aa - > time - pasi . start ;
} else {
len = pasi . start < aa - > time ? ( aa - > length - aa - > time ) + pasi . start : pasi . start - aa - > time ;
}
if ( len > pasi . len ) {
stop = true ;
}
}
if ( stop ) {
// Time to stop.
aa - > audio_stream_playback - > stop_stream ( pasi . index ) ;
erase_list . push_back ( K . key ) ;
}
}
for ( uint32_t erase_idx = 0 ; erase_idx < erase_list . size ( ) ; erase_idx + + ) {
aa - > playing_streams . erase ( erase_list [ erase_idx ] ) ;
}
}
2014-02-10 02:10:30 +01:00
}
2021-05-21 08:42:37 +02:00
void AnimationPlayer : : _animation_process ( double p_delta ) {
2014-02-10 02:10:30 +01:00
if ( playback . current . from ) {
2017-12-28 21:48:09 +01:00
end_reached = false ;
2014-02-10 02:10:30 +01:00
end_notify = false ;
2018-06-07 17:46:14 +02:00
2022-12-07 10:28:36 +01:00
bool started = playback . started ; // The animation may be changed during process, so it is safer that the state is changed before process.
2018-06-07 17:46:14 +02:00
if ( playback . started ) {
playback . started = false ;
}
2022-12-07 14:34:41 +01:00
cache_update_size = 0 ;
cache_update_prop_size = 0 ;
cache_update_bezier_size = 0 ;
2023-01-27 19:25:49 +01:00
cache_update_audio_size = 0 ;
2022-12-07 10:28:36 +01:00
2022-12-07 14:34:41 +01:00
AnimationData * prev_from = playback . current . from ;
_animation_process2 ( p_delta , started ) ;
if ( prev_from ! = playback . current . from ) {
return ; // Animation has been changed in the process (may be caused by method track), abort process.
2022-12-07 10:28:36 +01:00
}
2014-02-10 02:10:30 +01:00
_animation_update_transforms ( ) ;
2022-12-07 10:28:36 +01:00
2017-12-28 21:48:09 +01:00
if ( end_reached ) {
2023-02-04 19:50:55 +01:00
_clear_audio_streams ( ) ;
_stop_playing_caches ( false ) ;
2014-02-10 02:10:30 +01:00
if ( queued . size ( ) ) {
String old = playback . assigned ;
play ( queued . front ( ) - > get ( ) ) ;
String new_name = playback . assigned ;
queued . pop_front ( ) ;
2020-05-14 16:41:43 +02:00
if ( end_notify ) {
2017-12-28 21:48:09 +01:00
emit_signal ( SceneStringNames : : get_singleton ( ) - > animation_changed , old , new_name ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} else {
playing = false ;
_set_process ( false ) ;
2020-05-14 16:41:43 +02:00
if ( end_notify ) {
2017-12-28 21:48:09 +01:00
emit_signal ( SceneStringNames : : get_singleton ( ) - > animation_finished , playback . assigned ) ;
2022-06-26 01:38:20 +02:00
if ( movie_quit_on_finish & & OS : : get_singleton ( ) - > has_feature ( " movie " ) ) {
print_line ( vformat ( " Movie Maker mode is enabled. Quitting on animation finish as requested by: %s " , get_path ( ) ) ) ;
get_tree ( ) - > quit ( ) ;
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-12-28 21:48:09 +01:00
end_reached = false ;
2014-02-10 02:10:30 +01:00
}
} else {
_set_process ( false ) ;
}
}
2022-04-07 13:49:28 +02:00
void AnimationPlayer : : _animation_set_cache_update ( ) {
// Relatively fast function to update all animations.
2015-06-29 05:29:49 +02:00
2022-04-07 13:49:28 +02:00
animation_set_update_pass + + ;
bool clear_cache_needed = false ;
2016-03-09 00:00:52 +01:00
2022-04-07 13:49:28 +02:00
// Update changed and add otherwise
2022-12-29 01:24:45 +01:00
for ( const AnimationLibraryData & lib : animation_libraries ) {
for ( const KeyValue < StringName , Ref < Animation > > & K : lib . library - > animations ) {
StringName key = lib . name = = StringName ( ) ? K . key : StringName ( String ( lib . name ) + " / " + String ( K . key ) ) ;
2022-04-07 13:49:28 +02:00
if ( ! animation_set . has ( key ) ) {
AnimationData ad ;
ad . animation = K . value ;
2022-12-29 01:24:45 +01:00
ad . animation_library = lib . name ;
2022-04-07 13:49:28 +02:00
ad . name = key ;
ad . last_update = animation_set_update_pass ;
animation_set . insert ( ad . name , ad ) ;
} else {
AnimationData & ad = animation_set [ key ] ;
if ( ad . last_update ! = animation_set_update_pass ) {
// Was not updated, update. If the animation is duplicated, the second one will be ignored.
2022-12-29 01:24:45 +01:00
if ( ad . animation ! = K . value | | ad . animation_library ! = lib . name ) {
2022-04-07 13:49:28 +02:00
// Animation changed, update and clear caches.
clear_cache_needed = true ;
ad . animation = K . value ;
2022-12-29 01:24:45 +01:00
ad . animation_library = lib . name ;
2022-04-07 13:49:28 +02:00
}
2014-02-10 02:10:30 +01:00
2022-04-07 13:49:28 +02:00
ad . last_update = animation_set_update_pass ;
}
}
}
}
2014-02-10 02:10:30 +01:00
2022-04-07 13:49:28 +02:00
// Check removed
List < StringName > to_erase ;
for ( const KeyValue < StringName , AnimationData > & E : animation_set ) {
if ( E . value . last_update ! = animation_set_update_pass ) {
// Was not updated, must be erased
to_erase . push_back ( E . key ) ;
clear_cache_needed = true ;
}
}
2016-03-09 00:00:52 +01:00
2022-04-07 13:49:28 +02:00
while ( to_erase . size ( ) ) {
animation_set . erase ( to_erase . front ( ) - > get ( ) ) ;
to_erase . pop_front ( ) ;
}
2014-02-10 02:10:30 +01:00
2022-04-07 13:49:28 +02:00
if ( clear_cache_needed ) {
// If something was modified or removed, caches need to be cleared
clear_caches ( ) ;
}
2022-09-01 16:00:55 +02:00
emit_signal ( SNAME ( " animation_list_changed " ) ) ;
2014-02-10 02:10:30 +01:00
}
2022-04-07 13:49:28 +02:00
void AnimationPlayer : : _animation_added ( const StringName & p_name , const StringName & p_library ) {
_animation_set_cache_update ( ) ;
2014-02-10 02:10:30 +01:00
}
2022-04-07 13:49:28 +02:00
void AnimationPlayer : : _animation_removed ( const StringName & p_name , const StringName & p_library ) {
StringName name = p_library = = StringName ( ) ? p_name : StringName ( String ( p_library ) + " / " + String ( p_name ) ) ;
2014-02-10 02:10:30 +01:00
2022-04-07 13:49:28 +02:00
if ( ! animation_set . has ( name ) ) {
return ; // No need to update because not the one from the library being used.
}
2022-05-05 05:53:47 +02:00
2022-04-07 13:49:28 +02:00
_animation_set_cache_update ( ) ;
// Erase blends if needed
List < BlendKey > to_erase ;
2022-09-22 15:54:15 +02:00
for ( const KeyValue < BlendKey , double > & E : blend_times ) {
2022-04-07 13:49:28 +02:00
BlendKey bk = E . key ;
if ( bk . from = = name | | bk . to = = name ) {
to_erase . push_back ( bk ) ;
}
}
while ( to_erase . size ( ) ) {
blend_times . erase ( to_erase . front ( ) - > get ( ) ) ;
to_erase . pop_front ( ) ;
}
}
2014-02-10 02:10:30 +01:00
2022-04-07 13:49:28 +02:00
void AnimationPlayer : : _rename_animation ( const StringName & p_from_name , const StringName & p_to_name ) {
// Rename autoplay or blends if needed.
2014-02-10 02:10:30 +01:00
List < BlendKey > to_erase ;
2022-09-22 15:54:15 +02:00
HashMap < BlendKey , double , BlendKey > to_insert ;
for ( const KeyValue < BlendKey , double > & E : blend_times ) {
2021-08-09 22:13:42 +02:00
BlendKey bk = E . key ;
2014-02-10 02:10:30 +01:00
BlendKey new_bk = bk ;
bool erase = false ;
2022-04-07 13:49:28 +02:00
if ( bk . from = = p_from_name ) {
new_bk . from = p_to_name ;
2014-02-10 02:10:30 +01:00
erase = true ;
}
2022-04-07 13:49:28 +02:00
if ( bk . to = = p_from_name ) {
new_bk . to = p_to_name ;
2014-02-10 02:10:30 +01:00
erase = true ;
}
if ( erase ) {
to_erase . push_back ( bk ) ;
2021-08-09 22:13:42 +02:00
to_insert [ new_bk ] = E . value ;
2014-02-10 02:10:30 +01:00
}
}
while ( to_erase . size ( ) ) {
blend_times . erase ( to_erase . front ( ) - > get ( ) ) ;
2017-01-14 18:03:38 +01:00
to_erase . pop_front ( ) ;
2014-02-10 02:10:30 +01:00
}
while ( to_insert . size ( ) ) {
2022-05-13 15:04:37 +02:00
blend_times [ to_insert . begin ( ) - > key ] = to_insert . begin ( ) - > value ;
to_insert . remove ( to_insert . begin ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2022-04-07 13:49:28 +02:00
if ( autoplay = = p_from_name ) {
autoplay = p_to_name ;
2020-05-14 16:41:43 +02:00
}
2022-04-07 13:49:28 +02:00
}
void AnimationPlayer : : _animation_renamed ( const StringName & p_name , const StringName & p_to_name , const StringName & p_library ) {
StringName from_name = p_library = = StringName ( ) ? p_name : StringName ( String ( p_library ) + " / " + String ( p_name ) ) ;
StringName to_name = p_library = = StringName ( ) ? p_to_name : StringName ( String ( p_library ) + " / " + String ( p_to_name ) ) ;
if ( ! animation_set . has ( from_name ) ) {
return ; // No need to update because not the one from the library being used.
}
_animation_set_cache_update ( ) ;
_rename_animation ( from_name , to_name ) ;
}
Error AnimationPlayer : : add_animation_library ( const StringName & p_name , const Ref < AnimationLibrary > & p_animation_library ) {
ERR_FAIL_COND_V ( p_animation_library . is_null ( ) , ERR_INVALID_PARAMETER ) ;
# ifdef DEBUG_ENABLED
ERR_FAIL_COND_V_MSG ( String ( p_name ) . contains ( " / " ) | | String ( p_name ) . contains ( " : " ) | | String ( p_name ) . contains ( " , " ) | | String ( p_name ) . contains ( " [ " ) , ERR_INVALID_PARAMETER , " Invalid animation name: " + String ( p_name ) + " . " ) ;
# endif
int insert_pos = 0 ;
2022-12-29 01:24:45 +01:00
for ( const AnimationLibraryData & lib : animation_libraries ) {
ERR_FAIL_COND_V_MSG ( lib . name = = p_name , ERR_ALREADY_EXISTS , " Can't add animation library twice with name: " + String ( p_name ) ) ;
ERR_FAIL_COND_V_MSG ( lib . library = = p_animation_library , ERR_ALREADY_EXISTS , " Can't add animation library twice (adding as ' " + p_name . operator String ( ) + " ', exists as ' " + lib . name . operator String ( ) + " '. " ) ;
2022-04-07 13:49:28 +02:00
2022-12-29 01:24:45 +01:00
if ( lib . name . operator String ( ) > = p_name . operator String ( ) ) {
2022-04-07 13:49:28 +02:00
break ;
}
insert_pos + + ;
}
AnimationLibraryData ald ;
ald . name = p_name ;
ald . library = p_animation_library ;
animation_libraries . insert ( insert_pos , ald ) ;
2022-07-28 22:56:41 +02:00
ald . library - > connect ( SNAME ( " animation_added " ) , callable_mp ( this , & AnimationPlayer : : _animation_added ) . bind ( p_name ) ) ;
2022-10-10 23:42:12 +02:00
ald . library - > connect ( SNAME ( " animation_removed " ) , callable_mp ( this , & AnimationPlayer : : _animation_removed ) . bind ( p_name ) ) ;
2022-07-28 22:56:41 +02:00
ald . library - > connect ( SNAME ( " animation_renamed " ) , callable_mp ( this , & AnimationPlayer : : _animation_renamed ) . bind ( p_name ) ) ;
2022-06-12 03:14:40 +02:00
ald . library - > connect ( SNAME ( " animation_changed " ) , callable_mp ( this , & AnimationPlayer : : _animation_changed ) ) ;
2022-05-05 05:53:47 +02:00
2022-04-07 13:49:28 +02:00
_animation_set_cache_update ( ) ;
notify_property_list_changed ( ) ;
return OK ;
}
void AnimationPlayer : : remove_animation_library ( const StringName & p_name ) {
int at_pos = - 1 ;
for ( uint32_t i = 0 ; i < animation_libraries . size ( ) ; i + + ) {
if ( animation_libraries [ i ] . name = = p_name ) {
at_pos = i ;
break ;
}
}
ERR_FAIL_COND ( at_pos = = - 1 ) ;
animation_libraries [ at_pos ] . library - > disconnect ( SNAME ( " animation_added " ) , callable_mp ( this , & AnimationPlayer : : _animation_added ) ) ;
2022-05-05 05:53:47 +02:00
animation_libraries [ at_pos ] . library - > disconnect ( SNAME ( " animation_removed " ) , callable_mp ( this , & AnimationPlayer : : _animation_removed ) ) ;
2022-04-07 13:49:28 +02:00
animation_libraries [ at_pos ] . library - > disconnect ( SNAME ( " animation_renamed " ) , callable_mp ( this , & AnimationPlayer : : _animation_renamed ) ) ;
2022-06-12 03:14:40 +02:00
animation_libraries [ at_pos ] . library - > disconnect ( SNAME ( " animation_changed " ) , callable_mp ( this , & AnimationPlayer : : _animation_changed ) ) ;
2022-04-07 13:49:28 +02:00
stop ( ) ;
animation_libraries . remove_at ( at_pos ) ;
_animation_set_cache_update ( ) ;
2014-02-10 02:10:30 +01:00
2021-02-10 21:18:45 +01:00
notify_property_list_changed ( ) ;
2022-04-07 13:49:28 +02:00
}
void AnimationPlayer : : rename_animation_library ( const StringName & p_name , const StringName & p_new_name ) {
if ( p_name = = p_new_name ) {
return ;
}
# ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG ( String ( p_new_name ) . contains ( " / " ) | | String ( p_new_name ) . contains ( " : " ) | | String ( p_new_name ) . contains ( " , " ) | | String ( p_new_name ) . contains ( " [ " ) , " Invalid animation library name: " + String ( p_new_name ) + " . " ) ;
# endif
bool found = false ;
2022-12-29 01:24:45 +01:00
for ( AnimationLibraryData & lib : animation_libraries ) {
ERR_FAIL_COND_MSG ( lib . name = = p_new_name , " Can't rename animation library to another existing name: " + String ( p_new_name ) ) ;
if ( lib . name = = p_name ) {
2022-04-07 13:49:28 +02:00
found = true ;
2022-12-29 01:24:45 +01:00
lib . name = p_new_name ;
2022-04-07 13:49:28 +02:00
// rename connections
2022-12-29 01:24:45 +01:00
lib . library - > disconnect ( SNAME ( " animation_added " ) , callable_mp ( this , & AnimationPlayer : : _animation_added ) ) ;
lib . library - > disconnect ( SNAME ( " animation_removed " ) , callable_mp ( this , & AnimationPlayer : : _animation_removed ) ) ;
lib . library - > disconnect ( SNAME ( " animation_renamed " ) , callable_mp ( this , & AnimationPlayer : : _animation_renamed ) ) ;
2022-04-07 13:49:28 +02:00
2022-12-29 01:24:45 +01:00
lib . library - > connect ( SNAME ( " animation_added " ) , callable_mp ( this , & AnimationPlayer : : _animation_added ) . bind ( p_new_name ) ) ;
lib . library - > connect ( SNAME ( " animation_removed " ) , callable_mp ( this , & AnimationPlayer : : _animation_removed ) . bind ( p_new_name ) ) ;
lib . library - > connect ( SNAME ( " animation_renamed " ) , callable_mp ( this , & AnimationPlayer : : _animation_renamed ) . bind ( p_new_name ) ) ;
2022-04-07 13:49:28 +02:00
2022-12-29 01:24:45 +01:00
for ( const KeyValue < StringName , Ref < Animation > > & K : lib . library - > animations ) {
2022-04-07 13:49:28 +02:00
StringName old_name = p_name = = StringName ( ) ? K . key : StringName ( String ( p_name ) + " / " + String ( K . key ) ) ;
StringName new_name = p_new_name = = StringName ( ) ? K . key : StringName ( String ( p_new_name ) + " / " + String ( K . key ) ) ;
_rename_animation ( old_name , new_name ) ;
}
}
}
ERR_FAIL_COND ( ! found ) ;
stop ( ) ;
animation_libraries . sort ( ) ; // Must keep alphabetical order.
_animation_set_cache_update ( ) ; // Update cache.
notify_property_list_changed ( ) ;
}
bool AnimationPlayer : : has_animation_library ( const StringName & p_name ) const {
2022-12-29 01:24:45 +01:00
for ( const AnimationLibraryData & lib : animation_libraries ) {
if ( lib . name = = p_name ) {
2022-04-07 13:49:28 +02:00
return true ;
}
}
return false ;
}
Ref < AnimationLibrary > AnimationPlayer : : get_animation_library ( const StringName & p_name ) const {
2022-12-29 01:24:45 +01:00
for ( const AnimationLibraryData & lib : animation_libraries ) {
if ( lib . name = = p_name ) {
return lib . library ;
2022-04-07 13:49:28 +02:00
}
}
ERR_FAIL_V ( Ref < AnimationLibrary > ( ) ) ;
}
TypedArray < StringName > AnimationPlayer : : _get_animation_library_list ( ) const {
TypedArray < StringName > ret ;
2022-12-29 01:24:45 +01:00
for ( const AnimationLibraryData & lib : animation_libraries ) {
ret . push_back ( lib . name ) ;
2022-04-07 13:49:28 +02:00
}
return ret ;
}
void AnimationPlayer : : get_animation_library_list ( List < StringName > * p_libraries ) const {
2022-12-29 01:24:45 +01:00
for ( const AnimationLibraryData & lib : animation_libraries ) {
p_libraries - > push_back ( lib . name ) ;
2022-04-07 13:49:28 +02:00
}
}
2014-02-10 02:10:30 +01:00
bool AnimationPlayer : : has_animation ( const StringName & p_name ) const {
return animation_set . has ( p_name ) ;
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
Ref < Animation > AnimationPlayer : : get_animation ( const StringName & p_name ) const {
2022-08-29 08:18:01 +02:00
ERR_FAIL_COND_V_MSG ( ! animation_set . has ( p_name ) , Ref < Animation > ( ) , vformat ( " Animation not found: \" %s \" . " , p_name ) ) ;
2014-02-10 02:10:30 +01:00
2022-09-29 11:53:28 +02:00
const AnimationData & anim_data = animation_set [ p_name ] ;
2016-03-09 00:00:52 +01:00
2022-09-29 11:53:28 +02:00
return anim_data . animation ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : get_animation_list ( List < StringName > * p_animations ) const {
List < String > anims ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , AnimationData > & E : animation_set ) {
anims . push_back ( E . key ) ;
2014-02-10 02:10:30 +01:00
}
anims . sort ( ) ;
2021-07-24 15:46:25 +02:00
for ( const String & E : anims ) {
2021-07-16 05:45:57 +02:00
p_animations - > push_back ( E ) ;
2014-02-10 02:10:30 +01:00
}
}
2022-09-22 15:54:15 +02:00
void AnimationPlayer : : set_blend_time ( const StringName & p_animation1 , const StringName & p_animation2 , double p_time ) {
2022-05-14 18:54:14 +02:00
ERR_FAIL_COND_MSG ( ! animation_set . has ( p_animation1 ) , vformat ( " Animation not found: %s. " , p_animation1 ) ) ;
ERR_FAIL_COND_MSG ( ! animation_set . has ( p_animation2 ) , vformat ( " Animation not found: %s. " , p_animation2 ) ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( p_time < 0 , " Blend time cannot be smaller than 0. " ) ;
2014-02-10 02:10:30 +01:00
BlendKey bk ;
bk . from = p_animation1 ;
bk . to = p_animation2 ;
2020-05-14 16:41:43 +02:00
if ( p_time = = 0 ) {
2014-02-10 02:10:30 +01:00
blend_times . erase ( bk ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
blend_times [ bk ] = p_time ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-09-22 15:54:15 +02:00
double AnimationPlayer : : get_blend_time ( const StringName & p_animation1 , const StringName & p_animation2 ) const {
2014-02-10 02:10:30 +01:00
BlendKey bk ;
bk . from = p_animation1 ;
bk . to = p_animation2 ;
2020-05-14 16:41:43 +02:00
if ( blend_times . has ( bk ) ) {
2014-02-10 02:10:30 +01:00
return blend_times [ bk ] ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
void AnimationPlayer : : queue ( const StringName & p_name ) {
2020-05-14 16:41:43 +02:00
if ( ! is_playing ( ) ) {
2014-02-10 02:10:30 +01:00
play ( p_name ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
queued . push_back ( p_name ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-02-17 22:06:54 +01:00
Vector < String > AnimationPlayer : : get_queue ( ) {
Vector < String > ret ;
2021-07-24 15:46:25 +02:00
for ( const StringName & E : queued ) {
2021-07-16 05:45:57 +02:00
ret . push_back ( E ) ;
2018-11-28 01:43:34 +01:00
}
return ret ;
}
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : clear_queue ( ) {
queued . clear ( ) ;
2018-06-07 17:46:14 +02:00
}
2014-02-10 02:10:30 +01:00
2022-09-22 15:54:15 +02:00
void AnimationPlayer : : play_backwards ( const StringName & p_name , double p_custom_blend ) {
2015-09-10 05:10:54 +02:00
play ( p_name , p_custom_blend , - 1 , true ) ;
}
2022-09-22 15:54:15 +02:00
void AnimationPlayer : : play ( const StringName & p_name , double p_custom_blend , float p_custom_scale , bool p_from_end ) {
2014-02-10 02:10:30 +01:00
StringName name = p_name ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( String ( name ) = = " " ) {
2014-02-10 02:10:30 +01:00
name = playback . assigned ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-05-14 18:54:14 +02:00
ERR_FAIL_COND_MSG ( ! animation_set . has ( name ) , vformat ( " Animation not found: %s. " , name ) ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
Playback & c = playback ;
if ( c . current . from ) {
2022-09-22 15:54:15 +02:00
double blend_time = 0.0 ;
2014-02-10 02:10:30 +01:00
// find if it can blend
BlendKey bk ;
bk . from = c . current . from - > name ;
bk . to = name ;
if ( p_custom_blend > = 0 ) {
blend_time = p_custom_blend ;
} else if ( blend_times . has ( bk ) ) {
blend_time = blend_times [ bk ] ;
} else {
bk . from = " * " ;
if ( blend_times . has ( bk ) ) {
blend_time = blend_times [ bk ] ;
} else {
bk . from = c . current . from - > name ;
bk . to = " * " ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
if ( blend_times . has ( bk ) ) {
blend_time = blend_times [ bk ] ;
}
}
}
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( p_custom_blend < 0 & & blend_time = = 0 & & default_blend_time ) {
2014-02-10 02:10:30 +01:00
blend_time = default_blend_time ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
if ( blend_time > 0 ) {
Blend b ;
b . data = c . current ;
b . blend_time = b . blend_left = blend_time ;
2016-03-09 00:00:52 +01:00
c . blend . push_back ( b ) ;
2019-11-13 04:16:13 +01:00
} else {
c . blend . clear ( ) ;
2014-02-10 02:10:30 +01:00
}
}
2016-03-09 00:00:52 +01:00
2019-08-19 13:48:19 +02:00
if ( get_current_animation ( ) ! = p_name ) {
2023-01-27 19:25:49 +01:00
_clear_audio_streams ( ) ;
2019-11-19 14:39:10 +01:00
_stop_playing_caches ( false ) ;
2019-08-19 13:48:19 +02:00
}
2018-06-07 17:46:14 +02:00
2014-02-10 02:10:30 +01:00
c . current . from = & animation_set [ name ] ;
2019-02-13 18:40:22 +01:00
if ( c . assigned ! = name ) { // reset
c . current . pos = p_from_end ? c . current . from - > animation - > get_length ( ) : 0 ;
2019-02-20 19:58:53 +01:00
} else {
if ( p_from_end & & c . current . pos = = 0 ) {
// Animation reset BUT played backwards, set position to the end
c . current . pos = c . current . from - > animation - > get_length ( ) ;
} else if ( ! p_from_end & & c . current . pos = = c . current . from - > animation - > get_length ( ) ) {
2019-05-19 12:34:40 +02:00
// Animation resumed but already ended, set position to the beginning
2019-02-20 19:58:53 +01:00
c . current . pos = 0 ;
}
2019-02-13 18:40:22 +01:00
}
2014-02-10 02:10:30 +01:00
c . current . speed_scale = p_custom_scale ;
2019-02-13 18:40:22 +01:00
c . assigned = name ;
2018-06-07 17:46:14 +02:00
c . seeked = false ;
c . started = true ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! end_reached ) {
2014-08-14 15:31:38 +02:00
queued . clear ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
_set_process ( true ) ; // always process when starting an animation
playing = true ;
2016-05-21 15:29:25 +02:00
2016-04-14 17:19:20 +02:00
emit_signal ( SceneStringNames : : get_singleton ( ) - > animation_started , c . assigned ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( is_inside_tree ( ) & & Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2014-02-10 02:10:30 +01:00
return ; // no next in this case
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
StringName next = animation_get_next ( p_name ) ;
2014-10-28 02:54:32 +01:00
if ( next ! = StringName ( ) & & animation_set . has ( next ) ) {
2014-02-10 02:10:30 +01:00
queue ( next ) ;
}
}
bool AnimationPlayer : : is_playing ( ) const {
return playing ;
}
2018-01-14 11:28:57 +01:00
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : set_current_animation ( const String & p_anim ) {
2021-12-09 10:42:46 +01:00
if ( p_anim = = " [stop] " | | p_anim . is_empty ( ) ) {
2018-01-11 23:35:12 +01:00
stop ( ) ;
2023-01-21 06:51:03 +01:00
} else if ( ! is_playing ( ) ) {
2014-02-10 02:10:30 +01:00
play ( p_anim ) ;
2023-01-21 06:51:03 +01:00
} else if ( playback . assigned ! = p_anim ) {
2023-05-26 07:11:29 +02:00
float speed = playback . current . speed_scale ;
2023-01-21 06:51:03 +01:00
play ( p_anim , - 1.0 , speed , signbit ( speed ) ) ;
2014-02-10 02:10:30 +01:00
} else {
2018-01-11 23:35:12 +01:00
// Same animation, do not replay from start
2014-02-10 02:10:30 +01:00
}
}
String AnimationPlayer : : get_current_animation ( ) const {
2018-01-11 23:35:12 +01:00
return ( is_playing ( ) ? playback . assigned : " " ) ;
2014-02-10 02:10:30 +01:00
}
2018-01-14 11:28:57 +01:00
void AnimationPlayer : : set_assigned_animation ( const String & p_anim ) {
if ( is_playing ( ) ) {
2023-05-26 07:11:29 +02:00
float speed = playback . current . speed_scale ;
2023-01-21 06:51:03 +01:00
play ( p_anim , - 1.0 , speed , signbit ( speed ) ) ;
2018-01-14 11:28:57 +01:00
} else {
2022-05-14 18:54:14 +02:00
ERR_FAIL_COND_MSG ( ! animation_set . has ( p_anim ) , vformat ( " Animation not found: %s. " , p_anim ) ) ;
2018-01-14 11:28:57 +01:00
playback . current . pos = 0 ;
playback . current . from = & animation_set [ p_anim ] ;
playback . assigned = p_anim ;
}
}
String AnimationPlayer : : get_assigned_animation ( ) const {
return playback . assigned ;
}
2023-01-11 13:39:39 +01:00
void AnimationPlayer : : pause ( ) {
2023-01-18 14:02:04 +01:00
_stop_internal ( false , false ) ;
2023-01-11 13:39:39 +01:00
}
2023-01-18 14:02:04 +01:00
void AnimationPlayer : : stop ( bool p_keep_state ) {
_stop_internal ( true , p_keep_state ) ;
2014-02-10 02:10:30 +01:00
}
2017-01-13 23:36:04 +01:00
void AnimationPlayer : : set_speed_scale ( float p_speed ) {
2014-02-10 02:10:30 +01:00
speed_scale = p_speed ;
}
2020-05-14 14:29:06 +02:00
2017-01-13 23:36:04 +01:00
float AnimationPlayer : : get_speed_scale ( ) const {
2014-02-10 02:10:30 +01:00
return speed_scale ;
}
2020-05-14 14:29:06 +02:00
2018-03-01 19:52:00 +01:00
float AnimationPlayer : : get_playing_speed ( ) const {
if ( ! playing ) {
return 0 ;
}
return speed_scale * playback . current . speed_scale ;
}
2014-02-10 02:10:30 +01:00
2021-05-21 08:42:37 +02:00
void AnimationPlayer : : seek ( double p_time , bool p_update ) {
2023-01-30 15:23:18 +01:00
playback . current . pos = p_time ;
2014-02-10 02:10:30 +01:00
if ( ! playback . current . from ) {
2018-01-11 23:35:12 +01:00
if ( playback . assigned ) {
2022-05-14 18:54:14 +02:00
ERR_FAIL_COND_MSG ( ! animation_set . has ( playback . assigned ) , vformat ( " Animation not found: %s. " , playback . assigned ) ) ;
2018-01-11 23:35:12 +01:00
playback . current . from = & animation_set [ playback . assigned ] ;
}
2023-01-30 15:23:18 +01:00
if ( ! playback . current . from ) {
return ; // There is no animation.
}
2014-02-10 02:10:30 +01:00
}
2018-06-07 17:46:14 +02:00
playback . seeked = true ;
2014-02-10 02:10:30 +01:00
if ( p_update ) {
_animation_process ( 0 ) ;
}
}
2022-09-22 15:54:15 +02:00
void AnimationPlayer : : seek_delta ( double p_time , double p_delta ) {
2023-01-30 15:23:18 +01:00
playback . current . pos = p_time - p_delta ;
2014-02-10 02:10:30 +01:00
if ( ! playback . current . from ) {
2018-01-11 23:35:12 +01:00
if ( playback . assigned ) {
2022-05-14 18:54:14 +02:00
ERR_FAIL_COND_MSG ( ! animation_set . has ( playback . assigned ) , vformat ( " Animation not found: %s. " , playback . assigned ) ) ;
2018-01-11 23:35:12 +01:00
playback . current . from = & animation_set [ playback . assigned ] ;
}
2023-01-30 15:23:18 +01:00
if ( ! playback . current . from ) {
return ; // There is no animation.
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( speed_scale ! = 0.0 ) {
2014-02-10 02:10:30 +01:00
p_delta / = speed_scale ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
_animation_process ( p_delta ) ;
}
bool AnimationPlayer : : is_valid ( ) const {
return ( playback . current . from ) ;
}
2022-09-22 15:54:15 +02:00
double AnimationPlayer : : get_current_animation_position ( ) const {
2020-10-06 07:21:23 +02:00
ERR_FAIL_COND_V_MSG ( ! playback . current . from , 0 , " AnimationPlayer has no current animation " ) ;
2014-02-10 02:10:30 +01:00
return playback . current . pos ;
}
2022-09-22 15:54:15 +02:00
double AnimationPlayer : : get_current_animation_length ( ) const {
2020-10-06 07:21:23 +02:00
ERR_FAIL_COND_V_MSG ( ! playback . current . from , 0 , " AnimationPlayer has no current animation " ) ;
2014-02-10 02:10:30 +01:00
return playback . current . from - > animation - > get_length ( ) ;
}
2022-06-12 03:14:40 +02:00
void AnimationPlayer : : _animation_changed ( const StringName & p_name ) {
2014-02-10 02:10:30 +01:00
clear_caches ( ) ;
2018-11-19 17:14:37 +01:00
if ( is_playing ( ) ) {
playback . seeked = true ; //need to restart stuff, like audio
}
2014-02-10 02:10:30 +01:00
}
2019-11-19 14:39:10 +01:00
void AnimationPlayer : : _stop_playing_caches ( bool p_reset ) {
2022-05-19 01:43:40 +02:00
for ( TrackNodeCache * E : playing_caches ) {
if ( E - > node & & E - > audio_playing ) {
E - > node - > call ( SNAME ( " stop " ) ) ;
2018-06-07 17:46:14 +02:00
}
2022-05-19 01:43:40 +02:00
if ( E - > node & & E - > animation_playing ) {
AnimationPlayer * player = Object : : cast_to < AnimationPlayer > ( E - > node ) ;
2020-05-14 16:41:43 +02:00
if ( ! player ) {
2018-06-07 17:46:14 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2019-11-19 14:39:10 +01:00
if ( p_reset ) {
player - > stop ( ) ;
} else {
player - > pause ( ) ;
}
2018-06-07 17:46:14 +02:00
}
}
playing_caches . clear ( ) ;
}
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : _node_removed ( Node * p_node ) {
2021-03-12 14:35:16 +01:00
clear_caches ( ) ; // nodes contained here are being removed, clear the caches
2014-02-10 02:10:30 +01:00
}
void AnimationPlayer : : clear_caches ( ) {
2023-01-27 19:25:49 +01:00
_clear_audio_streams ( ) ;
2019-11-19 14:39:10 +01:00
_stop_playing_caches ( true ) ;
2018-06-07 17:46:14 +02:00
2014-02-10 02:10:30 +01:00
node_cache_map . clear ( ) ;
2021-08-09 22:13:42 +02:00
for ( KeyValue < StringName , AnimationData > & E : animation_set ) {
E . value . node_cache . clear ( ) ;
2014-02-10 02:10:30 +01:00
}
cache_update_size = 0 ;
cache_update_prop_size = 0 ;
2018-06-07 17:46:14 +02:00
cache_update_bezier_size = 0 ;
2023-01-27 19:25:49 +01:00
cache_update_audio_size = 0 ;
2022-12-02 06:05:04 +01:00
emit_signal ( SNAME ( " caches_cleared " ) ) ;
2014-02-10 02:10:30 +01:00
}
2023-01-27 19:25:49 +01:00
void AnimationPlayer : : _clear_audio_streams ( ) {
for ( int i = 0 ; i < playing_audio_stream_players . size ( ) ; i + + ) {
playing_audio_stream_players [ i ] - > call ( SNAME ( " stop " ) ) ;
playing_audio_stream_players [ i ] - > call ( SNAME ( " set_stream " ) , Ref < AudioStream > ( ) ) ;
}
playing_audio_stream_players . clear ( ) ;
}
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : set_active ( bool p_active ) {
2020-05-14 16:41:43 +02:00
if ( active = = p_active ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
active = p_active ;
_set_process ( processing , true ) ;
}
bool AnimationPlayer : : is_active ( ) const {
return active ;
}
StringName AnimationPlayer : : find_animation ( const Ref < Animation > & p_animation ) const {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , AnimationData > & E : animation_set ) {
if ( E . value . animation = = p_animation ) {
return E . key ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-04-07 13:49:28 +02:00
return StringName ( ) ;
}
StringName AnimationPlayer : : find_animation_library ( const Ref < Animation > & p_animation ) const {
for ( const KeyValue < StringName , AnimationData > & E : animation_set ) {
if ( E . value . animation = = p_animation ) {
return E . value . animation_library ;
}
}
return StringName ( ) ;
2014-02-10 02:10:30 +01:00
}
void AnimationPlayer : : set_autoplay ( const String & p_name ) {
2020-05-14 16:41:43 +02:00
if ( is_inside_tree ( ) & & ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2018-10-05 00:09:53 +02:00
WARN_PRINT ( " Setting autoplay after the node has been added to the scene has no effect. " ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
autoplay = p_name ;
}
String AnimationPlayer : : get_autoplay ( ) const {
return autoplay ;
}
2020-12-20 11:46:44 +01:00
void AnimationPlayer : : set_reset_on_save_enabled ( bool p_enabled ) {
reset_on_save = p_enabled ;
}
bool AnimationPlayer : : is_reset_on_save_enabled ( ) const {
return reset_on_save ;
}
2021-02-18 19:52:29 +01:00
void AnimationPlayer : : set_process_callback ( AnimationProcessCallback p_mode ) {
if ( process_callback = = p_mode ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
bool pr = processing ;
2020-05-14 16:41:43 +02:00
if ( pr ) {
2014-02-10 02:10:30 +01:00
_set_process ( false ) ;
2020-05-14 16:41:43 +02:00
}
2021-02-18 19:52:29 +01:00
process_callback = p_mode ;
2020-05-14 16:41:43 +02:00
if ( pr ) {
2014-02-10 02:10:30 +01:00
_set_process ( true ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2021-02-18 19:52:29 +01:00
AnimationPlayer : : AnimationProcessCallback AnimationPlayer : : get_process_callback ( ) const {
return process_callback ;
2014-02-10 02:10:30 +01:00
}
2018-12-08 19:56:20 +01:00
void AnimationPlayer : : set_method_call_mode ( AnimationMethodCallMode p_mode ) {
method_call_mode = p_mode ;
}
AnimationPlayer : : AnimationMethodCallMode AnimationPlayer : : get_method_call_mode ( ) const {
return method_call_mode ;
}
2023-01-27 19:25:49 +01:00
void AnimationPlayer : : set_audio_max_polyphony ( int p_audio_max_polyphony ) {
ERR_FAIL_COND ( p_audio_max_polyphony < 0 | | p_audio_max_polyphony > 128 ) ;
audio_max_polyphony = p_audio_max_polyphony ;
}
int AnimationPlayer : : get_audio_max_polyphony ( ) const {
return audio_max_polyphony ;
}
2022-06-26 01:38:20 +02:00
void AnimationPlayer : : set_movie_quit_on_finish_enabled ( bool p_enabled ) {
movie_quit_on_finish = p_enabled ;
}
bool AnimationPlayer : : is_movie_quit_on_finish_enabled ( ) const {
return movie_quit_on_finish ;
}
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : _set_process ( bool p_process , bool p_force ) {
2020-05-14 16:41:43 +02:00
if ( processing = = p_process & & ! p_force ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-02-18 19:52:29 +01:00
switch ( process_callback ) {
2020-05-10 13:00:47 +02:00
case ANIMATION_PROCESS_PHYSICS :
set_physics_process_internal ( p_process & & active ) ;
break ;
case ANIMATION_PROCESS_IDLE :
set_process_internal ( p_process & & active ) ;
break ;
case ANIMATION_PROCESS_MANUAL :
break ;
2014-02-10 02:10:30 +01:00
}
processing = p_process ;
}
2023-01-18 14:02:04 +01:00
void AnimationPlayer : : _stop_internal ( bool p_reset , bool p_keep_state ) {
2023-01-27 19:25:49 +01:00
_clear_audio_streams ( ) ;
2019-11-19 14:39:10 +01:00
_stop_playing_caches ( p_reset ) ;
2023-01-11 13:39:39 +01:00
Playback & c = playback ;
c . blend . clear ( ) ;
if ( p_reset ) {
2023-01-18 14:02:04 +01:00
if ( p_keep_state ) {
c . current . pos = 0 ;
} else {
is_stopping = true ;
seek ( 0 , true ) ;
is_stopping = false ;
}
2023-01-11 13:39:39 +01:00
c . current . from = nullptr ;
c . current . speed_scale = 1 ;
}
_set_process ( false ) ;
queued . clear ( ) ;
playing = false ;
}
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : animation_set_next ( const StringName & p_animation , const StringName & p_next ) {
2022-05-14 18:54:14 +02:00
ERR_FAIL_COND_MSG ( ! animation_set . has ( p_animation ) , vformat ( " Animation not found: %s. " , p_animation ) ) ;
2014-02-10 02:10:30 +01:00
animation_set [ p_animation ] . next = p_next ;
}
StringName AnimationPlayer : : animation_get_next ( const StringName & p_animation ) const {
2020-05-14 16:41:43 +02:00
if ( ! animation_set . has ( p_animation ) ) {
2014-02-10 02:10:30 +01:00
return StringName ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return animation_set [ p_animation ] . next ;
}
2022-09-22 15:54:15 +02:00
void AnimationPlayer : : set_default_blend_time ( double p_default ) {
2014-02-10 02:10:30 +01:00
default_blend_time = p_default ;
}
2022-09-22 15:54:15 +02:00
double AnimationPlayer : : get_default_blend_time ( ) const {
2014-02-10 02:10:30 +01:00
return default_blend_time ;
}
void AnimationPlayer : : set_root ( const NodePath & p_root ) {
root = p_root ;
clear_caches ( ) ;
}
NodePath AnimationPlayer : : get_root ( ) const {
return root ;
}
2014-12-17 02:31:57 +01:00
void AnimationPlayer : : get_argument_options ( const StringName & p_function , int p_idx , List < String > * r_options ) const {
String pf = p_function ;
2022-06-19 05:08:19 +02:00
if ( p_idx = = 0 & & ( p_function = = " play " | | p_function = = " play_backwards " | | p_function = = " has_animation " | | p_function = = " queue " ) ) {
2014-12-17 02:31:57 +01:00
List < StringName > al ;
get_animation_list ( & al ) ;
2021-08-13 21:24:02 +02:00
for ( const StringName & name : al ) {
2021-10-01 17:06:48 +02:00
r_options - > push_back ( String ( name ) . quote ( ) ) ;
2014-12-17 02:31:57 +01:00
}
}
Node : : get_argument_options ( p_function , p_idx , r_options ) ;
}
2014-02-10 02:10:30 +01:00
2017-11-01 21:32:39 +01:00
# ifdef TOOLS_ENABLED
2021-02-09 21:41:48 +01:00
Ref < AnimatedValuesBackup > AnimationPlayer : : backup_animated_values ( Node * p_root_override ) {
2020-12-20 11:46:44 +01:00
Ref < AnimatedValuesBackup > backup ;
2020-05-14 16:41:43 +02:00
if ( ! playback . current . from ) {
2020-12-20 11:46:44 +01:00
return backup ;
2020-05-14 16:41:43 +02:00
}
2017-11-01 21:32:39 +01:00
2021-02-09 21:41:48 +01:00
_ensure_node_caches ( playback . current . from , p_root_override ) ;
2017-11-01 21:32:39 +01:00
2021-06-18 00:03:09 +02:00
backup . instantiate ( ) ;
2017-11-01 21:32:39 +01:00
for ( int i = 0 ; i < playback . current . from - > node_cache . size ( ) ; i + + ) {
TrackNodeCache * nc = playback . current . from - > node_cache [ i ] ;
2020-05-14 16:41:43 +02:00
if ( ! nc ) {
2017-11-01 21:32:39 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-11-01 21:32:39 +01:00
if ( nc - > skeleton ) {
2020-05-14 16:41:43 +02:00
if ( nc - > bone_idx = = - 1 ) {
2017-11-01 21:32:39 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-11-01 21:32:39 +01:00
AnimatedValuesBackup : : Entry entry ;
entry . object = nc - > skeleton ;
entry . bone_idx = nc - > bone_idx ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-12 00:20:58 +02:00
Array arr ;
arr . resize ( 3 ) ;
arr [ 0 ] = nc - > skeleton - > get_bone_pose_position ( nc - > bone_idx ) ;
arr [ 1 ] = nc - > skeleton - > get_bone_pose_rotation ( nc - > bone_idx ) ;
arr [ 2 ] = nc - > skeleton - > get_bone_pose_scale ( nc - > bone_idx ) ;
entry . value = nc ;
2020-12-20 11:46:44 +01:00
backup - > entries . push_back ( entry ) ;
2017-11-01 21:32:39 +01:00
} else {
2021-03-18 00:35:42 +01:00
if ( nc - > node_3d ) {
2017-11-01 21:32:39 +01:00
AnimatedValuesBackup : : Entry entry ;
2021-03-18 00:35:42 +01:00
entry . object = nc - > node_3d ;
2017-11-01 21:32:39 +01:00
entry . subpath . push_back ( " transform " ) ;
2021-03-18 00:35:42 +01:00
entry . value = nc - > node_3d - > get_transform ( ) ;
2017-11-01 21:32:39 +01:00
entry . bone_idx = - 1 ;
2020-12-20 11:46:44 +01:00
backup - > entries . push_back ( entry ) ;
2017-11-01 21:32:39 +01:00
} else {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , TrackNodeCache : : PropertyAnim > & E : nc - > property_anim ) {
2017-11-01 21:32:39 +01:00
AnimatedValuesBackup : : Entry entry ;
2021-08-09 22:13:42 +02:00
entry . object = E . value . object ;
entry . subpath = E . value . subpath ;
2017-11-01 21:32:39 +01:00
bool valid ;
2021-08-09 22:13:42 +02:00
entry . value = E . value . object - > get_indexed ( E . value . subpath , & valid ) ;
2017-11-01 21:32:39 +01:00
entry . bone_idx = - 1 ;
2020-05-14 16:41:43 +02:00
if ( valid ) {
2020-12-20 11:46:44 +01:00
backup - > entries . push_back ( entry ) ;
2020-05-14 16:41:43 +02:00
}
2017-11-01 21:32:39 +01:00
}
}
}
}
return backup ;
}
2020-12-20 11:46:44 +01:00
Ref < AnimatedValuesBackup > AnimationPlayer : : apply_reset ( bool p_user_initiated ) {
ERR_FAIL_COND_V ( ! can_apply_reset ( ) , Ref < AnimatedValuesBackup > ( ) ) ;
2021-12-06 01:46:03 +01:00
Ref < Animation > reset_anim = animation_set [ SceneStringNames : : get_singleton ( ) - > RESET ] . animation ;
2020-12-20 11:46:44 +01:00
ERR_FAIL_COND_V ( reset_anim . is_null ( ) , Ref < AnimatedValuesBackup > ( ) ) ;
Node * root_node = get_node_or_null ( root ) ;
2023-06-06 14:59:54 +02:00
ERR_FAIL_NULL_V ( root_node , Ref < AnimatedValuesBackup > ( ) ) ;
2020-12-20 11:46:44 +01:00
AnimationPlayer * aux_player = memnew ( AnimationPlayer ) ;
EditorNode : : get_singleton ( ) - > add_child ( aux_player ) ;
2022-04-07 13:49:28 +02:00
Ref < AnimationLibrary > al ;
al . instantiate ( ) ;
al - > add_animation ( SceneStringNames : : get_singleton ( ) - > RESET , reset_anim ) ;
2022-06-23 07:49:41 +02:00
aux_player - > add_animation_library ( " " , al ) ;
aux_player - > set_assigned_animation ( SceneStringNames : : get_singleton ( ) - > RESET ) ;
2021-02-09 21:41:48 +01:00
// Forcing the use of the original root because the scene where original player belongs may be not the active one
2022-09-29 11:53:28 +02:00
Ref < AnimatedValuesBackup > old_values = aux_player - > backup_animated_values ( get_node ( get_root ( ) ) ) ;
2020-12-20 11:46:44 +01:00
aux_player - > seek ( 0.0f , true ) ;
2022-10-24 23:07:02 +02:00
aux_player - > queue_free ( ) ;
2020-12-20 11:46:44 +01:00
if ( p_user_initiated ) {
Ref < AnimatedValuesBackup > new_values = aux_player - > backup_animated_values ( ) ;
old_values - > restore ( ) ;
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * ur = EditorUndoRedoManager : : get_singleton ( ) ;
2022-12-10 13:45:03 +01:00
ur - > create_action ( TTR ( " Animation Apply Reset " ) ) ;
2020-12-20 11:46:44 +01:00
ur - > add_do_method ( new_values . ptr ( ) , " restore " ) ;
ur - > add_undo_method ( old_values . ptr ( ) , " restore " ) ;
ur - > commit_action ( ) ;
2017-11-01 21:32:39 +01:00
}
2020-12-20 11:46:44 +01:00
return old_values ;
}
bool AnimationPlayer : : can_apply_reset ( ) const {
2021-12-06 01:46:03 +01:00
return has_animation ( SceneStringNames : : get_singleton ( ) - > RESET ) & & playback . assigned ! = SceneStringNames : : get_singleton ( ) - > RESET ;
2017-11-01 21:32:39 +01:00
}
2022-03-28 15:24:14 +02:00
# endif // TOOLS_ENABLED
2017-11-01 21:32:39 +01:00
2014-02-10 02:10:30 +01:00
void AnimationPlayer : : _bind_methods ( ) {
2022-04-07 13:49:28 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_animation_library " , " name " , " library " ) , & AnimationPlayer : : add_animation_library ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_animation_library " , " name " ) , & AnimationPlayer : : remove_animation_library ) ;
ClassDB : : bind_method ( D_METHOD ( " rename_animation_library " , " name " , " newname " ) , & AnimationPlayer : : rename_animation_library ) ;
ClassDB : : bind_method ( D_METHOD ( " has_animation_library " , " name " ) , & AnimationPlayer : : has_animation_library ) ;
ClassDB : : bind_method ( D_METHOD ( " get_animation_library " , " name " ) , & AnimationPlayer : : get_animation_library ) ;
ClassDB : : bind_method ( D_METHOD ( " get_animation_library_list " ) , & AnimationPlayer : : _get_animation_library_list ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " has_animation " , " name " ) , & AnimationPlayer : : has_animation ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_animation " , " name " ) , & AnimationPlayer : : get_animation ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_animation_list " ) , & AnimationPlayer : : _get_animation_list ) ;
2016-03-09 00:00:52 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " animation_set_next " , " anim_from " , " anim_to " ) , & AnimationPlayer : : animation_set_next ) ;
ClassDB : : bind_method ( D_METHOD ( " animation_get_next " , " anim_from " ) , & AnimationPlayer : : animation_get_next ) ;
2016-05-07 18:27:52 +02:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_blend_time " , " anim_from " , " anim_to " , " sec " ) , & AnimationPlayer : : set_blend_time ) ;
ClassDB : : bind_method ( D_METHOD ( " get_blend_time " , " anim_from " , " anim_to " ) , & AnimationPlayer : : get_blend_time ) ;
2014-02-10 02:10:30 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_default_blend_time " , " sec " ) , & AnimationPlayer : : set_default_blend_time ) ;
ClassDB : : bind_method ( D_METHOD ( " get_default_blend_time " ) , & AnimationPlayer : : get_default_blend_time ) ;
2014-02-10 02:10:30 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " play " , " name " , " custom_blend " , " custom_speed " , " from_end " ) , & AnimationPlayer : : play , DEFVAL ( " " ) , DEFVAL ( - 1 ) , DEFVAL ( 1.0 ) , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " play_backwards " , " name " , " custom_blend " ) , & AnimationPlayer : : play_backwards , DEFVAL ( " " ) , DEFVAL ( - 1 ) ) ;
2023-01-11 13:39:39 +01:00
ClassDB : : bind_method ( D_METHOD ( " pause " ) , & AnimationPlayer : : pause ) ;
2023-01-18 14:02:04 +01:00
ClassDB : : bind_method ( D_METHOD ( " stop " , " keep_state " ) , & AnimationPlayer : : stop , DEFVAL ( false ) ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " is_playing " ) , & AnimationPlayer : : is_playing ) ;
2017-12-14 00:07:39 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_current_animation " , " anim " ) , & AnimationPlayer : : set_current_animation ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_animation " ) , & AnimationPlayer : : get_current_animation ) ;
2018-01-14 11:28:57 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_assigned_animation " , " anim " ) , & AnimationPlayer : : set_assigned_animation ) ;
ClassDB : : bind_method ( D_METHOD ( " get_assigned_animation " ) , & AnimationPlayer : : get_assigned_animation ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " queue " , " name " ) , & AnimationPlayer : : queue ) ;
2018-11-28 01:43:34 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_queue " ) , & AnimationPlayer : : get_queue ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " clear_queue " ) , & AnimationPlayer : : clear_queue ) ;
2014-02-10 02:10:30 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_active " , " active " ) , & AnimationPlayer : : set_active ) ;
ClassDB : : bind_method ( D_METHOD ( " is_active " ) , & AnimationPlayer : : is_active ) ;
2016-03-09 00:00:52 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_speed_scale " , " speed " ) , & AnimationPlayer : : set_speed_scale ) ;
ClassDB : : bind_method ( D_METHOD ( " get_speed_scale " ) , & AnimationPlayer : : get_speed_scale ) ;
2018-03-01 19:52:00 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_playing_speed " ) , & AnimationPlayer : : get_playing_speed ) ;
2014-02-10 02:10:30 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_autoplay " , " name " ) , & AnimationPlayer : : set_autoplay ) ;
ClassDB : : bind_method ( D_METHOD ( " get_autoplay " ) , & AnimationPlayer : : get_autoplay ) ;
2014-02-10 02:10:30 +01:00
2020-12-20 11:46:44 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_reset_on_save_enabled " , " enabled " ) , & AnimationPlayer : : set_reset_on_save_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_reset_on_save_enabled " ) , & AnimationPlayer : : is_reset_on_save_enabled ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_root " , " path " ) , & AnimationPlayer : : set_root ) ;
ClassDB : : bind_method ( D_METHOD ( " get_root " ) , & AnimationPlayer : : get_root ) ;
2014-02-10 02:10:30 +01:00
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " find_animation " , " animation " ) , & AnimationPlayer : : find_animation ) ;
2022-04-07 13:49:28 +02:00
ClassDB : : bind_method ( D_METHOD ( " find_animation_library " , " animation " ) , & AnimationPlayer : : find_animation_library ) ;
2014-02-10 02:10:30 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " clear_caches " ) , & AnimationPlayer : : clear_caches ) ;
2014-02-10 02:10:30 +01:00
2021-02-18 19:52:29 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_process_callback " , " mode " ) , & AnimationPlayer : : set_process_callback ) ;
ClassDB : : bind_method ( D_METHOD ( " get_process_callback " ) , & AnimationPlayer : : get_process_callback ) ;
2014-02-10 02:10:30 +01:00
2018-12-08 19:56:20 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_method_call_mode " , " mode " ) , & AnimationPlayer : : set_method_call_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_method_call_mode " ) , & AnimationPlayer : : get_method_call_mode ) ;
2023-01-27 19:25:49 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_audio_max_polyphony " , " max_polyphony " ) , & AnimationPlayer : : set_audio_max_polyphony ) ;
ClassDB : : bind_method ( D_METHOD ( " get_audio_max_polyphony " ) , & AnimationPlayer : : get_audio_max_polyphony ) ;
2022-08-08 14:18:26 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_movie_quit_on_finish_enabled " , " enabled " ) , & AnimationPlayer : : set_movie_quit_on_finish_enabled ) ;
2022-06-26 01:38:20 +02:00
ClassDB : : bind_method ( D_METHOD ( " is_movie_quit_on_finish_enabled " ) , & AnimationPlayer : : is_movie_quit_on_finish_enabled ) ;
2017-12-07 18:19:21 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_current_animation_position " ) , & AnimationPlayer : : get_current_animation_position ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_animation_length " ) , & AnimationPlayer : : get_current_animation_length ) ;
2017-12-14 00:07:39 +01:00
ClassDB : : bind_method ( D_METHOD ( " seek " , " seconds " , " update " ) , & AnimationPlayer : : seek , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " advance " , " delta " ) , & AnimationPlayer : : advance ) ;
2023-01-19 16:43:37 +01:00
GDVIRTUAL_BIND ( _post_process_key_value , " animation " , " track " , " value " , " object " , " object_idx " ) ;
2018-01-11 23:35:12 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " root_node " ) , " set_root " , " get_root " ) ;
2022-11-29 10:51:45 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING_NAME , " current_animation " , PROPERTY_HINT_ENUM , " " , PROPERTY_USAGE_EDITOR ) , " set_current_animation " , " get_current_animation " ) ;
2021-06-18 01:10:18 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING_NAME , " assigned_animation " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NONE ) , " set_assigned_animation " , " get_assigned_animation " ) ;
2021-11-03 23:06:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING_NAME , " autoplay " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) , " set_autoplay " , " get_autoplay " ) ;
2020-12-20 11:46:44 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " reset_on_save " , PROPERTY_HINT_NONE , " " ) , " set_reset_on_save_enabled " , " is_reset_on_save_enabled " ) ;
2021-06-18 01:10:18 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " current_animation_length " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NONE ) , " " , " get_current_animation_length " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " current_animation_position " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NONE ) , " " , " get_current_animation_position " ) ;
2018-01-11 23:35:12 +01:00
2017-08-07 03:51:56 +02:00
ADD_GROUP ( " Playback Options " , " playback_ " ) ;
2021-02-18 19:52:29 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " playback_process_mode " , PROPERTY_HINT_ENUM , " Physics,Idle,Manual " ) , " set_process_callback " , " get_process_callback " ) ;
2022-05-20 07:24:41 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " playback_default_blend_time " , PROPERTY_HINT_RANGE , " 0,4096,0.01,suffix:s " ) , " set_default_blend_time " , " get_default_blend_time " ) ;
2021-06-18 01:10:18 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " playback_active " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NONE ) , " set_active " , " is_active " ) ;
2023-01-21 06:51:03 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " speed_scale " , PROPERTY_HINT_RANGE , " -64,64,0.01 " ) , " set_speed_scale " , " get_speed_scale " ) ;
2018-12-08 19:56:20 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " method_call_mode " , PROPERTY_HINT_ENUM , " Deferred,Immediate " ) , " set_method_call_mode " , " get_method_call_mode " ) ;
2023-01-27 19:25:49 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " audio_max_polyphony " , PROPERTY_HINT_RANGE , " 1,127,1 " ) , " set_audio_max_polyphony " , " get_audio_max_polyphony " ) ;
2014-02-10 02:10:30 +01:00
2022-06-26 01:38:20 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " movie_quit_on_finish " ) , " set_movie_quit_on_finish_enabled " , " is_movie_quit_on_finish_enabled " ) ;
2020-02-20 22:58:05 +01:00
ADD_SIGNAL ( MethodInfo ( " animation_finished " , PropertyInfo ( Variant : : STRING_NAME , " anim_name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " animation_changed " , PropertyInfo ( Variant : : STRING_NAME , " old_name " ) , PropertyInfo ( Variant : : STRING_NAME , " new_name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " animation_started " , PropertyInfo ( Variant : : STRING_NAME , " anim_name " ) ) ) ;
2022-09-01 16:00:55 +02:00
ADD_SIGNAL ( MethodInfo ( " animation_list_changed " ) ) ;
2022-05-05 05:53:47 +02:00
ADD_SIGNAL ( MethodInfo ( " animation_libraries_updated " ) ) ;
2018-06-19 03:10:48 +02:00
ADD_SIGNAL ( MethodInfo ( " caches_cleared " ) ) ;
2014-02-10 02:10:30 +01:00
2017-09-30 16:19:07 +02:00
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_PHYSICS ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_IDLE ) ;
2018-08-02 09:22:24 +02:00
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_MANUAL ) ;
2018-12-08 19:56:20 +01:00
BIND_ENUM_CONSTANT ( ANIMATION_METHOD_CALL_DEFERRED ) ;
BIND_ENUM_CONSTANT ( ANIMATION_METHOD_CALL_IMMEDIATE ) ;
2014-02-10 02:10:30 +01:00
}
AnimationPlayer : : AnimationPlayer ( ) {
2015-06-29 05:29:49 +02:00
root = SceneStringNames : : get_singleton ( ) - > path_pp ;
2014-02-10 02:10:30 +01:00
}
AnimationPlayer : : ~ AnimationPlayer ( ) {
}