Merge pull request #86629 from TokageItLab/callback-mode-discrete

Add a `CallbackModeDiscrete` property to `AnimationMixer` to handle the case of blending Continuous and Discrete tracks
This commit is contained in:
Rémi Verschelde 2024-02-17 15:53:27 +01:00
commit e31b253241
No known key found for this signature in database
GPG key ID: C3336907360768E1
10 changed files with 183 additions and 66 deletions

View file

@ -234,6 +234,7 @@
<return type="float" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
<param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated blend shape value at the given time (in seconds). The [param track_idx] must be the index of a blend shape track.
</description>
@ -305,6 +306,7 @@
<return type="Vector3" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
<param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated position value at the given time (in seconds). The [param track_idx] must be the index of a 3D position track.
</description>
@ -329,6 +331,7 @@
<return type="Quaternion" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
<param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated rotation value at the given time (in seconds). The [param track_idx] must be the index of a 3D rotation track.
</description>
@ -346,6 +349,7 @@
<return type="Vector3" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
<param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated scale value at the given time (in seconds). The [param track_idx] must be the index of a 3D scale track.
</description>
@ -574,6 +578,7 @@
<return type="Variant" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
<param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated value at the given time (in seconds). The [param track_idx] must be the index of a value track.
</description>

View file

@ -273,6 +273,11 @@
The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers.
For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each.
</member>
<member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="1">
Ordinarily, tracks can be set to [constant Animation.UPDATE_DISCRETE] to update infrequently, usually when using nearest interpolation.
However, when blending with [constant Animation.UPDATE_CONTINUOUS] several results are considered. The [member callback_mode_discrete] specify it explicitly. See also [enum AnimationCallbackModeDiscrete].
To make the blended results look good, it is recommended to set this to [constant ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to update every frame during blending. Other values exist for compatibility and they are fine if there is no blending, but not so, may produce artifacts.
</member>
<member name="callback_mode_method" type="int" setter="set_callback_mode_method" getter="get_callback_mode_method" enum="AnimationMixer.AnimationCallbackModeMethod" default="0">
The call mode to use for Call Method tracks.
</member>
@ -350,5 +355,14 @@
<constant name="ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE" value="1" enum="AnimationCallbackModeMethod">
Make method calls immediately when reached in the animation.
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT" value="0" enum="AnimationCallbackModeDiscrete">
An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values.
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE" value="1" enum="AnimationCallbackModeDiscrete">
An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer].
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS" value="2" enum="AnimationCallbackModeDiscrete">
Always treat the [constant Animation.UPDATE_DISCRETE] track value as [constant Animation.UPDATE_CONTINUOUS] with [constant Animation.INTERPOLATION_NEAREST]. This is the default behavior for [AnimationTree].
</constant>
</constants>
</class>

View file

@ -31,6 +31,7 @@
<member name="anim_player" type="NodePath" setter="set_animation_player" getter="get_animation_player" default="NodePath(&quot;&quot;)">
The path to the [AnimationPlayer] used for animating.
</member>
<member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" overrides="AnimationMixer" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="2" />
<member name="deterministic" type="bool" setter="set_deterministic" getter="is_deterministic" overrides="AnimationMixer" default="true" />
<member name="tree_root" type="AnimationRootNode" setter="set_tree_root" getter="get_tree_root">
The root animation node of this [AnimationTree]. See [AnimationRootNode].

View file

@ -129,3 +129,14 @@ Validate extension JSON: API was removed: classes/GDExtension/methods/initialize
Validate extension JSON: API was removed: classes/GDExtension/methods/open_library
Since it was basically impossible to use these methods in any useful way, the GDExtension team agreed that breaking compatibility by removing them was OK.
GH-86629
--------
Validate extension JSON: Error: Field 'classes/Animation/methods/position_track_interpolate/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/Animation/methods/rotation_track_interpolate/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/Animation/methods/scale_track_interpolate/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/Animation/methods/blend_shape_track_interpolate/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/Animation/methods/value_track_interpolate/arguments': size changed value in new API, from 2 to 3.
Added optional argument to track_interpolate to treat playing backward correctly. Compatibility method registered.

View file

@ -502,6 +502,17 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me
return callback_mode_method;
}
void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) {
callback_mode_discrete = p_mode;
#ifdef TOOLS_ENABLED
emit_signal(SNAME("mixer_updated"));
#endif // TOOLS_ENABLED
}
AnimationMixer::AnimationCallbackModeDiscrete AnimationMixer::get_callback_mode_discrete() const {
return callback_mode_discrete;
}
void AnimationMixer::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;
@ -680,13 +691,7 @@ bool AnimationMixer::_update_caches() {
track_value->object_id = child->get_instance_id();
}
if (track_src_type == Animation::TYPE_VALUE) {
track_value->is_continuous = anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
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;
} else {
track_value->is_continuous = true;
track_value->is_using_angle = false;
}
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;
track_value->subpath = leftover_path;
@ -866,31 +871,18 @@ bool AnimationMixer::_update_caches() {
}
}
} else if (track_cache_type == Animation::TYPE_VALUE) {
// If it has at least one angle interpolation, it also uses angle interpolation for blending.
TrackCacheValue *track_value = static_cast<TrackCacheValue *>(track);
bool was_using_angle = track_value->is_using_angle;
if (track_src_type == Animation::TYPE_VALUE) {
track_value->is_continuous |= anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
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;
} else {
track_value->is_continuous |= true;
if (track_value->init_value.is_string() && anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE) {
WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm.");
}
// TODO: Currently, misc type cannot be blended.
// In the future, it should have a separate blend weight, just as bool is converted to 0 and 1.
// Then, it should provide the correct precedence value.
if (track_value->is_continuous) {
if (!Animation::is_variant_interpolatable(track_value->init_value)) {
WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' uses a non-numeric type as key value with UpdateMode.UPDATE_CONTINUOUS. This will not be blended correctly, so it is forced to UpdateMode.UPDATE_DISCRETE.");
track_value->is_continuous = false;
}
if (track_value->init_value.is_string()) {
WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm.");
}
// If it has at least one angle interpolation, it also uses angle interpolation for blending.
bool was_using_angle = track_value->is_using_angle;
if (track_src_type == Animation::TYPE_VALUE) {
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;
}
if (check_angle_interpolation && (was_using_angle != track_value->is_using_angle)) {
WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' has different interpolation types for rotation between some animations which may be blended together. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value. If you do not want further warnings, you can turn off the checking for the angle interpolation type conflicting in Project Settings.");
WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' has different interpolation types for rotation between some animations which may be blended together. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
}
}
@ -1012,6 +1004,7 @@ void AnimationMixer::_blend_init() {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
t->value = Animation::cast_to_blendwise(t->init_value);
t->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0;
t->use_discrete = false;
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
@ -1426,8 +1419,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue; // Nothing to blend.
}
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
if (t->is_continuous) {
Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time) : Variant(a->bezier_track_interpolate(i, time));
bool is_discrete = a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE;
bool force_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
if (!is_discrete || force_continuous) {
Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time, is_discrete && force_continuous ? backward : false) : Variant(a->bezier_track_interpolate(i, time));
value = post_process_key_value(a, i, value, t->object_id);
if (value == Variant()) {
continue;
@ -1485,6 +1480,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
}
}
t->use_discrete = true;
}
} break;
case Animation::TYPE_METHOD: {
@ -1736,7 +1732,7 @@ void AnimationMixer::_blend_apply() {
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
if (!t->is_continuous) {
if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete) {
break; // Don't overwrite the value set by UPDATE_DISCRETE.
}
@ -1978,7 +1974,6 @@ void AnimationMixer::_build_backup_track_cache() {
if (t_obj) {
t->value = t_obj->get_indexed(t->subpath);
}
t->is_continuous = true;
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
@ -2202,6 +2197,9 @@ void AnimationMixer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_callback_mode_method", "mode"), &AnimationMixer::set_callback_mode_method);
ClassDB::bind_method(D_METHOD("get_callback_mode_method"), &AnimationMixer::get_callback_mode_method);
ClassDB::bind_method(D_METHOD("set_callback_mode_discrete", "mode"), &AnimationMixer::set_callback_mode_discrete);
ClassDB::bind_method(D_METHOD("get_callback_mode_discrete"), &AnimationMixer::get_callback_mode_discrete);
ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationMixer::set_audio_max_polyphony);
ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationMixer::get_audio_max_polyphony);
@ -2245,6 +2243,7 @@ void AnimationMixer::_bind_methods() {
ADD_GROUP("Callback Mode", "callback_mode_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_callback_mode_process", "get_callback_mode_process");
ADD_PROPERTY(PropertyInfo(Variant::INT, "callback_mode_method", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_callback_mode_method", "get_callback_mode_method");
ADD_PROPERTY(PropertyInfo(Variant::INT, "callback_mode_discrete", PROPERTY_HINT_ENUM, "Dominant,Recessive,Force Continuous"), "set_callback_mode_discrete", "get_callback_mode_discrete");
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_PROCESS_IDLE);
@ -2253,6 +2252,10 @@ void AnimationMixer::_bind_methods() {
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_METHOD_DEFERRED);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS);
ADD_SIGNAL(MethodInfo(SNAME("animation_list_changed")));
ADD_SIGNAL(MethodInfo(SNAME("animation_libraries_updated")));
ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name")));

View file

@ -61,6 +61,12 @@ public:
ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE,
};
enum AnimationCallbackModeDiscrete {
ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT,
ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE,
ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS,
};
/* ---- Data ---- */
struct AnimationLibraryData {
StringName name;
@ -120,6 +126,7 @@ protected:
/* ---- General settings for animation ---- */
AnimationCallbackModeProcess callback_mode_process = ANIMATION_CALLBACK_MODE_PROCESS_IDLE;
AnimationCallbackModeMethod callback_mode_method = ANIMATION_CALLBACK_MODE_METHOD_DEFERRED;
AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE;
int audio_max_polyphony = 32;
NodePath root_node;
@ -215,7 +222,7 @@ protected:
Variant init_value;
Variant value;
Vector<StringName> subpath;
bool is_continuous = false;
bool use_discrete = false;
bool is_using_angle = false;
Variant element_size;
@ -224,7 +231,7 @@ protected:
init_value(p_other.init_value),
value(p_other.value),
subpath(p_other.subpath),
is_continuous(p_other.is_continuous),
use_discrete(p_other.use_discrete),
is_using_angle(p_other.is_using_angle),
element_size(p_other.element_size) {}
@ -402,6 +409,9 @@ public:
void set_callback_mode_method(AnimationCallbackModeMethod p_mode);
AnimationCallbackModeMethod get_callback_mode_method() const;
void set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode);
AnimationCallbackModeDiscrete get_callback_mode_discrete() const;
void set_audio_max_polyphony(int p_audio_max_polyphony);
int get_audio_max_polyphony() const;
@ -466,5 +476,6 @@ public:
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeProcess);
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeMethod);
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeDiscrete);
#endif // ANIMATION_MIXER_H

View file

@ -897,6 +897,7 @@ void AnimationTree::_bind_methods() {
AnimationTree::AnimationTree() {
deterministic = true;
callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
}
AnimationTree::~AnimationTree() {

View file

@ -0,0 +1,61 @@
/**************************************************************************/
/* animation.compat.inc */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
Vector3 Animation::_position_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
return position_track_interpolate(p_track, p_time, false);
}
Quaternion Animation::_rotation_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
return rotation_track_interpolate(p_track, p_time, false);
}
Vector3 Animation::_scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
return scale_track_interpolate(p_track, p_time, false);
}
float Animation::_blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
return blend_shape_track_interpolate(p_track, p_time, false);
}
Variant Animation::_value_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
return value_track_interpolate(p_track, p_time, false);
}
void Animation::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec"), &Animation::_position_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec"), &Animation::_rotation_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::_scale_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::_blend_shape_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::_value_track_interpolate_bind_compat_86629);
}
#endif // DISABLE_DEPRECATED

View file

@ -29,6 +29,7 @@
/**************************************************************************/
#include "animation.h"
#include "animation.compat.inc"
#include "core/io/marshalls.h"
#include "core/math/geometry_3d.h"
@ -1115,7 +1116,7 @@ Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_posit
return OK;
}
Error Animation::try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
Error Animation::try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
@ -1132,7 +1133,7 @@ Error Animation::try_position_track_interpolate(int p_track, double p_time, Vect
bool ok = false;
Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok);
Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@ -1141,10 +1142,10 @@ Error Animation::try_position_track_interpolate(int p_track, double p_time, Vect
return OK;
}
Vector3 Animation::position_track_interpolate(int p_track, double p_time) const {
Vector3 Animation::position_track_interpolate(int p_track, double p_time, bool p_backward) const {
Vector3 ret = Vector3(0, 0, 0);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
bool err = try_position_track_interpolate(p_track, p_time, &ret);
bool err = try_position_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "3D Position Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@ -1195,7 +1196,7 @@ Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_ro
return OK;
}
Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const {
Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
@ -1212,7 +1213,7 @@ Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quat
bool ok = false;
Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok);
Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@ -1221,10 +1222,10 @@ Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quat
return OK;
}
Quaternion Animation::rotation_track_interpolate(int p_track, double p_time) const {
Quaternion Animation::rotation_track_interpolate(int p_track, double p_time, bool p_backward) const {
Quaternion ret = Quaternion(0, 0, 0, 1);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
bool err = try_rotation_track_interpolate(p_track, p_time, &ret);
bool err = try_rotation_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "3D Rotation Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@ -1275,7 +1276,7 @@ Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) c
return OK;
}
Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
@ -1292,7 +1293,7 @@ Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3
bool ok = false;
Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok);
Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@ -1301,10 +1302,10 @@ Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3
return OK;
}
Vector3 Animation::scale_track_interpolate(int p_track, double p_time) const {
Vector3 Animation::scale_track_interpolate(int p_track, double p_time, bool p_backward) const {
Vector3 ret = Vector3(1, 1, 1);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
bool err = try_scale_track_interpolate(p_track, p_time, &ret);
bool err = try_scale_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "3D Scale Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@ -1355,7 +1356,7 @@ Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blen
return OK;
}
Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation) const {
Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
@ -1372,7 +1373,7 @@ Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, f
bool ok = false;
float tk = _interpolate(bst->blend_shapes, p_time, bst->interpolation, bst->loop_wrap, &ok);
float tk = _interpolate(bst->blend_shapes, p_time, bst->interpolation, bst->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@ -1381,10 +1382,10 @@ Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, f
return OK;
}
float Animation::blend_shape_track_interpolate(int p_track, double p_time) const {
float Animation::blend_shape_track_interpolate(int p_track, double p_time, bool p_backward) const {
float ret = 0;
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
bool err = try_blend_shape_track_interpolate(p_track, p_time, &ret);
bool err = try_blend_shape_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "Blend Shape Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@ -2465,7 +2466,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
ERR_FAIL_COND_V(idx == -2, T());
int maxi = len - 1;
bool is_start_edge = idx == -1;
bool is_start_edge = p_backward ? idx >= len : idx == -1;
bool is_end_edge = p_backward ? idx == 0 : idx >= maxi;
real_t c = 0.0;
@ -2647,7 +2648,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
// do a barrel roll
}
Variant Animation::value_track_interpolate(int p_track, double p_time) const {
Variant Animation::value_track_interpolate(int p_track, double p_time, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_VALUE, Variant());
@ -2655,7 +2656,7 @@ Variant Animation::value_track_interpolate(int p_track, double p_time) const {
bool ok = false;
Variant res = _interpolate(vt->values, p_time, (vt->update_mode == UPDATE_CONTINUOUS || vt->update_mode == UPDATE_CAPTURE) ? vt->interpolation : INTERPOLATION_NEAREST, vt->loop_wrap, &ok);
Variant res = _interpolate(vt->values, p_time, vt->update_mode == UPDATE_DISCRETE ? INTERPOLATION_NEAREST : vt->interpolation, vt->loop_wrap, &ok, p_backward);
if (ok) {
return res;
@ -3787,10 +3788,10 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key);
ClassDB::bind_method(D_METHOD("blend_shape_track_insert_key", "track_idx", "time", "amount"), &Animation::blend_shape_track_insert_key);
ClassDB::bind_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec"), &Animation::position_track_interpolate);
ClassDB::bind_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec"), &Animation::rotation_track_interpolate);
ClassDB::bind_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::scale_track_interpolate);
ClassDB::bind_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::blend_shape_track_interpolate);
ClassDB::bind_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::position_track_interpolate, DEFVAL(false));
ClassDB::bind_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::rotation_track_interpolate, DEFVAL(false));
ClassDB::bind_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::scale_track_interpolate, DEFVAL(false));
ClassDB::bind_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::blend_shape_track_interpolate, DEFVAL(false));
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
@ -3816,7 +3817,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode);
ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
ClassDB::bind_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::value_track_interpolate);
ClassDB::bind_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::value_track_interpolate, DEFVAL(false));
ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name);
ClassDB::bind_method(D_METHOD("method_track_get_params", "track_idx", "key_idx"), &Animation::method_track_get_params);

View file

@ -380,6 +380,15 @@ protected:
static bool inform_variant_array(int &r_min, int &r_max); // Returns true if max and min are swapped.
#ifndef DISABLE_DEPRECATED
Vector3 _position_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
Quaternion _rotation_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
Vector3 _scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
float _blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
Variant _value_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
static void _bind_compatibility_methods();
#endif // DISABLE_DEPRECATED
public:
int add_track(TrackType p_type, int p_at_pos = -1);
void remove_track(int p_track);
@ -419,23 +428,23 @@ public:
int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
Error try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
Vector3 position_track_interpolate(int p_track, double p_time) const;
Error try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward = false) const;
Vector3 position_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation);
Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const;
Error try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
Quaternion rotation_track_interpolate(int p_track, double p_time) const;
Error try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation, bool p_backward = false) const;
Quaternion rotation_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale);
Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
Error try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
Vector3 scale_track_interpolate(int p_track, double p_time) const;
Error try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward = false) const;
Vector3 scale_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
int blend_shape_track_insert_key(int p_track, double p_time, float p_blend);
Error blend_shape_track_get_key(int p_track, int p_key, float *r_blend) const;
Error try_blend_shape_track_interpolate(int p_track, double p_time, float *r_blend) const;
float blend_shape_track_interpolate(int p_track, double p_time) const;
Error try_blend_shape_track_interpolate(int p_track, double p_time, float *r_blend, bool p_backward = false) const;
float blend_shape_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
@ -471,7 +480,7 @@ public:
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
Variant value_track_interpolate(int p_track, double p_time) const;
Variant value_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const;