apply finished animations (#14743)
# Objective fix #14742 ## Solution the issue arises because "finished" animations (where current time >= last keyframe time) are not applied at all. when transitioning from a finished animation to another later-indexed anim, the transition kind-of works because the finished anim is skipped, then the new anim is applied with a lower weight (weight / total_weight) when transitioning from a finished animation to another earlier-indexed anim, the transition is instant as the new anim is applied with 1.0 (as weight == total_weight for the first applied), then the finished animation is skipped. to fix this we can always apply every animation based on the nearest 2 keyframes, and clamp the interpolation between them to [0,1]. pros: - finished animations can be transitioned out of correctly - blended animations where some curves have a last-keyframe before the end of the animation will blend properly - animations will actually finish on their last keyframe, rather than a fraction of a render-frame before the end cons: - we have to re-apply finished animations every frame whether it's necessary or not. i can't see a way to avoid this.
This commit is contained in:
parent
0793d05256
commit
650e7c9eb4
@ -155,6 +155,29 @@ impl VariableCurve {
|
||||
|
||||
Some(step_start)
|
||||
}
|
||||
|
||||
/// Find the index of the keyframe at or before the current time.
|
||||
///
|
||||
/// Returns the first keyframe if the `seek_time` is before the first keyframe, and
|
||||
/// the second-to-last keyframe if the `seek_time` is after the last keyframe.
|
||||
/// Panics if there are less than 2 keyframes.
|
||||
pub fn find_interpolation_start_keyframe(&self, seek_time: f32) -> usize {
|
||||
// An Ok(keyframe_index) result means an exact result was found by binary search
|
||||
// An Err result means the keyframe was not found, and the index is the keyframe
|
||||
// PERF: finding the current keyframe can be optimised
|
||||
let search_result = self
|
||||
.keyframe_timestamps
|
||||
.binary_search_by(|probe| probe.partial_cmp(&seek_time).unwrap());
|
||||
|
||||
// We want to find the index of the keyframe before the current time
|
||||
// If the keyframe is past the second-to-last keyframe, the animation cannot be interpolated.
|
||||
match search_result {
|
||||
// An exact match was found
|
||||
Ok(i) => i.clamp(0, self.keyframe_timestamps.len() - 2),
|
||||
// No exact match was found, so return the previous keyframe to interpolate from.
|
||||
Err(i) => (i.saturating_sub(1)).clamp(0, self.keyframe_timestamps.len() - 2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpolation method to use between keyframes.
|
||||
@ -877,15 +900,13 @@ impl AnimationTargetContext<'_> {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the current keyframe
|
||||
let Some(step_start) = curve.find_current_keyframe(seek_time) else {
|
||||
continue;
|
||||
};
|
||||
// Find the best keyframe to interpolate from
|
||||
let step_start = curve.find_interpolation_start_keyframe(seek_time);
|
||||
|
||||
let timestamp_start = curve.keyframe_timestamps[step_start];
|
||||
let timestamp_end = curve.keyframe_timestamps[step_start + 1];
|
||||
// Compute how far we are through the keyframe, normalized to [0, 1]
|
||||
let lerp = f32::inverse_lerp(timestamp_start, timestamp_end, seek_time);
|
||||
let lerp = f32::inverse_lerp(timestamp_start, timestamp_end, seek_time).clamp(0.0, 1.0);
|
||||
|
||||
self.apply_tweened_keyframe(
|
||||
curve,
|
||||
|
Loading…
Reference in New Issue
Block a user