Merge pull request #120241 from TokageItLab/anim-stretch

Fix stretch mode in `custom_timeline` on `AnimationNodeAnimation`
This commit is contained in:
Thaddeus Crews
2026-06-15 08:52:42 -05:00
+30 -27
View File
@@ -111,18 +111,20 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro
double anim_size = anim->get_length();
double cur_len; // The animation length seen in NodeTimeInfo, propagated throughout the tree.
// The start/end section passed to AnimationMixer, to trim for Discrete/Method/Audio key section.
double playback_start; // Start of the current section in AnimationMixer.
double playback_end; // End of the current section in AnimationMixer.
Animation::LoopMode cur_loop_mode;
if (use_custom_timeline) {
if (stretch_time_scale) {
// When time scale is stretched, the entire animation is played anyway using a time scale based on the timeline length.
cur_len = timeline_length;
cur_loop_mode = loop_mode;
if (stretch_time_scale || cur_loop_mode != Animation::LOOP_NONE) {
// When time scale is stretched or looped, the entire animation is played anyway using a time scale based on the timeline length.
// Therefore, the end of the animation section is the animation length.
cur_len = anim_size;
playback_start = 0.0;
playback_end = anim_size;
} else {
cur_len = timeline_length;
if (play_mode == PLAY_MODE_FORWARD) {
playback_start = start_offset;
playback_end = playback_start + timeline_length;
@@ -131,7 +133,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro
playback_start = playback_end - timeline_length;
}
}
cur_loop_mode = loop_mode;
} else {
cur_len = anim_size;
playback_start = 0.0;
@@ -139,8 +140,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro
cur_loop_mode = anim->get_loop_mode();
}
// AnimationNodeAnimation always uses times relative to the start of the section. ([0, cur_len])
// When creating the next PlaybackInfo, the relative time is converted back to absolute time. ([playback_start, playback_end])
double cur_time = p_playback_info.time;
double cur_delta = p_playback_info.delta;
bool cur_backward = p_instance.get_parameter_backward();
@@ -211,42 +210,46 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro
// 3. Progress for Animation.
double prev_playback_time = prev_time;
double cur_playback_time = cur_time;
if (use_custom_timeline && stretch_time_scale) {
double mlt = anim_size / timeline_length;
prev_playback_time *= mlt;
cur_playback_time *= mlt;
cur_delta *= mlt;
if (use_custom_timeline) {
prev_playback_time += start_offset;
cur_playback_time += start_offset;
if (stretch_time_scale) {
double mlt = anim_size / timeline_length;
prev_playback_time *= mlt;
cur_playback_time *= mlt;
cur_delta *= mlt;
}
}
if (cur_loop_mode == Animation::LOOP_LINEAR) {
if (!Math::is_zero_approx(cur_len)) {
prev_playback_time = Math::fposmod(prev_playback_time, cur_len);
cur_playback_time = Math::fposmod(cur_playback_time, cur_len);
if (!Math::is_zero_approx(anim_size)) {
prev_playback_time = Math::fposmod(prev_playback_time, anim_size);
cur_playback_time = Math::fposmod(cur_playback_time, anim_size);
if (Animation::is_greater_or_equal_approx(prev_playback_time, 0) && Animation::is_less_approx(cur_playback_time, 0)) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
}
if (Animation::is_less_or_equal_approx(prev_playback_time, cur_len) && Animation::is_greater_approx(cur_playback_time, cur_len)) {
if (Animation::is_less_or_equal_approx(prev_playback_time, anim_size) && Animation::is_greater_approx(cur_playback_time, anim_size)) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
}
}
} else if (cur_loop_mode == Animation::LOOP_PINGPONG) {
if (!Math::is_zero_approx(cur_len)) {
if (Animation::is_greater_or_equal_approx(Math::fposmod(cur_playback_time, cur_len * 2.0), cur_len)) {
if (!Math::is_zero_approx(anim_size)) {
if (Animation::is_greater_or_equal_approx(Math::fposmod(cur_playback_time, anim_size * 2.0), anim_size)) {
cur_delta = -cur_delta; // Needed for retrieving discrete keys correctly.
}
prev_playback_time = Math::pingpong(prev_playback_time, cur_len);
cur_playback_time = Math::pingpong(cur_playback_time, cur_len);
prev_playback_time = Math::pingpong(prev_playback_time, anim_size);
cur_playback_time = Math::pingpong(cur_playback_time, anim_size);
if (Animation::is_greater_or_equal_approx(prev_playback_time, 0) && Animation::is_less_approx(cur_playback_time, 0)) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
}
if (Animation::is_less_or_equal_approx(prev_playback_time, cur_len) && Animation::is_greater_approx(cur_playback_time, cur_len)) {
if (Animation::is_less_or_equal_approx(prev_playback_time, anim_size) && Animation::is_greater_approx(cur_playback_time, anim_size)) {
looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
}
}
} else {
if (Animation::is_less_approx(cur_playback_time, 0)) {
cur_playback_time = 0;
} else if (Animation::is_greater_approx(cur_playback_time, cur_len)) {
cur_playback_time = cur_len;
} else if (Animation::is_greater_approx(cur_playback_time, anim_size)) {
cur_playback_time = anim_size;
}
// Emit start & finish signal. Internally, the detections are the same for backward.
@@ -257,8 +260,8 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro
p_process_state.tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_started), animation);
}
// Finished.
if (Animation::is_less_approx(prev_playback_time, cur_len) && Animation::is_greater_or_equal_approx(cur_playback_time, cur_len)) {
cur_playback_time = cur_len;
if (Animation::is_less_approx(prev_playback_time, anim_size) && Animation::is_greater_or_equal_approx(cur_playback_time, anim_size)) {
cur_playback_time = anim_size;
p_process_state.tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_finished), animation);
}
}
@@ -284,9 +287,9 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(ProcessState &p_pro
pi.start = playback_start;
pi.end = playback_end;
if (play_mode == PLAY_MODE_FORWARD) {
pi.time = playback_start + cur_playback_time;
pi.time = cur_playback_time;
} else {
pi.time = playback_start + cur_len - cur_playback_time;
pi.time = anim_size - cur_playback_time;
}
if (node_backward ? cur_backward : !cur_backward) {
pi.delta = cur_delta;