Skip to main content

mirage_engine/
transform.rs

1use bytemuck::{Pod, Zeroable};
2
3use crate::math::{Mat4, Quat, Vec3};
4
5/// A draw's position, turn, and size.
6#[repr(transparent)]
7#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
8pub struct Transform(Mat4);
9
10impl Transform {
11    /// No position, turn, or size change.
12    pub const IDENTITY: Self = Self(Mat4::IDENTITY);
13
14    /// At `translation`, in meters.
15    pub fn from_translation(translation: Vec3) -> Self {
16        Self(Mat4::from_translation(translation))
17    }
18
19    /// Turned about the origin.
20    pub fn from_rotation(rotation: Quat) -> Self {
21        Self(Mat4::from_quat(rotation))
22    }
23
24    /// Sized about the origin, per axis.
25    pub fn from_scale(scale: Vec3) -> Self {
26        Self(Mat4::from_scale(scale))
27    }
28
29    /// Turned about the origin, then moved.
30    pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
31        Self(Mat4::from_rotation_translation(rotation, translation))
32    }
33
34    /// Sized, then turned, then moved.
35    pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
36        Self(Mat4::from_scale_rotation_translation(
37            scale,
38            rotation,
39            translation,
40        ))
41    }
42
43    /// The matrix that takes mesh space to world space.
44    pub const fn matrix(self) -> Mat4 {
45        self.0
46    }
47
48    /// True where every value of the matrix is finite, so the transform
49    /// places a draw.
50    pub(crate) fn is_finite(self) -> bool {
51        self.0.is_finite()
52    }
53}
54
55impl core::ops::Mul for Transform {
56    type Output = Self;
57
58    /// This transform applied over `after` — matrix order, so
59    /// `base * track.at(elapsed)` places the animation within `base`.
60    fn mul(self, after: Self) -> Self {
61        Self(self.0 * after.0)
62    }
63}
64
65impl Default for Transform {
66    /// [`Transform::IDENTITY`].
67    fn default() -> Self {
68        Self::IDENTITY
69    }
70}
71
72impl From<Vec3> for Transform {
73    /// A position.
74    fn from(translation: Vec3) -> Self {
75        Self::from_translation(translation)
76    }
77}
78
79impl From<Mat4> for Transform {
80    /// A matrix a game built itself; only its affine part is kept.
81    fn from(matrix: Mat4) -> Self {
82        Self(matrix)
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn a_rotation_and_a_translation_turn_a_point_before_they_move_it() {
92        let quarter = Quat::from_rotation_y(core::f32::consts::FRAC_PI_2);
93        let placed = Transform::from_rotation_translation(quarter, Vec3::X);
94
95        let point = placed.matrix().transform_point3(Vec3::X);
96
97        assert!(
98            point.abs_diff_eq(Vec3::new(1.0, 0.0, -1.0), 1e-6),
99            "a quarter turn takes {} onto {}, which the meter out then places at {point}",
100            Vec3::X,
101            Vec3::NEG_Z
102        );
103        assert!(
104            !point.abs_diff_eq(Vec3::new(0.0, 0.0, -2.0), 1e-6),
105            "and the meter is not moved before the turn"
106        );
107    }
108}