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