mesh_tools/
builder_animation.rs

1use crate::builder::GltfBuilder;
2use crate::models::{Animation, AnimationChannel, AnimationChannelTarget, AnimationSampler};
3
4/// Animation path type
5pub enum AnimationPath {
6    Translation,
7    Rotation,
8    Scale,
9    Weights,
10}
11
12impl AnimationPath {
13    /// Convert to string representation for glTF
14    pub fn to_string(&self) -> String {
15        match self {
16            AnimationPath::Translation => "translation".to_string(),
17            AnimationPath::Rotation => "rotation".to_string(),
18            AnimationPath::Scale => "scale".to_string(),
19            AnimationPath::Weights => "weights".to_string(),
20        }
21    }
22}
23
24/// Interpolation method for animation
25pub enum InterpolationType {
26    Linear,
27    Step,
28    CubicSpline,
29}
30
31impl InterpolationType {
32    /// Convert to string representation for glTF
33    pub fn to_string(&self) -> String {
34        match self {
35            InterpolationType::Linear => "LINEAR".to_string(),
36            InterpolationType::Step => "STEP".to_string(),
37            InterpolationType::CubicSpline => "CUBICSPLINE".to_string(),
38        }
39    }
40}
41
42impl GltfBuilder {
43    /// Add an animation to the glTF
44    /// 
45    /// # Arguments
46    /// 
47    /// * `name` - Optional name for the animation
48    /// 
49    /// # Returns
50    /// 
51    /// The index of the created animation
52    pub fn add_animation(&mut self, name: Option<String>) -> usize {
53        let animation = Animation {
54            name,
55            channels: Some(Vec::new()),
56            samplers: Some(Vec::new()),
57        };
58        
59        if let Some(animations) = &mut self.gltf.animations {
60            let index = animations.len();
61            animations.push(animation);
62            index
63        } else {
64            self.gltf.animations = Some(vec![animation]);
65            0
66        }
67    }
68    
69    /// Add a sampler to an animation
70    /// 
71    /// # Arguments
72    /// 
73    /// * `animation_index` - The index of the animation to add the sampler to
74    /// * `input_accessor` - The accessor containing keyframe timestamps (in seconds)
75    /// * `output_accessor` - The accessor containing output values
76    /// * `interpolation` - The interpolation method
77    /// 
78    /// # Returns
79    /// 
80    /// The index of the created sampler within the animation
81    pub fn add_animation_sampler(
82        &mut self, 
83        animation_index: usize, 
84        input_accessor: usize, 
85        output_accessor: usize,
86        interpolation: InterpolationType
87    ) -> usize {
88        let sampler = AnimationSampler {
89            input: input_accessor,
90            interpolation: Some(interpolation.to_string()),
91            output: output_accessor,
92        };
93        
94        let animations = self.gltf.animations.as_mut().expect("Animations array not initialized");
95        if animation_index >= animations.len() {
96            panic!("Animation index out of bounds");
97        }
98        
99        let animation = &mut animations[animation_index];
100        let samplers = animation.samplers.get_or_insert_with(|| Vec::new());
101        let sampler_index = samplers.len();
102        samplers.push(sampler);
103        
104        sampler_index
105    }
106    
107    /// Add a channel to an animation
108    /// 
109    /// # Arguments
110    /// 
111    /// * `animation_index` - The index of the animation to add the channel to
112    /// * `sampler_index` - The index of the sampler within the animation
113    /// * `target_node` - The index of the node being animated
114    /// * `target_path` - The property being animated (translation, rotation, scale, weights)
115    /// 
116    /// # Returns
117    /// 
118    /// The index of the created channel within the animation
119    pub fn add_animation_channel(
120        &mut self, 
121        animation_index: usize, 
122        sampler_index: usize, 
123        target_node: usize, 
124        target_path: AnimationPath
125    ) -> usize {
126        let channel = AnimationChannel {
127            sampler: sampler_index,
128            target: AnimationChannelTarget {
129                node: target_node,
130                path: target_path.to_string(),
131            }
132        };
133        
134        let animations = self.gltf.animations.as_mut().expect("Animations array not initialized");
135        if animation_index >= animations.len() {
136            panic!("Animation index out of bounds");
137        }
138        
139        let animation = &mut animations[animation_index];
140        let channels = animation.channels.get_or_insert_with(|| Vec::new());
141        let channel_index = channels.len();
142        channels.push(channel);
143        
144        channel_index
145    }
146    
147    /// Create translation keyframes for an animation
148    /// 
149    /// # Arguments
150    /// 
151    /// * `animation_index` - The index of the animation
152    /// * `node_index` - The index of the target node
153    /// * `timestamps` - Vector of keyframe timestamps (in seconds)
154    /// * `translations` - Vector of translation values ([x, y, z] for each keyframe)
155    /// * `interpolation` - The interpolation method
156    /// 
157    /// # Returns
158    /// 
159    /// The indices of the created channel and sampler
160    pub fn create_translation_animation(
161        &mut self,
162        animation_index: usize,
163        node_index: usize,
164        timestamps: Vec<f32>,
165        translations: Vec<[f32; 3]>,
166        interpolation: InterpolationType,
167    ) -> (usize, usize) {
168        if timestamps.len() != translations.len() {
169            panic!("Timestamps and translations must have the same length");
170        }
171        
172        // Create time input accessor
173        let timestamps_data: Vec<u8> = timestamps.iter().flat_map(|&t| t.to_le_bytes()).collect();
174        
175        // Add buffer data and create buffer view
176        let (time_offset, time_length) = self.add_buffer_data(&timestamps_data);
177        let time_buffer_view = self.add_buffer_view(
178            time_offset,
179            time_length,
180            None
181        );
182        
183        // Create input accessor (timestamps)
184        let input_accessor = self.add_accessor(
185            time_buffer_view,
186            5126, // FLOAT component type
187            timestamps.len(),
188            "SCALAR".to_string(),
189            None,
190            None,
191            None
192        );
193        
194        // Create translation output accessor
195        let translations_data: Vec<u8> = translations.iter().flat_map(|t| t.iter().flat_map(|&v| v.to_le_bytes())).collect();
196        
197        // Add buffer data and create buffer view
198        let (trans_offset, trans_length) = self.add_buffer_data(&translations_data);
199        let trans_buffer_view = self.add_buffer_view(
200            trans_offset,
201            trans_length,
202            None
203        );
204        
205        // Create output accessor (translations)
206        let output_accessor = self.add_accessor(
207            trans_buffer_view,
208            5126, // FLOAT component type
209            translations.len(),
210            "VEC3".to_string(),
211            None,
212            None,
213            None
214        );
215        
216        // Create sampler and channel
217        let sampler_index = self.add_animation_sampler(
218            animation_index,
219            input_accessor,
220            output_accessor,
221            interpolation,
222        );
223        
224        let channel_index = self.add_animation_channel(
225            animation_index,
226            sampler_index,
227            node_index,
228            AnimationPath::Translation,
229        );
230        
231        (channel_index, sampler_index)
232    }
233    
234    /// Create rotation keyframes for an animation
235    /// 
236    /// # Arguments
237    /// 
238    /// * `animation_index` - The index of the animation
239    /// * `node_index` - The index of the target node
240    /// * `timestamps` - Vector of keyframe timestamps (in seconds)
241    /// * `rotations` - Vector of rotation quaternions ([x, y, z, w] for each keyframe)
242    /// * `interpolation` - The interpolation method
243    /// 
244    /// # Returns
245    /// 
246    /// The indices of the created channel and sampler
247    pub fn create_rotation_animation(
248        &mut self,
249        animation_index: usize,
250        node_index: usize,
251        timestamps: Vec<f32>,
252        rotations: Vec<[f32; 4]>,
253        interpolation: InterpolationType,
254    ) -> (usize, usize) {
255        if timestamps.len() != rotations.len() {
256            panic!("Timestamps and rotations must have the same length");
257        }
258        
259        // Create time input accessor
260        let timestamps_data: Vec<u8> = timestamps.iter().flat_map(|&t| t.to_le_bytes()).collect();
261        
262        // Add buffer data and create buffer view
263        let (time_offset, time_length) = self.add_buffer_data(&timestamps_data);
264        let time_buffer_view = self.add_buffer_view(
265            time_offset,
266            time_length,
267            None
268        );
269        
270        // Create input accessor (timestamps)
271        let input_accessor = self.add_accessor(
272            time_buffer_view,
273            5126, // FLOAT component type
274            timestamps.len(),
275            "SCALAR".to_string(),
276            None,
277            None,
278            None
279        );
280        
281        // Create rotation output accessor
282        let rotations_data: Vec<u8> = rotations.iter().flat_map(|q| q.iter().flat_map(|&v| v.to_le_bytes())).collect();
283        
284        // Add buffer data and create buffer view
285        let (rot_offset, rot_length) = self.add_buffer_data(&rotations_data);
286        let rot_buffer_view = self.add_buffer_view(
287            rot_offset,
288            rot_length,
289            None
290        );
291        
292        // Create output accessor (rotations)
293        let output_accessor = self.add_accessor(
294            rot_buffer_view,
295            5126, // FLOAT component type
296            rotations.len(),
297            "VEC4".to_string(),
298            None,
299            None,
300            None
301        );
302        
303        // Create sampler and channel
304        let sampler_index = self.add_animation_sampler(
305            animation_index,
306            input_accessor,
307            output_accessor,
308            interpolation,
309        );
310        
311        let channel_index = self.add_animation_channel(
312            animation_index,
313            sampler_index,
314            node_index,
315            AnimationPath::Rotation,
316        );
317        
318        (channel_index, sampler_index)
319    }
320    
321    /// Create scale keyframes for an animation
322    /// 
323    /// # Arguments
324    /// 
325    /// * `animation_index` - The index of the animation
326    /// * `node_index` - The index of the target node
327    /// * `timestamps` - Vector of keyframe timestamps (in seconds)
328    /// * `scales` - Vector of scale values ([x, y, z] for each keyframe)
329    /// * `interpolation` - The interpolation method
330    /// 
331    /// # Returns
332    /// 
333    /// The indices of the created channel and sampler
334    pub fn create_scale_animation(
335        &mut self,
336        animation_index: usize,
337        node_index: usize,
338        timestamps: Vec<f32>,
339        scales: Vec<[f32; 3]>,
340        interpolation: InterpolationType,
341    ) -> (usize, usize) {
342        if timestamps.len() != scales.len() {
343            panic!("Timestamps and scales must have the same length");
344        }
345        
346        // Create time input accessor
347        let timestamps_data: Vec<u8> = timestamps.iter().flat_map(|&t| t.to_le_bytes()).collect();
348        
349        // Add buffer data and create buffer view
350        let (time_offset, time_length) = self.add_buffer_data(&timestamps_data);
351        let time_buffer_view = self.add_buffer_view(
352            time_offset,
353            time_length,
354            None
355        );
356        
357        // Create input accessor (timestamps)
358        let input_accessor = self.add_accessor(
359            time_buffer_view,
360            5126, // FLOAT component type
361            timestamps.len(),
362            "SCALAR".to_string(),
363            None,
364            None,
365            None
366        );
367        
368        // Create scale output accessor
369        let scales_data: Vec<u8> = scales.iter().flat_map(|s| s.iter().flat_map(|&v| v.to_le_bytes())).collect();
370        
371        // Add buffer data and create buffer view
372        let (scale_offset, scale_length) = self.add_buffer_data(&scales_data);
373        let scale_buffer_view = self.add_buffer_view(
374            scale_offset,
375            scale_length,
376            None
377        );
378        
379        // Create output accessor (scales)
380        let output_accessor = self.add_accessor(
381            scale_buffer_view,
382            5126, // FLOAT component type
383            scales.len(),
384            "VEC3".to_string(),
385            None,
386            None,
387            None
388        );
389        
390        // Create sampler and channel
391        let sampler_index = self.add_animation_sampler(
392            animation_index,
393            input_accessor,
394            output_accessor,
395            interpolation,
396        );
397        
398        let channel_index = self.add_animation_channel(
399            animation_index,
400            sampler_index,
401            node_index,
402            AnimationPath::Scale,
403        );
404        
405        (channel_index, sampler_index)
406    }
407}