Skip to main content

embedded_3dgfx/
animation.rs

1//! Vertex Animation System
2//!
3//! Provides keyframe-based vertex animation through linear interpolation.
4//! Perfect for animated characters, waving flags, pulsing objects, etc.
5
6/// A single keyframe containing vertex positions
7#[derive(Debug, Clone, Copy)]
8pub struct Keyframe<'a> {
9    pub vertices: &'a [[f32; 3]],
10    pub time: f32,
11}
12
13/// Vertex animation with multiple keyframes
14#[derive(Debug)]
15pub struct VertexAnimation<'a> {
16    keyframes: &'a [Keyframe<'a>],
17    looping: bool,
18}
19
20impl<'a> VertexAnimation<'a> {
21    /// Create a new vertex animation from keyframes
22    ///
23    /// # Arguments
24    /// * `keyframes` - Array of keyframes with vertex positions and timestamps
25    /// * `looping` - Whether the animation should loop
26    ///
27    /// # Panics
28    /// Panics if keyframes array is empty or if keyframes have inconsistent vertex counts
29    pub fn new(keyframes: &'a [Keyframe<'a>], looping: bool) -> Self {
30        assert!(!keyframes.is_empty(), "Keyframes array cannot be empty");
31
32        // Verify all keyframes have the same vertex count
33        let vertex_count = keyframes[0].vertices.len();
34        for kf in keyframes.iter() {
35            assert_eq!(
36                kf.vertices.len(),
37                vertex_count,
38                "All keyframes must have the same number of vertices"
39            );
40        }
41
42        Self { keyframes, looping }
43    }
44
45    /// Sample the animation at a given time
46    ///
47    /// Returns interpolated vertex positions for the given time.
48    /// Uses a temporary buffer to store interpolated vertices.
49    ///
50    /// # Arguments
51    /// * `time` - Current animation time
52    /// * `output` - Output buffer for interpolated vertices (must match keyframe vertex count)
53    pub fn sample(&self, time: f32, output: &mut [[f32; 3]]) {
54        assert_eq!(
55            output.len(),
56            self.keyframes[0].vertices.len(),
57            "Output buffer size must match keyframe vertex count"
58        );
59
60        // Handle edge cases
61        if self.keyframes.len() == 1 {
62            output.copy_from_slice(self.keyframes[0].vertices);
63            return;
64        }
65
66        // Get animation duration
67        let duration = self.keyframes.last().unwrap().time;
68
69        // Handle looping
70        let t = if self.looping {
71            if duration > 0.0 { time % duration } else { 0.0 }
72        } else {
73            time.clamp(0.0, duration)
74        };
75
76        // Find the two keyframes to interpolate between (binary search).
77        let mut kf1_idx = self
78            .keyframes
79            .partition_point(|kf| kf.time <= t)
80            .saturating_sub(1);
81        if self.keyframes[kf1_idx].time > t {
82            kf1_idx = 0;
83        }
84        let kf2_idx = (kf1_idx + 1).min(self.keyframes.len() - 1);
85
86        // If we're at or past the last keyframe
87        if kf1_idx == self.keyframes.len() - 1 {
88            output.copy_from_slice(self.keyframes[kf1_idx].vertices);
89            return;
90        }
91
92        let kf1 = &self.keyframes[kf1_idx];
93        let kf2 = &self.keyframes[kf2_idx];
94
95        // Calculate interpolation factor
96        let alpha = if kf2.time > kf1.time {
97            (t - kf1.time) / (kf2.time - kf1.time)
98        } else {
99            0.0
100        };
101
102        // Interpolate vertices
103        for (i, out_vertex) in output.iter_mut().enumerate() {
104            let v1 = &kf1.vertices[i];
105            let v2 = &kf2.vertices[i];
106
107            out_vertex[0] = v1[0] + alpha * (v2[0] - v1[0]);
108            out_vertex[1] = v1[1] + alpha * (v2[1] - v1[1]);
109            out_vertex[2] = v1[2] + alpha * (v2[2] - v1[2]);
110        }
111    }
112
113    /// Get the total duration of the animation
114    pub fn duration(&self) -> f32 {
115        self.keyframes.last().map(|kf| kf.time).unwrap_or(0.0)
116    }
117
118    /// Get the number of keyframes
119    pub fn keyframe_count(&self) -> usize {
120        self.keyframes.len()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    extern crate std;
127    use super::*;
128
129    #[test]
130    fn test_single_keyframe() {
131        let vertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]];
132        let kf = Keyframe {
133            vertices: &vertices,
134            time: 0.0,
135        };
136        let keyframes = [kf];
137        let anim = VertexAnimation::new(&keyframes, false);
138
139        let mut output = [[0.0, 0.0, 0.0]; 2];
140        anim.sample(0.5, &mut output);
141
142        assert_eq!(output[0], [0.0, 0.0, 0.0]);
143        assert_eq!(output[1], [1.0, 0.0, 0.0]);
144    }
145
146    #[test]
147    fn test_two_keyframe_interpolation() {
148        let vertices1 = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]];
149        let vertices2 = [[0.0, 2.0, 0.0], [1.0, 2.0, 0.0]];
150
151        let kf1 = Keyframe {
152            vertices: &vertices1,
153            time: 0.0,
154        };
155        let kf2 = Keyframe {
156            vertices: &vertices2,
157            time: 1.0,
158        };
159
160        let keyframes = [kf1, kf2];
161        let anim = VertexAnimation::new(&keyframes, false);
162
163        // Sample at halfway point
164        let mut output = [[0.0, 0.0, 0.0]; 2];
165        anim.sample(0.5, &mut output);
166
167        assert_eq!(output[0], [0.0, 1.0, 0.0]);
168        assert_eq!(output[1], [1.0, 1.0, 0.0]);
169    }
170
171    #[test]
172    fn test_looping_animation() {
173        let vertices1 = [[0.0, 0.0, 0.0]];
174        let vertices2 = [[1.0, 0.0, 0.0]];
175
176        let kf1 = Keyframe {
177            vertices: &vertices1,
178            time: 0.0,
179        };
180        let kf2 = Keyframe {
181            vertices: &vertices2,
182            time: 1.0,
183        };
184
185        let keyframes = [kf1, kf2];
186        let anim = VertexAnimation::new(&keyframes, true);
187
188        // Sample past the end - should loop
189        let mut output = [[0.0, 0.0, 0.0]; 1];
190        anim.sample(1.5, &mut output);
191
192        // Should be halfway between keyframes (0.5 after wrapping)
193        assert_eq!(output[0], [0.5, 0.0, 0.0]);
194    }
195
196    #[test]
197    fn test_clamping_non_looping() {
198        let vertices1 = [[0.0, 0.0, 0.0]];
199        let vertices2 = [[1.0, 0.0, 0.0]];
200
201        let kf1 = Keyframe {
202            vertices: &vertices1,
203            time: 0.0,
204        };
205        let kf2 = Keyframe {
206            vertices: &vertices2,
207            time: 1.0,
208        };
209
210        let keyframes = [kf1, kf2];
211        let anim = VertexAnimation::new(&keyframes, false);
212
213        // Sample past the end - should clamp to last keyframe
214        let mut output = [[0.0, 0.0, 0.0]; 1];
215        anim.sample(2.0, &mut output);
216
217        assert_eq!(output[0], [1.0, 0.0, 0.0]);
218    }
219
220    #[test]
221    fn test_duration() {
222        let vertices1 = [[0.0, 0.0, 0.0]];
223        let vertices2 = [[1.0, 0.0, 0.0]];
224
225        let kf1 = Keyframe {
226            vertices: &vertices1,
227            time: 0.0,
228        };
229        let kf2 = Keyframe {
230            vertices: &vertices2,
231            time: 2.5,
232        };
233
234        let keyframes = [kf1, kf2];
235        let anim = VertexAnimation::new(&keyframes, false);
236        assert_eq!(anim.duration(), 2.5);
237        assert_eq!(anim.keyframe_count(), 2);
238    }
239
240    #[test]
241    #[should_panic(expected = "Keyframes array cannot be empty")]
242    fn test_empty_keyframes_panics() {
243        let keyframes: &[Keyframe] = &[];
244        let _anim = VertexAnimation::new(keyframes, false);
245    }
246
247    #[test]
248    #[should_panic(expected = "All keyframes must have the same number of vertices")]
249    fn test_inconsistent_vertex_count_panics() {
250        let vertices1 = [[0.0, 0.0, 0.0]];
251        let vertices2 = [[1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
252
253        let kf1 = Keyframe {
254            vertices: &vertices1,
255            time: 0.0,
256        };
257        let kf2 = Keyframe {
258            vertices: &vertices2,
259            time: 1.0,
260        };
261
262        let keyframes = [kf1, kf2];
263        let _anim = VertexAnimation::new(&keyframes, false);
264    }
265}