aboutsummaryrefslogtreecommitdiff
path: root/src/f32/sse2/mat4.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/f32/sse2/mat4.rs')
-rw-r--r--src/f32/sse2/mat4.rs1371
1 files changed, 1371 insertions, 0 deletions
diff --git a/src/f32/sse2/mat4.rs b/src/f32/sse2/mat4.rs
new file mode 100644
index 0000000..aa69493
--- /dev/null
+++ b/src/f32/sse2/mat4.rs
@@ -0,0 +1,1371 @@
+// Generated from mat.rs.tera template. Edit the template, not the generated file.
+
+use crate::{sse2::*, swizzles::*, DMat4, EulerRot, Mat3, Mat3A, Quat, Vec3, Vec3A, Vec4};
+#[cfg(not(target_arch = "spirv"))]
+use core::fmt;
+use core::iter::{Product, Sum};
+use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
+
+#[cfg(target_arch = "x86")]
+use core::arch::x86::*;
+#[cfg(target_arch = "x86_64")]
+use core::arch::x86_64::*;
+
+#[cfg(feature = "libm")]
+#[allow(unused_imports)]
+use num_traits::Float;
+
+/// Creates a 4x4 matrix from column vectors.
+#[inline(always)]
+pub const fn mat4(x_axis: Vec4, y_axis: Vec4, z_axis: Vec4, w_axis: Vec4) -> Mat4 {
+ Mat4::from_cols(x_axis, y_axis, z_axis, w_axis)
+}
+
+/// A 4x4 column major matrix.
+///
+/// This 4x4 matrix type features convenience methods for creating and using affine transforms and
+/// perspective projections. If you are primarily dealing with 3D affine transformations
+/// considering using [`Affine3A`](crate::Affine3A) which is faster than a 4x4 matrix
+/// for some affine operations.
+///
+/// Affine transformations including 3D translation, rotation and scale can be created
+/// using methods such as [`Self::from_translation()`], [`Self::from_quat()`],
+/// [`Self::from_scale()`] and [`Self::from_scale_rotation_translation()`].
+///
+/// Othographic projections can be created using the methods [`Self::orthographic_lh()`] for
+/// left-handed coordinate systems and [`Self::orthographic_rh()`] for right-handed
+/// systems. The resulting matrix is also an affine transformation.
+///
+/// The [`Self::transform_point3()`] and [`Self::transform_vector3()`] convenience methods
+/// are provided for performing affine transformations on 3D vectors and points. These
+/// multiply 3D inputs as 4D vectors with an implicit `w` value of `1` for points and `0`
+/// for vectors respectively. These methods assume that `Self` contains a valid affine
+/// transform.
+///
+/// Perspective projections can be created using methods such as
+/// [`Self::perspective_lh()`], [`Self::perspective_infinite_lh()`] and
+/// [`Self::perspective_infinite_reverse_lh()`] for left-handed co-ordinate systems and
+/// [`Self::perspective_rh()`], [`Self::perspective_infinite_rh()`] and
+/// [`Self::perspective_infinite_reverse_rh()`] for right-handed co-ordinate systems.
+///
+/// The resulting perspective project can be use to transform 3D vectors as points with
+/// perspective correction using the [`Self::project_point3()`] convenience method.
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct Mat4 {
+ pub x_axis: Vec4,
+ pub y_axis: Vec4,
+ pub z_axis: Vec4,
+ pub w_axis: Vec4,
+}
+
+impl Mat4 {
+ /// A 4x4 matrix with all elements set to `0.0`.
+ pub const ZERO: Self = Self::from_cols(Vec4::ZERO, Vec4::ZERO, Vec4::ZERO, Vec4::ZERO);
+
+ /// A 4x4 identity matrix, where all diagonal elements are `1`, and all off-diagonal elements are `0`.
+ pub const IDENTITY: Self = Self::from_cols(Vec4::X, Vec4::Y, Vec4::Z, Vec4::W);
+
+ /// All NAN:s.
+ pub const NAN: Self = Self::from_cols(Vec4::NAN, Vec4::NAN, Vec4::NAN, Vec4::NAN);
+
+ #[allow(clippy::too_many_arguments)]
+ #[inline(always)]
+ const fn new(
+ m00: f32,
+ m01: f32,
+ m02: f32,
+ m03: f32,
+ m10: f32,
+ m11: f32,
+ m12: f32,
+ m13: f32,
+ m20: f32,
+ m21: f32,
+ m22: f32,
+ m23: f32,
+ m30: f32,
+ m31: f32,
+ m32: f32,
+ m33: f32,
+ ) -> Self {
+ Self {
+ x_axis: Vec4::new(m00, m01, m02, m03),
+ y_axis: Vec4::new(m10, m11, m12, m13),
+ z_axis: Vec4::new(m20, m21, m22, m23),
+ w_axis: Vec4::new(m30, m31, m32, m33),
+ }
+ }
+
+ /// Creates a 4x4 matrix from two column vectors.
+ #[inline(always)]
+ pub const fn from_cols(x_axis: Vec4, y_axis: Vec4, z_axis: Vec4, w_axis: Vec4) -> Self {
+ Self {
+ x_axis,
+ y_axis,
+ z_axis,
+ w_axis,
+ }
+ }
+
+ /// Creates a 4x4 matrix from a `[f32; 16]` array stored in column major order.
+ /// If your data is stored in row major you will need to `transpose` the returned
+ /// matrix.
+ #[inline]
+ pub const fn from_cols_array(m: &[f32; 16]) -> Self {
+ Self::new(
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13],
+ m[14], m[15],
+ )
+ }
+
+ /// Creates a `[f32; 16]` array storing data in column major order.
+ /// If you require data in row major order `transpose` the matrix first.
+ #[inline]
+ pub const fn to_cols_array(&self) -> [f32; 16] {
+ let [x_axis_x, x_axis_y, x_axis_z, x_axis_w] = self.x_axis.to_array();
+ let [y_axis_x, y_axis_y, y_axis_z, y_axis_w] = self.y_axis.to_array();
+ let [z_axis_x, z_axis_y, z_axis_z, z_axis_w] = self.z_axis.to_array();
+ let [w_axis_x, w_axis_y, w_axis_z, w_axis_w] = self.w_axis.to_array();
+
+ [
+ x_axis_x, x_axis_y, x_axis_z, x_axis_w, y_axis_x, y_axis_y, y_axis_z, y_axis_w,
+ z_axis_x, z_axis_y, z_axis_z, z_axis_w, w_axis_x, w_axis_y, w_axis_z, w_axis_w,
+ ]
+ }
+
+ /// Creates a 4x4 matrix from a `[[f32; 4]; 4]` 4D array stored in column major order.
+ /// If your data is in row major order you will need to `transpose` the returned
+ /// matrix.
+ #[inline]
+ pub const fn from_cols_array_2d(m: &[[f32; 4]; 4]) -> Self {
+ Self::from_cols(
+ Vec4::from_array(m[0]),
+ Vec4::from_array(m[1]),
+ Vec4::from_array(m[2]),
+ Vec4::from_array(m[3]),
+ )
+ }
+
+ /// Creates a `[[f32; 4]; 4]` 4D array storing data in column major order.
+ /// If you require data in row major order `transpose` the matrix first.
+ #[inline]
+ pub const fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
+ [
+ self.x_axis.to_array(),
+ self.y_axis.to_array(),
+ self.z_axis.to_array(),
+ self.w_axis.to_array(),
+ ]
+ }
+
+ /// Creates a 4x4 matrix with its diagonal set to `diagonal` and all other entries set to 0.
+ #[doc(alias = "scale")]
+ #[inline]
+ pub const fn from_diagonal(diagonal: Vec4) -> Self {
+ // diagonal.x, diagonal.y etc can't be done in a const-context
+ let [x, y, z, w] = diagonal.to_array();
+ Self::new(
+ x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, w,
+ )
+ }
+
+ #[inline]
+ fn quat_to_axes(rotation: Quat) -> (Vec4, Vec4, Vec4) {
+ glam_assert!(rotation.is_normalized());
+
+ let (x, y, z, w) = rotation.into();
+ let x2 = x + x;
+ let y2 = y + y;
+ let z2 = z + z;
+ let xx = x * x2;
+ let xy = x * y2;
+ let xz = x * z2;
+ let yy = y * y2;
+ let yz = y * z2;
+ let zz = z * z2;
+ let wx = w * x2;
+ let wy = w * y2;
+ let wz = w * z2;
+
+ let x_axis = Vec4::new(1.0 - (yy + zz), xy + wz, xz - wy, 0.0);
+ let y_axis = Vec4::new(xy - wz, 1.0 - (xx + zz), yz + wx, 0.0);
+ let z_axis = Vec4::new(xz + wy, yz - wx, 1.0 - (xx + yy), 0.0);
+ (x_axis, y_axis, z_axis)
+ }
+
+ /// Creates an affine transformation matrix from the given 3D `scale`, `rotation` and
+ /// `translation`.
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
+ #[inline]
+ pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
+ let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation);
+ Self::from_cols(
+ x_axis.mul(scale.x),
+ y_axis.mul(scale.y),
+ z_axis.mul(scale.z),
+ Vec4::from((translation, 1.0)),
+ )
+ }
+
+ /// Creates an affine transformation matrix from the given 3D `translation`.
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
+ #[inline]
+ pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
+ let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation);
+ Self::from_cols(x_axis, y_axis, z_axis, Vec4::from((translation, 1.0)))
+ }
+
+ /// Extracts `scale`, `rotation` and `translation` from `self`. The input matrix is
+ /// expected to be a 3D affine transformation matrix otherwise the output will be invalid.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if the determinant of `self` is zero or if the resulting scale vector
+ /// contains any zero elements when `glam_assert` is enabled.
+ #[inline]
+ pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
+ let det = self.determinant();
+ glam_assert!(det != 0.0);
+
+ let scale = Vec3::new(
+ self.x_axis.length() * det.signum(),
+ self.y_axis.length(),
+ self.z_axis.length(),
+ );
+
+ glam_assert!(scale.cmpne(Vec3::ZERO).all());
+
+ let inv_scale = scale.recip();
+
+ let rotation = Quat::from_rotation_axes(
+ self.x_axis.mul(inv_scale.x).xyz(),
+ self.y_axis.mul(inv_scale.y).xyz(),
+ self.z_axis.mul(inv_scale.z).xyz(),
+ );
+
+ let translation = self.w_axis.xyz();
+
+ (scale, rotation, translation)
+ }
+
+ /// Creates an affine transformation matrix from the given `rotation` quaternion.
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
+ #[inline]
+ pub fn from_quat(rotation: Quat) -> Self {
+ let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation);
+ Self::from_cols(x_axis, y_axis, z_axis, Vec4::W)
+ }
+
+ /// Creates an affine transformation matrix from the given 3x3 linear transformation
+ /// matrix.
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ #[inline]
+ pub fn from_mat3(m: Mat3) -> Self {
+ Self::from_cols(
+ Vec4::from((m.x_axis, 0.0)),
+ Vec4::from((m.y_axis, 0.0)),
+ Vec4::from((m.z_axis, 0.0)),
+ Vec4::W,
+ )
+ }
+
+ /// Creates an affine transformation matrix from the given 3x3 linear transformation
+ /// matrix.
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ #[inline]
+ pub fn from_mat3a(m: Mat3A) -> Self {
+ Self::from_cols(
+ Vec4::from((m.x_axis, 0.0)),
+ Vec4::from((m.y_axis, 0.0)),
+ Vec4::from((m.z_axis, 0.0)),
+ Vec4::W,
+ )
+ }
+
+ /// Creates an affine transformation matrix from the given 3D `translation`.
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ #[inline]
+ pub fn from_translation(translation: Vec3) -> Self {
+ Self::from_cols(
+ Vec4::X,
+ Vec4::Y,
+ Vec4::Z,
+ Vec4::new(translation.x, translation.y, translation.z, 1.0),
+ )
+ }
+
+ /// Creates an affine transformation matrix containing a 3D rotation around a normalized
+ /// rotation `axis` of `angle` (in radians).
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `axis` is not normalized when `glam_assert` is enabled.
+ #[inline]
+ pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
+ glam_assert!(axis.is_normalized());
+
+ let (sin, cos) = angle.sin_cos();
+ let axis_sin = axis.mul(sin);
+ let axis_sq = axis.mul(axis);
+ let omc = 1.0 - cos;
+ let xyomc = axis.x * axis.y * omc;
+ let xzomc = axis.x * axis.z * omc;
+ let yzomc = axis.y * axis.z * omc;
+ Self::from_cols(
+ Vec4::new(
+ axis_sq.x * omc + cos,
+ xyomc + axis_sin.z,
+ xzomc - axis_sin.y,
+ 0.0,
+ ),
+ Vec4::new(
+ xyomc - axis_sin.z,
+ axis_sq.y * omc + cos,
+ yzomc + axis_sin.x,
+ 0.0,
+ ),
+ Vec4::new(
+ xzomc + axis_sin.y,
+ yzomc - axis_sin.x,
+ axis_sq.z * omc + cos,
+ 0.0,
+ ),
+ Vec4::W,
+ )
+ }
+
+ #[inline]
+ /// Creates a affine transformation matrix containing a rotation from the given euler
+ /// rotation sequence and angles (in radians).
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ pub fn from_euler(order: EulerRot, a: f32, b: f32, c: f32) -> Self {
+ let quat = Quat::from_euler(order, a, b, c);
+ Self::from_quat(quat)
+ }
+
+ /// Creates an affine transformation matrix containing a 3D rotation around the x axis of
+ /// `angle` (in radians).
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ #[inline]
+ pub fn from_rotation_x(angle: f32) -> Self {
+ let (sina, cosa) = angle.sin_cos();
+ Self::from_cols(
+ Vec4::X,
+ Vec4::new(0.0, cosa, sina, 0.0),
+ Vec4::new(0.0, -sina, cosa, 0.0),
+ Vec4::W,
+ )
+ }
+
+ /// Creates an affine transformation matrix containing a 3D rotation around the y axis of
+ /// `angle` (in radians).
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ #[inline]
+ pub fn from_rotation_y(angle: f32) -> Self {
+ let (sina, cosa) = angle.sin_cos();
+ Self::from_cols(
+ Vec4::new(cosa, 0.0, -sina, 0.0),
+ Vec4::Y,
+ Vec4::new(sina, 0.0, cosa, 0.0),
+ Vec4::W,
+ )
+ }
+
+ /// Creates an affine transformation matrix containing a 3D rotation around the z axis of
+ /// `angle` (in radians).
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ #[inline]
+ pub fn from_rotation_z(angle: f32) -> Self {
+ let (sina, cosa) = angle.sin_cos();
+ Self::from_cols(
+ Vec4::new(cosa, sina, 0.0, 0.0),
+ Vec4::new(-sina, cosa, 0.0, 0.0),
+ Vec4::Z,
+ Vec4::W,
+ )
+ }
+
+ /// Creates an affine transformation matrix containing the given 3D non-uniform `scale`.
+ ///
+ /// The resulting matrix can be used to transform 3D points and vectors. See
+ /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+ ///
+ /// # Panics
+ ///
+ /// Will panic if all elements of `scale` are zero when `glam_assert` is enabled.
+ #[inline]
+ pub fn from_scale(scale: Vec3) -> Self {
+ // Do not panic as long as any component is non-zero
+ glam_assert!(scale.cmpne(Vec3::ZERO).any());
+
+ Self::from_cols(
+ Vec4::new(scale.x, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, scale.y, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, scale.z, 0.0),
+ Vec4::W,
+ )
+ }
+
+ /// Creates a 4x4 matrix from the first 16 values in `slice`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `slice` is less than 16 elements long.
+ #[inline]
+ pub const fn from_cols_slice(slice: &[f32]) -> Self {
+ Self::new(
+ slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
+ slice[8], slice[9], slice[10], slice[11], slice[12], slice[13], slice[14], slice[15],
+ )
+ }
+
+ /// Writes the columns of `self` to the first 16 elements in `slice`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `slice` is less than 16 elements long.
+ #[inline]
+ pub fn write_cols_to_slice(self, slice: &mut [f32]) {
+ slice[0] = self.x_axis.x;
+ slice[1] = self.x_axis.y;
+ slice[2] = self.x_axis.z;
+ slice[3] = self.x_axis.w;
+ slice[4] = self.y_axis.x;
+ slice[5] = self.y_axis.y;
+ slice[6] = self.y_axis.z;
+ slice[7] = self.y_axis.w;
+ slice[8] = self.z_axis.x;
+ slice[9] = self.z_axis.y;
+ slice[10] = self.z_axis.z;
+ slice[11] = self.z_axis.w;
+ slice[12] = self.w_axis.x;
+ slice[13] = self.w_axis.y;
+ slice[14] = self.w_axis.z;
+ slice[15] = self.w_axis.w;
+ }
+
+ /// Returns the matrix column for the given `index`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `index` is greater than 3.
+ #[inline]
+ pub fn col(&self, index: usize) -> Vec4 {
+ match index {
+ 0 => self.x_axis,
+ 1 => self.y_axis,
+ 2 => self.z_axis,
+ 3 => self.w_axis,
+ _ => panic!("index out of bounds"),
+ }
+ }
+
+ /// Returns a mutable reference to the matrix column for the given `index`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `index` is greater than 3.
+ #[inline]
+ pub fn col_mut(&mut self, index: usize) -> &mut Vec4 {
+ match index {
+ 0 => &mut self.x_axis,
+ 1 => &mut self.y_axis,
+ 2 => &mut self.z_axis,
+ 3 => &mut self.w_axis,
+ _ => panic!("index out of bounds"),
+ }
+ }
+
+ /// Returns the matrix row for the given `index`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `index` is greater than 3.
+ #[inline]
+ pub fn row(&self, index: usize) -> Vec4 {
+ match index {
+ 0 => Vec4::new(self.x_axis.x, self.y_axis.x, self.z_axis.x, self.w_axis.x),
+ 1 => Vec4::new(self.x_axis.y, self.y_axis.y, self.z_axis.y, self.w_axis.y),
+ 2 => Vec4::new(self.x_axis.z, self.y_axis.z, self.z_axis.z, self.w_axis.z),
+ 3 => Vec4::new(self.x_axis.w, self.y_axis.w, self.z_axis.w, self.w_axis.w),
+ _ => panic!("index out of bounds"),
+ }
+ }
+
+ /// Returns `true` if, and only if, all elements are finite.
+ /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+ #[inline]
+ pub fn is_finite(&self) -> bool {
+ self.x_axis.is_finite()
+ && self.y_axis.is_finite()
+ && self.z_axis.is_finite()
+ && self.w_axis.is_finite()
+ }
+
+ /// Returns `true` if any elements are `NaN`.
+ #[inline]
+ pub fn is_nan(&self) -> bool {
+ self.x_axis.is_nan() || self.y_axis.is_nan() || self.z_axis.is_nan() || self.w_axis.is_nan()
+ }
+
+ /// Returns the transpose of `self`.
+ #[must_use]
+ #[inline]
+ pub fn transpose(&self) -> Self {
+ unsafe {
+ // Based on https://github.com/microsoft/DirectXMath `XMMatrixTranspose`
+ let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
+ let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
+ let tmp2 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b01_00_01_00);
+ let tmp3 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b11_10_11_10);
+
+ Self {
+ x_axis: Vec4(_mm_shuffle_ps(tmp0, tmp2, 0b10_00_10_00)),
+ y_axis: Vec4(_mm_shuffle_ps(tmp0, tmp2, 0b11_01_11_01)),
+ z_axis: Vec4(_mm_shuffle_ps(tmp1, tmp3, 0b10_00_10_00)),
+ w_axis: Vec4(_mm_shuffle_ps(tmp1, tmp3, 0b11_01_11_01)),
+ }
+ }
+ }
+
+ /// Returns the determinant of `self`.
+ pub fn determinant(&self) -> f32 {
+ unsafe {
+ // Based on https://github.com/g-truc/glm `glm_mat4_determinant_lowp`
+ let swp2a = _mm_shuffle_ps(self.z_axis.0, self.z_axis.0, 0b00_01_01_10);
+ let swp3a = _mm_shuffle_ps(self.w_axis.0, self.w_axis.0, 0b11_10_11_11);
+ let swp2b = _mm_shuffle_ps(self.z_axis.0, self.z_axis.0, 0b11_10_11_11);
+ let swp3b = _mm_shuffle_ps(self.w_axis.0, self.w_axis.0, 0b00_01_01_10);
+ let swp2c = _mm_shuffle_ps(self.z_axis.0, self.z_axis.0, 0b00_00_01_10);
+ let swp3c = _mm_shuffle_ps(self.w_axis.0, self.w_axis.0, 0b01_10_00_00);
+
+ let mula = _mm_mul_ps(swp2a, swp3a);
+ let mulb = _mm_mul_ps(swp2b, swp3b);
+ let mulc = _mm_mul_ps(swp2c, swp3c);
+ let sube = _mm_sub_ps(mula, mulb);
+ let subf = _mm_sub_ps(_mm_movehl_ps(mulc, mulc), mulc);
+
+ let subfaca = _mm_shuffle_ps(sube, sube, 0b10_01_00_00);
+ let swpfaca = _mm_shuffle_ps(self.y_axis.0, self.y_axis.0, 0b00_00_00_01);
+ let mulfaca = _mm_mul_ps(swpfaca, subfaca);
+
+ let subtmpb = _mm_shuffle_ps(sube, subf, 0b00_00_11_01);
+ let subfacb = _mm_shuffle_ps(subtmpb, subtmpb, 0b11_01_01_00);
+ let swpfacb = _mm_shuffle_ps(self.y_axis.0, self.y_axis.0, 0b01_01_10_10);
+ let mulfacb = _mm_mul_ps(swpfacb, subfacb);
+
+ let subres = _mm_sub_ps(mulfaca, mulfacb);
+ let subtmpc = _mm_shuffle_ps(sube, subf, 0b01_00_10_10);
+ let subfacc = _mm_shuffle_ps(subtmpc, subtmpc, 0b11_11_10_00);
+ let swpfacc = _mm_shuffle_ps(self.y_axis.0, self.y_axis.0, 0b10_11_11_11);
+ let mulfacc = _mm_mul_ps(swpfacc, subfacc);
+
+ let addres = _mm_add_ps(subres, mulfacc);
+ let detcof = _mm_mul_ps(addres, _mm_setr_ps(1.0, -1.0, 1.0, -1.0));
+
+ dot4(self.x_axis.0, detcof)
+ }
+ }
+
+ /// Returns the inverse of `self`.
+ ///
+ /// If the matrix is not invertible the returned matrix will be invalid.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if the determinant of `self` is zero when `glam_assert` is enabled.
+ #[must_use]
+ pub fn inverse(&self) -> Self {
+ unsafe {
+ // Based on https://github.com/g-truc/glm `glm_mat4_inverse`
+ let fac0 = {
+ let swp0a = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b11_11_11_11);
+ let swp0b = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b10_10_10_10);
+
+ let swp00 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b10_10_10_10);
+ let swp01 = _mm_shuffle_ps(swp0a, swp0a, 0b10_00_00_00);
+ let swp02 = _mm_shuffle_ps(swp0b, swp0b, 0b10_00_00_00);
+ let swp03 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b11_11_11_11);
+
+ let mul00 = _mm_mul_ps(swp00, swp01);
+ let mul01 = _mm_mul_ps(swp02, swp03);
+ _mm_sub_ps(mul00, mul01)
+ };
+ let fac1 = {
+ let swp0a = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b11_11_11_11);
+ let swp0b = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b01_01_01_01);
+
+ let swp00 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b01_01_01_01);
+ let swp01 = _mm_shuffle_ps(swp0a, swp0a, 0b10_00_00_00);
+ let swp02 = _mm_shuffle_ps(swp0b, swp0b, 0b10_00_00_00);
+ let swp03 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b11_11_11_11);
+
+ let mul00 = _mm_mul_ps(swp00, swp01);
+ let mul01 = _mm_mul_ps(swp02, swp03);
+ _mm_sub_ps(mul00, mul01)
+ };
+ let fac2 = {
+ let swp0a = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b10_10_10_10);
+ let swp0b = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b01_01_01_01);
+
+ let swp00 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b01_01_01_01);
+ let swp01 = _mm_shuffle_ps(swp0a, swp0a, 0b10_00_00_00);
+ let swp02 = _mm_shuffle_ps(swp0b, swp0b, 0b10_00_00_00);
+ let swp03 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b10_10_10_10);
+
+ let mul00 = _mm_mul_ps(swp00, swp01);
+ let mul01 = _mm_mul_ps(swp02, swp03);
+ _mm_sub_ps(mul00, mul01)
+ };
+ let fac3 = {
+ let swp0a = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b11_11_11_11);
+ let swp0b = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b00_00_00_00);
+
+ let swp00 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b00_00_00_00);
+ let swp01 = _mm_shuffle_ps(swp0a, swp0a, 0b10_00_00_00);
+ let swp02 = _mm_shuffle_ps(swp0b, swp0b, 0b10_00_00_00);
+ let swp03 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b11_11_11_11);
+
+ let mul00 = _mm_mul_ps(swp00, swp01);
+ let mul01 = _mm_mul_ps(swp02, swp03);
+ _mm_sub_ps(mul00, mul01)
+ };
+ let fac4 = {
+ let swp0a = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b10_10_10_10);
+ let swp0b = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b00_00_00_00);
+
+ let swp00 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b00_00_00_00);
+ let swp01 = _mm_shuffle_ps(swp0a, swp0a, 0b10_00_00_00);
+ let swp02 = _mm_shuffle_ps(swp0b, swp0b, 0b10_00_00_00);
+ let swp03 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b10_10_10_10);
+
+ let mul00 = _mm_mul_ps(swp00, swp01);
+ let mul01 = _mm_mul_ps(swp02, swp03);
+ _mm_sub_ps(mul00, mul01)
+ };
+ let fac5 = {
+ let swp0a = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b01_01_01_01);
+ let swp0b = _mm_shuffle_ps(self.w_axis.0, self.z_axis.0, 0b00_00_00_00);
+
+ let swp00 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b00_00_00_00);
+ let swp01 = _mm_shuffle_ps(swp0a, swp0a, 0b10_00_00_00);
+ let swp02 = _mm_shuffle_ps(swp0b, swp0b, 0b10_00_00_00);
+ let swp03 = _mm_shuffle_ps(self.z_axis.0, self.y_axis.0, 0b01_01_01_01);
+
+ let mul00 = _mm_mul_ps(swp00, swp01);
+ let mul01 = _mm_mul_ps(swp02, swp03);
+ _mm_sub_ps(mul00, mul01)
+ };
+ let sign_a = _mm_set_ps(1.0, -1.0, 1.0, -1.0);
+ let sign_b = _mm_set_ps(-1.0, 1.0, -1.0, 1.0);
+
+ let temp0 = _mm_shuffle_ps(self.y_axis.0, self.x_axis.0, 0b00_00_00_00);
+ let vec0 = _mm_shuffle_ps(temp0, temp0, 0b10_10_10_00);
+
+ let temp1 = _mm_shuffle_ps(self.y_axis.0, self.x_axis.0, 0b01_01_01_01);
+ let vec1 = _mm_shuffle_ps(temp1, temp1, 0b10_10_10_00);
+
+ let temp2 = _mm_shuffle_ps(self.y_axis.0, self.x_axis.0, 0b10_10_10_10);
+ let vec2 = _mm_shuffle_ps(temp2, temp2, 0b10_10_10_00);
+
+ let temp3 = _mm_shuffle_ps(self.y_axis.0, self.x_axis.0, 0b11_11_11_11);
+ let vec3 = _mm_shuffle_ps(temp3, temp3, 0b10_10_10_00);
+
+ let mul00 = _mm_mul_ps(vec1, fac0);
+ let mul01 = _mm_mul_ps(vec2, fac1);
+ let mul02 = _mm_mul_ps(vec3, fac2);
+ let sub00 = _mm_sub_ps(mul00, mul01);
+ let add00 = _mm_add_ps(sub00, mul02);
+ let inv0 = _mm_mul_ps(sign_b, add00);
+
+ let mul03 = _mm_mul_ps(vec0, fac0);
+ let mul04 = _mm_mul_ps(vec2, fac3);
+ let mul05 = _mm_mul_ps(vec3, fac4);
+ let sub01 = _mm_sub_ps(mul03, mul04);
+ let add01 = _mm_add_ps(sub01, mul05);
+ let inv1 = _mm_mul_ps(sign_a, add01);
+
+ let mul06 = _mm_mul_ps(vec0, fac1);
+ let mul07 = _mm_mul_ps(vec1, fac3);
+ let mul08 = _mm_mul_ps(vec3, fac5);
+ let sub02 = _mm_sub_ps(mul06, mul07);
+ let add02 = _mm_add_ps(sub02, mul08);
+ let inv2 = _mm_mul_ps(sign_b, add02);
+
+ let mul09 = _mm_mul_ps(vec0, fac2);
+ let mul10 = _mm_mul_ps(vec1, fac4);
+ let mul11 = _mm_mul_ps(vec2, fac5);
+ let sub03 = _mm_sub_ps(mul09, mul10);
+ let add03 = _mm_add_ps(sub03, mul11);
+ let inv3 = _mm_mul_ps(sign_a, add03);
+
+ let row0 = _mm_shuffle_ps(inv0, inv1, 0b00_00_00_00);
+ let row1 = _mm_shuffle_ps(inv2, inv3, 0b00_00_00_00);
+ let row2 = _mm_shuffle_ps(row0, row1, 0b10_00_10_00);
+
+ let dot0 = dot4(self.x_axis.0, row2);
+ glam_assert!(dot0 != 0.0);
+
+ let rcp0 = _mm_set1_ps(dot0.recip());
+
+ Self {
+ x_axis: Vec4(_mm_mul_ps(inv0, rcp0)),
+ y_axis: Vec4(_mm_mul_ps(inv1, rcp0)),
+ z_axis: Vec4(_mm_mul_ps(inv2, rcp0)),
+ w_axis: Vec4(_mm_mul_ps(inv3, rcp0)),
+ }
+ }
+ }
+
+ /// Creates a left-handed view matrix using a camera position, an up direction, and a facing
+ /// direction.
+ ///
+ /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
+ #[inline]
+ pub fn look_to_lh(eye: Vec3, dir: Vec3, up: Vec3) -> Self {
+ Self::look_to_rh(eye, -dir, up)
+ }
+
+ /// Creates a right-handed view matrix using a camera position, an up direction, and a facing
+ /// direction.
+ ///
+ /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
+ #[inline]
+ pub fn look_to_rh(eye: Vec3, dir: Vec3, up: Vec3) -> Self {
+ let f = dir.normalize();
+ let s = f.cross(up).normalize();
+ let u = s.cross(f);
+
+ Self::from_cols(
+ Vec4::new(s.x, u.x, -f.x, 0.0),
+ Vec4::new(s.y, u.y, -f.y, 0.0),
+ Vec4::new(s.z, u.z, -f.z, 0.0),
+ Vec4::new(-eye.dot(s), -eye.dot(u), eye.dot(f), 1.0),
+ )
+ }
+
+ /// Creates a left-handed view matrix using a camera position, an up direction, and a focal
+ /// point.
+ /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `up` is not normalized when `glam_assert` is enabled.
+ #[inline]
+ pub fn look_at_lh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
+ glam_assert!(up.is_normalized());
+ Self::look_to_lh(eye, center.sub(eye), up)
+ }
+
+ /// Creates a right-handed view matrix using a camera position, an up direction, and a focal
+ /// point.
+ /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `up` is not normalized when `glam_assert` is enabled.
+ #[inline]
+ pub fn look_at_rh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
+ glam_assert!(up.is_normalized());
+ Self::look_to_rh(eye, center.sub(eye), up)
+ }
+
+ /// Creates a right-handed perspective projection matrix with [-1,1] depth range.
+ /// This is the same as the OpenGL `gluPerspective` function.
+ /// See <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml>
+ #[inline]
+ pub fn perspective_rh_gl(
+ fov_y_radians: f32,
+ aspect_ratio: f32,
+ z_near: f32,
+ z_far: f32,
+ ) -> Self {
+ let inv_length = 1.0 / (z_near - z_far);
+ let f = 1.0 / (0.5 * fov_y_radians).tan();
+ let a = f / aspect_ratio;
+ let b = (z_near + z_far) * inv_length;
+ let c = (2.0 * z_near * z_far) * inv_length;
+ Self::from_cols(
+ Vec4::new(a, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, f, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, b, -1.0),
+ Vec4::new(0.0, 0.0, c, 0.0),
+ )
+ }
+
+ /// Creates a left-handed perspective projection matrix with `[0,1]` depth range.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
+ /// enabled.
+ #[inline]
+ pub fn perspective_lh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> Self {
+ glam_assert!(z_near > 0.0 && z_far > 0.0);
+ let (sin_fov, cos_fov) = (0.5 * fov_y_radians).sin_cos();
+ let h = cos_fov / sin_fov;
+ let w = h / aspect_ratio;
+ let r = z_far / (z_far - z_near);
+ Self::from_cols(
+ Vec4::new(w, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, h, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, r, 1.0),
+ Vec4::new(0.0, 0.0, -r * z_near, 0.0),
+ )
+ }
+
+ /// Creates a right-handed perspective projection matrix with `[0,1]` depth range.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
+ /// enabled.
+ #[inline]
+ pub fn perspective_rh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> Self {
+ glam_assert!(z_near > 0.0 && z_far > 0.0);
+ let (sin_fov, cos_fov) = (0.5 * fov_y_radians).sin_cos();
+ let h = cos_fov / sin_fov;
+ let w = h / aspect_ratio;
+ let r = z_far / (z_near - z_far);
+ Self::from_cols(
+ Vec4::new(w, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, h, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, r, -1.0),
+ Vec4::new(0.0, 0.0, r * z_near, 0.0),
+ )
+ }
+
+ /// Creates an infinite left-handed perspective projection matrix with `[0,1]` depth range.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled.
+ #[inline]
+ pub fn perspective_infinite_lh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32) -> Self {
+ glam_assert!(z_near > 0.0);
+ let (sin_fov, cos_fov) = (0.5 * fov_y_radians).sin_cos();
+ let h = cos_fov / sin_fov;
+ let w = h / aspect_ratio;
+ Self::from_cols(
+ Vec4::new(w, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, h, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, 1.0, 1.0),
+ Vec4::new(0.0, 0.0, -z_near, 0.0),
+ )
+ }
+
+ /// Creates an infinite left-handed perspective projection matrix with `[0,1]` depth range.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled.
+ #[inline]
+ pub fn perspective_infinite_reverse_lh(
+ fov_y_radians: f32,
+ aspect_ratio: f32,
+ z_near: f32,
+ ) -> Self {
+ glam_assert!(z_near > 0.0);
+ let (sin_fov, cos_fov) = (0.5 * fov_y_radians).sin_cos();
+ let h = cos_fov / sin_fov;
+ let w = h / aspect_ratio;
+ Self::from_cols(
+ Vec4::new(w, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, h, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, 0.0, 1.0),
+ Vec4::new(0.0, 0.0, z_near, 0.0),
+ )
+ }
+
+ /// Creates an infinite right-handed perspective projection matrix with
+ /// `[0,1]` depth range.
+ #[inline]
+ pub fn perspective_infinite_rh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32) -> Self {
+ glam_assert!(z_near > 0.0);
+ let f = 1.0 / (0.5 * fov_y_radians).tan();
+ Self::from_cols(
+ Vec4::new(f / aspect_ratio, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, f, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, -1.0, -1.0),
+ Vec4::new(0.0, 0.0, -z_near, 0.0),
+ )
+ }
+
+ /// Creates an infinite reverse right-handed perspective projection matrix
+ /// with `[0,1]` depth range.
+ #[inline]
+ pub fn perspective_infinite_reverse_rh(
+ fov_y_radians: f32,
+ aspect_ratio: f32,
+ z_near: f32,
+ ) -> Self {
+ glam_assert!(z_near > 0.0);
+ let f = 1.0 / (0.5 * fov_y_radians).tan();
+ Self::from_cols(
+ Vec4::new(f / aspect_ratio, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, f, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, 0.0, -1.0),
+ Vec4::new(0.0, 0.0, z_near, 0.0),
+ )
+ }
+
+ /// Creates a right-handed orthographic projection matrix with `[-1,1]` depth
+ /// range. This is the same as the OpenGL `glOrtho` function in OpenGL.
+ /// See
+ /// <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml>
+ #[inline]
+ pub fn orthographic_rh_gl(
+ left: f32,
+ right: f32,
+ bottom: f32,
+ top: f32,
+ near: f32,
+ far: f32,
+ ) -> Self {
+ let a = 2.0 / (right - left);
+ let b = 2.0 / (top - bottom);
+ let c = -2.0 / (far - near);
+ let tx = -(right + left) / (right - left);
+ let ty = -(top + bottom) / (top - bottom);
+ let tz = -(far + near) / (far - near);
+
+ Self::from_cols(
+ Vec4::new(a, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, b, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, c, 0.0),
+ Vec4::new(tx, ty, tz, 1.0),
+ )
+ }
+
+ /// Creates a left-handed orthographic projection matrix with `[0,1]` depth range.
+ #[inline]
+ pub fn orthographic_lh(
+ left: f32,
+ right: f32,
+ bottom: f32,
+ top: f32,
+ near: f32,
+ far: f32,
+ ) -> Self {
+ let rcp_width = 1.0 / (right - left);
+ let rcp_height = 1.0 / (top - bottom);
+ let r = 1.0 / (far - near);
+ Self::from_cols(
+ Vec4::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, r, 0.0),
+ Vec4::new(
+ -(left + right) * rcp_width,
+ -(top + bottom) * rcp_height,
+ -r * near,
+ 1.0,
+ ),
+ )
+ }
+
+ /// Creates a right-handed orthographic projection matrix with `[0,1]` depth range.
+ #[inline]
+ pub fn orthographic_rh(
+ left: f32,
+ right: f32,
+ bottom: f32,
+ top: f32,
+ near: f32,
+ far: f32,
+ ) -> Self {
+ let rcp_width = 1.0 / (right - left);
+ let rcp_height = 1.0 / (top - bottom);
+ let r = 1.0 / (near - far);
+ Self::from_cols(
+ Vec4::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
+ Vec4::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
+ Vec4::new(0.0, 0.0, r, 0.0),
+ Vec4::new(
+ -(left + right) * rcp_width,
+ -(top + bottom) * rcp_height,
+ r * near,
+ 1.0,
+ ),
+ )
+ }
+
+ /// Transforms the given 3D vector as a point, applying perspective correction.
+ ///
+ /// This is the equivalent of multiplying the 3D vector as a 4D vector where `w` is `1.0`.
+ /// The perspective divide is performed meaning the resulting 3D vector is divided by `w`.
+ ///
+ /// This method assumes that `self` contains a projective transform.
+ #[inline]
+ pub fn project_point3(&self, rhs: Vec3) -> Vec3 {
+ let mut res = self.x_axis.mul(rhs.x);
+ res = self.y_axis.mul(rhs.y).add(res);
+ res = self.z_axis.mul(rhs.z).add(res);
+ res = self.w_axis.add(res);
+ res = res.mul(res.wwww().recip());
+ res.xyz()
+ }
+
+ /// Transforms the given 3D vector as a point.
+ ///
+ /// This is the equivalent of multiplying the 3D vector as a 4D vector where `w` is
+ /// `1.0`.
+ ///
+ /// This method assumes that `self` contains a valid affine transform. It does not perform
+ /// a persective divide, if `self` contains a perspective transform, or if you are unsure,
+ /// the [`Self::project_point3()`] method should be used instead.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if the 3rd row of `self` is not `(0, 0, 0, 1)` when `glam_assert` is enabled.
+ #[inline]
+ pub fn transform_point3(&self, rhs: Vec3) -> Vec3 {
+ glam_assert!(self.row(3).abs_diff_eq(Vec4::W, 1e-6));
+ let mut res = self.x_axis.mul(rhs.x);
+ res = self.y_axis.mul(rhs.y).add(res);
+ res = self.z_axis.mul(rhs.z).add(res);
+ res = self.w_axis.add(res);
+ res.xyz()
+ }
+
+ /// Transforms the give 3D vector as a direction.
+ ///
+ /// This is the equivalent of multiplying the 3D vector as a 4D vector where `w` is
+ /// `0.0`.
+ ///
+ /// This method assumes that `self` contains a valid affine transform.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if the 3rd row of `self` is not `(0, 0, 0, 1)` when `glam_assert` is enabled.
+ #[inline]
+ pub fn transform_vector3(&self, rhs: Vec3) -> Vec3 {
+ glam_assert!(self.row(3).abs_diff_eq(Vec4::W, 1e-6));
+ let mut res = self.x_axis.mul(rhs.x);
+ res = self.y_axis.mul(rhs.y).add(res);
+ res = self.z_axis.mul(rhs.z).add(res);
+ res.xyz()
+ }
+
+ /// Transforms the given `Vec3A` as 3D point.
+ ///
+ /// This is the equivalent of multiplying the `Vec3A` as a 4D vector where `w` is `1.0`.
+ #[inline]
+ pub fn transform_point3a(&self, rhs: Vec3A) -> Vec3A {
+ glam_assert!(self.row(3).abs_diff_eq(Vec4::W, 1e-6));
+ let mut res = self.x_axis.mul(rhs.xxxx());
+ res = self.y_axis.mul(rhs.yyyy()).add(res);
+ res = self.z_axis.mul(rhs.zzzz()).add(res);
+ res = self.w_axis.add(res);
+ res.into()
+ }
+
+ /// Transforms the give `Vec3A` as 3D vector.
+ ///
+ /// This is the equivalent of multiplying the `Vec3A` as a 4D vector where `w` is `0.0`.
+ #[inline]
+ pub fn transform_vector3a(&self, rhs: Vec3A) -> Vec3A {
+ glam_assert!(self.row(3).abs_diff_eq(Vec4::W, 1e-6));
+ let mut res = self.x_axis.mul(rhs.xxxx());
+ res = self.y_axis.mul(rhs.yyyy()).add(res);
+ res = self.z_axis.mul(rhs.zzzz()).add(res);
+ res.into()
+ }
+
+ /// Transforms a 4D vector.
+ #[inline]
+ pub fn mul_vec4(&self, rhs: Vec4) -> Vec4 {
+ let mut res = self.x_axis.mul(rhs.xxxx());
+ res = res.add(self.y_axis.mul(rhs.yyyy()));
+ res = res.add(self.z_axis.mul(rhs.zzzz()));
+ res = res.add(self.w_axis.mul(rhs.wwww()));
+ res
+ }
+
+ /// Multiplies two 4x4 matrices.
+ #[inline]
+ pub fn mul_mat4(&self, rhs: &Self) -> Self {
+ Self::from_cols(
+ self.mul(rhs.x_axis),
+ self.mul(rhs.y_axis),
+ self.mul(rhs.z_axis),
+ self.mul(rhs.w_axis),
+ )
+ }
+
+ /// Adds two 4x4 matrices.
+ #[inline]
+ pub fn add_mat4(&self, rhs: &Self) -> Self {
+ Self::from_cols(
+ self.x_axis.add(rhs.x_axis),
+ self.y_axis.add(rhs.y_axis),
+ self.z_axis.add(rhs.z_axis),
+ self.w_axis.add(rhs.w_axis),
+ )
+ }
+
+ /// Subtracts two 4x4 matrices.
+ #[inline]
+ pub fn sub_mat4(&self, rhs: &Self) -> Self {
+ Self::from_cols(
+ self.x_axis.sub(rhs.x_axis),
+ self.y_axis.sub(rhs.y_axis),
+ self.z_axis.sub(rhs.z_axis),
+ self.w_axis.sub(rhs.w_axis),
+ )
+ }
+
+ /// Multiplies a 4x4 matrix by a scalar.
+ #[inline]
+ pub fn mul_scalar(&self, rhs: f32) -> Self {
+ Self::from_cols(
+ self.x_axis.mul(rhs),
+ self.y_axis.mul(rhs),
+ self.z_axis.mul(rhs),
+ self.w_axis.mul(rhs),
+ )
+ }
+
+ /// Returns true if the absolute difference of all elements between `self` and `rhs`
+ /// is less than or equal to `max_abs_diff`.
+ ///
+ /// This can be used to compare if two matrices contain similar elements. It works best
+ /// when comparing with a known value. The `max_abs_diff` that should be used used
+ /// depends on the values being compared against.
+ ///
+ /// For more see
+ /// [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
+ #[inline]
+ pub fn abs_diff_eq(&self, rhs: Self, max_abs_diff: f32) -> bool {
+ self.x_axis.abs_diff_eq(rhs.x_axis, max_abs_diff)
+ && self.y_axis.abs_diff_eq(rhs.y_axis, max_abs_diff)
+ && self.z_axis.abs_diff_eq(rhs.z_axis, max_abs_diff)
+ && self.w_axis.abs_diff_eq(rhs.w_axis, max_abs_diff)
+ }
+
+ #[inline]
+ pub fn as_dmat4(&self) -> DMat4 {
+ DMat4::from_cols(
+ self.x_axis.as_dvec4(),
+ self.y_axis.as_dvec4(),
+ self.z_axis.as_dvec4(),
+ self.w_axis.as_dvec4(),
+ )
+ }
+}
+
+impl Default for Mat4 {
+ #[inline]
+ fn default() -> Self {
+ Self::IDENTITY
+ }
+}
+
+impl Add<Mat4> for Mat4 {
+ type Output = Self;
+ #[inline]
+ fn add(self, rhs: Self) -> Self::Output {
+ self.add_mat4(&rhs)
+ }
+}
+
+impl AddAssign<Mat4> for Mat4 {
+ #[inline]
+ fn add_assign(&mut self, rhs: Self) {
+ *self = self.add_mat4(&rhs);
+ }
+}
+
+impl Sub<Mat4> for Mat4 {
+ type Output = Self;
+ #[inline]
+ fn sub(self, rhs: Self) -> Self::Output {
+ self.sub_mat4(&rhs)
+ }
+}
+
+impl SubAssign<Mat4> for Mat4 {
+ #[inline]
+ fn sub_assign(&mut self, rhs: Self) {
+ *self = self.sub_mat4(&rhs);
+ }
+}
+
+impl Neg for Mat4 {
+ type Output = Self;
+ #[inline]
+ fn neg(self) -> Self::Output {
+ Self::from_cols(
+ self.x_axis.neg(),
+ self.y_axis.neg(),
+ self.z_axis.neg(),
+ self.w_axis.neg(),
+ )
+ }
+}
+
+impl Mul<Mat4> for Mat4 {
+ type Output = Self;
+ #[inline]
+ fn mul(self, rhs: Self) -> Self::Output {
+ self.mul_mat4(&rhs)
+ }
+}
+
+impl MulAssign<Mat4> for Mat4 {
+ #[inline]
+ fn mul_assign(&mut self, rhs: Self) {
+ *self = self.mul_mat4(&rhs);
+ }
+}
+
+impl Mul<Vec4> for Mat4 {
+ type Output = Vec4;
+ #[inline]
+ fn mul(self, rhs: Vec4) -> Self::Output {
+ self.mul_vec4(rhs)
+ }
+}
+
+impl Mul<Mat4> for f32 {
+ type Output = Mat4;
+ #[inline]
+ fn mul(self, rhs: Mat4) -> Self::Output {
+ rhs.mul_scalar(self)
+ }
+}
+
+impl Mul<f32> for Mat4 {
+ type Output = Self;
+ #[inline]
+ fn mul(self, rhs: f32) -> Self::Output {
+ self.mul_scalar(rhs)
+ }
+}
+
+impl MulAssign<f32> for Mat4 {
+ #[inline]
+ fn mul_assign(&mut self, rhs: f32) {
+ *self = self.mul_scalar(rhs);
+ }
+}
+
+impl Sum<Self> for Mat4 {
+ fn sum<I>(iter: I) -> Self
+ where
+ I: Iterator<Item = Self>,
+ {
+ iter.fold(Self::ZERO, Self::add)
+ }
+}
+
+impl<'a> Sum<&'a Self> for Mat4 {
+ fn sum<I>(iter: I) -> Self
+ where
+ I: Iterator<Item = &'a Self>,
+ {
+ iter.fold(Self::ZERO, |a, &b| Self::add(a, b))
+ }
+}
+
+impl Product for Mat4 {
+ fn product<I>(iter: I) -> Self
+ where
+ I: Iterator<Item = Self>,
+ {
+ iter.fold(Self::IDENTITY, Self::mul)
+ }
+}
+
+impl<'a> Product<&'a Self> for Mat4 {
+ fn product<I>(iter: I) -> Self
+ where
+ I: Iterator<Item = &'a Self>,
+ {
+ iter.fold(Self::IDENTITY, |a, &b| Self::mul(a, b))
+ }
+}
+
+impl PartialEq for Mat4 {
+ #[inline]
+ fn eq(&self, rhs: &Self) -> bool {
+ self.x_axis.eq(&rhs.x_axis)
+ && self.y_axis.eq(&rhs.y_axis)
+ && self.z_axis.eq(&rhs.z_axis)
+ && self.w_axis.eq(&rhs.w_axis)
+ }
+}
+
+#[cfg(not(target_arch = "spirv"))]
+impl AsRef<[f32; 16]> for Mat4 {
+ #[inline]
+ fn as_ref(&self) -> &[f32; 16] {
+ unsafe { &*(self as *const Self as *const [f32; 16]) }
+ }
+}
+
+#[cfg(not(target_arch = "spirv"))]
+impl AsMut<[f32; 16]> for Mat4 {
+ #[inline]
+ fn as_mut(&mut self) -> &mut [f32; 16] {
+ unsafe { &mut *(self as *mut Self as *mut [f32; 16]) }
+ }
+}
+
+#[cfg(not(target_arch = "spirv"))]
+impl fmt::Debug for Mat4 {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt.debug_struct(stringify!(Mat4))
+ .field("x_axis", &self.x_axis)
+ .field("y_axis", &self.y_axis)
+ .field("z_axis", &self.z_axis)
+ .field("w_axis", &self.w_axis)
+ .finish()
+ }
+}
+
+#[cfg(not(target_arch = "spirv"))]
+impl fmt::Display for Mat4 {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "[{}, {}, {}, {}]",
+ self.x_axis, self.y_axis, self.z_axis, self.w_axis
+ )
+ }
+}