fyrox_animation/
container.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//! Track data container is a flexible source of data for numeric parameters, it is built using a set
22//! of parametric curves. See [`TrackDataContainer`] docs for more info.
23
24use crate::{
25    core::{
26        algebra::{Vector2, Vector3, Vector4},
27        math::curve::Curve,
28        math::{quat_from_euler, RotationOrder},
29        reflect::prelude::*,
30        visitor::prelude::*,
31    },
32    value::TrackValue,
33};
34use fyrox_core::algebra::{Quaternion, UnitQuaternion};
35
36/// The kind of track output value, the animation system works only with numeric properties and the number
37/// of variants is small.
38#[derive(Clone, Copy, Debug, Visit, Reflect, PartialEq, Eq)]
39pub enum TrackValueKind {
40    /// A real number. Requires only 1 parametric curve.
41    Real,
42
43    /// A 2-dimensional vector of real values. Requires 2 parametric curves, where `X = 0` and `Y = 1`.
44    Vector2,
45
46    /// A 3-dimensional vector of real values. Requires 3 parametric curves, where `X = 0`, `Y = 1`, `Z = 2`.
47    Vector3,
48
49    /// A 4-dimensional vector of real values. Requires 4 parametric curves, where `X = 0`, `Y = 1`, `Z = 2`, `W = 3`.
50    Vector4,
51
52    /// A quaternion that represents some rotation. Requires 3 parametric curves, where `XAngle = 0`, `YAngle = 1`,
53    /// `ZAngle = 2`. The order of rotations is `XYZ`. This triple of curves forms Euler angles which are interpolated
54    /// and then converted to a quaternion.
55    UnitQuaternionEuler,
56
57    /// A quaternion that represents some rotation. Requires 4 parametric curves for each component.
58    /// The order is XYZW.
59    UnitQuaternion,
60}
61
62impl TrackValueKind {
63    /// Returns count of elementary components of a value kind. For example: Vector3 consists of 3 components where
64    /// each component has its own parametric curve.
65    pub fn components_count(self) -> usize {
66        match self {
67            TrackValueKind::Real => 1,
68            TrackValueKind::Vector2 => 2,
69            TrackValueKind::Vector3 => 3,
70            TrackValueKind::Vector4 => 4,
71            TrackValueKind::UnitQuaternionEuler => {
72                // Euler angles
73                3
74            }
75            TrackValueKind::UnitQuaternion => 4,
76        }
77    }
78}
79
80impl Default for TrackValueKind {
81    fn default() -> Self {
82        Self::Vector3
83    }
84}
85
86/// Interpolation mode for track data.
87#[derive(Visit, Reflect, Debug, Clone, Default, PartialEq)]
88pub enum InterpolationMode {
89    /// Default interpolation mode.
90    #[default]
91    Default,
92    /// This mode forces the engine to use short-path angle interpolation.
93    ShortPath,
94}
95
96/// Container for a track data. Strictly speaking, it is just a set of parametric curves which can be
97/// fetched at a given time position simultaneously, producing a value of desired type. Which type of
98/// value is produced is defined by [`TrackValueKind`] enumeration. Usually a container contains up to
99/// 4 curves (the largest type supported is [`Vector4`]).
100///
101/// Each component is bound to a specific curve. For example, in case of [`Vector3`] its components bound
102/// to the following curve indices: `X = 0`, `Y = 1`, `Z = 2`. This order cannot be changed.
103#[derive(Visit, Reflect, Debug, Clone, Default, PartialEq)]
104pub struct TrackDataContainer {
105    curves: Vec<Curve>,
106    kind: TrackValueKind,
107}
108
109impl TrackDataContainer {
110    /// Creates a new container, that is able to produce values defined by [`TrackValueKind`] input parameter.
111    /// The number of curves in the output container is defined by [`TrackValueKind::components_count`], for
112    /// example [`Vector3`] has 3 components (X, Y, Z). An empty container can be created using [`Self::default`]
113    /// method.
114    pub fn new(kind: TrackValueKind) -> Self {
115        Self {
116            kind,
117            // Do not use `vec![Default::default(); kind.components_count()]` here because
118            // it clones a curve that was created in first macro argument which leads to
119            // non-unique ids of the curves.
120            curves: (0..kind.components_count())
121                .map(|_| Curve::default())
122                .collect(),
123        }
124    }
125
126    /// Adds a new curve to the container. Keep in mind, that the actual useful amount of curves has soft limit
127    /// of four due to [`TrackValueKind`], any excessive curves will be ignored.
128    pub fn add_curve(&mut self, curve: Curve) {
129        self.curves.push(curve)
130    }
131
132    /// Tries to borrow a curve at a given index.
133    pub fn curve(&self, index: usize) -> Option<&Curve> {
134        self.curves.get(index)
135    }
136
137    /// Tries to borrow a curve at a given index.
138    pub fn curve_mut(&mut self, index: usize) -> Option<&mut Curve> {
139        self.curves.get_mut(index)
140    }
141
142    /// Returns a reference to curves container.
143    pub fn curves_ref(&self) -> &[Curve] {
144        &self.curves
145    }
146
147    /// Tries to borrow a curve at a given index.
148    pub fn curves_mut(&mut self) -> &mut [Curve] {
149        &mut self.curves
150    }
151
152    /// Sets new kind of output value. Keep in mind, that the curves will remain unchanged, if you need
153    /// to re-use the container you might need to re-create/re-fill the curves too.
154    pub fn set_value_kind(&mut self, kind: TrackValueKind) {
155        self.kind = kind;
156    }
157
158    /// Returns the kind of output value produced by the container.
159    pub fn value_kind(&self) -> TrackValueKind {
160        self.kind
161    }
162
163    #[inline(always)]
164    fn fetch_vector2(&self, time: f32) -> Option<TrackValue> {
165        if self.curves.len() < 2 {
166            return None;
167        }
168
169        // SAFETY: The indices are guaranteed to be correct by the above check.
170        unsafe {
171            Some(TrackValue::Vector2(Vector2::new(
172                self.curves.get_unchecked(0).value_at(time),
173                self.curves.get_unchecked(1).value_at(time),
174            )))
175        }
176    }
177
178    #[inline(always)]
179    fn fetch_vector3(&self, time: f32) -> Option<TrackValue> {
180        if self.curves.len() < 3 {
181            return None;
182        }
183
184        unsafe {
185            Some(TrackValue::Vector3(Vector3::new(
186                self.curves.get_unchecked(0).value_at(time),
187                self.curves.get_unchecked(1).value_at(time),
188                self.curves.get_unchecked(2).value_at(time),
189            )))
190        }
191    }
192
193    #[inline(always)]
194    fn fetch_vector4(&self, time: f32) -> Option<TrackValue> {
195        if self.curves.len() < 4 {
196            return None;
197        }
198
199        // SAFETY: The indices are guaranteed to be correct by the above check.
200        unsafe {
201            Some(TrackValue::Vector4(Vector4::new(
202                self.curves.get_unchecked(0).value_at(time),
203                self.curves.get_unchecked(1).value_at(time),
204                self.curves.get_unchecked(2).value_at(time),
205                self.curves.get_unchecked(3).value_at(time),
206            )))
207        }
208    }
209
210    #[inline(always)]
211    fn fetch_quaternion_euler(&self, time: f32) -> Option<TrackValue> {
212        if self.curves.len() < 3 {
213            return None;
214        }
215
216        // SAFETY: The indices are guaranteed to be correct by the above check.
217        unsafe {
218            let x_curve = self.curves.get_unchecked(0);
219            let y_curve = self.curves.get_unchecked(1);
220            let z_curve = self.curves.get_unchecked(2);
221
222            // Convert Euler angles to quaternion
223            let (x, y, z) = (
224                x_curve.value_at(time),
225                y_curve.value_at(time),
226                z_curve.value_at(time),
227            );
228
229            Some(TrackValue::UnitQuaternion(quat_from_euler(
230                Vector3::new(x, y, z),
231                RotationOrder::XYZ,
232            )))
233        }
234    }
235
236    #[inline(always)]
237    fn fetch_quaternion(&self, time: f32) -> Option<TrackValue> {
238        if self.curves.len() < 4 {
239            return None;
240        }
241
242        // SAFETY: The indices are guaranteed to be correct by the above check.
243        unsafe {
244            let x_curve = self.curves.get_unchecked(0);
245            let y_curve = self.curves.get_unchecked(1);
246            let z_curve = self.curves.get_unchecked(2);
247            let w_curve = self.curves.get_unchecked(3);
248
249            // Convert Euler angles to quaternion
250            let (x, y, z, w) = (
251                x_curve.value_at(time),
252                y_curve.value_at(time),
253                z_curve.value_at(time),
254                w_curve.value_at(time),
255            );
256
257            Some(TrackValue::UnitQuaternion(UnitQuaternion::from_quaternion(
258                Quaternion::new(w, x, y, z),
259            )))
260        }
261    }
262
263    /// Tries to get a value at a given time. The method could fail if the internal set of curves is malformed
264    /// and cannot produce a desired value (for example, [`Vector3`] can be fetched only if the amount of curves
265    /// is 3).
266    pub fn fetch(&self, time: f32) -> Option<TrackValue> {
267        match self.kind {
268            TrackValueKind::Real => Some(TrackValue::Real(self.curves.first()?.value_at(time))),
269            TrackValueKind::Vector2 => self.fetch_vector2(time),
270            TrackValueKind::Vector3 => self.fetch_vector3(time),
271            TrackValueKind::Vector4 => self.fetch_vector4(time),
272            TrackValueKind::UnitQuaternionEuler => self.fetch_quaternion_euler(time),
273            TrackValueKind::UnitQuaternion => self.fetch_quaternion(time),
274        }
275    }
276
277    /// Find a right-most key on one of the curves in the container and returns its position. This position
278    /// can be treated as a maximum "length" of the container.
279    pub fn time_length(&self) -> f32 {
280        let mut length = 0.0;
281        for curve in self.curves.iter() {
282            let max_location = curve.max_location();
283            if max_location > length {
284                length = max_location;
285            }
286        }
287        length
288    }
289}