diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index e04320c30d54..ebdca643ced4 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -234,6 +234,7 @@
+
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.
@@ -305,6 +306,7 @@
+
Returns the interpolated position value at the given time (in seconds). The [param track_idx] must be the index of a 3D position track.
@@ -329,6 +331,7 @@
+
Returns the interpolated rotation value at the given time (in seconds). The [param track_idx] must be the index of a 3D rotation track.
@@ -346,6 +349,7 @@
+
Returns the interpolated scale value at the given time (in seconds). The [param track_idx] must be the index of a 3D scale track.
@@ -574,6 +578,7 @@
+
Returns the interpolated value at the given time (in seconds). The [param track_idx] must be the index of a value track.
diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml
index d17e9377dac1..e521baffda26 100644
--- a/doc/classes/AnimationMixer.xml
+++ b/doc/classes/AnimationMixer.xml
@@ -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.
+
+ 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.
+
The call mode to use for Call Method tracks.
@@ -350,5 +355,14 @@
Make method calls immediately when reached in the animation.
+
+ 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.
+
+
+ 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].
+
+
+ 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].
+
diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml
index 3e0c088b8ae7..778b5cf51349 100644
--- a/doc/classes/AnimationTree.xml
+++ b/doc/classes/AnimationTree.xml
@@ -31,6 +31,7 @@
The path to the [AnimationPlayer] used for animating.
+
The root animation node of this [AnimationTree]. See [AnimationRootNode].
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 2a2e74df1f49..32795330c451 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -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.
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 2f99bdda4058..078d27aa5088 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -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(track);
- bool was_using_angle = track_value->is_using_angle;
+ 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.");
+ }
+ // 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_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;
- }
-
- // 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 (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(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(track);
@@ -1426,8 +1419,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue; // Nothing to blend.
}
TrackCacheValue *t = static_cast(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(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(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")));
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index d618d3833247..5447a00ee3ad 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -61,6 +61,12 @@ class AnimationMixer : public Node {
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 @@ class AnimationMixer : public Node {
/* ---- 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 @@ class AnimationMixer : public Node {
Variant init_value;
Variant value;
Vector subpath;
- bool is_continuous = false;
+ bool use_discrete = false;
bool is_using_angle = false;
Variant element_size;
@@ -224,7 +231,7 @@ class AnimationMixer : public Node {
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 @@ class AnimationMixer : public Node {
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 @@ class AnimatedValuesBackup : public RefCounted {
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeProcess);
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeMethod);
+VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeDiscrete);
#endif // ANIMATION_MIXER_H
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index b208d5af5bf2..9694e855b525 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -897,6 +897,7 @@ void AnimationTree::_bind_methods() {
AnimationTree::AnimationTree() {
deterministic = true;
+ callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
}
AnimationTree::~AnimationTree() {
diff --git a/scene/resources/animation.compat.inc b/scene/resources/animation.compat.inc
new file mode 100644
index 000000000000..bbf016b34d1d
--- /dev/null
+++ b/scene/resources/animation.compat.inc
@@ -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
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index d4401f348927..796a03bd8bac 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -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> &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> &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);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 6c31bbcd297d..115a1a50504d 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -380,6 +380,15 @@ class Animation : public Resource {
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 @@ class Animation : public Resource {
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 @@ class Animation : public Resource {
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;