use core::{
any::TypeId,
fmt::{self, Debug, Formatter},
marker::PhantomData,
};
use crate::{
graph::AnimationNodeIndex,
prelude::{Animatable, BlendInput},
AnimationEntityMut, AnimationEvaluationError,
};
use bevy_ecs::component::{Component, Mutable};
use bevy_math::curve::{
cores::{UnevenCore, UnevenCoreError},
iterable::IterableCurve,
Curve, Interval,
};
use bevy_mesh::morph::MorphWeights;
use bevy_platform::hash::Hashed;
use bevy_reflect::{FromReflect, Reflect, Reflectable, TypeInfo, Typed};
use downcast_rs::{impl_downcast, Downcast};
pub trait AnimatableProperty: Send + Sync + 'static {
type Property: Animatable;
fn get_mut<'a>(
&self,
entity: &'a mut AnimationEntityMut,
) -> Result<&'a mut Self::Property, AnimationEvaluationError>;
fn evaluator_id(&self) -> EvaluatorId<'_>;
}
#[derive(Clone)]
pub struct AnimatedField<C, A, F: Fn(&mut C) -> &mut A> {
func: F,
evaluator_id: Hashed<(TypeId, usize)>,
marker: PhantomData<(C, A)>,
}
impl<C, A, F> AnimatableProperty for AnimatedField<C, A, F>
where
C: Component<Mutability = Mutable>,
A: Animatable + Clone + Sync + Debug,
F: Fn(&mut C) -> &mut A + Send + Sync + 'static,
{
type Property = A;
fn get_mut<'a>(
&self,
entity: &'a mut AnimationEntityMut,
) -> Result<&'a mut A, AnimationEvaluationError> {
let c = entity
.get_mut::<C>()
.ok_or_else(|| AnimationEvaluationError::ComponentNotPresent(TypeId::of::<C>()))?;
Ok((self.func)(c.into_inner()))
}
fn evaluator_id(&self) -> EvaluatorId<'_> {
EvaluatorId::ComponentField(&self.evaluator_id)
}
}
impl<C: Typed, P, F: Fn(&mut C) -> &mut P + 'static> AnimatedField<C, P, F> {
pub fn new_unchecked(field_name: &str, func: F) -> Self {
let field_index;
if let TypeInfo::Struct(struct_info) = C::type_info() {
field_index = struct_info
.index_of(field_name)
.expect("Field name should exist");
} else if let TypeInfo::TupleStruct(struct_info) = C::type_info() {
field_index = field_name
.parse()
.expect("Field name should be a valid tuple index");
if field_index >= struct_info.field_len() {
panic!("Field name should be a valid tuple index");
}
} else {
panic!("Only structs are supported in `AnimatedField::new_unchecked`")
}
Self {
func,
evaluator_id: Hashed::new((TypeId::of::<C>(), field_index)),
marker: PhantomData,
}
}
}
pub trait AnimationCompatibleCurve<T>: Curve<T> + Debug + Clone + Reflectable {}
impl<T, C> AnimationCompatibleCurve<T> for C where C: Curve<T> + Debug + Clone + Reflectable {}
#[derive(Reflect, FromReflect)]
#[reflect(from_reflect = false)]
pub struct AnimatableCurve<P, C> {
pub property: P,
pub curve: C,
}
#[derive(Reflect)]
pub struct AnimatableCurveEvaluator<A: Animatable> {
evaluator: BasicAnimationCurveEvaluator<A>,
property: Box<dyn AnimatableProperty<Property = A>>,
}
impl<P, C> AnimatableCurve<P, C>
where
P: AnimatableProperty,
C: AnimationCompatibleCurve<P::Property>,
{
pub fn new(property: P, curve: C) -> Self {
Self { property, curve }
}
}
impl<P, C> Clone for AnimatableCurve<P, C>
where
C: Clone,
P: Clone,
{
fn clone(&self) -> Self {
Self {
curve: self.curve.clone(),
property: self.property.clone(),
}
}
}
impl<P, C> Debug for AnimatableCurve<P, C>
where
C: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("AnimatableCurve")
.field("curve", &self.curve)
.finish()
}
}
impl<P: Send + Sync + 'static, C> AnimationCurve for AnimatableCurve<P, C>
where
P: AnimatableProperty + Clone,
C: AnimationCompatibleCurve<P::Property> + Clone,
{
fn clone_value(&self) -> Box<dyn AnimationCurve> {
Box::new(self.clone())
}
fn domain(&self) -> Interval {
self.curve.domain()
}
fn evaluator_id(&self) -> EvaluatorId<'_> {
self.property.evaluator_id()
}
fn create_evaluator(&self) -> Box<dyn AnimationCurveEvaluator> {
Box::new(AnimatableCurveEvaluator::<P::Property> {
evaluator: BasicAnimationCurveEvaluator::default(),
property: Box::new(self.property.clone()),
})
}
fn apply(
&self,
curve_evaluator: &mut dyn AnimationCurveEvaluator,
t: f32,
weight: f32,
graph_node: AnimationNodeIndex,
) -> Result<(), AnimationEvaluationError> {
let curve_evaluator = curve_evaluator
.downcast_mut::<AnimatableCurveEvaluator<P::Property>>()
.unwrap();
let value = self.curve.sample_clamped(t);
curve_evaluator
.evaluator
.stack
.push(BasicAnimationCurveEvaluatorStackElement {
value,
weight,
graph_node,
});
Ok(())
}
}
impl<A: Animatable> AnimationCurveEvaluator for AnimatableCurveEvaluator<A> {
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.combine(graph_node, false)
}
fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.combine(graph_node, true)
}
fn push_blend_register(
&mut self,
weight: f32,
graph_node: AnimationNodeIndex,
) -> Result<(), AnimationEvaluationError> {
self.evaluator.push_blend_register(weight, graph_node)
}
fn commit(&mut self, mut entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError> {
let property = self.property.get_mut(&mut entity)?;
*property = self
.evaluator
.stack
.pop()
.ok_or_else(inconsistent::<AnimatableCurveEvaluator<A>>)?
.value;
Ok(())
}
}
#[derive(Debug, Clone, Reflect, FromReflect)]
#[reflect(from_reflect = false)]
pub struct WeightsCurve<C>(pub C);
#[derive(Reflect)]
struct WeightsCurveEvaluator {
stack_morph_target_weights: Vec<f32>,
stack_blend_weights_and_graph_nodes: Vec<(f32, AnimationNodeIndex)>,
blend_register_morph_target_weights: Vec<f32>,
blend_register_blend_weight: Option<f32>,
morph_target_count: Option<u32>,
}
impl<C> AnimationCurve for WeightsCurve<C>
where
C: IterableCurve<f32> + Debug + Clone + Reflectable,
{
fn clone_value(&self) -> Box<dyn AnimationCurve> {
Box::new(self.clone())
}
fn domain(&self) -> Interval {
self.0.domain()
}
fn evaluator_id(&self) -> EvaluatorId<'_> {
EvaluatorId::Type(TypeId::of::<WeightsCurveEvaluator>())
}
fn create_evaluator(&self) -> Box<dyn AnimationCurveEvaluator> {
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 = curve_evaluator
.downcast_mut::<WeightsCurveEvaluator>()
.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();
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, false)
}
fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.combine(graph_node, 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(&mut self, mut entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError> {
if self.stack_morph_target_weights.is_empty() {
return Ok(());
}
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::<MorphWeights>()
.ok_or_else(|| {
AnimationEvaluationError::ComponentNotPresent(TypeId::of::<MorphWeights>())
})?
.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<A>
where
A: Animatable,
{
stack: Vec<BasicAnimationCurveEvaluatorStackElement<A>>,
blend_register: Option<(A, f32)>,
}
#[derive(Reflect)]
struct BasicAnimationCurveEvaluatorStackElement<A>
where
A: Animatable,
{
value: A,
weight: f32,
graph_node: AnimationNodeIndex,
}
impl<A> Default for BasicAnimationCurveEvaluator<A>
where
A: Animatable,
{
fn default() -> Self {
BasicAnimationCurveEvaluator {
stack: vec![],
blend_register: None,
}
}
}
impl<A> BasicAnimationCurveEvaluator<A>
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(())
}
}
pub trait AnimationCurve: Debug + Send + Sync + 'static {
fn clone_value(&self) -> Box<dyn AnimationCurve>;
fn domain(&self) -> Interval;
fn evaluator_id(&self) -> EvaluatorId<'_>;
fn create_evaluator(&self) -> Box<dyn AnimationCurveEvaluator>;
fn apply(
&self,
curve_evaluator: &mut dyn AnimationCurveEvaluator,
t: f32,
weight: f32,
graph_node: AnimationNodeIndex,
) -> Result<(), AnimationEvaluationError>;
}
#[derive(Clone)]
pub enum EvaluatorId<'a> {
ComponentField(&'a Hashed<(TypeId, usize)>),
Type(TypeId),
}
pub trait AnimationCurveEvaluator: Downcast + Send + Sync + 'static {
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;
fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;
fn push_blend_register(
&mut self,
weight: f32,
graph_node: AnimationNodeIndex,
) -> Result<(), AnimationEvaluationError>;
fn commit(&mut self, entity: AnimationEntityMut) -> Result<(), AnimationEvaluationError>;
}
impl_downcast!(AnimationCurveEvaluator);
#[derive(Debug, Clone, Reflect)]
pub struct AnimatableKeyframeCurve<T> {
core: UnevenCore<T>,
}
impl<T> Curve<T> for AnimatableKeyframeCurve<T>
where
T: Animatable + Clone,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> T {
self.core.sample_with(t, <T as Animatable>::interpolate)
}
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
}
impl<T> AnimatableKeyframeCurve<T>
where
T: Animatable,
{
pub fn new(keyframes: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
Ok(Self {
core: UnevenCore::new(keyframes)?,
})
}
}
fn inconsistent<P>() -> AnimationEvaluationError
where
P: 'static + ?Sized,
{
AnimationEvaluationError::InconsistentEvaluatorImplementation(TypeId::of::<P>())
}
#[macro_export]
macro_rules! animated_field {
($component:ident::$field:tt) => {
AnimatedField::new_unchecked(stringify!($field), |component: &mut $component| {
&mut component.$field
})
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_animated_field_tuple_struct_simple_uses() {
#[derive(Clone, Debug, Component, Reflect)]
struct A(f32);
let _ = AnimatedField::new_unchecked("0", |a: &mut A| &mut a.0);
#[derive(Clone, Debug, Component, Reflect)]
struct B(f32, f64, f32);
let _ = AnimatedField::new_unchecked("0", |b: &mut B| &mut b.0);
let _ = AnimatedField::new_unchecked("1", |b: &mut B| &mut b.1);
let _ = AnimatedField::new_unchecked("2", |b: &mut B| &mut b.2);
}
}