{% import "macros.rs.tera" as macros %} {% import "coresimd.rs.tera" as coresimd %} {% import "neon.rs.tera" as neon %} {% import "sse2.rs.tera" as sse2 %} {% import "wasm32.rs.tera" as wasm32 %} // Generated from {{template_path}} template. Edit the template, not the generated file. {% if scalar_t == "f32" %} {% set vecn_t = "Vec" ~ dim %} {% if dim == 3 and is_align %} {% set self_t = "Mat3A" %} {% set col_t = "Vec3A" %} {% else %} {% set self_t = "Mat" ~ dim %} {% set col_t = vecn_t %} {% endif %} {% set quat_t = "Quat" %} {% set affine2_t = "Affine2" %} {% set affine3_t = "Affine3A" %} {% set vec2_t = "Vec2" %} {% set vec3_t = "Vec3" %} {% set vec4_t = "Vec4" %} {% set mat2_t = "Mat2" %} {% set mat3_t = "Mat3" %} {% set mat4_t = "Mat4" %} {% elif scalar_t == "f64" %} {% set vecn_t = "DVec" ~ dim %} {% set self_t = "DMat" ~ dim %} {% set col_t = vecn_t %} {% set quat_t = "DQuat" %} {% set affine2_t = "DAffine2" %} {% set affine3_t = "DAffine3" %} {% set vec2_t = "DVec2" %} {% set vec3_t = "DVec3" %} {% set vec4_t = "DVec4" %} {% set mat2_t = "DMat2" %} {% set mat3_t = "DMat3" %} {% set mat4_t = "DMat4" %} {% endif %} {% if self_t == "Mat2" %} {% if not is_scalar %} {% set is_simd = true %} {% if is_sse2 %} {% set simd_t = "__m128" %} {% elif is_wasm32 %} {% set simd_t = "v128" %} {% elif is_coresimd %} {% set simd_t = "f32x4" %} {% elif is_neon %} {% set simd_t = "float32x4_t" %} {% endif %} {% endif %} {% endif %} {% set size = dim * dim %} {% set nxn = dim ~ "x" ~ dim %} {% set components = ["x", "y", "z", "w"] | slice(end = dim) %} {% set axes = ["x_axis", "y_axis", "z_axis", "w_axis"] | slice(end = dim) %} {% set dimension_in_full = ["zero", "one", "two", "three", "four"] | nth(n = dim) %} use crate::{ {% if scalar_t == "f32" %} DMat{{ dim }}, {% elif scalar_t == "f64" %} Mat{{ dim }}, {% endif %} {% if dim == 2 %} {{ mat3_t }}, {{ vec2_t }}, {% if scalar_t == "f32" %} Mat3A, {% endif %} {% elif dim == 3 %} EulerRot, euler::{FromEuler, ToEuler}, {{ mat2_t }}, {{ mat4_t }}, {{ quat_t }}, {{ vec2_t }}, {{ col_t }}, {% if is_align %} {{ mat3_t }}, {{ vec3_t }}, {% elif scalar_t == "f32" %} Mat3A, Vec3A, {% endif %} {% elif dim == 4 %} EulerRot, euler::{FromEuler, ToEuler}, {{ mat3_t }}, {{ quat_t }}, {{ vec3_t }}, {{ col_t }}, {% if scalar_t == "f32" %} Mat3A, Vec3A, {% endif %} {% endif %} {% if self_t == "Mat4" %} {% if is_sse2 %} sse2::*, {% elif is_wasm32 %} wasm32::*, {% elif is_coresimd %} coresimd::*, {% elif is_neon %} neon::*, {% endif %} {% endif %} {{ scalar_t }}::math, swizzles::*, }; use core::fmt; use core::iter::{Product, Sum}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; {% if is_sse2 %} #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; {% elif is_wasm32 %} use core::arch::wasm32::*; {% elif is_coresimd %} use core::simd::*; {% elif is_neon %} use core::arch::aarch64::*; {% endif %} {% if not is_coresimd %} #[cfg(feature = "zerocopy")] use zerocopy_derive::*; {% endif %} {% set is_sse2_or_neon = is_sse2 or is_neon %} {% if self_t == "Mat2" and is_sse2_or_neon %} #[repr(C)] union UnionCast { a: [f32; 4], v: {{ self_t }} } {% endif %} /// Creates a {{ nxn }} matrix from {{ dimension_in_full }} column vectors. #[inline(always)] #[must_use] pub const fn {{ self_t | lower }}( {% for axis in axes %} {{ axis }}: {{ col_t }}, {% endfor %} ) -> {{ self_t }} { {{ self_t }}::from_cols({{ axes | join(sep=",") }}) } /// A {{ nxn }} column major matrix. {%- if dim == 3 %} /// /// This 3x3 matrix type features convenience methods for creating and using linear and /// affine transformations. If you are primarily dealing with 2D affine transformations the /// [`{{ affine2_t }}`](crate::{{ affine2_t }}) type is much faster and more space efficient than /// using a 3x3 matrix. /// /// Linear transformations including 3D rotation and scale can be created using methods /// such as [`Self::from_diagonal()`], [`Self::from_quat()`], [`Self::from_axis_angle()`], /// [`Self::from_rotation_x()`], [`Self::from_rotation_y()`], or /// [`Self::from_rotation_z()`]. /// /// The resulting matrices can be use to transform 3D vectors using regular vector /// multiplication. /// /// Affine transformations including 2D translation, rotation and scale can be created /// using methods such as [`Self::from_translation()`], [`Self::from_angle()`], /// [`Self::from_scale()`] and [`Self::from_scale_angle_translation()`]. /// /// The [`Self::transform_point2()`] and [`Self::transform_vector2()`] convenience methods /// are provided for performing affine transforms on 2D vectors and points. These multiply /// 2D inputs as 3D vectors with an implicit `z` value of `1` for points and `0` for /// vectors respectively. These methods assume that `Self` contains a valid affine /// transform. {%- elif dim == 4 %} /// /// 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 [`{{affine3_t}}`](crate::{{ affine3_t }}) 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()`]. /// /// Orthographic 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. {%- endif %} {%- if is_simd %} /// /// SIMD vector types are used for storage on supported platforms. /// /// This type is 16 byte aligned. {%- endif %} #[derive(Clone, Copy)] {%- if self_t == "Mat3A" and is_scalar %} #[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))] {%- if not is_coresimd %} #[cfg_attr(feature = "zerocopy", derive(FromBytes, Immutable, KnownLayout))] {%- endif %} {%- else %} #[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))] {%- if not is_coresimd %} #[cfg_attr(feature = "zerocopy", derive(FromBytes, Immutable, IntoBytes, KnownLayout))] {%- endif %} {%- endif %} {%- if self_t == "Mat4" and is_scalar %} #[cfg_attr(any(not(feature = "scalar-math"), feature = "cuda"), repr(align(16)))] {%- elif self_t == "Mat2" and is_scalar %} #[cfg_attr(not(feature = "scalar-math"), repr(align(16)))] #[cfg_attr(feature = "cuda", repr(align(8)))] {%- elif self_t == "DMat2" or self_t == "DMat4" %} #[cfg_attr(feature = "cuda", repr(align(16)))] {%- endif %} {%- if self_t == "Mat2" and not is_scalar %} #[repr(transparent)] pub struct {{ self_t }}(pub(crate) {{ simd_t }}); {%- else %} #[repr(C)] pub struct {{ self_t }} { {% for axis in axes %} pub {{ axis }}: {{ col_t }}, {%- endfor %} } {% endif %} impl {{ self_t }} { /// A {{ nxn }} matrix with all elements set to `0.0`. pub const ZERO: Self = Self::from_cols( {% for axis in axes %} {{ col_t }}::ZERO, {%- endfor %} ); /// A {{ nxn }} identity matrix, where all diagonal elements are `1`, and all off-diagonal elements are `0`. pub const IDENTITY: Self = Self::from_cols( {% for i in range(end = dim) %} {{ col_t }}::{{ components[i] | upper }}, {%- endfor %} ); /// All NAN:s. pub const NAN: Self = Self::from_cols( {% for axis in axes %} {{ col_t }}::NAN, {%- endfor %} ); #[allow(clippy::too_many_arguments)] #[inline(always)] #[must_use] const fn new( {% for i in range(end = dim) %} {%- for j in range(end = dim) %} m{{ i }}{{ j }}: {{ scalar_t }}, {%- endfor %} {%- endfor %} ) -> Self { {% if self_t == "Mat2" and is_sse2 %} unsafe { UnionCast { a: [m00, m01, m10, m11] }.v } {% elif self_t == "Mat2" and is_wasm32 %} Self(f32x4(m00, m01, m10, m11)) {% elif self_t == "Mat2" and is_coresimd %} Self(f32x4::from_array([m00, m01, m10, m11])) {% elif self_t == "Mat2" and is_neon %} unsafe { UnionCast { a: [m00, m01, m10, m11] }.v } {% else %} Self { {% for i in range(end = dim) %} {{ axes[i] }}: {{ col_t}}::new( {% for j in range(end = dim) %} m{{ i }}{{ j }}, {% endfor %} ), {%- endfor %} } {% endif %} } /// Creates a {{ nxn }} matrix from {{ dimension_in_full }} column vectors. #[inline(always)] #[must_use] pub const fn from_cols( {% for axis in axes %} {{ axis }}: {{ col_t }}, {% endfor %} ) -> Self { {% if self_t == "Mat2" and is_sse2 %} unsafe { UnionCast { a: [x_axis.x, x_axis.y, y_axis.x, y_axis.y] }.v } {% elif self_t == "Mat2" and is_wasm32 %} Self(f32x4(x_axis.x, x_axis.y, y_axis.x, y_axis.y)) {% elif self_t == "Mat2" and is_coresimd %} Self(f32x4::from_array([x_axis.x, x_axis.y, y_axis.x, y_axis.y])) {% elif self_t == "Mat2" and is_neon %} unsafe { UnionCast { a: [x_axis.x, x_axis.y, y_axis.x, y_axis.y] }.v } {% else %} Self { {% for axis in axes %} {{ axis }}, {%- endfor %} } {% endif %} } /// Creates a {{ nxn }} matrix from a `[{{ scalar_t }}; {{ size }}]` array stored in column major order. /// If your data is stored in row major you will need to `transpose` the returned /// matrix. #[inline] #[must_use] pub const fn from_cols_array(m: &[{{ scalar_t }}; {{ size }}]) -> Self { {% if self_t == "Mat2" and is_coresimd %} Self(f32x4::from_array(*m)) {% else %} Self::new( {% for i in range(end = size) %} m[{{ i }}], {%- endfor %} ) {% endif %} } /// Creates a `[{{ scalar_t }}; {{ size }}]` array storing data in column major order. /// If you require data in row major order `transpose` the matrix first. #[inline] #[must_use] pub const fn to_cols_array(&self) -> [{{ scalar_t }}; {{ size }}] { {% if self_t == "Mat2" and not is_scalar %} unsafe { *(self as *const Self as *const [f32; 4]) } {% else %} {% if dim >= 2 and not is_scalar %} {% for axis in axes %} let [{% for c in components %} {{ axis }}_{{ c }}, {% endfor %}] = self.{{ axis }}.to_array(); {%- endfor %} {% endif %} [ {% for axis in axes %} {% for c in components %} {% if dim >= 2 and not is_scalar %} {{ axis }}_{{ c }}, {% else %} self.{{ axis }}.{{ c }}, {% endif %} {%- endfor %} {%- endfor %} ] {% endif %} } /// Creates a {{ nxn }} matrix from a `[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]` {{ dim }}D array stored in column major order. /// If your data is in row major order you will need to `transpose` the returned /// matrix. #[inline] #[must_use] pub const fn from_cols_array_2d(m: &[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]) -> Self { Self::from_cols( {% for i in range(end = dim) %} {{ col_t }}::from_array(m[{{ i }}]), {%- endfor %} ) } /// Creates a `[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]` {{ dim }}D array storing data in column major order. /// If you require data in row major order `transpose` the matrix first. #[inline] #[must_use] pub const fn to_cols_array_2d(&self) -> [[{{ scalar_t }}; {{ dim }}]; {{ dim }}] { {% if self_t == "Mat2" and not is_scalar %} unsafe { *(self as *const Self as *const [[f32; 2]; 2]) } {% else %} [ {% for axis in axes %} self.{{ axis }}.to_array(), {%- endfor %} ] {% endif %} } /// Creates a {{ nxn }} matrix with its diagonal set to `diagonal` and all other entries set to 0. #[doc(alias = "scale")] #[inline] #[must_use] pub const fn from_diagonal(diagonal: {{ vecn_t }}) -> Self { {% if self_t == "Mat4" and not is_scalar %} // diagonal.x, diagonal.y etc can't be done in a const-context let [x, y, z, w] = diagonal.to_array(); Self::new( {% for i in range(end = dim) %} {% for j in range(end = dim) %} {% if i == j %} {{ components[i] }}, {% else %} 0.0, {% endif %} {%- endfor %} {%- endfor %} ) {% else %} Self::new( {% for i in range(end = dim) %} {% for j in range(end = dim) %} {% if i == j %} diagonal.{{ components[i] }}, {% else %} 0.0, {% endif %} {%- endfor %} {%- endfor %} ) {% endif %} } {% if dim == 2 %} /// Creates a {{ nxn }} matrix containing the combining non-uniform `scale` and rotation of /// `angle` (in radians). #[inline] #[must_use] pub fn from_scale_angle(scale: {{ col_t }}, angle: {{ scalar_t }}) -> Self { let (sin, cos) = math::sin_cos(angle); Self::new(cos * scale.x, sin * scale.x, -sin * scale.y, cos * scale.y) } /// Creates a {{ nxn }} matrix containing a rotation of `angle` (in radians). #[inline] #[must_use] pub fn from_angle(angle: {{ scalar_t }}) -> Self { let (sin, cos) = math::sin_cos(angle); Self::new(cos, sin, -sin, cos) } /// Creates a {{ nxn }} matrix from a 3x3 matrix, discarding the 2nd row and column. #[inline] #[must_use] pub fn from_mat3(m: {{ mat3_t }}) -> Self { Self::from_cols(m.x_axis.xy(), m.y_axis.xy()) } {{ macros::impl_mat3_minor(mat3_t=mat3_t) }} {% if scalar_t == "f32" %} /// Creates a {{ nxn }} matrix from a 3x3 matrix, discarding the 2nd row and column. #[inline] #[must_use] pub fn from_mat3a(m: Mat3A) -> Self { {# TODO: SIMD optimise #} Self::from_cols(m.x_axis.xy(), m.y_axis.xy()) } {{ macros::impl_mat3_minor(mat3_t="Mat3A", align="a") }} {% endif %} {% elif dim == 3 %} /// Creates a 3x3 matrix from a 4x4 matrix, discarding the 4th row and column. #[inline] #[must_use] pub fn from_mat4(m: {{ mat4_t }}) -> Self { Self::from_cols( {{ col_t }}::from_vec4(m.x_axis), {{ col_t }}::from_vec4(m.y_axis), {{ col_t }}::from_vec4(m.z_axis), ) } {{ macros::impl_mat4_minor(mat4_t=mat4_t, vec3_t=col_t) }} /// Creates a 3D rotation matrix from the given quaternion. /// /// # Panics /// /// Will panic if `rotation` is not normalized when `glam_assert` is enabled. #[inline] #[must_use] pub fn from_quat(rotation: {{ quat_t }}) -> Self { glam_assert!(rotation.is_normalized()); let x2 = rotation.x + rotation.x; let y2 = rotation.y + rotation.y; let z2 = rotation.z + rotation.z; let xx = rotation.x * x2; let xy = rotation.x * y2; let xz = rotation.x * z2; let yy = rotation.y * y2; let yz = rotation.y * z2; let zz = rotation.z * z2; let wx = rotation.w * x2; let wy = rotation.w * y2; let wz = rotation.w * z2; Self::from_cols( {{ col_t }}::new(1.0 - (yy + zz), xy + wz, xz - wy), {{ col_t }}::new(xy - wz, 1.0 - (xx + zz), yz + wx), {{ col_t }}::new(xz + wy, yz - wx, 1.0 - (xx + yy)), ) } /// Creates a 3D rotation matrix from a normalized rotation `axis` and `angle` (in /// radians). /// /// # Panics /// /// Will panic if `axis` is not normalized when `glam_assert` is enabled. #[inline] #[must_use] pub fn from_axis_angle(axis: {{ vec3_t }}, angle: {{ scalar_t }}) -> Self { {# TODO: make common with dim == 4 #} glam_assert!(axis.is_normalized()); let (sin, cos) = math::sin_cos(angle); let (xsin, ysin, zsin) = axis.mul(sin).into(); let (x, y, z) = axis.into(); let (x2, y2, z2) = axis.mul(axis).into(); let omc = 1.0 - cos; let xyomc = x * y * omc; let xzomc = x * z * omc; let yzomc = y * z * omc; Self::from_cols( {{ col_t }}::new(x2 * omc + cos, xyomc + zsin, xzomc - ysin), {{ col_t }}::new(xyomc - zsin, y2 * omc + cos, yzomc + xsin), {{ col_t }}::new(xzomc + ysin, yzomc - xsin, z2 * omc + cos), ) } /// Creates a 3D rotation matrix from the given euler rotation sequence and the angles (in /// radians). #[inline] #[must_use] pub fn from_euler(order: EulerRot, a: {{ scalar_t }}, b: {{ scalar_t }}, c: {{ scalar_t }}) -> Self { Self::from_euler_angles(order, a, b, c) } /// Extract Euler angles with the given Euler rotation order. /// /// Note if the input matrix contains scales, shears, or other non-rotation transformations then /// the resulting Euler angles will be ill-defined. /// /// # Panics /// /// Will panic if any input matrix column is not normalized when `glam_assert` is enabled. #[inline] #[must_use] pub fn to_euler(&self, order: EulerRot) -> ({{ scalar_t }}, {{ scalar_t }}, {{ scalar_t }}) { glam_assert!(self.x_axis.is_normalized() && self.y_axis.is_normalized() && self.z_axis.is_normalized()); self.to_euler_angles(order) } /// Creates a 3D rotation matrix from `angle` (in radians) around the x axis. #[inline] #[must_use] pub fn from_rotation_x(angle: {{ scalar_t }}) -> Self { let (sina, cosa) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::X, {{ col_t }}::new(0.0, cosa, sina), {{ col_t }}::new(0.0, -sina, cosa), ) } /// Creates a 3D rotation matrix from `angle` (in radians) around the y axis. #[inline] #[must_use] pub fn from_rotation_y(angle: {{ scalar_t }}) -> Self { let (sina, cosa) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::new(cosa, 0.0, -sina), {{ col_t }}::Y, {{ col_t }}::new(sina, 0.0, cosa), ) } /// Creates a 3D rotation matrix from `angle` (in radians) around the z axis. #[inline] #[must_use] pub fn from_rotation_z(angle: {{ scalar_t }}) -> Self { let (sina, cosa) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::new(cosa, sina, 0.0), {{ col_t }}::new(-sina, cosa, 0.0), {{ col_t }}::Z, ) } /// Creates an affine transformation matrix from the given 2D `translation`. /// /// The resulting matrix can be used to transform 2D points and vectors. See /// [`Self::transform_point2()`] and [`Self::transform_vector2()`]. #[inline] #[must_use] pub fn from_translation(translation: {{ vec2_t }}) -> Self { Self::from_cols( {{ col_t }}::X, {{ col_t }}::Y, {{ col_t }}::new(translation.x, translation.y, 1.0)) } /// Creates an affine transformation matrix from the given 2D rotation `angle` (in /// radians). /// /// The resulting matrix can be used to transform 2D points and vectors. See /// [`Self::transform_point2()`] and [`Self::transform_vector2()`]. #[inline] #[must_use] pub fn from_angle(angle: {{ scalar_t }}) -> Self { let (sin, cos) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::new(cos, sin, 0.0), {{ col_t }}::new(-sin, cos, 0.0), {{ col_t }}::Z, ) } /// Creates an affine transformation matrix from the given 2D `scale`, rotation `angle` (in /// radians) and `translation`. /// /// The resulting matrix can be used to transform 2D points and vectors. See /// [`Self::transform_point2()`] and [`Self::transform_vector2()`]. #[inline] #[must_use] pub fn from_scale_angle_translation(scale: {{ vec2_t }}, angle: {{ scalar_t }}, translation: {{ vec2_t }}) -> Self { let (sin, cos) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::new(cos * scale.x, sin * scale.x, 0.0), {{ col_t }}::new(-sin * scale.y, cos * scale.y, 0.0), {{ col_t }}::new(translation.x, translation.y, 1.0), ) } /// Creates an affine transformation matrix from the given non-uniform 2D `scale`. /// /// The resulting matrix can be used to transform 2D points and vectors. See /// [`Self::transform_point2()`] and [`Self::transform_vector2()`]. /// /// # Panics /// /// Will panic if all elements of `scale` are zero when `glam_assert` is enabled. #[inline] #[must_use] pub fn from_scale(scale: {{ vec2_t }}) -> Self { // Do not panic as long as any component is non-zero glam_assert!(scale.cmpne({{ vec2_t }}::ZERO).any()); Self::from_cols( {{ col_t }}::new(scale.x, 0.0, 0.0), {{ col_t }}::new(0.0, scale.y, 0.0), {{ col_t }}::Z, ) } /// Creates an affine transformation matrix from the given 2x2 matrix. /// /// The resulting matrix can be used to transform 2D points and vectors. See /// [`Self::transform_point2()`] and [`Self::transform_vector2()`]. #[inline] pub fn from_mat2(m: {{ mat2_t }}) -> Self { Self::from_cols((m.x_axis, 0.0).into(), (m.y_axis, 0.0).into(), {{ col_t }}::Z) } {% elif dim == 4 %} #[inline] #[must_use] fn quat_to_axes(rotation: {{ quat_t }}) -> ({{ col_t }}, {{ col_t }}, {{ col_t }}) { 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 = {{ col_t }}::new(1.0 - (yy + zz), xy + wz, xz - wy, 0.0); let y_axis = {{ col_t }}::new(xy - wz, 1.0 - (xx + zz), yz + wx, 0.0); let z_axis = {{ col_t }}::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] #[must_use] pub fn from_scale_rotation_translation( scale: {{ vec3_t }}, rotation: {{ quat_t }}, translation: {{ vec3_t }}, ) -> 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), {{ col_t }}::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] #[must_use] pub fn from_rotation_translation(rotation: {{ quat_t }}, translation: {{ vec3_t }}) -> Self { let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation); Self::from_cols(x_axis, y_axis, z_axis, {{ col_t }}::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] #[must_use] pub fn to_scale_rotation_translation(&self) -> ({{ vec3_t }}, {{ quat_t }}, {{ vec3_t }}) { let det = self.determinant(); glam_assert!(det != 0.0); let scale = {{ vec3_t }}::new( self.x_axis.length() * math::signum(det), self.y_axis.length(), self.z_axis.length(), ); glam_assert!(scale.cmpne({{ vec3_t }}::ZERO).all()); let inv_scale = scale.recip(); let rotation = {{ quat_t }}::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] #[must_use] pub fn from_quat(rotation: {{ quat_t }}) -> Self { let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation); Self::from_cols(x_axis, y_axis, z_axis, {{ col_t }}::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] #[must_use] pub fn from_mat3(m: {{ mat3_t }}) -> Self { Self::from_cols( {{ col_t }}::from((m.x_axis, 0.0)), {{ col_t }}::from((m.y_axis, 0.0)), {{ col_t }}::from((m.z_axis, 0.0)), {{ col_t }}::W, ) } /// Creates an affine transformation matrics from a 3x3 matrix (expressing scale, shear and /// rotation) and a translation vector. /// /// Equivalent to `{{ self_t }}::from_translation(translation) * {{ self_t }}::from_mat3(mat3)` #[inline] #[must_use] pub fn from_mat3_translation(mat3: {{ mat3_t }}, translation: {{ vec3_t }}) -> Self { Self::from_cols( {{ col_t }}::from((mat3.x_axis, 0.0)), {{ col_t }}::from((mat3.y_axis, 0.0)), {{ col_t }}::from((mat3.z_axis, 0.0)), {{ col_t }}::from((translation, 1.0)), ) } {% if scalar_t == "f32" %} /// 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] #[must_use] 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, ) } {% endif %} /// 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] #[must_use] pub fn from_translation(translation: {{ vec3_t }}) -> Self { Self::from_cols( {{ col_t }}::X, {{ col_t }}::Y, {{ col_t }}::Z, {{ col_t }}::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] #[must_use] pub fn from_axis_angle(axis: {{ vec3_t }}, angle: {{ scalar_t }}) -> Self { {# TODO: make common with dim == 3 #} glam_assert!(axis.is_normalized()); let (sin, cos) = math::sin_cos(angle); 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( {{ col_t }}::new( axis_sq.x * omc + cos, xyomc + axis_sin.z, xzomc - axis_sin.y, 0.0, ), {{ col_t }}::new( xyomc - axis_sin.z, axis_sq.y * omc + cos, yzomc + axis_sin.x, 0.0, ), {{ col_t }}::new( xzomc + axis_sin.y, yzomc - axis_sin.x, axis_sq.z * omc + cos, 0.0, ), {{ col_t }}::W, ) } /// 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()`]. #[inline] #[must_use] pub fn from_euler(order: EulerRot, a: {{ scalar_t }}, b: {{ scalar_t }}, c: {{ scalar_t }}) -> Self { Self::from_euler_angles(order, a, b, c) } /// Extract Euler angles with the given Euler rotation order. /// /// Note if the upper 3x3 matrix contain scales, shears, or other non-rotation transformations /// then the resulting Euler angles will be ill-defined. /// /// # Panics /// /// Will panic if any column of the upper 3x3 rotation matrix is not normalized when /// `glam_assert` is enabled. #[inline] #[must_use] pub fn to_euler(&self, order: EulerRot) -> ({{ scalar_t }}, {{ scalar_t }}, {{ scalar_t }}) { glam_assert!(self.x_axis.xyz().is_normalized() && self.y_axis.xyz().is_normalized() && self.z_axis.xyz().is_normalized()); self.to_euler_angles(order) } /// 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] #[must_use] pub fn from_rotation_x(angle: {{ scalar_t }}) -> Self { let (sina, cosa) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::X, {{ col_t }}::new(0.0, cosa, sina, 0.0), {{ col_t }}::new(0.0, -sina, cosa, 0.0), {{ col_t }}::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] #[must_use] pub fn from_rotation_y(angle: {{ scalar_t }}) -> Self { let (sina, cosa) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::new(cosa, 0.0, -sina, 0.0), {{ col_t }}::Y, {{ col_t }}::new(sina, 0.0, cosa, 0.0), {{ col_t }}::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] #[must_use] pub fn from_rotation_z(angle: {{ scalar_t }}) -> Self { let (sina, cosa) = math::sin_cos(angle); Self::from_cols( {{ col_t }}::new(cosa, sina, 0.0, 0.0), {{ col_t }}::new(-sina, cosa, 0.0, 0.0), {{ col_t }}::Z, {{ col_t }}::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] #[must_use] pub fn from_scale(scale: {{ vec3_t }}) -> Self { // Do not panic as long as any component is non-zero glam_assert!(scale.cmpne({{ vec3_t }}::ZERO).any()); Self::from_cols( {{ col_t }}::new(scale.x, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, scale.y, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, scale.z, 0.0), {{ col_t }}::W, ) } {% endif %} /// Creates a {{ nxn }} matrix from the first {{ size }} values in `slice`. /// /// # Panics /// /// Panics if `slice` is less than {{ size }} elements long. #[inline] #[must_use] pub const fn from_cols_slice(slice: &[{{ scalar_t }}]) -> Self { Self::new( {% for i in range(end = size) %} slice[{{ i }}], {%- endfor %} ) } /// Writes the columns of `self` to the first {{ size }} elements in `slice`. /// /// # Panics /// /// Panics if `slice` is less than {{ size }} elements long. #[inline] pub fn write_cols_to_slice(self, slice: &mut [{{ scalar_t }}]) { {% for i in range(end = dim) %} {%- for j in range(end = dim) %} slice[{{ i * dim + j }}] = self.{{ axes[i] }}.{{ components[j] }}; {%- endfor %} {%- endfor %} } /// Returns the matrix column for the given `index`. /// /// # Panics /// /// Panics if `index` is greater than {{ dim - 1 }}. #[inline] #[must_use] pub fn col(&self, index: usize) -> {{ col_t }} { match index { {% for axis in axes %} {{ loop.index0 }} => self.{{ axis }}, {%- endfor %} _ => panic!("index out of bounds"), } } /// Returns a mutable reference to the matrix column for the given `index`. /// /// # Panics /// /// Panics if `index` is greater than {{ dim - 1 }}. #[inline] pub fn col_mut(&mut self, index: usize) -> &mut {{ col_t }} { match index { {% for axis in axes %} {{ loop.index0 }} => &mut self.{{ axis }}, {%- endfor %} _ => panic!("index out of bounds"), } } /// Returns the matrix row for the given `index`. /// /// # Panics /// /// Panics if `index` is greater than {{ dim - 1 }}. #[inline] #[must_use] pub fn row(&self, index: usize) -> {{ col_t }} { match index { {% for i in range(end=dim) %} {{ i }} => {{ col_t }}::new( {% for axis in axes %} self.{{ axis }}.{{ components[i] }}, {%- endfor %} ), {%- endfor %} _ => 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] #[must_use] pub fn is_finite(&self) -> bool { {% for axis in axes %} self.{{ axis }}.is_finite() {% if not loop.last %} && {% endif %} {% endfor %} } /// Returns `true` if any elements are `NaN`. #[inline] #[must_use] pub fn is_nan(&self) -> bool { {% for axis in axes %} self.{{ axis }}.is_nan() {% if not loop.last %} || {% endif %} {% endfor %} } /// Returns the transpose of `self`. #[inline] #[must_use] pub fn transpose(&self) -> Self { {% if self_t == "Mat2" and is_sse2 %} Self(unsafe { _mm_shuffle_ps(self.0, self.0, 0b11_01_10_00) }) {% elif self_t == "Mat2" and is_wasm32 %} Self(i32x4_shuffle::<0, 2, 5, 7>(self.0, self.0)) {% elif self_t == "Mat2" and is_coresimd %} Self(simd_swizzle!(self.0, [0, 2, 1, 3])) {% elif self_t == "Mat2" and is_neon %} Self(unsafe { vsetq_lane_f32( vgetq_lane_f32(self.0, 2), vsetq_lane_f32(vgetq_lane_f32(self.0, 1), self.0, 2), 1, ) }) {% elif self_t == "Mat3A" and is_sse2 %} unsafe { 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); Self { x_axis: Vec3A(_mm_shuffle_ps(tmp0, self.z_axis.0, 0b00_00_10_00)), y_axis: Vec3A(_mm_shuffle_ps(tmp0, self.z_axis.0, 0b01_01_11_01)), z_axis: Vec3A(_mm_shuffle_ps(tmp1, self.z_axis.0, 0b10_10_10_00)), } } {% elif self_t == "Mat3A" and is_wasm32 %} let tmp0 = i32x4_shuffle::<0, 1, 4, 5>(self.x_axis.0, self.y_axis.0); let tmp1 = i32x4_shuffle::<2, 3, 6, 7>(self.x_axis.0, self.y_axis.0); Self { x_axis: Vec3A(i32x4_shuffle::<0, 2, 4, 4>(tmp0, self.z_axis.0)), y_axis: Vec3A(i32x4_shuffle::<1, 3, 5, 5>(tmp0, self.z_axis.0)), z_axis: Vec3A(i32x4_shuffle::<0, 2, 6, 6>(tmp1, self.z_axis.0)), } {% elif self_t == "Mat3A" and is_coresimd %} let tmp0 = simd_swizzle!( self.x_axis.0, self.y_axis.0, [0, 1, 4, 5] ); let tmp1 = simd_swizzle!( self.x_axis.0, self.y_axis.0, [2, 3, 6, 7] ); Self { x_axis: Vec3A(simd_swizzle!( tmp0, self.z_axis.0, [0, 2, 4, 4] )), y_axis: Vec3A(simd_swizzle!( tmp0, self.z_axis.0, [1, 3, 5, 5] )), z_axis: Vec3A(simd_swizzle!( tmp1, self.z_axis.0, [0, 2, 6, 6] )), } {% elif self_t == "Mat3A" and is_neon %} let x = self.x_axis.0; let y = self.y_axis.0; let z = self.z_axis.0; unsafe { let tmp0 = vreinterpretq_f32_u64(vsetq_lane_u64( vgetq_lane_u64(vreinterpretq_u64_f32(y), 0), vreinterpretq_u64_f32(x), 1, )); let tmp1 = vreinterpretq_f32_u64(vzip2q_u64( vreinterpretq_u64_f32(x), vreinterpretq_u64_f32(y), )); Mat3A::from_cols( Vec3A::from(vsetq_lane_f32(vgetq_lane_f32(z, 0), vuzp1q_f32(tmp0, z), 3)), Vec3A::from(vuzp2q_f32(tmp0, vdupq_laneq_f32(z, 1))), Vec3A::from(vsetq_lane_f32(vgetq_lane_f32(z, 2), vuzp1q_f32(tmp1, z), 2)), ) } {% elif self_t == "Mat4" and is_sse2 %} 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)), } } {% elif self_t == "Mat4" and is_wasm32 %} // Based on https://github.com/microsoft/DirectXMath `XMMatrixTranspose` let tmp0 = i32x4_shuffle::<0, 1, 4, 5>(self.x_axis.0, self.y_axis.0); let tmp1 = i32x4_shuffle::<2, 3, 6, 7>(self.x_axis.0, self.y_axis.0); let tmp2 = i32x4_shuffle::<0, 1, 4, 5>(self.z_axis.0, self.w_axis.0); let tmp3 = i32x4_shuffle::<2, 3, 6, 7>(self.z_axis.0, self.w_axis.0); Self { x_axis: Vec4(i32x4_shuffle::<0, 2, 4, 6>(tmp0, tmp2)), y_axis: Vec4(i32x4_shuffle::<1, 3, 5, 7>(tmp0, tmp2)), z_axis: Vec4(i32x4_shuffle::<0, 2, 4, 6>(tmp1, tmp3)), w_axis: Vec4(i32x4_shuffle::<1, 3, 5, 7>(tmp1, tmp3)), } {% elif self_t == "Mat4" and is_coresimd %} // Based on https://github.com/microsoft/DirectXMath `XMMatrixTranspose` let tmp0 = simd_swizzle!( self.x_axis.0, self.y_axis.0, [0, 1, 4, 5] ); let tmp1 = simd_swizzle!( self.x_axis.0, self.y_axis.0, [2, 3, 6, 7] ); let tmp2 = simd_swizzle!( self.z_axis.0, self.w_axis.0, [0, 1, 4, 5] ); let tmp3 = simd_swizzle!( self.z_axis.0, self.w_axis.0, [2, 3, 6, 7] ); Self { x_axis: Vec4(simd_swizzle!(tmp0, tmp2, [0, 2, 4, 6])), y_axis: Vec4(simd_swizzle!(tmp0, tmp2, [1, 3, 5, 7])), z_axis: Vec4(simd_swizzle!(tmp1, tmp3, [0, 2, 4, 6])), w_axis: Vec4(simd_swizzle!(tmp1, tmp3, [1, 3, 5, 7])), } {% else %} Self { {% for i in range(end = dim) %} {{ axes[i] }}: {{ col_t }}::new( {% for j in range(end = dim) %} self.{{ axes[j] }}.{{ components[i] }}, {% endfor %} ), {%- endfor %} } {% endif %} } /// Returns the determinant of `self`. {%- if dim < 4 %} #[inline] {%- endif %} #[must_use] pub fn determinant(&self) -> {{ scalar_t }} { {% if self_t == "Mat2" and is_sse2 %} unsafe { let abcd = self.0; let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11); let prod = _mm_mul_ps(abcd, dcba); let det = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01)); _mm_cvtss_f32(det) } {% elif self_t == "Mat2" and is_wasm32 %} let abcd = self.0; let dcba = i32x4_shuffle::<3, 2, 5, 4>(abcd, abcd); let prod = f32x4_mul(abcd, dcba); let det = f32x4_sub(prod, i32x4_shuffle::<1, 1, 5, 5>(prod, prod)); f32x4_extract_lane::<0>(det) {% elif self_t == "Mat2" and is_coresimd %} let abcd = self.0; let dcba = simd_swizzle!(abcd, [3, 2, 1, 0]); let prod = abcd * dcba; let det = prod - simd_swizzle!(prod, [1, 1, 1, 1]); det[0] {% elif self_t == "Mat2" and is_neon %} unsafe { let abcd = self.0; let badc = vrev64q_f32(abcd); let dcba = vextq_f32(badc, badc, 2); let prod = vmulq_f32(abcd, dcba); let det = vsubq_f32(prod, vdupq_laneq_f32(prod, 1)); vgetq_lane_f32(det, 0) } {% elif self_t == "Mat4" and is_sse2 %} 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) } {% elif self_t == "Mat4" and is_wasm32 %} // Based on https://github.com/g-truc/glm `glm_mat4_determinant` let swp2a = i32x4_shuffle::<2, 1, 1, 0>(self.z_axis.0, self.z_axis.0); let swp3a = i32x4_shuffle::<3, 3, 2, 3>(self.w_axis.0, self.w_axis.0); let swp2b = i32x4_shuffle::<3, 3, 2, 3>(self.z_axis.0, self.z_axis.0); let swp3b = i32x4_shuffle::<2, 1, 1, 0>(self.w_axis.0, self.w_axis.0); let swp2c = i32x4_shuffle::<2, 1, 0, 0>(self.z_axis.0, self.z_axis.0); let swp3c = i32x4_shuffle::<0, 0, 2, 1>(self.w_axis.0, self.w_axis.0); let mula = f32x4_mul(swp2a, swp3a); let mulb = f32x4_mul(swp2b, swp3b); let mulc = f32x4_mul(swp2c, swp3c); let sube = f32x4_sub(mula, mulb); let subf = f32x4_sub(i32x4_shuffle::<6, 7, 2, 3>(mulc, mulc), mulc); let subfaca = i32x4_shuffle::<0, 0, 1, 2>(sube, sube); let swpfaca = i32x4_shuffle::<1, 0, 0, 0>(self.y_axis.0, self.y_axis.0); let mulfaca = f32x4_mul(swpfaca, subfaca); let subtmpb = i32x4_shuffle::<1, 3, 4, 4>(sube, subf); let subfacb = i32x4_shuffle::<0, 1, 1, 3>(subtmpb, subtmpb); let swpfacb = i32x4_shuffle::<2, 2, 1, 1>(self.y_axis.0, self.y_axis.0); let mulfacb = f32x4_mul(swpfacb, subfacb); let subres = f32x4_sub(mulfaca, mulfacb); let subtmpc = i32x4_shuffle::<2, 2, 4, 5>(sube, subf); let subfacc = i32x4_shuffle::<0, 2, 3, 3>(subtmpc, subtmpc); let swpfacc = i32x4_shuffle::<3, 3, 3, 2>(self.y_axis.0, self.y_axis.0); let mulfacc = f32x4_mul(swpfacc, subfacc); let addres = f32x4_add(subres, mulfacc); let detcof = f32x4_mul(addres, f32x4(1.0, -1.0, 1.0, -1.0)); dot4(self.x_axis.0, detcof) {% elif self_t == "Mat4" and is_coresimd %} // Based on https://github.com/g-truc/glm `glm_mat4_determinant` let swp2a = simd_swizzle!(self.z_axis.0, [2, 1, 1, 0]); let swp3a = simd_swizzle!(self.w_axis.0, [3, 3, 2, 3]); let swp2b = simd_swizzle!(self.z_axis.0, [3, 3, 2, 3]); let swp3b = simd_swizzle!(self.w_axis.0, [2, 1, 1, 0]); let swp2c = simd_swizzle!(self.z_axis.0, [2, 1, 0, 0]); let swp3c = simd_swizzle!(self.w_axis.0, [0, 0, 2, 1]); let mula = swp2a * swp3a; let mulb = swp2b * swp3b; let mulc = swp2c * swp3c; let sube = mula - mulb; let subf = simd_swizzle!(mulc, [2, 3, 2, 3]) - mulc; let subfaca = simd_swizzle!(sube, [0, 0, 1, 2]); let swpfaca = simd_swizzle!(self.y_axis.0, [1, 0, 0, 0]); let mulfaca = swpfaca * subfaca; let subtmpb = simd_swizzle!(sube, subf, [1, 3, 4, 4]); let subfacb = simd_swizzle!(subtmpb, [0, 1, 1, 3]); let swpfacb = simd_swizzle!(self.y_axis.0, [2, 2, 1, 1]); let mulfacb = swpfacb * subfacb; let subres = mulfaca - mulfacb; let subtmpc = simd_swizzle!(sube, subf, [2, 2, 4, 5]); let subfacc = simd_swizzle!(subtmpc, [0, 2, 3, 3]); let swpfacc = simd_swizzle!(self.y_axis.0, [3, 3, 3, 2]); let mulfacc = swpfacc * subfacc; let addres = subres + mulfacc; let detcof = addres * f32x4::from_array([1.0, -1.0, 1.0, -1.0]); dot4(self.x_axis.0, detcof) {# // neon implementation is slower than scalar // {% elif self_t == "Mat4" and is_neon %} // unsafe { // let swizz2110 = |x| { // let x = vuzp1q_f32(x, vdupq_laneq_f32(x, 1)); // vextq_f32(x, x, 1) // }; // let swizz3323 = |x| { // let xy = vgetq_lane_f32(x, 3); // vsetq_lane_f32(xy, vsetq_lane_f32(xy, x, 0), 1) // }; // let swizz2100 = |x| { // let y = vuzp1q_f32(x, x); // vuzp1q_f32(vextq_f32(x, y, 3), y) // }; // let swizz0021 = |x| vtrn1q_f32(x, vzip1q_f32(x, x)); // // let swizz6723 = |x, y| { // // vsetq_lane_f64(vgetq_lane_f64(y, 1), 0) // // }; // let swizz2323 = |x| vreinterpretq_f32_f64(vdupq_laneq_f64(vreinterpretq_f64_f32(x), 1)); // let swizz0012 = |x| vzip1q_f32(x, vuzp1q_f32(x, x)); // let swizz1000 = |x| vsetq_lane_f32(vgetq_lane_f32(x, 1), vdupq_laneq_f32(x, 0), 0); // let swizz1344 = |x, y| vuzp2q_f32(x, vdupq_laneq_f32(y, 0)); // let swizz0113 = |x| vsetq_lane_f32(vgetq_lane_f32(x, 1), x, 2); // let swizz2211 = |x| { // let x = vsetq_lane_f32(vgetq_lane_f32(x, 1), x, 3); // vzip2q_f32(x, x) // }; // let swizz2245 = |x, y| vextq_f32(vtrn1q_f32(x, x), y, 1); // let swizz0233 = |x| vuzp1q_f32(x, vdupq_laneq_f32(x, 3)); // let swizz3332 = |x| vsetq_lane_f32(vgetq_lane_f32(x, 2), vdupq_laneq_f32(x, 3), 3); // // Based on https://github.com/g-truc/glm `glm_mat4_determinant` // let swp2a = swizz2110(self.z_axis.0); // let swp3a = swizz3323(self.w_axis.0); // let swp2b = swizz3323(self.z_axis.0); // let swp3b = swizz2110(self.w_axis.0); // let swp2c = swizz2100(self.z_axis.0); // let swp3c = swizz0021(self.w_axis.0); // let mula = vmulq_f32(swp2a, swp3a); // let mulb = vmulq_f32(swp2b, swp3b); // let mulc = vmulq_f32(swp2c, swp3c); // let sube = vsubq_f32(mula, mulb); // let subf = vsubq_f32(swizz2323(mulc), mulc); // let subfaca = swizz0012(sube); // let swpfaca = swizz1000(self.y_axis.0); // let mulfaca = vmulq_f32(swpfaca, subfaca); // let subtmpb = swizz1344(sube, subf); // let subfacb = swizz0113(subtmpb); // let swpfacb = swizz2211(self.y_axis.0); // let mulfacb = vmulq_f32(swpfacb, subfacb); // let subres = vsubq_f32(mulfaca, mulfacb); // let subtmpc = swizz2245(sube, subf); // let subfacc = swizz0233(subtmpc); // let swpfacc = swizz3332(self.y_axis.0); // let mulfacc = vmulq_f32(swpfacc, subfacc); // let addres = vaddq_f32(subres, mulfacc); // const COF: float32x4_t = Vec4::new(1.0, -1.0, 1.0, -1.0).0; // let detcof = vmulq_f32(addres, COF); // dot4(self.x_axis.0, detcof) // } #} {% elif dim == 2 %} self.x_axis.x * self.y_axis.y - self.x_axis.y * self.y_axis.x {% elif dim == 3 %} self.z_axis.dot(self.x_axis.cross(self.y_axis)) {% elif dim == 4 %} let (m00, m01, m02, m03) = self.x_axis.into(); let (m10, m11, m12, m13) = self.y_axis.into(); let (m20, m21, m22, m23) = self.z_axis.into(); let (m30, m31, m32, m33) = self.w_axis.into(); let a2323 = m22 * m33 - m23 * m32; let a1323 = m21 * m33 - m23 * m31; let a1223 = m21 * m32 - m22 * m31; let a0323 = m20 * m33 - m23 * m30; let a0223 = m20 * m32 - m22 * m30; let a0123 = m20 * m31 - m21 * m30; m00 * (m11 * a2323 - m12 * a1323 + m13 * a1223) - m01 * (m10 * a2323 - m12 * a0323 + m13 * a0223) + m02 * (m10 * a1323 - m11 * a0323 + m13 * a0123) - m03 * (m10 * a1223 - m11 * a0223 + m12 * a0123) {% endif %} } /// 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. {%- if dim < 4 %} #[inline] {%- endif %} #[must_use] pub fn inverse(&self) -> Self { {% if self_t == "Mat2" and is_sse2 %} unsafe { const SIGN: __m128 = crate::sse2::m128_from_f32x4([1.0, -1.0, -1.0, 1.0]); let abcd = self.0; let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11); let prod = _mm_mul_ps(abcd, dcba); let sub = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01)); let det = _mm_shuffle_ps(sub, sub, 0b00_00_00_00); let tmp = _mm_div_ps(SIGN, det); glam_assert!(Mat2(tmp).is_finite()); let dbca = _mm_shuffle_ps(abcd, abcd, 0b00_10_01_11); Self(_mm_mul_ps(dbca, tmp)) } {% elif self_t == "Mat2" and is_wasm32 %} const SIGN: v128 = crate::wasm32::v128_from_f32x4([1.0, -1.0, -1.0, 1.0]); let abcd = self.0; let dcba = i32x4_shuffle::<3, 2, 5, 4>(abcd, abcd); let prod = f32x4_mul(abcd, dcba); let sub = f32x4_sub(prod, i32x4_shuffle::<1, 1, 5, 5>(prod, prod)); let det = i32x4_shuffle::<0, 0, 4, 4>(sub, sub); let tmp = f32x4_div(SIGN, det); glam_assert!(Mat2(tmp).is_finite()); let dbca = i32x4_shuffle::<3, 1, 6, 4>(abcd, abcd); Self(f32x4_mul(dbca, tmp)) {% elif self_t == "Mat2" and is_coresimd %} const SIGN: f32x4 = f32x4::from_array([1.0, -1.0, -1.0, 1.0]); let abcd = self.0; let dcba = simd_swizzle!(abcd, [3, 2, 1, 0]); let prod = abcd * dcba; let sub = prod - simd_swizzle!(prod, [1, 1, 1, 1]); let det = simd_swizzle!(sub, [0, 0, 0, 0]); let tmp = SIGN / det; glam_assert!(Mat2(tmp).is_finite()); let dbca = simd_swizzle!(abcd, [3, 1, 2, 0]); Self(dbca.mul(tmp)) {% elif self_t == "Mat2" and is_neon %} unsafe { const SIGN: float32x4_t = crate::neon::f32x4_from_array([1.0, -1.0, -1.0, 1.0]); let abcd = self.0; let badc = vrev64q_f32(abcd); let dcba = vextq_f32(badc, badc, 2); let prod = vmulq_f32(abcd, dcba); let sub = vsubq_f32(prod, vdupq_laneq_f32(prod, 1)); let det = vdupq_laneq_f32(sub, 0); let tmp = vdivq_f32(SIGN, det); glam_assert!(Mat2(tmp).is_finite()); //let dbca = simd_swizzle!(abcd, [3, 1, 2, 0]); let dbca = vsetq_lane_f32( vgetq_lane_f32(abcd, 0), vsetq_lane_f32(vgetq_lane_f32(abcd, 3), abcd, 0), 3, ); Self(vmulq_f32(dbca, tmp)) } {% elif self_t == "Mat4" and is_sse2 %} {{ sse2::impl_mat4_inverse() }} {% elif self_t == "Mat4" and is_wasm32 %} {{ wasm32::impl_mat4_inverse() }} {% elif self_t == "Mat4" and is_coresimd %} {{ coresimd::impl_mat4_inverse() }} {% elif self_t == "Mat4" and is_neon %} {{ neon::impl_mat4_inverse() }} {% elif dim == 2 %} let inv_det = { let det = self.determinant(); glam_assert!(det != 0.0); det.recip() }; Self::new( self.y_axis.y * inv_det, self.x_axis.y * -inv_det, self.y_axis.x * -inv_det, self.x_axis.x * inv_det, ) {% elif dim == 3 %} let tmp0 = self.y_axis.cross(self.z_axis); let tmp1 = self.z_axis.cross(self.x_axis); let tmp2 = self.x_axis.cross(self.y_axis); let det = self.z_axis.dot(tmp2); glam_assert!(det != 0.0); let inv_det = {{ col_t }}::splat(det.recip()); Self::from_cols(tmp0.mul(inv_det), tmp1.mul(inv_det), tmp2.mul(inv_det)).transpose() {% elif dim == 4 %} let (m00, m01, m02, m03) = self.x_axis.into(); let (m10, m11, m12, m13) = self.y_axis.into(); let (m20, m21, m22, m23) = self.z_axis.into(); let (m30, m31, m32, m33) = self.w_axis.into(); let coef00 = m22 * m33 - m32 * m23; let coef02 = m12 * m33 - m32 * m13; let coef03 = m12 * m23 - m22 * m13; let coef04 = m21 * m33 - m31 * m23; let coef06 = m11 * m33 - m31 * m13; let coef07 = m11 * m23 - m21 * m13; let coef08 = m21 * m32 - m31 * m22; let coef10 = m11 * m32 - m31 * m12; let coef11 = m11 * m22 - m21 * m12; let coef12 = m20 * m33 - m30 * m23; let coef14 = m10 * m33 - m30 * m13; let coef15 = m10 * m23 - m20 * m13; let coef16 = m20 * m32 - m30 * m22; let coef18 = m10 * m32 - m30 * m12; let coef19 = m10 * m22 - m20 * m12; let coef20 = m20 * m31 - m30 * m21; let coef22 = m10 * m31 - m30 * m11; let coef23 = m10 * m21 - m20 * m11; let fac0 = {{ col_t }}::new(coef00, coef00, coef02, coef03); let fac1 = {{ col_t }}::new(coef04, coef04, coef06, coef07); let fac2 = {{ col_t }}::new(coef08, coef08, coef10, coef11); let fac3 = {{ col_t }}::new(coef12, coef12, coef14, coef15); let fac4 = {{ col_t }}::new(coef16, coef16, coef18, coef19); let fac5 = {{ col_t }}::new(coef20, coef20, coef22, coef23); let vec0 = {{ col_t }}::new(m10, m00, m00, m00); let vec1 = {{ col_t }}::new(m11, m01, m01, m01); let vec2 = {{ col_t }}::new(m12, m02, m02, m02); let vec3 = {{ col_t }}::new(m13, m03, m03, m03); let inv0 = vec1.mul(fac0).sub(vec2.mul(fac1)).add(vec3.mul(fac2)); let inv1 = vec0.mul(fac0).sub(vec2.mul(fac3)).add(vec3.mul(fac4)); let inv2 = vec0.mul(fac1).sub(vec1.mul(fac3)).add(vec3.mul(fac5)); let inv3 = vec0.mul(fac2).sub(vec1.mul(fac4)).add(vec2.mul(fac5)); let sign_a = {{ col_t }}::new(1.0, -1.0, 1.0, -1.0); let sign_b = {{ col_t }}::new(-1.0, 1.0, -1.0, 1.0); let inverse = Self::from_cols( inv0.mul(sign_a), inv1.mul(sign_b), inv2.mul(sign_a), inv3.mul(sign_b), ); let col0 = {{ col_t }}::new( inverse.x_axis.x, inverse.y_axis.x, inverse.z_axis.x, inverse.w_axis.x, ); let dot0 = self.x_axis.mul(col0); let dot1 = dot0.x + dot0.y + dot0.z + dot0.w; glam_assert!(dot1 != 0.0); let rcp_det = dot1.recip(); inverse.mul(rcp_det) {% endif %} } {% if dim == 3 %} /// Transforms the given 2D vector as a point. /// /// This is the equivalent of multiplying `rhs` as a 3D vector where `z` is `1`. /// /// This method assumes that `self` contains a valid affine transform. /// /// # Panics /// /// Will panic if the 2nd row of `self` is not `(0, 0, 1)` when `glam_assert` is enabled. #[inline] #[must_use] pub fn transform_point2(&self, rhs: {{ vec2_t }}) -> {{ vec2_t }} { glam_assert!(self.row(2).abs_diff_eq({{ col_t }}::Z, 1e-6)); {{ mat2_t }}::from_cols(self.x_axis.xy(), self.y_axis.xy()) * rhs + self.z_axis.xy() } /// Rotates the given 2D vector. /// /// This is the equivalent of multiplying `rhs` as a 3D vector where `z` is `0`. /// /// This method assumes that `self` contains a valid affine transform. /// /// # Panics /// /// Will panic if the 2nd row of `self` is not `(0, 0, 1)` when `glam_assert` is enabled. #[inline] #[must_use] pub fn transform_vector2(&self, rhs: {{ vec2_t }}) -> {{ vec2_t }} { glam_assert!(self.row(2).abs_diff_eq({{ col_t }}::Z, 1e-6)); {{ mat2_t }}::from_cols(self.x_axis.xy(), self.y_axis.xy()) * rhs } /// Creates a left-handed view matrix using a facing direction and an up direction. /// /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`. /// /// # Panics /// /// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled. #[inline] #[must_use] pub fn look_to_lh(dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { Self::look_to_rh(-dir, up) } /// Creates a right-handed view matrix using a facing direction and an up direction. /// /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`. /// /// # Panics /// /// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled. #[inline] #[must_use] pub fn look_to_rh(dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { glam_assert!(dir.is_normalized()); glam_assert!(up.is_normalized()); let f = dir; let s = f.cross(up).normalize(); let u = s.cross(f); Self::from_cols( {{ col_t }}::new(s.x, u.x, -f.x), {{ col_t }}::new(s.y, u.y, -f.y), {{ col_t }}::new(s.z, u.z, -f.z), ) } /// Creates a left-handed view matrix using a camera position, a focal point and an up /// direction. /// /// 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] #[must_use] pub fn look_at_lh(eye: {{ vec3_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { Self::look_to_lh(center.sub(eye).normalize(), up) } /// Creates a right-handed view matrix using a camera position, a focal point and an up /// direction. /// /// 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_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { Self::look_to_rh(center.sub(eye).normalize(), up) } {% elif dim == 4 %} /// Creates a left-handed view matrix using a camera position, a facing direction and an up /// direction /// /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`. /// /// # Panics /// /// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled. #[inline] #[must_use] pub fn look_to_lh(eye: {{ vec3_t }}, dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { Self::look_to_rh(eye, -dir, up) } /// Creates a right-handed view matrix using a camera position, a facing direction, and an up /// direction. /// /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`. /// /// # Panics /// /// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled. #[inline] #[must_use] pub fn look_to_rh(eye: {{ vec3_t }}, dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { glam_assert!(dir.is_normalized()); glam_assert!(up.is_normalized()); let f = dir; let s = f.cross(up).normalize(); let u = s.cross(f); Self::from_cols( {{ col_t }}::new(s.x, u.x, -f.x, 0.0), {{ col_t }}::new(s.y, u.y, -f.y, 0.0), {{ col_t }}::new(s.z, u.z, -f.z, 0.0), {{ col_t }}::new(-eye.dot(s), -eye.dot(u), eye.dot(f), 1.0), ) } /// Creates a left-handed view matrix using a camera position, a focal points and an up /// direction. /// /// 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] #[must_use] pub fn look_at_lh(eye: {{ vec3_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { Self::look_to_lh(eye, center.sub(eye).normalize(), up) } /// Creates a right-handed view matrix using a camera position, a focal point, and an up /// direction. /// /// 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_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self { Self::look_to_rh(eye, center.sub(eye).normalize(), up) } /// Creates a right-handed perspective projection matrix with [-1,1] depth range. /// /// This is the same as the OpenGL `glFrustum` function. /// /// See #[inline] #[must_use] pub fn frustum_rh_gl( left: {{ scalar_t }}, right: {{ scalar_t }}, bottom: {{ scalar_t }}, top: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}, ) -> Self { let inv_width = 1.0 / (right - left); let inv_height = 1.0 / (top - bottom); let inv_depth = 1.0 / (z_far - z_near); let a = (right + left) * inv_width; let b = (top + bottom) * inv_height; let c = -(z_far + z_near) * inv_depth; let d = -(2.0 * z_far * z_near) * inv_depth; let two_z_near = 2.0 * z_near; Self::from_cols( {{ col_t }}::new(two_z_near * inv_width, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, two_z_near * inv_height, 0.0, 0.0), {{ col_t }}::new(a, b, c, -1.0), {{ col_t }}::new(0.0, 0.0, d, 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] #[must_use] pub fn frustum_lh( left: {{ scalar_t }}, right: {{ scalar_t }}, bottom: {{ scalar_t }}, top: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}, ) -> Self { glam_assert!(z_near > 0.0 && z_far > 0.0); let inv_width = 1.0 / (right - left); let inv_height = 1.0 / (top - bottom); let inv_depth = 1.0 / (z_far - z_near); let a = (right + left) * inv_width; let b = (top + bottom) * inv_height; let c = z_far * inv_depth; let d = -(z_far * z_near) * inv_depth; let two_z_near = 2.0 * z_near; Self::from_cols( {{ col_t }}::new(two_z_near * inv_width, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, two_z_near * inv_height, 0.0, 0.0), {{ col_t }}::new(a, b, c, 1.0), {{ col_t }}::new(0.0, 0.0, d, 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] #[must_use] pub fn frustum_rh( left: {{ scalar_t }}, right: {{ scalar_t }}, bottom: {{ scalar_t }}, top: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}, ) -> Self { glam_assert!(z_near > 0.0 && z_far > 0.0); let inv_width = 1.0 / (right - left); let inv_height = 1.0 / (top - bottom); let inv_depth = 1.0 / (z_far - z_near); let a = (right + left) * inv_width; let b = (top + bottom) * inv_height; let c = -z_far * inv_depth; let d = -(z_far * z_near) * inv_depth; let two_z_near = 2.0 * z_near; Self::from_cols( {{ col_t }}::new(two_z_near * inv_width, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, two_z_near * inv_height, 0.0, 0.0), {{ col_t }}::new(a, b, c, -1.0), {{ col_t }}::new(0.0, 0.0, d, 0.0), ) } /// Creates a right-handed perspective projection matrix with `[-1,1]` depth range. /// /// Useful to map the standard right-handed coordinate system into what OpenGL expects. /// /// This is the same as the OpenGL `gluPerspective` function. /// See #[inline] #[must_use] pub fn perspective_rh_gl( fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}, ) -> Self { let inv_length = 1.0 / (z_near - z_far); let f = 1.0 / math::tan(0.5 * fov_y_radians); 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( {{ col_t }}::new(a, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, f, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, b, -1.0), {{ col_t }}::new(0.0, 0.0, c, 0.0), ) } /// Creates a left-handed perspective projection matrix with `[0,1]` depth range. /// /// Useful to map the standard left-handed coordinate system into what WebGPU/Metal/Direct3D expect. /// /// # Panics /// /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is /// enabled. #[inline] #[must_use] pub fn perspective_lh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}) -> Self { glam_assert!(z_near > 0.0 && z_far > 0.0); let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians); let h = cos_fov / sin_fov; let w = h / aspect_ratio; let r = z_far / (z_far - z_near); Self::from_cols( {{ col_t }}::new(w, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, h, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, r, 1.0), {{ col_t }}::new(0.0, 0.0, -r * z_near, 0.0), ) } /// Creates a right-handed perspective projection matrix with `[0,1]` depth range. /// /// Useful to map the standard right-handed coordinate system into what WebGPU/Metal/Direct3D expect. /// /// # Panics /// /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is /// enabled. #[inline] #[must_use] pub fn perspective_rh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}) -> Self { glam_assert!(z_near > 0.0 && z_far > 0.0); let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians); let h = cos_fov / sin_fov; let w = h / aspect_ratio; let r = z_far / (z_near - z_far); Self::from_cols( {{ col_t }}::new(w, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, h, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, r, -1.0), {{ col_t }}::new(0.0, 0.0, r * z_near, 0.0), ) } /// Creates an infinite left-handed perspective projection matrix with `[0,1]` depth range. /// /// Like `perspective_lh`, but with an infinite value for `z_far`. /// The result is that points near `z_near` are mapped to depth `0`, and as they move towards infinity the depth approaches `1`. /// /// # Panics /// /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is /// enabled. #[inline] #[must_use] pub fn perspective_infinite_lh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}) -> Self { glam_assert!(z_near > 0.0); let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians); let h = cos_fov / sin_fov; let w = h / aspect_ratio; Self::from_cols( {{ col_t }}::new(w, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, h, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, 1.0, 1.0), {{ col_t }}::new(0.0, 0.0, -z_near, 0.0), ) } /// Creates an infinite reverse left-handed perspective projection matrix with `[0,1]` depth range. /// /// Similar to `perspective_infinite_lh`, but maps `Z = z_near` to a depth of `1` and `Z = infinity` to a depth of `0`. /// /// # Panics /// /// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled. #[inline] #[must_use] pub fn perspective_infinite_reverse_lh( fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}, ) -> Self { glam_assert!(z_near > 0.0); let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians); let h = cos_fov / sin_fov; let w = h / aspect_ratio; Self::from_cols( {{ col_t }}::new(w, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, h, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, 0.0, 1.0), {{ col_t }}::new(0.0, 0.0, z_near, 0.0), ) } /// Creates an infinite right-handed perspective projection matrix with `[0,1]` depth range. /// /// Like `perspective_rh`, but with an infinite value for `z_far`. /// The result is that points near `z_near` are mapped to depth `0`, and as they move towards infinity the depth approaches `1`. /// /// # Panics /// /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is /// enabled. #[inline] #[must_use] pub fn perspective_infinite_rh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}) -> Self { glam_assert!(z_near > 0.0); let f = 1.0 / math::tan(0.5 * fov_y_radians); Self::from_cols( {{ col_t }}::new(f / aspect_ratio, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, f, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, -1.0, -1.0), {{ col_t }}::new(0.0, 0.0, -z_near, 0.0), ) } /// Creates an infinite reverse right-handed perspective projection matrix with `[0,1]` depth range. /// /// Similar to `perspective_infinite_rh`, but maps `Z = z_near` to a depth of `1` and `Z = infinity` to a depth of `0`. /// /// # Panics /// /// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled. #[inline] #[must_use] pub fn perspective_infinite_reverse_rh( fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}, ) -> Self { glam_assert!(z_near > 0.0); let f = 1.0 / math::tan(0.5 * fov_y_radians); Self::from_cols( {{ col_t }}::new(f / aspect_ratio, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, f, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, 0.0, -1.0), {{ col_t }}::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 /// /// /// Useful to map a right-handed coordinate system to the normalized device coordinates that OpenGL expects. #[inline] #[must_use] pub fn orthographic_rh_gl( left: {{ scalar_t }}, right: {{ scalar_t }}, bottom: {{ scalar_t }}, top: {{ scalar_t }}, near: {{ scalar_t }}, far: {{ scalar_t }}, ) -> 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( {{ col_t }}::new(a, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, b, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, c, 0.0), {{ col_t }}::new(tx, ty, tz, 1.0), ) } /// Creates a left-handed orthographic projection matrix with `[0,1]` depth range. /// /// Useful to map a left-handed coordinate system to the normalized device coordinates that WebGPU/Direct3D/Metal expect. #[inline] #[must_use] pub fn orthographic_lh( left: {{ scalar_t }}, right: {{ scalar_t }}, bottom: {{ scalar_t }}, top: {{ scalar_t }}, near: {{ scalar_t }}, far: {{ scalar_t }}, ) -> Self { let rcp_width = 1.0 / (right - left); let rcp_height = 1.0 / (top - bottom); let r = 1.0 / (far - near); Self::from_cols( {{ col_t }}::new(rcp_width + rcp_width, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, rcp_height + rcp_height, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, r, 0.0), {{ col_t }}::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. /// /// Useful to map a right-handed coordinate system to the normalized device coordinates that WebGPU/Direct3D/Metal expect. #[inline] #[must_use] pub fn orthographic_rh( left: {{ scalar_t }}, right: {{ scalar_t }}, bottom: {{ scalar_t }}, top: {{ scalar_t }}, near: {{ scalar_t }}, far: {{ scalar_t }}, ) -> Self { let rcp_width = 1.0 / (right - left); let rcp_height = 1.0 / (top - bottom); let r = 1.0 / (near - far); Self::from_cols( {{ col_t }}::new(rcp_width + rcp_width, 0.0, 0.0, 0.0), {{ col_t }}::new(0.0, rcp_height + rcp_height, 0.0, 0.0), {{ col_t }}::new(0.0, 0.0, r, 0.0), {{ col_t }}::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] #[must_use] pub fn project_point3(&self, rhs: {{ vec3_t }}) -> {{ vec3_t }} { 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.div(res.w); 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 perspective 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] #[must_use] pub fn transform_point3(&self, rhs: {{ vec3_t }}) -> {{ vec3_t }} { glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::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] #[must_use] pub fn transform_vector3(&self, rhs: {{ vec3_t }}) -> {{ vec3_t }} { glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::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() } {% endif %} {% if self_t == "Mat4" %} /// Transforms the given [`Vec3A`] as a 3D point, applying perspective correction. /// /// This is the equivalent of multiplying the [`Vec3A`] 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] #[must_use] pub fn project_point3a(&self, rhs: Vec3A) -> Vec3A { {% if is_scalar %} self.project_point3(rhs.into()).into() {% else %} 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 = res.div(res.wwww()); Vec3A::from_vec4(res) {% endif %} } /// 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] #[must_use] pub fn transform_point3a(&self, rhs: Vec3A) -> Vec3A { {% if is_scalar %} self.transform_point3(rhs.into()).into() {% else %} glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::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); Vec3A::from_vec4(res) {% endif %} } /// 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] #[must_use] pub fn transform_vector3a(&self, rhs: Vec3A) -> Vec3A { {% if is_scalar %} self.transform_vector3(rhs.into()).into() {% else %} glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::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); Vec3A::from_vec4(res) {% endif %} } {% endif %} /// Transforms a {{ dim }}D vector. #[inline] #[must_use] pub fn mul_vec{{ dim }}(&self, rhs: {{ vecn_t }}) -> {{ vecn_t }} { {% if self_t == "Mat2" and is_sse2 %} unsafe { use core::mem::MaybeUninit; use crate::Align16; let abcd = self.0; let xxyy = _mm_set_ps(rhs.y, rhs.y, rhs.x, rhs.x); let axbxcydy = _mm_mul_ps(abcd, xxyy); let cydyaxbx = _mm_shuffle_ps(axbxcydy, axbxcydy, 0b01_00_11_10); let result = _mm_add_ps(axbxcydy, cydyaxbx); let mut out: MaybeUninit> = MaybeUninit::uninit(); _mm_store_ps(out.as_mut_ptr().cast(), result); out.assume_init().0 } {% elif self_t == "Mat2" and is_wasm32 %} use core::mem::MaybeUninit; let abcd = self.0; let xxyy = f32x4(rhs.x, rhs.x, rhs.y, rhs.y); let axbxcydy = f32x4_mul(abcd, xxyy); let cydyaxbx = i32x4_shuffle::<2, 3, 4, 5>(axbxcydy, axbxcydy); let result = f32x4_add(axbxcydy, cydyaxbx); let mut out: MaybeUninit = MaybeUninit::uninit(); unsafe { v128_store(out.as_mut_ptr(), result); *(&out.assume_init() as *const v128 as *const Vec2) } {% elif self_t == "Mat2" and is_coresimd %} let abcd = self.0; let xxyy = f32x4::from_array([rhs.x, rhs.x, rhs.y, rhs.y]); let axbxcydy = abcd.mul(xxyy); let cydyaxbx = simd_swizzle!(axbxcydy, [2, 3, 0, 1]); let result = axbxcydy.add(cydyaxbx); unsafe { *(&result as *const f32x4 as *const Vec2) } {% elif self_t == "Mat2" and is_neon %} unsafe { let abcd = self.0; let xxyy = vld1q_f32([rhs.x, rhs.x, rhs.y, rhs.y].as_ptr()); let axbxcydy = vmulq_f32(abcd, xxyy); // let cydyaxbx = simd_swizzle!(axbxcydy, [2, 3, 0, 1]); let cydyaxbx = vextq_f32(axbxcydy, axbxcydy, 2); let result = vaddq_f32(axbxcydy, cydyaxbx); *(&result as *const float32x4_t as *const Vec2) } {% elif dim == 2 %} #[allow(clippy::suspicious_operation_groupings)] {{ col_t }}::new( (self.x_axis.x * rhs.x) + (self.y_axis.x * rhs.y), (self.x_axis.y * rhs.x) + (self.y_axis.y * rhs.y), ) {% elif self_t == "Mat3A" %} {# use the Vec3A implementation #} self.mul_vec3a(rhs.into()).into() {% elif dim == 3 %} let mut res = self.x_axis.mul(rhs.x); res = res.add(self.y_axis.mul(rhs.y)); res = res.add(self.z_axis.mul(rhs.z)); res {% elif dim == 4 %} {% if is_scalar %} let mut res = self.x_axis.mul(rhs.x); res = res.add(self.y_axis.mul(rhs.y)); res = res.add(self.z_axis.mul(rhs.z)); res = res.add(self.w_axis.mul(rhs.w)); res {% else %} {# use swizzles if simd #} 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 {% endif %} {% endif %} } {% if self_t == "Mat3" %} /// Transforms a [`Vec3A`]. #[inline] #[must_use] pub fn mul_vec3a(&self, rhs: Vec3A) -> Vec3A { self.mul_vec3(rhs.into()).into() } {% elif self_t == "Mat3A" %} /// Transforms a [`Vec3A`]. #[inline] #[must_use] pub fn mul_vec3a(&self, rhs: Vec3A) -> Vec3A { let mut res = self.x_axis.mul(rhs.xxx()); res = res.add(self.y_axis.mul(rhs.yyy())); res = res.add(self.z_axis.mul(rhs.zzz())); res } {% endif %} /// Multiplies two {{ nxn }} matrices. #[inline] #[must_use] pub fn mul_mat{{ dim }}(&self, rhs: &Self) -> Self { self.mul(rhs) } /// Adds two {{ nxn }} matrices. #[inline] #[must_use] pub fn add_mat{{ dim }}(&self, rhs: &Self) -> Self { self.add(rhs) } /// Subtracts two {{ nxn }} matrices. #[inline] #[must_use] pub fn sub_mat{{ dim }}(&self, rhs: &Self) -> Self { self.sub(rhs) } /// Multiplies a {{ nxn }} matrix by a scalar. #[inline] #[must_use] pub fn mul_scalar(&self, rhs: {{ scalar_t }}) -> Self { {% if self_t == "Mat2" and is_sse2 %} Self(unsafe { _mm_mul_ps(self.0, _mm_set_ps1(rhs)) }) {% elif self_t == "Mat2" and is_wasm32 %} Self(f32x4_mul(self.0, f32x4_splat(rhs))) {% elif self_t == "Mat2" and is_coresimd %} Self(self.0 * f32x4::splat(rhs)) {% elif self_t == "Mat2" and is_neon %} Self(unsafe { vmulq_f32(self.0, vld1q_dup_f32(&rhs)) }) {% else %} Self::from_cols( {% for axis in axes %} self.{{ axis }}.mul(rhs), {%- endfor %} ) {% endif %} } /// Divides a {{ nxn }} matrix by a scalar. #[inline] #[must_use] pub fn div_scalar(&self, rhs: {{ scalar_t }}) -> Self { {% if self_t == "Mat2" and is_sse2 %} Self(unsafe { _mm_div_ps(self.0, _mm_set_ps1(rhs)) }) {% elif self_t == "Mat2" and is_wasm32 %} Self(f32x4_div(self.0, f32x4_splat(rhs))) {% elif self_t == "Mat2" and is_coresimd %} Self(self.0 / f32x4::splat(rhs)) {% elif self_t == "Mat2" and is_neon %} Self(unsafe { vdivq_f32(self.0, vld1q_dup_f32(&rhs)) }) {% else %} let rhs = {{ col_t }}::splat(rhs); Self::from_cols( {% for axis in axes %} self.{{ axis }}.div(rhs), {%- endfor %} ) {% endif %} } /// 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] #[must_use] pub fn abs_diff_eq(&self, rhs: Self, max_abs_diff: {{ scalar_t }}) -> bool { {% for axis in axes %} self.{{ axis }}.abs_diff_eq(rhs.{{ axis }}, max_abs_diff) {% if not loop.last %} && {% endif %} {% endfor %} } /// Takes the absolute value of each element in `self` #[inline] #[must_use] pub fn abs(&self) -> Self { Self::from_cols( {% for axis in axes %} self.{{ axis }}.abs(), {% endfor %} ) } {% if scalar_t == "f32" %} #[inline] pub fn as_dmat{{ dim }}(&self) -> DMat{{ dim }} { DMat{{ dim }}::from_cols( {% for axis in axes %} self.{{ axis }}.as_dvec{{ dim }}(), {% endfor %} ) } {% elif scalar_t == "f64" %} #[inline] pub fn as_mat{{ dim }}(&self) -> Mat{{ dim }} { Mat{{ dim }}::from_cols( {% for axis in axes %} self.{{ axis }}.as_vec{{ dim }}(), {% endfor %} ) } {% endif %} } impl Default for {{ self_t }} { #[inline] fn default() -> Self { Self::IDENTITY } } impl Add for {{ self_t }} { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self { {% if self_t == "Mat2" and is_sse2 %} Self(unsafe { _mm_add_ps(self.0, rhs.0) }) {% elif self_t == "Mat2" and is_wasm32 %} Self(f32x4_add(self.0, rhs.0)) {% elif self_t == "Mat2" and is_coresimd %} Self(self.0 + rhs.0) {% elif self_t == "Mat2" and is_neon %} Self(unsafe { vaddq_f32(self.0, rhs.0) }) {% else %} Self::from_cols( {% for axis in axes %} self.{{ axis }}.add(rhs.{{ axis }}), {%- endfor %} ) {% endif %} } } {{ macros::impl_ref_binop(imp="Add", self_t=self_t, rhs_t=self_t, output_t=self_t) }} {{ macros::impl_op_assign(imp="Add", self_t=self_t, rhs_t=self_t) }} impl Sub for {{ self_t }} { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self { {% if self_t == "Mat2" and is_sse2 %} Self(unsafe { _mm_sub_ps(self.0, rhs.0) }) {% elif self_t == "Mat2" and is_wasm32 %} Self(f32x4_sub(self.0, rhs.0)) {% elif self_t == "Mat2" and is_coresimd %} Self(self.0 - rhs.0) {% elif self_t == "Mat2" and is_neon %} Self(unsafe { vsubq_f32(self.0, rhs.0) }) {% else %} Self::from_cols( {% for axis in axes %} self.{{ axis }}.sub(rhs.{{ axis }}), {%- endfor %} ) {% endif %} } } {{ macros::impl_ref_binop(imp="Sub", self_t=self_t, rhs_t=self_t, output_t=self_t) }} {{ macros::impl_op_assign(imp="Sub", self_t=self_t, rhs_t=self_t) }} impl Neg for {{ self_t }} { type Output = Self; #[inline] fn neg(self) -> Self::Output { {% if self_t == "Mat2" and is_sse2 %} Self(unsafe { _mm_xor_ps(self.0, _mm_set1_ps(-0.0)) }) {% elif self_t == "Mat2" and is_wasm32 %} Self(f32x4_neg(self.0)) {% elif self_t == "Mat2" and is_coresimd %} Self(-self.0) {% elif self_t == "Mat2" and is_neon %} Self(unsafe { vnegq_f32(self.0) }) {% else %} Self::from_cols( {% for axis in axes %} self.{{ axis }}.neg(), {%- endfor %} ) {% endif %} } } {{ macros::impl_ref_unop(imp="Neg", self_t=self_t, output_t=self_t) }} impl Mul for {{ self_t }} { type Output = Self; #[inline] fn mul(self, rhs: Self) -> Self { {% if self_t == "Mat2" and is_sse2 %} unsafe { let abcd = self.0; let rhs = rhs.0; let xxyy0 = _mm_shuffle_ps(rhs, rhs, 0b01_01_00_00); let xxyy1 = _mm_shuffle_ps(rhs, rhs, 0b11_11_10_10); let axbxcydy0 = _mm_mul_ps(abcd, xxyy0); let axbxcydy1 = _mm_mul_ps(abcd, xxyy1); let cydyaxbx0 = _mm_shuffle_ps(axbxcydy0, axbxcydy0, 0b01_00_11_10); let cydyaxbx1 = _mm_shuffle_ps(axbxcydy1, axbxcydy1, 0b01_00_11_10); let result0 = _mm_add_ps(axbxcydy0, cydyaxbx0); let result1 = _mm_add_ps(axbxcydy1, cydyaxbx1); Self(_mm_shuffle_ps(result0, result1, 0b01_00_01_00)) } {% elif self_t == "Mat2" and is_wasm32 %} let abcd = self.0; let rhs = rhs.0; let xxyy0 = i32x4_shuffle::<0, 0, 5, 5>(rhs, rhs); let xxyy1 = i32x4_shuffle::<2, 2, 7, 7>(rhs, rhs); let axbxcydy0 = f32x4_mul(abcd, xxyy0); let axbxcydy1 = f32x4_mul(abcd, xxyy1); let cydyaxbx0 = i32x4_shuffle::<2, 3, 4, 5>(axbxcydy0, axbxcydy0); let cydyaxbx1 = i32x4_shuffle::<2, 3, 4, 5>(axbxcydy1, axbxcydy1); let result0 = f32x4_add(axbxcydy0, cydyaxbx0); let result1 = f32x4_add(axbxcydy1, cydyaxbx1); Self(i32x4_shuffle::<0, 1, 4, 5>(result0, result1)) {% elif self_t == "Mat2" and is_coresimd %} let abcd = self.0; let xxyy0 = simd_swizzle!(rhs.0, [0, 0, 1, 1]); let xxyy1 = simd_swizzle!(rhs.0, [2, 2, 3, 3]); let axbxcydy0 = abcd * xxyy0; let axbxcydy1 = abcd * xxyy1; let cydyaxbx0 = simd_swizzle!(axbxcydy0, [2, 3, 0, 1]); let cydyaxbx1 = simd_swizzle!(axbxcydy1, [2, 3, 0, 1]); let result0 = axbxcydy0 + cydyaxbx0; let result1 = axbxcydy1 + cydyaxbx1; Self(simd_swizzle!(result0, result1, [0, 1, 4, 5])) {% elif self_t == "Mat2" and is_neon %} unsafe { let abcd = self.0; let xxyy0 = vzip1q_f32(rhs.0, rhs.0); let xxyy1 = vzip2q_f32(rhs.0, rhs.0); let axbxcydy0 = vmulq_f32(abcd, xxyy0); let axbxcydy1 = vmulq_f32(abcd, xxyy1); let cydyaxbx0 = vextq_f32(axbxcydy0, axbxcydy0, 2); let cydyaxbx1 = vextq_f32(axbxcydy1, axbxcydy1, 2); let result0 = vaddq_f32(axbxcydy0, cydyaxbx0); let result1 = vaddq_f32(axbxcydy1, cydyaxbx1); Self(vreinterpretq_f32_u64( vsetq_lane_u64(vgetq_lane_u64(vreinterpretq_u64_f32(result1), 0), vreinterpretq_u64_f32(result0), 1))) } {% else %} Self::from_cols( {% for axis in axes %} self.mul(rhs.{{ axis }}), {%- endfor %} ) {% endif %} } } {{ macros::impl_ref_binop(imp="Mul", self_t=self_t, rhs_t=self_t, output_t=self_t) }} {{ macros::impl_op_assign(imp="Mul", self_t=self_t, rhs_t=self_t) }} impl Mul<{{ col_t }}> for {{ self_t }} { type Output = {{ col_t }}; #[inline] fn mul(self, rhs: {{ col_t }}) -> Self::Output { {% if self_t == "Mat3A" %} self.mul_vec3a(rhs) {% else %} self.mul_vec{{ dim }}(rhs) {% endif %} } } {{ macros::impl_ref_binop(imp="Mul", self_t=self_t, rhs_t=col_t, output_t=col_t) }} impl Mul<{{ self_t }}> for {{ scalar_t }} { type Output = {{ self_t }}; #[inline] fn mul(self, rhs: {{ self_t }}) -> Self::Output { rhs.mul_scalar(self) } } {{ macros::impl_ref_binop(imp="Mul", self_t=scalar_t, rhs_t=self_t, output_t=self_t) }} impl Mul<{{ scalar_t }}> for {{ self_t }} { type Output = Self; #[inline] fn mul(self, rhs: {{ scalar_t }}) -> Self { self.mul_scalar(rhs) } } {{ macros::impl_ref_binop(imp="Mul", self_t=self_t, rhs_t=scalar_t, output_t=self_t) }} {{ macros::impl_op_assign(imp="Mul", self_t=self_t, rhs_t=scalar_t) }} impl Div<{{ self_t }}> for {{ scalar_t }} { type Output = {{ self_t }}; #[inline] fn div(self, rhs: {{ self_t }}) -> Self::Output { rhs.div_scalar(self) } } {{ macros::impl_ref_binop(imp="Div", self_t=scalar_t, rhs_t=self_t, output_t=self_t) }} impl Div<{{ scalar_t }}> for {{ self_t }} { type Output = Self; #[inline] fn div(self, rhs: {{ scalar_t }}) -> Self { self.div_scalar(rhs) } } {{ macros::impl_ref_binop(imp="Div", self_t=self_t, rhs_t=scalar_t, output_t=self_t) }} {{ macros::impl_op_assign(imp="Div", self_t=self_t, rhs_t=scalar_t) }} {% if self_t == "Mat3" %} impl Mul for Mat3 { type Output = Vec3A; #[inline] fn mul(self, rhs: Vec3A) -> Vec3A { self.mul_vec3a(rhs) } } {{ macros::impl_ref_binop(imp="Mul", self_t=self_t, rhs_t="Vec3A", output_t="Vec3A") }} impl From for Mat3 { #[inline] fn from(m: Mat3A) -> Self { Self { x_axis: m.x_axis.into(), y_axis: m.y_axis.into(), z_axis: m.z_axis.into(), } } } {% elif self_t == "Mat3A" %} impl Mul for Mat3A { type Output = Vec3; #[inline] fn mul(self, rhs: Vec3) -> Vec3 { self.mul_vec3a(rhs.into()).into() } } {{ macros::impl_ref_binop(imp="Mul", self_t=self_t, rhs_t="Vec3", output_t="Vec3") }} impl From for Mat3A { #[inline] fn from(m: Mat3) -> Self { Self { x_axis: m.x_axis.into(), y_axis: m.y_axis.into(), z_axis: m.z_axis.into(), } } } {% endif %} impl Sum for {{ self_t }} { fn sum(iter: I) -> Self where I: Iterator, { iter.fold(Self::ZERO, Self::add) } } impl<'a> Sum<&'a Self> for {{ self_t }} { fn sum(iter: I) -> Self where I: Iterator, { iter.fold(Self::ZERO, |a, &b| Self::add(a, b)) } } impl Product for {{ self_t }} { fn product(iter: I) -> Self where I: Iterator, { iter.fold(Self::IDENTITY, Self::mul) } } impl<'a> Product<&'a Self> for {{ self_t }} { fn product(iter: I) -> Self where I: Iterator, { iter.fold(Self::IDENTITY, |a, &b| Self::mul(a, b)) } } impl PartialEq for {{ self_t }} { #[inline] fn eq(&self, rhs: &Self) -> bool { {% for axis in axes %} self.{{ axis }}.eq(&rhs.{{ axis }}) {% if not loop.last %} && {% endif %} {% endfor %} } } {% if not is_align %} impl AsRef<[{{ scalar_t }}; {{ size }}]> for {{ self_t }} { #[inline] fn as_ref(&self) -> &[{{ scalar_t }}; {{ size }}] { unsafe { &*(self as *const Self as *const [{{ scalar_t }}; {{ size }}]) } } } impl AsMut<[{{ scalar_t }}; {{ size }}]> for {{ self_t }} { #[inline] fn as_mut(&mut self) -> &mut [{{ scalar_t }}; {{ size }}] { unsafe { &mut *(self as *mut Self as *mut [{{ scalar_t }}; {{ size }}]) } } } {% endif %} {% if self_t == "Mat2" and not is_scalar %} impl core::ops::Deref for Mat2 { type Target = crate::deref::Cols2; #[inline] fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } } } impl core::ops::DerefMut for Mat2 { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *(self as *mut Self as *mut Self::Target) } } } {% endif %} impl fmt::Debug for {{ self_t }} { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct(stringify!({{ self_t }})) {% for axis in axes %} .field("{{ axis }}", &self.{{ axis }}) {% endfor %} .finish() } } impl fmt::Display for {{ self_t }} { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(p) = f.precision() { {% if dim == 2 %} write!(f, "[{:.*}, {:.*}]", p, self.x_axis, p, self.y_axis) {% elif dim == 3 %} write!(f, "[{:.*}, {:.*}, {:.*}]", p, self.x_axis, p, self.y_axis, p, self.z_axis) {% elif dim == 4 %} write!(f, "[{:.*}, {:.*}, {:.*}, {:.*}]", p, self.x_axis, p, self.y_axis, p, self.z_axis, p, self.w_axis) {% endif %} } else { {% if dim == 2 %} write!(f, "[{}, {}]", self.x_axis, self.y_axis) {% elif dim == 3 %} write!(f, "[{}, {}, {}]", self.x_axis, self.y_axis, self.z_axis) {% elif dim == 4 %} write!(f, "[{}, {}, {}, {}]", self.x_axis, self.y_axis, self.z_axis, self.w_axis) {% endif %} } } }