viewport_lib/plugins/animation/
plugin.rs1use crate::interaction::selection::NodeId;
4use crate::runtime::context::RuntimeStepContext;
5use crate::runtime::plugin::{RuntimePlugin, phase};
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 {
64 (time - a.time) / span
65 } else {
66 0.0
67 };
68
69 let (sa, ra, ta) = a.transform.to_scale_rotation_translation();
70 let (sb, rb, tb) = b.transform.to_scale_rotation_translation();
71
72 let s = sa.lerp(sb, t);
73 let r = ra.slerp(rb, t);
74 let p = ta.lerp(tb, t);
75
76 Some(glam::Affine3A::from_scale_rotation_translation(s, r, p))
77 }
78}
79
80pub struct AnimationPlugin {
104 tracks: Vec<AnimationTrack>,
105 time: f32,
106 playing: bool,
107 speed: f32,
108}
109
110impl Default for AnimationPlugin {
111 fn default() -> Self {
112 Self::new()
113 }
114}
115
116impl AnimationPlugin {
117 pub fn new() -> Self {
119 Self {
120 tracks: Vec::new(),
121 time: 0.0,
122 playing: true,
123 speed: 1.0,
124 }
125 }
126
127 pub fn add_track(&mut self, track: AnimationTrack) {
129 self.tracks.push(track);
130 }
131
132 pub fn play(&mut self) {
134 self.playing = true;
135 }
136
137 pub fn pause(&mut self) {
139 self.playing = false;
140 }
141
142 pub fn reset(&mut self) {
144 self.time = 0.0;
145 }
146
147 pub fn set_speed(&mut self, speed: f32) {
149 self.speed = speed;
150 }
151
152 pub fn time(&self) -> f32 {
154 self.time
155 }
156
157 pub fn is_playing(&self) -> bool {
159 self.playing
160 }
161
162 pub fn duration(&self) -> f32 {
164 self.tracks
165 .iter()
166 .map(|t| t.duration())
167 .fold(0.0_f32, f32::max)
168 }
169}
170
171impl RuntimePlugin for AnimationPlugin {
172 fn priority(&self) -> i32 {
173 phase::ANIMATE
174 }
175
176 fn step(&mut self, ctx: &mut RuntimeStepContext<'_>) {
177 if self.playing {
178 self.time += ctx.dt * self.speed;
179 }
180 for track in &self.tracks {
181 if let Some(transform) = track.sample(self.time) {
182 ctx.writeback.set(track.node_id, transform);
183 }
184 }
185 }
186}