1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Animation playback state and controls.
use super::{clip::AnimationClip, data::AnimationData};
/// Animation player for a clip.
#[derive(Debug, Clone)]
pub struct AnimationPlayer<T = AnimationData> {
/// Playback-rate multiplier (dimensionless): `1.0` plays at the authored
/// rate, `2.0` double speed, `0.5` half. The clock runs in **seconds**
/// throughout — `update` is handed a seconds delta — so this carries no unit
/// conversion (`update_animations` converts its millisecond input once).
pub speed: f64,
pub loop_style: Option<AnimationLoopStyle>,
// will change with ping-pong as each end is hit
pub play_direction: AnimationPlayDirection,
clip: AnimationClip<T>,
state: AnimationState,
local_time: f64,
}
/// Playback state for an animation player.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AnimationState {
Playing,
Paused,
Ended,
}
/// Looping behavior for animation playback.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AnimationLoopStyle {
Loop,
PingPong,
}
/// Playback direction for an animation player.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AnimationPlayDirection {
Forward,
Backward,
}
impl<T> AnimationPlayer<T> {
/// Creates a new animation player for a clip.
pub fn new(clip: AnimationClip<T>) -> Self {
Self {
speed: 1.0,
loop_style: Some(AnimationLoopStyle::Loop),
play_direction: AnimationPlayDirection::Forward,
clip,
state: AnimationState::Playing,
local_time: 0.0,
}
}
/// Advances the animation by `dt_seconds` (the same unit as the clip's
/// keyframe times / duration; `update_animations` converts the frame's
/// millisecond delta to seconds before calling this).
pub fn update(&mut self, dt_seconds: f64) {
if self.state != AnimationState::Playing {
return;
}
let local_time_delta = dt_seconds * self.speed;
match self.play_direction {
AnimationPlayDirection::Forward => {
self.local_time += local_time_delta;
if self.local_time >= self.clip.duration {
match self.loop_style {
Some(AnimationLoopStyle::Loop) => {
self.local_time = self.local_time.rem_euclid(self.clip.duration);
}
Some(AnimationLoopStyle::PingPong) => {
self.play_direction = AnimationPlayDirection::Backward;
self.local_time = self.clip.duration;
}
None => {
self.local_time = self.clip.duration;
self.state = AnimationState::Ended;
}
}
}
}
AnimationPlayDirection::Backward => {
self.local_time -= local_time_delta;
if self.local_time <= 0.0 {
match self.loop_style {
Some(AnimationLoopStyle::Loop) => {
self.local_time =
self.clip.duration - self.local_time.rem_euclid(self.clip.duration);
}
Some(AnimationLoopStyle::PingPong) => {
self.play_direction = AnimationPlayDirection::Forward;
self.local_time = 0.0;
}
None => {
self.local_time = 0.0;
self.state = AnimationState::Ended;
}
}
}
}
}
}
}
impl<T> AnimationPlayer<T> {
/// The clip's duration (seconds).
pub fn duration(&self) -> f64 {
self.clip.duration
}
/// The current local playback time (seconds).
pub fn local_time(&self) -> f64 {
self.local_time
}
/// Seeks to an absolute local time (clamped to `[0, duration]`). Used by the
/// editor transport to scrub a paused clip. Does not change `state`.
pub fn set_local_time(&mut self, time: f64) {
self.local_time = time.clamp(0.0, self.clip.duration.max(0.0));
}
/// The current playback state.
pub fn state(&self) -> &AnimationState {
&self.state
}
/// Sets the playback state (e.g. `Paused` while the editor scrubs).
pub fn set_state(&mut self, state: AnimationState) {
self.state = state;
}
/// Resets to the start, forward, playing.
pub fn reset(&mut self) {
self.local_time = 0.0;
self.state = AnimationState::Playing;
self.play_direction = AnimationPlayDirection::Forward;
}
}
impl AnimationPlayer<AnimationData> {
/// Samples the animation at the current local time.
pub fn sample(&self) -> AnimationData {
self.clip.sampler.sample(self.local_time)
}
/// Samples at an arbitrary time without mutating `local_time` (for the editor
/// scrubbing a paused clip / one-shot pose reads).
pub fn sample_at(&self, time: f64) -> AnimationData {
self.clip.sampler.sample(time)
}
}