1use super::AnimError;
2use super::math::{lerp3, nlerp, quat_normalize, slerp};
3use super::pose::Pose;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
6pub enum Interpolation {
7 Step,
8 Linear,
9 CubicSpline,
10}
11
12impl Interpolation {
13 fn stride(self) -> usize {
14 match self {
15 Interpolation::CubicSpline => 3,
16 Interpolation::Step | Interpolation::Linear => 1,
17 }
18 }
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22pub enum Property {
23 Translation,
24 Rotation,
25 Scale,
26}
27
28impl Property {
29 pub fn width(self) -> usize {
30 match self {
31 Property::Rotation => 4,
32 Property::Translation | Property::Scale => 3,
33 }
34 }
35}
36
37#[derive(Clone, Debug, PartialEq)]
38pub struct Channel {
39 pub joint: usize,
40 pub property: Property,
41 pub interpolation: Interpolation,
42 pub times: Vec<f32>,
43 pub values: Vec<f32>,
44}
45
46impl Channel {
47 pub fn check(&self) -> Result<(), String> {
48 if self.times.is_empty() {
49 return Err("a channel has no keys".into());
50 }
51 if self
52 .times
53 .iter()
54 .any(|time| !time.is_finite() || *time < 0.0)
55 {
56 return Err("a key time is negative or not finite".into());
57 }
58 if self.times.windows(2).any(|pair| pair[1] <= pair[0]) {
59 return Err("key times do not rise".into());
60 }
61 let expected = self.times.len() * self.interpolation.stride() * self.property.width();
62 if self.values.len() != expected {
63 return Err(format!(
64 "a channel holds {} values where its {} keys need {expected}",
65 self.values.len(),
66 self.times.len()
67 ));
68 }
69 if self.values.iter().any(|value| !value.is_finite()) {
70 return Err("a key value is not finite".into());
71 }
72 Ok(())
73 }
74
75 fn key(&self, index: usize, part: usize) -> [f32; 4] {
76 let width = self.property.width();
77 let start = (index * self.interpolation.stride() + part) * width;
78 let mut out = [0.0; 4];
79 out[..width].copy_from_slice(&self.values[start..start + width]);
80 out
81 }
82
83 fn value(&self, index: usize) -> [f32; 4] {
84 match self.interpolation {
85 Interpolation::CubicSpline => self.key(index, 1),
86 Interpolation::Step | Interpolation::Linear => self.key(index, 0),
87 }
88 }
89
90 pub fn sample(&self, time: f32) -> [f32; 4] {
91 let after = self.times.partition_point(|key| *key <= time);
92 let last = self.times.len() - 1;
93 let raw = if after == 0 {
94 self.value(0)
95 } else if after > last {
96 self.value(last)
97 } else {
98 let k0 = after - 1;
99 let k1 = after;
100 let t0 = self.times[k0];
101 let t1 = self.times[k1];
102 let span = t1 - t0;
103 let u = ((time - t0) / span).clamp(0.0, 1.0);
104 match self.interpolation {
105 Interpolation::Step => self.value(k0),
106 Interpolation::Linear => {
107 let a = self.value(k0);
108 let b = self.value(k1);
109 if self.property == Property::Rotation {
110 slerp(a, b, u)
111 } else {
112 std::array::from_fn(|k| a[k] + (b[k] - a[k]) * u)
113 }
114 }
115 Interpolation::CubicSpline => {
116 let v0 = self.key(k0, 1);
117 let b0 = self.key(k0, 2);
118 let a1 = self.key(k1, 0);
119 let v1 = self.key(k1, 1);
120 let u2 = u * u;
121 let u3 = u2 * u;
122 let h00 = 2.0 * u3 - 3.0 * u2 + 1.0;
123 let h10 = u3 - 2.0 * u2 + u;
124 let h01 = -2.0 * u3 + 3.0 * u2;
125 let h11 = u3 - u2;
126 std::array::from_fn(|k| {
127 h00 * v0[k] + h10 * span * b0[k] + h01 * v1[k] + h11 * span * a1[k]
128 })
129 }
130 }
131 };
132 if self.property == Property::Rotation {
133 quat_normalize(raw)
134 } else {
135 raw
136 }
137 }
138
139 pub fn end(&self) -> f32 {
140 self.times.last().copied().unwrap_or(0.0)
141 }
142}
143
144#[derive(Clone, Debug, PartialEq)]
145pub struct ClipEvent {
146 pub time: f32,
147 pub name: String,
148}
149
150impl ClipEvent {
151 pub fn new(time: f32, name: impl Into<String>) -> Self {
152 Self {
153 time,
154 name: name.into(),
155 }
156 }
157}
158
159#[derive(Clone, Debug, PartialEq)]
160pub struct Clip {
161 name: String,
162 channels: Vec<Channel>,
163 events: Vec<ClipEvent>,
164 duration: f32,
165}
166
167impl Clip {
168 pub fn new(name: impl Into<String>, channels: Vec<Channel>) -> Result<Self, AnimError> {
169 let name = name.into();
170 for channel in &channels {
171 channel.check().map_err(|reason| AnimError::Channel {
172 clip: name.clone(),
173 reason,
174 })?;
175 }
176 let duration = channels.iter().map(Channel::end).fold(0.0f32, f32::max);
177 Ok(Self {
178 name,
179 channels,
180 events: Vec::new(),
181 duration,
182 })
183 }
184
185 pub(crate) fn empty(name: &str) -> Self {
186 Self {
187 name: name.to_string(),
188 channels: Vec::new(),
189 events: Vec::new(),
190 duration: 0.0,
191 }
192 }
193
194 pub fn with_duration(mut self, duration: f32) -> Result<Self, AnimError> {
195 if !duration.is_finite() || duration < self.duration {
196 return Err(AnimError::Channel {
197 clip: self.name.clone(),
198 reason: format!(
199 "a duration of {duration} is shorter than the clip's keys ({})",
200 self.duration
201 ),
202 });
203 }
204 self.duration = duration;
205 Ok(self)
206 }
207
208 pub fn with_events(mut self, mut events: Vec<ClipEvent>) -> Result<Self, AnimError> {
209 for event in &events {
210 if !event.time.is_finite() || event.time < 0.0 || event.time > self.duration {
211 return Err(AnimError::Event {
212 clip: self.name.clone(),
213 event: event.name.clone(),
214 reason: format!(
215 "its time {} is outside the clip's 0 to {}",
216 event.time, self.duration
217 ),
218 });
219 }
220 }
221 events.sort_by(|a, b| a.time.total_cmp(&b.time).then_with(|| a.name.cmp(&b.name)));
222 self.events = events;
223 Ok(self)
224 }
225
226 pub fn name(&self) -> &str {
227 &self.name
228 }
229
230 pub fn channels(&self) -> &[Channel] {
231 &self.channels
232 }
233
234 pub fn events(&self) -> &[ClipEvent] {
235 &self.events
236 }
237
238 pub fn duration(&self) -> f32 {
239 self.duration
240 }
241
242 pub fn sample(&self, time: f32, pose: &mut Pose) {
243 self.apply(time, pose, 1.0);
244 }
245
246 pub fn apply(&self, time: f32, pose: &mut Pose, weight: f32) {
247 if weight <= 0.0 {
248 return;
249 }
250 let locals = pose.locals_mut();
251 for channel in &self.channels {
252 let Some(local) = locals.get_mut(channel.joint) else {
253 continue;
254 };
255 let value = channel.sample(time);
256 let three = [value[0], value[1], value[2]];
257 match channel.property {
258 Property::Translation if weight >= 1.0 => local.translation = three,
259 Property::Translation => {
260 local.translation = lerp3(local.translation, three, weight)
261 }
262 Property::Scale if weight >= 1.0 => local.scale = three,
263 Property::Scale => local.scale = lerp3(local.scale, three, weight),
264 Property::Rotation if weight >= 1.0 => local.rotation = value,
265 Property::Rotation => local.rotation = nlerp(local.rotation, value, weight),
266 }
267 }
268 }
269}