Skip to main content

molgfx_math/types/
matrix.rs

1//! Project-owned quaternions and matrices lowered to glam operations.
2
3use std::ops::Mul;
4
5use bytemuck::{Pod, Zeroable};
6use serde::{Deserialize, Serialize};
7
8use super::{Vec3, Vec4};
9
10/// A SIMD-aligned quaternion stored as `(x, y, z, w)`.
11#[repr(C, align(16))]
12#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
13#[serde(from = "[f32; 4]", into = "[f32; 4]")]
14pub struct Quat {
15    /// X component.
16    pub x: f32,
17    /// Y component.
18    pub y: f32,
19    /// Z component.
20    pub z: f32,
21    /// Scalar component.
22    pub w: f32,
23}
24
25impl Quat {
26    /// Identity rotation.
27    pub const IDENTITY: Self = Self::from_xyzw(0.0, 0.0, 0.0, 1.0);
28    /// Builds a quaternion.
29    #[must_use]
30    #[inline]
31    pub const fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self {
32        Self { x, y, z, w }
33    }
34    /// Builds from an array.
35    #[must_use]
36    #[inline]
37    pub const fn from_array(value: [f32; 4]) -> Self {
38        Self::from_xyzw(value[0], value[1], value[2], value[3])
39    }
40    /// Returns components.
41    #[must_use]
42    #[inline]
43    pub const fn to_array(self) -> [f32; 4] {
44        [self.x, self.y, self.z, self.w]
45    }
46    /// Rotation around X.
47    #[must_use]
48    #[inline]
49    pub fn from_rotation_x(angle: f32) -> Self {
50        glam::Quat::from_rotation_x(angle).into()
51    }
52    /// Rotation around Y.
53    #[must_use]
54    #[inline]
55    pub fn from_rotation_y(angle: f32) -> Self {
56        glam::Quat::from_rotation_y(angle).into()
57    }
58    /// Rotation around Z.
59    #[must_use]
60    #[inline]
61    pub fn from_rotation_z(angle: f32) -> Self {
62        glam::Quat::from_rotation_z(angle).into()
63    }
64    /// Rotation around a normalized axis.
65    #[must_use]
66    #[inline]
67    pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
68        glam::Quat::from_axis_angle(axis.into(), angle).into()
69    }
70    /// Shortest rotation between directions.
71    #[must_use]
72    #[inline]
73    pub fn from_rotation_arc(from: Vec3, to: Vec3) -> Self {
74        glam::Quat::from_rotation_arc(from.into(), to.into()).into()
75    }
76    /// Builds from a rotation matrix.
77    #[must_use]
78    #[inline]
79    pub fn from_mat3(matrix: &Mat3) -> Self {
80        glam::Quat::from_mat3(&(*matrix).into()).into()
81    }
82    /// Spherical interpolation.
83    #[must_use]
84    #[inline]
85    pub fn slerp(self, rhs: Self, amount: f32) -> Self {
86        glam::Quat::from(self).slerp(rhs.into(), amount).into()
87    }
88    /// Returns whether every component is finite.
89    #[must_use]
90    #[inline]
91    pub fn is_finite(self) -> bool {
92        glam::Quat::from(self).is_finite()
93    }
94    /// Squared quaternion length.
95    #[must_use]
96    #[inline]
97    pub fn length_squared(self) -> f32 {
98        glam::Quat::from(self).length_squared()
99    }
100    /// Returns a normalized quaternion.
101    #[must_use]
102    #[inline]
103    pub fn normalize(self) -> Self {
104        glam::Quat::from(self).normalize().into()
105    }
106}
107
108impl Default for Quat {
109    #[inline]
110    fn default() -> Self {
111        Self::IDENTITY
112    }
113}
114impl From<[f32; 4]> for Quat {
115    #[inline]
116    fn from(value: [f32; 4]) -> Self {
117        Self::from_array(value)
118    }
119}
120impl From<Quat> for [f32; 4] {
121    #[inline]
122    fn from(value: Quat) -> Self {
123        value.to_array()
124    }
125}
126impl From<glam::Quat> for Quat {
127    #[inline]
128    fn from(value: glam::Quat) -> Self {
129        value.to_array().into()
130    }
131}
132impl From<Quat> for glam::Quat {
133    #[inline]
134    fn from(value: Quat) -> Self {
135        Self::from_array(value.to_array())
136    }
137}
138impl Mul for Quat {
139    type Output = Self;
140    #[inline]
141    fn mul(self, rhs: Self) -> Self {
142        (glam::Quat::from(self) * glam::Quat::from(rhs)).into()
143    }
144}
145impl Mul<Vec3> for Quat {
146    type Output = Vec3;
147    #[inline]
148    fn mul(self, rhs: Vec3) -> Vec3 {
149        (glam::Quat::from(self) * glam::Vec3::from(rhs)).into()
150    }
151}
152
153/// A column-major 3×3 matrix.
154#[repr(C)]
155#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
156pub struct Mat3 {
157    /// First column.
158    pub x_axis: Vec3,
159    /// Second column.
160    pub y_axis: Vec3,
161    /// Third column.
162    pub z_axis: Vec3,
163}
164
165impl Mat3 {
166    /// Identity matrix.
167    pub const IDENTITY: Self = Self::from_cols(Vec3::X, Vec3::Y, Vec3::Z);
168    /// Builds from columns.
169    #[must_use]
170    #[inline]
171    pub const fn from_cols(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Self {
172        Self {
173            x_axis,
174            y_axis,
175            z_axis,
176        }
177    }
178    /// Extracts the linear part of a 4×4 matrix.
179    #[must_use]
180    #[inline]
181    pub fn from_mat4(value: Mat4) -> Self {
182        glam::Mat3::from_mat4(value.into()).into()
183    }
184    /// Builds from a quaternion.
185    #[must_use]
186    #[inline]
187    pub fn from_quat(value: Quat) -> Self {
188        glam::Mat3::from_quat(value.into()).into()
189    }
190    /// Matrix inverse.
191    #[must_use]
192    #[inline]
193    pub fn inverse(self) -> Self {
194        glam::Mat3::from(self).inverse().into()
195    }
196    /// Matrix transpose.
197    #[must_use]
198    #[inline]
199    pub fn transpose(self) -> Self {
200        glam::Mat3::from(self).transpose().into()
201    }
202    /// Determinant.
203    #[must_use]
204    #[inline]
205    pub fn determinant(self) -> f32 {
206        glam::Mat3::from(self).determinant()
207    }
208    /// Returns whether all entries are finite.
209    #[must_use]
210    #[inline]
211    pub fn is_finite(self) -> bool {
212        glam::Mat3::from(self).is_finite()
213    }
214}
215
216impl From<glam::Mat3> for Mat3 {
217    #[inline]
218    fn from(value: glam::Mat3) -> Self {
219        Self::from_cols(
220            value.x_axis.into(),
221            value.y_axis.into(),
222            value.z_axis.into(),
223        )
224    }
225}
226impl From<Mat3> for glam::Mat3 {
227    #[inline]
228    fn from(value: Mat3) -> Self {
229        Self::from_cols(
230            value.x_axis.into(),
231            value.y_axis.into(),
232            value.z_axis.into(),
233        )
234    }
235}
236impl Mul for Mat3 {
237    type Output = Self;
238    #[inline]
239    fn mul(self, rhs: Self) -> Self {
240        (glam::Mat3::from(self) * glam::Mat3::from(rhs)).into()
241    }
242}
243impl Mul<Vec3> for Mat3 {
244    type Output = Vec3;
245    #[inline]
246    fn mul(self, rhs: Vec3) -> Vec3 {
247        (glam::Mat3::from(self) * glam::Vec3::from(rhs)).into()
248    }
249}
250
251/// A SIMD-aligned column-major 4×4 matrix.
252#[repr(C, align(16))]
253#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
254pub struct Mat4 {
255    /// First column.
256    pub x_axis: Vec4,
257    /// Second column.
258    pub y_axis: Vec4,
259    /// Third column.
260    pub z_axis: Vec4,
261    /// Fourth column.
262    pub w_axis: Vec4,
263}
264
265impl Mat4 {
266    /// Identity matrix.
267    pub const IDENTITY: Self = Self::from_cols(
268        Vec4::new(1.0, 0.0, 0.0, 0.0),
269        Vec4::new(0.0, 1.0, 0.0, 0.0),
270        Vec4::new(0.0, 0.0, 1.0, 0.0),
271        Vec4::new(0.0, 0.0, 0.0, 1.0),
272    );
273    /// Zero matrix.
274    pub const ZERO: Self = Self::from_cols(Vec4::ZERO, Vec4::ZERO, Vec4::ZERO, Vec4::ZERO);
275    /// Builds from columns.
276    #[must_use]
277    #[inline]
278    pub const fn from_cols(x_axis: Vec4, y_axis: Vec4, z_axis: Vec4, w_axis: Vec4) -> Self {
279        Self {
280            x_axis,
281            y_axis,
282            z_axis,
283            w_axis,
284        }
285    }
286    /// Builds from column-major elements.
287    #[must_use]
288    #[inline]
289    pub fn from_cols_array(value: &[f32; 16]) -> Self {
290        glam::Mat4::from_cols_array(value).into()
291    }
292    /// Returns column-major elements.
293    #[must_use]
294    #[inline]
295    pub fn to_cols_array(self) -> [f32; 16] {
296        glam::Mat4::from(self).to_cols_array()
297    }
298    /// Returns a column-major nested array.
299    #[must_use]
300    #[inline]
301    pub fn to_cols_array_2d(self) -> [[f32; 4]; 4] {
302        glam::Mat4::from(self).to_cols_array_2d()
303    }
304    /// Translation matrix.
305    #[must_use]
306    #[inline]
307    pub fn from_translation(value: Vec3) -> Self {
308        glam::Mat4::from_translation(value.into()).into()
309    }
310    /// Rotation around Y.
311    #[must_use]
312    #[inline]
313    pub fn from_rotation_y(angle: f32) -> Self {
314        glam::Mat4::from_rotation_y(angle).into()
315    }
316    /// Scale, rotation and translation matrix.
317    #[must_use]
318    #[inline]
319    pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
320        glam::Mat4::from_scale_rotation_translation(
321            scale.into(),
322            rotation.into(),
323            translation.into(),
324        )
325        .into()
326    }
327    /// Transforms a point.
328    #[must_use]
329    #[inline]
330    pub fn transform_point3(self, point: Vec3) -> Vec3 {
331        glam::Mat4::from(self).transform_point3(point.into()).into()
332    }
333    /// Transforms a direction.
334    #[must_use]
335    #[inline]
336    pub fn transform_vector3(self, vector: Vec3) -> Vec3 {
337        glam::Mat4::from(self)
338            .transform_vector3(vector.into())
339            .into()
340    }
341    /// Matrix inverse.
342    #[must_use]
343    #[inline]
344    pub fn inverse(self) -> Self {
345        glam::Mat4::from(self).inverse().into()
346    }
347    /// Determinant.
348    #[must_use]
349    #[inline]
350    pub fn determinant(self) -> f32 {
351        glam::Mat4::from(self).determinant()
352    }
353    /// Returns whether all entries are finite.
354    #[must_use]
355    #[inline]
356    pub fn is_finite(self) -> bool {
357        glam::Mat4::from(self).is_finite()
358    }
359    /// Returns a row.
360    #[must_use]
361    #[inline]
362    pub fn row(self, index: usize) -> Vec4 {
363        glam::Mat4::from(self).row(index).into()
364    }
365}
366
367impl Default for Mat4 {
368    #[inline]
369    fn default() -> Self {
370        Self::IDENTITY
371    }
372}
373impl From<glam::Mat4> for Mat4 {
374    #[inline]
375    fn from(value: glam::Mat4) -> Self {
376        Self::from_cols(
377            value.x_axis.into(),
378            value.y_axis.into(),
379            value.z_axis.into(),
380            value.w_axis.into(),
381        )
382    }
383}
384impl From<Mat4> for glam::Mat4 {
385    #[inline]
386    fn from(value: Mat4) -> Self {
387        Self::from_cols(
388            value.x_axis.into(),
389            value.y_axis.into(),
390            value.z_axis.into(),
391            value.w_axis.into(),
392        )
393    }
394}
395impl Mul for Mat4 {
396    type Output = Self;
397    #[inline]
398    fn mul(self, rhs: Self) -> Self {
399        (glam::Mat4::from(self) * glam::Mat4::from(rhs)).into()
400    }
401}
402impl Mul<Vec4> for Mat4 {
403    type Output = Vec4;
404    #[inline]
405    fn mul(self, rhs: Vec4) -> Vec4 {
406        (glam::Mat4::from(self) * glam::Vec4::from(rhs)).into()
407    }
408}