//! The [`AnimationCurve`] trait and adaptors that allow curves to implement it. //! //! # Overview //! //! The flow of curves into the animation system generally begins with something that //! implements the [`Curve`] trait. Let's imagine, for example, that we have some //! `Curve` that we want to use to animate something. That could be defined in //! a number of different ways, but let's imagine that we've defined it [using a function]: //! //! # use bevy_math::curve::{Curve, Interval, FunctionCurve}; //! # use bevy_math::vec3; //! let wobble_curve = FunctionCurve::new( //! Interval::UNIT, //! |t| { vec3(t.cos(), 0.0, 0.0) }, //! ); //! //! Okay, so we have a curve, but the animation system also needs to know, in some way, //! how the values from this curve should actually be used. That is, it needs to know what //! to animate! That's what [`AnimationCurve`] is for. In particular, what we need to do //! is take our curve and turn it into an `AnimationCurve` which will be usable by the //! animation system. //! //! For instance, let's imagine that we want to use the `Vec3` output //! from our curve to animate the [translation component of a `Transform`]. For this, there is //! the adaptor [`TranslationCurve`], which wraps any `Curve` and turns it into an //! [`AnimationCurve`] that will use the given curve to animate the entity's translation: //! //! # use bevy_math::curve::{Curve, Interval, FunctionCurve}; //! # use bevy_math::vec3; //! # use bevy_animation::animation_curves::*; //! # let wobble_curve = FunctionCurve::new( //! # Interval::UNIT, //! # |t| vec3(t.cos(), 0.0, 0.0) //! # ); //! let wobble_animation = TranslationCurve(wobble_curve); //! //! And finally, this `AnimationCurve` needs to be added to an [`AnimationClip`] in order to //! actually animate something. This is what that looks like: //! //! # use bevy_math::curve::{Curve, Interval, FunctionCurve}; //! # use bevy_animation::{AnimationClip, AnimationTargetId, animation_curves::*}; //! # use bevy_core::Name; //! # use bevy_math::vec3; //! # let wobble_curve = FunctionCurve::new( //! # Interval::UNIT, //! # |t| { vec3(t.cos(), 0.0, 0.0) }, //! # ); //! # let wobble_animation = TranslationCurve(wobble_curve); //! # let animation_target_id = AnimationTargetId::from(&Name::new("Test")); //! let mut animation_clip = AnimationClip::default(); //! animation_clip.add_curve_to_target( //! animation_target_id, //! wobble_animation, //! ); //! //! # Making animation curves //! //! The overview showed one example, but in general there are a few different ways of going from //! a [`Curve`], which produces time-related data of some kind, to an [`AnimationCurve`], which //! knows how to apply that data to an entity. //! //! ## `Transform` //! //! [`Transform`] is special and has its own adaptors: //! - [`TranslationCurve`], which uses `Vec3` output to animate [`Transform::translation`] //! - [`RotationCurve`], which uses `Quat` output to animate [`Transform::rotation`] //! - [`ScaleCurve`], which uses `Vec3` output to animate [`Transform::scale`] //! //! ## Animatable properties //! //! Animation of arbitrary components can be accomplished using [`AnimatableProperty`] in //! conjunction with [`AnimatableCurve`]. See the documentation [there] for details. //! //! [using a function]: bevy_math::curve::FunctionCurve //! [translation component of a `Transform`]: bevy_transform::prelude::Transform::translation //! [`AnimationClip`]: crate::AnimationClip //! [there]: AnimatableProperty use core::{ any::TypeId, fmt::{self, Debug, Formatter}, marker::PhantomData, }; use bevy_ecs::{component::Component, world::Mut}; use bevy_math::{ curve::{ cores::{UnevenCore, UnevenCoreError}, iterable::IterableCurve, Curve, Interval, }, Quat, Vec3, }; use bevy_reflect::{FromReflect, Reflect, Reflectable, TypePath}; use bevy_render::mesh::morph::MorphWeights; use bevy_transform::prelude::Transform; use crate::{ graph::AnimationNodeIndex, prelude::{Animatable, BlendInput}, AnimationEntityMut, AnimationEvaluationError, }; /// A value on a component that Bevy can animate. /// /// You can implement this trait on a unit struct in order to support animating /// custom components other than transforms and morph weights. Use that type in /// conjunction with [`AnimatableCurve`] (and perhaps [`AnimatableKeyframeCurve`] /// to define the animation itself). /// For example, in order to animate field of view, you might use: /// /// # use bevy_animation::prelude::AnimatableProperty; /// # use bevy_reflect::Reflect; /// # use bevy_render::camera::PerspectiveProjection; /// #[derive(Reflect)] /// struct FieldOfViewProperty; /// /// impl AnimatableProperty for FieldOfViewProperty { /// type Component = PerspectiveProjection; /// type Property = f32; /// fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> { /// Some(&mut component.fov) /// } /// } /// /// You can then create an [`AnimationClip`] to animate this property like so: /// /// # use bevy_animation::{AnimationClip, AnimationTargetId, VariableCurve}; /// # use bevy_animation::prelude::{AnimatableProperty, AnimatableKeyframeCurve, AnimatableCurve}; /// # use bevy_core::Name; /// # use bevy_reflect::Reflect; /// # use bevy_render::camera::PerspectiveProjection; /// # let animation_target_id = AnimationTargetId::from(&Name::new("Test")); /// # #[derive(Reflect)] /// # struct FieldOfViewProperty; /// # impl AnimatableProperty for FieldOfViewProperty { /// # type Component = PerspectiveProjection; /// # type Property = f32; /// # fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> { /// # Some(&mut component.fov) /// # } /// # } /// let mut animation_clip = AnimationClip::default(); /// animation_clip.add_curve_to_target( /// animation_target_id, /// AnimatableKeyframeCurve::new( /// [ /// (0.0, core::f32::consts::PI / 4.0), /// (1.0, core::f32::consts::PI / 3.0), /// ] /// ) /// .map(AnimatableCurve::::from_curve) /// .expect("Failed to create font size curve") /// ); /// /// Here, the use of [`AnimatableKeyframeCurve`] creates a curve out of the given keyframe time-value /// pairs, using the [`Animatable`] implementation of `f32` to interpolate between them. The /// invocation of [`AnimatableCurve::from_curve`] with `FieldOfViewProperty` indicates that the `f32` /// output from that curve is to be used to animate the font size of a `PerspectiveProjection` component (as /// configured above). /// /// [`AnimationClip`]: crate::AnimationClip pub trait AnimatableProperty: Reflect + TypePath { /// The type of the component that the property lives on. type Component: Component; /// The type of the property to be animated. type Property: Animatable + FromReflect + Reflectable + Clone + Sync + Debug; /// Given a reference to the component, returns a reference to the property. /// /// If the property couldn't be found, returns `None`. fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property>; } /// This trait collects the additional requirements on top of [`Curve`] needed for a /// curve to be used as an [`AnimationCurve`]. pub trait AnimationCompatibleCurve: Curve + Debug + Clone + Reflectable {} impl AnimationCompatibleCurve for C where C: Curve + Debug + Clone + Reflectable {} /// This type allows the conversion of a [curve] valued in the [property type] of an /// [`AnimatableProperty`] into an [`AnimationCurve`] which animates that property. /// /// [curve]: Curve /// [property type]: AnimatableProperty::Property #[derive(Reflect, FromReflect)] #[reflect(from_reflect = false)] pub struct AnimatableCurve { /// The inner [curve] whose values are used to animate the property. /// /// [curve]: Curve pub curve: C, #[reflect(ignore)] _phantom: PhantomData

, } /// An [`AnimatableCurveEvaluator`] for [`AnimatableProperty`] instances. /// /// You shouldn't ordinarily need to instantiate one of these manually. Bevy /// will automatically do so when you use an [`AnimatableCurve`] instance. #[derive(Reflect)] pub struct AnimatableCurveEvaluator

where P: AnimatableProperty, { evaluator: BasicAnimationCurveEvaluator, #[reflect(ignore)] phantom: PhantomData

, } impl AnimatableCurve where P: AnimatableProperty, C: AnimationCompatibleCurve, { /// Create an [`AnimatableCurve`] (and thus an [`AnimationCurve`]) from a curve /// valued in an [animatable property]. /// /// [animatable property]: AnimatableProperty::Property pub fn from_curve(curve: C) -> Self { Self { curve, _phantom: PhantomData, } } } impl Clone for AnimatableCurve where C: Clone, { fn clone(&self) -> Self { Self { curve: self.curve.clone(), _phantom: PhantomData, } } } impl Debug for AnimatableCurve where C: Debug, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("AnimatableCurve") .field("curve", &self.curve) .finish() } } impl AnimationCurve for AnimatableCurve where P: AnimatableProperty, C: AnimationCompatibleCurve, { fn clone_value(&self) -> Box { Box::new(self.clone()) } fn domain(&self) -> Interval { self.curve.domain() } fn evaluator_type(&self) -> TypeId { TypeId::of::>() } fn create_evaluator(&self) -> Box { Box::new(AnimatableCurveEvaluator { evaluator: BasicAnimationCurveEvaluator::default(), phantom: PhantomData::

, }) } fn apply( &self, curve_evaluator: &mut dyn AnimationCurveEvaluator, t: f32, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) .downcast_mut::>() .unwrap(); let value = self.curve.sample_clamped(t); curve_evaluator .evaluator .stack .push(BasicAnimationCurveEvaluatorStackElement { value, weight, graph_node, }); Ok(()) } } impl

AnimationCurveEvaluator for AnimatableCurveEvaluator

where P: AnimatableProperty, { fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ false) } fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ true) } fn push_blend_register( &mut self, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { self.evaluator.push_blend_register(weight, graph_node) } fn commit<'a>( &mut self, _: Option>, mut entity: AnimationEntityMut<'a>, ) -> Result<(), AnimationEvaluationError> { let mut component = entity.get_mut::().ok_or_else(|| { AnimationEvaluationError::ComponentNotPresent(TypeId::of::()) })?; let property = P::get_mut(&mut component) .ok_or_else(|| AnimationEvaluationError::PropertyNotPresent(TypeId::of::

()))?; *property = self .evaluator .stack .pop() .ok_or_else(inconsistent::>)? .value; Ok(()) } } /// This type allows a [curve] valued in `Vec3` to become an [`AnimationCurve`] that animates /// the translation component of a transform. /// /// [curve]: Curve #[derive(Debug, Clone, Reflect, FromReflect)] #[reflect(from_reflect = false)] pub struct TranslationCurve(pub C); /// An [`AnimationCurveEvaluator`] for use with [`TranslationCurve`]s. /// /// You shouldn't need to instantiate this manually; Bevy will automatically do /// so. #[derive(Reflect)] pub struct TranslationCurveEvaluator { evaluator: BasicAnimationCurveEvaluator, } impl AnimationCurve for TranslationCurve where C: AnimationCompatibleCurve, { fn clone_value(&self) -> Box { Box::new(self.clone()) } fn domain(&self) -> Interval { self.0.domain() } fn evaluator_type(&self) -> TypeId { TypeId::of::() } fn create_evaluator(&self) -> Box { Box::new(TranslationCurveEvaluator { evaluator: BasicAnimationCurveEvaluator::default(), }) } fn apply( &self, curve_evaluator: &mut dyn AnimationCurveEvaluator, t: f32, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) .downcast_mut::() .unwrap(); let value = self.0.sample_clamped(t); curve_evaluator .evaluator .stack .push(BasicAnimationCurveEvaluatorStackElement { value, weight, graph_node, }); Ok(()) } } impl AnimationCurveEvaluator for TranslationCurveEvaluator { fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ false) } fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ true) } fn push_blend_register( &mut self, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { self.evaluator.push_blend_register(weight, graph_node) } fn commit<'a>( &mut self, transform: Option>, _: AnimationEntityMut<'a>, ) -> Result<(), AnimationEvaluationError> { let mut component = transform.ok_or_else(|| { AnimationEvaluationError::ComponentNotPresent(TypeId::of::()) })?; component.translation = self .evaluator .stack .pop() .ok_or_else(inconsistent::)? .value; Ok(()) } } /// This type allows a [curve] valued in `Quat` to become an [`AnimationCurve`] that animates /// the rotation component of a transform. /// /// [curve]: Curve #[derive(Debug, Clone, Reflect, FromReflect)] #[reflect(from_reflect = false)] pub struct RotationCurve(pub C); /// An [`AnimationCurveEvaluator`] for use with [`RotationCurve`]s. /// /// You shouldn't need to instantiate this manually; Bevy will automatically do /// so. #[derive(Reflect)] pub struct RotationCurveEvaluator { evaluator: BasicAnimationCurveEvaluator, } impl AnimationCurve for RotationCurve where C: AnimationCompatibleCurve, { fn clone_value(&self) -> Box { Box::new(self.clone()) } fn domain(&self) -> Interval { self.0.domain() } fn evaluator_type(&self) -> TypeId { TypeId::of::() } fn create_evaluator(&self) -> Box { Box::new(RotationCurveEvaluator { evaluator: BasicAnimationCurveEvaluator::default(), }) } fn apply( &self, curve_evaluator: &mut dyn AnimationCurveEvaluator, t: f32, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) .downcast_mut::() .unwrap(); let value = self.0.sample_clamped(t); curve_evaluator .evaluator .stack .push(BasicAnimationCurveEvaluatorStackElement { value, weight, graph_node, }); Ok(()) } } impl AnimationCurveEvaluator for RotationCurveEvaluator { fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ false) } fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ true) } fn push_blend_register( &mut self, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { self.evaluator.push_blend_register(weight, graph_node) } fn commit<'a>( &mut self, transform: Option>, _: AnimationEntityMut<'a>, ) -> Result<(), AnimationEvaluationError> { let mut component = transform.ok_or_else(|| { AnimationEvaluationError::ComponentNotPresent(TypeId::of::()) })?; component.rotation = self .evaluator .stack .pop() .ok_or_else(inconsistent::)? .value; Ok(()) } } /// This type allows a [curve] valued in `Vec3` to become an [`AnimationCurve`] that animates /// the scale component of a transform. /// /// [curve]: Curve #[derive(Debug, Clone, Reflect, FromReflect)] #[reflect(from_reflect = false)] pub struct ScaleCurve(pub C); /// An [`AnimationCurveEvaluator`] for use with [`ScaleCurve`]s. /// /// You shouldn't need to instantiate this manually; Bevy will automatically do /// so. #[derive(Reflect)] pub struct ScaleCurveEvaluator { evaluator: BasicAnimationCurveEvaluator, } impl AnimationCurve for ScaleCurve where C: AnimationCompatibleCurve, { fn clone_value(&self) -> Box { Box::new(self.clone()) } fn domain(&self) -> Interval { self.0.domain() } fn evaluator_type(&self) -> TypeId { TypeId::of::() } fn create_evaluator(&self) -> Box { Box::new(ScaleCurveEvaluator { evaluator: BasicAnimationCurveEvaluator::default(), }) } fn apply( &self, curve_evaluator: &mut dyn AnimationCurveEvaluator, t: f32, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) .downcast_mut::() .unwrap(); let value = self.0.sample_clamped(t); curve_evaluator .evaluator .stack .push(BasicAnimationCurveEvaluatorStackElement { value, weight, graph_node, }); Ok(()) } } impl AnimationCurveEvaluator for ScaleCurveEvaluator { fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ false) } fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.evaluator.combine(graph_node, /*additive=*/ true) } fn push_blend_register( &mut self, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { self.evaluator.push_blend_register(weight, graph_node) } fn commit<'a>( &mut self, transform: Option>, _: AnimationEntityMut<'a>, ) -> Result<(), AnimationEvaluationError> { let mut component = transform.ok_or_else(|| { AnimationEvaluationError::ComponentNotPresent(TypeId::of::()) })?; component.scale = self .evaluator .stack .pop() .ok_or_else(inconsistent::)? .value; Ok(()) } } /// This type allows an [`IterableCurve`] valued in `f32` to be used as an [`AnimationCurve`] /// that animates [morph weights]. /// /// [morph weights]: MorphWeights #[derive(Debug, Clone, Reflect, FromReflect)] #[reflect(from_reflect = false)] pub struct WeightsCurve(pub C); #[derive(Reflect)] struct WeightsCurveEvaluator { /// The values of the stack, in which each element is a list of morph target /// weights. /// /// The stack elements are concatenated and tightly packed together. /// /// The number of elements in this stack will always be a multiple of /// [`Self::morph_target_count`]. stack_morph_target_weights: Vec, /// The blend weights and graph node indices for each element of the stack. /// /// This should have as many elements as there are stack nodes. In other /// words, `Self::stack_morph_target_weights.len() * /// Self::morph_target_counts as usize == /// Self::stack_blend_weights_and_graph_nodes`. stack_blend_weights_and_graph_nodes: Vec<(f32, AnimationNodeIndex)>, /// The morph target weights in the blend register, if any. /// /// This field should be ignored if [`Self::blend_register_blend_weight`] is /// `None`. If non-empty, it will always have [`Self::morph_target_count`] /// elements in it. blend_register_morph_target_weights: Vec, /// The weight in the blend register. /// /// This will be `None` if the blend register is empty. In that case, /// [`Self::blend_register_morph_target_weights`] will be empty. blend_register_blend_weight: Option, /// The number of morph targets that are to be animated. morph_target_count: Option, } impl AnimationCurve for WeightsCurve where C: IterableCurve + Debug + Clone + Reflectable, { fn clone_value(&self) -> Box { Box::new(self.clone()) } fn domain(&self) -> Interval { self.0.domain() } fn evaluator_type(&self) -> TypeId { TypeId::of::() } fn create_evaluator(&self) -> Box { Box::new(WeightsCurveEvaluator { stack_morph_target_weights: vec![], stack_blend_weights_and_graph_nodes: vec![], blend_register_morph_target_weights: vec![], blend_register_blend_weight: None, morph_target_count: None, }) } fn apply( &self, curve_evaluator: &mut dyn AnimationCurveEvaluator, t: f32, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { let curve_evaluator = (*Reflect::as_any_mut(curve_evaluator)) .downcast_mut::() .unwrap(); let prev_morph_target_weights_len = curve_evaluator.stack_morph_target_weights.len(); curve_evaluator .stack_morph_target_weights .extend(self.0.sample_iter_clamped(t)); curve_evaluator.morph_target_count = Some( (curve_evaluator.stack_morph_target_weights.len() - prev_morph_target_weights_len) as u32, ); curve_evaluator .stack_blend_weights_and_graph_nodes .push((weight, graph_node)); Ok(()) } } impl WeightsCurveEvaluator { fn combine( &mut self, graph_node: AnimationNodeIndex, additive: bool, ) -> Result<(), AnimationEvaluationError> { let Some(&(_, top_graph_node)) = self.stack_blend_weights_and_graph_nodes.last() else { return Ok(()); }; if top_graph_node != graph_node { return Ok(()); } let (weight_to_blend, _) = self.stack_blend_weights_and_graph_nodes.pop().unwrap(); let stack_iter = self.stack_morph_target_weights.drain( (self.stack_morph_target_weights.len() - self.morph_target_count.unwrap() as usize).., ); match self.blend_register_blend_weight { None => { self.blend_register_blend_weight = Some(weight_to_blend); self.blend_register_morph_target_weights.clear(); // In the additive case, the values pushed onto the blend register need // to be scaled by the weight. if additive { self.blend_register_morph_target_weights .extend(stack_iter.map(|m| m * weight_to_blend)); } else { self.blend_register_morph_target_weights.extend(stack_iter); } } Some(ref mut current_weight) => { *current_weight += weight_to_blend; for (dest, src) in self .blend_register_morph_target_weights .iter_mut() .zip(stack_iter) { if additive { *dest += src * weight_to_blend; } else { *dest = f32::interpolate(dest, &src, weight_to_blend / *current_weight); } } } } Ok(()) } } impl AnimationCurveEvaluator for WeightsCurveEvaluator { fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.combine(graph_node, /*additive=*/ false) } fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> { self.combine(graph_node, /*additive=*/ true) } fn push_blend_register( &mut self, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { if self.blend_register_blend_weight.take().is_some() { self.stack_morph_target_weights .append(&mut self.blend_register_morph_target_weights); self.stack_blend_weights_and_graph_nodes .push((weight, graph_node)); } Ok(()) } fn commit<'a>( &mut self, _: Option>, mut entity: AnimationEntityMut<'a>, ) -> Result<(), AnimationEvaluationError> { if self.stack_morph_target_weights.is_empty() { return Ok(()); } // Compute the index of the first morph target in the last element of // the stack. let index_of_first_morph_target = self.stack_morph_target_weights.len() - self.morph_target_count.unwrap() as usize; for (dest, src) in entity .get_mut::() .ok_or_else(|| { AnimationEvaluationError::ComponentNotPresent(TypeId::of::()) })? .weights_mut() .iter_mut() .zip(self.stack_morph_target_weights[index_of_first_morph_target..].iter()) { *dest = *src; } self.stack_morph_target_weights.clear(); self.stack_blend_weights_and_graph_nodes.clear(); Ok(()) } } #[derive(Reflect)] struct BasicAnimationCurveEvaluator where A: Animatable, { stack: Vec>, blend_register: Option<(A, f32)>, } #[derive(Reflect)] struct BasicAnimationCurveEvaluatorStackElement where A: Animatable, { value: A, weight: f32, graph_node: AnimationNodeIndex, } impl Default for BasicAnimationCurveEvaluator where A: Animatable, { fn default() -> Self { BasicAnimationCurveEvaluator { stack: vec![], blend_register: None, } } } impl BasicAnimationCurveEvaluator where A: Animatable, { fn combine( &mut self, graph_node: AnimationNodeIndex, additive: bool, ) -> Result<(), AnimationEvaluationError> { let Some(top) = self.stack.last() else { return Ok(()); }; if top.graph_node != graph_node { return Ok(()); } let BasicAnimationCurveEvaluatorStackElement { value: value_to_blend, weight: weight_to_blend, graph_node: _, } = self.stack.pop().unwrap(); match self.blend_register.take() { None => { self.initialize_blend_register(value_to_blend, weight_to_blend, additive); } Some((mut current_value, mut current_weight)) => { current_weight += weight_to_blend; if additive { current_value = A::blend( [ BlendInput { weight: 1.0, value: current_value, additive: true, }, BlendInput { weight: weight_to_blend, value: value_to_blend, additive: true, }, ] .into_iter(), ); } else { current_value = A::interpolate( ¤t_value, &value_to_blend, weight_to_blend / current_weight, ); } self.blend_register = Some((current_value, current_weight)); } } Ok(()) } fn initialize_blend_register(&mut self, value: A, weight: f32, additive: bool) { if additive { let scaled_value = A::blend( [BlendInput { weight, value, additive: true, }] .into_iter(), ); self.blend_register = Some((scaled_value, weight)); } else { self.blend_register = Some((value, weight)); } } fn push_blend_register( &mut self, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError> { if let Some((value, _)) = self.blend_register.take() { self.stack.push(BasicAnimationCurveEvaluatorStackElement { value, weight, graph_node, }); } Ok(()) } } /// A low-level trait that provides control over how curves are actually applied /// to entities by the animation system. /// /// Typically, this will not need to be implemented manually, since it is /// automatically implemented by [`AnimatableCurve`] and other curves used by /// the animation system (e.g. those that animate parts of transforms or morph /// weights). However, this can be implemented manually when `AnimatableCurve` /// is not sufficiently expressive. /// /// In many respects, this behaves like a type-erased form of [`Curve`], where /// the output type of the curve is remembered only in the components that are /// mutated in the implementation of [`apply`]. /// /// [`apply`]: AnimationCurve::apply pub trait AnimationCurve: Reflect + Debug + Send + Sync { /// Returns a boxed clone of this value. fn clone_value(&self) -> Box; /// The range of times for which this animation is defined. fn domain(&self) -> Interval; /// Returns the type ID of the [`AnimationCurveEvaluator`]. /// /// This must match the type returned by [`Self::create_evaluator`]. It must /// be a single type that doesn't depend on the type of the curve. fn evaluator_type(&self) -> TypeId; /// Returns a newly-instantiated [`AnimationCurveEvaluator`] for use with /// this curve. /// /// All curve types must return the same type of /// [`AnimationCurveEvaluator`]. The returned value must match the type /// returned by [`Self::evaluator_type`]. fn create_evaluator(&self) -> Box; /// Samples the curve at the given time `t`, and pushes the sampled value /// onto the evaluation stack of the `curve_evaluator`. /// /// The `curve_evaluator` parameter points to the value returned by /// [`Self::create_evaluator`], upcast to an `&mut dyn /// AnimationCurveEvaluator`. Typically, implementations of [`Self::apply`] /// will want to downcast the `curve_evaluator` parameter to the concrete /// type [`Self::evaluator_type`] in order to push values of the appropriate /// type onto its evaluation stack. /// /// Be sure not to confuse the `t` and `weight` values. The former /// determines the position at which the *curve* is sampled, while `weight` /// ultimately determines how much the *stack values* will be blended /// together (see the definition of [`AnimationCurveEvaluator::blend`]). fn apply( &self, curve_evaluator: &mut dyn AnimationCurveEvaluator, t: f32, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError>; } /// A low-level trait for use in [`crate::VariableCurve`] that provides fine /// control over how animations are evaluated. /// /// You can implement this trait when the generic [`AnimatableCurveEvaluator`] /// isn't sufficiently-expressive for your needs. For example, [`MorphWeights`] /// implements this trait instead of using [`AnimatableCurveEvaluator`] because /// it needs to animate arbitrarily many weights at once, which can't be done /// with [`Animatable`] as that works on fixed-size values only. /// /// If you implement this trait, you should also implement [`AnimationCurve`] on /// your curve type, as that trait allows creating instances of this one. /// /// Implementations of [`AnimatableCurveEvaluator`] should maintain a *stack* of /// (value, weight, node index) triples, as well as a *blend register*, which is /// either a (value, weight) pair or empty. *Value* here refers to an instance /// of the value being animated: for example, [`Vec3`] in the case of /// translation keyframes. The stack stores intermediate values generated while /// evaluating the [`crate::graph::AnimationGraph`], while the blend register /// stores the result of a blend operation. pub trait AnimationCurveEvaluator: Reflect { /// Blends the top element of the stack with the blend register. /// /// The semantics of this method are as follows: /// /// 1. Pop the top element of the stack. Call its value vₘ and its weight /// wₘ. If the stack was empty, return success. /// /// 2. If the blend register is empty, set the blend register value to vₘ /// and the blend register weight to wₘ; then, return success. /// /// 3. If the blend register is nonempty, call its current value vₙ and its /// current weight wₙ. Then, set the value of the blend register to /// `interpolate(vₙ, vₘ, wₘ / (wₘ + wₙ))`, and set the weight of the blend /// register to wₘ + wₙ. /// /// 4. Return success. fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>; /// Additively blends the top element of the stack with the blend register. /// /// The semantics of this method are as follows: /// /// 1. Pop the top element of the stack. Call its value vₘ and its weight /// wₘ. If the stack was empty, return success. /// /// 2. If the blend register is empty, set the blend register value to vₘ /// and the blend register weight to wₘ; then, return success. /// /// 3. If the blend register is nonempty, call its current value vₙ. /// Then, set the value of the blend register to vₙ + vₘwₘ. /// /// 4. Return success. fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>; /// Pushes the current value of the blend register onto the stack. /// /// If the blend register is empty, this method does nothing successfully. /// Otherwise, this method pushes the current value of the blend register /// onto the stack, alongside the weight and graph node supplied to this /// function. The weight present in the blend register is discarded; only /// the weight parameter to this function is pushed onto the stack. The /// blend register is emptied after this process. fn push_blend_register( &mut self, weight: f32, graph_node: AnimationNodeIndex, ) -> Result<(), AnimationEvaluationError>; /// Pops the top value off the stack and writes it into the appropriate /// component. /// /// If the stack is empty, this method does nothing successfully. Otherwise, /// it pops the top value off the stack, fetches the associated component /// from either the `transform` or `entity` values as appropriate, and /// updates the appropriate property with the value popped from the stack. /// The weight and node index associated with the popped stack element are /// discarded. After doing this, the stack is emptied. /// /// The property on the component must be overwritten with the value from /// the stack, not blended with it. fn commit<'a>( &mut self, transform: Option>, entity: AnimationEntityMut<'a>, ) -> Result<(), AnimationEvaluationError>; } /// A [curve] defined by keyframes with values in an [animatable] type. /// /// The keyframes are interpolated using the type's [`Animatable::interpolate`] implementation. /// /// [curve]: Curve /// [animatable]: Animatable #[derive(Debug, Clone, Reflect)] pub struct AnimatableKeyframeCurve { core: UnevenCore, } impl Curve for AnimatableKeyframeCurve where T: Animatable + Clone, { #[inline] fn domain(&self) -> Interval { self.core.domain() } #[inline] fn sample_clamped(&self, t: f32) -> T { // `UnevenCore::sample_with` is implicitly clamped. self.core.sample_with(t, ::interpolate) } #[inline] fn sample_unchecked(&self, t: f32) -> T { self.sample_clamped(t) } } impl AnimatableKeyframeCurve where T: Animatable, { /// Create a new [`AnimatableKeyframeCurve`] from the given `keyframes`. The values of this /// curve are interpolated from the keyframes using the output type's implementation of /// [`Animatable::interpolate`]. /// /// There must be at least two samples in order for this method to succeed. pub fn new(keyframes: impl IntoIterator) -> Result { Ok(Self { core: UnevenCore::new(keyframes)?, }) } } fn inconsistent

() -> AnimationEvaluationError where P: 'static + ?Sized, { AnimationEvaluationError::InconsistentEvaluatorImplementation(TypeId::of::

()) }