Path: blob/main/crates/bevy_animation/src/animation_curves.rs
9441 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};9091#[cfg(feature = "bevy_mesh")]92pub use crate::morph::*;93use crate::{94graph::AnimationNodeIndex,95prelude::{Animatable, BlendInput},96AnimationEntityMut, AnimationEvaluationError,97};98use bevy_ecs::component::{Component, Mutable};99use bevy_math::curve::{100cores::{UnevenCore, UnevenCoreError},101Curve, Interval,102};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;418self.evaluator.stack.clear();419Ok(())420}421}422423#[derive(Reflect)]424struct BasicAnimationCurveEvaluator<A>425where426A: Animatable,427{428stack: Vec<BasicAnimationCurveEvaluatorStackElement<A>>,429blend_register: Option<(A, f32)>,430}431432#[derive(Reflect)]433struct BasicAnimationCurveEvaluatorStackElement<A>434where435A: Animatable,436{437value: A,438weight: f32,439graph_node: AnimationNodeIndex,440}441442impl<A> Default for BasicAnimationCurveEvaluator<A>443where444A: Animatable,445{446fn default() -> Self {447BasicAnimationCurveEvaluator {448stack: vec![],449blend_register: None,450}451}452}453454impl<A> BasicAnimationCurveEvaluator<A>455where456A: Animatable,457{458fn combine(459&mut self,460graph_node: AnimationNodeIndex,461additive: bool,462) -> Result<(), AnimationEvaluationError> {463let Some(top) = self.stack.last() else {464return Ok(());465};466if top.graph_node != graph_node {467return Ok(());468}469470let BasicAnimationCurveEvaluatorStackElement {471value: value_to_blend,472weight: weight_to_blend,473graph_node: _,474} = self.stack.pop().unwrap();475476match self.blend_register.take() {477None => {478self.initialize_blend_register(value_to_blend, weight_to_blend, additive);479}480Some((mut current_value, mut current_weight)) => {481current_weight += weight_to_blend;482483if additive {484current_value = A::blend(485[486BlendInput {487weight: 1.0,488value: current_value,489additive: true,490},491BlendInput {492weight: weight_to_blend,493value: value_to_blend,494additive: true,495},496]497.into_iter(),498);499} else {500current_value = A::interpolate(501¤t_value,502&value_to_blend,503weight_to_blend / current_weight,504);505}506507self.blend_register = Some((current_value, current_weight));508}509}510511Ok(())512}513514fn initialize_blend_register(&mut self, value: A, weight: f32, additive: bool) {515if additive {516let scaled_value = A::blend(517[BlendInput {518weight,519value,520additive: true,521}]522.into_iter(),523);524self.blend_register = Some((scaled_value, weight));525} else {526self.blend_register = Some((value, weight));527}528}529530fn push_blend_register(531&mut self,532weight: f32,533graph_node: AnimationNodeIndex,534) -> Result<(), AnimationEvaluationError> {535if let Some((value, _)) = self.blend_register.take() {536self.stack.push(BasicAnimationCurveEvaluatorStackElement {537value,538weight,539graph_node,540});541}542Ok(())543}544}545546/// A low-level trait that provides control over how curves are actually applied547/// to entities by the animation system.548///549/// Typically, this will not need to be implemented manually, since it is550/// automatically implemented by [`AnimatableCurve`] and other curves used by551/// the animation system (e.g. those that animate parts of transforms or morph552/// weights). However, this can be implemented manually when `AnimatableCurve`553/// is not sufficiently expressive.554///555/// In many respects, this behaves like a type-erased form of [`Curve`], where556/// the output type of the curve is remembered only in the components that are557/// mutated in the implementation of [`apply`].558///559/// [`apply`]: AnimationCurve::apply560pub trait AnimationCurve: Debug + Send + Sync + 'static {561/// Returns a boxed clone of this value.562fn clone_value(&self) -> Box<dyn AnimationCurve>;563564/// The range of times for which this animation is defined.565fn domain(&self) -> Interval;566567/// Returns the type ID of the [`AnimationCurveEvaluator`].568///569/// This must match the type returned by [`Self::create_evaluator`]. It must570/// be a single type that doesn't depend on the type of the curve.571fn evaluator_id(&self) -> EvaluatorId<'_>;572573/// Returns a newly-instantiated [`AnimationCurveEvaluator`] for use with574/// this curve.575///576/// All curve types must return the same type of577/// [`AnimationCurveEvaluator`]. The returned value must match the type578/// returned by [`Self::evaluator_id`].579fn create_evaluator(&self) -> Box<dyn AnimationCurveEvaluator>;580581/// Samples the curve at the given time `t`, and pushes the sampled value582/// onto the evaluation stack of the `curve_evaluator`.583///584/// The `curve_evaluator` parameter points to the value returned by585/// [`Self::create_evaluator`], upcast to an `&mut dyn586/// AnimationCurveEvaluator`. Typically, implementations of [`Self::apply`]587/// will want to downcast the `curve_evaluator` parameter to the concrete588/// type [`Self::evaluator_id`] in order to push values of the appropriate589/// type onto its evaluation stack.590///591/// Be sure not to confuse the `t` and `weight` values. The former592/// determines the position at which the *curve* is sampled, while `weight`593/// ultimately determines how much the *stack values* will be blended594/// together (see the definition of [`AnimationCurveEvaluator::blend`]).595fn apply(596&self,597curve_evaluator: &mut dyn AnimationCurveEvaluator,598t: f32,599weight: f32,600graph_node: AnimationNodeIndex,601) -> Result<(), AnimationEvaluationError>;602}603604/// The [`EvaluatorId`] is used to look up the [`AnimationCurveEvaluator`] for an [`AnimatableProperty`].605/// For a given animated property, this ID should always be the same to allow things like animation blending to occur.606#[derive(Clone)]607pub enum EvaluatorId<'a> {608/// Corresponds to a specific field on a specific component type.609/// The `TypeId` should correspond to the component type, and the `usize`610/// should correspond to the Reflect-ed field index of the field.611//612// IMPLEMENTATION NOTE: The Hashed<(TypeId, usize) is intentionally cheap to clone, as it will be cloned per frame by the evaluator613// Switching the field index `usize` for something like a field name `String` would probably be too expensive to justify614ComponentField(&'a Hashed<(TypeId, usize)>),615/// Corresponds to a custom property of a given type. This should be the [`TypeId`]616/// of the custom [`AnimatableProperty`].617Type(TypeId),618}619620/// A low-level trait for use in [`VariableCurve`](`crate::VariableCurve`) that provides fine621/// control over how animations are evaluated.622///623/// You can implement this trait when the generic [`AnimatableCurveEvaluator`]624/// isn't sufficiently-expressive for your needs. For example, `MorphWeights`625/// implements this trait instead of using [`AnimatableCurveEvaluator`] because626/// it needs to animate arbitrarily many weights at once, which can't be done627/// with [`Animatable`] as that works on fixed-size values only.628///629/// If you implement this trait, you should also implement [`AnimationCurve`] on630/// your curve type, as that trait allows creating instances of this one.631///632/// Implementations of [`AnimatableCurveEvaluator`] should maintain a *stack* of633/// (value, weight, node index) triples, as well as a *blend register*, which is634/// either a (value, weight) pair or empty. *Value* here refers to an instance635/// of the value being animated: for example, [`Vec3`] in the case of636/// translation keyframes. The stack stores intermediate values generated while637/// evaluating the [`AnimationGraph`](`crate::graph::AnimationGraph`), while the blend register638/// stores the result of a blend operation.639///640/// [`Vec3`]: bevy_math::Vec3641pub trait AnimationCurveEvaluator: Downcast + Send + Sync + 'static {642/// Blends the top element of the stack with the blend register.643///644/// The semantics of this method are as follows:645///646/// 1. Pop the top element of the stack. Call its value vₘ and its weight647/// wₘ. If the stack was empty, return success.648///649/// 2. If the blend register is empty, set the blend register value to vₘ650/// and the blend register weight to wₘ; then, return success.651///652/// 3. If the blend register is nonempty, call its current value vₙ and its653/// current weight wₙ. Then, set the value of the blend register to654/// `interpolate(vₙ, vₘ, wₘ / (wₘ + wₙ))`, and set the weight of the blend655/// register to wₘ + wₙ.656///657/// 4. Return success.658fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;659660/// Additively blends the top element of the stack with the blend register.661///662/// The semantics of this method are as follows:663///664/// 1. Pop the top element of the stack. Call its value vₘ and its weight665/// wₘ. If the stack was empty, return success.666///667/// 2. If the blend register is empty, set the blend register value to vₘ668/// and the blend register weight to wₘ; then, return success.669///670/// 3. If the blend register is nonempty, call its current value vₙ.671/// Then, set the value of the blend register to vₙ + vₘwₘ.672///673/// 4. Return success.674fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;675676/// Pushes the current value of the blend register onto the stack.677///678/// If the blend register is empty, this method does nothing successfully.679/// Otherwise, this method pushes the current value of the blend register680/// onto the stack, alongside the weight and graph node supplied to this681/// function. The weight present in the blend register is discarded; only682/// the weight parameter to this function is pushed onto the stack. The683/// blend register is emptied after this process.684fn push_blend_register(685&mut self,686weight: f32,687graph_node: AnimationNodeIndex,688) -> Result<(), AnimationEvaluationError>;689690/// Pops the top value off the stack and writes it into the appropriate691/// component.692///693/// If the stack is empty, this method does nothing successfully. Otherwise,694/// it pops the top value off the stack, fetches the associated component695/// from either the `transform` or `entity` values as appropriate, and696/// updates the appropriate property with the value popped from the stack.697/// The weight and node index associated with the popped stack element are698/// discarded. After doing this, the stack is emptied.699///700/// The property on the component must be overwritten with the value from701/// the stack, not blended with it.702fn commit(&mut self, entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError>;703}704705impl_downcast!(AnimationCurveEvaluator);706707/// A [curve] defined by keyframes with values in an [animatable] type.708///709/// The keyframes are interpolated using the type's [`Animatable::interpolate`] implementation.710///711/// [curve]: Curve712/// [animatable]: Animatable713#[derive(Debug, Clone, Reflect)]714pub struct AnimatableKeyframeCurve<T> {715core: UnevenCore<T>,716}717718impl<T> Curve<T> for AnimatableKeyframeCurve<T>719where720T: Animatable + Clone,721{722#[inline]723fn domain(&self) -> Interval {724self.core.domain()725}726727#[inline]728fn sample_clamped(&self, t: f32) -> T {729// `UnevenCore::sample_with` is implicitly clamped.730self.core.sample_with(t, <T as Animatable>::interpolate)731}732733#[inline]734fn sample_unchecked(&self, t: f32) -> T {735self.sample_clamped(t)736}737}738739impl<T> AnimatableKeyframeCurve<T>740where741T: Animatable,742{743/// Create a new [`AnimatableKeyframeCurve`] from the given `keyframes`. The values of this744/// curve are interpolated from the keyframes using the output type's implementation of745/// [`Animatable::interpolate`].746///747/// There must be at least two samples in order for this method to succeed.748pub fn new(keyframes: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {749Ok(Self {750core: UnevenCore::new(keyframes)?,751})752}753}754755fn inconsistent<P>() -> AnimationEvaluationError756where757P: 'static + ?Sized,758{759AnimationEvaluationError::InconsistentEvaluatorImplementation(TypeId::of::<P>())760}761762/// Returns an [`AnimatedField`] with a given `$component` and `$field`.763///764/// This can be used in the following way:765///766/// ```767/// # use bevy_animation::{animation_curves::AnimatedField, animated_field};768/// # use bevy_color::Srgba;769/// # use bevy_ecs::component::Component;770/// # use bevy_math::Vec3;771/// # use bevy_reflect::Reflect;772/// #[derive(Component, Reflect)]773/// struct Transform {774/// translation: Vec3,775/// }776///777/// let field = animated_field!(Transform::translation);778///779/// #[derive(Component, Reflect)]780/// struct Color(Srgba);781///782/// let tuple_field = animated_field!(Color::0);783/// ```784#[macro_export]785macro_rules! animated_field {786($component:ident::$field:tt) => {787AnimatedField::new_unchecked(stringify!($field), |component: &mut $component| {788&mut component.$field789})790};791}792793#[cfg(test)]794mod tests {795use super::*;796797#[test]798fn test_animated_field_tuple_struct_simple_uses() {799#[derive(Clone, Debug, Component, Reflect)]800struct A(f32);801let _ = AnimatedField::new_unchecked("0", |a: &mut A| &mut a.0);802803#[derive(Clone, Debug, Component, Reflect)]804struct B(f32, f64, f32);805let _ = AnimatedField::new_unchecked("0", |b: &mut B| &mut b.0);806let _ = AnimatedField::new_unchecked("1", |b: &mut B| &mut b.1);807let _ = AnimatedField::new_unchecked("2", |b: &mut B| &mut b.2);808}809}810811812