2870 lines
104 KiB
Text
2870 lines
104 KiB
Text
{% 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 <https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml>
|
|
#[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 <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml>
|
|
#[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
|
|
/// <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml>
|
|
///
|
|
/// 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<Align16<Vec2>> = 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<v128> = 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<Vec3A> 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<Mat3A> 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<Vec3> 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<Mat3> 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<Self> for {{ self_t }} {
|
|
fn sum<I>(iter: I) -> Self
|
|
where
|
|
I: Iterator<Item = Self>,
|
|
{
|
|
iter.fold(Self::ZERO, Self::add)
|
|
}
|
|
}
|
|
|
|
impl<'a> Sum<&'a Self> for {{ self_t }} {
|
|
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 {{ self_t }} {
|
|
fn product<I>(iter: I) -> Self
|
|
where
|
|
I: Iterator<Item = Self>,
|
|
{
|
|
iter.fold(Self::IDENTITY, Self::mul)
|
|
}
|
|
}
|
|
|
|
impl<'a> Product<&'a Self> for {{ self_t }} {
|
|
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 {{ 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<Vec2>;
|
|
#[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 %}
|
|
}
|
|
}
|
|
}
|
|
|