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
77        let mut kf1_idx = 0;
78        let mut kf2_idx = 0;
79
80        for (i, kf) in self.keyframes.iter().enumerate() {
81            if kf.time <= t {
82                kf1_idx = i;
83            }
84            if kf.time >= t {
85                kf2_idx = i;
86                break;
87            }
88        }
89
90        // If we're at or past the last keyframe
91        if kf1_idx == self.keyframes.len() - 1 {
92            output.copy_from_slice(self.keyframes[kf1_idx].vertices);
93            return;
94        }
95
96        let kf1 = &self.keyframes[kf1_idx];
97        let kf2 = &self.keyframes[kf2_idx];
98
99        // Calculate interpolation factor
100        let alpha = if kf2.time > kf1.time {
101            (t - kf1.time) / (kf2.time - kf1.time)
102        } else {
103            0.0
104        };
105
106        // Interpolate vertices
107        for (i, out_vertex) in output.iter_mut().enumerate() {
108            let v1 = &kf1.vertices[i];
109            let v2 = &kf2.vertices[i];
110
111            out_vertex[0] = v1[0] + alpha * (v2[0] - v1[0]);
112            out_vertex[1] = v1[1] + alpha * (v2[1] - v1[1]);
113            out_vertex[2] = v1[2] + alpha * (v2[2] - v1[2]);
114        }
115    }
116
117    /// Get the total duration of the animation
118    pub fn duration(&self) -> f32 {
119        self.keyframes.last().map(|kf| kf.time).unwrap_or(0.0)
120    }
121
122    /// Get the number of keyframes
123    pub fn keyframe_count(&self) -> usize {
124        self.keyframes.len()
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    extern crate std;
131    use super::*;
132
133    #[test]
134    fn test_single_keyframe() {
135        let vertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]];
136        let kf = Keyframe {
137            vertices: &vertices,
138            time: 0.0,
139        };
140        let keyframes = [kf];
141        let anim = VertexAnimation::new(&keyframes, false);
142
143        let mut output = [[0.0, 0.0, 0.0]; 2];
144        anim.sample(0.5, &mut output);
145
146        assert_eq!(output[0], [0.0, 0.0, 0.0]);
147        assert_eq!(output[1], [1.0, 0.0, 0.0]);
148    }
149
150    #[test]
151    fn test_two_keyframe_interpolation() {
152        let vertices1 = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]];
153        let vertices2 = [[0.0, 2.0, 0.0], [1.0, 2.0, 0.0]];
154
155        let kf1 = Keyframe {
156            vertices: &vertices1,
157            time: 0.0,
158        };
159        let kf2 = Keyframe {
160            vertices: &vertices2,
161            time: 1.0,
162        };
163
164        let keyframes = [kf1, kf2];
165        let anim = VertexAnimation::new(&keyframes, false);
166
167        // Sample at halfway point
168        let mut output = [[0.0, 0.0, 0.0]; 2];
169        anim.sample(0.5, &mut output);
170
171        assert_eq!(output[0], [0.0, 1.0, 0.0]);
172        assert_eq!(output[1], [1.0, 1.0, 0.0]);
173    }
174
175    #[test]
176    fn test_looping_animation() {
177        let vertices1 = [[0.0, 0.0, 0.0]];
178        let vertices2 = [[1.0, 0.0, 0.0]];
179
180        let kf1 = Keyframe {
181            vertices: &vertices1,
182            time: 0.0,
183        };
184        let kf2 = Keyframe {
185            vertices: &vertices2,
186            time: 1.0,
187        };
188
189        let keyframes = [kf1, kf2];
190        let anim = VertexAnimation::new(&keyframes, true);
191
192        // Sample past the end - should loop
193        let mut output = [[0.0, 0.0, 0.0]; 1];
194        anim.sample(1.5, &mut output);
195
196        // Should be halfway between keyframes (0.5 after wrapping)
197        assert_eq!(output[0], [0.5, 0.0, 0.0]);
198    }
199
200    #[test]
201    fn test_clamping_non_looping() {
202        let vertices1 = [[0.0, 0.0, 0.0]];
203        let vertices2 = [[1.0, 0.0, 0.0]];
204
205        let kf1 = Keyframe {
206            vertices: &vertices1,
207            time: 0.0,
208        };
209        let kf2 = Keyframe {
210            vertices: &vertices2,
211            time: 1.0,
212        };
213
214        let keyframes = [kf1, kf2];
215        let anim = VertexAnimation::new(&keyframes, false);
216
217        // Sample past the end - should clamp to last keyframe
218        let mut output = [[0.0, 0.0, 0.0]; 1];
219        anim.sample(2.0, &mut output);
220
221        assert_eq!(output[0], [1.0, 0.0, 0.0]);
222    }
223
224    #[test]
225    fn test_duration() {
226        let vertices1 = [[0.0, 0.0, 0.0]];
227        let vertices2 = [[1.0, 0.0, 0.0]];
228
229        let kf1 = Keyframe {
230            vertices: &vertices1,
231            time: 0.0,
232        };
233        let kf2 = Keyframe {
234            vertices: &vertices2,
235            time: 2.5,
236        };
237
238        let keyframes = [kf1, kf2];
239        let anim = VertexAnimation::new(&keyframes, false);
240        assert_eq!(anim.duration(), 2.5);
241        assert_eq!(anim.keyframe_count(), 2);
242    }
243
244    #[test]
245    #[should_panic(expected = "Keyframes array cannot be empty")]
246    fn test_empty_keyframes_panics() {
247        let keyframes: &[Keyframe] = &[];
248        let _anim = VertexAnimation::new(keyframes, false);
249    }
250
251    #[test]
252    #[should_panic(expected = "All keyframes must have the same number of vertices")]
253    fn test_inconsistent_vertex_count_panics() {
254        let vertices1 = [[0.0, 0.0, 0.0]];
255        let vertices2 = [[1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
256
257        let kf1 = Keyframe {
258            vertices: &vertices1,
259            time: 0.0,
260        };
261        let kf2 = Keyframe {
262            vertices: &vertices2,
263            time: 1.0,
264        };
265
266        let keyframes = [kf1, kf2];
267        let _anim = VertexAnimation::new(&keyframes, false);
268    }
269}