moon_engine/
transform.rs

1//! The [`Transform`] ans [`Transform2D`] structs.
2
3use std::ops::Add;
4
5use crate::Mat4;
6use crate::Vec2;
7use crate::Vec3;
8
9/// A 3D representation of an entity's position, rotation and scale.
10///
11/// A [`Transform`] contains [`Vec3`]s for Position, ROtation and Scale.
12#[derive(Debug, Clone)]
13pub struct Transform {
14    matrix: Mat4,
15    /// Position component of the [`Transform`].
16    pub position: Vec3,
17    /// Rotation component of the [`Transform`].
18    pub rotation: Vec3,
19    /// Scale component of the [`Transform`].
20    pub scale: Vec3,
21}
22
23impl Default for Transform {
24    fn default() -> Self {
25        Self {
26            matrix: Mat4::identity(),
27            position: Vec3::zeros(),
28            rotation: Vec3::zeros(),
29            scale: Vec3::from_element(1.0),
30        }
31    }
32}
33
34impl Transform {
35    /// Create a new [`Transform`] with default values.
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Create a new [`Transform`] with an initial position.
41    pub fn new_with_position(position: Vec3) -> Self {
42        Self {
43            position,
44            matrix: Mat4::new_translation(&position),
45            ..Self::default()
46        }
47    }
48
49    /// Get a clone of the [`Mat4`].
50    pub fn matrix(&self) -> Mat4 {
51        self.matrix
52    }
53
54    /// Get the [`Mat4`] representing the transform as a slice of [`f32`] to use with WebGL.
55    pub fn matrix_slice(&mut self) -> &[f32] {
56        self.matrix.as_slice()
57    }
58
59    /// Recalculate the matrix of the [`Transform`].
60    fn recalculate_matrix(&mut self) {
61        self.matrix = Mat4::new_translation(&self.position)
62            * Mat4::new_rotation(self.rotation)
63            * Mat4::new_nonuniform_scaling(&self.scale);
64    }
65
66    /// Set the [`Transform`]'s position and calculate its matrix.
67    pub fn set_position(&mut self, position: Vec3) {
68        self.position = position;
69        self.recalculate_matrix();
70    }
71
72    /// Set the [`Transform`]'s rotation and calculate its matrix.
73    pub fn set_rotation(&mut self, rotation: f32) {
74        self.rotation = Vec3::z() * rotation;
75        self.recalculate_matrix();
76    }
77
78    /// Set the [`Transform`]'s scale and calculate its matrix.
79    pub fn set_scale(&mut self, scale: Vec3) {
80        self.scale = scale;
81        self.recalculate_matrix();
82    }
83
84    /// Get the position as a slice.
85    pub fn get_position(&self) -> &[f32] {
86        self.position.as_slice()
87    }
88
89    /// Get the rotation as an f32.
90    pub fn get_rotation(&self) -> f32 {
91        self.rotation.z
92    }
93
94    /// Get the scale as a slice.
95    pub fn get_scale(&self) -> &[f32] {
96        self.scale.as_slice()
97    }
98}
99
100/// A 2D counterpart for the [`Transform`].
101///
102/// A [`Transform2D`] contains Position and Scale [`Vec2`]s and a float for rotation.
103#[derive(Debug, Clone, Copy)]
104pub struct Transform2D {
105    /// Position of the [`Transform2D`].
106    pub position: Vec2,
107    /// Rotation of the [`Transform2D`].
108    pub rotation: f32,
109    /// Scale of the [`Transform2D`].
110    pub scale: Vec2,
111}
112
113impl Default for Transform2D {
114    fn default() -> Self {
115        Self {
116            position: Vec2::zeros(),
117            rotation: 0.0f32,
118            scale: Vec2::from_element(1.0),
119        }
120    }
121}
122
123impl Add for Transform2D {
124    type Output = Transform2D;
125
126    fn add(self, rhs: Self) -> Self::Output {
127        Transform2D {
128            position: self.position + rhs.position,
129            rotation: self.rotation + rhs.rotation,
130            ..rhs
131        }
132    }
133}
134
135impl Transform2D {
136    /// Create a new [`Transform2D`] a given scale
137    pub fn new_with_position(pos_x: f32, pos_y: f32) -> Self {
138        Self {
139            position: Vec2::new(pos_x, pos_y),
140            ..Default::default()
141        }
142    }
143
144    /// Create a new [`Transform2D`] a given scale
145    pub fn new_with_scale(scale_x: f32, scale_y: f32) -> Self {
146        Self {
147            scale: Vec2::new(scale_x, scale_y),
148            ..Default::default()
149        }
150    }
151
152    /// Translate a [`Transform2D`] using `X` and `Y` deltas.
153    pub fn translate(&mut self, delta_x: f32, delta_y: f32) {
154        self.position.x += delta_x;
155        self.position.y += delta_y;
156    }
157
158    /// Get a [`Mat4`] of the [`Transform2D`].
159    pub fn matrix(&self) -> Mat4 {
160        Mat4::new_translation(&Vec3::new(self.position.x, self.position.y, 0.0))
161    }
162}