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
							
								
									6adf31babf
								
							
						
					
					
						commit
						9ca5540b75
					
				| @ -155,6 +155,29 @@ impl VariableCurve { | |||||||
| 
 | 
 | ||||||
|         Some(step_start) |         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.
 | /// Interpolation method to use between keyframes.
 | ||||||
| @ -878,15 +901,13 @@ impl AnimationTargetContext<'_> { | |||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Find the current keyframe
 |             // Find the best keyframe to interpolate from
 | ||||||
|             let Some(step_start) = curve.find_current_keyframe(seek_time) else { |             let step_start = curve.find_interpolation_start_keyframe(seek_time); | ||||||
|                 continue; |  | ||||||
|             }; |  | ||||||
| 
 | 
 | ||||||
|             let timestamp_start = curve.keyframe_timestamps[step_start]; |             let timestamp_start = curve.keyframe_timestamps[step_start]; | ||||||
|             let timestamp_end = curve.keyframe_timestamps[step_start + 1]; |             let timestamp_end = curve.keyframe_timestamps[step_start + 1]; | ||||||
|             // Compute how far we are through the keyframe, normalized to [0, 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( |             self.apply_tweened_keyframe( | ||||||
|                 curve, |                 curve, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 robtfm
						robtfm