1use crate::util;
4use bevy_color::{Laba, LinearRgba, Oklaba, Srgba, Xyza};
5use bevy_math::*;
6use bevy_reflect::Reflect;
7use bevy_transform::prelude::Transform;
8
9pub struct BlendInput<T> {
11 pub weight: f32,
13 pub value: T,
15 pub additive: bool,
17}
18
19pub trait Animatable: Reflect + Sized + Send + Sync + 'static {
21 fn interpolate(a: &Self, b: &Self, time: f32) -> Self;
25
26 fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self;
30}
31
32macro_rules! impl_float_animatable {
33 ($ty: ty, $base: ty) => {
34 impl Animatable for $ty {
35 #[inline]
36 fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
37 let t = <$base>::from(t);
38 (*a) * (1.0 - t) + (*b) * t
39 }
40
41 #[inline]
42 fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
43 let mut value = Default::default();
44 for input in inputs {
45 if input.additive {
46 value += <$base>::from(input.weight) * input.value;
47 } else {
48 value = Self::interpolate(&value, &input.value, input.weight);
49 }
50 }
51 value
52 }
53 }
54 };
55}
56
57macro_rules! impl_color_animatable {
58 ($ty: ident) => {
59 impl Animatable for $ty {
60 #[inline]
61 fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
62 let value = *a * (1. - t) + *b * t;
63 value
64 }
65
66 #[inline]
67 fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
68 let mut value = Default::default();
69 for input in inputs {
70 if input.additive {
71 value += input.weight * input.value;
72 } else {
73 value = Self::interpolate(&value, &input.value, input.weight);
74 }
75 }
76 value
77 }
78 }
79 };
80}
81
82impl_float_animatable!(f32, f32);
83impl_float_animatable!(Vec2, f32);
84impl_float_animatable!(Vec3A, f32);
85impl_float_animatable!(Vec4, f32);
86
87impl_float_animatable!(f64, f64);
88impl_float_animatable!(DVec2, f64);
89impl_float_animatable!(DVec3, f64);
90impl_float_animatable!(DVec4, f64);
91
92impl_color_animatable!(LinearRgba);
93impl_color_animatable!(Laba);
94impl_color_animatable!(Oklaba);
95impl_color_animatable!(Srgba);
96impl_color_animatable!(Xyza);
97
98impl Animatable for Vec3 {
100 #[inline]
101 fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
102 (*a) * (1.0 - t) + (*b) * t
103 }
104
105 #[inline]
106 fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
107 let mut value = Vec3A::ZERO;
108 for input in inputs {
109 if input.additive {
110 value += input.weight * Vec3A::from(input.value);
111 } else {
112 value = Vec3A::interpolate(&value, &Vec3A::from(input.value), input.weight);
113 }
114 }
115 Self::from(value)
116 }
117}
118
119impl Animatable for bool {
120 #[inline]
121 fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
122 util::step_unclamped(*a, *b, t)
123 }
124
125 #[inline]
126 fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
127 inputs
128 .max_by_key(|x| FloatOrd(x.weight))
129 .is_some_and(|input| input.value)
130 }
131}
132
133impl Animatable for Transform {
134 fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
135 Self {
136 translation: Vec3::interpolate(&a.translation, &b.translation, t),
137 rotation: Quat::interpolate(&a.rotation, &b.rotation, t),
138 scale: Vec3::interpolate(&a.scale, &b.scale, t),
139 }
140 }
141
142 fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
143 let mut translation = Vec3A::ZERO;
144 let mut scale = Vec3A::ZERO;
145 let mut rotation = Quat::IDENTITY;
146
147 for input in inputs {
148 if input.additive {
149 translation += input.weight * Vec3A::from(input.value.translation);
150 scale += input.weight * Vec3A::from(input.value.scale);
151 rotation =
152 Quat::slerp(Quat::IDENTITY, input.value.rotation, input.weight) * rotation;
153 } else {
154 translation = Vec3A::interpolate(
155 &translation,
156 &Vec3A::from(input.value.translation),
157 input.weight,
158 );
159 scale = Vec3A::interpolate(&scale, &Vec3A::from(input.value.scale), input.weight);
160 rotation = Quat::interpolate(&rotation, &input.value.rotation, input.weight);
161 }
162 }
163
164 Self {
165 translation: Vec3::from(translation),
166 rotation,
167 scale: Vec3::from(scale),
168 }
169 }
170}
171
172impl Animatable for Quat {
173 #[inline]
175 fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
176 a.slerp(*b, t)
179 }
180
181 #[inline]
182 fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
183 let mut value = Self::IDENTITY;
184 for BlendInput {
185 weight,
186 value: incoming_value,
187 additive,
188 } in inputs
189 {
190 if additive {
191 value = Self::slerp(Self::IDENTITY, incoming_value, weight) * value;
192 } else {
193 value = Self::interpolate(&value, &incoming_value, weight);
194 }
195 }
196 value
197 }
198}
199
200pub fn interpolate_with_cubic_bezier<T>(p0: &T, d0: &T, d3: &T, p3: &T, t: f32, duration: f32) -> T
205where
206 T: Animatable + Clone,
207{
208 let p1 = T::blend(
235 [
236 BlendInput {
237 weight: duration / 3.0,
238 value: (*d0).clone(),
239 additive: true,
240 },
241 BlendInput {
242 weight: 1.0,
243 value: (*p0).clone(),
244 additive: true,
245 },
246 ]
247 .into_iter(),
248 );
249 let p2 = T::blend(
250 [
251 BlendInput {
252 weight: duration / -3.0,
253 value: (*d3).clone(),
254 additive: true,
255 },
256 BlendInput {
257 weight: 1.0,
258 value: (*p3).clone(),
259 additive: true,
260 },
261 ]
262 .into_iter(),
263 );
264
265 let p0p1 = T::interpolate(p0, &p1, t);
267 let p1p2 = T::interpolate(&p1, &p2, t);
268 let p2p3 = T::interpolate(&p2, p3, t);
269 let p0p1p2 = T::interpolate(&p0p1, &p1p2, t);
270 let p1p2p3 = T::interpolate(&p1p2, &p2p3, t);
271 T::interpolate(&p0p1p2, &p1p2p3, t)
272}