2018-08-29 20:38:13 +00:00
/*************************************************************************/
/* animation_tree.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 20:27:34 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2018-08-29 20:38:13 +00:00
/* */
/* 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-06-25 21:40:24 +00:00
# include "animation_tree.h"
2018-09-11 16:13:45 +00:00
2018-06-19 01:10:48 +00:00
# include "animation_blend_tree.h"
2020-11-07 22:33:38 +00:00
# include "core/config/engine.h"
2021-10-15 13:25:00 +00:00
# include "scene/resources/animation.h"
2018-06-19 01:10:48 +00:00
# include "scene/scene_string_names.h"
# include "servers/audio/audio_stream.h"
2018-08-20 16:38:18 +00:00
void AnimationNode : : get_parameter_list ( List < PropertyInfo > * r_list ) const {
2021-08-22 01:52:44 +00:00
Array parameters ;
if ( GDVIRTUAL_CALL ( _get_parameter_list , parameters ) ) {
2018-11-25 19:56:49 +00:00
for ( int i = 0 ; i < parameters . size ( ) ; i + + ) {
Dictionary d = parameters [ i ] ;
2020-12-15 12:04:21 +00:00
ERR_CONTINUE ( d . is_empty ( ) ) ;
2018-11-25 19:56:49 +00:00
r_list - > push_back ( PropertyInfo : : from_dict ( d ) ) ;
}
}
2018-08-20 16:38:18 +00:00
}
Variant AnimationNode : : get_parameter_default_value ( const StringName & p_parameter ) const {
2021-08-22 01:52:44 +00:00
Variant ret ;
if ( GDVIRTUAL_CALL ( _get_parameter_default_value , p_parameter , ret ) ) {
return ret ;
2018-11-25 19:56:49 +00:00
}
2018-08-20 16:38:18 +00:00
return Variant ( ) ;
}
2018-08-21 19:28:06 +00:00
void AnimationNode : : set_parameter ( const StringName & p_name , const Variant & p_value ) {
2018-08-20 16:38:18 +00: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 ] ;
2018-08-21 19:28:06 +00:00
state - > tree - > property_map [ path ] = p_value ;
2018-08-20 16:38:18 +00:00
}
2018-08-21 19:28:06 +00: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 16:38:18 +00:00
StringName path = state - > tree - > property_parent_map [ base_path ] [ p_name ] ;
return state - > tree - > property_map [ path ] ;
}
void AnimationNode : : get_child_nodes ( List < ChildNode > * r_child_nodes ) {
2021-08-22 01:52:44 +00:00
Dictionary cn ;
if ( GDVIRTUAL_CALL ( _get_child_nodes , cn ) ) {
2018-11-25 19:56:49 +00:00
List < Variant > keys ;
cn . get_key_list ( & keys ) ;
2021-07-24 13:46:25 +00:00
for ( const Variant & E : keys ) {
2018-11-25 19:56:49 +00:00
ChildNode child ;
2021-07-16 03:45:57 +00:00
child . name = E ;
child . node = cn [ E ] ;
2018-11-25 19:56:49 +00:00
r_child_nodes - > push_back ( child ) ;
}
}
2018-08-20 16:38:18 +00:00
}
2022-01-28 20:07:30 +00:00
void AnimationNode : : blend_animation ( const StringName & p_animation , double p_time , double p_delta , bool p_seeked , real_t p_blend , int p_pingponged ) {
2018-06-19 01:10:48 +00: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 19:28:06 +00:00
AnimationNodeBlendTree * btree = Object : : cast_to < AnimationNodeBlendTree > ( parent ) ;
2018-08-20 16:38:18 +00:00
if ( btree ) {
2018-06-21 18:45:44 +00:00
String name = btree - > get_node_name ( Ref < AnimationNodeAnimation > ( this ) ) ;
make_invalid ( vformat ( RTR ( " In node '%s', invalid animation: '%s'. " ) , name , p_animation ) ) ;
} else {
make_invalid ( vformat ( RTR ( " Invalid animation: '%s'. " ) , p_animation ) ) ;
}
2018-06-19 01:10:48 +00: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 ;
2021-10-15 13:25:00 +00:00
anim_state . pingponged = p_pingponged ;
2018-06-19 01:10:48 +00:00
state - > animation_states . push_back ( anim_state ) ;
}
2022-01-28 20:07:30 +00:00
double AnimationNode : : _pre_process ( const StringName & p_base_path , AnimationNode * p_parent , State * p_state , double p_time , bool p_seek , const Vector < StringName > & p_connections ) {
2018-08-20 16:38:18 +00:00
base_path = p_base_path ;
parent = p_parent ;
2018-08-21 19:28:06 +00:00
connections = p_connections ;
2018-06-19 01:10:48 +00:00
state = p_state ;
2018-08-20 16:38:18 +00:00
2022-01-28 20:07:30 +00:00
double t = process ( p_time , p_seek ) ;
2018-08-20 16:38:18 +00:00
2020-04-01 23:20:12 +00:00
state = nullptr ;
parent = nullptr ;
2018-08-20 16:38:18 +00:00
base_path = StringName ( ) ;
connections . clear ( ) ;
2018-06-19 01:10:48 +00:00
return t ;
}
void AnimationNode : : make_invalid ( const String & p_reason ) {
ERR_FAIL_COND ( ! state ) ;
state - > valid = false ;
2021-12-09 09:42:46 +00:00
if ( ! state - > invalid_reasons . is_empty ( ) ) {
2018-06-19 01:10:48 +00:00
state - > invalid_reasons + = " \n " ;
}
2021-07-28 17:40:45 +00:00
state - > invalid_reasons + = String : : utf8 ( " • " ) + p_reason ;
2018-06-19 01:10:48 +00:00
}
2022-01-28 20:07:30 +00:00
double AnimationNode : : blend_input ( int p_input , double p_time , bool p_seek , real_t p_blend , FilterAction p_filter , bool p_optimize ) {
2018-06-19 01:10:48 +00:00
ERR_FAIL_INDEX_V ( p_input , inputs . size ( ) , 0 ) ;
ERR_FAIL_COND_V ( ! state , 0 ) ;
2018-08-21 19:28:06 +00:00
AnimationNodeBlendTree * blend_tree = Object : : cast_to < AnimationNodeBlendTree > ( parent ) ;
ERR_FAIL_COND_V ( ! blend_tree , 0 ) ;
2018-06-19 01:10:48 +00:00
2018-08-20 16:38:18 +00:00
StringName node_name = connections [ p_input ] ;
2018-06-19 01:10:48 +00:00
2018-08-20 16:38:18 +00:00
if ( ! blend_tree - > has_node ( node_name ) ) {
String name = blend_tree - > get_node_name ( Ref < AnimationNode > ( this ) ) ;
2018-06-19 01:10:48 +00:00
make_invalid ( vformat ( RTR ( " Nothing connected to input '%s' of node '%s'. " ) , get_input_name ( p_input ) , name ) ) ;
return 0 ;
}
2018-08-20 16:38:18 +00:00
Ref < AnimationNode > node = blend_tree - > get_node ( node_name ) ;
2018-06-19 01:10:48 +00:00
2018-08-20 16:38:18 +00:00
//inputs.write[p_input].last_pass = state->last_pass;
2021-08-09 22:15:17 +00:00
real_t activity = 0.0 ;
2022-01-28 20:07:30 +00:00
double ret = _blend_node ( node_name , blend_tree - > get_node_connection_array ( node_name ) , nullptr , node , p_time , p_seek , p_blend , p_filter , p_optimize , & activity ) ;
2018-08-23 19:44:10 +00:00
Vector < AnimationTree : : Activity > * activity_ptr = state - > tree - > input_activity_map . getptr ( base_path ) ;
2018-08-24 14:50:29 +00:00
if ( activity_ptr & & p_input < activity_ptr - > size ( ) ) {
2018-08-23 19:44:10 +00:00
activity_ptr - > write [ p_input ] . last_pass = state - > last_pass ;
activity_ptr - > write [ p_input ] . activity = activity ;
}
return ret ;
2018-06-19 01:10:48 +00:00
}
2022-01-28 20:07:30 +00:00
double AnimationNode : : blend_node ( const StringName & p_sub_path , Ref < AnimationNode > p_node , double p_time , bool p_seek , real_t p_blend , FilterAction p_filter , bool p_optimize ) {
2018-08-21 19:28:06 +00:00
return _blend_node ( p_sub_path , Vector < StringName > ( ) , this , p_node , p_time , p_seek , p_blend , p_filter , p_optimize ) ;
2018-06-19 01:10:48 +00:00
}
2022-01-28 20:07:30 +00: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 , real_t p_blend , FilterAction p_filter , bool p_optimize , real_t * r_max ) {
2018-06-19 01:10:48 +00: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-09 22:15:17 +00:00
real_t * blendw = p_node - > blends . ptrw ( ) ;
const real_t * blendr = blends . ptr ( ) ;
2018-06-19 01:10:48 +00: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
}
2020-04-01 23:20:12 +00:00
const NodePath * K = nullptr ;
2018-06-19 01:10:48 +00:00
while ( ( K = filter . next ( K ) ) ) {
if ( ! state - > track_map . has ( * K ) ) {
continue ;
}
int idx = state - > track_map [ * K ] ;
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 01:38:39 +00:00
//values filtered pass, the rest don't
2018-06-19 01:10:48 +00:00
for ( int i = 0 ; i < blend_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( blendw [ i ] = = 0 ) { //not filtered, does not pass
2018-06-19 01:10:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
blendw [ i ] = blendr [ i ] * p_blend ;
if ( blendw [ i ] > CMP_EPSILON ) {
any_valid = true ;
}
}
} break ;
case FILTER_STOP : {
2018-09-13 01:38:39 +00:00
//values filtered don't pass, the rest are blended
2018-06-19 01:10:48 +00:00
for ( int i = 0 ; i < blend_count ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( blendw [ i ] > 0 ) { //filtered, does not pass
2018-06-19 01:10:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
blendw [ i ] = blendr [ i ] * p_blend ;
if ( blendw [ i ] > CMP_EPSILON ) {
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
}
if ( blendw [ i ] > CMP_EPSILON ) {
any_valid = true ;
}
}
} break ;
}
} else {
for ( int i = 0 ; i < blend_count ; i + + ) {
//regular blend
blendw [ i ] = blendr [ i ] * p_blend ;
if ( blendw [ i ] > CMP_EPSILON ) {
any_valid = true ;
}
}
}
if ( r_max ) {
* r_max = 0 ;
for ( int i = 0 ; i < blend_count ; i + + ) {
* r_max = MAX ( * r_max , blendw [ i ] ) ;
}
}
2020-05-14 14:41:43 +00:00
if ( ! p_seek & & p_optimize & & ! any_valid ) { //pointless to go on, all are zero
2018-06-19 01:10:48 +00:00
return 0 ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
2018-08-20 16:38:18 +00:00
String new_path ;
AnimationNode * new_parent ;
//this is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations
if ( p_new_parent ) {
new_parent = p_new_parent ;
2018-08-21 19:28:06 +00:00
new_path = String ( base_path ) + String ( p_subpath ) + " / " ;
2018-08-20 16:38:18 +00:00
} else {
2018-08-21 19:28:06 +00:00
ERR_FAIL_COND_V ( ! parent , 0 ) ;
2018-08-20 16:38:18 +00:00
new_parent = parent ;
2018-08-21 19:28:06 +00:00
new_path = String ( parent - > base_path ) + String ( p_subpath ) + " / " ;
2018-08-20 16:38:18 +00:00
}
2018-08-21 19:28:06 +00:00
return p_node - > _pre_process ( new_path , new_parent , state , p_time , p_seek , p_connections ) ;
2018-06-19 01:10:48 +00:00
}
int AnimationNode : : get_input_count ( ) const {
return inputs . size ( ) ;
}
2020-05-14 12:29:06 +00:00
2018-06-19 01:10:48 +00: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 {
2021-08-22 01:52:44 +00:00
String ret ;
if ( GDVIRTUAL_CALL ( _get_caption , ret ) ) {
return ret ;
2018-06-27 06:00:08 +00:00
}
2018-06-19 01:10:48 +00:00
return " Node " ;
}
void AnimationNode : : add_input ( const String & p_name ) {
2018-09-13 01:38:39 +00:00
//root nodes can't add inputs
2020-04-01 23:20:12 +00:00
ERR_FAIL_COND ( Object : : cast_to < AnimationRootNode > ( this ) ! = nullptr ) ;
2018-06-19 01:10:48 +00:00
Input input ;
2022-02-03 16:03:38 +00:00
ERR_FAIL_COND ( p_name . contains ( " . " ) | | p_name . contains ( " / " ) ) ;
2018-06-19 01:10:48 +00: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 16:03:38 +00:00
ERR_FAIL_COND ( p_name . contains ( " . " ) | | p_name . contains ( " / " ) ) ;
2018-07-25 01:11:03 +00:00
inputs . write [ p_input ] . name = p_name ;
2018-06-19 01:10:48 +00:00
emit_changed ( ) ;
}
void AnimationNode : : remove_input ( int p_index ) {
ERR_FAIL_INDEX ( p_index , inputs . size ( ) ) ;
2021-07-03 22:17:03 +00:00
inputs . remove_at ( p_index ) ;
2018-06-19 01:10:48 +00:00
emit_changed ( ) ;
}
2021-05-21 06:42:37 +00:00
double AnimationNode : : process ( double p_time , bool p_seek ) {
2021-08-22 01:52:44 +00:00
double ret ;
if ( GDVIRTUAL_CALL ( _process , p_time , p_seek , ret ) ) {
return ret ;
2018-06-19 01:10:48 +00:00
}
return 0 ;
}
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 {
2021-08-22 01:52:44 +00:00
bool ret ;
if ( GDVIRTUAL_CALL ( _has_filter , ret ) ) {
return ret ;
2021-05-15 21:48:59 +00:00
}
2018-06-19 01:10:48 +00:00
return false ;
}
Array AnimationNode : : _get_filters ( ) const {
Array paths ;
2020-04-01 23:20:12 +00:00
const NodePath * K = nullptr ;
2018-06-19 01:10:48 +00:00
while ( ( K = filter . next ( K ) ) ) {
paths . push_back ( String ( * K ) ) ; //use strings, so sorting is possible
}
paths . sort ( ) ; //done so every time the scene is saved, it does not change
return paths ;
}
2020-05-14 12:29:06 +00:00
2018-06-19 01:10:48 +00: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 ) ;
}
}
void AnimationNode : : _validate_property ( PropertyInfo & property ) const {
if ( ! has_filter ( ) & & ( property . name = = " filter_enabled " | | property . name = = " filters " ) ) {
2021-07-01 01:24:34 +00:00
property . usage = PROPERTY_USAGE_NONE ;
2018-06-19 01:10:48 +00:00
}
}
2018-08-20 16:38:18 +00:00
Ref < AnimationNode > AnimationNode : : get_child_by_name ( const StringName & p_name ) {
2021-08-22 01:52:44 +00:00
Ref < AnimationNode > ret ;
if ( GDVIRTUAL_CALL ( _get_child_by_name , p_name , ret ) ) {
return ret ;
2018-11-25 19:56:49 +00:00
}
2018-08-20 16:38:18 +00:00
return Ref < AnimationNode > ( ) ;
}
2018-06-19 01:10:48 +00: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 ) ;
2021-10-15 13:25:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " blend_animation " , " animation " , " time " , " delta " , " seeked " , " blend " , " pingponged " ) , & AnimationNode : : blend_animation , DEFVAL ( 0 ) ) ;
2018-08-21 19:28:06 +00:00
ClassDB : : bind_method ( D_METHOD ( " blend_node " , " name " , " node " , " time " , " seek " , " blend " , " filter " , " optimize " ) , & AnimationNode : : blend_node , DEFVAL ( FILTER_IGNORE ) , DEFVAL ( true ) ) ;
2018-06-19 01:10:48 +00:00
ClassDB : : bind_method ( D_METHOD ( " blend_input " , " input_index " , " time " , " seek " , " blend " , " filter " , " optimize " ) , & AnimationNode : : blend_input , DEFVAL ( FILTER_IGNORE ) , DEFVAL ( true ) ) ;
2018-08-21 19:28:06 +00: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 06:00:08 +00:00
2021-11-03 22:06:17 +00: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 01:10:48 +00:00
2021-08-22 01:52:44 +00: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 " ) ;
GDVIRTUAL_BIND ( _process , " time " , " seek " ) ;
GDVIRTUAL_BIND ( _get_caption ) ;
GDVIRTUAL_BIND ( _has_filter ) ;
2018-06-19 01:10:48 +00:00
2018-06-21 21:08:11 +00:00
ADD_SIGNAL ( MethodInfo ( " removed_from_graph " ) ) ;
2018-08-20 16:38:18 +00:00
ADD_SIGNAL ( MethodInfo ( " tree_changed " ) ) ;
2018-06-19 01:10:48 +00: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 06:00:08 +00:00
void AnimationTree : : set_tree_root ( const Ref < AnimationNode > & p_root ) {
2018-06-19 01:10:48 +00:00
if ( root . is_valid ( ) ) {
2020-02-21 17:28:45 +00:00
root - > disconnect ( " tree_changed " , callable_mp ( this , & AnimationTree : : _tree_changed ) ) ;
2018-06-19 01:10:48 +00:00
}
2018-08-20 16:38:18 +00:00
2018-06-19 01:10:48 +00:00
root = p_root ;
if ( root . is_valid ( ) ) {
2020-02-21 17:28:45 +00:00
root - > connect ( " tree_changed " , callable_mp ( this , & AnimationTree : : _tree_changed ) ) ;
2018-06-19 01:10:48 +00:00
}
2018-08-21 19:28:06 +00:00
properties_dirty = true ;
2018-08-20 16:38:18 +00:00
2020-10-29 10:01:28 +00:00
update_configuration_warnings ( ) ;
2018-06-19 01:10:48 +00:00
}
2018-06-27 06:00:08 +00:00
Ref < AnimationNode > AnimationTree : : get_tree_root ( ) const {
2018-06-19 01:10:48 +00:00
return root ;
}
2018-06-25 21:40:24 +00:00
void AnimationTree : : set_active ( bool p_active ) {
2020-05-14 14:41:43 +00:00
if ( active = = p_active ) {
2018-06-19 01:10:48 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
active = p_active ;
started = active ;
2021-02-18 18:52:29 +00:00
if ( process_callback = = ANIMATION_PROCESS_IDLE ) {
2018-06-19 01:10:48 +00:00
set_process_internal ( active ) ;
} else {
set_physics_process_internal ( active ) ;
}
if ( ! active & & is_inside_tree ( ) ) {
for ( Set < TrackCache * > : : Element * E = playing_caches . front ( ) ; E ; E = E - > next ( ) ) {
if ( ObjectDB : : get_instance ( E - > get ( ) - > object_id ) ) {
E - > get ( ) - > object - > call ( " stop " ) ;
}
}
playing_caches . clear ( ) ;
}
}
2018-06-25 21:40:24 +00:00
bool AnimationTree : : is_active ( ) const {
2018-06-19 01:10:48 +00:00
return active ;
}
2021-02-18 18:52:29 +00:00
void AnimationTree : : set_process_callback ( AnimationProcessCallback p_mode ) {
if ( process_callback = = p_mode ) {
2018-06-19 01:10:48 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
bool was_active = is_active ( ) ;
if ( was_active ) {
set_active ( false ) ;
}
2021-02-18 18:52:29 +00:00
process_callback = p_mode ;
2018-06-19 01:10:48 +00:00
if ( was_active ) {
set_active ( true ) ;
}
}
2021-02-18 18:52:29 +00:00
AnimationTree : : AnimationProcessCallback AnimationTree : : get_process_callback ( ) const {
return process_callback ;
2018-06-19 01:10:48 +00:00
}
2018-06-25 21:40:24 +00:00
void AnimationTree : : _node_removed ( Node * p_node ) {
2018-06-19 01:10:48 +00:00
cache_valid = false ;
}
2018-06-25 21:40:24 +00:00
bool AnimationTree : : _update_caches ( AnimationPlayer * player ) {
2018-06-19 01:10:48 +00:00
setup_pass + + ;
if ( ! player - > has_node ( player - > get_root ( ) ) ) {
2018-06-25 21:40:24 +00:00
ERR_PRINT ( " AnimationTree: AnimationPlayer root is invalid. " ) ;
2018-06-19 01:10:48 +00:00
set_active ( false ) ;
return false ;
}
Node * parent = player - > get_node ( player - > get_root ( ) ) ;
List < StringName > sname ;
player - > get_animation_list ( & sname ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & E : sname ) {
2021-07-16 03:45:57 +00:00
Ref < Animation > anim = player - > get_animation ( E ) ;
2018-06-19 01:10:48 +00: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-11 22:20:58 +00: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-01 23:20:12 +00:00
TrackCache * track = nullptr ;
2018-06-19 01:10:48 +00: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-11 22:20:58 +00:00
if ( track & & ( track - > type ! = track_cache_type | | ObjectDB : : get_instance ( track - > object_id ) = = nullptr ) ) {
2018-06-19 01:10:48 +00:00
playing_caches . erase ( track ) ;
memdelete ( track ) ;
track_cache . erase ( path ) ;
2020-04-01 23:20:12 +00:00
track = nullptr ;
2018-06-19 01:10:48 +00:00
}
if ( ! track ) {
RES resource ;
Vector < StringName > leftover_path ;
Node * child = parent - > get_node_and_resource ( path , resource , leftover_path ) ;
if ( ! child ) {
2021-07-16 03:45:57 +00:00
ERR_PRINT ( " AnimationTree: ' " + String ( E ) + " ', couldn't resolve track: ' " + String ( path ) + " ' " ) ;
2018-06-19 01:10:48 +00:00
continue ;
}
2020-02-21 17:28:45 +00:00
if ( ! child - > is_connected ( " tree_exited " , callable_mp ( this , & AnimationTree : : _node_removed ) ) ) {
child - > connect ( " tree_exited " , callable_mp ( this , & AnimationTree : : _node_removed ) , varray ( child ) ) ;
2018-06-19 01:10:48 +00: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 ;
}
track_value - > subpath = leftover_path ;
track_value - > object_id = track_value - > object - > get_instance_id ( ) ;
track = track_value ;
} 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-11 22:20:58 +00:00
case Animation : : TYPE_POSITION_3D :
case Animation : : TYPE_ROTATION_3D :
case Animation : : TYPE_SCALE_3D : {
2021-03-17 23:35:42 +00:00
# ifndef _3D_DISABLED
Node3D * node_3d = Object : : cast_to < Node3D > ( child ) ;
2018-06-19 01:10:48 +00:00
2021-03-17 23:35:42 +00:00
if ( ! node_3d ) {
2021-07-16 03:45:57 +00:00
ERR_PRINT ( " AnimationTree: ' " + String ( E ) + " ', transform track does not point to Node3D: ' " + String ( path ) + " ' " ) ;
2018-06-19 01:10:48 +00: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-11 22:20:58 +00:00
track_xform - > type = Animation : : TYPE_POSITION_3D ;
2018-06-19 01:10:48 +00:00
2021-03-17 23:35:42 +00:00
track_xform - > node_3d = node_3d ;
2020-04-01 23:20:12 +00:00
track_xform - > skeleton = nullptr ;
2018-06-19 01:10:48 +00:00
track_xform - > bone_idx = - 1 ;
2021-03-17 23:35:42 +00:00
if ( path . get_subname_count ( ) = = 1 & & Object : : cast_to < Skeleton3D > ( node_3d ) ) {
Skeleton3D * sk = Object : : cast_to < Skeleton3D > ( node_3d ) ;
2020-08-21 04:56:04 +00:00
track_xform - > skeleton = sk ;
2018-06-19 01:10:48 +00:00
int bone_idx = sk - > find_bone ( path . get_subname ( 0 ) ) ;
2019-09-18 22:46:32 +00:00
if ( bone_idx ! = - 1 ) {
2018-06-19 01:10:48 +00:00
track_xform - > bone_idx = bone_idx ;
}
}
2021-03-17 23:35:42 +00:00
track_xform - > object = node_3d ;
2018-06-19 01:10:48 +00: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-11 22:20:58 +00: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 : {
}
}
2021-03-17 23:35:42 +00:00
# endif // _3D_DISABLED
2021-10-15 22:04:35 +00: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 ;
# endif
2018-06-19 01:10:48 +00: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 ;
} break ;
case Animation : : TYPE_AUDIO : {
TrackCacheAudio * track_audio = memnew ( TrackCacheAudio ) ;
track_audio - > object = child ;
track_audio - > object_id = track_audio - > object - > get_instance_id ( ) ;
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 12:09:10 +00:00
default : {
2019-01-26 21:35:31 +00:00
ERR_PRINT ( " Animation corrupted (invalid track type) " ) ;
2019-01-25 12:09:10 +00:00
continue ;
}
2018-06-19 01:10:48 +00: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-11 22:20:58 +00: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 : {
}
}
2018-06-19 01:10:48 +00:00
}
track - > setup_pass = setup_pass ;
}
}
List < NodePath > to_delete ;
2020-04-01 23:20:12 +00:00
const NodePath * K = nullptr ;
2018-06-19 01:10:48 +00:00
while ( ( K = track_cache . next ( K ) ) ) {
TrackCache * tc = track_cache [ * K ] ;
if ( tc - > setup_pass ! = setup_pass ) {
to_delete . push_back ( * K ) ;
}
}
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 ( ) ;
2020-04-01 23:20:12 +00:00
K = nullptr ;
2018-06-19 01:10:48 +00:00
int idx = 0 ;
while ( ( K = track_cache . next ( K ) ) ) {
state . track_map [ * K ] = idx ;
idx + + ;
}
state . track_count = idx ;
cache_valid = true ;
return true ;
}
2018-06-25 21:40:24 +00:00
void AnimationTree : : _clear_caches ( ) {
2020-04-01 23:20:12 +00:00
const NodePath * K = nullptr ;
2018-06-19 01:10:48 +00:00
while ( ( K = track_cache . next ( K ) ) ) {
memdelete ( track_cache [ * K ] ) ;
}
playing_caches . clear ( ) ;
track_cache . clear ( ) ;
cache_valid = false ;
}
2022-01-28 20:07:30 +00:00
void AnimationTree : : _process_graph ( double p_delta ) {
2018-08-20 16:38:18 +00:00
_update_properties ( ) ; //if properties need updating, update them
2018-06-19 01:10:48 +00:00
//check all tracks, see if they need modification
2018-08-20 16:38:18 +00:00
2020-10-17 05:08:21 +00:00
root_motion_transform = Transform3D ( ) ;
2018-06-19 01:10:48 +00:00
if ( ! root . is_valid ( ) ) {
2018-06-25 21:40:24 +00:00
ERR_PRINT ( " AnimationTree: root AnimationNode is not set, disabling playback. " ) ;
2018-06-19 01:10:48 +00:00
set_active ( false ) ;
cache_valid = false ;
return ;
}
if ( ! has_node ( animation_player ) ) {
2018-06-25 21:40:24 +00:00
ERR_PRINT ( " AnimationTree: no valid AnimationPlayer path set, disabling playback " ) ;
2018-06-19 01:10:48 +00:00
set_active ( false ) ;
cache_valid = false ;
return ;
}
AnimationPlayer * player = Object : : cast_to < AnimationPlayer > ( get_node ( animation_player ) ) ;
2020-02-12 17:24:06 +00:00
ObjectID current_animation_player ;
2018-08-24 14:41:04 +00:00
if ( player ) {
2018-08-24 14:50:29 +00:00
current_animation_player = player - > get_instance_id ( ) ;
2018-08-24 14:41:04 +00:00
}
if ( last_animation_player ! = current_animation_player ) {
2020-02-12 17:24:06 +00:00
if ( last_animation_player . is_valid ( ) ) {
2018-08-24 14:41:04 +00:00
Object * old_player = ObjectDB : : get_instance ( last_animation_player ) ;
if ( old_player ) {
2020-02-21 17:28:45 +00:00
old_player - > disconnect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
2018-08-24 14:41:04 +00:00
}
}
if ( player ) {
2020-02-21 17:28:45 +00:00
player - > connect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
2018-08-24 14:41:04 +00:00
}
last_animation_player = current_animation_player ;
}
2018-06-19 01:10:48 +00:00
if ( ! player ) {
2018-06-25 21:40:24 +00:00
ERR_PRINT ( " AnimationTree: path points to a node not an AnimationPlayer, disabling playback " ) ;
2018-06-19 01:10:48 +00: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 . valid = true ;
state . player = player ;
state . last_pass = process_pass ;
2018-08-20 16:38:18 +00:00
state . tree = this ;
2018-06-19 01:10:48 +00:00
// root source blends
root - > blends . resize ( state . track_count ) ;
2021-08-09 22:15:17 +00:00
real_t * src_blendsw = root - > blends . ptrw ( ) ;
2018-06-19 01:10:48 +00: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
2020-04-01 23:20:12 +00:00
root - > _pre_process ( SceneStringNames : : get_singleton ( ) - > parameters_base_path , nullptr , & state , 0 , true , Vector < StringName > ( ) ) ;
2018-06-19 01:10:48 +00:00
started = false ;
}
2020-04-01 23:20:12 +00:00
root - > _pre_process ( SceneStringNames : : get_singleton ( ) - > parameters_base_path , nullptr , & state , p_delta , false , Vector < StringName > ( ) ) ;
2018-06-19 01:10:48 +00:00
}
if ( ! state . valid ) {
return ; //state is not valid. do nothing.
}
//apply value/transform/bezier blends to track caches and execute method/audio/animation tracks
{
bool can_call = is_inside_tree ( ) & & ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
2021-07-24 13:46:25 +00:00
for ( const AnimationNode : : AnimationState & as : state . animation_states ) {
2018-06-19 01:10:48 +00:00
Ref < Animation > a = as . animation ;
2021-05-21 06:42:37 +00:00
double time = as . time ;
double delta = as . delta ;
2021-08-09 22:15:17 +00:00
real_t weight = as . blend ;
2018-06-19 01:10:48 +00:00
bool seeked = as . seeked ;
2021-10-15 13:25:00 +00:00
int pingponged = as . pingponged ;
2021-11-12 15:30:34 +00:00
# ifndef _3D_DISABLED
2021-10-15 13:25:00 +00:00
bool backward = signbit ( delta ) ;
2021-11-12 15:30:34 +00:00
# endif // _3D_DISABLED
2018-06-19 01:10:48 +00:00
for ( int i = 0 ; i < a - > get_track_count ( ) ; i + + ) {
NodePath path = a - > track_get_path ( i ) ;
2019-01-25 12:09:10 +00:00
ERR_CONTINUE ( ! track_cache . has ( path ) ) ;
2018-06-19 01:10:48 +00: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-11 22:20:58 +00: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 01:10:48 +00:00
}
2018-06-26 22:05:11 +00:00
track - > root_motion = root_motion_track = = path ;
2018-06-19 01:10:48 +00: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 ) ;
2021-08-09 22:15:17 +00:00
real_t blend = ( * as . track_blends ) [ blend_idx ] * weight ;
2018-06-19 01:10:48 +00:00
2020-05-14 14:41:43 +00:00
if ( blend < CMP_EPSILON ) {
2018-06-19 01:10:48 +00:00
continue ; //nothing to blend
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00: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-11 22:20:58 +00:00
switch ( ttype ) {
case Animation : : TYPE_POSITION_3D : {
2021-03-17 23:35:42 +00:00
# ifndef _3D_DISABLED
2018-06-19 01:10:48 +00:00
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( 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-11 22:20:58 +00:00
if ( t - > process_pass ! = process_pass ) {
t - > process_pass = process_pass ;
t - > loc = Vector3 ( ) ;
t - > rot = Quaternion ( ) ;
t - > rot_blend_accum = 0 ;
t - > scale = Vector3 ( 1 , 1 , 1 ) ;
}
2019-03-31 20:07:09 +00:00
if ( track - > root_motion ) {
2021-10-15 13:25:00 +00: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-11 22:20:58 +00:00
}
2019-03-31 20:07:09 +00:00
}
2018-06-26 22:05:11 +00: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-11 22:20:58 +00:00
Vector3 loc [ 2 ] ;
2021-10-15 13:25:00 +00:00
if ( ! backward ) {
if ( prev_time > time ) {
Error err = a - > position_track_interpolate ( i , prev_time , & loc [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
a - > position_track_interpolate ( i , ( double ) a - > get_length ( ) , & loc [ 1 ] ) ;
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 ;
}
a - > position_track_interpolate ( i , 0 , & loc [ 1 ] ) ;
t - > loc + = ( loc [ 1 ] - loc [ 0 ] ) * blend ;
prev_time = 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-11 22:20:58 +00:00
}
}
Error err = a - > position_track_interpolate ( i , prev_time , & loc [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
a - > position_track_interpolate ( i , time , & loc [ 1 ] ) ;
t - > loc + = ( loc [ 1 ] - loc [ 0 ] ) * blend ;
2021-10-15 13:25:00 +00: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-11 22:20:58 +00:00
} else {
Vector3 loc ;
Error err = a - > position_track_interpolate ( i , time , & loc ) ;
if ( err ! = OK ) {
continue ;
}
t - > loc = t - > loc . lerp ( loc , blend ) ;
}
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_ROTATION_3D : {
# ifndef _3D_DISABLED
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
if ( t - > process_pass ! = process_pass ) {
t - > process_pass = process_pass ;
t - > loc = Vector3 ( ) ;
t - > rot = Quaternion ( ) ;
t - > rot_blend_accum = 0 ;
t - > scale = Vector3 ( 1 , 1 , 1 ) ;
}
if ( track - > root_motion ) {
2021-10-15 13:25:00 +00: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-26 22:05:11 +00:00
}
}
2021-01-20 07:02:02 +00:00
Quaternion rot [ 2 ] ;
2018-06-26 22:05:11 +00:00
2021-10-15 13:25:00 +00:00
if ( ! backward ) {
if ( prev_time > time ) {
Error err = a - > rotation_track_interpolate ( i , prev_time , & rot [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
a - > rotation_track_interpolate ( i , ( double ) a - > get_length ( ) , & rot [ 1 ] ) ;
Quaternion q = Quaternion ( ) . slerp ( rot [ 0 ] . normalized ( ) . inverse ( ) * rot [ 1 ] . normalized ( ) , blend ) . normalized ( ) ;
t - > rot = ( t - > rot * q ) . normalized ( ) ;
prev_time = 0 ;
}
} else {
if ( prev_time < time ) {
Error err = a - > rotation_track_interpolate ( i , prev_time , & rot [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
a - > rotation_track_interpolate ( i , 0 , & rot [ 1 ] ) ;
Quaternion q = Quaternion ( ) . slerp ( rot [ 0 ] . normalized ( ) . inverse ( ) * rot [ 1 ] . normalized ( ) , blend ) . normalized ( ) ;
t - > rot = ( t - > rot * q ) . normalized ( ) ;
prev_time = 0 ;
2018-06-26 22:05:11 +00: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-11 22:20:58 +00:00
Error err = a - > rotation_track_interpolate ( i , prev_time , & rot [ 0 ] ) ;
2018-06-26 22:05:11 +00:00
if ( err ! = OK ) {
continue ;
}
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-11 22:20:58 +00:00
a - > rotation_track_interpolate ( i , time , & rot [ 1 ] ) ;
2021-01-20 07:02:02 +00:00
Quaternion q = Quaternion ( ) . slerp ( rot [ 0 ] . normalized ( ) . inverse ( ) * rot [ 1 ] . normalized ( ) , blend ) . normalized ( ) ;
2018-06-26 22:05:11 +00:00
t - > rot = ( t - > rot * q ) . normalized ( ) ;
2021-10-15 13:25:00 +00:00
prev_time = ! backward ? 0 : ( double ) a - > get_length ( ) ;
2021-10-11 22:27:50 +00:00
2018-06-26 22:05:11 +00:00
} else {
2021-01-20 07:02:02 +00:00
Quaternion rot ;
2018-06-26 22:05:11 +00: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-11 22:20:58 +00:00
Error err = a - > rotation_track_interpolate ( i , time , & rot ) ;
2020-05-14 14:41:43 +00:00
if ( err ! = OK ) {
2018-06-26 22:05:11 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-26 22:05:11 +00:00
2018-06-21 12:47:07 +00:00
if ( t - > rot_blend_accum = = 0 ) {
2018-06-27 19:30:48 +00:00
t - > rot = rot ;
t - > rot_blend_accum = blend ;
} else {
2021-08-09 22:15:17 +00:00
real_t rot_total = t - > rot_blend_accum + blend ;
2018-06-27 19:30:48 +00:00
t - > rot = rot . slerp ( t - > rot , t - > rot_blend_accum / rot_total ) . normalized ( ) ;
t - > rot_blend_accum = rot_total ;
}
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-11 22:20:58 +00:00
}
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_SCALE_3D : {
# ifndef _3D_DISABLED
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
if ( t - > process_pass ! = process_pass ) {
t - > process_pass = process_pass ;
t - > loc = Vector3 ( ) ;
t - > rot = Quaternion ( ) ;
t - > rot_blend_accum = 0 ;
t - > scale = Vector3 ( 1 , 1 , 1 ) ;
}
if ( track - > root_motion ) {
2021-10-15 13:25:00 +00: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-11 22:20:58 +00:00
}
}
Vector3 scale [ 2 ] ;
2021-10-15 13:25:00 +00:00
if ( ! backward ) {
if ( prev_time > time ) {
Error err = a - > scale_track_interpolate ( i , prev_time , & scale [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
a - > scale_track_interpolate ( i , ( double ) a - > get_length ( ) , & scale [ 1 ] ) ;
t - > scale + = ( scale [ 1 ] - scale [ 0 ] ) * blend ;
prev_time = 0 ;
}
} else {
if ( prev_time < time ) {
Error err = a - > scale_track_interpolate ( i , prev_time , & scale [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
a - > scale_track_interpolate ( i , 0 , & scale [ 1 ] ) ;
t - > scale + = ( scale [ 1 ] - scale [ 0 ] ) * blend ;
prev_time = 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-11 22:20:58 +00:00
}
}
Error err = a - > scale_track_interpolate ( i , prev_time , & scale [ 0 ] ) ;
if ( err ! = OK ) {
continue ;
}
a - > scale_track_interpolate ( i , time , & scale [ 1 ] ) ;
t - > scale + = ( scale [ 1 ] - scale [ 0 ] ) * blend ;
2021-10-15 13:25:00 +00: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-11 22:20:58 +00:00
} else {
Vector3 scale ;
Error err = a - > scale_track_interpolate ( i , time , & scale ) ;
if ( err ! = OK ) {
continue ;
}
2020-03-16 09:07:33 +00:00
t - > scale = t - > scale . lerp ( scale , blend ) ;
2018-06-26 22:05:11 +00:00
}
2021-10-15 22:04:35 +00:00
# endif // _3D_DISABLED
} break ;
case Animation : : TYPE_BLEND_SHAPE : {
# ifndef _3D_DISABLED
TrackCacheBlendShape * t = static_cast < TrackCacheBlendShape * > ( track ) ;
if ( t - > process_pass ! = process_pass ) {
t - > process_pass = process_pass ;
t - > value = 0 ;
}
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 ;
}
2021-09-16 06:03:50 +00:00
t - > value = Math : : lerp ( t - > value , value , ( float ) blend ) ;
2021-03-17 23:35:42 +00:00
# endif // _3D_DISABLED
2018-06-19 01:10:48 +00:00
} break ;
case Animation : : TYPE_VALUE : {
TrackCacheValue * t = static_cast < TrackCacheValue * > ( track ) ;
Animation : : UpdateMode update_mode = a - > value_track_get_update_mode ( i ) ;
if ( update_mode = = Animation : : UPDATE_CONTINUOUS | | update_mode = = Animation : : UPDATE_CAPTURE ) { //delta == 0 means seek
Variant value = a - > value_track_interpolate ( i , time ) ;
2020-05-14 14:41:43 +00:00
if ( value = = Variant ( ) ) {
2018-06-19 01:10:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
if ( t - > process_pass ! = process_pass ) {
2019-03-31 20:07:09 +00:00
t - > value = value ;
2018-06-19 01:10:48 +00:00
t - > process_pass = process_pass ;
}
Variant : : interpolate ( t - > value , value , blend , t - > value ) ;
2021-09-10 20:58:17 +00:00
} else {
2018-06-19 01:10:48 +00:00
List < int > indices ;
2021-10-15 13:25:00 +00:00
a - > value_track_get_key_indices ( i , time , delta , & indices , pingponged ) ;
2018-06-19 01:10:48 +00:00
2021-07-16 03:45:57 +00:00
for ( int & F : indices ) {
Variant value = a - > track_get_key_value ( i , F ) ;
2018-06-19 01:10:48 +00:00
t - > object - > set_indexed ( t - > subpath , value ) ;
}
}
} break ;
case Animation : : TYPE_METHOD : {
if ( delta = = 0 ) {
continue ;
}
TrackCacheMethod * t = static_cast < TrackCacheMethod * > ( track ) ;
List < int > indices ;
2021-10-15 13:25:00 +00:00
a - > method_track_get_key_indices ( i , time , delta , & indices , pingponged ) ;
2018-06-19 01:10:48 +00:00
2021-07-16 03:45:57 +00:00
for ( int & F : indices ) {
StringName method = a - > method_track_get_name ( i , F ) ;
Vector < Variant > params = a - > method_track_get_params ( i , F ) ;
2018-06-19 01:10:48 +00:00
int s = params . size ( ) ;
2021-07-06 15:07:16 +00:00
static_assert ( VARIANT_ARG_MAX = = 8 , " This code needs to be updated if VARIANT_ARG_MAX != 8 " ) ;
2018-06-19 01:10:48 +00:00
ERR_CONTINUE ( s > VARIANT_ARG_MAX ) ;
if ( can_call ) {
t - > object - > call_deferred (
method ,
s > = 1 ? params [ 0 ] : Variant ( ) ,
s > = 2 ? params [ 1 ] : Variant ( ) ,
s > = 3 ? params [ 2 ] : Variant ( ) ,
s > = 4 ? params [ 3 ] : Variant ( ) ,
2021-07-06 15:07:16 +00:00
s > = 5 ? params [ 4 ] : Variant ( ) ,
s > = 6 ? params [ 5 ] : Variant ( ) ,
s > = 7 ? params [ 6 ] : Variant ( ) ,
s > = 8 ? params [ 7 ] : Variant ( ) ) ;
2018-06-19 01:10:48 +00:00
}
}
} break ;
case Animation : : TYPE_BEZIER : {
TrackCacheBezier * t = static_cast < TrackCacheBezier * > ( track ) ;
2021-08-09 22:15:17 +00:00
real_t bezier = a - > bezier_track_interpolate ( i , time ) ;
2018-06-19 01:10:48 +00:00
if ( t - > process_pass ! = process_pass ) {
2019-03-31 20:07:09 +00:00
t - > value = bezier ;
2018-06-19 01:10:48 +00:00
t - > process_pass = process_pass ;
}
t - > value = Math : : lerp ( t - > value , bezier , blend ) ;
} break ;
case Animation : : TYPE_AUDIO : {
TrackCacheAudio * t = static_cast < TrackCacheAudio * > ( track ) ;
if ( seeked ) {
2021-03-12 13:35:16 +00:00
//find whatever should be playing
2018-06-19 01:10:48 +00:00
int idx = a - > track_find_key ( i , time ) ;
2020-05-14 14:41:43 +00:00
if ( idx < 0 ) {
2018-06-19 01:10:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
Ref < AudioStream > stream = a - > audio_track_get_key_stream ( i , idx ) ;
if ( ! stream . is_valid ( ) ) {
t - > object - > call ( " stop " ) ;
t - > playing = false ;
playing_caches . erase ( t ) ;
} else {
2022-01-28 20:07:30 +00:00
double start_ofs = a - > audio_track_get_key_start_offset ( i , idx ) ;
2018-06-19 01:10:48 +00:00
start_ofs + = time - a - > track_get_key_time ( i , idx ) ;
2022-01-28 20:07:30 +00:00
double end_ofs = a - > audio_track_get_key_end_offset ( i , idx ) ;
double len = stream - > get_length ( ) ;
2018-06-19 01:10:48 +00:00
if ( start_ofs > len - end_ofs ) {
t - > object - > call ( " stop " ) ;
t - > playing = false ;
playing_caches . erase ( t ) ;
continue ;
}
t - > object - > call ( " set_stream " , stream ) ;
t - > object - > call ( " play " , start_ofs ) ;
t - > playing = true ;
playing_caches . insert ( t ) ;
2021-03-12 13:35:16 +00:00
if ( len & & end_ofs > 0 ) { //force an end at a time
2018-06-19 01:10:48 +00:00
t - > len = len - start_ofs - end_ofs ;
} else {
t - > len = 0 ;
}
t - > start = time ;
}
} else {
//find stuff to play
List < int > to_play ;
2021-10-15 13:25:00 +00:00
a - > track_get_key_indices_in_range ( i , time , delta , & to_play , pingponged ) ;
2018-06-19 01:10:48 +00:00
if ( to_play . size ( ) ) {
int idx = to_play . back ( ) - > get ( ) ;
Ref < AudioStream > stream = a - > audio_track_get_key_stream ( i , idx ) ;
if ( ! stream . is_valid ( ) ) {
t - > object - > call ( " stop " ) ;
t - > playing = false ;
playing_caches . erase ( t ) ;
} else {
2022-01-28 20:07:30 +00:00
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 ( ) ;
2018-06-19 01:10:48 +00:00
t - > object - > call ( " set_stream " , stream ) ;
t - > object - > call ( " play " , start_ofs ) ;
t - > playing = true ;
playing_caches . insert ( t ) ;
2021-03-12 13:35:16 +00:00
if ( len & & end_ofs > 0 ) { //force an end at a time
2018-06-19 01:10:48 +00:00
t - > len = len - start_ofs - end_ofs ;
} else {
t - > len = 0 ;
}
t - > start = time ;
}
} else if ( t - > playing ) {
2021-10-15 13:25:00 +00:00
bool loop = a - > get_loop_mode ( ) ! = Animation : : LoopMode : : LOOP_NONE ;
2018-07-01 20:44:15 +00:00
bool stop = false ;
2021-10-15 13:25:00 +00:00
if ( ! loop ) {
if ( delta > 0 ) {
if ( time < t - > start ) {
stop = true ;
}
} else if ( delta < 0 ) {
if ( time > t - > start ) {
stop = true ;
}
}
2018-07-01 20:44:15 +00:00
} else if ( t - > len > 0 ) {
2022-01-28 20:07:30 +00:00
double len = t - > start > time ? ( a - > get_length ( ) - t - > start ) + time : time - t - > start ;
2018-07-01 20:44:15 +00:00
if ( len > t - > len ) {
2018-06-21 12:47:07 +00:00
stop = true ;
2018-07-01 20:44:15 +00:00
}
}
if ( stop ) {
2018-06-19 01:10:48 +00:00
//time to stop
t - > object - > call ( " stop " ) ;
t - > playing = false ;
playing_caches . erase ( t ) ;
}
}
}
2021-08-09 22:15:17 +00:00
real_t db = Math : : linear2db ( MAX ( blend , 0.00001 ) ) ;
2018-07-01 20:44:15 +00:00
if ( t - > object - > has_method ( " set_unit_db " ) ) {
t - > object - > call ( " set_unit_db " , db ) ;
} else {
t - > object - > call ( " set_volume_db " , db ) ;
}
2018-06-19 01:10:48 +00:00
} break ;
case Animation : : TYPE_ANIMATION : {
TrackCacheAnimation * t = static_cast < TrackCacheAnimation * > ( track ) ;
2019-02-12 20:10:08 +00:00
AnimationPlayer * player2 = Object : : cast_to < AnimationPlayer > ( t - > object ) ;
2018-06-19 01:10:48 +00:00
2020-05-14 14:41:43 +00:00
if ( ! player2 ) {
2018-06-19 01:10:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
2021-10-15 13:25:00 +00:00
if ( seeked ) {
2018-06-19 01:10:48 +00:00
//seek
int idx = a - > track_find_key ( i , time ) ;
2020-05-14 14:41:43 +00:00
if ( idx < 0 ) {
2018-06-19 01:10:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
2021-05-21 06:42:37 +00:00
double pos = a - > track_get_key_time ( i , idx ) ;
2018-06-19 01:10:48 +00:00
StringName anim_name = a - > animation_track_get_key_animation ( i , idx ) ;
2020-05-14 14:41:43 +00:00
if ( String ( anim_name ) = = " [stop] " | | ! player2 - > has_animation ( anim_name ) ) {
2018-06-19 01:10:48 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00:00
2019-02-12 20:10:08 +00:00
Ref < Animation > anim = player2 - > get_animation ( anim_name ) ;
2018-06-19 01:10:48 +00:00
2022-01-28 20:07:30 +00:00
double at_anim_pos = 0.0 ;
2021-10-15 13:25:00 +00:00
switch ( anim - > get_loop_mode ( ) ) {
case Animation : : LoopMode : : LOOP_NONE : {
at_anim_pos = MAX ( ( double ) anim - > get_length ( ) , time - pos ) ; //seek to end
} break ;
case Animation : : LoopMode : : LOOP_LINEAR : {
at_anim_pos = Math : : fposmod ( time - pos , ( double ) anim - > get_length ( ) ) ; //seek to loop
} break ;
case Animation : : LoopMode : : LOOP_PINGPONG : {
at_anim_pos = Math : : pingpong ( time - pos , ( double ) a - > get_length ( ) ) ;
} break ;
default :
break ;
2018-06-19 01:10:48 +00:00
}
2019-02-12 20:10:08 +00:00
if ( player2 - > is_playing ( ) | | seeked ) {
player2 - > play ( anim_name ) ;
player2 - > seek ( at_anim_pos ) ;
2018-06-19 01:10:48 +00:00
t - > playing = true ;
playing_caches . insert ( t ) ;
} else {
2019-02-12 20:10:08 +00:00
player2 - > set_assigned_animation ( anim_name ) ;
player2 - > seek ( at_anim_pos , true ) ;
2018-06-19 01:10:48 +00:00
}
} else {
//find stuff to play
List < int > to_play ;
2021-10-15 13:25:00 +00:00
a - > track_get_key_indices_in_range ( i , time , delta , & to_play , pingponged ) ;
2018-06-19 01:10:48 +00: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 20:10:08 +00:00
if ( String ( anim_name ) = = " [stop] " | | ! player2 - > has_animation ( anim_name ) ) {
2018-06-19 01:10:48 +00:00
if ( playing_caches . has ( t ) ) {
playing_caches . erase ( t ) ;
2019-02-12 20:10:08 +00:00
player2 - > stop ( ) ;
2018-06-19 01:10:48 +00:00
t - > playing = false ;
}
} else {
2019-02-12 20:10:08 +00:00
player2 - > play ( anim_name ) ;
2018-06-19 01:10:48 +00:00
t - > playing = true ;
playing_caches . insert ( t ) ;
}
}
}
} break ;
}
}
}
}
{
// finally, set the tracks
2020-04-01 23:20:12 +00:00
const NodePath * K = nullptr ;
2018-06-19 01:10:48 +00:00
while ( ( K = track_cache . next ( K ) ) ) {
TrackCache * track = track_cache [ * K ] ;
2020-05-14 14:41:43 +00:00
if ( track - > process_pass ! = process_pass ) {
2018-06-19 01:10:48 +00:00
continue ; //not processed, ignore
2020-05-14 14:41:43 +00:00
}
2018-06-19 01:10:48 +00: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-11 22:20:58 +00:00
case Animation : : TYPE_POSITION_3D : {
2021-03-17 23:35:42 +00:00
# ifndef _3D_DISABLED
2018-06-19 01:10:48 +00:00
TrackCacheTransform * t = static_cast < TrackCacheTransform * > ( track ) ;
2018-06-26 22:05:11 +00:00
if ( t - > root_motion ) {
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-11 22:20:58 +00:00
Transform3D xform ;
xform . origin = t - > loc ;
xform . basis . set_quaternion_scale ( t - > rot , t - > scale ) ;
2018-06-26 22:05:11 +00:00
root_motion_transform = xform ;
2018-06-27 06:36:26 +00: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-11 22:20:58 +00: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 01:10:48 +00:00
2020-08-21 04:56:04 +00: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-11 22:20:58 +00: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 01:10:48 +00:00
}
2021-10-15 22:04:35 +00: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-17 23:35:42 +00:00
# endif // _3D_DISABLED
2018-06-19 01:10:48 +00:00
} break ;
case Animation : : TYPE_VALUE : {
TrackCacheValue * t = static_cast < TrackCacheValue * > ( track ) ;
t - > object - > set_indexed ( t - > subpath , t - > value ) ;
} break ;
case Animation : : TYPE_BEZIER : {
TrackCacheBezier * t = static_cast < TrackCacheBezier * > ( track ) ;
t - > object - > set_indexed ( t - > subpath , t - > value ) ;
} break ;
2019-04-09 15:08:36 +00:00
default : {
} //the rest don't matter
2018-06-19 01:10:48 +00:00
}
}
}
}
2021-08-09 22:15:17 +00:00
void AnimationTree : : advance ( real_t p_time ) {
2018-08-02 07:22:24 +00:00
_process_graph ( p_time ) ;
}
2018-06-25 21:40:24 +00:00
void AnimationTree : : _notification ( int p_what ) {
2021-02-18 18:52:29 +00:00
if ( active & & p_what = = NOTIFICATION_INTERNAL_PHYSICS_PROCESS & & process_callback = = ANIMATION_PROCESS_PHYSICS ) {
2018-06-19 01:10:48 +00:00
_process_graph ( get_physics_process_delta_time ( ) ) ;
}
2021-02-18 18:52:29 +00:00
if ( active & & p_what = = NOTIFICATION_INTERNAL_PROCESS & & process_callback = = ANIMATION_PROCESS_IDLE ) {
2018-06-19 01:10:48 +00:00
_process_graph ( get_process_delta_time ( ) ) ;
}
if ( p_what = = NOTIFICATION_EXIT_TREE ) {
_clear_caches ( ) ;
2020-02-12 17:24:06 +00:00
if ( last_animation_player . is_valid ( ) ) {
2019-04-10 13:48:18 +00:00
Object * player = ObjectDB : : get_instance ( last_animation_player ) ;
if ( player ) {
2020-02-21 17:28:45 +00:00
player - > disconnect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
2019-04-10 13:48:18 +00:00
}
}
} else if ( p_what = = NOTIFICATION_ENTER_TREE ) {
2020-02-12 17:24:06 +00:00
if ( last_animation_player . is_valid ( ) ) {
2019-04-10 13:48:18 +00:00
Object * player = ObjectDB : : get_instance ( last_animation_player ) ;
if ( player ) {
2020-02-21 17:28:45 +00:00
player - > connect ( " caches_cleared " , callable_mp ( this , & AnimationTree : : _clear_caches ) ) ;
2018-08-24 14:41:04 +00:00
}
}
2018-06-19 01:10:48 +00:00
}
}
2018-06-25 21:40:24 +00:00
void AnimationTree : : set_animation_player ( const NodePath & p_player ) {
2018-06-19 01:10:48 +00:00
animation_player = p_player ;
2020-10-29 10:01:28 +00:00
update_configuration_warnings ( ) ;
2018-06-19 01:10:48 +00:00
}
2018-06-25 21:40:24 +00:00
NodePath AnimationTree : : get_animation_player ( ) const {
2018-06-19 01:10:48 +00:00
return animation_player ;
}
2018-06-25 21:40:24 +00:00
bool AnimationTree : : is_state_invalid ( ) const {
2018-06-19 01:10:48 +00:00
return ! state . valid ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 21:40:24 +00:00
String AnimationTree : : get_invalid_state_reason ( ) const {
2018-06-19 01:10:48 +00:00
return state . invalid_reasons ;
}
2018-06-25 21:40:24 +00:00
uint64_t AnimationTree : : get_last_process_pass ( ) const {
2018-06-19 01:10:48 +00:00
return process_pass ;
}
2020-10-29 10:01:28 +00:00
TypedArray < String > AnimationTree : : get_configuration_warnings ( ) const {
TypedArray < String > warnings = Node : : get_configuration_warnings ( ) ;
2018-06-19 01:10:48 +00:00
if ( ! root . is_valid ( ) ) {
2020-10-29 10:01:28 +00:00
warnings . push_back ( TTR ( " No root AnimationNode for the graph is set. " ) ) ;
2018-06-19 01:10:48 +00:00
}
if ( ! has_node ( animation_player ) ) {
2020-10-29 10:01:28 +00:00
warnings . push_back ( TTR ( " Path to an AnimationPlayer node containing animations is not set. " ) ) ;
2020-05-14 20:59:27 +00:00
} else {
AnimationPlayer * player = Object : : cast_to < AnimationPlayer > ( get_node ( animation_player ) ) ;
2018-06-19 01:10:48 +00:00
2020-05-14 20:59:27 +00:00
if ( ! player ) {
2020-10-29 10:01:28 +00:00
warnings . push_back ( TTR ( " Path set for AnimationPlayer does not lead to an AnimationPlayer node. " ) ) ;
2020-05-14 20:59:27 +00:00
} else if ( ! player - > has_node ( player - > get_root ( ) ) ) {
2020-10-29 10:01:28 +00:00
warnings . push_back ( TTR ( " The AnimationPlayer root node is not a valid node. " ) ) ;
2018-06-19 01:10:48 +00:00
}
}
2020-10-29 10:01:28 +00:00
return warnings ;
2018-06-19 01:10:48 +00:00
}
2018-06-27 06:36:26 +00:00
void AnimationTree : : set_root_motion_track ( const NodePath & p_track ) {
root_motion_track = p_track ;
2018-06-26 22:05:11 +00:00
}
NodePath AnimationTree : : get_root_motion_track ( ) const {
return root_motion_track ;
}
2020-10-17 05:08:21 +00:00
Transform3D AnimationTree : : get_root_motion_transform ( ) const {
2018-06-26 22:05:11 +00:00
return root_motion_transform ;
}
2018-08-20 16:38:18 +00:00
void AnimationTree : : _tree_changed ( ) {
if ( properties_dirty ) {
return ;
}
2021-07-17 21:22:52 +00:00
call_deferred ( SNAME ( " _update_properties " ) ) ;
2018-08-21 19:28:06 +00:00
properties_dirty = true ;
2018-08-20 16:38:18 +00:00
}
2018-08-21 19:28:06 +00:00
void AnimationTree : : _update_properties_for_node ( const String & p_base_path , Ref < AnimationNode > node ) {
2021-03-17 11:33:29 +00:00
ERR_FAIL_COND ( node . is_null ( ) ) ;
2018-08-20 16:38:18 +00:00
if ( ! property_parent_map . has ( p_base_path ) ) {
2018-08-21 19:28:06 +00:00
property_parent_map [ p_base_path ] = HashMap < StringName , StringName > ( ) ;
2018-08-20 16:38:18 +00:00
}
2018-08-23 19:44:10 +00:00
if ( node - > get_input_count ( ) & & ! input_activity_map . has ( p_base_path ) ) {
Vector < Activity > activity ;
2018-08-24 14:50:29 +00:00
for ( int i = 0 ; i < node - > get_input_count ( ) ; i + + ) {
2018-08-23 19:44:10 +00:00
Activity a ;
2019-08-27 15:13:08 +00:00
a . activity = 0 ;
2018-08-24 14:50:29 +00:00
a . last_pass = 0 ;
2018-08-23 19:44:10 +00:00
activity . push_back ( a ) ;
}
input_activity_map [ p_base_path ] = activity ;
2018-08-24 14:50:29 +00: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 19:44:10 +00:00
}
2018-08-20 16:38:18 +00:00
List < PropertyInfo > plist ;
node - > get_parameter_list ( & plist ) ;
2021-07-16 03:45:57 +00:00
for ( PropertyInfo & pinfo : plist ) {
2018-08-20 16:38:18 +00:00
StringName key = pinfo . name ;
2018-08-21 19:28:06 +00:00
if ( ! property_map . has ( p_base_path + key ) ) {
2018-08-20 16:38:18 +00:00
property_map [ p_base_path + key ] = node - > get_parameter_default_value ( key ) ;
}
2018-08-21 19:28:06 +00:00
property_parent_map [ p_base_path ] [ key ] = p_base_path + key ;
2018-08-20 16:38:18 +00:00
pinfo . name = p_base_path + key ;
properties . push_back ( pinfo ) ;
}
List < AnimationNode : : ChildNode > children ;
node - > get_child_nodes ( & children ) ;
2021-07-24 13:46:25 +00:00
for ( const AnimationNode : : ChildNode & E : children ) {
2021-07-16 03:45:57 +00:00
_update_properties_for_node ( p_base_path + E . name + " / " , E . node ) ;
2018-08-20 16:38:18 +00:00
}
}
void AnimationTree : : _update_properties ( ) {
if ( ! properties_dirty ) {
return ;
}
properties . clear ( ) ;
property_parent_map . clear ( ) ;
2018-08-23 19:44:10 +00:00
input_activity_map . clear ( ) ;
input_activity_map_get . clear ( ) ;
2018-08-20 16:38:18 +00:00
if ( root . is_valid ( ) ) {
2018-08-21 19:28:06 +00:00
_update_properties_for_node ( SceneStringNames : : get_singleton ( ) - > parameters_base_path , root ) ;
2018-08-20 16:38:18 +00:00
}
properties_dirty = false ;
2021-02-10 20:18:45 +00:00
notify_property_list_changed ( ) ;
2018-08-20 16:38:18 +00:00
}
bool AnimationTree : : _set ( const StringName & p_name , const Variant & p_value ) {
if ( properties_dirty ) {
_update_properties ( ) ;
}
if ( property_map . has ( p_name ) ) {
2018-08-21 19:28:06 +00:00
property_map [ p_name ] = p_value ;
2018-08-20 16:38:18 +00:00
return true ;
}
return false ;
}
bool AnimationTree : : _get ( const StringName & p_name , Variant & r_ret ) const {
if ( properties_dirty ) {
2018-08-21 19:28:06 +00:00
const_cast < AnimationTree * > ( this ) - > _update_properties ( ) ;
2018-08-20 16:38:18 +00:00
}
if ( property_map . has ( p_name ) ) {
2018-08-21 19:28:06 +00:00
r_ret = property_map [ p_name ] ;
2018-08-20 16:38:18 +00:00
return true ;
}
return false ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
void AnimationTree : : _get_property_list ( List < PropertyInfo > * p_list ) const {
if ( properties_dirty ) {
2018-08-21 19:28:06 +00:00
const_cast < AnimationTree * > ( this ) - > _update_properties ( ) ;
2018-08-20 16:38:18 +00:00
}
2021-07-16 03:45:57 +00:00
for ( const PropertyInfo & E : properties ) {
p_list - > push_back ( E ) ;
2018-08-20 16:38:18 +00:00
}
}
2018-08-21 19:28:06 +00:00
void AnimationTree : : rename_parameter ( const String & p_base , const String & p_new_base ) {
2018-08-20 16:38:18 +00:00
//rename values first
2021-07-16 03:45:57 +00: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 16:38:18 +00:00
}
}
//update tree second
2018-08-21 19:28:06 +00:00
properties_dirty = true ;
2018-08-20 16:38:18 +00:00
_update_properties ( ) ;
}
2021-08-09 22:15:17 +00:00
real_t AnimationTree : : get_connection_activity ( const StringName & p_path , int p_connection ) const {
2018-08-23 19:44:10 +00:00
if ( ! input_activity_map_get . has ( p_path ) ) {
return 0 ;
}
const Vector < Activity > * activity = input_activity_map_get [ p_path ] ;
2018-08-24 14:50:29 +00:00
if ( ! activity | | p_connection < 0 | | p_connection > = activity - > size ( ) ) {
2018-08-23 19:44:10 +00:00
return 0 ;
}
if ( ( * activity ) [ p_connection ] . last_pass ! = process_pass ) {
return 0 ;
}
return ( * activity ) [ p_connection ] . activity ;
}
2018-06-25 21:40:24 +00: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 01:10:48 +00:00
2018-06-27 06:00:08 +00: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 01:10:48 +00:00
2021-02-18 18:52:29 +00: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 01:10:48 +00:00
2018-06-25 21:40:24 +00: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 01:10:48 +00:00
2018-06-26 22:05:11 +00: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 ) ;
2018-06-29 12:13:20 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_root_motion_transform " ) , & AnimationTree : : get_root_motion_transform ) ;
2018-08-20 16:38:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " _update_properties " ) , & AnimationTree : : _update_properties ) ;
2018-08-21 19:28:06 +00:00
ClassDB : : bind_method ( D_METHOD ( " rename_parameter " , " old_name " , " new_name " ) , & AnimationTree : : rename_parameter ) ;
2018-08-20 16:38:18 +00:00
2018-08-02 07:22:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " advance " , " delta " ) , & AnimationTree : : advance ) ;
2018-08-20 16:38:18 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " tree_root " , PROPERTY_HINT_RESOURCE_TYPE , " AnimationRootNode " ) , " set_tree_root " , " get_tree_root " ) ;
2018-06-21 12:47:07 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " anim_player " , PROPERTY_HINT_NODE_PATH_VALID_TYPES , " AnimationPlayer " ) , " set_animation_player " , " get_animation_player " ) ;
2018-06-19 01:10:48 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " active " ) , " set_active " , " is_active " ) ;
2021-02-18 18:52:29 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " process_callback " , PROPERTY_HINT_ENUM , " Physics,Idle,Manual " ) , " set_process_callback " , " get_process_callback " ) ;
2018-06-27 06:36:26 +00: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 12:47:07 +00:00
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_PHYSICS ) ;
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_IDLE ) ;
2018-08-02 07:22:24 +00:00
BIND_ENUM_CONSTANT ( ANIMATION_PROCESS_MANUAL ) ;
2018-06-19 01:10:48 +00:00
}
2018-06-25 21:40:24 +00:00
AnimationTree : : AnimationTree ( ) {
2018-06-19 01:10:48 +00:00
}
2018-06-25 21:40:24 +00:00
AnimationTree : : ~ AnimationTree ( ) {
2018-06-19 01:10:48 +00:00
}