1use rustc_hash::FxHashMap;
2use slotmap::{SlotMap, new_key_type};
3
4use crate::action::AnimationAction;
5use crate::binding::{Rig, TargetPath};
6use crate::blending::{BlendEntry, FrameBlendState};
7use crate::clip::TrackData;
8use crate::events::{self, FiredEvent};
9use crate::target::AnimationTarget;
10use myth_core::NodeHandle;
11
12new_key_type! {
13 pub struct ActionHandle;
14}
15
16pub struct AnimationMixer {
40 actions: SlotMap<ActionHandle, AnimationAction>,
41 name_map: FxHashMap<String, ActionHandle>,
42 active_handles: Vec<ActionHandle>,
43
44 rig: Rig,
46
47 pub time: f32,
49 pub time_scale: f32,
51
52 blend_state: FrameBlendState,
54 fired_events: Vec<FiredEvent>,
56 animated_last_frame: Vec<NodeHandle>,
59
60 morph_buffer: crate::values::MorphWeightData,
62
63 pub enabled: bool,
64}
65
66impl Default for AnimationMixer {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl AnimationMixer {
73 #[must_use]
74 pub fn new() -> Self {
75 Self {
76 actions: SlotMap::with_key(),
77 name_map: FxHashMap::default(),
78 active_handles: Vec::new(),
79 rig: Rig {
80 bones: Vec::new(),
81 bone_paths: Vec::new(),
82 },
83 time: 0.0,
84 time_scale: 1.0,
85 blend_state: FrameBlendState::new(),
86 fired_events: Vec::new(),
87 animated_last_frame: Vec::new(),
88 morph_buffer: crate::values::MorphWeightData::default(),
89 enabled: true,
90 }
91 }
92
93 pub fn set_rig(&mut self, rig: Rig) {
95 self.rig = rig;
96 }
97
98 #[must_use]
100 pub fn rig(&self) -> &Rig {
101 &self.rig
102 }
103
104 #[must_use]
106 pub fn list_animations(&self) -> Vec<String> {
107 self.name_map.keys().cloned().collect()
108 }
109
110 pub fn add_action(&mut self, action: AnimationAction) -> ActionHandle {
112 let name = action.clip().name.clone();
113 let handle = self.actions.insert(action);
114 self.name_map.insert(name, handle);
115 handle
116 }
117
118 #[must_use]
120 pub fn get_action(&self, name: &str) -> Option<&AnimationAction> {
121 let handle = *self.name_map.get(name)?;
122 self.actions.get(handle)
123 }
124
125 #[must_use]
127 pub fn get_action_by_handle(&self, handle: ActionHandle) -> Option<&AnimationAction> {
128 self.actions.get(handle)
129 }
130
131 pub fn action(&mut self, name: &str) -> Option<ActionControl<'_>> {
133 let handle = *self.name_map.get(name)?;
134 Some(ActionControl {
135 mixer: self,
136 handle,
137 })
138 }
139
140 pub fn any_action(&mut self) -> Option<ActionControl<'_>> {
142 if let Some((handle, _)) = self.actions.iter().next() {
143 Some(ActionControl {
144 mixer: self,
145 handle,
146 })
147 } else {
148 None
149 }
150 }
151
152 pub fn get_control(&mut self, handle: ActionHandle) -> Option<ActionControl<'_>> {
154 if self.actions.contains_key(handle) {
155 Some(ActionControl {
156 mixer: self,
157 handle,
158 })
159 } else {
160 None
161 }
162 }
163
164 pub fn get_control_by_name(&mut self, name: &str) -> Option<ActionControl<'_>> {
165 let handle = *self.name_map.get(name)?;
166 self.get_control(handle)
167 }
168
169 pub fn play(&mut self, name: &str) {
171 if let Some(&handle) = self.name_map.get(name) {
172 if !self.active_handles.contains(&handle) {
173 self.active_handles.push(handle);
174 }
175 if let Some(action) = self.actions.get_mut(handle) {
176 action.enabled = true;
177 action.weight = 1.0;
178 action.paused = false;
179 }
180 } else {
181 log::warn!("Animation not found: {name}");
182 }
183 }
184
185 pub fn stop(&mut self, name: &str) {
187 if let Some(&handle) = self.name_map.get(name) {
188 if let Some(action) = self.actions.get_mut(handle) {
189 action.stop();
190 }
191 self.active_handles.retain(|&h| h != handle);
192 }
193 }
194
195 pub fn stop_all(&mut self) {
197 for handle in &self.active_handles {
198 if let Some(action) = self.actions.get_mut(*handle) {
199 action.stop();
200 }
201 }
202 self.active_handles.clear();
203 }
204
205 pub fn drain_events(&mut self) -> Vec<FiredEvent> {
207 std::mem::take(&mut self.fired_events)
208 }
209
210 #[must_use]
212 pub fn events(&self) -> &[FiredEvent] {
213 &self.fired_events
214 }
215
216 pub fn update(&mut self, dt: f32, target: &mut dyn AnimationTarget) {
230 if !self.enabled {
231 return;
232 }
233
234 for &prev_handle in &self.animated_last_frame {
236 if let Some(rest) = target.rest_transform(prev_handle) {
237 target.set_node_position(prev_handle, rest.position);
238 target.set_node_rotation(prev_handle, rest.rotation);
239 target.set_node_scale(prev_handle, rest.scale);
240 target.mark_node_dirty(prev_handle);
241 }
242 }
243
244 let dt = dt * self.time_scale;
245 self.time += dt;
246
247 self.blend_state.clear();
249 self.fired_events.clear();
250
251 self.animated_last_frame.clear();
252
253 for &handle in &self.active_handles {
255 if let Some(action) = self.actions.get_mut(handle) {
256 let t_prev = action.time;
257
258 let mut real_time_scale = action.time_scale;
259 if action.loop_mode == crate::action::LoopMode::PingPong && action.ping_pong_reverse
260 {
261 real_time_scale = -real_time_scale;
262 }
263
264 let is_forward = (dt * real_time_scale) >= 0.0;
265
266 action.update(dt);
267 let t_curr = action.time;
268
269 let clip = action.clip();
270 events::collect_events(
271 &clip.events,
272 t_prev,
273 t_curr,
274 clip.duration,
275 is_forward,
276 &clip.name,
277 &mut self.fired_events,
278 );
279 }
280 }
281
282 for &handle in &self.active_handles {
284 let action = match self.actions.get_mut(handle) {
285 Some(a) if a.enabled && !a.paused && a.weight > 0.0 => a,
286 _ => continue,
287 };
288
289 let AnimationAction {
290 clip,
291 track_cursors,
292 clip_binding,
293 time,
294 weight,
295 ..
296 } = action;
297
298 for tb in &clip_binding.bindings {
299 let track = &clip.tracks[tb.track_index];
300 let cursor = &mut track_cursors[tb.track_index];
301 let node_handle = self.rig.bones[tb.bone_index];
302
303 match (&track.data, tb.target) {
304 (TrackData::Vector3(t), TargetPath::Translation) => {
305 let val = t.sample_with_cursor(*time, cursor);
306 self.blend_state
307 .accumulate_translation(node_handle, val, *weight);
308 }
309 (TrackData::Vector3(t), TargetPath::Scale) => {
310 let val = t.sample_with_cursor(*time, cursor);
311 self.blend_state.accumulate_scale(node_handle, val, *weight);
312 }
313 (TrackData::Quaternion(t), TargetPath::Rotation) => {
314 let val = t.sample_with_cursor(*time, cursor);
315 self.blend_state
316 .accumulate_rotation(node_handle, val, *weight);
317 }
318 (TrackData::MorphWeights(t), TargetPath::Weights) => {
319 t.sample_with_cursor_into(*time, cursor, &mut self.morph_buffer);
320 self.blend_state.accumulate_morph_weights(
321 node_handle,
322 &self.morph_buffer,
323 *weight,
324 );
325 }
326 _ => {}
327 }
328 }
329 }
330
331 for (&node_handle, props) in self.blend_state.iter_nodes() {
333 self.animated_last_frame.push(node_handle);
334
335 let rest_transform = target.rest_transform(node_handle).unwrap_or_default();
336
337 for (t, entry) in props {
338 match (t, entry) {
339 (TargetPath::Translation, BlendEntry::Translation { value, weight }) => {
340 if *weight < 1.0 {
341 target.set_node_position(
342 node_handle,
343 rest_transform.position.lerp(*value, *weight),
344 );
345 } else {
346 target.set_node_position(node_handle, *value);
347 }
348 target.mark_node_dirty(node_handle);
349 }
350 (TargetPath::Rotation, BlendEntry::Rotation { value, weight }) => {
351 if *weight < 1.0 {
352 let corrected = if rest_transform.rotation.dot(*value) < 0.0 {
353 -*value
354 } else {
355 *value
356 };
357 target.set_node_rotation(
358 node_handle,
359 rest_transform.rotation.lerp(corrected, *weight).normalize(),
360 );
361 } else {
362 target.set_node_rotation(node_handle, *value);
363 }
364 target.mark_node_dirty(node_handle);
365 }
366 (TargetPath::Scale, BlendEntry::Scale { value, weight }) => {
367 if *weight < 1.0 {
368 target.set_node_scale(
369 node_handle,
370 rest_transform.scale.lerp(*value, *weight),
371 );
372 } else {
373 target.set_node_scale(node_handle, *value);
374 }
375 target.mark_node_dirty(node_handle);
376 }
377 (
378 TargetPath::Weights,
379 BlendEntry::MorphWeights {
380 weights,
381 total_weight,
382 },
383 ) => {
384 apply_morph_weights(target, node_handle, weights, *total_weight);
385 }
386 _ => {}
387 }
388 }
389 }
390 }
391}
392
393fn apply_morph_weights(
396 target: &mut dyn AnimationTarget,
397 node: NodeHandle,
398 weights: &[f32],
399 total_weight: f32,
400) {
401 let dst = target.morph_weights_mut(node);
402 if dst.len() < weights.len() {
403 dst.resize(weights.len(), 0.0);
404 }
405 if total_weight >= 1.0 {
406 dst[..weights.len()].copy_from_slice(weights);
407 } else {
408 for (d, &src) in dst.iter_mut().zip(weights.iter()) {
409 *d = src * total_weight;
410 }
411 }
412}
413
414pub struct ActionControl<'a> {
423 mixer: &'a mut AnimationMixer,
424 handle: ActionHandle,
425}
426
427#[allow(clippy::return_self_not_must_use)]
428#[allow(clippy::must_use_candidate)]
429impl ActionControl<'_> {
430 pub fn play(self) -> Self {
432 if !self.mixer.active_handles.contains(&self.handle) {
433 self.mixer.active_handles.push(self.handle);
434 }
435 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
436 action.enabled = true;
437 action.paused = false;
438 action.weight = 1.0;
439 action.time = 0.0;
440 }
441 self
442 }
443
444 pub fn set_loop_mode(self, mode: crate::action::LoopMode) -> Self {
445 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
446 action.loop_mode = mode;
447 action.ping_pong_reverse = false;
448 }
449 self
450 }
451
452 pub fn set_time_scale(self, scale: f32) -> Self {
453 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
454 action.time_scale = scale;
455 }
456 self
457 }
458
459 pub fn set_weight(self, weight: f32) -> Self {
460 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
461 action.weight = weight;
462 }
463 self
464 }
465
466 pub fn set_time(self, time: f32) -> Self {
467 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
468 action.time = time;
469 }
470 self
471 }
472
473 pub fn resume(self) -> Self {
474 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
475 action.paused = false;
476 }
477 self
478 }
479
480 pub fn pause(self) -> Self {
481 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
482 action.paused = true;
483 }
484 self
485 }
486
487 pub fn stop(self) -> Self {
489 if let Some(action) = self.mixer.actions.get_mut(self.handle) {
490 action.enabled = false;
491 action.weight = 0.0;
492 }
493 self.mixer.active_handles.retain(|&h| h != self.handle);
494 self
495 }
496
497 pub fn fade_in(self, _duration: f32) -> Self {
499 self.play()
501 }
502}
503
504impl std::ops::Deref for ActionControl<'_> {
505 type Target = AnimationAction;
506 fn deref(&self) -> &Self::Target {
507 self.mixer.actions.get(self.handle).unwrap()
508 }
509}