Merge pull request #92126 from TokageItLab/reset-dominant

Fix Deterministic blending with Dominant/Recessive doesn't have initial value even if there is no Discrete track
This commit is contained in:
Rémi Verschelde 2024-05-29 11:19:45 +02:00
commit 20ad681da2
No known key found for this signature in database
GPG key ID: C3336907360768E1
4 changed files with 31 additions and 15 deletions

View file

@ -273,7 +273,7 @@
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="0"> <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. 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]. 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. 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.
@ -361,14 +361,14 @@
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"> <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. This is the default behavior for [AnimationPlayer]. 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.
[b]Note:[/b] If a value track has non-numeric type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT] with [constant Animation.UPDATE_DISCRETE].
</constant> </constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE" value="1" enum="AnimationCallbackModeDiscrete"> <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. 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>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS" value="2" enum="AnimationCallbackModeDiscrete"> <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]. 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].
If a value track has non-numeric type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE] with [constant Animation.UPDATE_DISCRETE].
</constant> </constant>
</constants> </constants>
</class> </class>

View file

@ -501,6 +501,7 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me
void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) { void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) {
callback_mode_discrete = p_mode; callback_mode_discrete = p_mode;
_clear_caches();
emit_signal(SNAME("mixer_updated")); emit_signal(SNAME("mixer_updated"));
} }
@ -688,7 +689,7 @@ bool AnimationMixer::_update_caches() {
track_value->init_value = anim->track_get_key_value(i, 0); track_value->init_value = anim->track_get_key_value(i, 0);
track_value->init_value.zero(); track_value->init_value.zero();
track_value->init_use_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS; track_value->is_init = false;
// Can't interpolate them, need to convert. // Can't interpolate them, need to convert.
track_value->is_variant_interpolatable = Animation::is_variant_interpolatable(track_value->init_value); track_value->is_variant_interpolatable = Animation::is_variant_interpolatable(track_value->init_value);
@ -698,7 +699,6 @@ bool AnimationMixer::_update_caches() {
int rt = reset_anim->find_track(path, track_src_type); int rt = reset_anim->find_track(path, track_src_type);
if (rt >= 0) { if (rt >= 0) {
if (track_src_type == Animation::TYPE_VALUE) { if (track_src_type == Animation::TYPE_VALUE) {
track_value->init_use_continuous = track_value->init_use_continuous || (reset_anim->value_track_get_update_mode(rt) != Animation::UPDATE_DISCRETE); // Take precedence Force Continuous.
if (reset_anim->track_get_key_count(rt) > 0) { if (reset_anim->track_get_key_count(rt) > 0) {
track_value->init_value = reset_anim->track_get_key_value(rt, 0); track_value->init_value = reset_anim->track_get_key_value(rt, 0);
} }
@ -1006,7 +1006,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_continuous = t->init_use_continuous; t->use_continuous = false;
t->use_discrete = false; t->use_discrete = false;
} break; } break;
case Animation::TYPE_AUDIO: { case Animation::TYPE_AUDIO: {
@ -1462,12 +1462,12 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
t->value = Animation::blend_variant(t->value, value, blend); t->value = Animation::blend_variant(t->value, value, blend);
} }
} else { } else {
t->use_discrete = true;
if (seeked) { if (seeked) {
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true); int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true);
if (idx < 0) { if (idx < 0) {
continue; continue;
} }
t->use_discrete = true;
Variant value = a->track_get_key_value(i, idx); Variant value = a->track_get_key_value(i, idx);
value = post_process_key_value(a, i, value, t->object_id); value = post_process_key_value(a, i, value, t->object_id);
Object *t_obj = ObjectDB::get_instance(t->object_id); Object *t_obj = ObjectDB::get_instance(t->object_id);
@ -1478,6 +1478,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
List<int> indices; List<int> indices;
a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) { for (int &F : indices) {
t->use_discrete = true;
Variant value = a->track_get_key_value(i, F); Variant value = a->track_get_key_value(i, F);
value = post_process_key_value(a, i, value, t->object_id); value = post_process_key_value(a, i, value, t->object_id);
Object *t_obj = ObjectDB::get_instance(t->object_id); Object *t_obj = ObjectDB::get_instance(t->object_id);
@ -1682,7 +1683,8 @@ void AnimationMixer::_blend_apply() {
// Finally, set the tracks. // Finally, set the tracks.
for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
TrackCache *track = K.value; TrackCache *track = K.value;
if (!deterministic && Math::is_zero_approx(track->total_weight)) { bool is_zero_amount = Math::is_zero_approx(track->total_weight);
if (!deterministic && is_zero_amount) {
continue; continue;
} }
switch (track->type) { switch (track->type) {
@ -1742,10 +1744,24 @@ 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_variant_interpolatable || !t->use_continuous || (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete)) { if (t->use_discrete && !t->use_continuous) {
t->is_init = true; // If only disctere value is applied, no more RESET.
}
if ((t->is_init && (is_zero_amount || !t->use_continuous)) ||
(callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS &&
!is_zero_amount &&
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.
} }
if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
t->is_init = false; // Always update in Force Continuous.
} else {
t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET.
}
// Trim unused elements if init array/string is not blended. // Trim unused elements if init array/string is not blended.
if (t->value.is_array()) { if (t->value.is_array()) {
int actual_blended_size = (int)Math::round(Math::abs(t->element_size.operator real_t())); int actual_blended_size = (int)Math::round(Math::abs(t->element_size.operator real_t()));

View file

@ -126,7 +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_DOMINANT; 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;
@ -224,7 +224,7 @@ protected:
Vector<StringName> subpath; Vector<StringName> subpath;
// TODO: There are many boolean, can be packed into one integer. // TODO: There are many boolean, can be packed into one integer.
bool init_use_continuous = false; bool is_init = false;
bool use_continuous = false; bool use_continuous = false;
bool use_discrete = false; bool use_discrete = false;
bool is_using_angle = false; bool is_using_angle = false;
@ -237,7 +237,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),
init_use_continuous(p_other.init_use_continuous), is_init(p_other.is_init),
use_continuous(p_other.use_continuous), use_continuous(p_other.use_continuous),
use_discrete(p_other.use_discrete), use_discrete(p_other.use_discrete),
is_using_angle(p_other.is_using_angle), is_using_angle(p_other.is_using_angle),

View file

@ -447,10 +447,10 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
} else { } else {
if (p_from_end && c.current.pos == 0) { if (p_from_end && c.current.pos == 0) {
// Animation reset but played backwards, set position to the end. // Animation reset but played backwards, set position to the end.
c.current.pos = c.current.from->animation->get_length(); seek(c.current.from->animation->get_length(), true, true);
} else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) { } else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) {
// Animation resumed but already ended, set position to the beginning. // Animation resumed but already ended, set position to the beginning.
c.current.pos = 0; seek(0, true, true);
} else if (playing) { } else if (playing) {
return; return;
} }