//! Animation for the game engine Bevy mod animatable; mod util; use std::hash::{Hash, Hasher}; use std::iter; use std::ops::{Add, Mul}; use std::time::Duration; use bevy_app::{App, Plugin, PostUpdate}; use bevy_asset::{Asset, AssetApp, Assets, Handle}; use bevy_core::Name; use bevy_ecs::entity::MapEntities; use bevy_ecs::prelude::*; use bevy_ecs::reflect::ReflectMapEntities; use bevy_math::{FloatExt, Quat, Vec3}; use bevy_reflect::Reflect; use bevy_render::mesh::morph::MorphWeights; use bevy_time::Time; use bevy_transform::{prelude::Transform, TransformSystem}; use bevy_utils::hashbrown::HashMap; use bevy_utils::{tracing::error, NoOpHash}; use sha1_smol::Sha1; use uuid::Uuid; #[allow(missing_docs)] pub mod prelude { #[doc(hidden)] pub use crate::{ animatable::*, AnimationClip, AnimationPlayer, AnimationPlugin, Interpolation, Keyframes, VariableCurve, }; } /// The [UUID namespace] of animation targets (e.g. bones). /// /// [UUID namespace]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based) pub static ANIMATION_TARGET_NAMESPACE: Uuid = Uuid::from_u128(0x3179f519d9274ff2b5966fd077023911); /// List of keyframes for one of the attribute of a [`Transform`]. #[derive(Reflect, Clone, Debug)] pub enum Keyframes { /// Keyframes for rotation. Rotation(Vec), /// Keyframes for translation. Translation(Vec), /// Keyframes for scale. Scale(Vec), /// Keyframes for morph target weights. /// /// Note that in `.0`, each contiguous `target_count` values is a single /// keyframe representing the weight values at given keyframe. /// /// This follows the [glTF design]. /// /// [glTF design]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#animations Weights(Vec), } impl Keyframes { /// Returns the number of keyframes. pub fn len(&self) -> usize { match self { Keyframes::Weights(vec) => vec.len(), Keyframes::Translation(vec) | Keyframes::Scale(vec) => vec.len(), Keyframes::Rotation(vec) => vec.len(), } } /// Returns true if the number of keyframes is zero. pub fn is_empty(&self) -> bool { self.len() == 0 } } /// Describes how an attribute of a [`Transform`] or [`MorphWeights`] should be animated. /// /// `keyframe_timestamps` and `keyframes` should have the same length. #[derive(Reflect, Clone, Debug)] pub struct VariableCurve { /// Timestamp for each of the keyframes. pub keyframe_timestamps: Vec, /// List of the keyframes. /// /// The representation will depend on the interpolation type of this curve: /// /// - for `Interpolation::Step` and `Interpolation::Linear`, each keyframe is a single value /// - for `Interpolation::CubicSpline`, each keyframe is made of three values for `tangent_in`, /// `keyframe_value` and `tangent_out` pub keyframes: Keyframes, /// Interpolation method to use between keyframes. pub interpolation: Interpolation, } impl VariableCurve { /// Find the index of the keyframe at or before the current time. /// /// Returns [`None`] if the curve is finished or not yet started. /// To be more precise, this returns [`None`] if the frame is at or past the last keyframe: /// we cannot get the *next* keyframe to interpolate to in that case. pub fn find_current_keyframe(&self, seek_time: f32) -> Option { // 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()); // Subtract one for zero indexing! let last_keyframe = self.keyframes.len() - 1; // 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. let step_start = match search_result { // An exact match was found, and it is the last keyframe (or something has gone terribly wrong). // This means that the curve is finished. Ok(n) if n >= last_keyframe => return None, // An exact match was found, and it is not the last keyframe. Ok(i) => i, // No exact match was found, and the seek_time is before the start of the animation. // This occurs because the binary search returns the index of where we could insert a value // without disrupting the order of the vector. // If the value is less than the first element, the index will be 0. Err(0) => return None, // No exact match was found, and it was after the last keyframe. // The curve is finished. Err(n) if n > last_keyframe => return None, // No exact match was found, so return the previous keyframe to interpolate from. Err(i) => i - 1, }; // Consumers need to be able to interpolate between the return keyframe and the next assert!(step_start < self.keyframe_timestamps.len()); Some(step_start) } } /// Interpolation method to use between keyframes. #[derive(Reflect, Clone, Debug)] pub enum Interpolation { /// Linear interpolation between the two closest keyframes. Linear, /// Step interpolation, the value of the start keyframe is used. Step, /// Cubic spline interpolation. The value of the two closest keyframes is used, with the out /// tangent of the start keyframe and the in tangent of the end keyframe. CubicSpline, } /// A list of [`VariableCurve`]s and the [`AnimationTargetId`]s to which they /// apply. /// /// Because animation clips refer to targets by UUID, they can target any /// [`AnimationTarget`] with that ID. #[derive(Asset, Reflect, Clone, Debug, Default)] pub struct AnimationClip { curves: AnimationCurves, duration: f32, } /// A mapping from [`AnimationTargetId`] (e.g. bone in a skinned mesh) to the /// animation curves. pub type AnimationCurves = HashMap, NoOpHash>; /// A unique [UUID] for an animation target (e.g. bone in a skinned mesh). /// /// The [`AnimationClip`] asset and the [`AnimationTarget`] component both use /// this to refer to targets (e.g. bones in a skinned mesh) to be animated. /// /// When importing an armature or an animation clip, asset loaders typically use /// the full path name from the armature to the bone to generate these UUIDs. /// The ID is unique to the full path name and based only on the names. So, for /// example, any imported armature with a bone at the root named `Hips` will /// assign the same [`AnimationTargetId`] to its root bone. Likewise, any /// imported animation clip that animates a root bone named `Hips` will /// reference the same [`AnimationTargetId`]. Any animation is playable on any /// armature as long as the bone names match, which allows for easy animation /// retargeting. /// /// Note that asset loaders generally use the *full* path name to generate the /// [`AnimationTargetId`]. Thus a bone named `Chest` directly connected to a /// bone named `Hips` will have a different ID from a bone named `Chest` that's /// connected to a bone named `Stomach`. /// /// [UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Reflect, Debug)] pub struct AnimationTargetId(pub Uuid); impl Hash for AnimationTargetId { fn hash(&self, state: &mut H) { let (hi, lo) = self.0.as_u64_pair(); state.write_u64(hi ^ lo); } } /// An entity that can be animated by an [`AnimationPlayer`]. /// /// These are frequently referred to as *bones* or *joints*, because they often /// refer to individually-animatable parts of an armature. /// /// Asset loaders for armatures are responsible for adding these as necessary. /// Typically, they're generated from hashed versions of the entire name path /// from the root of the armature to the bone. See the [`AnimationTargetId`] /// documentation for more details. /// /// By convention, asset loaders add [`AnimationTarget`] components to the /// descendants of an [`AnimationPlayer`], as well as to the [`AnimationPlayer`] /// entity itself, but Bevy doesn't require this in any way. So, for example, /// it's entirely possible for an [`AnimationPlayer`] to animate a target that /// it isn't an ancestor of. If you add a new bone to or delete a bone from an /// armature at runtime, you may want to update the [`AnimationTarget`] /// component as appropriate, as Bevy won't do this automatically. /// /// Note that each entity can only be animated by one animation player at a /// time. However, you can change [`AnimationTarget`]'s `player` property at /// runtime to change which player is responsible for animating the entity. #[derive(Clone, Component, Reflect)] #[reflect(Component, MapEntities)] pub struct AnimationTarget { /// The ID of this animation target. /// /// Typically, this is derived from the path. pub id: AnimationTargetId, /// The entity containing the [`AnimationPlayer`]. pub player: Entity, } impl AnimationClip { #[inline] /// [`VariableCurve`]s for each animation target. Indexed by the [`AnimationTargetId`]. pub fn curves(&self) -> &AnimationCurves { &self.curves } /// Gets the curves for a single animation target. /// /// Returns `None` if this clip doesn't animate the target. #[inline] pub fn curves_for_target( &self, target_id: AnimationTargetId, ) -> Option<&'_ Vec> { self.curves.get(&target_id) } /// Duration of the clip, represented in seconds. #[inline] pub fn duration(&self) -> f32 { self.duration } /// Adds a [`VariableCurve`] to an [`AnimationTarget`] named by an /// [`AnimationTargetId`]. /// /// If the curve extends beyond the current duration of this clip, this /// method lengthens this clip to include the entire time span that the /// curve covers. pub fn add_curve_to_target(&mut self, target_id: AnimationTargetId, curve: VariableCurve) { // Update the duration of the animation by this curve duration if it's longer self.duration = self .duration .max(*curve.keyframe_timestamps.last().unwrap_or(&0.0)); self.curves.entry(target_id).or_default().push(curve); } } /// Repetition behavior of an animation. #[derive(Reflect, Debug, PartialEq, Eq, Copy, Clone, Default)] pub enum RepeatAnimation { /// The animation will finish after running once. #[default] Never, /// The animation will finish after running "n" times. Count(u32), /// The animation will never finish. Forever, } #[derive(Debug, Reflect)] struct PlayingAnimation { repeat: RepeatAnimation, speed: f32, /// Total time the animation has been played. /// /// Note: Time does not increase when the animation is paused or after it has completed. elapsed: f32, /// The timestamp inside of the animation clip. /// /// Note: This will always be in the range [0.0, animation clip duration] seek_time: f32, animation_clip: Handle, /// Number of times the animation has completed. /// If the animation is playing in reverse, this increments when the animation passes the start. completions: u32, } impl Default for PlayingAnimation { fn default() -> Self { Self { repeat: RepeatAnimation::default(), speed: 1.0, elapsed: 0.0, seek_time: 0.0, animation_clip: Default::default(), completions: 0, } } } impl PlayingAnimation { /// Check if the animation has finished, based on its repetition behavior and the number of times it has repeated. /// /// Note: An animation with `RepeatAnimation::Forever` will never finish. #[inline] pub fn is_finished(&self) -> bool { match self.repeat { RepeatAnimation::Forever => false, RepeatAnimation::Never => self.completions >= 1, RepeatAnimation::Count(n) => self.completions >= n, } } /// Update the animation given the delta time and the duration of the clip being played. #[inline] fn update(&mut self, delta: f32, clip_duration: f32) { if self.is_finished() { return; } self.elapsed += delta; self.seek_time += delta * self.speed; let over_time = self.speed > 0.0 && self.seek_time >= clip_duration; let under_time = self.speed < 0.0 && self.seek_time < 0.0; if over_time || under_time { self.completions += 1; if self.is_finished() { return; } } if self.seek_time >= clip_duration { self.seek_time %= clip_duration; } // Note: assumes delta is never lower than -clip_duration if self.seek_time < 0.0 { self.seek_time += clip_duration; } } /// Reset back to the initial state as if no time has elapsed. fn replay(&mut self) { self.completions = 0; self.elapsed = 0.0; self.seek_time = 0.0; } } /// An animation that is being faded out as part of a transition struct AnimationTransition { /// The current weight. Starts at 1.0 and goes to 0.0 during the fade-out. current_weight: f32, /// How much to decrease `current_weight` per second weight_decline_per_sec: f32, /// The animation that is being faded out animation: PlayingAnimation, } /// Animation controls #[derive(Component, Default, Reflect)] #[reflect(Component)] pub struct AnimationPlayer { paused: bool, animation: PlayingAnimation, // List of previous animations we're currently transitioning away from. // Usually this is empty, when transitioning between animations, there is // one entry. When another animation transition happens while a transition // is still ongoing, then there can be more than one entry. // Once a transition is finished, it will be automatically removed from the list #[reflect(ignore)] transitions: Vec, } /// The components that we might need to read or write during animation of each /// animation target. struct AnimationTargetContext<'a> { entity: Entity, target: &'a AnimationTarget, name: Option<&'a Name>, transform: Option>, morph_weights: Option>, } impl AnimationPlayer { /// Start playing an animation, resetting state of the player. /// This will use a linear blending between the previous and the new animation to make a smooth transition. pub fn start(&mut self, handle: Handle) -> &mut Self { self.animation = PlayingAnimation { animation_clip: handle, ..Default::default() }; // We want a hard transition. // In case any previous transitions are still playing, stop them self.transitions.clear(); self } /// Start playing an animation, resetting state of the player. /// This will use a linear blending between the previous and the new animation to make a smooth transition. pub fn start_with_transition( &mut self, handle: Handle, transition_duration: Duration, ) -> &mut Self { let mut animation = PlayingAnimation { animation_clip: handle, ..Default::default() }; std::mem::swap(&mut animation, &mut self.animation); // Add the current transition. If other transitions are still ongoing, // this will keep those transitions running and cause a transition between // the output of that previous transition to the new animation. self.transitions.push(AnimationTransition { current_weight: 1.0, weight_decline_per_sec: 1.0 / transition_duration.as_secs_f32(), animation, }); self } /// Start playing an animation, resetting state of the player, unless the requested animation is already playing. pub fn play(&mut self, handle: Handle) -> &mut Self { if !self.is_playing_clip(&handle) || self.is_paused() { self.start(handle); } self } /// Start playing an animation, resetting state of the player, unless the requested animation is already playing. /// This will use a linear blending between the previous and the new animation to make a smooth transition pub fn play_with_transition( &mut self, handle: Handle, transition_duration: Duration, ) -> &mut Self { if !self.is_playing_clip(&handle) || self.is_paused() { self.start_with_transition(handle, transition_duration); } self } /// Handle to the animation clip being played. pub fn animation_clip(&self) -> &Handle { &self.animation.animation_clip } /// Check if the given animation clip is being played. pub fn is_playing_clip(&self, handle: &Handle) -> bool { self.animation_clip() == handle } /// Check if the playing animation has finished, according to the repetition behavior. pub fn is_finished(&self) -> bool { self.animation.is_finished() } /// Sets repeat to [`RepeatAnimation::Forever`]. /// /// See also [`Self::set_repeat`]. pub fn repeat(&mut self) -> &mut Self { self.animation.repeat = RepeatAnimation::Forever; self } /// Set the repetition behaviour of the animation. pub fn set_repeat(&mut self, repeat: RepeatAnimation) -> &mut Self { self.animation.repeat = repeat; self } /// Repetition behavior of the animation. pub fn repeat_mode(&self) -> RepeatAnimation { self.animation.repeat } /// Number of times the animation has completed. pub fn completions(&self) -> u32 { self.animation.completions } /// Check if the animation is playing in reverse. pub fn is_playback_reversed(&self) -> bool { self.animation.speed < 0.0 } /// Pause the animation pub fn pause(&mut self) { self.paused = true; } /// Unpause the animation pub fn resume(&mut self) { self.paused = false; } /// Is the animation paused pub fn is_paused(&self) -> bool { self.paused } /// Speed of the animation playback pub fn speed(&self) -> f32 { self.animation.speed } /// Set the speed of the animation playback pub fn set_speed(&mut self, speed: f32) -> &mut Self { self.animation.speed = speed; self } /// Time elapsed playing the animation pub fn elapsed(&self) -> f32 { self.animation.elapsed } /// Seek time inside of the animation. Always within the range [0.0, clip duration]. pub fn seek_time(&self) -> f32 { self.animation.seek_time } /// Seek to a specific time in the animation. pub fn seek_to(&mut self, seek_time: f32) -> &mut Self { self.animation.seek_time = seek_time; self } /// Reset the animation to its initial state, as if no time has elapsed. pub fn replay(&mut self) { self.animation.replay(); } } /// A system that advances the time for all playing animations. pub fn advance_animations( time: Res