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