Path: blob/main/crates/bevy_animation/src/animation_curves.rs
6595 views
//! The [`AnimationCurve`] trait and adaptors that allow curves to implement it.1//!2//! # Overview3//!4//! The flow of curves into the animation system generally begins with something that5//! implements the [`Curve`] trait. Let's imagine, for example, that we have some6//! `Curve<Vec3>` that we want to use to animate something. That could be defined in7//! a number of different ways, but let's imagine that we've defined it [using a function]:8//!9//! # use bevy_math::curve::{Curve, Interval, FunctionCurve};10//! # use bevy_math::vec3;11//! let wobble_curve = FunctionCurve::new(12//! Interval::UNIT,13//! |t| { vec3(t.cos(), 0.0, 0.0) },14//! );15//!16//! Okay, so we have a curve, but the animation system also needs to know, in some way,17//! how the values from this curve should actually be used. That is, it needs to know what18//! to animate! That's what [`AnimationCurve`] is for. In particular, what we need to do19//! is take our curve and turn it into an `AnimationCurve` which will be usable by the20//! animation system.21//!22//! For instance, let's imagine that we want to use the `Vec3` output23//! from our curve to animate the [translation component of a `Transform`]. For this, there is24//! the adaptor [`AnimatableCurve`], which wraps any [`Curve`] and [`AnimatableProperty`] and turns it into an25//! [`AnimationCurve`] that will use the given curve to animate the entity's property:26//!27//! # use bevy_math::curve::{Curve, Interval, FunctionCurve};28//! # use bevy_math::vec3;29//! # use bevy_transform::components::Transform;30//! # use bevy_animation::{animated_field, animation_curves::*};31//! # let wobble_curve = FunctionCurve::new(32//! # Interval::UNIT,33//! # |t| vec3(t.cos(), 0.0, 0.0)34//! # );35//! let wobble_animation = AnimatableCurve::new(animated_field!(Transform::translation), wobble_curve);36//!37//! And finally, this [`AnimationCurve`] needs to be added to an [`AnimationClip`] in order to38//! actually animate something. This is what that looks like:39//!40//! # use bevy_math::curve::{Curve, Interval, FunctionCurve};41//! # use bevy_animation::{AnimationClip, AnimationTargetId, animated_field, animation_curves::*};42//! # use bevy_transform::components::Transform;43//! # use bevy_ecs::name::Name;44//! # use bevy_math::vec3;45//! # let wobble_curve = FunctionCurve::new(46//! # Interval::UNIT,47//! # |t| { vec3(t.cos(), 0.0, 0.0) },48//! # );49//! # let wobble_animation = AnimatableCurve::new(animated_field!(Transform::translation), wobble_curve);50//! # let animation_target_id = AnimationTargetId::from(&Name::new("Test"));51//! let mut animation_clip = AnimationClip::default();52//! animation_clip.add_curve_to_target(53//! animation_target_id,54//! wobble_animation,55//! );56//!57//! # Making animation curves58//!59//! The overview showed one example, but in general there are a few different ways of going from60//! a [`Curve`], which produces time-related data of some kind, to an [`AnimationCurve`], which61//! knows how to apply that data to an entity.62//!63//! ## Animated Fields64//!65//! The [`animated_field`] macro (which returns an [`AnimatedField`]), in combination with [`AnimatableCurve`]66//! is the easiest way to make an animation curve (see the example above).67//!68//! This will select a field on a component and pass it to a [`Curve`] with a type that matches the field.69//!70//! ## Animatable Properties71//!72//! Animation of arbitrary aspects of entities can be accomplished using [`AnimatableProperty`] in73//! conjunction with [`AnimatableCurve`]. See the documentation [there] for details.74//!75//! ## Custom [`AnimationCurve`] and [`AnimationCurveEvaluator`]76//!77//! This is the lowest-level option with the most control, but it is also the most complicated.78//!79//! [using a function]: bevy_math::curve::FunctionCurve80//! [translation component of a `Transform`]: bevy_transform::prelude::Transform::translation81//! [`AnimationClip`]: crate::AnimationClip82//! [there]: AnimatableProperty83//! [`animated_field`]: crate::animated_field8485use core::{86any::TypeId,87fmt::{self, Debug, Formatter},88marker::PhantomData,89};9091use crate::{92graph::AnimationNodeIndex,93prelude::{Animatable, BlendInput},94AnimationEntityMut, AnimationEvaluationError,95};96use bevy_ecs::component::{Component, Mutable};97use bevy_math::curve::{98cores::{UnevenCore, UnevenCoreError},99iterable::IterableCurve,100Curve, Interval,101};102use bevy_mesh::morph::MorphWeights;103use bevy_platform::hash::Hashed;104use bevy_reflect::{FromReflect, Reflect, Reflectable, TypeInfo, Typed};105use downcast_rs::{impl_downcast, Downcast};106107/// A trait for exposing a value in an entity so that it can be animated.108///109/// `AnimatableProperty` allows any value contained in an entity to be animated110/// as long as it can be obtained by mutable reference. This makes it more111/// flexible than [`animated_field`].112///113/// [`animated_field`]: crate::animated_field114///115/// Here, `AnimatableProperty` is used to animate a value inside an `Option`,116/// returning an error if the option is `None`.117///118/// # use bevy_animation::{prelude::AnimatableProperty, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId};119/// # use bevy_ecs::component::Component;120/// # use std::any::TypeId;121/// #[derive(Component)]122/// struct ExampleComponent {123/// power_level: Option<f32>124/// }125///126/// #[derive(Clone)]127/// struct PowerLevelProperty;128///129/// impl AnimatableProperty for PowerLevelProperty {130/// type Property = f32;131/// fn get_mut<'a>(132/// &self,133/// entity: &'a mut AnimationEntityMut134/// ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {135/// let component = entity136/// .get_mut::<ExampleComponent>()137/// .ok_or(AnimationEvaluationError::ComponentNotPresent(138/// TypeId::of::<ExampleComponent>()139/// ))?140/// .into_inner();141/// component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(142/// TypeId::of::<Option<f32>>()143/// ))144/// }145///146/// fn evaluator_id(&self) -> EvaluatorId {147/// EvaluatorId::Type(TypeId::of::<Self>())148/// }149/// }150///151///152/// You can then create an [`AnimatableCurve`] to animate this property like so:153///154/// # use bevy_animation::{VariableCurve, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId};155/// # use bevy_animation::prelude::{AnimatableProperty, AnimatableKeyframeCurve, AnimatableCurve};156/// # use bevy_ecs::{name::Name, component::Component};157/// # use std::any::TypeId;158/// # #[derive(Component)]159/// # struct ExampleComponent { power_level: Option<f32> }160/// # #[derive(Clone)]161/// # struct PowerLevelProperty;162/// # impl AnimatableProperty for PowerLevelProperty {163/// # type Property = f32;164/// # fn get_mut<'a>(165/// # &self,166/// # entity: &'a mut AnimationEntityMut167/// # ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {168/// # let component = entity169/// # .get_mut::<ExampleComponent>()170/// # .ok_or(AnimationEvaluationError::ComponentNotPresent(171/// # TypeId::of::<ExampleComponent>()172/// # ))?173/// # .into_inner();174/// # component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(175/// # TypeId::of::<Option<f32>>()176/// # ))177/// # }178/// # fn evaluator_id(&self) -> EvaluatorId {179/// # EvaluatorId::Type(TypeId::of::<Self>())180/// # }181/// # }182/// AnimatableCurve::new(183/// PowerLevelProperty,184/// AnimatableKeyframeCurve::new([185/// (0.0, 0.0),186/// (1.0, 9001.0),187/// ]).expect("Failed to create power level curve")188/// );189pub trait AnimatableProperty: Send + Sync + 'static {190/// The animated property type.191type Property: Animatable;192193/// Retrieves the property from the given `entity`.194fn get_mut<'a>(195&self,196entity: &'a mut AnimationEntityMut,197) -> Result<&'a mut Self::Property, AnimationEvaluationError>;198199/// The [`EvaluatorId`] used to look up the [`AnimationCurveEvaluator`] for this [`AnimatableProperty`].200/// For a given animated property, this ID should always be the same to allow things like animation blending to occur.201fn evaluator_id(&self) -> EvaluatorId<'_>;202}203204/// A [`Component`] field that can be animated, defined by a function that reads the component and returns205/// the accessed field / property.206///207/// The best way to create an instance of this type is via the [`animated_field`] macro.208///209/// `C` is the component being animated, `A` is the type of the [`Animatable`] field on the component, and `F` is an accessor210/// function that accepts a reference to `C` and retrieves the field `A`.211///212/// [`animated_field`]: crate::animated_field213#[derive(Clone)]214pub struct AnimatedField<C, A, F: Fn(&mut C) -> &mut A> {215func: F,216/// A pre-hashed (component-type-id, reflected-field-index) pair, uniquely identifying a component field217evaluator_id: Hashed<(TypeId, usize)>,218marker: PhantomData<(C, A)>,219}220221impl<C, A, F> AnimatableProperty for AnimatedField<C, A, F>222where223C: Component<Mutability = Mutable>,224A: Animatable + Clone + Sync + Debug,225F: Fn(&mut C) -> &mut A + Send + Sync + 'static,226{227type Property = A;228fn get_mut<'a>(229&self,230entity: &'a mut AnimationEntityMut,231) -> Result<&'a mut A, AnimationEvaluationError> {232let c = entity233.get_mut::<C>()234.ok_or_else(|| AnimationEvaluationError::ComponentNotPresent(TypeId::of::<C>()))?;235Ok((self.func)(c.into_inner()))236}237238fn evaluator_id(&self) -> EvaluatorId<'_> {239EvaluatorId::ComponentField(&self.evaluator_id)240}241}242243impl<C: Typed, P, F: Fn(&mut C) -> &mut P + 'static> AnimatedField<C, P, F> {244/// Creates a new instance of [`AnimatedField`]. This operates under the assumption that245/// `C` is a reflect-able struct, and that `field_name` is a valid field on that struct.246///247/// # Panics248/// If the type of `C` is not a struct or if the `field_name` does not exist.249pub fn new_unchecked(field_name: &str, func: F) -> Self {250let field_index;251if let TypeInfo::Struct(struct_info) = C::type_info() {252field_index = struct_info253.index_of(field_name)254.expect("Field name should exist");255} else if let TypeInfo::TupleStruct(struct_info) = C::type_info() {256field_index = field_name257.parse()258.expect("Field name should be a valid tuple index");259if field_index >= struct_info.field_len() {260panic!("Field name should be a valid tuple index");261}262} else {263panic!("Only structs are supported in `AnimatedField::new_unchecked`")264}265266Self {267func,268evaluator_id: Hashed::new((TypeId::of::<C>(), field_index)),269marker: PhantomData,270}271}272}273274/// This trait collects the additional requirements on top of [`Curve<T>`] needed for a275/// curve to be used as an [`AnimationCurve`].276pub trait AnimationCompatibleCurve<T>: Curve<T> + Debug + Clone + Reflectable {}277278impl<T, C> AnimationCompatibleCurve<T> for C where C: Curve<T> + Debug + Clone + Reflectable {}279280/// This type allows the conversion of a [curve] valued in the [property type] of an281/// [`AnimatableProperty`] into an [`AnimationCurve`] which animates that property.282///283/// [curve]: Curve284/// [property type]: AnimatableProperty::Property285#[derive(Reflect, FromReflect)]286#[reflect(from_reflect = false)]287pub struct AnimatableCurve<P, C> {288/// The property selector, which defines what component to access and how to access289/// a property on that component.290pub property: P,291292/// The inner [curve] whose values are used to animate the property.293///294/// [curve]: Curve295pub curve: C,296}297298/// An [`AnimatableCurveEvaluator`] for [`AnimatableProperty`] instances.299///300/// You shouldn't ordinarily need to instantiate one of these manually. Bevy301/// will automatically do so when you use an [`AnimatableCurve`] instance.302#[derive(Reflect)]303pub struct AnimatableCurveEvaluator<A: Animatable> {304evaluator: BasicAnimationCurveEvaluator<A>,305property: Box<dyn AnimatableProperty<Property = A>>,306}307308impl<P, C> AnimatableCurve<P, C>309where310P: AnimatableProperty,311C: AnimationCompatibleCurve<P::Property>,312{313/// Create an [`AnimatableCurve`] (and thus an [`AnimationCurve`]) from a curve314/// valued in an [animatable property].315///316/// [animatable property]: AnimatableProperty::Property317pub fn new(property: P, curve: C) -> Self {318Self { property, curve }319}320}321322impl<P, C> Clone for AnimatableCurve<P, C>323where324C: Clone,325P: Clone,326{327fn clone(&self) -> Self {328Self {329curve: self.curve.clone(),330property: self.property.clone(),331}332}333}334335impl<P, C> Debug for AnimatableCurve<P, C>336where337C: Debug,338{339fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {340f.debug_struct("AnimatableCurve")341.field("curve", &self.curve)342.finish()343}344}345346impl<P: Send + Sync + 'static, C> AnimationCurve for AnimatableCurve<P, C>347where348P: AnimatableProperty + Clone,349C: AnimationCompatibleCurve<P::Property> + Clone,350{351fn clone_value(&self) -> Box<dyn AnimationCurve> {352Box::new(self.clone())353}354355fn domain(&self) -> Interval {356self.curve.domain()357}358359fn evaluator_id(&self) -> EvaluatorId<'_> {360self.property.evaluator_id()361}362363fn create_evaluator(&self) -> Box<dyn AnimationCurveEvaluator> {364Box::new(AnimatableCurveEvaluator::<P::Property> {365evaluator: BasicAnimationCurveEvaluator::default(),366property: Box::new(self.property.clone()),367})368}369370fn apply(371&self,372curve_evaluator: &mut dyn AnimationCurveEvaluator,373t: f32,374weight: f32,375graph_node: AnimationNodeIndex,376) -> Result<(), AnimationEvaluationError> {377let curve_evaluator = curve_evaluator378.downcast_mut::<AnimatableCurveEvaluator<P::Property>>()379.unwrap();380let value = self.curve.sample_clamped(t);381curve_evaluator382.evaluator383.stack384.push(BasicAnimationCurveEvaluatorStackElement {385value,386weight,387graph_node,388});389Ok(())390}391}392393impl<A: Animatable> AnimationCurveEvaluator for AnimatableCurveEvaluator<A> {394fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {395self.evaluator.combine(graph_node, /*additive=*/ false)396}397398fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {399self.evaluator.combine(graph_node, /*additive=*/ true)400}401402fn push_blend_register(403&mut self,404weight: f32,405graph_node: AnimationNodeIndex,406) -> Result<(), AnimationEvaluationError> {407self.evaluator.push_blend_register(weight, graph_node)408}409410fn commit(&mut self, mut entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError> {411let property = self.property.get_mut(&mut entity)?;412*property = self413.evaluator414.stack415.pop()416.ok_or_else(inconsistent::<AnimatableCurveEvaluator<A>>)?417.value;418Ok(())419}420}421422/// This type allows an [`IterableCurve`] valued in `f32` to be used as an [`AnimationCurve`]423/// that animates [morph weights].424///425/// [morph weights]: MorphWeights426#[derive(Debug, Clone, Reflect, FromReflect)]427#[reflect(from_reflect = false)]428pub struct WeightsCurve<C>(pub C);429430#[derive(Reflect)]431struct WeightsCurveEvaluator {432/// The values of the stack, in which each element is a list of morph target433/// weights.434///435/// The stack elements are concatenated and tightly packed together.436///437/// The number of elements in this stack will always be a multiple of438/// [`Self::morph_target_count`].439stack_morph_target_weights: Vec<f32>,440441/// The blend weights and graph node indices for each element of the stack.442///443/// This should have as many elements as there are stack nodes. In other444/// words, `Self::stack_morph_target_weights.len() *445/// Self::morph_target_counts as usize ==446/// Self::stack_blend_weights_and_graph_nodes`.447stack_blend_weights_and_graph_nodes: Vec<(f32, AnimationNodeIndex)>,448449/// The morph target weights in the blend register, if any.450///451/// This field should be ignored if [`Self::blend_register_blend_weight`] is452/// `None`. If non-empty, it will always have [`Self::morph_target_count`]453/// elements in it.454blend_register_morph_target_weights: Vec<f32>,455456/// The weight in the blend register.457///458/// This will be `None` if the blend register is empty. In that case,459/// [`Self::blend_register_morph_target_weights`] will be empty.460blend_register_blend_weight: Option<f32>,461462/// The number of morph targets that are to be animated.463morph_target_count: Option<u32>,464}465466impl<C> AnimationCurve for WeightsCurve<C>467where468C: IterableCurve<f32> + Debug + Clone + Reflectable,469{470fn clone_value(&self) -> Box<dyn AnimationCurve> {471Box::new(self.clone())472}473474fn domain(&self) -> Interval {475self.0.domain()476}477478fn evaluator_id(&self) -> EvaluatorId<'_> {479EvaluatorId::Type(TypeId::of::<WeightsCurveEvaluator>())480}481482fn create_evaluator(&self) -> Box<dyn AnimationCurveEvaluator> {483Box::new(WeightsCurveEvaluator {484stack_morph_target_weights: vec![],485stack_blend_weights_and_graph_nodes: vec![],486blend_register_morph_target_weights: vec![],487blend_register_blend_weight: None,488morph_target_count: None,489})490}491492fn apply(493&self,494curve_evaluator: &mut dyn AnimationCurveEvaluator,495t: f32,496weight: f32,497graph_node: AnimationNodeIndex,498) -> Result<(), AnimationEvaluationError> {499let curve_evaluator = curve_evaluator500.downcast_mut::<WeightsCurveEvaluator>()501.unwrap();502503let prev_morph_target_weights_len = curve_evaluator.stack_morph_target_weights.len();504curve_evaluator505.stack_morph_target_weights506.extend(self.0.sample_iter_clamped(t));507curve_evaluator.morph_target_count = Some(508(curve_evaluator.stack_morph_target_weights.len() - prev_morph_target_weights_len)509as u32,510);511512curve_evaluator513.stack_blend_weights_and_graph_nodes514.push((weight, graph_node));515Ok(())516}517}518519impl WeightsCurveEvaluator {520fn combine(521&mut self,522graph_node: AnimationNodeIndex,523additive: bool,524) -> Result<(), AnimationEvaluationError> {525let Some(&(_, top_graph_node)) = self.stack_blend_weights_and_graph_nodes.last() else {526return Ok(());527};528if top_graph_node != graph_node {529return Ok(());530}531532let (weight_to_blend, _) = self.stack_blend_weights_and_graph_nodes.pop().unwrap();533let stack_iter = self.stack_morph_target_weights.drain(534(self.stack_morph_target_weights.len() - self.morph_target_count.unwrap() as usize)..,535);536537match self.blend_register_blend_weight {538None => {539self.blend_register_blend_weight = Some(weight_to_blend);540self.blend_register_morph_target_weights.clear();541542// In the additive case, the values pushed onto the blend register need543// to be scaled by the weight.544if additive {545self.blend_register_morph_target_weights546.extend(stack_iter.map(|m| m * weight_to_blend));547} else {548self.blend_register_morph_target_weights.extend(stack_iter);549}550}551552Some(ref mut current_weight) => {553*current_weight += weight_to_blend;554for (dest, src) in self555.blend_register_morph_target_weights556.iter_mut()557.zip(stack_iter)558{559if additive {560*dest += src * weight_to_blend;561} else {562*dest = f32::interpolate(dest, &src, weight_to_blend / *current_weight);563}564}565}566}567568Ok(())569}570}571572impl AnimationCurveEvaluator for WeightsCurveEvaluator {573fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {574self.combine(graph_node, /*additive=*/ false)575}576577fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {578self.combine(graph_node, /*additive=*/ true)579}580581fn push_blend_register(582&mut self,583weight: f32,584graph_node: AnimationNodeIndex,585) -> Result<(), AnimationEvaluationError> {586if self.blend_register_blend_weight.take().is_some() {587self.stack_morph_target_weights588.append(&mut self.blend_register_morph_target_weights);589self.stack_blend_weights_and_graph_nodes590.push((weight, graph_node));591}592Ok(())593}594595fn commit(&mut self, mut entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError> {596if self.stack_morph_target_weights.is_empty() {597return Ok(());598}599600// Compute the index of the first morph target in the last element of601// the stack.602let index_of_first_morph_target =603self.stack_morph_target_weights.len() - self.morph_target_count.unwrap() as usize;604605for (dest, src) in entity606.get_mut::<MorphWeights>()607.ok_or_else(|| {608AnimationEvaluationError::ComponentNotPresent(TypeId::of::<MorphWeights>())609})?610.weights_mut()611.iter_mut()612.zip(self.stack_morph_target_weights[index_of_first_morph_target..].iter())613{614*dest = *src;615}616self.stack_morph_target_weights.clear();617self.stack_blend_weights_and_graph_nodes.clear();618Ok(())619}620}621622#[derive(Reflect)]623struct BasicAnimationCurveEvaluator<A>624where625A: Animatable,626{627stack: Vec<BasicAnimationCurveEvaluatorStackElement<A>>,628blend_register: Option<(A, f32)>,629}630631#[derive(Reflect)]632struct BasicAnimationCurveEvaluatorStackElement<A>633where634A: Animatable,635{636value: A,637weight: f32,638graph_node: AnimationNodeIndex,639}640641impl<A> Default for BasicAnimationCurveEvaluator<A>642where643A: Animatable,644{645fn default() -> Self {646BasicAnimationCurveEvaluator {647stack: vec![],648blend_register: None,649}650}651}652653impl<A> BasicAnimationCurveEvaluator<A>654where655A: Animatable,656{657fn combine(658&mut self,659graph_node: AnimationNodeIndex,660additive: bool,661) -> Result<(), AnimationEvaluationError> {662let Some(top) = self.stack.last() else {663return Ok(());664};665if top.graph_node != graph_node {666return Ok(());667}668669let BasicAnimationCurveEvaluatorStackElement {670value: value_to_blend,671weight: weight_to_blend,672graph_node: _,673} = self.stack.pop().unwrap();674675match self.blend_register.take() {676None => {677self.initialize_blend_register(value_to_blend, weight_to_blend, additive);678}679Some((mut current_value, mut current_weight)) => {680current_weight += weight_to_blend;681682if additive {683current_value = A::blend(684[685BlendInput {686weight: 1.0,687value: current_value,688additive: true,689},690BlendInput {691weight: weight_to_blend,692value: value_to_blend,693additive: true,694},695]696.into_iter(),697);698} else {699current_value = A::interpolate(700¤t_value,701&value_to_blend,702weight_to_blend / current_weight,703);704}705706self.blend_register = Some((current_value, current_weight));707}708}709710Ok(())711}712713fn initialize_blend_register(&mut self, value: A, weight: f32, additive: bool) {714if additive {715let scaled_value = A::blend(716[BlendInput {717weight,718value,719additive: true,720}]721.into_iter(),722);723self.blend_register = Some((scaled_value, weight));724} else {725self.blend_register = Some((value, weight));726}727}728729fn push_blend_register(730&mut self,731weight: f32,732graph_node: AnimationNodeIndex,733) -> Result<(), AnimationEvaluationError> {734if let Some((value, _)) = self.blend_register.take() {735self.stack.push(BasicAnimationCurveEvaluatorStackElement {736value,737weight,738graph_node,739});740}741Ok(())742}743}744745/// A low-level trait that provides control over how curves are actually applied746/// to entities by the animation system.747///748/// Typically, this will not need to be implemented manually, since it is749/// automatically implemented by [`AnimatableCurve`] and other curves used by750/// the animation system (e.g. those that animate parts of transforms or morph751/// weights). However, this can be implemented manually when `AnimatableCurve`752/// is not sufficiently expressive.753///754/// In many respects, this behaves like a type-erased form of [`Curve`], where755/// the output type of the curve is remembered only in the components that are756/// mutated in the implementation of [`apply`].757///758/// [`apply`]: AnimationCurve::apply759pub trait AnimationCurve: Debug + Send + Sync + 'static {760/// Returns a boxed clone of this value.761fn clone_value(&self) -> Box<dyn AnimationCurve>;762763/// The range of times for which this animation is defined.764fn domain(&self) -> Interval;765766/// Returns the type ID of the [`AnimationCurveEvaluator`].767///768/// This must match the type returned by [`Self::create_evaluator`]. It must769/// be a single type that doesn't depend on the type of the curve.770fn evaluator_id(&self) -> EvaluatorId<'_>;771772/// Returns a newly-instantiated [`AnimationCurveEvaluator`] for use with773/// this curve.774///775/// All curve types must return the same type of776/// [`AnimationCurveEvaluator`]. The returned value must match the type777/// returned by [`Self::evaluator_id`].778fn create_evaluator(&self) -> Box<dyn AnimationCurveEvaluator>;779780/// Samples the curve at the given time `t`, and pushes the sampled value781/// onto the evaluation stack of the `curve_evaluator`.782///783/// The `curve_evaluator` parameter points to the value returned by784/// [`Self::create_evaluator`], upcast to an `&mut dyn785/// AnimationCurveEvaluator`. Typically, implementations of [`Self::apply`]786/// will want to downcast the `curve_evaluator` parameter to the concrete787/// type [`Self::evaluator_id`] in order to push values of the appropriate788/// type onto its evaluation stack.789///790/// Be sure not to confuse the `t` and `weight` values. The former791/// determines the position at which the *curve* is sampled, while `weight`792/// ultimately determines how much the *stack values* will be blended793/// together (see the definition of [`AnimationCurveEvaluator::blend`]).794fn apply(795&self,796curve_evaluator: &mut dyn AnimationCurveEvaluator,797t: f32,798weight: f32,799graph_node: AnimationNodeIndex,800) -> Result<(), AnimationEvaluationError>;801}802803/// The [`EvaluatorId`] is used to look up the [`AnimationCurveEvaluator`] for an [`AnimatableProperty`].804/// For a given animated property, this ID should always be the same to allow things like animation blending to occur.805#[derive(Clone)]806pub enum EvaluatorId<'a> {807/// Corresponds to a specific field on a specific component type.808/// The `TypeId` should correspond to the component type, and the `usize`809/// should correspond to the Reflect-ed field index of the field.810//811// IMPLEMENTATION NOTE: The Hashed<(TypeId, usize) is intentionally cheap to clone, as it will be cloned per frame by the evaluator812// Switching the field index `usize` for something like a field name `String` would probably be too expensive to justify813ComponentField(&'a Hashed<(TypeId, usize)>),814/// Corresponds to a custom property of a given type. This should be the [`TypeId`]815/// of the custom [`AnimatableProperty`].816Type(TypeId),817}818819/// A low-level trait for use in [`VariableCurve`](`crate::VariableCurve`) that provides fine820/// control over how animations are evaluated.821///822/// You can implement this trait when the generic [`AnimatableCurveEvaluator`]823/// isn't sufficiently-expressive for your needs. For example, [`MorphWeights`]824/// implements this trait instead of using [`AnimatableCurveEvaluator`] because825/// it needs to animate arbitrarily many weights at once, which can't be done826/// with [`Animatable`] as that works on fixed-size values only.827///828/// If you implement this trait, you should also implement [`AnimationCurve`] on829/// your curve type, as that trait allows creating instances of this one.830///831/// Implementations of [`AnimatableCurveEvaluator`] should maintain a *stack* of832/// (value, weight, node index) triples, as well as a *blend register*, which is833/// either a (value, weight) pair or empty. *Value* here refers to an instance834/// of the value being animated: for example, [`Vec3`] in the case of835/// translation keyframes. The stack stores intermediate values generated while836/// evaluating the [`AnimationGraph`](`crate::graph::AnimationGraph`), while the blend register837/// stores the result of a blend operation.838///839/// [`Vec3`]: bevy_math::Vec3840pub trait AnimationCurveEvaluator: Downcast + Send + Sync + 'static {841/// Blends the top element of the stack with the blend register.842///843/// The semantics of this method are as follows:844///845/// 1. Pop the top element of the stack. Call its value vₘ and its weight846/// wₘ. If the stack was empty, return success.847///848/// 2. If the blend register is empty, set the blend register value to vₘ849/// and the blend register weight to wₘ; then, return success.850///851/// 3. If the blend register is nonempty, call its current value vₙ and its852/// current weight wₙ. Then, set the value of the blend register to853/// `interpolate(vₙ, vₘ, wₘ / (wₘ + wₙ))`, and set the weight of the blend854/// register to wₘ + wₙ.855///856/// 4. Return success.857fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;858859/// Additively blends the top element of the stack with the blend register.860///861/// The semantics of this method are as follows:862///863/// 1. Pop the top element of the stack. Call its value vₘ and its weight864/// wₘ. If the stack was empty, return success.865///866/// 2. If the blend register is empty, set the blend register value to vₘ867/// and the blend register weight to wₘ; then, return success.868///869/// 3. If the blend register is nonempty, call its current value vₙ.870/// Then, set the value of the blend register to vₙ + vₘwₘ.871///872/// 4. Return success.873fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;874875/// Pushes the current value of the blend register onto the stack.876///877/// If the blend register is empty, this method does nothing successfully.878/// Otherwise, this method pushes the current value of the blend register879/// onto the stack, alongside the weight and graph node supplied to this880/// function. The weight present in the blend register is discarded; only881/// the weight parameter to this function is pushed onto the stack. The882/// blend register is emptied after this process.883fn push_blend_register(884&mut self,885weight: f32,886graph_node: AnimationNodeIndex,887) -> Result<(), AnimationEvaluationError>;888889/// Pops the top value off the stack and writes it into the appropriate890/// component.891///892/// If the stack is empty, this method does nothing successfully. Otherwise,893/// it pops the top value off the stack, fetches the associated component894/// from either the `transform` or `entity` values as appropriate, and895/// updates the appropriate property with the value popped from the stack.896/// The weight and node index associated with the popped stack element are897/// discarded. After doing this, the stack is emptied.898///899/// The property on the component must be overwritten with the value from900/// the stack, not blended with it.901fn commit(&mut self, entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError>;902}903904impl_downcast!(AnimationCurveEvaluator);905906/// A [curve] defined by keyframes with values in an [animatable] type.907///908/// The keyframes are interpolated using the type's [`Animatable::interpolate`] implementation.909///910/// [curve]: Curve911/// [animatable]: Animatable912#[derive(Debug, Clone, Reflect)]913pub struct AnimatableKeyframeCurve<T> {914core: UnevenCore<T>,915}916917impl<T> Curve<T> for AnimatableKeyframeCurve<T>918where919T: Animatable + Clone,920{921#[inline]922fn domain(&self) -> Interval {923self.core.domain()924}925926#[inline]927fn sample_clamped(&self, t: f32) -> T {928// `UnevenCore::sample_with` is implicitly clamped.929self.core.sample_with(t, <T as Animatable>::interpolate)930}931932#[inline]933fn sample_unchecked(&self, t: f32) -> T {934self.sample_clamped(t)935}936}937938impl<T> AnimatableKeyframeCurve<T>939where940T: Animatable,941{942/// Create a new [`AnimatableKeyframeCurve`] from the given `keyframes`. The values of this943/// curve are interpolated from the keyframes using the output type's implementation of944/// [`Animatable::interpolate`].945///946/// There must be at least two samples in order for this method to succeed.947pub fn new(keyframes: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {948Ok(Self {949core: UnevenCore::new(keyframes)?,950})951}952}953954fn inconsistent<P>() -> AnimationEvaluationError955where956P: 'static + ?Sized,957{958AnimationEvaluationError::InconsistentEvaluatorImplementation(TypeId::of::<P>())959}960961/// Returns an [`AnimatedField`] with a given `$component` and `$field`.962///963/// This can be used in the following way:964///965/// ```966/// # use bevy_animation::{animation_curves::AnimatedField, animated_field};967/// # use bevy_color::Srgba;968/// # use bevy_ecs::component::Component;969/// # use bevy_math::Vec3;970/// # use bevy_reflect::Reflect;971/// #[derive(Component, Reflect)]972/// struct Transform {973/// translation: Vec3,974/// }975///976/// let field = animated_field!(Transform::translation);977///978/// #[derive(Component, Reflect)]979/// struct Color(Srgba);980///981/// let tuple_field = animated_field!(Color::0);982/// ```983#[macro_export]984macro_rules! animated_field {985($component:ident::$field:tt) => {986AnimatedField::new_unchecked(stringify!($field), |component: &mut $component| {987&mut component.$field988})989};990}991992#[cfg(test)]993mod tests {994use super::*;995996#[test]997fn test_animated_field_tuple_struct_simple_uses() {998#[derive(Clone, Debug, Component, Reflect)]999struct A(f32);1000let _ = AnimatedField::new_unchecked("0", |a: &mut A| &mut a.0);10011002#[derive(Clone, Debug, Component, Reflect)]1003struct B(f32, f64, f32);1004let _ = AnimatedField::new_unchecked("0", |b: &mut B| &mut b.0);1005let _ = AnimatedField::new_unchecked("1", |b: &mut B| &mut b.1);1006let _ = AnimatedField::new_unchecked("2", |b: &mut B| &mut b.2);1007}1008}100910101011