Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_animation/src/gltf_curves.rs
6595 views
1
//! Concrete curve structures used to load glTF curves into the animation system.
2
3
use bevy_math::{
4
curve::{cores::*, iterable::IterableCurve, *},
5
vec4, Quat, Vec4, VectorSpace,
6
};
7
use bevy_reflect::Reflect;
8
use either::Either;
9
use thiserror::Error;
10
11
/// A keyframe-defined curve that "interpolates" by stepping at `t = 1.0` to the next keyframe.
12
#[derive(Debug, Clone, Reflect)]
13
pub struct SteppedKeyframeCurve<T> {
14
core: UnevenCore<T>,
15
}
16
17
impl<T> Curve<T> for SteppedKeyframeCurve<T>
18
where
19
T: Clone,
20
{
21
#[inline]
22
fn domain(&self) -> Interval {
23
self.core.domain()
24
}
25
26
#[inline]
27
fn sample_clamped(&self, t: f32) -> T {
28
self.core
29
.sample_with(t, |x, y, t| if t >= 1.0 { y.clone() } else { x.clone() })
30
}
31
32
#[inline]
33
fn sample_unchecked(&self, t: f32) -> T {
34
self.sample_clamped(t)
35
}
36
}
37
38
impl<T> SteppedKeyframeCurve<T> {
39
/// Create a new [`SteppedKeyframeCurve`]. If the curve could not be constructed from the
40
/// given data, an error is returned.
41
#[inline]
42
pub fn new(timed_samples: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
43
Ok(Self {
44
core: UnevenCore::new(timed_samples)?,
45
})
46
}
47
}
48
49
/// A keyframe-defined curve that uses cubic spline interpolation, backed by a contiguous buffer.
50
#[derive(Debug, Clone, Reflect)]
51
pub struct CubicKeyframeCurve<T> {
52
// Note: the sample width here should be 3.
53
core: ChunkedUnevenCore<T>,
54
}
55
56
impl<V> Curve<V> for CubicKeyframeCurve<V>
57
where
58
V: VectorSpace<Scalar = f32>,
59
{
60
#[inline]
61
fn domain(&self) -> Interval {
62
self.core.domain()
63
}
64
65
#[inline]
66
fn sample_clamped(&self, t: f32) -> V {
67
match self.core.sample_interp_timed(t) {
68
// In all the cases where only one frame matters, defer to the position within it.
69
InterpolationDatum::Exact((_, v))
70
| InterpolationDatum::LeftTail((_, v))
71
| InterpolationDatum::RightTail((_, v)) => v[1],
72
73
InterpolationDatum::Between((t0, u), (t1, v), s) => {
74
cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
75
}
76
}
77
}
78
79
#[inline]
80
fn sample_unchecked(&self, t: f32) -> V {
81
self.sample_clamped(t)
82
}
83
}
84
85
impl<T> CubicKeyframeCurve<T> {
86
/// Create a new [`CubicKeyframeCurve`] from keyframe `times` and their associated `values`.
87
/// Because 3 values are needed to perform cubic interpolation, `values` must have triple the
88
/// length of `times` — each consecutive triple `a_k, v_k, b_k` associated to time `t_k`
89
/// consists of:
90
/// - The in-tangent `a_k` for the sample at time `t_k`
91
/// - The actual value `v_k` for the sample at time `t_k`
92
/// - The out-tangent `b_k` for the sample at time `t_k`
93
///
94
/// For example, for a curve built from two keyframes, the inputs would have the following form:
95
/// - `times`: `[t_0, t_1]`
96
/// - `values`: `[a_0, v_0, b_0, a_1, v_1, b_1]`
97
#[inline]
98
pub fn new(
99
times: impl IntoIterator<Item = f32>,
100
values: impl IntoIterator<Item = T>,
101
) -> Result<Self, ChunkedUnevenCoreError> {
102
Ok(Self {
103
core: ChunkedUnevenCore::new(times, values, 3)?,
104
})
105
}
106
}
107
108
// NOTE: We can probably delete `CubicRotationCurve` once we improve the `Reflect` implementations
109
// for the `Curve` API adaptors; this is basically a `CubicKeyframeCurve` composed with `map`.
110
111
/// A keyframe-defined curve that uses cubic spline interpolation, special-cased for quaternions
112
/// since it uses `Vec4` internally.
113
#[derive(Debug, Clone, Reflect)]
114
#[reflect(Clone)]
115
pub struct CubicRotationCurve {
116
// Note: The sample width here should be 3.
117
core: ChunkedUnevenCore<Vec4>,
118
}
119
120
impl Curve<Quat> for CubicRotationCurve {
121
#[inline]
122
fn domain(&self) -> Interval {
123
self.core.domain()
124
}
125
126
#[inline]
127
fn sample_clamped(&self, t: f32) -> Quat {
128
let vec = match self.core.sample_interp_timed(t) {
129
// In all the cases where only one frame matters, defer to the position within it.
130
InterpolationDatum::Exact((_, v))
131
| InterpolationDatum::LeftTail((_, v))
132
| InterpolationDatum::RightTail((_, v)) => v[1],
133
134
InterpolationDatum::Between((t0, u), (t1, v), s) => {
135
cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
136
}
137
};
138
Quat::from_vec4(vec.normalize())
139
}
140
141
#[inline]
142
fn sample_unchecked(&self, t: f32) -> Quat {
143
self.sample_clamped(t)
144
}
145
}
146
147
impl CubicRotationCurve {
148
/// Create a new [`CubicRotationCurve`] from keyframe `times` and their associated `values`.
149
/// Because 3 values are needed to perform cubic interpolation, `values` must have triple the
150
/// length of `times` — each consecutive triple `a_k, v_k, b_k` associated to time `t_k`
151
/// consists of:
152
/// - The in-tangent `a_k` for the sample at time `t_k`
153
/// - The actual value `v_k` for the sample at time `t_k`
154
/// - The out-tangent `b_k` for the sample at time `t_k`
155
///
156
/// For example, for a curve built from two keyframes, the inputs would have the following form:
157
/// - `times`: `[t_0, t_1]`
158
/// - `values`: `[a_0, v_0, b_0, a_1, v_1, b_1]`
159
///
160
/// To sample quaternions from this curve, the resulting interpolated `Vec4` output is normalized
161
/// and interpreted as a quaternion.
162
pub fn new(
163
times: impl IntoIterator<Item = f32>,
164
values: impl IntoIterator<Item = Vec4>,
165
) -> Result<Self, ChunkedUnevenCoreError> {
166
Ok(Self {
167
core: ChunkedUnevenCore::new(times, values, 3)?,
168
})
169
}
170
}
171
172
/// A keyframe-defined curve that uses linear interpolation over many samples at once, backed
173
/// by a contiguous buffer.
174
#[derive(Debug, Clone, Reflect)]
175
pub struct WideLinearKeyframeCurve<T> {
176
// Here the sample width is the number of things to simultaneously interpolate.
177
core: ChunkedUnevenCore<T>,
178
}
179
180
impl<T> IterableCurve<T> for WideLinearKeyframeCurve<T>
181
where
182
T: VectorSpace<Scalar = f32>,
183
{
184
#[inline]
185
fn domain(&self) -> Interval {
186
self.core.domain()
187
}
188
189
#[inline]
190
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
191
match self.core.sample_interp(t) {
192
InterpolationDatum::Exact(v)
193
| InterpolationDatum::LeftTail(v)
194
| InterpolationDatum::RightTail(v) => Either::Left(v.iter().copied()),
195
196
InterpolationDatum::Between(u, v, s) => {
197
let interpolated = u.iter().zip(v.iter()).map(move |(x, y)| x.lerp(*y, s));
198
Either::Right(interpolated)
199
}
200
}
201
}
202
203
#[inline]
204
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
205
self.sample_iter_clamped(t)
206
}
207
}
208
209
impl<T> WideLinearKeyframeCurve<T> {
210
/// Create a new [`WideLinearKeyframeCurve`]. An error will be returned if:
211
/// - `values` has length zero.
212
/// - `times` has less than `2` unique valid entries.
213
/// - The length of `values` is not divisible by that of `times` (once sorted, filtered,
214
/// and deduplicated).
215
#[inline]
216
pub fn new(
217
times: impl IntoIterator<Item = f32>,
218
values: impl IntoIterator<Item = T>,
219
) -> Result<Self, WideKeyframeCurveError> {
220
Ok(Self {
221
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
222
})
223
}
224
}
225
226
/// A keyframe-defined curve that uses stepped "interpolation" over many samples at once, backed
227
/// by a contiguous buffer.
228
#[derive(Debug, Clone, Reflect)]
229
pub struct WideSteppedKeyframeCurve<T> {
230
// Here the sample width is the number of things to simultaneously interpolate.
231
core: ChunkedUnevenCore<T>,
232
}
233
234
impl<T> IterableCurve<T> for WideSteppedKeyframeCurve<T>
235
where
236
T: Clone,
237
{
238
#[inline]
239
fn domain(&self) -> Interval {
240
self.core.domain()
241
}
242
243
#[inline]
244
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
245
match self.core.sample_interp(t) {
246
InterpolationDatum::Exact(v)
247
| InterpolationDatum::LeftTail(v)
248
| InterpolationDatum::RightTail(v) => Either::Left(v.iter().cloned()),
249
250
InterpolationDatum::Between(u, v, s) => {
251
let interpolated =
252
u.iter()
253
.zip(v.iter())
254
.map(move |(x, y)| if s >= 1.0 { y.clone() } else { x.clone() });
255
Either::Right(interpolated)
256
}
257
}
258
}
259
260
#[inline]
261
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
262
self.sample_iter_clamped(t)
263
}
264
}
265
266
impl<T> WideSteppedKeyframeCurve<T> {
267
/// Create a new [`WideSteppedKeyframeCurve`]. An error will be returned if:
268
/// - `values` has length zero.
269
/// - `times` has less than `2` unique valid entries.
270
/// - The length of `values` is not divisible by that of `times` (once sorted, filtered,
271
/// and deduplicated).
272
#[inline]
273
pub fn new(
274
times: impl IntoIterator<Item = f32>,
275
values: impl IntoIterator<Item = T>,
276
) -> Result<Self, WideKeyframeCurveError> {
277
Ok(Self {
278
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
279
})
280
}
281
}
282
283
/// A keyframe-defined curve that uses cubic interpolation over many samples at once, backed by a
284
/// contiguous buffer.
285
#[derive(Debug, Clone, Reflect)]
286
pub struct WideCubicKeyframeCurve<T> {
287
core: ChunkedUnevenCore<T>,
288
}
289
290
impl<T> IterableCurve<T> for WideCubicKeyframeCurve<T>
291
where
292
T: VectorSpace<Scalar = f32>,
293
{
294
#[inline]
295
fn domain(&self) -> Interval {
296
self.core.domain()
297
}
298
299
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
300
match self.core.sample_interp_timed(t) {
301
InterpolationDatum::Exact((_, v))
302
| InterpolationDatum::LeftTail((_, v))
303
| InterpolationDatum::RightTail((_, v)) => {
304
// Pick out the part of this that actually represents the position (instead of tangents),
305
// which is the middle third.
306
let width = self.core.width();
307
Either::Left(v[width..(width * 2)].iter().copied())
308
}
309
310
InterpolationDatum::Between((t0, u), (t1, v), s) => Either::Right(
311
cubic_spline_interpolate_slices(self.core.width() / 3, u, v, s, t1 - t0),
312
),
313
}
314
}
315
316
#[inline]
317
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
318
self.sample_iter_clamped(t)
319
}
320
}
321
322
/// An error indicating that a multisampling keyframe curve could not be constructed.
323
#[derive(Debug, Error)]
324
#[error("unable to construct a curve using this data")]
325
pub enum WideKeyframeCurveError {
326
/// The number of given values was not divisible by a multiple of the number of keyframes.
327
#[error("number of values ({values_given}) is not divisible by {divisor}")]
328
LengthMismatch {
329
/// The number of values given.
330
values_given: usize,
331
/// The number that `values_given` was supposed to be divisible by.
332
divisor: usize,
333
},
334
/// An error was returned by the internal core constructor.
335
#[error(transparent)]
336
CoreError(#[from] ChunkedUnevenCoreError),
337
}
338
339
impl<T> WideCubicKeyframeCurve<T> {
340
/// Create a new [`WideCubicKeyframeCurve`].
341
///
342
/// An error will be returned if:
343
/// - `values` has length zero.
344
/// - `times` has less than `2` unique valid entries.
345
/// - The length of `values` is not divisible by three times that of `times` (once sorted,
346
/// filtered, and deduplicated).
347
#[inline]
348
pub fn new(
349
times: impl IntoIterator<Item = f32>,
350
values: impl IntoIterator<Item = T>,
351
) -> Result<Self, WideKeyframeCurveError> {
352
let times: Vec<f32> = times.into_iter().collect();
353
let values: Vec<T> = values.into_iter().collect();
354
let divisor = times.len() * 3;
355
356
if !values.len().is_multiple_of(divisor) {
357
return Err(WideKeyframeCurveError::LengthMismatch {
358
values_given: values.len(),
359
divisor,
360
});
361
}
362
363
Ok(Self {
364
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
365
})
366
}
367
}
368
369
/// A curve specifying the [`MorphWeights`] for a mesh in animation. The variants are broken
370
/// down by interpolation mode (with the exception of `Constant`, which never interpolates).
371
///
372
/// This type is, itself, a `Curve<Vec<f32>>`; however, in order to avoid allocation, it is
373
/// recommended to use its implementation of the [`IterableCurve`] trait, which allows iterating
374
/// directly over information derived from the curve without allocating.
375
///
376
/// [`MorphWeights`]: bevy_mesh::morph::MorphWeights
377
#[derive(Debug, Clone, Reflect)]
378
#[reflect(Clone)]
379
pub enum WeightsCurve {
380
/// A curve which takes a constant value over its domain. Notably, this is how animations with
381
/// only a single keyframe are interpreted.
382
Constant(ConstantCurve<Vec<f32>>),
383
384
/// A curve which interpolates weights linearly between keyframes.
385
Linear(WideLinearKeyframeCurve<f32>),
386
387
/// A curve which interpolates weights between keyframes in steps.
388
Step(WideSteppedKeyframeCurve<f32>),
389
390
/// A curve which interpolates between keyframes by using auxiliary tangent data to join
391
/// adjacent keyframes with a cubic Hermite spline, which is then sampled.
392
CubicSpline(WideCubicKeyframeCurve<f32>),
393
}
394
395
//---------//
396
// HELPERS //
397
//---------//
398
399
/// Helper function for cubic spline interpolation.
400
fn cubic_spline_interpolation<T>(
401
value_start: T,
402
tangent_out_start: T,
403
tangent_in_end: T,
404
value_end: T,
405
lerp: f32,
406
step_duration: f32,
407
) -> T
408
where
409
T: VectorSpace<Scalar = f32>,
410
{
411
let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp;
412
value_start * (coeffs.x * lerp + 1.0)
413
+ tangent_out_start * step_duration * lerp * (coeffs.y + 1.0)
414
+ value_end * lerp * coeffs.z
415
+ tangent_in_end * step_duration * lerp * coeffs.w
416
}
417
418
fn cubic_spline_interpolate_slices<'a, T: VectorSpace<Scalar = f32>>(
419
width: usize,
420
first: &'a [T],
421
second: &'a [T],
422
s: f32,
423
step_between: f32,
424
) -> impl Iterator<Item = T> + 'a {
425
(0..width).map(move |idx| {
426
cubic_spline_interpolation(
427
first[idx + width],
428
first[idx + (width * 2)],
429
second[idx + width],
430
second[idx],
431
s,
432
step_between,
433
)
434
})
435
}
436
437