2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* animation_tree.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-08-29 22:38:13 +02:00
2018-06-25 23:40:24 +02:00
# include "animation_tree.h"
2018-09-11 18:13:45 +02:00
2018-06-19 03:10:48 +02:00
# include "animation_blend_tree.h"
2020-11-07 23:33:38 +01:00
# include "core/config/engine.h"
2021-10-15 15:25:00 +02:00
# include "scene/resources/animation.h"
2018-06-19 03:10:48 +02:00
# include "scene/scene_string_names.h"
# include "servers/audio/audio_stream.h"
2018-08-20 18:38:18 +02:00
void AnimationNode : : get_parameter_list ( List < PropertyInfo > * r_list ) const {
2021-08-22 03:52:44 +02:00
Array parameters ;
if ( GDVIRTUAL_CALL ( _get_parameter_list , parameters ) ) {
2018-11-25 20:56:49 +01:00
for ( int i = 0 ; i < parameters . size ( ) ; i + + ) {
Dictionary d = parameters [ i ] ;
2020-12-15 13:04:21 +01:00
ERR_CONTINUE ( d . is_empty ( ) ) ;
2018-11-25 20:56:49 +01:00
r_list - > push_back ( PropertyInfo : : from_dict ( d ) ) ;
}
}
2018-08-20 18:38:18 +02:00
}
Variant AnimationNode : : get_parameter_default_value ( const StringName & p_parameter ) const {
2021-08-22 03:52:44 +02:00
Variant ret ;
2022-10-18 18:47:44 +02:00
GDVIRTUAL_CALL ( _get_parameter_default_value , p_parameter , ret ) ;
return ret ;
2018-08-20 18:38:18 +02:00
}
2023-01-12 13:51:03 +01:00
bool AnimationNode : : is_parameter_read_only ( const StringName & p_parameter ) const {
bool ret = false ;
GDVIRTUAL_CALL ( _is_parameter_read_only , p_parameter , ret ) ;
return ret ;
}
2018-08-21 21:28:06 +02:00
void AnimationNode : : set_parameter ( const StringName & p_name , const Variant & p_value ) {
2018-08-20 18:38:18 +02:00
ERR_FAIL_COND ( ! state ) ;
ERR_FAIL_COND ( ! state - > tree - > property_parent_map . has ( base_path ) ) ;
ERR_FAIL_COND ( ! state - > tree - > property_parent_map [ base_path ] . has ( p_name ) ) ;
StringName path = state - > tree - > property_parent_map [ base_path ] [ p_name ] ;
2023-01-12 13:51:03 +01:00
state - > tree - > property_map [ path ] . first = p_value ;
2018-08-20 18:38:18 +02:00
}
2018-08-21 21:28:06 +02:00
Variant AnimationNode : : get_parameter ( const StringName & p_name ) const {
ERR_FAIL_COND_V ( ! state , Variant ( ) ) ;
ERR_FAIL_COND_V ( ! state - > tree - > property_parent_map . has ( base_path ) , Variant ( ) ) ;
ERR_FAIL_COND_V ( ! state - > tree - > property_parent_map [ base_path ] . has ( p_name ) , Variant ( ) ) ;
2018-08-20 18:38:18 +02:00
StringName path = state - > tree - > property_parent_map [ base_path ] [ p_name ] ;
2023-01-12 13:51:03 +01:00
return state - > tree - > property_map [ path ] . first ;
2018-08-20 18:38:18 +02:00
}
void AnimationNode : : get_child_nodes ( List < ChildNode > * r_child_nodes ) {
2021-08-22 03:52:44 +02:00
Dictionary cn ;
if ( GDVIRTUAL_CALL ( _get_child_nodes , cn ) ) {
2018-11-25 20:56:49 +01:00
List < Variant > keys ;
cn . get_key_list ( & keys ) ;
2021-07-24 15:46:25 +02:00
for ( const Variant & E : keys ) {
2018-11-25 20:56:49 +01:00
ChildNode child ;
2021-07-16 05:45:57 +02:00
child . name = E ;
child . node = cn [ E ] ;
2018-11-25 20:56:49 +01:00
r_child_nodes - > push_back ( child ) ;
}
}
2018-08-20 18:38:18 +02:00
}
2022-11-29 10:51:45 +01:00
void AnimationNode : : blend_animation ( const StringName & p_animation , double p_time , double p_delta , bool p_seeked , bool p_is_external_seeking , real_t p_blend , Animation : : LoopedFlag p_looped_flag ) {
2018-06-19 03:10:48 +02:00
ERR_FAIL_COND ( ! state ) ;
ERR_FAIL_COND ( ! state - > player - > has_animation ( p_animation ) ) ;
Ref < Animation > animation = state - > player - > get_animation ( p_animation ) ;
if ( animation . is_null ( ) ) {
2018-08-21 21:28:06 +02:00
AnimationNodeBlendTree * btree = Object : : cast_to < AnimationNodeBlendTree > ( parent ) ;
2018-08-20 18:38:18 +02:00
if ( btree ) {
2022-09-29 11:53:28 +02:00
String node_name = btree - > get_node_name ( Ref < AnimationNodeAnimation > ( this ) ) ;
make_invalid ( vformat ( RTR ( " In node '%s', invalid animation: '%s'. " ) , node_name , p_animation ) ) ;
2018-06-21 20:45:44 +02:00
} else {
make_invalid ( vformat ( RTR ( " Invalid animation: '%s'. " ) , p_animation ) ) ;
}
2018-06-19 03:10:48 +02:00
return ;
}
ERR_FAIL_COND ( ! animation . is_valid ( ) ) ;
AnimationState anim_state ;
anim_state . blend = p_blend ;
anim_state . track_blends = & blends ;
anim_state . delta = p_delta ;
anim_state . time = p_time ;
anim_state . animation = animation ;
anim_state . seeked = p_seeked ;
2022-11-29 10:51:45 +01:00
anim_state . looped_flag = p_looped_flag ;
2022-11-21 20:30:55 +01:00
anim_state . is_external_seeking = p_is_external_seeking ;
2018-06-19 03:10:48 +02:00
state - > animation_states . push_back ( anim_state ) ;
}
2022-11-21 20:30:55 +01:00
double AnimationNode : : _pre_process ( const StringName & p_base_path , AnimationNode * p_parent , State * p_state , double p_time , bool p_seek , bool p_is_external_seeking , const Vector < StringName > & p_connections ) {
2018-08-20 18:38:18 +02:00
base_path = p_base_path ;
parent = p_parent ;
2018-08-21 21:28:06 +02:00
connections = p_connections ;
2018-06-19 03:10:48 +02:00
state = p_state ;
2018-08-20 18:38:18 +02:00
2022-11-21 20:30:55 +01:00
double t = process ( p_time , p_seek , p_is_external_seeking ) ;
2018-08-20 18:38:18 +02:00
2020-04-02 01:20:12 +02:00
state = nullptr ;
parent = nullptr ;
2018-08-20 18:38:18 +02:00
base_path = StringName ( ) ;
connections . clear ( ) ;
2018-06-19 03:10:48 +02:00
return t ;
}
2022-04-20 12:36:54 +02:00
AnimationTree * AnimationNode : : get_animation_tree ( ) const {
ERR_FAIL_COND_V ( ! state , nullptr ) ;
return state - > tree ;
}
2018-06-19 03:10:48 +02:00
void AnimationNode : : make_invalid ( const String & p_reason ) {
ERR_FAIL_COND ( ! state ) ;
state - > valid = false ;
2021-12-09 10:42:46 +01:00
if ( ! state - > invalid_reasons . is_empty ( ) ) {
2018-06-19 03:10:48 +02:00
state - > invalid_reasons + = " \n " ;
}
2021-07-28 19:40:45 +02:00
state - > invalid_reasons + = String : : utf8 ( " • " ) + p_reason ;
2018-06-19 03:10:48 +02:00
}
2022-11-21 20:30:55 +01:00
double AnimationNode : : blend_input ( int p_input , double p_time , bool p_seek , bool p_is_external_seeking , real_t p_blend , FilterAction p_filter , bool p_sync ) {
2018-06-19 03:10:48 +02:00
ERR_FAIL_INDEX_V ( p_input , inputs . size ( ) , 0 ) ;
ERR_FAIL_COND_V ( ! state , 0 ) ;
2018-08-21 21:28:06 +02:00
AnimationNodeBlendTree * blend_tree = Object : : cast_to < AnimationNodeBlendTree > ( parent ) ;
ERR_FAIL_COND_V ( ! blend_tree , 0 ) ;
2018-06-19 03:10:48 +02:00
2018-08-20 18:38:18 +02:00
StringName node_name = connections [ p_input ] ;
2018-06-19 03:10:48 +02:00
2018-08-20 18:38:18 +02:00
if ( ! blend_tree - > has_node ( node_name ) ) {
2022-09-29 11:53:28 +02:00
String node_name2 = blend_tree - > get_node_name ( Ref < AnimationNode > ( this ) ) ;
make_invalid ( vformat ( RTR ( " Nothing connected to input '%s' of node '%s'. " ) , get_input_name ( p_input ) , node_name2 ) ) ;
2018-06-19 03:10:48 +02:00
return 0 ;
}
2018-08-20 18:38:18 +02:00
Ref < AnimationNode > node = blend_tree - > get_node ( node_name ) ;
2018-06-19 03:10:48 +02:00
2018-08-20 18:38:18 +02:00
//inputs.write[p_input].last_pass = state->last_pass;
2021-08-10 00:15:17 +02:00
real_t activity = 0.0 ;
2022-11-21 20:30:55 +01:00
double ret = _blend_node ( node_name , blend_tree - > get_node_connection_array ( node_name ) , nullptr , node , p_time , p_seek , p_is_external_seeking , p_blend , p_filter , p_sync , & activity ) ;
2018-08-23 21:44:10 +02:00
Vector < AnimationTree : : Activity > * activity_ptr = state - > tree - > input_activity_map . getptr ( base_path ) ;
2018-08-24 16:50:29 +02:00
if ( activity_ptr & & p_input < activity_ptr - > size ( ) ) {
2018-08-23 21:44:10 +02:00
activity_ptr - > write [ p_input ] . last_pass = state - > last_pass ;
activity_ptr - > write [ p_input ] . activity = activity ;
}
return ret ;
2018-06-19 03:10:48 +02:00
}
2022-11-21 20:30:55 +01:00
double AnimationNode : : blend_node ( const StringName & p_sub_path , Ref < AnimationNode > p_node , double p_time , bool p_seek , bool p_is_external_seeking , real_t p_blend , FilterAction p_filter , bool p_sync ) {
return _blend_node ( p_sub_path , Vector < StringName > ( ) , this , p_node , p_time , p_seek , p_is_external_seeking , p_blend , p_filter , p_sync ) ;
2018-06-19 03:10:48 +02:00
}
2022-11-21 20:30:55 +01:00
double AnimationNode : : _blend_node ( const StringName & p_subpath , const Vector < StringName > & p_connections , AnimationNode * p_new_parent , Ref < AnimationNode > p_node , double p_time , bool p_seek , bool p_is_external_seeking , real_t p_blend , FilterAction p_filter , bool p_sync , real_t * r_max ) {
2018-06-19 03:10:48 +02:00
ERR_FAIL_COND_V ( ! p_node . is_valid ( ) , 0 ) ;
ERR_FAIL_COND_V ( ! state , 0 ) ;
int blend_count = blends . size ( ) ;
if ( p_node - > blends . size ( ) ! = blend_count ) {
p_node - > blends . resize ( blend_count ) ;
}
2021-08-10 00:15:17 +02:00
real_t * blendw = p_node - > blends . ptrw ( ) ;
const real_t * blendr = blends . ptr ( ) ;
2018-06-19 03:10:48 +02:00
bool any_valid = false ;
if ( has_filter ( ) & & is_filter_enabled ( ) & & p_filter ! = FILTER_IGNORE ) {
for ( int i = 0 ; i < blend_count ; i + + ) {
blendw [ i ] = 0.0 ; //all to zero by default
}
2022-05-08 10:09:19 +02:00
for ( const KeyValue < NodePath , bool > & E : filter ) {
if ( ! state - > track_map . has ( E . key ) ) {
2018-06-19 03:10:48 +02:00
continue ;
}
2022-05-08 10:09:19 +02:00
int idx = state - > track_map [ E . key ] ;
2018-06-19 03:10:48 +02:00
blendw [ idx ] = 1.0 ; //filtered goes to one
}
switch ( p_filter ) {
case FILTER_IGNORE :
break ; //will not happen anyway
case FILTER_PASS : {
2018-09-13 03:38:39 +02:00
//values filtered pass, the rest don't
2018-06-19 03:10:48 +02:00
for ( int i = 0 ; i < blend_count ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( blendw [ i ] = = 0 ) { //not filtered, does not pass
2018-06-19 03:10:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
blendw [ i ] = blendr [ i ] * p_blend ;
2022-11-23 02:46:01 +01:00
if ( ! Math : : is_zero_approx ( blendw [ i ] ) ) {
2018-06-19 03:10:48 +02:00
any_valid = true ;
}
}
} break ;
case FILTER_STOP : {
2018-09-13 03:38:39 +02:00
//values filtered don't pass, the rest are blended
2018-06-19 03:10:48 +02:00
for ( int i = 0 ; i < blend_count ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( blendw [ i ] > 0 ) { //filtered, does not pass
2018-06-19 03:10:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
blendw [ i ] = blendr [ i ] * p_blend ;
2022-11-23 02:46:01 +01:00
if ( ! Math : : is_zero_approx ( blendw [ i ] ) ) {
2018-06-19 03:10:48 +02:00
any_valid = true ;
}
}
} break ;
case FILTER_BLEND : {
//filtered values are blended, the rest are passed without blending
for ( int i = 0 ; i < blend_count ; i + + ) {
if ( blendw [ i ] = = 1.0 ) {
blendw [ i ] = blendr [ i ] * p_blend ; //filtered, blend
} else {
blendw [ i ] = blendr [ i ] ; //not filtered, do not blend
}
2022-11-23 02:46:01 +01:00
if ( ! Math : : is_zero_approx ( blendw [ i ] ) ) {
2018-06-19 03:10:48 +02:00
any_valid = true ;
}
}
} break ;
}
} else {
for ( int i = 0 ; i < blend_count ; i + + ) {
//regular blend
blendw [ i ] = blendr [ i ] * p_blend ;
2022-11-23 02:46:01 +01:00
if ( ! Math : : is_zero_approx ( blendw [ i ] ) ) {
2018-06-19 03:10:48 +02:00
any_valid = true ;
}
}
}
if ( r_max ) {
* r_max = 0 ;
for ( int i = 0 ; i < blend_count ; i + + ) {
* r_max = MAX ( * r_max , blendw [ i ] ) ;
}
}
2018-08-20 18:38:18 +02:00
String new_path ;
AnimationNode * new_parent ;
2022-06-10 10:01:07 +02:00
// This is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations.
2018-08-20 18:38:18 +02:00
if ( p_new_parent ) {
new_parent = p_new_parent ;
2018-08-21 21:28:06 +02:00
new_path = String ( base_path ) + String ( p_subpath ) + " / " ;
2018-08-20 18:38:18 +02:00
} else {
2018-08-21 21:28:06 +02:00
ERR_FAIL_COND_V ( ! parent , 0 ) ;
2018-08-20 18:38:18 +02:00
new_parent = parent ;
2018-08-21 21:28:06 +02:00
new_path = String ( parent - > base_path ) + String ( p_subpath ) + " / " ;
2018-08-20 18:38:18 +02:00
}
2022-03-15 17:35:25 +01:00
2022-11-15 07:06:10 +01:00
// This process, which depends on p_sync is needed to process sync correctly in the case of
// that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync.
2022-06-29 08:35:29 +02:00
if ( ! p_seek & & ! p_sync & & ! any_valid ) {
2022-11-21 20:30:55 +01:00
return p_node - > _pre_process ( new_path , new_parent , state , 0 , p_seek , p_is_external_seeking , p_connections ) ;
2022-03-15 17:35:25 +01:00
}
2022-11-21 20:30:55 +01:00
return p_node - > _pre_process ( new_path , new_parent , state , p_time , p_seek , p_is_external_seeking , p_connections ) ;
2018-06-19 03:10:48 +02:00
}
int AnimationNode : : get_input_count ( ) const {
return inputs . size ( ) ;
}
2020-05-14 14:29:06 +02:00
2018-06-19 03:10:48 +02:00
String AnimationNode : : get_input_name ( int p_input ) {
ERR_FAIL_INDEX_V ( p_input , inputs . size ( ) , String ( ) ) ;
return inputs [ p_input ] . name ;
}
String AnimationNode : : get_caption ( ) const {
2022-10-18 18:47:44 +02:00
String ret = " Node " ;
GDVIRTUAL_CALL ( _get_caption , ret ) ;
return ret ;
2018-06-19 03:10:48 +02:00
}
void AnimationNode : : add_input ( const String & p_name ) {
2018-09-13 03:38:39 +02:00
//root nodes can't add inputs
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND ( Object : : cast_to < AnimationRootNode > ( this ) ! = nullptr ) ;
2018-06-19 03:10:48 +02:00
Input input ;
2022-02-03 17:03:38 +01:00
ERR_FAIL_COND ( p_name . contains ( " . " ) | | p_name . contains ( " / " ) ) ;
2018-06-19 03:10:48 +02:00
input . name = p_name ;
inputs . push_back ( input ) ;
emit_changed ( ) ;
}
void AnimationNode : : set_input_name ( int p_input , const String & p_name ) {
ERR_FAIL_INDEX ( p_input , inputs . size ( ) ) ;
2022-02-03 17:03:38 +01:00
ERR_FAIL_COND ( p_name . contains ( " . " ) | | p_name . contains ( " / " ) ) ;
2018-07-25 03:11:03 +02:00
inputs . write [ p_input ] . name = p_name ;
2018-06-19 03:10:48 +02:00
emit_changed ( ) ;
}
void AnimationNode : : remove_input ( int p_index ) {
ERR_FAIL_INDEX ( p_index , inputs . size ( ) ) ;
2021-07-04 00:17:03 +02:00
inputs . remove_at ( p_index ) ;
2018-06-19 03:10:48 +02:00
emit_changed ( ) ;
}
2022-11-21 20:30:55 +01:00
double AnimationNode : : process ( double p_time , bool p_seek , bool p_is_external_seeking ) {
2022-10-18 18:47:44 +02:00
double ret = 0 ;
2022-11-21 20:30:55 +01:00
GDVIRTUAL_CALL ( _process , p_time , p_seek , p_is_external_seeking , ret ) ;
2022-10-18 18:47:44 +02:00
return ret ;
2018-06-19 03:10:48 +02:00
}
void AnimationNode : : set_filter_path ( const NodePath & p_path , bool p_enable ) {
if ( p_enable ) {
filter [ p_path ] = true ;
} else {
filter . erase ( p_path ) ;
}
}
void AnimationNode : : set_filter_enabled ( bool p_enable ) {
filter_enabled = p_enable ;
}
bool AnimationNode : : is_filter_enabled ( ) const {
return filter_enabled ;
}
bool AnimationNode : : is_path_filtered ( const NodePath & p_path ) const {
return filter . has ( p_path ) ;
}
bool AnimationNode : : has_filter ( ) const {
2022-10-18 18:47:44 +02:00
bool ret = false ;
GDVIRTUAL_CALL ( _has_filter , ret ) ;
return ret ;
2018-06-19 03:10:48 +02:00
}
Array AnimationNode : : _get_filters ( ) const {
Array paths ;
2022-05-08 10:09:19 +02:00
for ( const KeyValue < NodePath , bool > & E : filter ) {
paths . push_back ( String ( E . key ) ) ; //use strings, so sorting is possible
2018-06-19 03:10:48 +02:00
}
paths . sort ( ) ; //done so every time the scene is saved, it does not change
return paths ;
}
2020-05-14 14:29:06 +02:00
2018-06-19 03:10:48 +02:00
void AnimationNode : : _set_filters ( const Array & p_filters ) {
filter . clear ( ) ;
for ( int i = 0 ; i < p_filters . size ( ) ; i + + ) {
set_filter_path ( p_filters [ i ] , true ) ;
}
}
2022-08-12 22:57:11 +02:00
void AnimationNode : : _validate_property ( PropertyInfo & p_property ) const {
if ( ! has_filter ( ) & & ( p_property . name = = " filter_enabled " | | p_property . name = = " filters " ) ) {
p_property . usage = PROPERTY_USAGE_NONE ;
2018-06-19 03:10:48 +02:00
}
}
2018-08-20 18:38:18 +02:00
Ref < AnimationNode > AnimationNode : : get_child_by_name ( const StringName & p_name ) {
2021-08-22 03:52:44 +02:00
Ref < AnimationNode > ret ;
2022-10-18 18:47:44 +02:00
GDVIRTUAL_CALL ( _get_child_by_name , p_name , ret ) ;
return ret ;
2018-08-20 18:38:18 +02:00
}
2018-06-19 03:10:48 +02:00
void AnimationNode : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " get_input_count " ) , & AnimationNode : : get_input_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_input_name " , " input " ) , & AnimationNode : : get_input_name ) ;
ClassDB : : bind_method ( D_METHOD ( " add_input " , " name " ) , & AnimationNode : : add_input ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_input " , " index " ) , & AnimationNode : : remove_input ) ;
ClassDB : : bind_method ( D_METHOD ( " set_filter_path " , " path " , " enable " ) , & AnimationNode : : set_filter_path ) ;
ClassDB : : bind_method ( D_METHOD ( " is_path_filtered " , " path " ) , & AnimationNode : : is_path_filtered ) ;
ClassDB : : bind_method ( D_METHOD ( " set_filter_enabled " , " enable " ) , & AnimationNode : : set_filter_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_filter_enabled " ) , & AnimationNode : : is_filter_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_filters " , " filters " ) , & AnimationNode : : _set_filters ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_filters " ) , & AnimationNode : : _get_filters ) ;
2022-11-29 10:51:45 +01:00
ClassDB : : bind_method ( D_METHOD ( " blend_animation " , " animation " , " time " , " delta " , " seeked " , " is_external_seeking " , " blend " , " looped_flag " ) , & AnimationNode : : blend_animation , DEFVAL ( Animation : : LOOPED_FLAG_NONE ) ) ;
2022-11-21 20:30:55 +01:00
ClassDB : : bind_method ( D_METHOD ( " blend_node " , " name " , " node " , " time " , " seek " , " is_external_seeking " , " blend " , " filter " , " sync " ) , & AnimationNode : : blend_node , DEFVAL ( FILTER_IGNORE ) , DEFVAL ( true ) ) ;
ClassDB : : bind_method ( D_METHOD ( " blend_input " , " input_index " , " time " , " seek " , " is_external_seeking " , " blend " , " filter " , " sync " ) , & AnimationNode : : blend_input , DEFVAL ( FILTER_IGNORE ) , DEFVAL ( true ) ) ;
2018-06-19 03:10:48 +02:00
2018-08-21 21:28:06 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_parameter " , " name " , " value " ) , & AnimationNode : : set_parameter ) ;
ClassDB : : bind_method ( D_METHOD ( " get_parameter " , " name " ) , & AnimationNode : : get_parameter ) ;
2018-06-27 08:00:08 +02:00
2021-11-03 23:06:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " filter_enabled " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) , " set_filter_enabled " , " is_filter_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " filters " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) , " _set_filters " , " _get_filters " ) ;
2018-06-19 03:10:48 +02:00
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _get_child_nodes ) ;
GDVIRTUAL_BIND ( _get_parameter_list ) ;
GDVIRTUAL_BIND ( _get_child_by_name , " name " ) ;
GDVIRTUAL_BIND ( _get_parameter_default_value , " parameter " ) ;
2023-01-12 13:51:03 +01:00
GDVIRTUAL_BIND ( _is_parameter_read_only , " parameter " ) ;
2022-11-21 20:30:55 +01:00
GDVIRTUAL_BIND ( _process , " time " , " seek " , " is_external_seeking " ) ;
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _get_caption ) ;
GDVIRTUAL_BIND ( _has_filter ) ;
2018-06-19 03:10:48 +02:00
2018-08-20 18:38:18 +02:00
ADD_SIGNAL ( MethodInfo ( " tree_changed " ) ) ;
2018-06-19 03:10:48 +02:00
BIND_ENUM_CONSTANT ( FILTER_IGNORE ) ;
BIND_ENUM_CONSTANT ( FILTER_PASS ) ;
BIND_ENUM_CONSTANT ( FILTER_STOP ) ;
BIND_ENUM_CONSTANT ( FILTER_BLEND ) ;
}
AnimationNode : : AnimationNode ( ) {
}
////////////////////
2018-06-27 08:00:08 +02:00
void AnimationTree : : set_tree_root ( const Ref < AnimationNode > & p_root ) {
2018-06-19 03:10:48 +02:00
if ( root . is_valid ( ) ) {
2020-02-21 18:28:45 +01:00
root - > disconnect ( " tree_changed " , callable_mp ( this , & AnimationTree : : _tree_changed ) ) ;
2018-06-19 03:10:48 +02:00
}
2018-08-20 18:38:18 +02:00
2018-06-19 03:10:48 +02:00
root = p_root ;
if ( root . is_valid ( ) ) {
2020-02-21 18:28:45 +01:00
root - > connect ( " tree_changed " , callable_mp ( this , & AnimationTree : : _tree_changed ) ) ;
2018-06-19 03:10:48 +02:00
}
2018-08-21 21:28:06 +02:00
properties_dirty = true ;
2018-08-20 18:38:18 +02:00
2020-10-29 11:01:28 +01:00
update_configuration_warnings ( ) ;
2018-06-19 03:10:48 +02:00
}
2018-06-27 08:00:08 +02:00
Ref < AnimationNode > AnimationTree : : get_tree_root ( ) const {
2018-06-19 03:10:48 +02:00
return root ;
}
2018-06-25 23:40:24 +02:00
void AnimationTree : : set_active ( bool p_active ) {
2020-05-14 16:41:43 +02:00
if ( active = = p_active ) {
2018-06-19 03:10:48 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
active = p_active ;
started = active ;
2021-02-18 19:52:29 +01:00
if ( process_callback = = ANIMATION_PROCESS_IDLE ) {
2018-06-19 03:10:48 +02:00
set_process_internal ( active ) ;
} else {
set_physics_process_internal ( active ) ;
}
if ( ! active & & is_inside_tree ( ) ) {
2023-01-27 19:25:49 +01:00
_clear_caches ( ) ;
2018-06-19 03:10:48 +02:00
}
}
2018-06-25 23:40:24 +02:00
bool AnimationTree : : is_active ( ) const {
2018-06-19 03:10:48 +02:00
return active ;
}
2021-02-18 19:52:29 +01:00
void AnimationTree : : set_process_callback ( AnimationProcessCallback p_mode ) {
if ( process_callback = = p_mode ) {
2018-06-19 03:10:48 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
bool was_active = is_active ( ) ;
if ( was_active ) {
set_active ( false ) ;
}
2021-02-18 19:52:29 +01:00
process_callback = p_mode ;
2018-06-19 03:10:48 +02:00
if ( was_active ) {
set_active ( true ) ;
}
}
2021-02-18 19:52:29 +01:00
AnimationTree : : AnimationProcessCallback AnimationTree : : get_process_callback ( ) const {
return process_callback ;
2018-06-19 03:10:48 +02:00
}
2018-06-25 23:40:24 +02:00
void AnimationTree : : _node_removed ( Node * p_node ) {
2018-06-19 03:10:48 +02:00
cache_valid = false ;
}
2018-06-25 23:40:24 +02:00
bool AnimationTree : : _update_caches ( AnimationPlayer * player ) {
2018-06-19 03:10:48 +02:00
setup_pass + + ;
if ( ! player - > has_node ( player - > get_root ( ) ) ) {
2018-06-25 23:40:24 +02:00
ERR_PRINT ( " AnimationTree: AnimationPlayer root is invalid. " ) ;
2018-06-19 03:10:48 +02:00
set_active ( false ) ;
2023-01-27 19:25:49 +01:00
_clear_caches ( ) ;
2018-06-19 03:10:48 +02:00
return false ;
}
Node * parent = player - > get_node ( player - > get_root ( ) ) ;
List < StringName > sname ;
player - > get_animation_list ( & sname ) ;
2022-02-05 06:16:43 +01:00
Ref < Animation > reset_anim ;
2022-04-22 20:37:28 +02:00
bool has_reset_anim = player - > has_animation ( SceneStringNames : : get_singleton ( ) - > RESET ) ;
2022-02-05 06:16:43 +01:00
if ( has_reset_anim ) {
2022-04-22 20:37:28 +02:00
reset_anim = player - > get_animation ( SceneStringNames : : get_singleton ( ) - > RESET ) ;
2022-02-05 06:16:43 +01:00
}
2021-07-24 15:46:25 +02:00
for ( const StringName & E : sname ) {
2021-07-16 05:45:57 +02:00
Ref < Animation > anim = player - > get_animation ( E ) ;
2018-06-19 03:10:48 +02:00
for ( int i = 0 ; i < anim - > get_track_count ( ) ; i + + ) {
NodePath path = anim - > track_get_path ( i ) ;
Animation : : TrackType track_type = anim - > 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
Animation : : TrackType track_cache_type = track_type ;
if ( track_cache_type = = Animation : : TYPE_POSITION_3D | | track_cache_type = = Animation : : TYPE_ROTATION_3D | | track_cache_type = = Animation : : TYPE_SCALE_3D ) {
track_cache_type = Animation : : TYPE_POSITION_3D ; //reference them as position3D tracks, even if they modify rotation or scale
}
2020-04-02 01:20:12 +02:00
TrackCache * track = nullptr ;
2018-06-19 03:10:48 +02:00
if ( track_cache . has ( path ) ) {
track = track_cache . get ( path ) ;
}
//if not valid, delete track
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 ( track & & ( track - > type ! = track_cache_type | | ObjectDB : : get_instance ( track - > object_id ) = = nullptr ) ) {
2018-06-19 03:10:48 +02:00
playing_caches . erase ( track ) ;
memdelete ( track ) ;
track_cache . erase ( path ) ;
2020-04-02 01:20:12 +02:00
track = nullptr ;
2018-06-19 03:10:48 +02:00
}
if ( ! track ) {
2022-05-03 01:43:50 +02:00
Ref < Resource > resource ;
2018-06-19 03:10:48 +02:00
Vector < StringName > leftover_path ;
Node * child = parent - > get_node_and_resource ( path , resource , leftover_path ) ;
if ( ! child ) {
2021-07-16 05:45:57 +02:00
ERR_PRINT ( " AnimationTree: ' " + String ( E ) + " ', couldn't resolve track: ' " + String ( path ) + " ' " ) ;
2018-06-19 03:10:48 +02:00
continue ;
}
2020-02-21 18:28:45 +01:00
if ( ! child - > is_connected ( " tree_exited " , callable_mp ( this , & AnimationTree : : _node_removed ) ) ) {
2022-07-28 22:56:41 +02:00
child - > connect ( " tree_exited " , callable_mp ( this , & AnimationTree : : _node_removed ) . bind ( child ) ) ;
2018-06-19 03:10:48 +02:00
}
switch ( track_type ) {
case Animation : : TYPE_VALUE : {
TrackCacheValue * track_value = memnew ( TrackCacheValue ) ;
if ( resource . is_valid ( ) ) {
track_value - > object = resource . ptr ( ) ;
} else {
track_value - > object = child ;
}
2022-11-29 10:51:45 +01:00
track_value - > is_discrete = anim - > value_track_get_update_mode ( i ) = = Animation : : UPDATE_DISCRETE ;
2022-08-26 04:42:00 +02:00
track_value - > is_using_angle = anim - > track_get_interpolation_type ( i ) = = Animation : : INTERPOLATION_LINEAR_ANGLE | | anim - > track_get_interpolation_type ( i ) = = Animation : : INTERPOLATION_CUBIC_ANGLE ;
2018-06-19 03:10:48 +02:00
track_value - > subpath = leftover_path ;
track_value - > object_id = track_value - > object - > get_instance_id ( ) ;
track = track_value ;
2022-11-15 07:06:10 +01:00
// If a value track without a key is cached first, the initial value cannot be determined.
// It is a corner case, but which may cause problems with blending.
ERR_CONTINUE_MSG ( anim - > track_get_key_count ( i ) = = 0 , " AnimationTree: ' " + String ( E ) + " ', value track: ' " + String ( path ) + " ' must have at least one key to cache for blending. " ) ;
track_value - > init_value = anim - > track_get_key_value ( i , 0 ) ;
track_value - > init_value . zero ( ) ;
// If there is a Reset Animation, it takes precedence by overwriting.
2022-02-05 06:16:43 +01:00
if ( has_reset_anim ) {
int rt = reset_anim - > find_track ( path , track_type ) ;
if ( rt > = 0 & & reset_anim - > track_get_key_count ( rt ) > 0 ) {
track_value - > init_value = reset_anim - > track_get_key_value ( rt , 0 ) ;
}
}
2018-06-19 03:10:48 +02:00
} break ;
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 :
case Animation : : TYPE_ROTATION_3D :
case Animation : : TYPE_SCALE_3D : {
2021-03-18 00:35:42 +01:00
# ifndef _3D_DISABLED
Node3D * node_3d = Object : : cast_to < Node3D > ( child ) ;
2018-06-19 03:10:48 +02:00
2021-03-18 00:35:42 +01:00
if ( ! node_3d ) {
2021-07-16 05:45:57 +02:00
ERR_PRINT ( " AnimationTree: ' " + String ( E ) + " ', transform track does not point to Node3D: ' " + String ( path ) + " ' " ) ;
2018-06-19 03:10:48 +02:00
continue ;
}
TrackCacheTransform * track_xform = memnew ( TrackCacheTransform ) ;
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
track_xform - > type = Animation : : TYPE_POSITION_3D ;
2018-06-19 03:10:48 +02:00
2021-03-18 00:35:42 +01:00
track_xform - > node_3d = node_3d ;
2020-04-02 01:20:12 +02:00
track_xform - > skeleton = nullptr ;
2018-06-19 03:10:48 +02:00
track_xform - > bone_idx = - 1 ;
2022-04-14 11:21:26 +02:00
bool has_rest = false ;
2021-03-18 00:35:42 +01:00
if ( path . get_subname_count ( ) = = 1 & & Object : : cast_to < Skeleton3D > ( node_3d ) ) {
Skeleton3D * sk = Object : : cast_to < Skeleton3D > ( node_3d ) ;
2020-08-21 06:56:04 +02:00
track_xform - > skeleton = sk ;
2018-06-19 03:10:48 +02:00
int bone_idx = sk - > find_bone ( path . get_subname ( 0 ) ) ;
2019-09-19 00:46:32 +02:00
if ( bone_idx ! = - 1 ) {
2022-04-14 11:21:26 +02:00
has_rest = true ;
2018-06-19 03:10:48 +02:00
track_xform - > bone_idx = bone_idx ;
2022-03-15 17:35:25 +01:00
Transform3D rest = sk - > get_bone_rest ( bone_idx ) ;
track_xform - > init_loc = rest . origin ;
2022-04-16 21:34:28 +02:00
track_xform - > init_rot = rest . basis . get_rotation_quaternion ( ) ;
2022-03-15 17:35:25 +01:00
track_xform - > init_scale = rest . basis . get_scale ( ) ;
2018-06-19 03:10:48 +02:00
}
}
2021-03-18 00:35:42 +01:00
track_xform - > object = node_3d ;
2018-06-19 03:10:48 +02:00
track_xform - > object_id = track_xform - > object - > get_instance_id ( ) ;
track = track_xform ;
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
switch ( track_type ) {
case Animation : : TYPE_POSITION_3D : {
track_xform - > loc_used = true ;
} break ;
case Animation : : TYPE_ROTATION_3D : {
track_xform - > rot_used = true ;
} break ;
case Animation : : TYPE_SCALE_3D : {
track_xform - > scale_used = true ;
} break ;
default : {
}
}
2022-04-14 11:21:26 +02:00
// For non Skeleton3D bone animation.
if ( has_reset_anim & & ! has_rest ) {
2022-02-05 06:16:43 +01:00
int rt = reset_anim - > find_track ( path , track_type ) ;
if ( rt > = 0 & & reset_anim - > track_get_key_count ( rt ) > 0 ) {
switch ( track_type ) {
case Animation : : TYPE_POSITION_3D : {
track_xform - > init_loc = reset_anim - > track_get_key_value ( rt , 0 ) ;
} break ;
case Animation : : TYPE_ROTATION_3D : {
2022-04-16 21:34:28 +02:00
track_xform - > init_rot = reset_anim - > track_get_key_value ( rt , 0 ) ;
2022-02-05 06:16:43 +01:00
} break ;
case Animation : : TYPE_SCALE_3D : {
track_xform - > init_scale = reset_anim - > track_get_key_value ( rt , 0 ) ;
} break ;
default : {
}
}
}
}
2021-03-18 00:35:42 +01:00
# endif // _3D_DISABLED
2021-10-16 00:04:35 +02:00
} break ;
case Animation : : TYPE_BLEND_SHAPE : {
# ifndef _3D_DISABLED
if ( path . get_subname_count ( ) ! = 1 ) {
ERR_PRINT ( " AnimationTree: ' " + String ( E ) + " ', blend shape track does not contain a blend shape subname: ' " + String ( path ) + " ' " ) ;
continue ;
}
MeshInstance3D * mesh_3d = Object : : cast_to < MeshInstance3D > ( child ) ;
if ( ! mesh_3d ) {
ERR_PRINT ( " AnimationTree: ' " + String ( E ) + " ', blend shape track does not point to MeshInstance3D: ' " + String ( path ) + " ' " ) ;
continue ;
}
StringName blend_shape_name = path . get_subname ( 0 ) ;
int blend_shape_idx = mesh_3d - > find_blend_shape_by_name ( blend_shape_name ) ;
if ( blend_shape_idx = = - 1 ) {
ERR_PRINT ( " AnimationTree: ' " + String ( E ) + " ', blend shape track points to a non-existing name: ' " + String ( blend_shape_name ) + " ' " ) ;
continue ;
}
TrackCacheBlendShape * track_bshape = memnew ( TrackCacheBlendShape ) ;
track_bshape - > mesh_3d = mesh_3d ;
track_bshape - > shape_index = blend_shape_idx ;
track_bshape - > object = mesh_3d ;
track_bshape - > object_id = mesh_3d - > get_instance_id ( ) ;
track = track_bshape ;
2022-02-05 06:16:43 +01:00
if ( has_reset_anim ) {
int rt = reset_anim - > find_track ( path , track_type ) ;
if ( rt > = 0 & & reset_anim - > track_get_key_count ( rt ) > 0 ) {
track_bshape - > init_value = reset_anim - > track_get_key_value ( rt , 0 ) ;
}
}
2021-10-16 00:04:35 +02:00
# endif
2018-06-19 03:10:48 +02:00
} break ;
case Animation : : TYPE_METHOD : {
TrackCacheMethod * track_method = memnew ( TrackCacheMethod ) ;
if ( resource . is_valid ( ) ) {
track_method - > object = resource . ptr ( ) ;
} else {
track_method - > object = child ;
}
track_method - > object_id = track_method - > object - > get_instance_id ( ) ;
track = track_method ;
} break ;
case Animation : : TYPE_BEZIER : {
TrackCacheBezier * track_bezier = memnew ( TrackCacheBezier ) ;
if ( resource . is_valid ( ) ) {
track_bezier - > object = resource . ptr ( ) ;
} else {
track_bezier - > object = child ;
}
track_bezier - > subpath = leftover_path ;
track_bezier - > object_id = track_bezier - > object - > get_instance_id ( ) ;
track = track_bezier ;
2022-02-05 06:16:43 +01:00
if ( has_reset_anim ) {
int rt = reset_anim - > find_track ( path , track_type ) ;
if ( rt > = 0 & & reset_anim - > track_get_key_count ( rt ) > 0 ) {
2023-01-21 22:52:40 +01:00
track_bezier - > init_value = ( reset_anim - > track_get_key_value ( rt , 0 ) . operator Array ( ) ) [ 0 ] ;
2022-02-05 06:16:43 +01:00
}
}
2018-06-19 03:10:48 +02:00
} break ;
case Animation : : TYPE_AUDIO : {
TrackCacheAudio * track_audio = memnew ( TrackCacheAudio ) ;
track_audio - > object = child ;
track_audio - > object_id = track_audio - > object - > get_instance_id ( ) ;
2023-01-27 19:25:49 +01:00
track_audio - > audio_stream . instantiate ( ) ;
track_audio - > audio_stream - > set_polyphony ( audio_max_polyphony ) ;
2018-06-19 03:10:48 +02:00
track = track_audio ;
} break ;
case Animation : : TYPE_ANIMATION : {
TrackCacheAnimation * track_animation = memnew ( TrackCacheAnimation ) ;
track_animation - > object = child ;
track_animation - > object_id = track_animation - > object - > get_instance_id ( ) ;
track = track_animation ;
} break ;
2019-01-25 13:09:10 +01:00
default : {
2019-01-26 22:35:31 +01:00
ERR_PRINT ( " Animation corrupted (invalid track type) " ) ;
2019-01-25 13:09:10 +01:00
continue ;
}
2018-06-19 03:10:48 +02:00
}
track_cache [ path ] = track ;
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 if ( track_cache_type = = Animation : : TYPE_POSITION_3D ) {
TrackCacheTransform * track_xform = static_cast < TrackCacheTransform * > ( track ) ;
if ( track - > setup_pass ! = setup_pass ) {
track_xform - > loc_used = false ;
track_xform - > rot_used = false ;
track_xform - > scale_used = false ;
}
switch ( track_type ) {
case Animation : : TYPE_POSITION_3D : {
track_xform - > loc_used = true ;
} break ;
case Animation : : TYPE_ROTATION_3D : {
track_xform - > rot_used = true ;
} break ;
case Animation : : TYPE_SCALE_3D : {
track_xform - > scale_used = true ;
} break ;
default : {
}
}
2022-08-26 04:42:00 +02:00
} else if ( track_cache_type = = Animation : : TYPE_VALUE ) {
// If it has at least one angle interpolation, it also uses angle interpolation for blending.
2022-11-27 04:51:39 +01:00
TrackCacheValue * track_value = static_cast < TrackCacheValue * > ( track ) ;
bool was_discrete = track_value - > is_discrete ;
bool was_using_angle = track_value - > is_using_angle ;
2022-11-29 10:51:45 +01:00
track_value - > is_discrete | = anim - > value_track_get_update_mode ( i ) = = Animation : : UPDATE_DISCRETE ;
2022-08-26 04:42:00 +02:00
track_value - > is_using_angle | = anim - > track_get_interpolation_type ( i ) = = Animation : : INTERPOLATION_LINEAR_ANGLE | | anim - > track_get_interpolation_type ( i ) = = Animation : : INTERPOLATION_CUBIC_ANGLE ;
2022-11-27 04:51:39 +01:00
if ( was_discrete ! = track_value - > is_discrete ) {
2022-11-29 10:51:45 +01:00
ERR_PRINT_ED ( " Value track: " + String ( path ) + " with different update modes are blended. Blending prioritizes Discrete mode, so other update mode tracks will not be blended. " ) ;
2022-11-27 04:51:39 +01:00
}
if ( was_using_angle ! = track_value - > is_using_angle ) {
2022-11-30 18:13:24 +01:00
WARN_PRINT_ED ( " Value track: " + String ( path ) + " with different interpolation types for rotation are blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value. " ) ;
2022-11-27 04:51:39 +01:00
}
2018-06-19 03:10:48 +02:00
}
track - > setup_pass = setup_pass ;
}
}
List < NodePath > to_delete ;
2022-05-08 10:09:19 +02:00
for ( const KeyValue < NodePath , TrackCache * > & K : track_cache ) {
TrackCache * tc = track_cache [ K . key ] ;
2018-06-19 03:10:48 +02:00
if ( tc - > setup_pass ! = setup_pass ) {
2022-05-08 10:09:19 +02:00
to_delete . push_back ( K . key ) ;
2018-06-19 03:10:48 +02:00
}
}
while ( to_delete . front ( ) ) {
NodePath np = to_delete . front ( ) - > get ( ) ;
memdelete ( track_cache [ np ] ) ;
track_cache . erase ( np ) ;
to_delete . pop_front ( ) ;
}
state . track_map . clear ( ) ;
int idx = 0 ;
2022-05-08 10:09:19 +02:00
for ( const KeyValue < NodePath , TrackCache * > & K : track_cache ) {
state . track_map [ K . key ] = idx ;
2018-06-19 03:10:48 +02:00
idx + + ;
}
state . track_count = idx ;
cache_valid = true ;
return true ;
}
2022-09-01 16:00:55 +02:00
void AnimationTree : : _animation_player_changed ( ) {
emit_signal ( SNAME ( " animation_player_changed " ) ) ;
_clear_caches ( ) ;
}
2018-06-25 23:40:24 +02:00
void AnimationTree : : _clear_caches ( ) {
2023-01-27 19:25:49 +01:00
_clear_audio_streams ( ) ;
_clear_playing_caches ( ) ;
2022-05-08 10:09:19 +02:00
for ( KeyValue < NodePath , TrackCache * > & K : track_cache ) {
memdelete ( K . value ) ;
2018-06-19 03:10:48 +02:00
}
track_cache . clear ( ) ;
cache_valid = false ;
}
2023-01-27 19:25:49 +01:00
void AnimationTree : : _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 ( ) ;
}
void AnimationTree : : _clear_playing_caches ( ) {
for ( const TrackCache * E : playing_caches ) {
if ( ObjectDB : : get_instance ( E - > object_id ) ) {
E - > object - > call ( SNAME ( " stop " ) ) ;
}
}
playing_caches . clear ( ) ;
}
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 ) ;
}
}
2022-01-28 21:07:30 +01:00
void AnimationTree : : _process_graph ( double p_delta ) {
2018-08-20 18:38:18 +02:00
_update_properties ( ) ; //if properties need updating, update them
2018-06-19 03:10:48 +02:00
//check all tracks, see if they need modification
2022-11-26 08:04:15 +01:00
root_motion_position = Vector3 ( 0 , 0 , 0 ) ;
root_motion_rotation = Quaternion ( 0 , 0 , 0 , 1 ) ;
root_motion_scale = Vector3 ( 0 , 0 , 0 ) ;
2018-06-19 03:10:48 +02:00
if ( ! root . is_valid ( ) ) {
2018-06-25 23:40:24 +02:00
ERR_PRINT ( " AnimationTree: root AnimationNode is not set, disabling playback. " ) ;
2018-06-19 03:10:48 +02:00
set_active ( false ) ;
cache_valid = false ;
return ;
}
if ( ! has_node ( animation_player ) ) {
2018-06-25 23:40:24 +02:00
ERR_PRINT ( " AnimationTree: no valid AnimationPlayer path set, disabling playback " ) ;
2018-06-19 03:10:48 +02:00
set_active ( false ) ;
cache_valid = false ;
return ;
}
AnimationPlayer * player = Object : : cast_to < AnimationPlayer > ( get_node ( animation_player ) ) ;
2020-02-12 18:24:06 +01:00
ObjectID current_animation_player ;
2018-08-24 16:41:04 +02:00
if ( player ) {
2018-08-24 16:50:29 +02:00
current_animation_player = player - > get_instance_id ( ) ;
2018-08-24 16:41:04 +02:00
}
if ( last_animation_player ! = current_animation_player ) {
2020-02-12 18:24:06 +01:00
if ( last_animation_player . is_valid ( ) ) {
2018-08-24 16:41:04 +02:00
Object * old_player = ObjectDB : : get_instance ( last_animation_player ) ;
if ( old_player ) {
2020-02-21 18:28:45 +01:00
old_player - > disconnect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
2018-08-24 16:41:04 +02:00
}
}
if ( player ) {
2020-02-21 18:28:45 +01:00
player - > connect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
2018-08-24 16:41:04 +02:00
}
last_animation_player = current_animation_player ;
}
2018-06-19 03:10:48 +02:00
if ( ! player ) {
2018-06-25 23:40:24 +02:00
ERR_PRINT ( " AnimationTree: path points to a node not an AnimationPlayer, disabling playback " ) ;
2018-06-19 03:10:48 +02:00
set_active ( false ) ;
cache_valid = false ;
return ;
}
if ( ! cache_valid ) {
if ( ! _update_caches ( player ) ) {
return ;
}
}
{ //setup
process_pass + + ;
state . valid = true ;
state . invalid_reasons = " " ;
state . animation_states . clear ( ) ; //will need to be re-created
state . player = player ;
state . last_pass = process_pass ;
2018-08-20 18:38:18 +02:00
state . tree = this ;
2018-06-19 03:10:48 +02:00
// root source blends
root - > blends . resize ( state . track_count ) ;
2021-08-10 00:15:17 +02:00
real_t * src_blendsw = root - > blends . ptrw ( ) ;
2018-06-19 03:10:48 +02:00
for ( int i = 0 ; i < state . track_count ; i + + ) {
src_blendsw [ i ] = 1.0 ; //by default all go to 1 for the root input
}
}
//process
{
if ( started ) {
//if started, seek
2022-05-04 20:53:48 +02:00
root - > _pre_process ( SceneStringNames : : get_singleton ( ) - > parameters_base_path , nullptr , & state , 0 , true , false , Vector < StringName > ( ) ) ;
2018-06-19 03:10:48 +02:00
started = false ;
}
2022-05-04 20:53:48 +02:00
root - > _pre_process ( SceneStringNames : : get_singleton ( ) - > parameters_base_path , nullptr , & state , p_delta , false , false , Vector < StringName > ( ) ) ;
2018-06-19 03:10:48 +02:00
}
if ( ! state . valid ) {
return ; //state is not valid. do nothing.
}
2022-11-15 07:06:10 +01:00
// Init all value/transform/blend/bezier tracks that track_cache has.
{
for ( const KeyValue < NodePath , TrackCache * > & K : track_cache ) {
TrackCache * track = K . value ;
switch ( track - > type ) {
case Animation : : TYPE_POSITION_3D : {
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
if ( track - > root_motion ) {
t - > loc = Vector3 ( 0 , 0 , 0 ) ;
t - > rot = Quaternion ( 0 , 0 , 0 , 1 ) ;
2022-11-26 08:04:15 +01:00
t - > scale = Vector3 ( 1 , 1 , 1 ) ;
2022-11-15 07:06:10 +01:00
} else {
t - > loc = t - > init_loc ;
t - > rot = t - > init_rot ;
t - > scale = t - > init_scale ;
}
} break ;
case Animation : : TYPE_BLEND_SHAPE : {
TrackCacheBlendShape * t = static_cast < TrackCacheBlendShape * > ( track ) ;
t - > value = t - > init_value ;
} break ;
case Animation : : TYPE_VALUE : {
TrackCacheValue * t = static_cast < TrackCacheValue * > ( track ) ;
t - > value = t - > init_value ;
} break ;
case Animation : : TYPE_BEZIER : {
TrackCacheBezier * t = static_cast < TrackCacheBezier * > ( track ) ;
t - > value = t - > init_value ;
} break ;
2023-01-27 19:25:49 +01:00
case Animation : : TYPE_AUDIO : {
TrackCacheAudio * t = static_cast < TrackCacheAudio * > ( track ) ;
for ( KeyValue < ObjectID , PlayingAudioTrackInfo > & L : t - > playing_streams ) {
PlayingAudioTrackInfo & track_info = L . value ;
track_info . volume = 0.0 ;
}
} break ;
2022-11-15 07:06:10 +01:00
default : {
} break ;
}
}
}
// Apply value/transform/blend/bezier blends to track caches and execute method/audio/animation tracks.
2018-06-19 03:10:48 +02:00
{
2023-01-26 05:01:26 +01:00
# ifdef TOOLS_ENABLED
2018-06-19 03:10:48 +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-07-24 15:46:25 +02:00
for ( const AnimationNode : : AnimationState & as : state . animation_states ) {
2018-06-19 03:10:48 +02:00
Ref < Animation > a = as . animation ;
2021-05-21 08:42:37 +02:00
double time = as . time ;
double delta = as . delta ;
2021-08-10 00:15:17 +02:00
real_t weight = as . blend ;
2018-06-19 03:10:48 +02:00
bool seeked = as . seeked ;
2022-11-29 10:51:45 +01:00
Animation : : LoopedFlag looped_flag = as . looped_flag ;
2022-11-21 20:30:55 +01:00
bool is_external_seeking = as . is_external_seeking ;
2023-01-27 19:25:49 +01:00
bool backward = signbit ( delta ) ; // This flag is used by the root motion calculates or detecting the end of audio stream.
2021-11-12 16:30:34 +01:00
# ifndef _3D_DISABLED
2022-11-21 20:30:55 +01:00
bool calc_root = ! seeked | | is_external_seeking ;
2021-11-12 16:30:34 +01:00
# endif // _3D_DISABLED
2018-06-19 03:10:48 +02:00
for ( int i = 0 ; i < a - > get_track_count ( ) ; i + + ) {
2022-06-23 08:35:34 +02:00
if ( ! a - > track_is_enabled ( i ) ) {
continue ;
}
2018-06-19 03:10:48 +02:00
NodePath path = a - > track_get_path ( i ) ;
2022-12-07 10:31:48 +01:00
if ( ! track_cache . has ( path ) ) {
continue ; // No path, but avoid error spamming.
}
2018-06-19 03:10:48 +02:00
TrackCache * track = track_cache [ path ] ;
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
2022-11-15 07:06:10 +01:00
ERR_CONTINUE ( ! state . track_map . has ( path ) ) ;
int blend_idx = state . track_map [ path ] ;
ERR_CONTINUE ( blend_idx < 0 | | blend_idx > = state . track_count ) ;
real_t blend = ( * as . track_blends ) [ blend_idx ] * weight ;
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
Animation : : TrackType ttype = a - > track_get_type ( i ) ;
if ( ttype ! = Animation : : TYPE_POSITION_3D & & ttype ! = Animation : : TYPE_ROTATION_3D & & ttype ! = Animation : : TYPE_SCALE_3D & & track - > type ! = ttype ) {
//broken animation, but avoid error spamming
continue ;
2018-06-19 03:10:48 +02:00
}
2018-06-27 00:05:11 +02:00
track - > root_motion = root_motion_track = = path ;
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
switch ( ttype ) {
case Animation : : TYPE_POSITION_3D : {
2021-03-18 00:35:42 +01:00
# ifndef _3D_DISABLED
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
2018-06-19 03:10:48 +02:00
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
2022-05-04 20:53:48 +02:00
if ( track - > root_motion & & calc_root ) {
2021-10-15 15:25:00 +02:00
double prev_time = time - delta ;
if ( ! backward ) {
if ( prev_time < 0 ) {
switch ( a - > get_loop_mode ( ) ) {
case Animation : : LOOP_NONE : {
prev_time = 0 ;
} break ;
case Animation : : LOOP_LINEAR : {
prev_time = Math : : fposmod ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
case Animation : : LOOP_PINGPONG : {
prev_time = Math : : pingpong ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
}
}
} else {
if ( prev_time > a - > get_length ( ) ) {
switch ( a - > get_loop_mode ( ) ) {
case Animation : : LOOP_NONE : {
prev_time = ( double ) a - > get_length ( ) ;
} break ;
case Animation : : LOOP_LINEAR : {
prev_time = Math : : fposmod ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
case Animation : : LOOP_PINGPONG : {
prev_time = Math : : pingpong ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
}
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
}
2019-03-31 22:07:09 +02:00
}
2018-06-27 00:05:11 +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
Vector3 loc [ 2 ] ;
2021-10-15 15:25:00 +02:00
if ( ! backward ) {
if ( prev_time > time ) {
Error err = a - > position_track_interpolate ( i , prev_time , & loc [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
loc [ 0 ] = post_process_key_value ( a , i , loc [ 0 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
a - > position_track_interpolate ( i , ( double ) a - > get_length ( ) , & loc [ 1 ] ) ;
2023-01-19 16:43:37 +01:00
loc [ 1 ] = post_process_key_value ( a , i , loc [ 1 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
t - > loc + = ( loc [ 1 ] - loc [ 0 ] ) * blend ;
prev_time = 0 ;
}
} else {
if ( prev_time < time ) {
Error err = a - > position_track_interpolate ( i , prev_time , & loc [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
loc [ 0 ] = post_process_key_value ( a , i , loc [ 0 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
a - > position_track_interpolate ( i , 0 , & loc [ 1 ] ) ;
2023-01-19 16:43:37 +01:00
loc [ 1 ] = post_process_key_value ( a , i , loc [ 1 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
t - > loc + = ( loc [ 1 ] - loc [ 0 ] ) * blend ;
2022-05-04 20:53:48 +02:00
prev_time = ( double ) a - > get_length ( ) ;
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
}
}
Error err = a - > position_track_interpolate ( i , prev_time , & loc [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
loc [ 0 ] = post_process_key_value ( a , i , loc [ 0 ] , t - > object , t - > 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
a - > position_track_interpolate ( i , time , & loc [ 1 ] ) ;
2023-01-19 16:43:37 +01:00
loc [ 1 ] = post_process_key_value ( a , i , loc [ 1 ] , t - > object , t - > 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
t - > loc + = ( loc [ 1 ] - loc [ 0 ] ) * blend ;
2021-10-15 15:25:00 +02:00
prev_time = ! backward ? 0 : ( double ) a - > get_length ( ) ;
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 {
Vector3 loc ;
Error err = a - > position_track_interpolate ( i , time , & loc ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
loc = post_process_key_value ( a , i , loc , t - > object , t - > 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
2022-03-15 17:35:25 +01:00
t - > loc + = ( loc - t - > init_loc ) * blend ;
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
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
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
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
2022-05-04 20:53:48 +02:00
if ( track - > root_motion & & calc_root ) {
2021-10-15 15:25:00 +02:00
double prev_time = time - delta ;
if ( ! backward ) {
if ( prev_time < 0 ) {
switch ( a - > get_loop_mode ( ) ) {
case Animation : : LOOP_NONE : {
prev_time = 0 ;
} break ;
case Animation : : LOOP_LINEAR : {
prev_time = Math : : fposmod ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
case Animation : : LOOP_PINGPONG : {
prev_time = Math : : pingpong ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
}
}
} else {
if ( prev_time > a - > get_length ( ) ) {
switch ( a - > get_loop_mode ( ) ) {
case Animation : : LOOP_NONE : {
prev_time = ( double ) a - > get_length ( ) ;
} break ;
case Animation : : LOOP_LINEAR : {
prev_time = Math : : fposmod ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
case Animation : : LOOP_PINGPONG : {
prev_time = Math : : pingpong ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
}
2018-06-27 00:05:11 +02:00
}
}
2021-01-20 08:02:02 +01:00
Quaternion rot [ 2 ] ;
2018-06-27 00:05:11 +02:00
2021-10-15 15:25:00 +02:00
if ( ! backward ) {
if ( prev_time > time ) {
Error err = a - > rotation_track_interpolate ( i , prev_time , & rot [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
rot [ 0 ] = post_process_key_value ( a , i , rot [ 0 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
a - > rotation_track_interpolate ( i , ( double ) a - > get_length ( ) , & rot [ 1 ] ) ;
2023-01-19 16:43:37 +01:00
rot [ 1 ] = post_process_key_value ( a , i , rot [ 1 ] , t - > object , t - > bone_idx ) ;
2022-04-16 21:34:28 +02:00
t - > rot = ( t - > rot * Quaternion ( ) . slerp ( rot [ 0 ] . inverse ( ) * rot [ 1 ] , blend ) ) . normalized ( ) ;
2021-10-15 15:25:00 +02:00
prev_time = 0 ;
}
} else {
if ( prev_time < time ) {
Error err = a - > rotation_track_interpolate ( i , prev_time , & rot [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
rot [ 0 ] = post_process_key_value ( a , i , rot [ 0 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
a - > rotation_track_interpolate ( i , 0 , & rot [ 1 ] ) ;
2022-04-16 21:34:28 +02:00
t - > rot = ( t - > rot * Quaternion ( ) . slerp ( rot [ 0 ] . inverse ( ) * rot [ 1 ] , blend ) ) . normalized ( ) ;
2022-05-04 20:53:48 +02:00
prev_time = ( double ) a - > get_length ( ) ;
2018-06-27 00:05:11 +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
Error err = a - > rotation_track_interpolate ( i , prev_time , & rot [ 0 ] ) ;
2018-06-27 00:05:11 +02:00
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
rot [ 0 ] = post_process_key_value ( a , i , rot [ 0 ] , t - > object , t - > bone_idx ) ;
2018-06-27 00:05:11 +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
a - > rotation_track_interpolate ( i , time , & rot [ 1 ] ) ;
2023-01-19 16:43:37 +01:00
rot [ 1 ] = post_process_key_value ( a , i , rot [ 1 ] , t - > object , t - > bone_idx ) ;
2022-04-16 21:34:28 +02:00
t - > rot = ( t - > rot * Quaternion ( ) . slerp ( rot [ 0 ] . inverse ( ) * rot [ 1 ] , blend ) ) . normalized ( ) ;
2021-10-15 15:25:00 +02:00
prev_time = ! backward ? 0 : ( double ) a - > get_length ( ) ;
2021-10-12 00:27:50 +02:00
2018-06-27 00:05:11 +02:00
} else {
2021-01-20 08:02:02 +01:00
Quaternion rot ;
2018-06-27 00:05:11 +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
Error err = a - > rotation_track_interpolate ( i , time , & rot ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2018-06-27 00:05:11 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2023-01-19 16:43:37 +01:00
rot = post_process_key_value ( a , i , rot , t - > object , t - > bone_idx ) ;
2018-06-27 00:05:11 +02:00
2022-04-16 21:34:28 +02:00
t - > rot = ( t - > rot * Quaternion ( ) . slerp ( t - > init_rot . inverse ( ) * rot , blend ) ) . normalized ( ) ;
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
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
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
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
2022-05-04 20:53:48 +02:00
if ( track - > root_motion & & calc_root ) {
2021-10-15 15:25:00 +02:00
double prev_time = time - delta ;
if ( ! backward ) {
if ( prev_time < 0 ) {
switch ( a - > get_loop_mode ( ) ) {
case Animation : : LOOP_NONE : {
prev_time = 0 ;
} break ;
case Animation : : LOOP_LINEAR : {
prev_time = Math : : fposmod ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
case Animation : : LOOP_PINGPONG : {
prev_time = Math : : pingpong ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
}
}
} else {
if ( prev_time > a - > get_length ( ) ) {
switch ( a - > get_loop_mode ( ) ) {
case Animation : : LOOP_NONE : {
prev_time = ( double ) a - > get_length ( ) ;
} break ;
case Animation : : LOOP_LINEAR : {
prev_time = Math : : fposmod ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
case Animation : : LOOP_PINGPONG : {
prev_time = Math : : pingpong ( prev_time , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
}
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
}
}
Vector3 scale [ 2 ] ;
2021-10-15 15:25:00 +02:00
if ( ! backward ) {
if ( prev_time > time ) {
Error err = a - > scale_track_interpolate ( i , prev_time , & scale [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
scale [ 0 ] = post_process_key_value ( a , i , scale [ 0 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
a - > scale_track_interpolate ( i , ( double ) a - > get_length ( ) , & scale [ 1 ] ) ;
t - > scale + = ( scale [ 1 ] - scale [ 0 ] ) * blend ;
2023-01-19 16:43:37 +01:00
scale [ 1 ] = post_process_key_value ( a , i , scale [ 1 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
prev_time = 0 ;
}
} else {
if ( prev_time < time ) {
Error err = a - > scale_track_interpolate ( i , prev_time , & scale [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
scale [ 0 ] = post_process_key_value ( a , i , scale [ 0 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
a - > scale_track_interpolate ( i , 0 , & scale [ 1 ] ) ;
2023-01-19 16:43:37 +01:00
scale [ 1 ] = post_process_key_value ( a , i , scale [ 1 ] , t - > object , t - > bone_idx ) ;
2021-10-15 15:25:00 +02:00
t - > scale + = ( scale [ 1 ] - scale [ 0 ] ) * blend ;
2022-05-04 20:53:48 +02:00
prev_time = ( double ) a - > get_length ( ) ;
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
}
}
Error err = a - > scale_track_interpolate ( i , prev_time , & scale [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
scale [ 0 ] = post_process_key_value ( a , i , scale [ 0 ] , t - > object , t - > 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
a - > scale_track_interpolate ( i , time , & scale [ 1 ] ) ;
2023-01-19 16:43:37 +01:00
scale [ 1 ] = post_process_key_value ( a , i , scale [ 1 ] , t - > object , t - > 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
t - > scale + = ( scale [ 1 ] - scale [ 0 ] ) * blend ;
2021-10-15 15:25:00 +02:00
prev_time = ! backward ? 0 : ( double ) a - > get_length ( ) ;
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 {
Vector3 scale ;
Error err = a - > scale_track_interpolate ( i , time , & scale ) ;
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
scale = post_process_key_value ( a , i , scale , t - > object , t - > 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
2022-03-15 17:35:25 +01:00
t - > scale + = ( scale - t - > init_scale ) * blend ;
2018-06-27 00:05:11 +02:00
}
2021-10-16 00:04:35 +02:00
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_BLEND_SHAPE : {
# ifndef _3D_DISABLED
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
2021-10-16 00:04:35 +02:00
TrackCacheBlendShape * t = static_cast < TrackCacheBlendShape * > ( track ) ;
float value ;
Error err = a - > blend_shape_track_interpolate ( i , time , & value ) ;
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
if ( err ! = OK ) {
continue ;
}
2023-01-19 16:43:37 +01:00
value = post_process_key_value ( a , i , value , t - > object , t - > shape_index ) ;
2021-10-16 00:04:35 +02:00
2022-02-05 06:16:43 +01:00
t - > value + = ( value - t - > init_value ) * blend ;
2021-03-18 00:35:42 +01:00
# endif // _3D_DISABLED
2018-06-19 03:10:48 +02:00
} break ;
case Animation : : TYPE_VALUE : {
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
2018-06-19 03:10:48 +02:00
TrackCacheValue * t = static_cast < TrackCacheValue * > ( track ) ;
Animation : : UpdateMode update_mode = a - > value_track_get_update_mode ( i ) ;
2022-05-04 20:53:48 +02:00
if ( update_mode = = Animation : : UPDATE_CONTINUOUS | | update_mode = = Animation : : UPDATE_CAPTURE ) {
2018-06-19 03:10:48 +02:00
Variant value = a - > value_track_interpolate ( i , time ) ;
2023-01-19 16:43:37 +01:00
value = post_process_key_value ( a , i , value , t - > object ) ;
2018-06-19 03:10:48 +02:00
2020-05-14 16:41:43 +02:00
if ( value = = Variant ( ) ) {
2018-06-19 03:10:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
2022-08-26 04:42:00 +02:00
// Special case for angle interpolation.
if ( t - > is_using_angle ) {
// For blending consistency, it prevents rotation of more than 180 degrees from init_value.
// This is the same as for Quaternion blends.
float rot_a = t - > value ;
float rot_b = value ;
float rot_init = t - > init_value ;
rot_a = Math : : fposmod ( rot_a , ( float ) Math_TAU ) ;
rot_b = Math : : fposmod ( rot_b , ( float ) Math_TAU ) ;
rot_init = Math : : fposmod ( rot_init , ( float ) Math_TAU ) ;
if ( rot_init < Math_PI ) {
rot_a = rot_a > rot_init + Math_PI ? rot_a - Math_TAU : rot_a ;
rot_b = rot_b > rot_init + Math_PI ? rot_b - Math_TAU : rot_b ;
} else {
rot_a = rot_a < rot_init - Math_PI ? rot_a + Math_TAU : rot_a ;
rot_b = rot_b < rot_init - Math_PI ? rot_b + Math_TAU : rot_b ;
}
t - > value = Math : : fposmod ( rot_a + ( rot_b - rot_init ) * ( float ) blend , ( float ) Math_TAU ) ;
} else {
2022-09-06 05:24:06 +02:00
if ( t - > init_value . get_type ( ) = = Variant : : BOOL ) {
value = Animation : : subtract_variant ( value . operator real_t ( ) , t - > init_value . operator real_t ( ) ) ;
t - > value = Animation : : blend_variant ( t - > value . operator real_t ( ) , value . operator real_t ( ) , blend ) ;
} else {
value = Animation : : subtract_variant ( value , t - > init_value ) ;
t - > value = Animation : : blend_variant ( t - > value , value , blend ) ;
}
2022-08-26 04:42:00 +02:00
}
2021-09-10 22:58:17 +02:00
} else {
2022-06-10 10:01:07 +02:00
if ( seeked ) {
2022-12-08 13:38:01 +01:00
int idx = a - > track_find_key ( i , time , is_external_seeking ? Animation : : FIND_MODE_NEAREST : Animation : : FIND_MODE_EXACT ) ;
2022-06-10 10:01:07 +02:00
if ( idx < 0 ) {
continue ;
}
Variant value = a - > track_get_key_value ( i , idx ) ;
2023-01-19 16:43:37 +01:00
value = post_process_key_value ( a , i , value , t - > object ) ;
2018-06-19 03:10:48 +02:00
t - > object - > set_indexed ( t - > subpath , value ) ;
2022-06-10 10:01:07 +02:00
} else {
List < int > indices ;
2022-11-29 10:51:45 +01:00
a - > track_get_key_indices_in_range ( i , time , delta , & indices , looped_flag ) ;
2022-06-10 10:01:07 +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 , t - > object ) ;
2022-06-10 10:01:07 +02:00
t - > object - > set_indexed ( t - > subpath , value ) ;
}
2018-06-19 03:10:48 +02:00
}
}
} break ;
case Animation : : TYPE_METHOD : {
2023-01-26 05:01:26 +01:00
# ifdef TOOLS_ENABLED
if ( ! can_call ) {
2023-01-27 09:44:25 +01:00
continue ;
2023-01-26 05:01:26 +01:00
}
# endif // TOOLS_ENABLED
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
2018-06-19 03:10:48 +02:00
TrackCacheMethod * t = static_cast < TrackCacheMethod * > ( track ) ;
2022-06-10 10:01:07 +02:00
if ( seeked ) {
2022-12-08 13:38:01 +01:00
int idx = a - > track_find_key ( i , time , is_external_seeking ? Animation : : FIND_MODE_NEAREST : Animation : : FIND_MODE_EXACT ) ;
2022-06-10 10:01:07 +02:00
if ( idx < 0 ) {
continue ;
}
StringName method = a - > method_track_get_name ( i , idx ) ;
Vector < Variant > params = a - > method_track_get_params ( i , idx ) ;
2023-01-26 05:01:26 +01:00
_call_object ( t - > object , method , params , false ) ;
2022-06-10 10:01:07 +02:00
} else {
List < int > indices ;
2022-11-29 10:51:45 +01:00
a - > track_get_key_indices_in_range ( i , time , delta , & indices , looped_flag ) ;
2022-06-10 10:01:07 +02:00
for ( int & F : indices ) {
StringName method = a - > method_track_get_name ( i , F ) ;
Vector < Variant > params = a - > method_track_get_params ( i , F ) ;
2023-01-26 05:01:26 +01:00
_call_object ( t - > object , method , params , true ) ;
2018-06-19 03:10:48 +02:00
}
}
} break ;
case Animation : : TYPE_BEZIER : {
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
2018-06-19 03:10:48 +02:00
TrackCacheBezier * t = static_cast < TrackCacheBezier * > ( track ) ;
2021-08-10 00:15:17 +02:00
real_t bezier = a - > bezier_track_interpolate ( i , time ) ;
2023-01-19 16:43:37 +01:00
bezier = post_process_key_value ( a , i , bezier , t - > object ) ;
2018-06-19 03:10:48 +02:00
2022-02-05 06:16:43 +01:00
t - > value + = ( bezier - t - > init_value ) * blend ;
2018-06-19 03:10:48 +02:00
} break ;
case Animation : : TYPE_AUDIO : {
TrackCacheAudio * t = static_cast < TrackCacheAudio * > ( track ) ;
2023-01-27 19:25:49 +01:00
Node * asp = Object : : cast_to < Node > ( t - > object ) ;
if ( ! asp ) {
t - > playing_streams . clear ( ) ;
continue ;
}
2018-06-19 03:10:48 +02:00
2023-01-27 19:25:49 +01:00
ObjectID oid = a - > get_instance_id ( ) ;
if ( ! t - > playing_streams . has ( oid ) ) {
t - > playing_streams [ oid ] = PlayingAudioTrackInfo ( ) ;
}
// The end of audio should be observed even if the blend value is 0, build up the information and store to the cache for that.
PlayingAudioTrackInfo & track_info = t - > playing_streams [ oid ] ;
track_info . length = a - > get_length ( ) ;
track_info . time = time ;
track_info . volume + = blend ;
track_info . loop = a - > get_loop_mode ( ) ! = Animation : : LOOP_NONE ;
track_info . backward = backward ;
track_info . use_blend = a - > audio_track_is_use_blend ( i ) ;
HashMap < int , PlayingAudioStreamInfo > & map = track_info . stream_info ;
// Find stream.
int idx = - 1 ;
if ( seeked ) {
idx = a - > track_find_key ( i , time , is_external_seeking ? Animation : : FIND_MODE_NEAREST : Animation : : FIND_MODE_EXACT ) ;
// Discard previous stream when seeking.
if ( map . has ( idx ) ) {
t - > audio_stream_playback - > stop_stream ( map [ idx ] . index ) ;
map . erase ( idx ) ;
2018-06-19 03:10:48 +02:00
}
} else {
List < int > to_play ;
2022-11-29 10:51:45 +01:00
a - > track_get_key_indices_in_range ( i , time , delta , & to_play , looped_flag ) ;
2018-06-19 03:10:48 +02:00
if ( to_play . size ( ) ) {
2023-01-27 19:25:49 +01:00
idx = to_play . back ( ) - > get ( ) ;
}
}
if ( idx < 0 ) {
continue ;
}
2018-06-19 03:10:48 +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 ( ) ;
if ( t - > object - > call ( SNAME ( " get_stream " ) ) ! = t - > audio_stream ) {
t - > object - > call ( SNAME ( " set_stream " ) , t - > audio_stream ) ;
t - > 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 ( ! t - > object - > call ( SNAME ( " is_playing " ) ) ) {
t - > object - > call ( SNAME ( " play " ) ) ;
}
if ( ! t - > object - > call ( SNAME ( " has_stream_playback " ) ) ) {
t - > audio_stream_playback . unref ( ) ;
continue ;
}
if ( t - > audio_stream_playback . is_null ( ) ) {
t - > audio_stream_playback = t - > object - > call ( SNAME ( " get_stream_playback " ) ) ;
}
2018-07-01 22:44:15 +02:00
2023-01-27 19:25:49 +01:00
PlayingAudioStreamInfo pasi ;
pasi . index = t - > audio_stream_playback - > play_stream ( stream , start_ofs ) ;
pasi . start = 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-19 03:10:48 +02:00
}
2023-01-27 19:25:49 +01:00
map [ idx ] = pasi ;
2018-06-19 03:10:48 +02:00
}
} break ;
case Animation : : TYPE_ANIMATION : {
2023-01-27 19:25:49 +01:00
if ( Math : : is_zero_approx ( blend ) ) {
continue ; // Nothing to blend.
}
2018-06-19 03:10:48 +02:00
TrackCacheAnimation * t = static_cast < TrackCacheAnimation * > ( track ) ;
2019-02-12 21:10:08 +01:00
AnimationPlayer * player2 = Object : : cast_to < AnimationPlayer > ( t - > object ) ;
2018-06-19 03:10:48 +02:00
2020-05-14 16:41:43 +02:00
if ( ! player2 ) {
2018-06-19 03:10:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
2021-10-15 15:25:00 +02:00
if ( seeked ) {
2018-06-19 03:10:48 +02:00
//seek
2022-12-08 13:38:01 +01:00
int idx = a - > track_find_key ( i , time , is_external_seeking ? Animation : : FIND_MODE_NEAREST : Animation : : FIND_MODE_EXACT ) ;
2020-05-14 16:41:43 +02:00
if ( idx < 0 ) {
2018-06-19 03:10:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
2021-05-21 08:42:37 +02:00
double pos = a - > track_get_key_time ( i , idx ) ;
2018-06-19 03:10:48 +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] " | | ! player2 - > has_animation ( anim_name ) ) {
2018-06-19 03:10:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-06-19 03:10:48 +02:00
2019-02-12 21:10:08 +01:00
Ref < Animation > anim = player2 - > get_animation ( anim_name ) ;
2018-06-19 03:10:48 +02:00
2022-01-28 21:07:30 +01:00
double at_anim_pos = 0.0 ;
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 = MAX ( ( double ) anim - > get_length ( ) , 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 ( 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 ( time - pos , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
2018-06-19 03:10:48 +02:00
}
2019-02-12 21:10:08 +01:00
if ( player2 - > is_playing ( ) | | seeked ) {
player2 - > seek ( at_anim_pos ) ;
2022-12-11 22:25:50 +01:00
player2 - > play ( anim_name ) ;
2018-06-19 03:10:48 +02:00
t - > playing = true ;
playing_caches . insert ( t ) ;
} else {
2019-02-12 21:10:08 +01:00
player2 - > set_assigned_animation ( anim_name ) ;
player2 - > seek ( at_anim_pos , true ) ;
2018-06-19 03:10:48 +02:00
}
} else {
//find stuff to play
List < int > to_play ;
2022-11-29 10:51:45 +01:00
a - > track_get_key_indices_in_range ( i , time , delta , & to_play , looped_flag ) ;
2018-06-19 03:10:48 +02:00
if ( to_play . size ( ) ) {
int idx = to_play . back ( ) - > get ( ) ;
StringName anim_name = a - > animation_track_get_key_animation ( i , idx ) ;
2019-02-12 21:10:08 +01:00
if ( String ( anim_name ) = = " [stop] " | | ! player2 - > has_animation ( anim_name ) ) {
2018-06-19 03:10:48 +02:00
if ( playing_caches . has ( t ) ) {
playing_caches . erase ( t ) ;
2019-02-12 21:10:08 +01:00
player2 - > stop ( ) ;
2018-06-19 03:10:48 +02:00
t - > playing = false ;
}
} else {
2019-02-12 21:10:08 +01:00
player2 - > play ( anim_name ) ;
2018-06-19 03:10:48 +02:00
t - > playing = true ;
playing_caches . insert ( t ) ;
}
}
}
} break ;
}
}
}
}
{
// finally, set the tracks
2022-05-08 10:09:19 +02:00
for ( const KeyValue < NodePath , TrackCache * > & K : track_cache ) {
TrackCache * track = K . value ;
2018-06-19 03:10:48 +02:00
switch ( track - > type ) {
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
2018-06-19 03:10:48 +02:00
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
2018-06-27 00:05:11 +02:00
if ( t - > root_motion ) {
2022-12-01 03:11:21 +01:00
root_motion_position = t - > loc ;
2022-11-26 08:04:15 +01:00
root_motion_rotation = t - > rot ;
root_motion_scale = t - > scale - Vector3 ( 1 , 1 , 1 ) ;
2018-06-27 00:05:11 +02:00
2018-06-27 08:36:26 +02:00
} else if ( t - > skeleton & & t - > 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 ( t - > loc_used ) {
t - > skeleton - > set_bone_pose_position ( t - > bone_idx , t - > loc ) ;
}
if ( t - > rot_used ) {
t - > skeleton - > set_bone_pose_rotation ( t - > bone_idx , t - > rot ) ;
}
if ( t - > scale_used ) {
t - > skeleton - > set_bone_pose_scale ( t - > bone_idx , t - > scale ) ;
}
2018-06-19 03:10:48 +02:00
2020-08-21 06:56:04 +02:00
} else if ( ! t - > 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
if ( t - > loc_used ) {
t - > node_3d - > set_position ( t - > loc ) ;
}
if ( t - > rot_used ) {
t - > node_3d - > set_rotation ( t - > rot . get_euler ( ) ) ;
}
if ( t - > scale_used ) {
t - > node_3d - > set_scale ( t - > scale ) ;
}
2018-06-19 03:10:48 +02:00
}
2021-10-16 00:04:35 +02:00
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_BLEND_SHAPE : {
# ifndef _3D_DISABLED
TrackCacheBlendShape * t = static_cast < TrackCacheBlendShape * > ( track ) ;
if ( t - > mesh_3d ) {
t - > mesh_3d - > set_blend_shape_value ( t - > shape_index , t - > value ) ;
}
2021-03-18 00:35:42 +01:00
# endif // _3D_DISABLED
2018-06-19 03:10:48 +02:00
} break ;
case Animation : : TYPE_VALUE : {
TrackCacheValue * t = static_cast < TrackCacheValue * > ( track ) ;
2022-11-22 07:10:38 +01:00
if ( t - > is_discrete ) {
2022-11-29 10:51:45 +01:00
break ; // Don't overwrite the value set by UPDATE_DISCRETE.
2022-11-22 07:10:38 +01:00
}
2022-09-06 05:24:06 +02:00
if ( t - > init_value . get_type ( ) = = Variant : : BOOL ) {
t - > object - > set_indexed ( t - > subpath , t - > value . operator real_t ( ) > = 0.5 ) ;
} else {
t - > object - > set_indexed ( t - > subpath , t - > value ) ;
}
2018-06-19 03:10:48 +02:00
} break ;
case Animation : : TYPE_BEZIER : {
TrackCacheBezier * t = static_cast < TrackCacheBezier * > ( track ) ;
t - > object - > set_indexed ( t - > subpath , t - > value ) ;
} break ;
2023-01-27 19:25:49 +01:00
case Animation : : TYPE_AUDIO : {
TrackCacheAudio * t = static_cast < TrackCacheAudio * > ( track ) ;
// Audio ending process.
LocalVector < ObjectID > erase_maps ;
for ( KeyValue < ObjectID , PlayingAudioTrackInfo > & L : t - > playing_streams ) {
PlayingAudioTrackInfo & track_info = L . value ;
float db = Math : : linear_to_db ( track_info . use_blend ? track_info . volume : 1.0 ) ;
LocalVector < int > erase_streams ;
HashMap < int , PlayingAudioStreamInfo > & map = track_info . stream_info ;
for ( const KeyValue < int , PlayingAudioStreamInfo > & M : map ) {
PlayingAudioStreamInfo pasi = M . value ;
bool stop = false ;
if ( ! t - > audio_stream_playback - > is_stream_playing ( pasi . index ) ) {
stop = true ;
}
if ( ! track_info . loop ) {
if ( ! track_info . backward ) {
if ( track_info . time < pasi . start ) {
stop = true ;
}
} else if ( track_info . backward ) {
if ( track_info . time > pasi . start ) {
stop = true ;
}
}
}
if ( pasi . len > 0 ) {
double len = 0.0 ;
if ( ! track_info . backward ) {
len = pasi . start > track_info . time ? ( track_info . length - pasi . start ) + track_info . time : track_info . time - pasi . start ;
} else {
len = pasi . start < track_info . time ? ( track_info . length - track_info . time ) + pasi . start : pasi . start - track_info . time ;
}
if ( len > pasi . len ) {
stop = true ;
}
}
if ( stop ) {
// Time to stop.
t - > audio_stream_playback - > stop_stream ( pasi . index ) ;
erase_streams . push_back ( M . key ) ;
} else {
t - > audio_stream_playback - > set_stream_volume ( pasi . index , db ) ;
}
}
for ( uint32_t erase_idx = 0 ; erase_idx < erase_streams . size ( ) ; erase_idx + + ) {
map . erase ( erase_streams [ erase_idx ] ) ;
}
if ( map . size ( ) = = 0 ) {
erase_maps . push_back ( L . key ) ;
}
}
for ( uint32_t erase_idx = 0 ; erase_idx < erase_maps . size ( ) ; erase_idx + + ) {
t - > playing_streams . erase ( erase_maps [ erase_idx ] ) ;
}
} break ;
2019-04-09 17:08:36 +02:00
default : {
} //the rest don't matter
2018-06-19 03:10:48 +02:00
}
}
}
}
2023-01-19 16:43:37 +01:00
Variant AnimationTree : : 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 AnimationTree : : _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-09-22 15:54:15 +02:00
void AnimationTree : : advance ( double p_time ) {
2018-08-02 09:22:24 +02:00
_process_graph ( p_time ) ;
}
2018-06-25 23:40:24 +02:00
void AnimationTree : : _notification ( int p_what ) {
2022-02-15 18:06:48 +01:00
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
2022-09-01 16:00:55 +02:00
_setup_animation_player ( ) ;
2022-02-15 18:06:48 +01:00
if ( last_animation_player . is_valid ( ) ) {
Object * player = ObjectDB : : get_instance ( last_animation_player ) ;
if ( player ) {
player - > connect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
}
}
} break ;
case NOTIFICATION_EXIT_TREE : {
_clear_caches ( ) ;
if ( last_animation_player . is_valid ( ) ) {
Object * player = ObjectDB : : get_instance ( last_animation_player ) ;
if ( player ) {
player - > disconnect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
}
}
} break ;
2018-06-19 03:10:48 +02:00
2022-02-15 18:06:48 +01:00
case NOTIFICATION_INTERNAL_PROCESS : {
if ( active & & process_callback = = ANIMATION_PROCESS_IDLE ) {
_process_graph ( get_process_delta_time ( ) ) ;
2019-04-10 15:48:18 +02:00
}
2022-02-15 18:06:48 +01:00
} break ;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
if ( active & & process_callback = = ANIMATION_PROCESS_PHYSICS ) {
_process_graph ( get_physics_process_delta_time ( ) ) ;
2018-08-24 16:41:04 +02:00
}
2022-02-15 18:06:48 +01:00
} break ;
2018-06-19 03:10:48 +02:00
}
}
2022-09-01 16:00:55 +02:00
void AnimationTree : : _setup_animation_player ( ) {
if ( ! is_inside_tree ( ) ) {
return ;
}
AnimationPlayer * new_player = nullptr ;
if ( ! animation_player . is_empty ( ) ) {
2022-12-12 19:00:11 +01:00
new_player = Object : : cast_to < AnimationPlayer > ( get_node_or_null ( animation_player ) ) ;
2022-09-01 16:00:55 +02:00
if ( new_player & & ! new_player - > is_connected ( " animation_list_changed " , callable_mp ( this , & AnimationTree : : _animation_player_changed ) ) ) {
new_player - > connect ( " animation_list_changed " , callable_mp ( this , & AnimationTree : : _animation_player_changed ) ) ;
}
}
if ( new_player ) {
if ( ! last_animation_player . is_valid ( ) ) {
// Animation player set newly.
emit_signal ( SNAME ( " animation_player_changed " ) ) ;
return ;
} else if ( last_animation_player = = new_player - > get_instance_id ( ) ) {
// Animation player isn't changed.
return ;
}
} else if ( ! last_animation_player . is_valid ( ) ) {
// Animation player is being empty.
return ;
}
AnimationPlayer * old_player = Object : : cast_to < AnimationPlayer > ( ObjectDB : : get_instance ( last_animation_player ) ) ;
if ( old_player & & old_player - > is_connected ( " animation_list_changed " , callable_mp ( this , & AnimationTree : : _animation_player_changed ) ) ) {
old_player - > disconnect ( " animation_list_changed " , callable_mp ( this , & AnimationTree : : _animation_player_changed ) ) ;
}
emit_signal ( SNAME ( " animation_player_changed " ) ) ;
}
2018-06-25 23:40:24 +02:00
void AnimationTree : : set_animation_player ( const NodePath & p_player ) {
2018-06-19 03:10:48 +02:00
animation_player = p_player ;
2022-09-01 16:00:55 +02:00
_setup_animation_player ( ) ;
2020-10-29 11:01:28 +01:00
update_configuration_warnings ( ) ;
2018-06-19 03:10:48 +02:00
}
2018-06-25 23:40:24 +02:00
NodePath AnimationTree : : get_animation_player ( ) const {
2018-06-19 03:10:48 +02:00
return animation_player ;
}
2022-04-20 12:36:54 +02:00
void AnimationTree : : set_advance_expression_base_node ( const NodePath & p_advance_expression_base_node ) {
advance_expression_base_node = p_advance_expression_base_node ;
}
NodePath AnimationTree : : get_advance_expression_base_node ( ) const {
return advance_expression_base_node ;
}
2023-01-27 19:25:49 +01:00
void AnimationTree : : 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 AnimationTree : : get_audio_max_polyphony ( ) const {
return audio_max_polyphony ;
}
2018-06-25 23:40:24 +02:00
bool AnimationTree : : is_state_invalid ( ) const {
2018-06-19 03:10:48 +02:00
return ! state . valid ;
}
2020-05-14 14:29:06 +02:00
2018-06-25 23:40:24 +02:00
String AnimationTree : : get_invalid_state_reason ( ) const {
2018-06-19 03:10:48 +02:00
return state . invalid_reasons ;
}
2018-06-25 23:40:24 +02:00
uint64_t AnimationTree : : get_last_process_pass ( ) const {
2018-06-19 03:10:48 +02:00
return process_pass ;
}
2022-09-19 17:43:15 +02:00
PackedStringArray AnimationTree : : get_configuration_warnings ( ) const {
PackedStringArray warnings = Node : : get_configuration_warnings ( ) ;
2018-06-19 03:10:48 +02:00
if ( ! root . is_valid ( ) ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " No root AnimationNode for the graph is set. " ) ) ;
2018-06-19 03:10:48 +02:00
}
if ( ! has_node ( animation_player ) ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " Path to an AnimationPlayer node containing animations is not set. " ) ) ;
2020-05-14 22:59:27 +02:00
} else {
AnimationPlayer * player = Object : : cast_to < AnimationPlayer > ( get_node ( animation_player ) ) ;
2018-06-19 03:10:48 +02:00
2020-05-14 22:59:27 +02:00
if ( ! player ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " Path set for AnimationPlayer does not lead to an AnimationPlayer node. " ) ) ;
2020-05-14 22:59:27 +02:00
} else if ( ! player - > has_node ( player - > get_root ( ) ) ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " The AnimationPlayer root node is not a valid node. " ) ) ;
2018-06-19 03:10:48 +02:00
}
}
2020-10-29 11:01:28 +01:00
return warnings ;
2018-06-19 03:10:48 +02:00
}
2018-06-27 08:36:26 +02:00
void AnimationTree : : set_root_motion_track ( const NodePath & p_track ) {
root_motion_track = p_track ;
2018-06-27 00:05:11 +02:00
}
NodePath AnimationTree : : get_root_motion_track ( ) const {
return root_motion_track ;
}
2022-11-26 08:04:15 +01:00
Vector3 AnimationTree : : get_root_motion_position ( ) const {
return root_motion_position ;
}
Quaternion AnimationTree : : get_root_motion_rotation ( ) const {
return root_motion_rotation ;
}
Vector3 AnimationTree : : get_root_motion_scale ( ) const {
return root_motion_scale ;
2018-06-27 00:05:11 +02:00
}
2018-08-20 18:38:18 +02:00
void AnimationTree : : _tree_changed ( ) {
if ( properties_dirty ) {
return ;
}
2021-07-17 23:22:52 +02:00
call_deferred ( SNAME ( " _update_properties " ) ) ;
2018-08-21 21:28:06 +02:00
properties_dirty = true ;
2018-08-20 18:38:18 +02:00
}
2018-08-21 21:28:06 +02:00
void AnimationTree : : _update_properties_for_node ( const String & p_base_path , Ref < AnimationNode > node ) {
2021-03-17 12:33:29 +01:00
ERR_FAIL_COND ( node . is_null ( ) ) ;
2018-08-20 18:38:18 +02:00
if ( ! property_parent_map . has ( p_base_path ) ) {
2018-08-21 21:28:06 +02:00
property_parent_map [ p_base_path ] = HashMap < StringName , StringName > ( ) ;
2018-08-20 18:38:18 +02:00
}
2018-08-23 21:44:10 +02:00
if ( node - > get_input_count ( ) & & ! input_activity_map . has ( p_base_path ) ) {
Vector < Activity > activity ;
2018-08-24 16:50:29 +02:00
for ( int i = 0 ; i < node - > get_input_count ( ) ; i + + ) {
2018-08-23 21:44:10 +02:00
Activity a ;
2019-08-27 17:13:08 +02:00
a . activity = 0 ;
2018-08-24 16:50:29 +02:00
a . last_pass = 0 ;
2018-08-23 21:44:10 +02:00
activity . push_back ( a ) ;
}
input_activity_map [ p_base_path ] = activity ;
2018-08-24 16:50:29 +02:00
input_activity_map_get [ String ( p_base_path ) . substr ( 0 , String ( p_base_path ) . length ( ) - 1 ) ] = & input_activity_map [ p_base_path ] ;
2018-08-23 21:44:10 +02:00
}
2018-08-20 18:38:18 +02:00
List < PropertyInfo > plist ;
node - > get_parameter_list ( & plist ) ;
2021-07-16 05:45:57 +02:00
for ( PropertyInfo & pinfo : plist ) {
2018-08-20 18:38:18 +02:00
StringName key = pinfo . name ;
2018-08-21 21:28:06 +02:00
if ( ! property_map . has ( p_base_path + key ) ) {
2023-01-12 13:51:03 +01:00
Pair < Variant , bool > param ;
param . first = node - > get_parameter_default_value ( key ) ;
param . second = node - > is_parameter_read_only ( key ) ;
property_map [ p_base_path + key ] = param ;
2018-08-20 18:38:18 +02:00
}
2018-08-21 21:28:06 +02:00
property_parent_map [ p_base_path ] [ key ] = p_base_path + key ;
2018-08-20 18:38:18 +02:00
pinfo . name = p_base_path + key ;
properties . push_back ( pinfo ) ;
}
List < AnimationNode : : ChildNode > children ;
node - > get_child_nodes ( & children ) ;
2021-07-24 15:46:25 +02:00
for ( const AnimationNode : : ChildNode & E : children ) {
2021-07-16 05:45:57 +02:00
_update_properties_for_node ( p_base_path + E . name + " / " , E . node ) ;
2018-08-20 18:38:18 +02:00
}
}
void AnimationTree : : _update_properties ( ) {
if ( ! properties_dirty ) {
return ;
}
properties . clear ( ) ;
property_parent_map . clear ( ) ;
2018-08-23 21:44:10 +02:00
input_activity_map . clear ( ) ;
input_activity_map_get . clear ( ) ;
2018-08-20 18:38:18 +02:00
if ( root . is_valid ( ) ) {
2018-08-21 21:28:06 +02:00
_update_properties_for_node ( SceneStringNames : : get_singleton ( ) - > parameters_base_path , root ) ;
2018-08-20 18:38:18 +02:00
}
properties_dirty = false ;
2021-02-10 21:18:45 +01:00
notify_property_list_changed ( ) ;
2018-08-20 18:38:18 +02:00
}
bool AnimationTree : : _set ( const StringName & p_name , const Variant & p_value ) {
if ( properties_dirty ) {
_update_properties ( ) ;
}
if ( property_map . has ( p_name ) ) {
2023-01-12 13:51:03 +01:00
if ( is_inside_tree ( ) & & property_map [ p_name ] . second ) {
return false ; // Prevent to set property by user.
}
property_map [ p_name ] . first = p_value ;
2018-08-20 18:38:18 +02:00
return true ;
}
return false ;
}
bool AnimationTree : : _get ( const StringName & p_name , Variant & r_ret ) const {
if ( properties_dirty ) {
2018-08-21 21:28:06 +02:00
const_cast < AnimationTree * > ( this ) - > _update_properties ( ) ;
2018-08-20 18:38:18 +02:00
}
if ( property_map . has ( p_name ) ) {
2023-01-12 13:51:03 +01:00
r_ret = property_map [ p_name ] . first ;
2018-08-20 18:38:18 +02:00
return true ;
}
return false ;
}
2020-05-14 14:29:06 +02:00
2018-08-20 18:38:18 +02:00
void AnimationTree : : _get_property_list ( List < PropertyInfo > * p_list ) const {
if ( properties_dirty ) {
2018-08-21 21:28:06 +02:00
const_cast < AnimationTree * > ( this ) - > _update_properties ( ) ;
2018-08-20 18:38:18 +02:00
}
2021-07-16 05:45:57 +02:00
for ( const PropertyInfo & E : properties ) {
p_list - > push_back ( E ) ;
2018-08-20 18:38:18 +02:00
}
}
2018-08-21 21:28:06 +02:00
void AnimationTree : : rename_parameter ( const String & p_base , const String & p_new_base ) {
2018-08-20 18:38:18 +02:00
//rename values first
2021-07-16 05:45:57 +02:00
for ( const PropertyInfo & E : properties ) {
if ( E . name . begins_with ( p_base ) ) {
String new_name = E . name . replace_first ( p_base , p_new_base ) ;
property_map [ new_name ] = property_map [ E . name ] ;
2018-08-20 18:38:18 +02:00
}
}
//update tree second
2018-08-21 21:28:06 +02:00
properties_dirty = true ;
2018-08-20 18:38:18 +02:00
_update_properties ( ) ;
}
2021-08-10 00:15:17 +02:00
real_t AnimationTree : : get_connection_activity ( const StringName & p_path , int p_connection ) const {
2018-08-23 21:44:10 +02:00
if ( ! input_activity_map_get . has ( p_path ) ) {
return 0 ;
}
const Vector < Activity > * activity = input_activity_map_get [ p_path ] ;
2018-08-24 16:50:29 +02:00
if ( ! activity | | p_connection < 0 | | p_connection > = activity - > size ( ) ) {
2018-08-23 21:44:10 +02:00
return 0 ;
}
if ( ( * activity ) [ p_connection ] . last_pass ! = process_pass ) {
return 0 ;
}
return ( * activity ) [ p_connection ] . activity ;
}
2018-06-25 23:40:24 +02:00
void AnimationTree : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_active " , " active " ) , & AnimationTree : : set_active ) ;
ClassDB : : bind_method ( D_METHOD ( " is_active " ) , & AnimationTree : : is_active ) ;
2018-06-19 03:10:48 +02:00
2018-06-27 08:00:08 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_tree_root " , " root " ) , & AnimationTree : : set_tree_root ) ;
ClassDB : : bind_method ( D_METHOD ( " get_tree_root " ) , & AnimationTree : : get_tree_root ) ;
2018-06-19 03:10:48 +02:00
2021-02-18 19:52:29 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_process_callback " , " mode " ) , & AnimationTree : : set_process_callback ) ;
ClassDB : : bind_method ( D_METHOD ( " get_process_callback " ) , & AnimationTree : : get_process_callback ) ;
2018-06-19 03:10:48 +02:00
2018-06-25 23:40:24 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_animation_player " , " root " ) , & AnimationTree : : set_animation_player ) ;
ClassDB : : bind_method ( D_METHOD ( " get_animation_player " ) , & AnimationTree : : get_animation_player ) ;
2018-06-19 03:10:48 +02:00
2022-04-20 12:36:54 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_advance_expression_base_node " , " node " ) , & AnimationTree : : set_advance_expression_base_node ) ;
ClassDB : : bind_method ( D_METHOD ( " get_advance_expression_base_node " ) , & AnimationTree : : get_advance_expression_base_node ) ;
2018-06-27 00:05:11 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_root_motion_track " , " path " ) , & AnimationTree : : set_root_motion_track ) ;
ClassDB : : bind_method ( D_METHOD ( " get_root_motion_track " ) , & AnimationTree : : get_root_motion_track ) ;
2023-01-27 19:25:49 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_audio_max_polyphony " , " max_polyphony " ) , & AnimationTree : : set_audio_max_polyphony ) ;
ClassDB : : bind_method ( D_METHOD ( " get_audio_max_polyphony " ) , & AnimationTree : : get_audio_max_polyphony ) ;
2022-11-26 08:04:15 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_root_motion_position " ) , & AnimationTree : : get_root_motion_position ) ;
ClassDB : : bind_method ( D_METHOD ( " get_root_motion_rotation " ) , & AnimationTree : : get_root_motion_rotation ) ;
ClassDB : : bind_method ( D_METHOD ( " get_root_motion_scale " ) , & AnimationTree : : get_root_motion_scale ) ;
2018-06-29 14:13:20 +02:00
2018-08-20 18:38:18 +02:00
ClassDB : : bind_method ( D_METHOD ( " _update_properties " ) , & AnimationTree : : _update_properties ) ;
2018-08-21 21:28:06 +02:00
ClassDB : : bind_method ( D_METHOD ( " rename_parameter " , " old_name " , " new_name " ) , & AnimationTree : : rename_parameter ) ;
2018-08-20 18:38:18 +02:00
2018-08-02 09:22:24 +02:00
ClassDB : : bind_method ( D_METHOD ( " advance " , " delta " ) , & AnimationTree : : advance ) ;
2023-01-19 16:43:37 +01:00
GDVIRTUAL_BIND ( _post_process_key_value , " animation " , " track " , " value " , " object " , " object_idx " ) ;
2018-08-20 18:38:18 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " tree_root " , PROPERTY_HINT_RESOURCE_TYPE , " AnimationRootNode " ) , " set_tree_root " , " get_tree_root " ) ;
2018-06-21 14:47:07 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " anim_player " , PROPERTY_HINT_NODE_PATH_VALID_TYPES , " AnimationPlayer " ) , " set_animation_player " , " get_animation_player " ) ;
2022-04-20 12:36:54 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " advance_expression_base_node " , PROPERTY_HINT_NODE_PATH_VALID_TYPES , " Node " ) , " set_advance_expression_base_node " , " get_advance_expression_base_node " ) ;
2018-06-19 03:10:48 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " active " ) , " set_active " , " is_active " ) ;
2021-02-18 19:52:29 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " process_callback " , PROPERTY_HINT_ENUM , " Physics,Idle,Manual " ) , " set_process_callback " , " get_process_callback " ) ;
2023-01-27 19:25:49 +01:00
ADD_GROUP ( " Audio " , " audio_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " audio_max_polyphony " , PROPERTY_HINT_RANGE , " 1,127,1 " ) , " set_audio_max_polyphony " , " get_audio_max_polyphony " ) ;
2018-06-27 08:36:26 +02:00
ADD_GROUP ( " Root Motion " , " root_motion_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " root_motion_track " ) , " set_root_motion_track " , " get_root_motion_track " ) ;
2018-06-21 14:47:07 +02:00
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_PHYSICS ) ;
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_IDLE ) ;
2018-08-02 09:22:24 +02:00
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_MANUAL ) ;
2022-09-01 16:00:55 +02:00
ADD_SIGNAL ( MethodInfo ( " animation_player_changed " ) ) ;
2022-12-19 01:10:15 +01:00
// Signals from AnimationNodes.
ADD_SIGNAL ( MethodInfo ( " animation_started " , PropertyInfo ( Variant : : STRING_NAME , " anim_name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " animation_finished " , PropertyInfo ( Variant : : STRING_NAME , " anim_name " ) ) ) ;
2018-06-19 03:10:48 +02:00
}
2018-06-25 23:40:24 +02:00
AnimationTree : : AnimationTree ( ) {
2018-06-19 03:10:48 +02:00
}
2018-06-25 23:40:24 +02:00
AnimationTree : : ~ AnimationTree ( ) {
2018-06-19 03:10:48 +02:00
}