fyrox_animation/lib.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Animation allows you to change properties of arbitrary objects at runtime using a set of key frames.
22//! See [`Animation`] docs for more info.
23
24#![warn(missing_docs)]
25#![allow(clippy::doc_lazy_continuation)]
26#![allow(mismatched_lifetime_syntaxes)]
27
28use crate::{
29 core::{
30 algebra::{UnitQuaternion, Vector3},
31 math::wrapf,
32 pool::{ErasedHandle, Handle, Pool, Ticket},
33 reflect::prelude::*,
34 type_traits::prelude::*,
35 uuid::{uuid, Uuid},
36 visitor::{Visit, VisitResult, Visitor},
37 ImmutableString, NameProvider,
38 },
39 track::Track,
40};
41use fxhash::FxHashMap;
42use fyrox_resource::{Resource, ResourceData};
43use std::{
44 collections::VecDeque,
45 error::Error,
46 fmt::Debug,
47 hash::Hash,
48 ops::{Index, IndexMut, Range},
49 path::Path,
50};
51use value::{nlerp, TrackValue, ValueBinding};
52
53use crate::container::TrackDataContainer;
54use crate::track::TrackBinding;
55pub use fyrox_core as core;
56use fyrox_resource::untyped::ResourceKind;
57pub use pose::{AnimationPose, NodePose};
58pub use signal::{AnimationEvent, AnimationSignal};
59
60pub mod container;
61pub mod machine;
62pub mod pose;
63pub mod signal;
64pub mod spritesheet;
65pub mod track;
66pub mod value;
67
68/// A container for animation tracks. Multiple animations can share the same container to reduce
69/// memory consumption. It could be extremely useful in case of many instances of a little amount
70/// of kinds of animated models.
71#[derive(Default, Debug, Reflect, Clone, PartialEq, TypeUuidProvider)]
72#[type_uuid(id = "044d9f7c-5c6c-4b29-8de9-d0d975a48256")]
73pub struct AnimationTracksData {
74 /// Tracks of the animation. See [`Track`] docs for more info.
75 pub tracks: Vec<Track>,
76}
77
78impl AnimationTracksData {
79 /// Adds new track to the animation. Animation can have unlimited number of tracks, each track is responsible
80 /// for animation of a single scene node.
81 pub fn add_track(&mut self, track: Track) {
82 self.tracks.push(track);
83 }
84
85 /// Removes a track at given index.
86 pub fn remove_track(&mut self, index: usize) -> Track {
87 self.tracks.remove(index)
88 }
89
90 /// Inserts a track at given index.
91 pub fn insert_track(&mut self, index: usize, track: Track) {
92 self.tracks.insert(index, track)
93 }
94
95 /// Removes last track from the list of tracks of the animation.
96 pub fn pop_track(&mut self) -> Option<Track> {
97 self.tracks.pop()
98 }
99
100 /// Returns a reference to tracks container.
101 pub fn tracks(&self) -> &[Track] {
102 &self.tracks
103 }
104
105 /// Returns a mutable reference to the track container.
106 pub fn tracks_mut(&mut self) -> &mut [Track] {
107 &mut self.tracks
108 }
109
110 /// Removes all tracks from the animation for which the given `filter` closure returns `false`. Could be useful
111 /// to remove undesired animation tracks.
112 pub fn retain_tracks<F>(&mut self, filter: F)
113 where
114 F: FnMut(&Track) -> bool,
115 {
116 self.tracks.retain(filter)
117 }
118}
119
120impl Visit for AnimationTracksData {
121 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
122 self.tracks.visit(name, visitor)
123 }
124}
125
126impl ResourceData for AnimationTracksData {
127 fn type_uuid(&self) -> Uuid {
128 <AnimationTracksData as TypeUuidProvider>::type_uuid()
129 }
130
131 fn save(&mut self, _path: &Path) -> Result<(), Box<dyn Error>> {
132 // TODO
133 Ok(())
134 }
135
136 fn can_be_saved(&self) -> bool {
137 true
138 }
139
140 fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
141 Some(Box::new(self.clone()))
142 }
143}
144
145/// A resource that holds animation tracks. This resource can be shared across multiple animations.
146pub type AnimationTracksDataResource = Resource<AnimationTracksData>;
147
148/// # Overview
149///
150/// Animation allows you to change properties of arbitrary entities at runtime using a set of key frames. Animation
151/// consists of multiple tracks, where each track is bound to a property of an entity. A track can animate
152/// any numeric properties, starting from numbers (including `bool`) end ending by 2/3/4 dimensional vectors.
153/// Each component (number, x/y/z/w vector components) is stored in a _parametric curve_ (see
154/// [`crate::core::math::curve::Curve`] docs for more info). Every parametric curve contains zero or more _key frames_.
155/// Graphically this could be represented like so:
156///
157/// ```text
158/// Timeline
159/// v
160/// Time > |---------------|------------------------------------>
161/// | |
162/// Track1 > | node.position |
163/// | X curve |..1..........5...........10..........
164/// | Y curve |..2.........-2..................1.... < Curve key frames
165/// | Z curve |..1..........9......................4
166/// |_______________|
167/// Track2 | node.property |
168/// | ............ |.....................................
169/// | ............ |.....................................
170/// | ............ |.....................................
171/// ```
172///
173/// Each key frame is just a real number with interpolation mode. Interpolation mode tells the engine how to
174/// calculate intermediate values between key frames. There are three kinds of interpolation used in animations
175/// (you can skip "boring math" if you want):
176///
177/// - **Constant** - intermediate value will be calculated using leftmost value of two. Constant "interpolation" is
178/// usually used to create step-like behaviour, the most common case is to "interpolate" two boolean values.
179/// - **Linear** - intermediate value will be calculated using linear interpolation `i = left + (right - left) / t`,
180/// where `t = (time_position - left) / (right - left)`. `t` is always in `0..1` range. Linear interpolation is usually
181/// used to create "straight" transitions between two values.
182/// - **Cubic** - intermediate value will be calculated using Hermite cubic spline:
183/// `i = (2t^3 - 3t^2 + 1) * left + (t^3 - 2t^2 + t) * left_tangent + (-2t^3 + 3t^2) * right + (t^3 - t^2) * right_tangent`,
184/// where `t = (time_position - left) / (right - left)` (`t` is always in `0..1` range), `left_tangent` and `right_tangent`
185/// is usually a `tan(angle)`. Cubic interpolation is usually used to create "smooth" transitions between two values.
186///
187/// # Track binding
188///
189/// Each track is always bound to a property in a node, either by its name or by a special binding. The name is used to fetch the
190/// property using reflection, the special binding is a faster way of fetching built-in properties. It is usually used to animate
191/// position, scale and rotation (these are the most common properties available in every scene node).
192///
193/// # Time slice and looping
194///
195/// While key frames on the curves can be located at arbitrary position in time, animations usually plays a specific time slice.
196/// By default, each animation will play on a given time slice infinitely - it is called _animation looping_, it works in both
197/// playback directions.
198///
199/// # Speed
200///
201/// You can vary playback speed in wide range, by default every animation has playback speed multiplier set to 1.0. The multiplier
202/// tells how faster (>1) or slower (<1) the animation needs to be played. Negative speed multiplier values will reverse playback.
203///
204/// # Enabling or disabling animations
205///
206/// Sometimes there's a need to disable/enable an animation or check if it is enabled or not, you can do this by using the pair
207/// of respective methods - [`Animation::set_enabled`] and [`Animation::is_enabled`].
208///
209/// # Signals
210///
211/// Signal is a named marker on specific time position on the animation timeline. Signal will emit an event if the animation playback
212/// time passes signal's position from left-to-right (or vice versa depending on playback direction). Signals are usually used to
213/// attach some specific actions to a position in time. For example, you can have a walking animation and you want to emit sounds
214/// when character's feet touch ground. In this case you need to add a few signals at times when each foot touches the ground.
215/// After that all you need to do is to fetch animation events one-by-one and emit respective sounds. See [`AnimationSignal`] docs
216/// for more info and examples.
217///
218/// # Examples
219///
220/// Usually, animations are created from the editor or some external tool and then imported in the engine.
221/// However, sometimes there's a need for procedural animations. Use the following example code as
222/// a guide **only** if you need to create procedural animations:
223///
224/// ```rust
225/// # use fyrox_animation::{
226/// # container::{TrackDataContainer, TrackValueKind},
227/// # track::Track,
228/// # value::ValueBinding,
229/// # Animation,
230/// # core::{
231/// # math::curve::{Curve, CurveKey, CurveKeyKind},
232/// # pool::Handle,
233/// # },
234/// # };
235/// # use fyrox_animation::track::TrackBinding;
236/// # use fyrox_core::pool::ErasedHandle;
237/// fn create_animation(target: ErasedHandle) -> Animation<ErasedHandle> {
238/// let mut frames_container = TrackDataContainer::new(TrackValueKind::Vector3);
239///
240/// // We'll animate only X coordinate (at index 0).
241/// frames_container.curves_mut()[0] = Curve::from(vec![
242/// CurveKey::new(0.5, 2.0, CurveKeyKind::Linear),
243/// CurveKey::new(0.75, 1.0, CurveKeyKind::Linear),
244/// CurveKey::new(1.0, 3.0, CurveKeyKind::Linear),
245/// ]);
246///
247/// // Create a track that will animate the node using the curve above.
248/// let mut track = Track::new(frames_container, ValueBinding::Position);
249///
250/// // Finally create an animation and set its time slice and turn it on.
251/// let mut animation = Animation::default();
252/// animation.add_track_with_binding(TrackBinding::new(target), track);
253/// animation.set_time_slice(0.0..1.0);
254/// animation.set_enabled(true);
255///
256/// animation
257/// }
258///
259/// // Create the animation.
260/// let mut animation = create_animation(Default::default());
261///
262/// // Emulate some ticks (like it was updated from the main loop of your game).
263/// for _ in 0..10 {
264/// animation.tick(1.0 / 60.0);
265/// }
266/// ```
267///
268/// The code above creates a simple animation that moves a node along X axis in various ways. The usage of the animation
269/// is only for the sake of completeness of the example. In the real games you need to add the animation to an animation
270/// player scene node and it will do the job for you.
271#[derive(Debug, Reflect, PartialEq)]
272pub struct Animation<T: EntityId> {
273 name: ImmutableString,
274 tracks_data: AnimationTracksDataResource,
275 track_bindings: FxHashMap<Uuid, TrackBinding<T>>,
276 time_position: f32,
277 time_slice: Range<f32>,
278 speed: f32,
279 looped: bool,
280 enabled: bool,
281 signals: Vec<AnimationSignal>,
282 root_motion_settings: Option<RootMotionSettings<T>>,
283 max_event_capacity: usize,
284
285 #[reflect(hidden)]
286 root_motion: Option<RootMotion>,
287 // Non-serialized
288 #[reflect(hidden)]
289 pose: AnimationPose<T>,
290 // Non-serialized
291 #[reflect(hidden)]
292 events: VecDeque<AnimationEvent>,
293}
294
295#[derive(Visit, Default)]
296struct OldTrack<T: EntityId> {
297 binding: ValueBinding,
298 frames: TrackDataContainer,
299 enabled: bool,
300 node: T,
301 id: Uuid,
302}
303
304impl<T: EntityId> Visit for Animation<T> {
305 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
306 let mut region = visitor.enter_region(name)?;
307
308 // Backward compatibility.
309 let mut old_tracks = Vec::<OldTrack<T>>::new();
310 if region.is_reading() {
311 if old_tracks.visit("Tracks", &mut region).is_ok() {
312 let mut tracks_data = AnimationTracksData::default();
313 for old_track in old_tracks {
314 self.track_bindings.insert(
315 old_track.id,
316 TrackBinding {
317 enabled: old_track.enabled,
318 target: old_track.node,
319 },
320 );
321 tracks_data.tracks.push(Track {
322 binding: old_track.binding,
323 frames: old_track.frames,
324 id: old_track.id,
325 });
326 }
327 self.tracks_data = AnimationTracksDataResource::new_ok(
328 Uuid::new_v4(),
329 ResourceKind::Embedded,
330 tracks_data,
331 );
332 } else {
333 self.tracks_data.visit("TracksData", &mut region)?;
334 self.track_bindings.visit("TrackBindings", &mut region)?;
335 }
336 } else {
337 self.tracks_data.visit("TracksData", &mut region)?;
338 self.track_bindings.visit("TrackBindings", &mut region)?;
339 }
340
341 let _ = self.name.visit("Name", &mut region);
342 self.time_position.visit("TimePosition", &mut region)?;
343 let _ = self.time_slice.visit("TimeSlice", &mut region);
344 self.speed.visit("Speed", &mut region)?;
345 self.looped.visit("Looped", &mut region)?;
346 self.enabled.visit("Enabled", &mut region)?;
347 self.signals.visit("Signals", &mut region)?;
348 let _ = self
349 .max_event_capacity
350 .visit("MaxEventCapacity", &mut region);
351 let _ = self
352 .root_motion_settings
353 .visit("RootMotionSettings", &mut region);
354
355 Ok(())
356 }
357}
358
359impl<T: EntityId> TypeUuidProvider for Animation<T> {
360 fn type_uuid() -> Uuid {
361 uuid!("aade8e9d-e2cf-401d-a4d1-59c6943645f3")
362 }
363}
364
365/// Identifier of an entity, that can be animated.
366pub trait EntityId:
367 Default + Send + Copy + Reflect + Visit + PartialEq + Eq + Hash + Debug + Ord + PartialEq + 'static
368{
369}
370
371impl<T: Reflect> EntityId for Handle<T> {}
372impl EntityId for ErasedHandle {}
373
374/// Root motion settings. It allows you to set a node (root) from which the motion will be taken
375/// as well as filter out some unnecessary parts of the motion (i.e. do not extract motion on
376/// Y axis).
377#[derive(Default, Debug, Clone, PartialEq, Reflect, Visit)]
378pub struct RootMotionSettings<T: EntityId> {
379 /// A handle to a node which movement will be extracted and put in root motion field of an animation
380 /// to which these settings were set to.
381 pub node: T,
382 /// Keeps X part of the translational part of the motion.
383 pub ignore_x_movement: bool,
384 /// Keeps Y part of the translational part of the motion.
385 pub ignore_y_movement: bool,
386 /// Keeps Z part of the translational part of the motion.
387 pub ignore_z_movement: bool,
388 /// Keeps rotational part of the motion.
389 pub ignore_rotations: bool,
390}
391
392/// Motion of a root node of an hierarchy of nodes. It contains relative rotation and translation in local
393/// space of the node. To transform this data into velocity and orientation you need to multiply these
394/// parts with some global transform, usually with the global transform of the mesh that is being animated.
395#[derive(Default, Debug, Clone, PartialEq)]
396pub struct RootMotion {
397 /// Relative offset between current and a previous frame of an animation.
398 pub delta_position: Vector3<f32>,
399 /// Relative rotation between current and a previous frame of an animation.
400 pub delta_rotation: UnitQuaternion<f32>,
401
402 prev_position: Vector3<f32>,
403 position_offset_remainder: Option<Vector3<f32>>,
404
405 prev_rotation: UnitQuaternion<f32>,
406 rotation_remainder: Option<UnitQuaternion<f32>>,
407}
408
409impl RootMotion {
410 /// Blend this motion with some other using `weight` as a proportion.
411 pub fn blend_with(&mut self, other: &RootMotion, weight: f32) {
412 self.delta_position = self.delta_position.lerp(&other.delta_position, weight);
413 self.delta_rotation = nlerp(self.delta_rotation, &other.delta_rotation, weight);
414 }
415}
416
417impl<T: EntityId> NameProvider for Animation<T> {
418 fn name(&self) -> &str {
419 &self.name
420 }
421}
422
423impl<T: EntityId> Clone for Animation<T> {
424 fn clone(&self) -> Self {
425 Self {
426 name: self.name.clone(),
427 tracks_data: self.tracks_data.clone(),
428 speed: self.speed,
429 time_position: self.time_position,
430 looped: self.looped,
431 enabled: self.enabled,
432 pose: Default::default(),
433 signals: self.signals.clone(),
434 root_motion_settings: self.root_motion_settings.clone(),
435 events: Default::default(),
436 time_slice: self.time_slice.clone(),
437 root_motion: self.root_motion.clone(),
438 max_event_capacity: 32,
439 track_bindings: self.track_bindings.clone(),
440 }
441 }
442}
443
444impl<T: EntityId> Animation<T> {
445 /// Gets the maximum capacity of events.
446 pub fn get_max_event_capacity(&self) -> usize {
447 self.max_event_capacity
448 }
449
450 /// Sets the maximum capacity of events.
451 pub fn set_max_event_capacity(&mut self, max_event_capacity: usize) {
452 self.max_event_capacity = max_event_capacity;
453 }
454
455 /// Sets a new name for the animation. The name then could be used to find the animation in a container.
456 pub fn set_name<S: AsRef<str>>(&mut self, name: S) {
457 self.name = ImmutableString::new(name);
458 }
459
460 /// Returns current name of the animation.
461 pub fn name(&self) -> &str {
462 self.name.as_ref()
463 }
464
465 /// Sets a new source of data for animation tracks. Keep in mind, that all existing track bindings
466 /// stored in the animation could become invalid, if the new resource does not have tracks with
467 /// the same ids that the bindings has.
468 pub fn set_tracks_data(&mut self, resource: AnimationTracksDataResource) {
469 self.tracks_data = resource;
470 }
471
472 /// Returns a reference to the current animation tracks resource.
473 pub fn tracks_data(&self) -> &AnimationTracksDataResource {
474 &self.tracks_data
475 }
476
477 /// Calculates new length of the animation based on the content of its tracks. It looks for the most "right"
478 /// curve key in all curves of all tracks and treats it as length of the animation. The method could be used
479 /// in case if you formed animation from code using just curves and don't know the actual length of the
480 /// animation.
481 pub fn fit_length_to_content(&mut self) {
482 let state = self.tracks_data.state();
483 let Some(tracks_data) = state.data_ref() else {
484 return;
485 };
486 self.time_slice.start = 0.0;
487 for track in tracks_data.tracks.iter() {
488 if track.time_length() > self.time_slice.end {
489 self.time_slice.end = track.time_length();
490 }
491 }
492 }
493
494 /// Sets new time position of the animation. The actual time position the animation will have after the call,
495 /// can be different in two reasons:
496 ///
497 /// - If the animation is looping and the new time position is outside of the time slice of the animation, then
498 /// the actual time position will be wrapped to fit the time slice. For example, if you have an animation that has
499 /// `0.0..5.0s` time slice and you trying to set `7.5s` position, the actual time position will be `2.5s` (it
500 /// wraps the input value on the given time slice).
501 /// - If the animation is **not** looping and the new time position is outside of the time slice of the animation,
502 /// then the actual time position will be clamped to the time clice of the animation.
503 pub fn set_time_position(&mut self, time: f32) -> &mut Self {
504 if self.looped {
505 self.time_position = wrapf(time, self.time_slice.start, self.time_slice.end);
506 } else {
507 self.time_position = time.clamp(self.time_slice.start, self.time_slice.end);
508 }
509
510 self
511 }
512
513 /// Sets new time slice of the animation in seconds. It defines a time interval in which the animation will
514 /// be played. Current playback position will be clamped (or wrapped if the animation is looping) to fit to new
515 /// bounds.
516 pub fn set_time_slice(&mut self, time_slice: Range<f32>) {
517 assert!(time_slice.start <= time_slice.end);
518
519 self.time_slice = time_slice;
520
521 // Ensure time position is in given time slice.
522 self.set_time_position(self.time_position);
523 }
524
525 /// Returns current time slice of the animation.
526 pub fn time_slice(&self) -> Range<f32> {
527 self.time_slice.clone()
528 }
529
530 /// Rewinds the animation to the beginning.
531 pub fn rewind(&mut self) -> &mut Self {
532 self.set_time_position(self.time_slice.start)
533 }
534
535 /// Returns length of the animation in seconds.
536 pub fn length(&self) -> f32 {
537 self.time_slice.end - self.time_slice.start
538 }
539
540 /// Performs a single update tick and calculates an output pose. This method is low level, you should not use it
541 /// in normal circumstances - the engine will call it for you.
542 pub fn tick(&mut self, dt: f32) {
543 self.update_pose();
544
545 let current_time_position = self.time_position();
546 let new_time_position = current_time_position + dt * self.speed();
547
548 for signal in self.signals.iter_mut().filter(|s| s.enabled) {
549 if self.speed >= 0.0
550 && (current_time_position < signal.time && new_time_position >= signal.time)
551 || self.speed < 0.0
552 && (current_time_position > signal.time && new_time_position <= signal.time)
553 && self.events.len() < self.max_event_capacity
554 {
555 self.events.push_back(AnimationEvent {
556 signal_id: signal.id,
557 name: signal.name.clone(),
558 });
559 }
560 }
561
562 let prev_time_position = current_time_position;
563
564 self.set_time_position(new_time_position);
565
566 self.update_root_motion(prev_time_position);
567 }
568
569 fn update_root_motion(&mut self, prev_time_position: f32) {
570 let state = self.tracks_data.state();
571 let Some(tracks_data) = state.data_ref() else {
572 return;
573 };
574 let tracks = &tracks_data.tracks;
575
576 fn fetch_position_at_time(tracks: &[Track], time: f32) -> Vector3<f32> {
577 tracks
578 .iter()
579 .find(|track| track.value_binding() == &ValueBinding::Position)
580 .and_then(|track| track.fetch(time))
581 .and_then(|value| {
582 if let TrackValue::Vector3(position) = value.value {
583 Some(position)
584 } else {
585 None
586 }
587 })
588 .unwrap_or_default()
589 }
590
591 fn fetch_rotation_at_time(tracks: &[Track], time: f32) -> UnitQuaternion<f32> {
592 tracks
593 .iter()
594 .find(|track| track.value_binding() == &ValueBinding::Rotation)
595 .and_then(|track| track.fetch(time))
596 .and_then(|value| {
597 if let TrackValue::UnitQuaternion(rotation) = value.value {
598 Some(rotation)
599 } else {
600 None
601 }
602 })
603 .unwrap_or_default()
604 }
605
606 // If we have root motion enabled, try to extract the actual motion values. We'll take only relative motion
607 // here, relative to the previous values.
608 if let Some(root_motion_settings) = self.root_motion_settings.as_ref() {
609 let mut prev_root_motion = self.root_motion.clone().unwrap_or_default();
610
611 // Check if we've started another loop cycle.
612 let new_loop_cycle_started = self.looped
613 && (self.speed > 0.0 && self.time_position < prev_time_position
614 || self.speed < 0.0 && self.time_position > prev_time_position);
615
616 let cycle_start_time = if self.speed > 0.0 {
617 self.time_slice.start
618 } else {
619 self.time_slice.end
620 };
621
622 let cycle_end_time = if self.speed > 0.0 {
623 self.time_slice.end
624 } else {
625 self.time_slice.start
626 };
627
628 let mut root_motion = RootMotion::default();
629 if let Some(root_pose) = self.pose.poses_mut().get_mut(&root_motion_settings.node) {
630 for bound_value in root_pose.values.values.iter_mut() {
631 match bound_value.binding {
632 ValueBinding::Position => {
633 if let TrackValue::Vector3(pose_position) = bound_value.value {
634 if new_loop_cycle_started {
635 root_motion.prev_position =
636 fetch_position_at_time(tracks, cycle_start_time);
637 root_motion.position_offset_remainder = Some(
638 fetch_position_at_time(tracks, cycle_end_time)
639 - pose_position,
640 );
641 } else {
642 root_motion.prev_position = pose_position;
643 }
644
645 let remainder = prev_root_motion
646 .position_offset_remainder
647 .take()
648 .unwrap_or_default();
649 let current_offset = pose_position - prev_root_motion.prev_position;
650 let delta = current_offset + remainder;
651
652 root_motion.delta_position.x =
653 if root_motion_settings.ignore_x_movement {
654 0.0
655 } else {
656 delta.x
657 };
658 root_motion.delta_position.y =
659 if root_motion_settings.ignore_y_movement {
660 0.0
661 } else {
662 delta.y
663 };
664 root_motion.delta_position.z =
665 if root_motion_settings.ignore_z_movement {
666 0.0
667 } else {
668 delta.z
669 };
670
671 // Reset position so the root won't move.
672 let start_position =
673 fetch_position_at_time(tracks, self.time_slice.start);
674
675 bound_value.value = TrackValue::Vector3(Vector3::new(
676 if root_motion_settings.ignore_x_movement {
677 pose_position.x
678 } else {
679 start_position.x
680 },
681 if root_motion_settings.ignore_y_movement {
682 pose_position.y
683 } else {
684 start_position.y
685 },
686 if root_motion_settings.ignore_z_movement {
687 pose_position.z
688 } else {
689 start_position.z
690 },
691 ));
692 }
693 }
694 ValueBinding::Rotation => {
695 if let TrackValue::UnitQuaternion(pose_rotation) = bound_value.value {
696 if !root_motion_settings.ignore_rotations {
697 if new_loop_cycle_started {
698 root_motion.prev_rotation =
699 fetch_rotation_at_time(tracks, cycle_start_time);
700 root_motion.rotation_remainder = Some(
701 fetch_rotation_at_time(tracks, cycle_end_time)
702 .inverse()
703 * pose_rotation,
704 );
705 } else {
706 root_motion.prev_rotation = pose_rotation;
707 }
708
709 // Compute relative rotation that can be used to "turn" a node later on.
710 let remainder = prev_root_motion
711 .rotation_remainder
712 .take()
713 .unwrap_or_else(UnitQuaternion::identity);
714 let current_relative_rotation =
715 prev_root_motion.prev_rotation.inverse() * pose_rotation;
716 root_motion.delta_rotation =
717 remainder * current_relative_rotation;
718
719 // Reset rotation so the root won't rotate.
720 bound_value.value = TrackValue::UnitQuaternion(
721 fetch_rotation_at_time(tracks, self.time_slice.start),
722 );
723 }
724 }
725 }
726 _ => (),
727 }
728 }
729 }
730 self.root_motion = Some(root_motion);
731 }
732 }
733
734 /// Sets new root motion settings.
735 pub fn set_root_motion_settings(&mut self, settings: Option<RootMotionSettings<T>>) {
736 self.root_motion_settings = settings;
737 }
738
739 /// Returns a reference to the root motion settings (if any).
740 pub fn root_motion_settings_ref(&self) -> Option<&RootMotionSettings<T>> {
741 self.root_motion_settings.as_ref()
742 }
743
744 /// Returns a reference to the root motion settings (if any).
745 pub fn root_motion_settings_mut(&mut self) -> Option<&mut RootMotionSettings<T>> {
746 self.root_motion_settings.as_mut()
747 }
748
749 /// Returns a reference to the root motion (if any).
750 pub fn root_motion(&self) -> Option<&RootMotion> {
751 self.root_motion.as_ref()
752 }
753
754 /// Extracts a first event from the events queue of the animation.
755 pub fn pop_event(&mut self) -> Option<AnimationEvent> {
756 self.events.pop_front()
757 }
758
759 /// Returns a reference to inner events queue. It is useful when you need to iterate over the events, but
760 /// don't extract them from the queue.
761 pub fn events_ref(&self) -> &VecDeque<AnimationEvent> {
762 &self.events
763 }
764
765 /// Return a mutable reference to inner events queue. Provides you a full controls over animation events,
766 /// you can even manually inject events in the queue.
767 pub fn events_mut(&mut self) -> &mut VecDeque<AnimationEvent> {
768 &mut self.events
769 }
770
771 /// Takes the events queue and returns it to the caller, leaving the internal queue empty.
772 pub fn take_events(&mut self) -> VecDeque<AnimationEvent> {
773 std::mem::take(&mut self.events)
774 }
775
776 /// Returns current time position of the animation. The time position is guaranteed to be in the range of
777 /// current time slice of the animation.
778 pub fn time_position(&self) -> f32 {
779 self.time_position
780 }
781
782 /// Sets new speed multiplier for the animation. By default it is set to 1.0. Negative values can be used
783 /// to play the animation in reverse.
784 pub fn set_speed(&mut self, speed: f32) -> &mut Self {
785 self.speed = speed;
786 self
787 }
788
789 /// Returns speed multiplier of the animation.
790 pub fn speed(&self) -> f32 {
791 self.speed
792 }
793
794 /// Enables or disables looping of the animation.
795 pub fn set_loop(&mut self, state: bool) -> &mut Self {
796 self.looped = state;
797 self
798 }
799
800 /// Returns `true` if the animation is looping, `false` - otherwise.
801 pub fn is_loop(&self) -> bool {
802 self.looped
803 }
804
805 /// Returns `true` if the animation was played until the end of current time slice of the animation, `false` -
806 /// otherwise. Looping animations will always return `false`.
807 pub fn has_ended(&self) -> bool {
808 !self.looped && (self.time_position - self.time_slice.end).abs() <= f32::EPSILON
809 }
810
811 /// Enables or disables the animation, disabled animations does not updated and their output pose will remain
812 /// the same. By default every animation is enabled.
813 pub fn set_enabled(&mut self, enabled: bool) -> &mut Self {
814 self.enabled = enabled;
815 self
816 }
817
818 /// Returns `true` if the animation is enabled, `false` - otherwise.
819 pub fn is_enabled(&self) -> bool {
820 self.enabled
821 }
822
823 /// Adds a new track to the animation track data container and binds it with the specified target.
824 /// Keep in mind, that this method will modify potentially shared track data container, which might
825 /// affect other animations using it.
826 pub fn add_track_with_binding(&mut self, binding: TrackBinding<T>, track: Track) {
827 let mut state = self.tracks_data.state();
828 let Some(tracks_data) = state.data() else {
829 return;
830 };
831 let id = track.id();
832 tracks_data.tracks.push(track);
833 self.track_bindings.insert(id, binding);
834 }
835
836 /// Removes last track from the current tracks data resource and the respective binding to it
837 /// from the animation. This method will fail if the resource is not loaded, or if there's no
838 /// tracks in it. It will also fail if there's no respective binding to the track in the
839 /// animation.
840 ///
841 /// Keep in mind, that this method modifies the tracks data resource, which might be used by
842 /// some other animation.
843 pub fn pop_track_with_binding(&mut self) -> Option<(TrackBinding<T>, Track)> {
844 let mut state = self.tracks_data.state();
845 let tracks_data = state.data()?;
846 let track = tracks_data.tracks.pop()?;
847 let binding = self.track_bindings.remove(&track.id())?;
848 Some((binding, track))
849 }
850
851 /// Removes the specified track from the current tracks data resource and the respective binding
852 /// to it from the animation. This method will fail if the resource is not loaded, or if there's
853 /// no tracks in it. It will also fail if there's no respective binding to the track in the
854 /// animation.
855 ///
856 /// Keep in mind, that this method modifies the tracks data resource, which might be used by
857 /// some other animation.
858 pub fn remove_track_with_binding(&mut self, index: usize) -> Option<(TrackBinding<T>, Track)> {
859 let mut state = self.tracks_data.state();
860 let tracks_data = state.data()?;
861 let track = tracks_data.tracks.remove(index);
862 let binding = self.track_bindings.remove(&track.id())?;
863 Some((binding, track))
864 }
865
866 /// Inserts a new track in the tracks data resource and creates a new binding to it.
867 ///
868 /// Keep in mind, that this method modifies the tracks data resource, which might be used by
869 /// some other animation.
870 pub fn insert_track_with_binding(
871 &mut self,
872 index: usize,
873 binding: TrackBinding<T>,
874 track: Track,
875 ) {
876 let mut state = self.tracks_data.state();
877 let Some(tracks_data) = state.data() else {
878 return;
879 };
880 assert!(self.track_bindings.insert(track.id(), binding).is_none());
881 tracks_data.tracks.insert(index, track);
882 }
883
884 /// Adds a new animation signal to the animation. See [`AnimationSignal`] docs for more info and examples.
885 pub fn add_signal(&mut self, signal: AnimationSignal) -> &mut Self {
886 self.signals.push(signal);
887 self
888 }
889
890 /// Removes last animation signal from the container of the animation.
891 pub fn pop_signal(&mut self) -> Option<AnimationSignal> {
892 self.signals.pop()
893 }
894
895 /// Inserts a new animation signal at given position.
896 pub fn insert_signal(&mut self, index: usize, signal: AnimationSignal) {
897 self.signals.insert(index, signal)
898 }
899
900 /// Removes an animation signal at given index.
901 pub fn remove_signal(&mut self, index: usize) -> AnimationSignal {
902 self.signals.remove(index)
903 }
904
905 /// Returns a reference to the animation signals container.
906 pub fn signals(&self) -> &[AnimationSignal] {
907 &self.signals
908 }
909
910 /// Returns a mutable reference to the inner animation signals container, allowing you to modify the signals.
911 pub fn signals_mut(&mut self) -> &mut [AnimationSignal] {
912 &mut self.signals
913 }
914
915 /// Tries to find all tracks that refer to a given node and enables or disables them.
916 pub fn set_node_track_enabled(&mut self, handle: T, enabled: bool) {
917 for track in self.track_bindings.values_mut() {
918 if track.target() == handle {
919 track.set_enabled(enabled);
920 }
921 }
922 }
923
924 /// Returns a reference to the current set of track bindings used by the animation. The returned
925 /// hash map contains `(track_id -> binding)` pairs.
926 pub fn track_bindings(&self) -> &FxHashMap<Uuid, TrackBinding<T>> {
927 &self.track_bindings
928 }
929
930 /// Returns a reference to the current set of track bindings used by the animation. The returned
931 /// hash map contains `(track_id -> binding)` pairs.
932 pub fn track_bindings_mut(&mut self) -> &mut FxHashMap<Uuid, TrackBinding<T>> {
933 &mut self.track_bindings
934 }
935
936 /// Tries to find a layer by its name. Returns index of the signal and its reference.
937 #[inline]
938 pub fn find_signal_by_name_ref<S: AsRef<str>>(
939 &self,
940 name: S,
941 ) -> Option<(usize, &AnimationSignal)> {
942 core::find_by_name_ref(self.signals.iter().enumerate(), name)
943 }
944
945 /// Tries to find a signal by its name. Returns index of the signal and its reference.
946 #[inline]
947 pub fn find_signal_by_name_mut<S: AsRef<str>>(
948 &mut self,
949 name: S,
950 ) -> Option<(usize, &mut AnimationSignal)> {
951 core::find_by_name_mut(self.signals.iter_mut().enumerate(), name)
952 }
953
954 /// Returns `true` if there's a signal with given name and id.
955 #[inline]
956 pub fn has_signal<S: AsRef<str>>(&self, name: S, id: Uuid) -> bool {
957 self.find_signal_by_name_ref(name)
958 .is_some_and(|(_, s)| s.id == id)
959 }
960
961 /// Removes all tracks from the animation.
962 pub fn remove_tracks(&mut self) {
963 self.track_bindings.clear();
964 }
965
966 fn update_pose(&mut self) {
967 let state = self.tracks_data.state();
968 let Some(tracks_data) = state.data_ref() else {
969 return;
970 };
971
972 self.pose.reset();
973 for track in tracks_data.tracks.iter() {
974 let Some(binding) = self.track_bindings.get(&track.id()) else {
975 continue;
976 };
977
978 if binding.is_enabled() {
979 if let Some(bound_value) = track.fetch(self.time_position) {
980 self.pose.add_to_node_pose(binding.target(), bound_value);
981 }
982 }
983 }
984 }
985
986 /// Returns current pose of the animation (a final result that can be applied to a scene graph).
987 pub fn pose(&self) -> &AnimationPose<T> {
988 &self.pose
989 }
990}
991
992impl<T: EntityId> Default for Animation<T> {
993 fn default() -> Self {
994 Self {
995 name: Default::default(),
996 tracks_data: Resource::new_ok(
997 Uuid::default(),
998 ResourceKind::Embedded,
999 AnimationTracksData::default(),
1000 ),
1001 speed: 1.0,
1002 time_position: 0.0,
1003 enabled: true,
1004 looped: true,
1005 pose: Default::default(),
1006 signals: Default::default(),
1007 root_motion_settings: None,
1008 events: Default::default(),
1009 time_slice: Default::default(),
1010 root_motion: None,
1011 max_event_capacity: 32,
1012 track_bindings: Default::default(),
1013 }
1014 }
1015}
1016
1017/// A container for animations. It is a tiny wrapper around [`Pool`], you should never create the container yourself,
1018/// it is managed by the engine.
1019#[derive(Debug, Clone, Reflect, PartialEq)]
1020pub struct AnimationContainer<T: EntityId> {
1021 pool: Pool<Animation<T>>,
1022}
1023
1024impl<T: EntityId> Default for AnimationContainer<T> {
1025 fn default() -> Self {
1026 Self::new()
1027 }
1028}
1029
1030impl<T: EntityId> AnimationContainer<T> {
1031 /// Creates an empty animation container.
1032 pub fn new() -> Self {
1033 Self { pool: Pool::new() }
1034 }
1035
1036 /// Returns a total amount of animations in the container.
1037 #[inline]
1038 pub fn alive_count(&self) -> u32 {
1039 self.pool.alive_count()
1040 }
1041
1042 /// Returns an iterator yielding a references to animations in the container.
1043 #[inline]
1044 pub fn iter(&self) -> impl Iterator<Item = &Animation<T>> {
1045 self.pool.iter()
1046 }
1047
1048 /// Returns an iterator yielding a pair (handle, reference) to animations in the container.
1049 #[inline]
1050 pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Animation<T>>, &Animation<T>)> {
1051 self.pool.pair_iter()
1052 }
1053
1054 /// Returns an iterator yielding a pair (handle, reference) to animations in the container.
1055 #[inline]
1056 pub fn pair_iter_mut(
1057 &mut self,
1058 ) -> impl Iterator<Item = (Handle<Animation<T>>, &mut Animation<T>)> {
1059 self.pool.pair_iter_mut()
1060 }
1061
1062 /// Returns an iterator yielding a references to animations in the container.
1063 #[inline]
1064 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Animation<T>> {
1065 self.pool.iter_mut()
1066 }
1067
1068 /// Adds a new animation to the container and returns its handle.
1069 #[inline]
1070 pub fn add(&mut self, animation: Animation<T>) -> Handle<Animation<T>> {
1071 self.pool.spawn(animation)
1072 }
1073
1074 /// Tries to remove an animation from the container by its handle.
1075 #[inline]
1076 pub fn remove(&mut self, handle: Handle<Animation<T>>) -> Option<Animation<T>> {
1077 self.pool.try_free(handle)
1078 }
1079
1080 /// Extracts animation from container and reserves its handle. It is used to temporarily take
1081 /// ownership over animation, and then put animation back using given ticket.
1082 pub fn take_reserve(
1083 &mut self,
1084 handle: Handle<Animation<T>>,
1085 ) -> (Ticket<Animation<T>>, Animation<T>) {
1086 self.pool.take_reserve(handle)
1087 }
1088
1089 /// Puts animation back by given ticket.
1090 pub fn put_back(
1091 &mut self,
1092 ticket: Ticket<Animation<T>>,
1093 animation: Animation<T>,
1094 ) -> Handle<Animation<T>> {
1095 self.pool.put_back(ticket, animation)
1096 }
1097
1098 /// Makes animation handle vacant again.
1099 pub fn forget_ticket(&mut self, ticket: Ticket<Animation<T>>) {
1100 self.pool.forget_ticket(ticket)
1101 }
1102
1103 /// Removes all animations.
1104 #[inline]
1105 pub fn clear(&mut self) {
1106 self.pool.clear()
1107 }
1108
1109 /// Tries to borrow a reference to an animation in the container. Panics if the handle is invalid.
1110 #[inline]
1111 pub fn get(&self, handle: Handle<Animation<T>>) -> &Animation<T> {
1112 self.pool.borrow(handle)
1113 }
1114
1115 /// Tries to borrow a mutable reference to an animation in the container. Panics if the handle is invalid.
1116 #[inline]
1117 pub fn get_mut(&mut self, handle: Handle<Animation<T>>) -> &mut Animation<T> {
1118 self.pool.borrow_mut(handle)
1119 }
1120
1121 /// Tries to borrow a reference to an animation in the container.
1122 #[inline]
1123 pub fn try_get(&self, handle: Handle<Animation<T>>) -> Option<&Animation<T>> {
1124 self.pool.try_borrow(handle)
1125 }
1126
1127 /// Tries to borrow a mutable reference to an animation in the container.
1128 #[inline]
1129 pub fn try_get_mut(&mut self, handle: Handle<Animation<T>>) -> Option<&mut Animation<T>> {
1130 self.pool.try_borrow_mut(handle)
1131 }
1132
1133 /// Tries to find an animation by its name in the container.
1134 #[inline]
1135 pub fn find_by_name_ref<S: AsRef<str>>(
1136 &self,
1137 name: S,
1138 ) -> Option<(Handle<Animation<T>>, &Animation<T>)> {
1139 core::find_by_name_ref(self.pool.pair_iter(), name)
1140 }
1141
1142 /// Tries to find an animation by its name in the container.
1143 #[inline]
1144 pub fn find_by_name_mut<S: AsRef<str>>(
1145 &mut self,
1146 name: S,
1147 ) -> Option<(Handle<Animation<T>>, &mut Animation<T>)> {
1148 core::find_by_name_mut(self.pool.pair_iter_mut(), name)
1149 }
1150
1151 /// Removes every animation from the container that does not satisfy a particular condition represented by the given
1152 /// closue.
1153 #[inline]
1154 pub fn retain<P>(&mut self, pred: P)
1155 where
1156 P: FnMut(&Animation<T>) -> bool,
1157 {
1158 self.pool.retain(pred)
1159 }
1160
1161 /// Removes queued animation events from every animation in the container.
1162 ///
1163 /// # Potential use cases
1164 ///
1165 /// Sometimes there is a need to use animation events only from one frame, in this case you should clear events each frame.
1166 /// This situation might come up when you have multiple animations with signals, but at each frame not every event gets
1167 /// processed. This might result in unwanted side effects, like multiple attack events may result in huge damage in a single
1168 /// frame.
1169 pub fn clear_animation_events(&mut self) {
1170 for animation in self.pool.iter_mut() {
1171 animation.events.clear();
1172 }
1173 }
1174}
1175
1176impl<T: EntityId> Visit for AnimationContainer<T> {
1177 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
1178 if visitor.is_reading() && self.pool.get_capacity() != 0 {
1179 panic!("Animation pool must be empty on load!");
1180 }
1181
1182 let mut region = visitor.enter_region(name)?;
1183
1184 self.pool.visit("Pool", &mut region)?;
1185
1186 Ok(())
1187 }
1188}
1189
1190impl<T: EntityId> Index<Handle<Animation<T>>> for AnimationContainer<T> {
1191 type Output = Animation<T>;
1192
1193 fn index(&self, index: Handle<Animation<T>>) -> &Self::Output {
1194 &self.pool[index]
1195 }
1196}
1197
1198impl<T: EntityId> IndexMut<Handle<Animation<T>>> for AnimationContainer<T> {
1199 fn index_mut(&mut self, index: Handle<Animation<T>>) -> &mut Self::Output {
1200 &mut self.pool[index]
1201 }
1202}