Add CallbackModeDiscrete to AnimationMixer

This commit is contained in:
Silc Lizard (Tokage) Renew 2023-12-30 15:26:17 +09:00
parent 8ff8216705
commit bc20fdf16f
10 changed files with 183 additions and 66 deletions

View file

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

View file

@ -273,6 +273,11 @@
The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers. 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. 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>
<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"> <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. The call mode to use for Call Method tracks.
</member> </member>
@ -350,5 +355,14 @@
<constant name="ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE" value="1" enum="AnimationCallbackModeMethod"> <constant name="ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE" value="1" enum="AnimationCallbackModeMethod">
Make method calls immediately when reached in the animation. Make method calls immediately when reached in the animation.
</constant> </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> </constants>
</class> </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;)"> <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. The path to the [AnimationPlayer] used for animating.
</member> </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="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"> <member name="tree_root" type="AnimationRootNode" setter="set_tree_root" getter="get_tree_root">
The root animation node of this [AnimationTree]. See [AnimationRootNode]. 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 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. 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; 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) { void AnimationMixer::set_audio_max_polyphony(int p_audio_max_polyphony) {
ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128); ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128);
audio_max_polyphony = p_audio_max_polyphony; audio_max_polyphony = p_audio_max_polyphony;
@ -680,13 +691,7 @@ bool AnimationMixer::_update_caches() {
track_value->object_id = child->get_instance_id(); track_value->object_id = child->get_instance_id();
} }
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;
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->subpath = leftover_path; track_value->subpath = leftover_path;
@ -866,31 +871,18 @@ bool AnimationMixer::_update_caches() {
} }
} }
} else if (track_cache_type == Animation::TYPE_VALUE) { } 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); TrackCacheValue *track_value = static_cast<TrackCacheValue *>(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 (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. // If it has at least one angle interpolation, it also uses angle interpolation for blending.
// In the future, it should have a separate blend weight, just as bool is converted to 0 and 1. bool was_using_angle = track_value->is_using_angle;
// Then, it should provide the correct precedence value. if (track_src_type == Animation::TYPE_VALUE) {
if (track_value->is_continuous) { 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 (!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)) { 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); TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
t->value = Animation::cast_to_blendwise(t->init_value); 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->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0;
t->use_discrete = false;
} break; } break;
case Animation::TYPE_AUDIO: { case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); 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. continue; // Nothing to blend.
} }
TrackCacheValue *t = static_cast<TrackCacheValue *>(track); TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
if (t->is_continuous) { bool is_discrete = a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE;
Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time) : Variant(a->bezier_track_interpolate(i, time)); 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); value = post_process_key_value(a, i, value, t->object_id);
if (value == Variant()) { if (value == Variant()) {
continue; continue;
@ -1485,6 +1480,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
} }
} }
} }
t->use_discrete = true;
} }
} break; } break;
case Animation::TYPE_METHOD: { case Animation::TYPE_METHOD: {
@ -1736,7 +1732,7 @@ void AnimationMixer::_blend_apply() {
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track); 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. break; // Don't overwrite the value set by UPDATE_DISCRETE.
} }
@ -1978,7 +1974,6 @@ void AnimationMixer::_build_backup_track_cache() {
if (t_obj) { if (t_obj) {
t->value = t_obj->get_indexed(t->subpath); t->value = t_obj->get_indexed(t->subpath);
} }
t->is_continuous = true;
} break; } break;
case Animation::TYPE_AUDIO: { case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); 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("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("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("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); 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_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_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_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_PHYSICS);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_PROCESS_IDLE); 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_DEFERRED);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE); 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_list_changed")));
ADD_SIGNAL(MethodInfo(SNAME("animation_libraries_updated"))); ADD_SIGNAL(MethodInfo(SNAME("animation_libraries_updated")));
ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name")));

View file

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

View file

@ -897,6 +897,7 @@ void AnimationTree::_bind_methods() {
AnimationTree::AnimationTree() { AnimationTree::AnimationTree() {
deterministic = true; deterministic = true;
callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
} }
AnimationTree::~AnimationTree() { 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.h"
#include "animation.compat.inc"
#include "core/io/marshalls.h" #include "core/io/marshalls.h"
#include "core/math/geometry_3d.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; 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); ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track]; Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); 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; 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) { if (!ok) {
return ERR_UNAVAILABLE; return ERR_UNAVAILABLE;
@ -1141,10 +1142,10 @@ Error Animation::try_position_track_interpolate(int p_track, double p_time, Vect
return OK; 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); Vector3 ret = Vector3(0, 0, 0);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret); 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."); ERR_FAIL_COND_V_MSG(err, ret, "3D Position Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret; return ret;
} }
@ -1195,7 +1196,7 @@ Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_ro
return OK; 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); ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track]; Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); 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; 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) { if (!ok) {
return ERR_UNAVAILABLE; return ERR_UNAVAILABLE;
@ -1221,10 +1222,10 @@ Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quat
return OK; 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); Quaternion ret = Quaternion(0, 0, 0, 1);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret); 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."); ERR_FAIL_COND_V_MSG(err, ret, "3D Rotation Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret; return ret;
} }
@ -1275,7 +1276,7 @@ Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) c
return OK; 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); ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track]; Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); 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; 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) { if (!ok) {
return ERR_UNAVAILABLE; return ERR_UNAVAILABLE;
@ -1301,10 +1302,10 @@ Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3
return OK; 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); Vector3 ret = Vector3(1, 1, 1);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret); 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."); ERR_FAIL_COND_V_MSG(err, ret, "3D Scale Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret; return ret;
} }
@ -1355,7 +1356,7 @@ Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blen
return OK; 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); ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track]; Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER); 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; 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) { if (!ok) {
return ERR_UNAVAILABLE; return ERR_UNAVAILABLE;
@ -1381,10 +1382,10 @@ Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, f
return OK; 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; float ret = 0;
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret); 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."); ERR_FAIL_COND_V_MSG(err, ret, "Blend Shape Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret; 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()); ERR_FAIL_COND_V(idx == -2, T());
int maxi = len - 1; 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; bool is_end_edge = p_backward ? idx == 0 : idx >= maxi;
real_t c = 0.0; 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 // 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); ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track]; Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_VALUE, Variant()); 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; 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) { if (ok) {
return res; 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("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("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("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"), &Animation::rotation_track_interpolate); 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"), &Animation::scale_track_interpolate); 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"), &Animation::blend_shape_track_interpolate); 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_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); 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_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_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_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); 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. 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: public:
int add_track(TrackType p_type, int p_at_pos = -1); int add_track(TrackType p_type, int p_at_pos = -1);
void remove_track(int p_track); 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); 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 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; 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) 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); 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 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; 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) 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); 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 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; 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) 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); 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 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; 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) 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); void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const; 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); void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const; 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); void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const; UpdateMode value_track_get_update_mode(int p_track) const;