diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index f3fe022035e76..b769e06891ef1 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -680,6 +680,8 @@ fn apply_animation( // SAFETY: As above, there can't be other AnimationPlayers with this target so this fetch can't alias let mut morphs = unsafe { morphs.get_unchecked(target) }.ok(); for curve in curves { + let len = curve.keyframe_timestamps.len(); + // Some curves have only one keyframe used to set a transform if curve.keyframe_timestamps.len() == 1 { match &curve.keyframes { @@ -707,16 +709,47 @@ fn apply_animation( continue; } - // Find the current keyframe + // Attempt to find the keyframe at or before the current time + // 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 step_start = match curve + let index = curve .keyframe_timestamps - .binary_search_by(|probe| probe.partial_cmp(&animation.seek_time).unwrap()) - { - Ok(n) if n >= curve.keyframe_timestamps.len() - 1 => continue, // this curve is finished + .binary_search_by(|probe| probe.partial_cmp(&animation.seek_time).unwrap()); + + // Find the keyframe that our animation starts from + let step_start = match index { + // We could find an exact match, + // and the match was the last or second last keyframe + Ok(i) if i >= curve.keyframe_timestamps.len() - 1 => { + set_transform_to_keyframe( + curve, + &mut transform, + weight, + &mut morphs, + len - 1, + ); + continue; + } + // We could find an exact match, + // and the match was not the last or second last keyframe Ok(i) => i, - Err(0) => continue, // this curve isn't started yet - Err(n) if n > curve.keyframe_timestamps.len() - 1 => continue, // this curve is finished + // The animation was not started yet + Err(0) => continue, + // We could not find an exact match, + // and the timestamp was after the last keyframe + Err(i) if i > curve.keyframe_timestamps.len() - 1 => { + set_transform_to_keyframe( + curve, + &mut transform, + weight, + &mut morphs, + len - 1, + ); + continue; + } + // We could not find an exact match, + // But we are Err(i) => i - 1, }; let ts_start = curve.keyframe_timestamps[step_start]; @@ -741,6 +774,39 @@ fn apply_animation( } } +/// Sets the [`Transform`] value to exactly the value it has at the provided keyframe. +fn set_transform_to_keyframe( + curve: &VariableCurve, + transform: &mut Mut<'_, Transform>, + weight: f32, + morphs: &mut Option>, + keyframe: usize, +) { + match &curve.keyframes { + Keyframes::Rotation(keyframes) => { + transform.rotation = transform.rotation.slerp(keyframes[keyframe], weight); + } + Keyframes::Translation(keyframes) => { + transform.translation = transform.translation.lerp(keyframes[keyframe], weight); + } + Keyframes::Scale(keyframes) => { + transform.scale = transform.scale.lerp(keyframes[keyframe], weight); + } + Keyframes::Weights(keyframes) => { + if let Some(morphs) = morphs { + let target_count = morphs.weights().len(); + lerp_morph_weights( + morphs.weights_mut(), + get_keyframe(target_count, keyframes, keyframe) + .iter() + .copied(), + weight, + ); + } + } + } +} + #[inline(always)] fn apply_keyframe( curve: &VariableCurve,