Skip to content

Commit

Permalink
Add CallbackModeDiscrete to AnimationMixer
Browse files Browse the repository at this point in the history
  • Loading branch information
TokageItLab committed Feb 17, 2024
1 parent 8ff8216 commit bc20fdf
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 65 deletions.
5 changes: 5 additions & 0 deletions doc/classes/Animation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand All @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
14 changes: 14 additions & 0 deletions doc/classes/AnimationMixer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
1 change: 1 addition & 0 deletions doc/classes/AnimationTree.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down
11 changes: 11 additions & 0 deletions misc/extension_api_validation/4.2-stable.expected
Original file line number Diff line number Diff line change
Expand Up @@ -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.
63 changes: 33 additions & 30 deletions scene/animation/animation_mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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_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.");
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1485,6 +1480,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
}
}
t->use_discrete = true;
}
} break;
case Animation::TYPE_METHOD: {
Expand Down Expand Up @@ -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.
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand All @@ -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")));
Expand Down
15 changes: 13 additions & 2 deletions scene/animation/animation_mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -215,7 +222,7 @@ class AnimationMixer : public Node {
Variant init_value;
Variant value;
Vector<StringName> subpath;
bool is_continuous = false;
bool use_discrete = false;
bool is_using_angle = false;
Variant element_size;

Expand All @@ -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) {}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions scene/animation/animation_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@ void AnimationTree::_bind_methods() {

AnimationTree::AnimationTree() {
deterministic = true;
callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
}

AnimationTree::~AnimationTree() {
Expand Down
Loading

0 comments on commit bc20fdf

Please sign in to comment.