Skip to main content

myth_animation/
values.rs

1use glam::{Quat, Vec3, Vec4};
2use myth_core::MAX_MORPH_TARGETS;
3use smallvec::{SmallVec, smallvec};
4
5/// Trait for types that support keyframe interpolation.
6///
7/// Implementations must provide linear and cubic Hermite spline interpolation.
8/// The trait requires `Clone` for value copying during keyframe sampling.
9pub trait Interpolatable: Clone + Sized {
10    fn interpolate_linear(start: &Self, end: &Self, t: f32) -> Self;
11
12    fn interpolate_cubic(
13        v0: &Self,
14        out_tangent0: &Self,
15        in_tangent1: &Self,
16        v1: &Self,
17        t: f32,
18        dt: f32,
19    ) -> Self;
20
21    fn interpolate_linear_into(start: &Self, end: &Self, t: f32, out: &mut Self) {
22        *out = Self::interpolate_linear(start, end, t);
23    }
24
25    fn interpolate_cubic_into(
26        v0: &Self,
27        out_tangent0: &Self,
28        in_tangent1: &Self,
29        v1: &Self,
30        t: f32,
31        dt: f32,
32        out: &mut Self,
33    ) {
34        *out = Self::interpolate_cubic(v0, out_tangent0, in_tangent1, v1, t, dt);
35    }
36}
37
38/// Container for morph target blend weights.
39///
40/// Uses `SmallVec` to avoid heap allocation for common morph target counts
41/// (up to [`MAX_MORPH_TARGETS`] weights stored inline on the stack).
42#[repr(C)]
43#[derive(Clone, Debug, Default)]
44pub struct MorphWeightData {
45    pub weights: SmallVec<[f32; MAX_MORPH_TARGETS]>,
46}
47
48impl MorphWeightData {
49    /// Creates a zero-initialized weight vector of the given length.
50    #[must_use]
51    pub fn allocate(n: usize) -> Self {
52        Self {
53            weights: smallvec![0.0; n],
54        }
55    }
56
57    /// Performs zero-allocation linear interpolation, writing results directly
58    /// into a pre-allocated output buffer.
59    ///
60    /// The number of weights written is `min(start.len, end.len, out_buffer.len)`.
61    pub fn interpolate_linear_into(start: &Self, end: &Self, t: f32, out_buffer: &mut [f32]) {
62        let len = start
63            .weights
64            .len()
65            .min(end.weights.len())
66            .min(out_buffer.len());
67        // for i in 0..len {
68        for (i, item) in out_buffer.iter_mut().enumerate().take(len) {
69            *item = start.weights[i] + (end.weights[i] - start.weights[i]) * t;
70        }
71    }
72
73    /// Performs zero-allocation cubic Hermite interpolation, writing results
74    /// directly into a pre-allocated output buffer.
75    pub fn interpolate_cubic_into(
76        v0: &Self,
77        out_tangent0: &Self,
78        in_tangent1: &Self,
79        v1: &Self,
80        t: f32,
81        dt: f32,
82        out_buffer: &mut [f32],
83    ) {
84        let t2 = t * t;
85        let t3 = t2 * t;
86        let s2 = -2.0 * t3 + 3.0 * t2;
87        let s3 = t3 - t2;
88        let s0 = 1.0 - s2;
89        let s1 = s3 - t2 + t;
90
91        let len = v0.weights.len().min(v1.weights.len()).min(out_buffer.len());
92        for (i, item) in out_buffer.iter_mut().enumerate().take(len) {
93            let m0 = out_tangent0.weights[i] * dt;
94            let m1 = in_tangent1.weights[i] * dt;
95            *item = s0 * v0.weights[i] + s1 * m0 + s2 * v1.weights[i] + s3 * m1;
96        }
97    }
98}
99
100impl Interpolatable for MorphWeightData {
101    fn interpolate_linear(start: &Self, end: &Self, t: f32) -> Self {
102        let len = start.weights.len().max(end.weights.len());
103        let mut result = MorphWeightData::allocate(len);
104        for i in 0..len {
105            let s = if i < start.weights.len() {
106                start.weights[i]
107            } else {
108                0.0
109            };
110            let e = if i < end.weights.len() {
111                end.weights[i]
112            } else {
113                0.0
114            };
115            result.weights[i] = s + (e - s) * t;
116        }
117        result
118    }
119
120    fn interpolate_cubic(
121        v0: &Self,
122        out_tangent0: &Self,
123        in_tangent1: &Self,
124        v1: &Self,
125        t: f32,
126        dt: f32,
127    ) -> Self {
128        let t2 = t * t;
129        let t3 = t2 * t;
130        let s2 = -2.0 * t3 + 3.0 * t2;
131        let s3 = t3 - t2;
132        let s0 = 1.0 - s2;
133        let s1 = s3 - t2 + t;
134
135        let len = v0.weights.len().max(v1.weights.len());
136        let mut result = MorphWeightData::allocate(len);
137
138        for i in 0..len {
139            let m0 = out_tangent0.weights[i] * dt;
140            let m1 = in_tangent1.weights[i] * dt;
141            result.weights[i] = s0 * v0.weights[i] + s1 * m0 + s2 * v1.weights[i] + s3 * m1;
142        }
143        result
144    }
145
146    fn interpolate_linear_into(start: &Self, end: &Self, t: f32, out: &mut Self) {
147        let len = start.weights.len().min(end.weights.len());
148        if out.weights.len() < len {
149            out.weights.resize(len, 0.0);
150        }
151        for i in 0..len {
152            out.weights[i] = start.weights[i] + (end.weights[i] - start.weights[i]) * t;
153        }
154    }
155
156    fn interpolate_cubic_into(
157        v0: &Self,
158        out_tangent0: &Self,
159        in_tangent1: &Self,
160        v1: &Self,
161        t: f32,
162        dt: f32,
163        out: &mut Self,
164    ) {
165        let len = v0.weights.len().min(v1.weights.len());
166        if out.weights.len() < len {
167            out.weights.resize(len, 0.0);
168        }
169
170        let t2 = t * t;
171        let t3 = t2 * t;
172        let s2 = -2.0 * t3 + 3.0 * t2;
173        let s3 = t3 - t2;
174        let s0 = 1.0 - s2;
175        let s1 = s3 - t2 + t;
176
177        for i in 0..len {
178            let m0 = out_tangent0.weights[i] * dt;
179            let m1 = in_tangent1.weights[i] * dt;
180            out.weights[i] = s0 * v0.weights[i] + s1 * m0 + s2 * v1.weights[i] + s3 * m1;
181        }
182    }
183}
184
185impl Interpolatable for f32 {
186    fn interpolate_linear(start: &Self, end: &Self, t: f32) -> Self {
187        start + (end - start) * t
188    }
189
190    fn interpolate_cubic(
191        v0: &Self,
192        out_tangent0: &Self,
193        in_tangent1: &Self,
194        v1: &Self,
195        t: f32,
196        dt: f32,
197    ) -> Self {
198        let t2 = t * t;
199        let t3 = t2 * t;
200
201        let s2 = -2.0 * t3 + 3.0 * t2;
202        let s3 = t3 - t2;
203        let s0 = 1.0 - s2;
204        let s1 = s3 - t2 + t;
205
206        let m0 = out_tangent0 * dt;
207        let m1 = in_tangent1 * dt;
208
209        s0 * v0 + s1 * m0 + s2 * v1 + s3 * m1
210    }
211}
212
213impl Interpolatable for Vec3 {
214    fn interpolate_linear(start: &Self, end: &Self, t: f32) -> Self {
215        start.lerp(*end, t)
216    }
217
218    fn interpolate_cubic(
219        v0: &Self,
220        out_tangent0: &Self,
221        in_tangent1: &Self,
222        v1: &Self,
223        t: f32,
224        dt: f32,
225    ) -> Self {
226        let t2 = t * t;
227        let t3 = t2 * t;
228
229        let s2 = -2.0 * t3 + 3.0 * t2;
230        let s3 = t3 - t2;
231        let s0 = 1.0 - s2;
232        let s1 = s3 - t2 + t;
233
234        let m0 = out_tangent0 * dt;
235        let m1 = in_tangent1 * dt;
236
237        v0 * s0 + m0 * s1 + v1 * s2 + m1 * s3
238    }
239}
240
241impl Interpolatable for Quat {
242    fn interpolate_linear(start: &Self, end: &Self, t: f32) -> Self {
243        start.slerp(*end, t)
244    }
245
246    fn interpolate_cubic(
247        v0: &Self,
248        out_tangent0: &Self,
249        in_tangent1: &Self,
250        v1: &Self,
251        t: f32,
252        dt: f32,
253    ) -> Self {
254        let t2 = t * t;
255        let t3 = t2 * t;
256
257        let s2 = -2.0 * t3 + 3.0 * t2;
258        let s3 = t3 - t2;
259        let s0 = 1.0 - s2;
260        let s1 = s3 - t2 + t;
261
262        let v0_v = Vec4::from(*v0);
263        let v1_v = Vec4::from(*v1);
264        let m0_v = Vec4::from(*out_tangent0) * dt;
265        let m1_v = Vec4::from(*in_tangent1) * dt;
266
267        let result = v0_v * s0 + m0_v * s1 + v1_v * s2 + m1_v * s3;
268
269        Quat::from_vec4(result).normalize()
270    }
271}