viewport_lib/runtime/plugins/animation/
plugin.rs1use crate::interaction::selection::NodeId;
4use crate::runtime::context::RuntimeStepContext;
5use crate::runtime::plugin::{phase, RuntimePlugin};
6
7#[derive(Debug, Clone)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct Keyframe {
11 pub time: f32,
13 pub transform: glam::Affine3A,
15}
16
17#[derive(Debug, Clone)]
22pub struct AnimationTrack {
23 pub node_id: NodeId,
25 pub keyframes: Vec<Keyframe>,
27 pub looping: bool,
29}
30
31impl AnimationTrack {
32 pub fn duration(&self) -> f32 {
34 self.keyframes.last().map_or(0.0, |k| k.time)
35 }
36
37 pub fn sample(&self, mut time: f32) -> Option<glam::Affine3A> {
41 if self.keyframes.is_empty() {
42 return None;
43 }
44 if self.keyframes.len() == 1 {
45 return Some(self.keyframes[0].transform);
46 }
47
48 let dur = self.duration();
49 if self.looping && dur > 1e-6 {
50 time = time.rem_euclid(dur);
51 } else {
52 time = time.clamp(0.0, dur);
53 }
54
55 let upper = self.keyframes.partition_point(|k| k.time <= time);
57 let idx = upper.saturating_sub(1).min(self.keyframes.len() - 2);
58
59 let a = &self.keyframes[idx];
60 let b = &self.keyframes[idx + 1];
61
62 let span = b.time - a.time;
63 let t = if span > 1e-6 { (time - a.time) / span } else { 0.0 };
64
65 let (sa, ra, ta) = a.transform.to_scale_rotation_translation();
66 let (sb, rb, tb) = b.transform.to_scale_rotation_translation();
67
68 let s = sa.lerp(sb, t);
69 let r = ra.slerp(rb, t);
70 let p = ta.lerp(tb, t);
71
72 Some(glam::Affine3A::from_scale_rotation_translation(s, r, p))
73 }
74}
75
76pub struct AnimationPlugin {
100 tracks: Vec<AnimationTrack>,
101 time: f32,
102 playing: bool,
103 speed: f32,
104}
105
106impl Default for AnimationPlugin {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112impl AnimationPlugin {
113 pub fn new() -> Self {
115 Self {
116 tracks: Vec::new(),
117 time: 0.0,
118 playing: true,
119 speed: 1.0,
120 }
121 }
122
123 pub fn add_track(&mut self, track: AnimationTrack) {
125 self.tracks.push(track);
126 }
127
128 pub fn play(&mut self) {
130 self.playing = true;
131 }
132
133 pub fn pause(&mut self) {
135 self.playing = false;
136 }
137
138 pub fn reset(&mut self) {
140 self.time = 0.0;
141 }
142
143 pub fn set_speed(&mut self, speed: f32) {
145 self.speed = speed;
146 }
147
148 pub fn time(&self) -> f32 {
150 self.time
151 }
152
153 pub fn is_playing(&self) -> bool {
155 self.playing
156 }
157
158 pub fn duration(&self) -> f32 {
160 self.tracks
161 .iter()
162 .map(|t| t.duration())
163 .fold(0.0_f32, f32::max)
164 }
165}
166
167impl RuntimePlugin for AnimationPlugin {
168 fn priority(&self) -> i32 {
169 phase::ANIMATE
170 }
171
172 fn step(&mut self, ctx: &mut RuntimeStepContext<'_>) {
173 if self.playing {
174 self.time += ctx.dt * self.speed;
175 }
176 for track in &self.tracks {
177 if let Some(transform) = track.sample(self.time) {
178 ctx.writeback.set(track.node_id, transform);
179 }
180 }
181 }
182}