dreamwell_engine/content/
animation.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
7pub enum Interpolation {
8 Step,
9 #[default]
10 Linear,
11 CubicSpline,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Keyframe {
17 pub time: f32,
19 pub value: Vec<f32>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct AnimationTrack {
29 pub node_index: usize,
31 pub property: AnimatedProperty,
33 pub interpolation: Interpolation,
35 pub keyframes: Vec<Keyframe>,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
41pub enum AnimatedProperty {
42 Translation,
43 Rotation,
44 Scale,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct ImportedAnimation {
50 pub name: String,
52 pub duration: f32,
54 pub tracks: Vec<AnimationTrack>,
56}
57
58impl ImportedAnimation {
59 pub fn track_count(&self) -> usize {
61 self.tracks.len()
62 }
63
64 pub fn total_keyframes(&self) -> usize {
66 self.tracks.iter().map(|t| t.keyframes.len()).sum()
67 }
68
69 pub fn validate(&self) -> Result<(), String> {
71 if self.duration < 0.0 || !self.duration.is_finite() {
72 return Err(format!("content_animation_invalid_duration:{}", self.duration));
73 }
74 for (i, track) in self.tracks.iter().enumerate() {
75 for w in track.keyframes.windows(2) {
77 if w[1].time < w[0].time {
78 return Err(format!(
79 "content_animation_unsorted_keyframes:track[{i}] time {} > {}",
80 w[0].time, w[1].time
81 ));
82 }
83 }
84 }
85 Ok(())
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 fn sample_animation() -> ImportedAnimation {
94 ImportedAnimation {
95 name: "Walk".into(),
96 duration: 1.0,
97 tracks: vec![AnimationTrack {
98 node_index: 0,
99 property: AnimatedProperty::Translation,
100 interpolation: Interpolation::Linear,
101 keyframes: vec![
102 Keyframe {
103 time: 0.0,
104 value: vec![0.0, 0.0, 0.0],
105 },
106 Keyframe {
107 time: 0.5,
108 value: vec![1.0, 0.0, 0.0],
109 },
110 Keyframe {
111 time: 1.0,
112 value: vec![2.0, 0.0, 0.0],
113 },
114 ],
115 }],
116 }
117 }
118
119 #[test]
120 fn valid_animation() {
121 let anim = sample_animation();
122 assert!(anim.validate().is_ok());
123 assert_eq!(anim.track_count(), 1);
124 assert_eq!(anim.total_keyframes(), 3);
125 }
126
127 #[test]
128 fn negative_duration() {
129 let mut anim = sample_animation();
130 anim.duration = -1.0;
131 assert!(anim.validate().unwrap_err().contains("invalid_duration"));
132 }
133
134 #[test]
135 fn unsorted_keyframes() {
136 let mut anim = sample_animation();
137 anim.tracks[0].keyframes[1].time = 2.0; assert!(anim.validate().unwrap_err().contains("unsorted"));
139 }
140
141 #[test]
142 fn interpolation_default_linear() {
143 assert_eq!(Interpolation::default(), Interpolation::Linear);
144 }
145}