#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![forbid(unsafe_code)] #![doc( html_logo_url = "https://bevyengine.org/assets/icon.png", html_favicon_url = "https://bevyengine.org/assets/icon.png" )] //! Animation for the game engine Bevy mod animatable; mod graph; mod transition; mod util; use std::cell::RefCell; use std::collections::BTreeMap; use std::hash::{Hash, Hasher}; use std::iter; use std::ops::{Add, Mul}; 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, trace}, NoOpHash, }; use fixedbitset::FixedBitSet; use graph::{AnimationGraph, AnimationNodeIndex}; use petgraph::graph::NodeIndex; use petgraph::Direction; use prelude::{AnimationGraphAssetLoader, AnimationTransitions}; use thread_local::ThreadLocal; use uuid::Uuid; #[allow(missing_docs)] pub mod prelude { #[doc(hidden)] pub use crate::{ animatable::*, graph::*, transition::*, AnimationClip, AnimationPlayer, AnimationPlugin, Interpolation, Keyframes, VariableCurve, }; } use crate::transition::{advance_transitions, expire_completed_transitions}; /// 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.keyframe_timestamps.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) } /// 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. #[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, Copy, 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 } #[inline] /// Get mutable references of [`VariableCurve`]s for each animation target. Indexed by the [`AnimationTargetId`]. pub fn curves_mut(&mut self) -> &mut AnimationCurves { &mut 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) } /// Gets mutable references of the curves for a single animation target. /// /// Returns `None` if this clip doesn't animate the target. #[inline] pub fn curves_for_target_mut( &mut self, target_id: AnimationTargetId, ) -> Option<&'_ mut Vec> { self.curves.get_mut(&target_id) } /// Duration of the clip, represented in seconds. #[inline] pub fn duration(&self) -> f32 { self.duration } /// Set the duration of the clip in seconds. #[inline] pub fn set_duration(&mut self, duration_sec: f32) { self.duration = duration_sec; } /// 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, } /// An animation that an [`AnimationPlayer`] is currently either playing or was /// playing, but is presently paused. /// /// An stopped animation is considered no longer active. #[derive(Debug, Clone, Copy, Reflect)] pub struct ActiveAnimation { /// The factor by which the weight from the [`AnimationGraph`] is multiplied. weight: f32, /// The actual weight of this animation this frame, taking the /// [`AnimationGraph`] into account. computed_weight: f32, 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, /// Number of times the animation has completed. /// If the animation is playing in reverse, this increments when the animation passes the start. completions: u32, paused: bool, } impl Default for ActiveAnimation { fn default() -> Self { Self { weight: 1.0, computed_weight: 1.0, repeat: RepeatAnimation::default(), speed: 1.0, elapsed: 0.0, seek_time: 0.0, completions: 0, paused: false, } } } impl ActiveAnimation { /// 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. pub fn replay(&mut self) { self.completions = 0; self.elapsed = 0.0; self.seek_time = 0.0; } /// Returns the current weight of this animation. pub fn weight(&self) -> f32 { self.weight } /// Sets the weight of this animation. pub fn set_weight(&mut self, weight: f32) { self.weight = weight; } /// Pause the animation. pub fn pause(&mut self) -> &mut Self { self.paused = true; self } /// Unpause the animation. pub fn resume(&mut self) -> &mut Self { self.paused = false; self } /// Returns true if this animation is currently paused. /// /// Note that paused animations are still [`ActiveAnimation`]s. #[inline] pub fn is_paused(&self) -> bool { self.paused } /// Sets the repeat mode for this playing animation. pub fn set_repeat(&mut self, repeat: RepeatAnimation) -> &mut Self { self.repeat = repeat; self } /// Marks this animation as repeating forever. pub fn repeat(&mut self) -> &mut Self { self.set_repeat(RepeatAnimation::Forever) } /// Returns the repeat mode assigned to this active animation. pub fn repeat_mode(&self) -> RepeatAnimation { self.repeat } /// Returns the number of times this animation has completed. pub fn completions(&self) -> u32 { self.completions } /// Returns true if the animation is playing in reverse. pub fn is_playback_reversed(&self) -> bool { self.speed < 0.0 } /// Returns the speed of the animation playback. pub fn speed(&self) -> f32 { self.speed } /// Sets the speed of the animation playback. pub fn set_speed(&mut self, speed: f32) -> &mut Self { self.speed = speed; self } /// Returns the amount of time the animation has been playing. pub fn elapsed(&self) -> f32 { self.elapsed } /// Returns the seek time of the animation. /// /// This is nonnegative and no more than the clip duration. pub fn seek_time(&self) -> f32 { self.seek_time } /// Seeks to a specific time in the animation. pub fn seek_to(&mut self, seek_time: f32) -> &mut Self { self.seek_time = seek_time; self } /// Seeks to the beginning of the animation. pub fn rewind(&mut self) -> &mut Self { self.seek_time = 0.0; self } } /// Animation controls #[derive(Component, Default, Reflect)] #[reflect(Component)] pub struct AnimationPlayer { /// We use a `BTreeMap` instead of a `HashMap` here to ensure a consistent /// ordering when applying the animations. active_animations: BTreeMap, blend_weights: HashMap, } // This is needed since `#[derive(Clone)]` does not generate optimized `clone_from`. impl Clone for AnimationPlayer { fn clone(&self) -> Self { Self { active_animations: self.active_animations.clone(), blend_weights: self.blend_weights.clone(), } } fn clone_from(&mut self, source: &Self) { self.active_animations.clone_from(&source.active_animations); self.blend_weights.clone_from(&source.blend_weights); } } /// 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>, } /// Information needed during the traversal of the animation graph in /// [`advance_animations`]. #[derive(Default)] pub struct AnimationGraphEvaluator { /// The stack used for the depth-first search of the graph. dfs_stack: Vec, /// The list of visited nodes during the depth-first traversal. dfs_visited: FixedBitSet, /// Accumulated weights for each node. weights: Vec, } thread_local! { /// A cached per-thread copy of the graph evaluator. /// /// Caching the evaluator lets us save allocation traffic from frame to /// frame. static ANIMATION_GRAPH_EVALUATOR: RefCell = RefCell::new(AnimationGraphEvaluator::default()); } impl AnimationPlayer { /// Start playing an animation, restarting it if necessary. pub fn start(&mut self, animation: AnimationNodeIndex) -> &mut ActiveAnimation { let playing_animation = self.active_animations.entry(animation).or_default(); playing_animation.replay(); playing_animation } /// Start playing an animation, unless the requested animation is already playing. pub fn play(&mut self, animation: AnimationNodeIndex) -> &mut ActiveAnimation { self.active_animations.entry(animation).or_default() } /// Stops playing the given animation, removing it from the list of playing /// animations. pub fn stop(&mut self, animation: AnimationNodeIndex) -> &mut Self { self.active_animations.remove(&animation); self } /// Stops all currently-playing animations. pub fn stop_all(&mut self) -> &mut Self { self.active_animations.clear(); self } /// Iterates through all animations that this [`AnimationPlayer`] is /// currently playing. pub fn playing_animations( &self, ) -> impl Iterator { self.active_animations.iter() } /// Iterates through all animations that this [`AnimationPlayer`] is /// currently playing, mutably. pub fn playing_animations_mut( &mut self, ) -> impl Iterator { self.active_animations.iter_mut() } #[deprecated = "Use `animation_is_playing` instead"] /// Check if the given animation node is being played. pub fn is_playing_animation(&self, animation: AnimationNodeIndex) -> bool { self.active_animations.contains_key(&animation) } /// Check if all playing animations have finished, according to the repetition behavior. pub fn all_finished(&self) -> bool { self.active_animations .values() .all(ActiveAnimation::is_finished) } /// Check if all playing animations are paused. #[doc(alias = "is_paused")] pub fn all_paused(&self) -> bool { self.active_animations .values() .all(ActiveAnimation::is_paused) } /// Resume all playing animations. #[doc(alias = "pause")] pub fn pause_all(&mut self) -> &mut Self { for (_, playing_animation) in self.playing_animations_mut() { playing_animation.pause(); } self } /// Resume all active animations. #[doc(alias = "resume")] pub fn resume_all(&mut self) -> &mut Self { for (_, playing_animation) in self.playing_animations_mut() { playing_animation.resume(); } self } /// Rewinds all active animations. #[doc(alias = "rewind")] pub fn rewind_all(&mut self) -> &mut Self { for (_, playing_animation) in self.playing_animations_mut() { playing_animation.rewind(); } self } /// Multiplies the speed of all active animations by the given factor. #[doc(alias = "set_speed")] pub fn adjust_speeds(&mut self, factor: f32) -> &mut Self { for (_, playing_animation) in self.playing_animations_mut() { let new_speed = playing_animation.speed() * factor; playing_animation.set_speed(new_speed); } self } /// Seeks all active animations forward or backward by the same amount. /// /// To seek forward, pass a positive value; to seek negative, pass a /// negative value. Values below 0.0 or beyond the end of the animation clip /// are clamped appropriately. #[doc(alias = "seek_to")] pub fn seek_all_by(&mut self, amount: f32) -> &mut Self { for (_, playing_animation) in self.playing_animations_mut() { let new_time = playing_animation.seek_time(); playing_animation.seek_to(new_time + amount); } self } /// Returns the [`ActiveAnimation`] associated with the given animation /// node if it's currently playing. /// /// If the animation isn't currently active, returns `None`. pub fn animation(&self, animation: AnimationNodeIndex) -> Option<&ActiveAnimation> { self.active_animations.get(&animation) } /// Returns a mutable reference to the [`ActiveAnimation`] associated with /// the given animation node if it's currently active. /// /// If the animation isn't currently active, returns `None`. pub fn animation_mut(&mut self, animation: AnimationNodeIndex) -> Option<&mut ActiveAnimation> { self.active_animations.get_mut(&animation) } /// Returns true if the animation is currently playing or paused, or false /// if the animation is stopped. pub fn animation_is_playing(&self, animation: AnimationNodeIndex) -> bool { self.active_animations.contains_key(&animation) } } /// A system that advances the time for all playing animations. pub fn advance_animations( time: Res