1use std::collections::HashMap;
2
3use bevy::prelude::*;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum Interpolation {
8 Step,
10 Linear
11}
12
13#[derive(Clone, Debug)]
15pub struct Keyframes<T> {
16 pub times: Vec<f32>,
17 pub values: Vec<T>,
18 pub interpolation: Interpolation
19}
20
21pub trait Interpolate: Copy {
23 fn interpolate(from: Self, to: Self, t: f32) -> Self;
24}
25impl Interpolate for Vec3 {
26 fn interpolate(from: Self, to: Self, t: f32) -> Self {
27 from.lerp(to, t)
28 }
29}
30impl Interpolate for Quat {
31 fn interpolate(from: Self, to: Self, t: f32) -> Self {
32 from.slerp(to, t)
33 }
34}
35
36impl<T: Interpolate> Keyframes<T> {
37 pub fn sample(&self, time: f32) -> Option<T> {
39 let count = self.times.len().min(self.values.len());
40 if count == 0 {
41 return None;
42 }
43 let next = self.times[..count].partition_point(|&t| t <= time);
44 if next == 0 {
45 return Some(self.values[0]);
46 }
47 if next == count {
48 return Some(self.values[count - 1]);
49 }
50 let (from, to) = (self.times[next - 1], self.times[next]);
51 Some(match self.interpolation {
52 Interpolation::Step => self.values[next - 1],
53 Interpolation::Linear => T::interpolate(
54 self.values[next - 1],
55 self.values[next],
56 (time - from) / (to - from).max(f32::EPSILON)
57 )
58 })
59 }
60}
61
62#[derive(Clone, Debug, Default)]
64pub struct BoneTrack {
65 pub translation: Option<Keyframes<Vec3>>,
66 pub rotation: Option<Keyframes<Quat>>,
67 pub scale: Option<Keyframes<Vec3>>
68}
69impl BoneTrack {
70 pub fn sample(&self, time: f32, rest: &Transform) -> Transform {
72 let sample = |keys: &Option<Keyframes<Vec3>>, default| {
73 keys.as_ref()
74 .and_then(|k| k.sample(time))
75 .unwrap_or(default)
76 };
77 Transform {
78 translation: sample(&self.translation, rest.translation),
79 rotation: self
80 .rotation
81 .as_ref()
82 .and_then(|k| k.sample(time))
83 .unwrap_or(rest.rotation),
84 scale: sample(&self.scale, rest.scale)
85 }
86 }
87}
88
89#[derive(Asset, TypePath, Clone, Debug, Default)]
91pub struct AnimationClip {
92 pub name: String,
93 pub duration: f32,
95 pub tracks: HashMap<String, BoneTrack>,
96 pub ground_speed: Option<f32>
99}