1use bevy::prelude::*;
2
3use crate::clip::AnimationClip;
4
5#[derive(Component, Clone, Debug)]
7pub struct AnimationBone {
8 pub name: String,
9 pub rest: Transform
11}
12
13#[derive(Component)]
15pub(crate) struct BoneOf(Entity);
16
17#[derive(Clone, Debug)]
19struct PlayingClip {
20 clip: Handle<AnimationClip>,
21 time: f32,
22 speed: f32,
23 repeat: bool,
25 finished: bool
26}
27impl PlayingClip {
28 fn new(clip: Handle<AnimationClip>, speed: f32, repeat: bool) -> Self {
29 Self {
30 clip,
31 time: 0.0,
32 speed,
33 repeat,
34 finished: false
35 }
36 }
37}
38
39#[derive(Component, Clone, Debug, Default)]
42pub struct AnimationPlayer {
43 active: Option<PlayingClip>,
44 previous: Option<PlayingClip>,
46 fade: f32,
47 fade_duration: f32
48}
49impl AnimationPlayer {
50 pub fn play(&mut self, clip: Handle<AnimationClip>) -> &mut Self {
52 self.active = Some(PlayingClip::new(clip, 1.0, true));
53 self.previous = None;
54 self
55 }
56
57 pub fn play_once(&mut self, clip: Handle<AnimationClip>, fade: f32) -> &mut Self {
60 self.previous = self.active.take();
61 self.active = Some(PlayingClip::new(clip, 1.0, false));
62 self.fade = 0.0;
63 self.fade_duration = fade;
64 self
65 }
66
67 pub fn is_finished(&self) -> bool {
69 self.active.as_ref().is_some_and(|a| a.finished)
70 }
71
72 pub fn crossfade(&mut self, clip: Handle<AnimationClip>, duration: f32) -> &mut Self {
75 if self.is_playing(&clip) {
76 return self;
77 }
78 let speed = self.active.as_ref().map_or(1.0, |a| a.speed);
79 self.previous = self.active.take();
80 self.active = Some(PlayingClip::new(clip, speed, true));
81 self.fade = 0.0;
82 self.fade_duration = duration;
83 self
84 }
85
86 pub fn set_speed(&mut self, speed: f32) -> &mut Self {
88 if let Some(active) = &mut self.active {
89 active.speed = speed;
90 }
91 self
92 }
93
94 pub fn stop(&mut self) {
96 self.active = None;
97 self.previous = None;
98 }
99
100 pub fn is_playing(&self, clip: &Handle<AnimationClip>) -> bool {
101 self.active.as_ref().is_some_and(|a| a.clip == *clip)
102 }
103
104 fn blend(&self) -> f32 {
106 match self.previous {
107 Some(_) => (self.fade / self.fade_duration.max(f32::EPSILON)).min(1.0),
108 None => 1.0
109 }
110 }
111}
112
113pub(crate) fn link_bones(
115 mut commands: Commands,
116 bones: Query<Entity, (With<AnimationBone>, Without<BoneOf>)>,
117 parents: Query<&ChildOf>,
118 players: Query<(), With<AnimationPlayer>>
119) {
120 for bone in &bones {
121 if let Some(player) = parents.iter_ancestors(bone).find(|e| players.contains(*e)) {
122 commands.entity(bone).insert(BoneOf(player));
123 }
124 }
125}
126
127pub(crate) fn advance_players(
128 mut players: Query<&mut AnimationPlayer>,
129 clips: Res<Assets<AnimationClip>>,
130 time: Res<Time>
131) {
132 let dt = time.delta_secs();
133 for mut player in &mut players {
134 let player = player.as_mut();
135 for playing in [&mut player.active, &mut player.previous]
136 .into_iter()
137 .flatten()
138 {
139 let duration = clips.get(&playing.clip).map_or(0.0, |c| c.duration);
140 if duration <= 0.0 {
141 playing.time = 0.0;
142 } else if playing.repeat {
143 playing.time = (playing.time + dt * playing.speed).rem_euclid(duration);
144 } else {
145 playing.time = (playing.time + dt * playing.speed).min(duration);
146 playing.finished = playing.time >= duration;
147 }
148 }
149 if player.previous.is_some() {
150 player.fade += dt;
151 if player.fade >= player.fade_duration {
152 player.previous = None;
153 }
154 }
155 }
156}
157
158pub(crate) fn pose_bones(
159 mut bones: Query<(&AnimationBone, &BoneOf, &mut Transform)>,
160 players: Query<&AnimationPlayer>,
161 clips: Res<Assets<AnimationClip>>
162) {
163 for (bone, bone_of, mut transform) in &mut bones {
164 let Ok(player) = players.get(bone_of.0) else {
165 continue;
166 };
167 let pose = |playing: &Option<PlayingClip>| {
168 playing.as_ref().map(|p| {
169 clips
170 .get(&p.clip)
171 .and_then(|c| c.tracks.get(&bone.name))
172 .map_or(bone.rest, |track| track.sample(p.time, &bone.rest))
173 })
174 };
175 let active = pose(&player.active).unwrap_or(bone.rest);
176 let posed = match pose(&player.previous) {
177 Some(previous) => {
178 let t = player.blend();
179 Transform {
180 translation: previous.translation.lerp(active.translation, t),
181 rotation: previous.rotation.slerp(active.rotation, t),
182 scale: previous.scale.lerp(active.scale, t)
183 }
184 }
185 None => active
186 };
187 transform.set_if_neq(posed);
188 }
189}