fyrox_animation/container.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
//! Track data container is a flexible source of data for numeric parameters, it is built using a set
//! of parametric curves. See [`TrackDataContainer`] docs for more info.
use crate::{
core::{
algebra::{Vector2, Vector3, Vector4},
math::curve::Curve,
math::{quat_from_euler, RotationOrder},
reflect::prelude::*,
visitor::prelude::*,
},
value::TrackValue,
};
/// The kind of track output value, the animation system works only with numeric properties and the number
/// of variants is small.
#[derive(Clone, Copy, Debug, Visit, Reflect, PartialEq, Eq)]
pub enum TrackValueKind {
/// A real number. Requires only 1 parametric curve.
Real,
/// A 2-dimensional vector of real values. Requires 2 parametric curves, where `X = 0` and `Y = 1`.
Vector2,
/// A 3-dimensional vector of real values. Requires 3 parametric curves, where `X = 0`, `Y = 1`, `Z = 2`.
Vector3,
/// A 4-dimensional vector of real values. Requires 4 parametric curves, where `X = 0`, `Y = 1`, `Z = 2`, `W = 3`.
Vector4,
/// A quaternion that represents some rotation. Requires 3 parametric curves, where `XAngle = 0`, `YAngle = 1`,
/// `ZAngle = 2`. The order of rotations is `XYZ`. This triple of curves forms Euler angles which are interpolated
/// and then converted to a quaternion.
UnitQuaternion,
}
impl TrackValueKind {
/// Returns count of elementary components of a value kind. For example: Vector3 consists of 3 components where
/// each component has its own parametric curve.
pub fn components_count(self) -> usize {
match self {
TrackValueKind::Real => 1,
TrackValueKind::Vector2 => 2,
TrackValueKind::Vector3 => 3,
TrackValueKind::Vector4 => 4,
TrackValueKind::UnitQuaternion => {
// Euler angles
3
}
}
}
}
impl Default for TrackValueKind {
fn default() -> Self {
Self::Vector3
}
}
/// Interpolation mode for track data.
#[derive(Visit, Reflect, Debug, Clone, Default, PartialEq)]
pub enum InterpolationMode {
/// Default interpolation mode.
#[default]
Default,
/// This mode forces the engine to use short-path angle interpolation.
ShortPath,
}
/// Container for a track data. Strictly speaking, it is just a set of parametric curves which can be
/// fetched at a given time position simultaneously, producing a value of desired type. Which type of
/// value is produced is defined by [`TrackValueKind`] enumeration. Usually a container contains up to
/// 4 curves (the largest type supported is [`Vector4`]).
///
/// Each component is bound to a specific curve. For example, in case of [`Vector3`] its components bound
/// to the following curve indices: `X = 0`, `Y = 1`, `Z = 2`. This order cannot be changed.
#[derive(Visit, Reflect, Debug, Clone, Default, PartialEq)]
pub struct TrackDataContainer {
curves: Vec<Curve>,
kind: TrackValueKind,
/// Interpolation mode.
#[visit(optional)] // Backward compatibility.
pub mode: InterpolationMode,
}
impl TrackDataContainer {
/// Creates a new container, that is able to produce values defined by [`TrackValueKind`] input parameter.
/// The number of curves in the output container is defined by [`TrackValueKind::components_count`], for
/// example [`Vector3`] has 3 components (X, Y, Z). An empty container can be created using [`Self::default`]
/// method.
pub fn new(kind: TrackValueKind) -> Self {
Self {
kind,
// Do not use `vec![Default::default(); kind.components_count()]` here because
// it clones a curve that was created in first macro argument which leads to
// non-unique ids of the curves.
curves: (0..kind.components_count())
.map(|_| Curve::default())
.collect(),
mode: Default::default(),
}
}
/// Adds a new curve to the container. Keep in mind, that the actual useful amount of curves has soft limit
/// of four due to [`TrackValueKind`], any excessive curves will be ignored.
pub fn add_curve(&mut self, curve: Curve) {
self.curves.push(curve)
}
/// Tries to borrow a curve at a given index.
pub fn curve(&self, index: usize) -> Option<&Curve> {
self.curves.get(index)
}
/// Tries to borrow a curve at a given index.
pub fn curve_mut(&mut self, index: usize) -> Option<&mut Curve> {
self.curves.get_mut(index)
}
/// Returns a reference to curves container.
pub fn curves_ref(&self) -> &[Curve] {
&self.curves
}
/// Tries to borrow a curve at a given index.
pub fn curves_mut(&mut self) -> &mut [Curve] {
&mut self.curves
}
/// Sets new kind of output value. Keep in mind, that the curves will remain unchanged, if you need
/// to re-use the container you might need to re-create/re-fill the curves too.
pub fn set_value_kind(&mut self, kind: TrackValueKind) {
self.kind = kind;
}
/// Returns the kind of output value produced by the container.
pub fn value_kind(&self) -> TrackValueKind {
self.kind
}
/// Tries to get a value at a given time. The method could fail if the internal set of curves is malformed
/// and cannot produce a desired value (for example, [`Vector3`] can be fetched only if the amount of curves
/// is 3).
pub fn fetch(&self, time: f32) -> Option<TrackValue> {
match self.kind {
TrackValueKind::Real => Some(TrackValue::Real(self.curves.first()?.value_at(time))),
TrackValueKind::Vector2 => Some(TrackValue::Vector2(Vector2::new(
self.curves.first()?.value_at(time),
self.curves.get(1)?.value_at(time),
))),
TrackValueKind::Vector3 => Some(TrackValue::Vector3(Vector3::new(
self.curves.first()?.value_at(time),
self.curves.get(1)?.value_at(time),
self.curves.get(2)?.value_at(time),
))),
TrackValueKind::Vector4 => Some(TrackValue::Vector4(Vector4::new(
self.curves.first()?.value_at(time),
self.curves.get(1)?.value_at(time),
self.curves.get(2)?.value_at(time),
self.curves.get(3)?.value_at(time),
))),
TrackValueKind::UnitQuaternion => {
// Convert Euler angles to quaternion
let (x, y, z) = match self.mode {
InterpolationMode::Default => (
self.curves.first()?.value_at(time),
self.curves.get(1)?.value_at(time),
self.curves.get(2)?.value_at(time),
),
InterpolationMode::ShortPath => (
self.curves.first()?.angle_at(time),
self.curves.get(1)?.angle_at(time),
self.curves.get(2)?.angle_at(time),
),
};
Some(TrackValue::UnitQuaternion(quat_from_euler(
Vector3::new(x, y, z),
RotationOrder::XYZ,
)))
}
}
}
/// Find a right-most key on one of the curves in the container and returns its position. This position
/// can be treated as a maximum "length" of the container.
pub fn time_length(&self) -> f32 {
let mut length = 0.0;
for curve in self.curves.iter() {
let max_location = curve.max_location();
if max_location > length {
length = max_location;
}
}
length
}
}