Repository: bitshifter/glam-rs
Pull request #165 (2021-04-15 11:44:56)

Rename Affine3D to Affine3, add DAffine3 for f64
Most internals migrated to use core module to reduce some code duplication and for f64 support.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 145e492..eec8928 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,9 @@ The format is based on [Keep a Changelog], and this project adheres to
 
 ### Added
 
-* Added `Affine3D`: an affine 3D transform.
+* Added 3D affine transform types `Affine3` and `DAffine3`. These are more
+  efficient than using `Mat4` and `DMat4` respectively when working with affine
+  transforms.
 
 ## [0.14.0] - 2021-04-09
 
diff --git a/Cargo.toml b/Cargo.toml
index 4820952..3c12cc9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -62,7 +62,7 @@ name = "mat3"
 harness = false
 
 [[bench]]
-name = "affine3d"
+name = "affine3"
 harness = false
 
 [[bench]]
diff --git a/benches/affine3.rs b/benches/affine3.rs
new file mode 100644
index 0000000..301e24a
--- /dev/null
+++ b/benches/affine3.rs
@@ -0,0 +1,71 @@
+#[path = "support/macros.rs"]
+#[macro_use]
+mod macros;
+mod support;
+
+use criterion::{criterion_group, criterion_main, Criterion};
+use glam::Affine3;
+use std::ops::Mul;
+use support::*;
+
+bench_unop!(affine3_inverse, "affine3 inverse", op => inverse, from => random_srt_affine3);
+bench_binop!(
+    affine3_transform_point3,
+    "affine3 transform point3",
+    op => transform_point3,
+    from1 => random_srt_affine3,
+    from2 => random_vec3
+);
+
+bench_binop!(
+    affine3_transform_vector3,
+    "affine3 transform vector3",
+    op => transform_vector3,
+    from1 => random_srt_affine3,
+    from2 => random_vec3
+);
+bench_binop!(affine3_mul_affine3, "affine3 mul affine3", op => mul, from => random_srt_affine3);
+bench_binop!(affine3_mul_mat4, "affine3 mul mat4", op => mul, from1 => random_srt_affine3, from2 => random_srt_mat4);
+bench_binop!(mat4_mul_affine3, "mat4 mul affine3", op => mul, from1 => random_srt_mat4, from2 => random_srt_affine3);
+
+pub fn affine3_from_srt(c: &mut Criterion) {
+    use glam::{Quat, Vec3};
+    const SIZE: usize = 1 << 13;
+    let mut rng = support::PCG32::default();
+    let inputs = criterion::black_box(
+        (0..SIZE)
+            .map(|_| {
+                (
+                    random_nonzero_vec3(&mut rng),
+                    random_quat(&mut rng),
+                    random_vec3(&mut rng),
+                )
+            })
+            .collect::<Vec<(Vec3, Quat, Vec3)>>(),
+    );
+    let mut outputs = vec![Affine3::default(); SIZE];
+    let mut i = 0;
+    c.bench_function("affine3 from srt", |b| {
+        b.iter(|| {
+            i = (i + 1) & (SIZE - 1);
+            unsafe {
+                let data = inputs.get_unchecked(i);
+                *outputs.get_unchecked_mut(i) =
+                    Affine3::from_scale_rotation_translation(data.0, data.1, data.2)
+            }
+        })
+    });
+}
+
+criterion_group!(
+    benches,
+    affine3_inverse,
+    affine3_transform_point3,
+    affine3_transform_vector3,
+    affine3_mul_affine3,
+    affine3_mul_mat4,
+    mat4_mul_affine3,
+    affine3_from_srt,
+);
+
+criterion_main!(benches);
diff --git a/benches/affine3d.rs b/benches/affine3d.rs
deleted file mode 100644
index c3866fe..0000000
--- a/benches/affine3d.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-#[path = "support/macros.rs"]
-#[macro_use]
-mod macros;
-mod support;
-
-use criterion::{criterion_group, criterion_main, Criterion};
-use glam::Affine3D;
-use std::ops::Mul;
-use support::*;
-
-bench_unop!(affine3d_inverse, "affine3d inverse", op => inverse, from => random_srt_affine3d);
-bench_binop!(
-    affine3d_transform_point3,
-    "affine3d transform point3",
-    op => transform_point3,
-    from1 => random_srt_affine3d,
-    from2 => random_vec3
-);
-
-bench_binop!(
-    affine3d_transform_vector3,
-    "affine3d transform vector3",
-    op => transform_vector3,
-    from1 => random_srt_affine3d,
-    from2 => random_vec3
-);
-bench_binop!(affine3d_mul_affine3d, "affine3d mul affine3d", op => mul, from => random_srt_affine3d);
-bench_binop!(affine3d_mul_mat4, "affine3d mul mat4", op => mul, from1 => random_srt_affine3d, from2 => random_srt_mat4);
-bench_binop!(mat4_mul_affine3d, "mat4 mul affine3d", op => mul, from1 => random_srt_mat4, from2 => random_srt_affine3d);
-
-pub fn affine3d_from_srt(c: &mut Criterion) {
-    use glam::{Quat, Vec3};
-    const SIZE: usize = 1 << 13;
-    let mut rng = support::PCG32::default();
-    let inputs = criterion::black_box(
-        (0..SIZE)
-            .map(|_| {
-                (
-                    random_nonzero_vec3(&mut rng),
-                    random_quat(&mut rng),
-                    random_vec3(&mut rng),
-                )
-            })
-            .collect::<Vec<(Vec3, Quat, Vec3)>>(),
-    );
-    let mut outputs = vec![Affine3D::default(); SIZE];
-    let mut i = 0;
-    c.bench_function("affine3d from srt", |b| {
-        b.iter(|| {
-            i = (i + 1) & (SIZE - 1);
-            unsafe {
-                let data = inputs.get_unchecked(i);
-                *outputs.get_unchecked_mut(i) =
-                    Affine3D::from_scale_rotation_translation(data.0, data.1, data.2)
-            }
-        })
-    });
-}
-
-criterion_group!(
-    benches,
-    affine3d_inverse,
-    affine3d_transform_point3,
-    affine3d_transform_vector3,
-    affine3d_mul_affine3d,
-    affine3d_mul_mat4,
-    mat4_mul_affine3d,
-    affine3d_from_srt,
-);
-
-criterion_main!(benches);
diff --git a/benches/support/mod.rs b/benches/support/mod.rs
index f2fc35e..e4b1d78 100644
--- a/benches/support/mod.rs
+++ b/benches/support/mod.rs
@@ -1,6 +1,6 @@
 #![allow(dead_code)]
 use core::f32;
-use glam::{Affine3D, Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4};
+use glam::{Affine3, Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4};
 
 pub struct PCG32 {
     state: u64,
@@ -108,8 +108,8 @@ pub fn random_srt_mat3(rng: &mut PCG32) -> Mat3 {
     )
 }
 
-pub fn random_srt_affine3d(rng: &mut PCG32) -> Affine3D {
-    Affine3D::from_scale_rotation_translation(
+pub fn random_srt_affine3(rng: &mut PCG32) -> Affine3 {
+    Affine3::from_scale_rotation_translation(
         random_nonzero_vec3(rng),
         random_quat(rng),
         random_vec3(rng),
diff --git a/benches/vec2.rs b/benches/vec2.rs
index 66f3f02..a334e5d 100644
--- a/benches/vec2.rs
+++ b/benches/vec2.rs
@@ -5,6 +5,7 @@ mod support;
 
 use criterion::{criterion_group, criterion_main, Criterion};
 use glam::Vec2;
+use std::ops::Mul;
 use support::*;
 
 euler!(
@@ -15,6 +16,14 @@ euler!(
     zero => Vec2::ZERO,
     rand => random_vec2);
 
+bench_binop!(
+    vec2_mul_vec2,
+    "vec2 mul vec2",
+    op => mul,
+    from1 => random_vec2,
+    from2 => random_vec2
+);
+
 bench_binop!(
     vec2_angle_between,
     "vec2 angle_between",
@@ -31,6 +40,12 @@ bench_select!(
     from => random_vec2
 );
 
-criterion_group!(benches, vec2_euler, vec2_select, vec2_angle_between);
+criterion_group!(
+    benches,
+    vec2_mul_vec2,
+    vec2_euler,
+    vec2_select,
+    vec2_angle_between
+);
 
 criterion_main!(benches);
diff --git a/src/affine3.rs b/src/affine3.rs
new file mode 100644
index 0000000..d9b2ff6
--- /dev/null
+++ b/src/affine3.rs
@@ -0,0 +1,527 @@
+use crate::core::{
+    storage::{Columns3, Columns4, XYZ},
+    traits::{
+        matrix::{FloatMatrix3x3, Matrix3x3, MatrixConst},
+        vector::{FloatVector3, SignedVector, Vector, VectorConst},
+    },
+};
+use crate::{DMat3, DMat4, DQuat, DVec3, Mat3, Mat4, Quat, Vec3};
+use core::ops::{Deref, DerefMut};
+
+#[cfg(all(
+    target_arch = "x86",
+    target_feature = "sse2",
+    not(feature = "scalar-math")
+))]
+use core::arch::x86::*;
+#[cfg(all(
+    target_arch = "x86_64",
+    target_feature = "sse2",
+    not(feature = "scalar-math")
+))]
+use core::arch::x86_64::*;
+
+#[cfg(not(feature = "std"))]
+use num_traits::Float;
+
+macro_rules! define_affine3_struct {
+    ($affine3:ident, $transform:ident, $translate:ident) => {
+        /// A 3D affine transform, which can represent translation, rotation, scaling and shear.
+        #[derive(Copy, Clone)]
+        pub struct $affine3 {
+            pub(crate) transform: $transform,
+            pub(crate) translate: $translate,
+        }
+    };
+}
+
+macro_rules! impl_affine3_methods {
+    ($t:ty, $mat3:ident, $mat4:ident, $quat:ident, $vec3:ident, $affine3:ident, $transform:ident, $translate:ident) => {
+        impl $affine3 {
+            /// The degenerate zero transform.
+            ///
+            /// This transforms any finite vector and point to zero.
+            /// The zero transform is non-invertible.
+            pub const ZERO: Self = Self {
+                transform: $transform::ZERO,
+                translate: VectorConst::ZERO,
+            };
+
+            /// The identity transform.
+            ///
+            /// Multiplying a vector with this returns the same vector.
+            pub const IDENTITY: Self = Self {
+                transform: $transform::IDENTITY,
+                translate: VectorConst::ZERO,
+            };
+
+            /// Creates an affine transform that changes scale.
+            /// Note that if any scale is zero the transform will be non-invertible.
+            #[inline(always)]
+            pub fn from_scale(scale: $vec3) -> Self {
+                Self {
+                    transform: $transform::from_diagonal(scale.0),
+                    translate: VectorConst::ZERO,
+                }
+            }
+
+            /// Creates an affine transform from the given `rotation` quaternion.
+            #[inline(always)]
+            pub fn from_quat(rotation: $quat) -> Self {
+                Self {
+                    // TODO: unnecessary into
+                    transform: $transform::from_quaternion(rotation.0.into()),
+                    translate: VectorConst::ZERO,
+                }
+            }
+
+            /// Creates an affine transform containing a 3D rotation around a normalized
+            /// rotation `axis` of `angle` (in radians).
+            #[inline(always)]
+            pub fn from_axis_angle(axis: $vec3, angle: $t) -> Self {
+                Self {
+                    transform: $transform::from_axis_angle(axis.0, angle),
+                    translate: VectorConst::ZERO,
+                }
+            }
+
+            /// Creates an affine transform containing a 3D rotation around the x axis of
+            /// `angle` (in radians).
+            #[inline(always)]
+            pub fn from_rotation_x(angle: $t) -> Self {
+                Self {
+                    transform: $transform::from_rotation_x(angle),
+                    translate: VectorConst::ZERO,
+                }
+            }
+
+            /// Creates an affine transform containing a 3D rotation around the y axis of
+            /// `angle` (in radians).
+            #[inline]
+            pub fn from_rotation_y(angle: $t) -> Self {
+                Self {
+                    transform: $transform::from_rotation_y(angle),
+                    translate: VectorConst::ZERO,
+                }
+            }
+
+            /// Creates an affine transform containing a 3D rotation around the z axis of
+            /// `angle` (in radians).
+            #[inline]
+            pub fn from_rotation_z(angle: $t) -> Self {
+                Self {
+                    transform: $transform::from_rotation_z(angle),
+                    translate: VectorConst::ZERO,
+                }
+            }
+
+            /// Creates an affine transformation from the given 3D `translation`.
+            #[inline(always)]
+            pub fn from_translation(translation: $vec3) -> Self {
+                Self {
+                    transform: $transform::IDENTITY,
+                    translate: translation.0.into(),
+                }
+            }
+
+            /// Creates an affine transform from a 3x3 matrix (expressing scale, shear and
+            /// rotation)
+            #[inline(always)]
+            pub fn from_mat3(mat3: $mat3) -> Self {
+                Self {
+                    transform: mat3.0.into(),
+                    translate: VectorConst::ZERO,
+                }
+            }
+
+            /// Creates an affine transform from a 3x3 matrix (expressing scale, shear and rotation)
+            /// and a translation vector.
+            ///
+            /// Equivalent to `Affine3::from_translation(translation) * Affine3::from_mat3(mat3)`
+            #[inline(always)]
+            pub fn from_mat3_translation(mat3: $mat3, translation: $vec3) -> Self {
+                Self {
+                    transform: mat3.0.into(),
+                    translate: translation.0.into(),
+                }
+            }
+
+            /// Creates an affine transform from the given 3D `scale`, `rotation` and
+            /// `translation`.
+            ///
+            /// Equivalent to `Affine3::from_translation(translation) *
+            /// Affine3::from_quat(rotation) * Affine3::from_scale(scale)`
+            #[inline(always)]
+            pub fn from_scale_rotation_translation(
+                scale: $vec3,
+                rotation: $quat,
+                translation: $vec3,
+            ) -> Self {
+                let rotation = $transform::from_quaternion(rotation.0.into());
+                Self {
+                    transform: $transform::from_cols(
+                        rotation.x_axis.mul_scalar(scale.x),
+                        rotation.y_axis.mul_scalar(scale.y),
+                        rotation.z_axis.mul_scalar(scale.z),
+                    ),
+                    translate: translation.0.into(),
+                }
+            }
+
+            /// Creates an affine transform from the given 3D `rotation` and `translation`.
+            ///
+            /// Equivalent to `Affine3::from_translation(translation) * Affine3::from_quat(rotation)`
+            #[inline(always)]
+            pub fn from_rotation_translation(rotation: $quat, translation: $vec3) -> Self {
+                Self {
+                    transform: $transform::from_quaternion(rotation.0.into()),
+                    translate: translation.0.into(),
+                }
+            }
+
+            /// The given `Mat4` must be an affine transform,
+            /// i.e. contain no persepctive transform.
+            #[inline]
+            pub fn from_mat4(m: $mat4) -> Self {
+                Self {
+                    transform: $transform::from_cols(
+                        m.x_axis.0.into(),
+                        m.y_axis.0.into(),
+                        m.z_axis.0.into(),
+                    ),
+                    translate: m.w_axis.0.into(),
+                }
+            }
+
+            /// The translation expressed by this transform.
+            /// The translation is applied last, so is separatable from scale, shear and rotation.
+            #[inline(always)]
+            pub fn translation(&self) -> $vec3 {
+                $vec3(self.translate.into())
+            }
+
+            /// Set the translation part of this transform.
+            /// The translation is applied last, so is separatable from scale, shear and rotation.
+            #[inline(always)]
+            pub fn set_translation(&mut self, translation: $vec3) {
+                self.translate = translation.0.into();
+            }
+
+            /// The scale, shear and rotation expressed by this transform.
+            #[inline(always)]
+            pub fn mat3(&self) -> $mat3 {
+                $mat3(self.transform.into())
+            }
+
+            /// Set the scale, shear and rotation expressed by this transform.
+            #[inline(always)]
+            pub fn set_mat3(&mut self, m: $mat3) {
+                self.transform = m.0.into();
+            }
+
+            /// Extracts `scale`, `rotation` and `translation` from `self`.
+            ///
+            /// The transform is expected to be non-degenerate and without shearing, or the output
+            /// will be invalid.
+            #[inline(always)]
+            pub fn to_scale_rotation_translation(&self) -> ($vec3, $quat, $vec3) {
+                // TODO: migrate to core module
+                let det = self.transform.determinant();
+                glam_assert!(det != 0.0);
+
+                let scale = $vec3::new(
+                    self.transform.x_axis.length() * det.signum(),
+                    self.transform.y_axis.length(),
+                    self.transform.z_axis.length(),
+                );
+
+                glam_assert!(scale.cmpne($vec3::ZERO).all());
+
+                let inv_scale = scale.recip();
+
+                let rotation = $quat::from_rotation_mat3(&$mat3::from_cols(
+                    $vec3(self.transform.x_axis.mul_scalar(inv_scale.x).into()),
+                    $vec3(self.transform.y_axis.mul_scalar(inv_scale.y).into()),
+                    $vec3(self.transform.z_axis.mul_scalar(inv_scale.z).into()),
+                ));
+
+                (scale, rotation, self.translation())
+            }
+
+            #[inline]
+            fn look_to_lh(eye: $vec3, dir: $vec3, up: $vec3) -> Self {
+                let f = dir.normalize();
+                let s = up.cross(f).normalize();
+                let u = f.cross(s);
+                Self {
+                    transform: $transform::from_cols(
+                        $vec3::new(s.x, u.x, f.x).0.into(),
+                        $vec3::new(s.y, u.y, f.y).0.into(),
+                        $vec3::new(s.z, u.z, f.z).0.into(),
+                    ),
+                    translate: $vec3::new(-s.dot(eye), -u.dot(eye), -f.dot(eye)).0.into(),
+                }
+            }
+
+            /// Creates a left-handed view transform using a camera position, an up direction, and
+            /// a focal point.
+            ///
+            /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
+            #[inline]
+            pub fn look_at_lh(eye: $vec3, center: $vec3, up: $vec3) -> Self {
+                glam_assert!(up.is_normalized());
+                Self::look_to_lh(eye, center - eye, up)
+            }
+
+            /// Creates a right-handed view transform using a camera position, an up direction, and
+            /// a focal point.
+            ///
+            /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
+            #[inline]
+            pub fn look_at_rh(eye: $vec3, center: $vec3, up: $vec3) -> Self {
+                glam_assert!(up.is_normalized());
+                Self::look_to_lh(eye, eye - center, up)
+            }
+
+            /// Transforms the given 3D points, applying shear, scale, rotation and translation.
+            #[inline(always)]
+            pub fn transform_point3(&self, other: $vec3) -> $vec3 {
+                $vec3(
+                    (self
+                        .transform
+                        .x_axis
+                        .mul_scalar(other.0.x)
+                        .add(self.transform.y_axis.mul_scalar(other.0.y))
+                        .add(self.transform.z_axis.mul_scalar(other.0.z))
+                        .add(self.translate))
+                    .into(),
+                )
+            }
+
+            /// Transforms the give 3D vector, applying shear, scale and rotation (but NOT
+            /// translation).
+            ///
+            /// To also apply translation, use [`Self::transform_point3`] instead.
+            #[inline(always)]
+            pub fn transform_vector3(&self, other: $vec3) -> $vec3 {
+                $vec3(
+                    (self
+                        .transform
+                        .x_axis
+                        .mul_scalar(other.0.x)
+                        .add(self.transform.y_axis.mul_scalar(other.0.y))
+                        .add(self.transform.z_axis.mul_scalar(other.0.z)))
+                    .into(),
+                )
+            }
+
+            /// Returns `true` if, and only if, all elements are finite.
+            ///
+            /// If any element is either `NaN`, positive or negative infinity, this will return
+            /// `false`.
+            #[inline]
+            pub fn is_finite(&self) -> bool {
+                self.transform.is_finite() && self.translate.is_finite()
+            }
+
+            /// Returns `true` if any elements are `NaN`.
+            #[inline]
+            pub fn is_nan(&self) -> bool {
+                self.transform.is_nan() || self.translate.is_nan()
+            }
+
+            /// Returns true if the absolute difference of all elements between `self` and `other`
+            /// is less than or equal to `max_abs_diff`.
+            ///
+            /// This can be used to compare if two 3x4 matrices contain similar elements. It works
+            /// best when comparing with a known value. The `max_abs_diff` that should be used used
+            /// depends on the values being compared against.
+            ///
+            /// For more see
+            /// [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
+            #[inline]
+            pub fn abs_diff_eq(&self, other: Self, max_abs_diff: $t) -> bool {
+                self.transform.abs_diff_eq(&other.transform, max_abs_diff)
+                    && self.translate.abs_diff_eq(other.translate, max_abs_diff)
+            }
+
+            /// Return the inverse of this transform.
+            ///
+            /// Note that if the transform is not invertible the result will be invalid.
+            pub fn inverse(&self) -> Self {
+                let transform = self.transform.inverse();
+                // transform negative translation by the 3x3 inverse:
+                let translate = transform.mul_vector(self.translate).neg();
+
+                Self {
+                    transform,
+                    translate,
+                }
+            }
+        }
+    };
+}
+
+macro_rules! impl_affine3_traits {
+    ($t:ty, $mat3:ident, $mat4:ident, $vec3:ident, $vec4:ident, $affine3:ident, $transform:ident, $translate:ident, $deref:ident) => {
+        impl Default for $affine3 {
+            #[inline(always)]
+            fn default() -> Self {
+                Self::IDENTITY
+            }
+        }
+
+        impl Deref for $affine3 {
+            type Target = $deref;
+            #[inline(always)]
+            fn deref(&self) -> &Self::Target {
+                unsafe { &*(self as *const Self as *const Self::Target) }
+            }
+        }
+
+        impl DerefMut for $affine3 {
+            #[inline(always)]
+            fn deref_mut(&mut self) -> &mut Self::Target {
+                unsafe { &mut *(self as *mut Self as *mut Self::Target) }
+            }
+        }
+
+        impl PartialEq for $affine3 {
+            #[inline]
+            fn eq(&self, other: &Self) -> bool {
+                self.x_axis.eq(&other.x_axis)
+                    && self.y_axis.eq(&other.y_axis)
+                    && self.z_axis.eq(&other.z_axis)
+                    && self.w_axis.eq(&other.w_axis)
+            }
+        }
+
+        #[cfg(not(target_arch = "spirv"))]
+        impl core::fmt::Debug for $affine3 {
+            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
+                fmt.debug_struct(stringify!($affine3d))
+                    .field("transform", &$mat3(self.transform.into()))
+                    .field("translate", &$vec3(self.translate.into()))
+                    .finish()
+            }
+        }
+
+        impl From<$affine3> for $mat4 {
+            #[inline]
+            fn from(m: $affine3) -> $mat4 {
+                $mat4::from_cols(
+                    m.x_axis.extend(0.0),
+                    m.y_axis.extend(0.0),
+                    m.z_axis.extend(0.0),
+                    m.w_axis.extend(1.0),
+                )
+            }
+        }
+
+        impl core::ops::Mul for $affine3 {
+            type Output = $affine3;
+
+            #[inline(always)]
+            fn mul(self, other: $affine3) -> Self::Output {
+                Self {
+                    transform: self.transform.mul_matrix(&other.transform),
+                    translate: self
+                        .transform
+                        .mul_vector(other.translate)
+                        .add(self.translate),
+                }
+            }
+        }
+
+        impl core::ops::Mul<$mat4> for $affine3 {
+            type Output = $mat4;
+
+            #[inline(always)]
+            fn mul(self, rhs: $mat4) -> Self::Output {
+                $mat4::from(self) * rhs
+            }
+        }
+
+        impl core::ops::Mul<$affine3> for $mat4 {
+            type Output = $mat4;
+
+            #[inline(always)]
+            fn mul(self, rhs: $affine3) -> Self::Output {
+                self * $mat4::from(rhs)
+            }
+        }
+
+        impl<'a> core::iter::Product<&'a Self> for $affine3 {
+            fn product<I>(iter: I) -> Self
+            where
+                I: Iterator<Item = &'a Self>,
+            {
+                iter.fold(Self::IDENTITY, |a, &b| a * b)
+            }
+        }
+    };
+}
+
+#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+type TransformF32 = Columns3<__m128>;
+#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+type TranslateF32 = __m128;
+#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+type DerefTargetF32 = Columns4<crate::Vec3A>;
+
+#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+type TransformF32 = Columns3<XYZ<f32>>;
+#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+type TranslateF32 = XYZ<f32>;
+#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+type DerefTargetF32 = Columns4<Vec3>;
+
+define_affine3_struct!(Affine3, TransformF32, TranslateF32);
+impl_affine3_methods!(
+    f32,
+    Mat3,
+    Mat4,
+    Quat,
+    Vec3,
+    Affine3,
+    TransformF32,
+    TranslateF32
+);
+impl_affine3_traits!(
+    f32,
+    Mat3,
+    Mat4,
+    Vec3,
+    Vec4,
+    Affine3,
+    TransformF32,
+    TranslateF32,
+    DerefTargetF32
+);
+
+type TransformF64 = Columns3<XYZ<f64>>;
+type TranslateF64 = XYZ<f64>;
+type DerefTargetF64 = Columns4<DVec3>;
+
+define_affine3_struct!(DAffine3, TransformF64, TranslateF64);
+impl_affine3_methods!(
+    f64,
+    DMat3,
+    DMat4,
+    DQuat,
+    DVec3,
+    DAffine3,
+    TransformF64,
+    TranslateF64
+);
+impl_affine3_traits!(
+    f64,
+    DMat3,
+    DMat4,
+    DVec3,
+    DVec4,
+    DAffine3,
+    TransformF64,
+    TranslateF64,
+    DerefTargetF64
+);
diff --git a/src/affine3d.rs b/src/affine3d.rs
deleted file mode 100644
index d84e42f..0000000
--- a/src/affine3d.rs
+++ /dev/null
@@ -1,569 +0,0 @@
-use crate::{Mat3, Mat4, Quat, Vec3, Vec3A};
-
-#[cfg(not(feature = "std"))]
-use num_traits::Float;
-
-/// An affine transform, which can represent translation, rotation, scaling and shear.
-#[derive(Copy, Clone, PartialEq)]
-pub struct Affine3D {
-    x_col: Vec3A,
-    y_col: Vec3A,
-    z_col: Vec3A,
-    /// The translation
-    w_col: Vec3A,
-}
-
-impl Default for Affine3D {
-    #[inline(always)]
-    fn default() -> Self {
-        Self::IDENTITY
-    }
-}
-
-impl Affine3D {
-    /// The degenerate zero transform.
-    ///
-    /// This transforms any finite vector and point to zero.
-    /// The zero transform is non-invertible.
-    pub const ZERO: Self = Self {
-        x_col: Vec3A::ZERO,
-        y_col: Vec3A::ZERO,
-        z_col: Vec3A::ZERO,
-        w_col: Vec3A::ZERO,
-    };
-
-    /// The identity transform.
-    ///
-    /// Multiplying a vector with this returns the same vector.
-    pub const IDENTITY: Self = Self {
-        x_col: Vec3A::X,
-        y_col: Vec3A::Y,
-        z_col: Vec3A::Z,
-        w_col: Vec3A::ZERO,
-    };
-
-    /// Creates a transformation matrix that changes scale.
-    /// Note that if any scale is zero the transform will be non-invertible.
-    #[inline(always)]
-    pub fn from_scale(scale: Vec3) -> Self {
-        let [x, y, z] = scale.to_array();
-        Self {
-            x_col: Vec3A::new(x, 0.0, 0.0),
-            y_col: Vec3A::new(0.0, y, 0.0),
-            z_col: Vec3A::new(0.0, 0.0, z),
-            w_col: Vec3A::ZERO,
-        }
-    }
-
-    /// Creates an affine transformation matrix from the given `rotation` quaternion.
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline]
-    pub fn from_quat(rotation: Quat) -> Self {
-        glam_assert!(rotation.is_normalized());
-
-        let x = rotation.x;
-        let y = rotation.y;
-        let z = rotation.z;
-        let w = rotation.w;
-        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;
-
-        Self {
-            x_col: Vec3A::new(1.0 - (yy + zz), xy + wz, xz - wy),
-            y_col: Vec3A::new(xy - wz, 1.0 - (xx + zz), yz + wx),
-            z_col: Vec3A::new(xz + wy, yz - wx, 1.0 - (xx + yy)),
-            w_col: Vec3A::ZERO,
-        }
-    }
-
-    /// Creates an affine transformation matrix containing a 3D rotation around a normalized
-    /// rotation `axis` of `angle` (in radians).
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline]
-    pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
-        glam_assert!(axis.is_normalized());
-        let (sin, cos) = angle.sin_cos();
-        let axis_sin = axis * sin;
-        let axis_sq = axis * 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 {
-            x_col: Vec3A::new(
-                axis_sq.x * omc + cos,
-                xyomc + axis_sin.z,
-                xzomc - axis_sin.y,
-            ),
-            y_col: Vec3A::new(
-                xyomc - axis_sin.z,
-                axis_sq.y * omc + cos,
-                yzomc + axis_sin.x,
-            ),
-            z_col: Vec3A::new(
-                xzomc + axis_sin.y,
-                yzomc - axis_sin.x,
-                axis_sq.z * omc + cos,
-            ),
-            w_col: Vec3A::ZERO,
-        }
-    }
-
-    /// Creates an affine transformation matrix containing a 3D rotation around the x axis of
-    /// `angle` (in radians).
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline]
-    pub fn from_rotation_x(angle: f32) -> Self {
-        let (sina, cosa) = angle.sin_cos();
-        Self {
-            x_col: Vec3A::X,
-            y_col: Vec3A::new(0.0, cosa, sina),
-            z_col: Vec3A::new(0.0, -sina, cosa),
-            w_col: Vec3A::ZERO,
-        }
-    }
-
-    /// Creates an affine transformation matrix containing a 3D rotation around the y axis of
-    /// `angle` (in radians).
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline]
-    pub fn from_rotation_y(angle: f32) -> Self {
-        let (sina, cosa) = angle.sin_cos();
-        Self {
-            x_col: Vec3A::new(cosa, 0.0, -sina),
-            y_col: Vec3A::Y,
-            z_col: Vec3A::new(sina, 0.0, cosa),
-            w_col: Vec3A::ZERO,
-        }
-    }
-
-    /// Creates an affine transformation matrix containing a 3D rotation around the z axis of
-    /// `angle` (in radians).
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline]
-    pub fn from_rotation_z(angle: f32) -> Self {
-        let (sina, cosa) = angle.sin_cos();
-        Self {
-            x_col: Vec3A::new(cosa, sina, 0.0),
-            y_col: Vec3A::new(-sina, cosa, 0.0),
-            z_col: Vec3A::Z,
-            w_col: Vec3A::ZERO,
-        }
-    }
-
-    /// Creates an affine transformation from the given 3D `translation`.
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline(always)]
-    pub fn from_translation(translation: Vec3) -> Self {
-        Self {
-            x_col: Vec3A::X,
-            y_col: Vec3A::Y,
-            z_col: Vec3A::Z,
-            w_col: translation.into(),
-        }
-    }
-
-    /// Creates an affine transformation from a `Mat3` (expressing scale, shear and rotation)
-    #[inline(always)]
-    pub fn from_mat3(mat3: Mat3) -> Self {
-        Self {
-            x_col: mat3.x_axis.into(),
-            y_col: mat3.y_axis.into(),
-            z_col: mat3.z_axis.into(),
-            w_col: Vec3A::ZERO,
-        }
-    }
-
-    /// Creates an affine transformation from a `Mat3` (expressing scale, shear and rotation)
-    /// and a translation vector.
-    ///
-    /// Equivalent to `Affine3D::from_translation(translation) * Affine3D::from_mat3(mat3)`
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline(always)]
-    pub fn from_mat3_translation(mat3: Mat3, translation: Vec3) -> Self {
-        Self {
-            x_col: mat3.x_axis.into(),
-            y_col: mat3.y_axis.into(),
-            z_col: mat3.z_axis.into(),
-            w_col: translation.into(),
-        }
-    }
-
-    /// Creates an affine transformation from the given 3D `scale`, `rotation` and
-    /// `translation`.
-    ///
-    /// Equivalent to `Affine3D::from_translation(translation) * Affine3D::from_quat(rotation) * Affine3D::from_scale(scale)`
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline(always)]
-    pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
-        let mut m = Self::from_quat(rotation);
-        m.x_col *= scale.x;
-        m.y_col *= scale.y;
-        m.z_col *= scale.z;
-        m.w_col = translation.into();
-        m
-    }
-
-    /// Creates an affine transformation from the given 3D `rotation` and `translation`.
-    ///
-    /// Equivalent to `Affine3D::from_translation(translation) * Affine3D::from_quat(rotation)`
-    ///
-    /// The result can be used to transform 3D points and vectors.
-    /// See [`Self::transform_point3()`] and [`Self::transform_vector3()`].
-    #[inline(always)]
-    pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
-        let mut m = Self::from_quat(rotation);
-        m.w_col = translation.into();
-        m
-    }
-
-    /// The given `Mat4` must be an affine transform,
-    /// i.e. contain no persepctive transform.
-    #[inline]
-    pub fn from_mat4(m: Mat4) -> Self {
-        Self {
-            x_col: m.x_axis.into(),
-            y_col: m.y_axis.into(),
-            z_col: m.z_axis.into(),
-            w_col: m.w_axis.into(),
-        }
-    }
-
-    /// The first column.
-    #[inline(always)]
-    fn x_col(&self) -> Vec3 {
-        self.x_col.into()
-    }
-
-    /// The second column.
-    #[inline(always)]
-    fn y_col(&self) -> Vec3 {
-        self.y_col.into()
-    }
-
-    /// The thirs column.
-    #[inline(always)]
-    fn z_col(&self) -> Vec3 {
-        self.z_col.into()
-    }
-
-    /// The translation expressed by this transform.
-    /// The translation is applied last, so is separatable from scale, shear and rotation.
-    #[inline(always)]
-    pub fn translation(&self) -> Vec3 {
-        self.w_col.into()
-    }
-
-    /// Set the translation part of this transform.
-    /// The translation is applied last, so is separatable from scale, shear and rotation.
-    #[inline(always)]
-    pub fn set_translation(&mut self, translation: Vec3) {
-        self.w_col = translation.into();
-    }
-
-    /// The scale, shear and rotation expressed by this transform.
-    #[inline(always)]
-    pub fn mat3(&self) -> Mat3 {
-        Mat3::from_cols(self.x_col.into(), self.y_col.into(), self.z_col.into())
-    }
-
-    /// Set the scale, shear and rotation expressed by this transform.
-    #[inline(always)]
-    pub fn set_mat3(&mut self, m: Mat3) {
-        self.x_col = m.x_axis.into();
-        self.y_col = m.y_axis.into();
-        self.z_col = m.z_axis.into();
-    }
-
-    /// Extracts `scale`, `rotation` and `translation` from `self`.
-    ///
-    /// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.
-    #[inline(always)]
-    pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
-        let det = self.determinant();
-        glam_assert!(det != 0.0);
-
-        let x_col = self.x_col();
-        let y_col = self.y_col();
-        let z_col = self.z_col();
-
-        let scale = Vec3::new(
-            x_col.length() * det.signum(),
-            y_col.length(),
-            z_col.length(),
-        );
-
-        glam_assert!(scale.cmpne(Vec3::ZERO).all());
-
-        let inv_scale = scale.recip();
-
-        let rotation = Quat::from_rotation_mat3(&Mat3::from_cols(
-            x_col * inv_scale.x,
-            y_col * inv_scale.y,
-            z_col * inv_scale.z,
-        ));
-
-        (scale, rotation, self.translation())
-    }
-
-    /// If this is zero, or close to zero, it is a singular transform (zero-scale on at least one axis).
-    #[inline]
-    fn determinant(&self) -> f32 {
-        // As if the last row was `[0, 0, 0, 1]`.
-        let [m00, m10, m20] = self.x_col.to_array();
-        let [m01, m11, m21] = self.y_col.to_array();
-        let [m02, m12, m22] = self.z_col.to_array();
-
-        m00 * m11 * m22 - m00 * m12 * m21 - m01 * m10 * m22 + m01 * m12 * m20 + m02 * m10 * m21
-            - m02 * m11 * m20
-    }
-
-    #[inline]
-    fn look_to_lh(eye: Vec3, dir: Vec3, up: Vec3) -> Self {
-        let f = dir.normalize();
-        let s = up.cross(f).normalize();
-        let u = f.cross(s);
-        Self {
-            x_col: Vec3A::new(s.x, u.x, f.x),
-            y_col: Vec3A::new(s.y, u.y, f.y),
-            z_col: Vec3A::new(s.z, u.z, f.z),
-            w_col: Vec3A::new(-s.dot(eye), -u.dot(eye), -f.dot(eye)),
-        }
-    }
-
-    /// Creates a left-handed view transform using a camera position, an up direction, and a focal
-    /// point.
-    /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
-    #[inline]
-    pub fn look_at_lh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
-        glam_assert!(up.is_normalized());
-        Self::look_to_lh(eye, center - eye, up)
-    }
-
-    /// Creates a right-handed view transform using a camera position, an up direction, and a focal
-    /// point.
-    /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
-    #[inline]
-    pub fn look_at_rh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
-        glam_assert!(up.is_normalized());
-        Self::look_to_lh(eye, eye - center, up)
-    }
-
-    /// Transforms the given 3D points, applying shear, scale, rotation and translatio.
-    #[inline(always)]
-    pub fn transform_point3(&self, point: Vec3) -> Vec3 {
-        (self.x_col * point.x + self.y_col * point.y + self.z_col * point.z + self.w_col).into()
-    }
-
-    /// Transforms the give 3D vector, applying shear, scale and rotation (but NOT translation).
-    ///
-    /// To also apply translation, use [`Self::transform_point3`] instead.
-    #[inline(always)]
-    pub fn transform_vector3(&self, vec: Vec3) -> Vec3 {
-        (self.x_col * vec.x + self.y_col * vec.y + self.z_col * vec.z).into()
-    }
-
-    /// Returns `true` if, and only if, all elements are finite.
-    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
-    #[inline]
-    pub fn is_finite(&self) -> bool {
-        self.x_col.is_finite()
-            && self.y_col.is_finite()
-            && self.z_col.is_finite()
-            && self.w_col.is_finite()
-    }
-
-    /// Returns `true` if any elements are `NaN`.
-    #[inline]
-    pub fn is_nan(&self) -> bool {
-        self.x_col.is_nan() || self.y_col.is_nan() || self.z_col.is_nan() || self.w_col.is_nan()
-    }
-
-    /// Returns true if the absolute difference of all elements between `self` and `other`
-    /// is less than or equal to `max_abs_diff`.
-    ///
-    /// This can be used to compare if two 3x4 matrices contain similar elements. It works
-    /// best when comparing with a known value. The `max_abs_diff` that should be used used
-    /// depends on the values being compared against.
-    ///
-    /// For more see
-    /// [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
-    #[inline]
-    pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
-        self.x_col.abs_diff_eq(other.x_col, max_abs_diff)
-            && self.y_col.abs_diff_eq(other.y_col, max_abs_diff)
-            && self.z_col.abs_diff_eq(other.z_col, max_abs_diff)
-            && self.w_col.abs_diff_eq(other.w_col, max_abs_diff)
-    }
-
-    /// Return the inverse of this transform.
-    pub fn inverse(&self) -> Self {
-        // invert 3x3 matrix:
-        let x_row = self.y_col.cross(self.z_col);
-        let y_row = self.z_col.cross(self.x_col);
-        let z_row = self.x_col.cross(self.y_col);
-        let det = self.z_col.dot(z_row);
-        let inv_det = det.recip();
-        let x_row = x_row * inv_det;
-        let y_row = y_row * inv_det;
-        let z_row = z_row * inv_det;
-
-        let x_col = Vec3A::new(x_row.x, y_row.x, z_row.x);
-        let y_col = Vec3A::new(x_row.y, y_row.y, z_row.y);
-        let z_col = Vec3A::new(x_row.z, y_row.z, z_row.z);
-
-        // transform negative translation by the 3x3 inverse:
-        let w_col = -(x_col * self.w_col.x + y_col * self.w_col.y + z_col * self.w_col.z);
-
-        Self {
-            x_col,
-            y_col,
-            z_col,
-            w_col,
-        }
-    }
-}
-
-#[cfg(not(target_arch = "spirv"))]
-impl core::fmt::Debug for Affine3D {
-    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
-        fmt.debug_struct(stringify!($affine3d))
-            .field("x_col", &self.x_col)
-            .field("y_col", &self.y_col)
-            .field("z_col", &self.z_col)
-            .field("w_col", &self.w_col)
-            .finish()
-    }
-}
-
-impl From<Affine3D> for Mat4 {
-    #[inline]
-    fn from(m: Affine3D) -> Mat4 {
-        Mat4::from_cols(
-            m.x_col.extend(0.0),
-            m.y_col.extend(0.0),
-            m.z_col.extend(0.0),
-            m.w_col.extend(1.0),
-        )
-    }
-}
-
-impl core::ops::Mul for Affine3D {
-    type Output = Affine3D;
-
-    #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
-    #[inline(always)]
-    fn mul(self, rhs: Affine3D) -> Self::Output {
-        use crate::core::traits::vector::Vector4;
-        #[cfg(target_arch = "x86")]
-        use core::arch::x86::*;
-        #[cfg(target_arch = "x86_64")]
-        use core::arch::x86_64::*;
-
-        unsafe {
-            // TODO: optimize
-            let lhs_x_col: __m128 = self.x_col.into();
-            let lhs_y_col: __m128 = self.y_col.into();
-            let lhs_z_col: __m128 = self.z_col.into();
-            let lhs_w_col: __m128 = self.w_col.into();
-
-            let rhs_x_col: __m128 = rhs.x_col.into();
-            let rhs_y_col: __m128 = rhs.y_col.into();
-            let rhs_z_col: __m128 = rhs.z_col.into();
-            let rhs_w_col: __m128 = rhs.w_col.into();
-
-            // Based on https://github.com/microsoft/DirectXMath XMMatrixMultiply
-
-            let v_x = _mm_mul_ps(rhs_x_col.splat_x(), lhs_x_col);
-            let v_y = _mm_mul_ps(rhs_x_col.splat_y(), lhs_y_col);
-            let v_z = _mm_mul_ps(rhs_x_col.splat_z(), lhs_z_col);
-            let out_x_col = _mm_add_ps(_mm_add_ps(v_x, v_z), v_y);
-
-            let v_x = _mm_mul_ps(rhs_y_col.splat_x(), lhs_x_col);
-            let v_y = _mm_mul_ps(rhs_y_col.splat_y(), lhs_y_col);
-            let v_z = _mm_mul_ps(rhs_y_col.splat_z(), lhs_z_col);
-            let out_y_col = _mm_add_ps(_mm_add_ps(v_x, v_z), v_y);
-
-            let v_x = _mm_mul_ps(rhs_z_col.splat_x(), lhs_x_col);
-            let v_y = _mm_mul_ps(rhs_z_col.splat_y(), lhs_y_col);
-            let v_z = _mm_mul_ps(rhs_z_col.splat_z(), lhs_z_col);
-            let out_z_col = _mm_add_ps(_mm_add_ps(v_x, v_z), v_y);
-
-            let v_x = _mm_mul_ps(rhs_w_col.splat_x(), lhs_x_col);
-            let v_y = _mm_mul_ps(rhs_w_col.splat_y(), lhs_y_col);
-            let v_z = _mm_mul_ps(rhs_w_col.splat_z(), lhs_z_col);
-            let out_w_col = _mm_add_ps(_mm_add_ps(v_x, v_z), _mm_add_ps(v_y, lhs_w_col));
-
-            Self {
-                x_col: out_x_col.into(),
-                y_col: out_y_col.into(),
-                z_col: out_z_col.into(),
-                w_col: out_w_col.into(),
-            }
-        }
-    }
-
-    #[cfg(not(all(target_feature = "sse2", not(feature = "scalar-math"))))]
-    #[inline(always)]
-    fn mul(self, rhs: Affine3D) -> Self::Output {
-        Self {
-            x_col: self.transform_vector3(rhs.x_col.into()).into(),
-            y_col: self.transform_vector3(rhs.y_col.into()).into(),
-            z_col: self.transform_vector3(rhs.z_col.into()).into(),
-            w_col: self.transform_point3(rhs.w_col.into()).into(),
-        }
-    }
-}
-
-impl core::ops::Mul<Mat4> for Affine3D {
-    type Output = Mat4;
-
-    #[inline(always)]
-    fn mul(self, rhs: Mat4) -> Self::Output {
-        Mat4::from(self) * rhs
-    }
-}
-
-impl core::ops::Mul<Affine3D> for Mat4 {
-    type Output = Mat4;
-
-    #[inline(always)]
-    fn mul(self, rhs: Affine3D) -> Self::Output {
-        self * Mat4::from(rhs)
-    }
-}
-
-impl<'a> core::iter::Product<&'a Self> for Affine3D {
-    fn product<I>(iter: I) -> Self
-    where
-        I: Iterator<Item = &'a Self>,
-    {
-        iter.fold(Self::IDENTITY, |a, &b| a * b)
-    }
-}
diff --git a/src/core/scalar/matrix.rs b/src/core/scalar/matrix.rs
index 6aeaac8..34ae1f5 100644
--- a/src/core/scalar/matrix.rs
+++ b/src/core/scalar/matrix.rs
@@ -1,5 +1,5 @@
 use crate::core::{
-    storage::{Vector2x2, Vector3x3, Vector4x4, XY, XYZ, XYZW},
+    storage::{Columns2, Columns3, Columns4, XY, XYZ, XYZW},
     traits::{
         matrix::{
             FloatMatrix2x2, FloatMatrix3x3, FloatMatrix4x4, Matrix, Matrix2x2, Matrix3x3,
@@ -11,7 +11,7 @@ use crate::core::{
     },
 };
 
-impl<T: NumEx> MatrixConst for Vector2x2<XY<T>> {
+impl<T: NumEx> MatrixConst for Columns2<XY<T>> {
     const ZERO: Self = Self {
         x_axis: XY::ZERO,
         y_axis: XY::ZERO,
@@ -22,21 +22,21 @@ impl<T: NumEx> MatrixConst for Vector2x2<XY<T>> {
     };
 }
 
-impl<T: NumEx> Matrix<T> for Vector2x2<XY<T>> {}
+impl<T: NumEx> Matrix<T> for Columns2<XY<T>> {}
 
-impl<T: NumEx> Matrix2x2<T, XY<T>> for Vector2x2<XY<T>> {
+impl<T: NumEx> Matrix2x2<T, XY<T>> for Columns2<XY<T>> {
     #[inline(always)]
     fn from_cols(x_axis: XY<T>, y_axis: XY<T>) -> Self {
         Self { x_axis, y_axis }
     }
 
     #[inline(always)]
-    fn as_ref_vector2x2(&self) -> &Vector2x2<XY<T>> {
+    fn as_ref_vector2x2(&self) -> &Columns2<XY<T>> {
         self
     }
 
     #[inline(always)]
-    fn as_mut_vector2x2(&mut self) -> &mut Vector2x2<XY<T>> {
+    fn as_mut_vector2x2(&mut self) -> &mut Columns2<XY<T>> {
         self
     }
 
@@ -79,7 +79,7 @@ impl<T: NumEx> Matrix2x2<T, XY<T>> for Vector2x2<XY<T>> {
     }
 }
 
-impl<T: FloatEx> FloatMatrix2x2<T, XY<T>> for Vector2x2<XY<T>> {
+impl<T: FloatEx> FloatMatrix2x2<T, XY<T>> for Columns2<XY<T>> {
     #[inline]
     fn inverse(&self) -> Self {
         let inv_det = {
@@ -96,7 +96,7 @@ impl<T: FloatEx> FloatMatrix2x2<T, XY<T>> for Vector2x2<XY<T>> {
     }
 }
 
-impl<T: NumEx> MatrixConst for Vector3x3<XYZ<T>> {
+impl<T: NumEx> MatrixConst for Columns3<XYZ<T>> {
     const ZERO: Self = Self {
         x_axis: XYZ::ZERO,
         y_axis: XYZ::ZERO,
@@ -109,9 +109,9 @@ impl<T: NumEx> MatrixConst for Vector3x3<XYZ<T>> {
     };
 }
 
-impl<T: NumEx> Matrix<T> for Vector3x3<XYZ<T>> {}
+impl<T: NumEx> Matrix<T> for Columns3<XYZ<T>> {}
 
-impl<T: NumEx> Matrix3x3<T, XYZ<T>> for Vector3x3<XYZ<T>> {
+impl<T: NumEx> Matrix3x3<T, XYZ<T>> for Columns3<XYZ<T>> {
     #[inline(always)]
     fn from_cols(x_axis: XYZ<T>, y_axis: XYZ<T>, z_axis: XYZ<T>) -> Self {
         Self {
@@ -122,20 +122,15 @@ impl<T: NumEx> Matrix3x3<T, XYZ<T>> for Vector3x3<XYZ<T>> {
     }
 
     #[inline(always)]
-    fn as_ref_vector3x3(&self) -> &Vector3x3<XYZ<T>> {
+    fn as_ref_vector3x3(&self) -> &Columns3<XYZ<T>> {
         self
     }
 
     #[inline(always)]
-    fn as_mut_vector3x3(&mut self) -> &mut Vector3x3<XYZ<T>> {
+    fn as_mut_vector3x3(&mut self) -> &mut Columns3<XYZ<T>> {
         self
     }
 
-    #[inline]
-    fn determinant(&self) -> T {
-        self.z_axis.dot(self.x_axis.cross(self.y_axis))
-    }
-
     #[inline(always)]
     fn transpose(&self) -> Self {
         Self::from_cols(
@@ -159,50 +154,15 @@ impl<T: NumEx> Matrix3x3<T, XYZ<T>> for Vector3x3<XYZ<T>> {
 
     #[inline]
     fn mul_vector(&self, other: XYZ<T>) -> XYZ<T> {
+        // default implementation uses splat_x etc, which might not be optimial. Need to check.
         let mut res = self.x_axis.mul_scalar(other.x);
         res = self.y_axis.mul_scalar(other.y).add(res);
         res = self.z_axis.mul_scalar(other.z).add(res);
         res
     }
-
-    #[inline]
-    fn mul_matrix(&self, other: &Self) -> Self {
-        Self::from_cols(
-            self.mul_vector(other.x_axis),
-            self.mul_vector(other.y_axis),
-            self.mul_vector(other.z_axis),
-        )
-    }
-
-    #[inline]
-    fn mul_scalar(&self, other: T) -> Self {
-        Self::from_cols(
-            self.x_axis.mul_scalar(other),
-            self.y_axis.mul_scalar(other),
-            self.z_axis.mul_scalar(other),
-        )
-    }
-
-    #[inline]
-    fn add_matrix(&self, other: &Self) -> Self {
-        Self::from_cols(
-            self.x_axis.add(other.x_axis),
-            self.y_axis.add(other.y_axis),
-            self.z_axis.add(other.z_axis),
-        )
-    }
-
-    #[inline]
-    fn sub_matrix(&self, other: &Self) -> Self {
-        Self::from_cols(
-            self.x_axis.sub(other.x_axis),
-            self.y_axis.sub(other.y_axis),
-            self.z_axis.sub(other.z_axis),
-        )
-    }
 }
 
-impl<T: FloatEx> FloatMatrix3x3<T, XYZ<T>> for Vector3x3<XYZ<T>> {
+impl<T: FloatEx> FloatMatrix3x3<T, XYZ<T>> for Columns3<XYZ<T>> {
     #[inline]
     fn inverse(&self) -> Self {
         let tmp0 = self.y_axis.cross(self.z_axis);
@@ -217,21 +177,20 @@ impl<T: FloatEx> FloatMatrix3x3<T, XYZ<T>> for Vector3x3<XYZ<T>> {
 
     #[inline]
     fn transform_point2(&self, other: XY<T>) -> XY<T> {
-        let mut res = self.x_axis.mul_scalar(other.x);
-        res = self.y_axis.mul_scalar(other.y).add(res);
-        res = self.z_axis.add(res);
-        res.into()
+        // TODO: This is untested, probably slower than the high level code that uses a SIMD mat2
+        Columns2::from_cols(self.x_axis.into(), self.y_axis.into())
+            .mul_vector(other)
+            .add(self.z_axis.into())
     }
 
     #[inline]
     fn transform_vector2(&self, other: XY<T>) -> XY<T> {
-        let mut res = self.x_axis.mul_scalar(other.x);
-        res = self.y_axis.mul_scalar(other.y).add(res);
-        res.into()
+        // TODO: This is untested, probably slower than the high level code that uses a SIMD mat2
+        Columns2::from_cols(self.x_axis.into(), self.y_axis.into()).mul_vector(other)
     }
 }
 
-impl<T: NumEx> MatrixConst for Vector4x4<XYZW<T>> {
+impl<T: NumEx> MatrixConst for Columns4<XYZW<T>> {
     const ZERO: Self = Self {
         x_axis: XYZW::ZERO,
         y_axis: XYZW::ZERO,
@@ -246,9 +205,9 @@ impl<T: NumEx> MatrixConst for Vector4x4<XYZW<T>> {
     };
 }
 
-impl<T: NumEx> Matrix<T> for Vector4x4<XYZW<T>> {}
+impl<T: NumEx> Matrix<T> for Columns4<XYZW<T>> {}
 
-impl<T: NumEx> Matrix4x4<T, XYZW<T>> for Vector4x4<XYZW<T>> {
+impl<T: NumEx> Matrix4x4<T, XYZW<T>> for Columns4<XYZW<T>> {
     #[rustfmt::skip]
     #[inline(always)]
     fn from_cols(x_axis: XYZW<T>, y_axis: XYZW<T>, z_axis: XYZW<T>, w_axis: XYZW<T>) -> Self {
@@ -276,12 +235,12 @@ impl<T: NumEx> Matrix4x4<T, XYZW<T>> for Vector4x4<XYZW<T>> {
     }
 
     #[inline(always)]
-    fn as_ref_vector4x4(&self) -> &Vector4x4<XYZW<T>> {
+    fn as_ref_vector4x4(&self) -> &Columns4<XYZW<T>> {
         self
     }
 
     #[inline(always)]
-    fn as_mut_vector4x4(&mut self) -> &mut Vector4x4<XYZW<T>> {
+    fn as_mut_vector4x4(&mut self) -> &mut Columns4<XYZW<T>> {
         self
     }
 
@@ -321,7 +280,7 @@ impl<T: NumEx> Matrix4x4<T, XYZW<T>> for Vector4x4<XYZW<T>> {
     }
 }
 
-impl<T: FloatEx> FloatMatrix4x4<T, XYZW<T>> for Vector4x4<XYZW<T>> {
+impl<T: FloatEx> FloatMatrix4x4<T, XYZW<T>> for Columns4<XYZW<T>> {
     type SIMDVector3 = XYZ<T>;
 
     #[inline(always)]
@@ -413,4 +372,4 @@ impl<T: FloatEx> FloatMatrix4x4<T, XYZW<T>> for Vector4x4<XYZW<T>> {
     }
 }
 
-impl<T: FloatEx> ProjectionMatrix<T, XYZW<T>> for Vector4x4<XYZW<T>> {}
+impl<T: FloatEx> ProjectionMatrix<T, XYZW<T>> for Columns4<XYZW<T>> {}
diff --git a/src/core/sse2/matrix.rs b/src/core/sse2/matrix.rs
index 838d0c1..66c69a1 100644
--- a/src/core/sse2/matrix.rs
+++ b/src/core/sse2/matrix.rs
@@ -8,9 +8,12 @@ use core::mem::MaybeUninit;
 use crate::{
     const_m128,
     core::{
-        storage::{Align16, Vector2x2, Vector4x4, XY, XYZ},
+        storage::{Align16, Columns2, Columns3, Columns4, XY, XYZ},
         traits::{
-            matrix::{FloatMatrix2x2, FloatMatrix4x4, Matrix, Matrix2x2, Matrix4x4, MatrixConst},
+            matrix::{
+                FloatMatrix2x2, FloatMatrix3x3, FloatMatrix4x4, Matrix, Matrix2x2, Matrix3x3,
+                Matrix4x4, MatrixConst,
+            },
             projection::ProjectionMatrix,
             vector::{FloatVector4, Vector, Vector4, Vector4Const},
         },
@@ -37,13 +40,13 @@ impl Matrix2x2<f32, XY<f32>> for __m128 {
     }
 
     #[inline(always)]
-    fn as_ref_vector2x2(&self) -> &Vector2x2<XY<f32>> {
-        unsafe { &*(self as *const Self as *const Vector2x2<XY<f32>>) }
+    fn as_ref_vector2x2(&self) -> &Columns2<XY<f32>> {
+        unsafe { &*(self as *const Self as *const Columns2<XY<f32>>) }
     }
 
     #[inline(always)]
-    fn as_mut_vector2x2(&mut self) -> &mut Vector2x2<XY<f32>> {
-        unsafe { &mut *(self as *mut Self as *mut Vector2x2<XY<f32>>) }
+    fn as_mut_vector2x2(&mut self) -> &mut Columns2<XY<f32>> {
+        unsafe { &mut *(self as *mut Self as *mut Columns2<XY<f32>>) }
     }
 
     #[inline]
@@ -132,14 +135,96 @@ impl FloatMatrix2x2<f32, XY<f32>> for __m128 {
     }
 }
 
-impl MatrixConst for Vector4x4<__m128> {
-    const ZERO: Vector4x4<__m128> = Vector4x4 {
+impl MatrixConst for Columns3<__m128> {
+    const ZERO: Columns3<__m128> = Columns3 {
+        x_axis: __m128::ZERO,
+        y_axis: __m128::ZERO,
+        z_axis: __m128::ZERO,
+    };
+    const IDENTITY: Columns3<__m128> = Columns3 {
+        x_axis: __m128::X,
+        y_axis: __m128::Y,
+        z_axis: __m128::Z,
+    };
+}
+
+impl Matrix<f32> for Columns3<__m128> {}
+
+impl Matrix3x3<f32, __m128> for Columns3<__m128> {
+    #[inline(always)]
+    fn from_cols(x_axis: __m128, y_axis: __m128, z_axis: __m128) -> Self {
+        Self {
+            x_axis,
+            y_axis,
+            z_axis,
+        }
+    }
+
+    #[inline(always)]
+    fn x_axis(&self) -> &__m128 {
+        &self.x_axis
+    }
+
+    #[inline(always)]
+    fn y_axis(&self) -> &__m128 {
+        &self.y_axis
+    }
+
+    #[inline(always)]
+    fn z_axis(&self) -> &__m128 {
+        &self.z_axis
+    }
+
+    #[inline(always)]
+    fn as_ref_vector3x3(&self) -> &Columns3<__m128> {
+        self
+    }
+
+    #[inline(always)]
+    fn as_mut_vector3x3(&mut self) -> &mut Columns3<__m128> {
+        self
+    }
+
+    #[inline]
+    fn transpose(&self) -> Self {
+        unsafe {
+            let tmp0 = _mm_shuffle_ps(self.x_axis, self.y_axis, 0b01_00_01_00);
+            let tmp1 = _mm_shuffle_ps(self.x_axis, self.y_axis, 0b11_10_11_10);
+
+            Self {
+                x_axis: _mm_shuffle_ps(tmp0, self.z_axis, 0b00_00_10_00),
+                y_axis: _mm_shuffle_ps(tmp0, self.z_axis, 0b01_01_11_01),
+                z_axis: _mm_shuffle_ps(tmp1, self.z_axis, 0b10_10_10_00),
+            }
+        }
+    }
+}
+
+impl FloatMatrix3x3<f32, __m128> for Columns3<__m128> {
+    #[inline]
+    fn transform_point2(&self, other: XY<f32>) -> XY<f32> {
+        let mut res = self.x_axis.mul_scalar(other.x);
+        res = self.y_axis.mul_scalar(other.y).add(res);
+        res = self.z_axis.add(res);
+        res.into()
+    }
+
+    #[inline]
+    fn transform_vector2(&self, other: XY<f32>) -> XY<f32> {
+        let mut res = self.x_axis.mul_scalar(other.x);
+        res = self.y_axis.mul_scalar(other.y).add(res);
+        res.into()
+    }
+}
+
+impl MatrixConst for Columns4<__m128> {
+    const ZERO: Columns4<__m128> = Columns4 {
         x_axis: __m128::ZERO,
         y_axis: __m128::ZERO,
         z_axis: __m128::ZERO,
         w_axis: __m128::ZERO,
     };
-    const IDENTITY: Vector4x4<__m128> = Vector4x4 {
+    const IDENTITY: Columns4<__m128> = Columns4 {
         x_axis: __m128::X,
         y_axis: __m128::Y,
         z_axis: __m128::Z,
@@ -147,9 +232,9 @@ impl MatrixConst for Vector4x4<__m128> {
     };
 }
 
-impl Matrix<f32> for Vector4x4<__m128> {}
+impl Matrix<f32> for Columns4<__m128> {}
 
-impl Matrix4x4<f32, __m128> for Vector4x4<__m128> {
+impl Matrix4x4<f32, __m128> for Columns4<__m128> {
     #[inline(always)]
     fn from_cols(x_axis: __m128, y_axis: __m128, z_axis: __m128, w_axis: __m128) -> Self {
         Self {
@@ -181,13 +266,13 @@ impl Matrix4x4<f32, __m128> for Vector4x4<__m128> {
     }
 
     #[inline(always)]
-    fn as_ref_vector4x4(&self) -> &Vector4x4<__m128> {
-        unsafe { &*(self as *const Self as *const Vector4x4<__m128>) }
+    fn as_ref_vector4x4(&self) -> &Columns4<__m128> {
+        self
     }
 
     #[inline(always)]
-    fn as_mut_vector4x4(&mut self) -> &mut Vector4x4<__m128> {
-        unsafe { &mut *(self as *mut Self as *mut Vector4x4<__m128>) }
+    fn as_mut_vector4x4(&mut self) -> &mut Columns4<__m128> {
+        self
     }
 
     #[inline]
@@ -225,7 +310,7 @@ impl Matrix4x4<f32, __m128> for Vector4x4<__m128> {
             let addres = _mm_add_ps(subres, mulfacc);
             let detcof = _mm_mul_ps(addres, _mm_setr_ps(1.0, -1.0, 1.0, -1.0));
 
-            self.x_axis.dot(detcof)
+            Vector4::dot(self.x_axis, detcof)
         }
     }
 
@@ -248,7 +333,7 @@ impl Matrix4x4<f32, __m128> for Vector4x4<__m128> {
     }
 }
 
-impl FloatMatrix4x4<f32, __m128> for Vector4x4<__m128> {
+impl FloatMatrix4x4<f32, __m128> for Columns4<__m128> {
     type SIMDVector3 = __m128;
 
     fn inverse(&self) -> Self {
@@ -379,7 +464,7 @@ impl FloatMatrix4x4<f32, __m128> for Vector4x4<__m128> {
             let row1 = _mm_shuffle_ps(inv2, inv3, 0b00_00_00_00);
             let row2 = _mm_shuffle_ps(row0, row1, 0b10_00_10_00);
 
-            let dot0 = self.x_axis.dot(row2);
+            let dot0 = Vector4::dot(self.x_axis, row2);
             glam_assert!(dot0 != 0.0);
 
             let rcp0 = _mm_set1_ps(dot0.recip());
@@ -414,30 +499,50 @@ impl FloatMatrix4x4<f32, __m128> for Vector4x4<__m128> {
 
     #[inline]
     fn transform_float4_as_point3(&self, other: __m128) -> __m128 {
-        let mut res = self.x_axis.mul(other.splat_x());
-        res = self.y_axis.mul_add(other.splat_y(), res);
-        res = self.z_axis.mul_add(other.splat_z(), res);
+        let mut res = self.x_axis.mul(Vector4::splat_x(other));
+        res = self.y_axis.mul_add(Vector4::splat_y(other), res);
+        res = self.z_axis.mul_add(Vector4::splat_z(other), res);
         res = self.w_axis.add(res);
         res
     }
 
     #[inline]
     fn transform_float4_as_vector3(&self, other: __m128) -> __m128 {
-        let mut res = self.x_axis.mul(other.splat_x());
-        res = self.y_axis.mul_add(other.splat_y(), res);
-        res = self.z_axis.mul_add(other.splat_z(), res);
+        let mut res = self.x_axis.mul(Vector4::splat_x(other));
+        res = self.y_axis.mul_add(Vector4::splat_y(other), res);
+        res = self.z_axis.mul_add(Vector4::splat_z(other), res);
         res
     }
 
     #[inline]
     fn project_float4_as_point3(&self, other: __m128) -> __m128 {
-        let mut res = self.x_axis.mul(other.splat_x());
-        res = self.y_axis.mul_add(other.splat_y(), res);
-        res = self.z_axis.mul_add(other.splat_z(), res);
+        let mut res = self.x_axis.mul(Vector4::splat_x(other));
+        res = self.y_axis.mul_add(Vector4::splat_y(other), res);
+        res = self.z_axis.mul_add(Vector4::splat_z(other), res);
         res = self.w_axis.add(res);
         res = res.mul(res.splat_w().recip());
         res
     }
 }
 
-impl ProjectionMatrix<f32, __m128> for Vector4x4<__m128> {}
+impl ProjectionMatrix<f32, __m128> for Columns4<__m128> {}
+
+impl From<Columns3<XYZ<f32>>> for Columns3<__m128> {
+    fn from(v: Columns3<XYZ<f32>>) -> Columns3<__m128> {
+        Self {
+            x_axis: v.x_axis.into(),
+            y_axis: v.y_axis.into(),
+            z_axis: v.z_axis.into(),
+        }
+    }
+}
+
+impl From<Columns3<__m128>> for Columns3<XYZ<f32>> {
+    fn from(v: Columns3<__m128>) -> Columns3<XYZ<f32>> {
+        Self {
+            x_axis: v.x_axis.into(),
+            y_axis: v.y_axis.into(),
+            z_axis: v.z_axis.into(),
+        }
+    }
+}
diff --git a/src/core/storage.rs b/src/core/storage.rs
index fd67d3c..b0f6e02 100644
--- a/src/core/storage.rs
+++ b/src/core/storage.rs
@@ -27,26 +27,26 @@ pub struct XYZW<T> {
 
 #[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
 #[cfg_attr(not(target_arch = "spirv"), repr(C))]
-pub struct Vector2x2<V2> {
-    pub x_axis: V2,
-    pub y_axis: V2,
+pub struct Columns2<V> {
+    pub x_axis: V,
+    pub y_axis: V,
 }
 
 #[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
 #[cfg_attr(not(target_arch = "spirv"), repr(C))]
-pub struct Vector3x3<V3> {
-    pub x_axis: V3,
-    pub y_axis: V3,
-    pub z_axis: V3,
+pub struct Columns3<V> {
+    pub x_axis: V,
+    pub y_axis: V,
+    pub z_axis: V,
 }
 
 #[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
 #[cfg_attr(not(target_arch = "spirv"), repr(C))]
-pub struct Vector4x4<V4> {
-    pub x_axis: V4,
-    pub y_axis: V4,
-    pub z_axis: V4,
-    pub w_axis: V4,
+pub struct Columns4<V> {
+    pub x_axis: V,
+    pub y_axis: V,
+    pub z_axis: V,
+    pub w_axis: V,
 }
 
 #[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
diff --git a/src/core/traits/matrix.rs b/src/core/traits/matrix.rs
index 89d0080..0d70597 100644
--- a/src/core/traits/matrix.rs
+++ b/src/core/traits/matrix.rs
@@ -1,5 +1,5 @@
 use crate::core::{
-    storage::{Vector2x2, Vector3x3, Vector4x4, XY, XYZ, XYZW},
+    storage::{Columns2, Columns3, Columns4, XY, XYZ, XYZW},
     traits::{
         quaternion::Quaternion,
         scalar::{FloatEx, NumEx},
@@ -32,8 +32,8 @@ pub trait Matrix2x2<T: NumEx, V2: Vector2<T>>: Matrix<T> {
         &self.as_ref_vector2x2().y_axis
     }
 
-    fn as_ref_vector2x2(&self) -> &Vector2x2<V2>;
-    fn as_mut_vector2x2(&mut self) -> &mut Vector2x2<V2>;
+    fn as_ref_vector2x2(&self) -> &Columns2<V2>;
+    fn as_mut_vector2x2(&mut self) -> &mut Columns2<V2>;
 
     #[inline(always)]
     fn from_cols_array(m: &[T; 4]) -> Self {
@@ -61,11 +61,10 @@ pub trait Matrix2x2<T: NumEx, V2: Vector2<T>>: Matrix<T> {
 
     #[rustfmt::skip]
     #[inline(always)]
-    fn from_diagonal(diagonal: V2) -> Self {
-        let (x, y) = diagonal.into_tuple();
+    fn from_diagonal(diagonal: XY<T>) -> Self {
         Self::new(
-            x, T::ZERO,
-            T::ZERO, y)
+            diagonal.x, T::ZERO,
+            T::ZERO, diagonal.y)
     }
 
     fn determinant(&self) -> T;
@@ -122,8 +121,8 @@ pub trait Matrix3x3<T: NumEx, V3: Vector3<T>>: Matrix<T> {
         &self.as_ref_vector3x3().z_axis
     }
 
-    fn as_ref_vector3x3(&self) -> &Vector3x3<V3>;
-    fn as_mut_vector3x3(&mut self) -> &mut Vector3x3<V3>;
+    fn as_ref_vector3x3(&self) -> &Columns3<V3>;
+    fn as_mut_vector3x3(&mut self) -> &mut Columns3<V3>;
 
     #[rustfmt::skip]
     #[inline(always)]
@@ -167,13 +166,11 @@ pub trait Matrix3x3<T: NumEx, V3: Vector3<T>>: Matrix<T> {
 
     #[rustfmt::skip]
     #[inline(always)]
-    fn from_diagonal(diagonal: V3) -> Self {
-        // glam_assert!(MaskVector3::any(scale.cmpne(V3::ZERO)));
-        let (x, y, z) = diagonal.into_tuple();
+    fn from_diagonal(diagonal: XYZ<T>) -> Self {
         Self::from_cols(
-            V3::new(x, T::ZERO, T::ZERO),
-            V3::new(T::ZERO, y, T::ZERO),
-            V3::new(T::ZERO, T::ZERO, z),
+            V3::new(diagonal.x, T::ZERO, T::ZERO),
+            V3::new(T::ZERO, diagonal.y, T::ZERO),
+            V3::new(T::ZERO, T::ZERO, diagonal.z),
         )
     }
 
@@ -193,13 +190,56 @@ pub trait Matrix3x3<T: NumEx, V3: Vector3<T>>: Matrix<T> {
         Self::from_cols(V3::X, V3::Y, V3::new(translation.x, translation.y, T::ONE))
     }
 
-    fn determinant(&self) -> T;
+    #[inline]
+    fn determinant(&self) -> T {
+        self.z_axis().dot(self.x_axis().cross(*self.y_axis()))
+    }
+
     fn transpose(&self) -> Self;
-    fn mul_vector(&self, other: V3) -> V3;
-    fn mul_matrix(&self, other: &Self) -> Self;
-    fn mul_scalar(&self, other: T) -> Self;
-    fn add_matrix(&self, other: &Self) -> Self;
-    fn sub_matrix(&self, other: &Self) -> Self;
+
+    #[inline]
+    fn mul_vector(&self, other: V3) -> V3 {
+        let mut res = self.x_axis().mul(other.splat_x());
+        res = self.y_axis().mul_add(other.splat_y(), res);
+        res = self.z_axis().mul_add(other.splat_z(), res);
+        res
+    }
+
+    #[inline]
+    fn mul_matrix(&self, other: &Self) -> Self {
+        Self::from_cols(
+            self.mul_vector(*other.x_axis()),
+            self.mul_vector(*other.y_axis()),
+            self.mul_vector(*other.z_axis()),
+        )
+    }
+
+    #[inline]
+    fn mul_scalar(&self, other: T) -> Self {
+        Self::from_cols(
+            self.x_axis().mul_scalar(other),
+            self.y_axis().mul_scalar(other),
+            self.z_axis().mul_scalar(other),
+        )
+    }
+
+    #[inline]
+    fn add_matrix(&self, other: &Self) -> Self {
+        Self::from_cols(
+            self.x_axis().add(*other.x_axis()),
+            self.y_axis().add(*other.y_axis()),
+            self.z_axis().add(*other.z_axis()),
+        )
+    }
+
+    #[inline]
+    fn sub_matrix(&self, other: &Self) -> Self {
+        Self::from_cols(
+            self.x_axis().sub(*other.x_axis()),
+            self.y_axis().sub(*other.y_axis()),
+            self.z_axis().sub(*other.z_axis()),
+        )
+    }
 }
 
 pub trait FloatMatrix3x3<T: FloatEx, V3: FloatVector3<T>>: Matrix3x3<T, V3> {
@@ -233,7 +273,7 @@ pub trait FloatMatrix3x3<T: FloatEx, V3: FloatVector3<T>>: Matrix3x3<T, V3> {
     }
 
     #[inline]
-    fn from_axis_angle(axis: V3, angle: T) -> Self {
+    fn from_axis_angle(axis: XYZ<T>, angle: T) -> Self {
         glam_assert!(axis.is_normalized());
         let (sin, cos) = angle.sin_cos();
         let (xsin, ysin, zsin) = axis.mul_scalar(sin).into_tuple();
@@ -306,7 +346,30 @@ pub trait FloatMatrix3x3<T: FloatEx, V3: FloatVector3<T>>: Matrix3x3<T, V3> {
     fn transform_point2(&self, other: XY<T>) -> XY<T>;
     fn transform_vector2(&self, other: XY<T>) -> XY<T>;
 
-    fn inverse(&self) -> Self;
+    #[inline]
+    fn inverse(&self) -> Self
+    where
+        <V3 as Vector<T>>::Mask: MaskVector3,
+    {
+        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_into_vec(tmp2);
+        glam_assert!(det.cmpne(V3::ZERO).all());
+        let inv_det = det.recip();
+        // TODO: Work out if it's possible to get rid of the transpose
+        Self::from_cols(tmp0.mul(inv_det), tmp1.mul(inv_det), tmp2.mul(inv_det)).transpose()
+    }
+
+    #[inline]
+    fn is_finite(&self) -> bool {
+        self.x_axis().is_finite() && self.y_axis().is_finite() && self.z_axis().is_finite()
+    }
+
+    #[inline]
+    fn is_nan(&self) -> bool {
+        self.x_axis().is_nan() || self.y_axis().is_nan() || self.z_axis().is_nan()
+    }
 }
 
 pub trait Matrix4x4<T: NumEx, V4: Vector4<T>>: Matrix<T> {
@@ -317,8 +380,8 @@ pub trait Matrix4x4<T: NumEx, V4: Vector4<T>>: Matrix<T> {
     fn z_axis(&self) -> &V4;
     fn w_axis(&self) -> &V4;
 
-    fn as_ref_vector4x4(&self) -> &Vector4x4<V4>;
-    fn as_mut_vector4x4(&mut self) -> &mut Vector4x4<V4>;
+    fn as_ref_vector4x4(&self) -> &Columns4<V4>;
+    fn as_mut_vector4x4(&mut self) -> &mut Columns4<V4>;
 
     #[rustfmt::skip]
     #[inline(always)]
@@ -366,13 +429,12 @@ pub trait Matrix4x4<T: NumEx, V4: Vector4<T>>: Matrix<T> {
     }
 
     #[inline(always)]
-    fn from_diagonal(diagonal: V4) -> Self {
-        let (x, y, z, w) = diagonal.into_tuple();
+    fn from_diagonal(diagonal: XYZW<T>) -> Self {
         Self::from_cols(
-            V4::new(x, T::ZERO, T::ZERO, T::ZERO),
-            V4::new(T::ZERO, y, T::ZERO, T::ZERO),
-            V4::new(T::ZERO, T::ZERO, z, T::ZERO),
-            V4::new(T::ZERO, T::ZERO, T::ZERO, w),
+            V4::new(diagonal.x, T::ZERO, T::ZERO, T::ZERO),
+            V4::new(T::ZERO, diagonal.y, T::ZERO, T::ZERO),
+            V4::new(T::ZERO, T::ZERO, diagonal.z, T::ZERO),
+            V4::new(T::ZERO, T::ZERO, T::ZERO, diagonal.w),
         )
     }
 
diff --git a/src/features/impl_serde.rs b/src/features/impl_serde.rs
index ed259f3..f562814 100644
--- a/src/features/impl_serde.rs
+++ b/src/features/impl_serde.rs
@@ -608,54 +608,52 @@ mod u32 {
     impl_serde_vec_types!(u32, UVec2, UVec3, UVec4);
 }
 
-mod affine3d {
-    use crate::{Affine3D, Mat3, Vec3};
+mod affine3 {
+    use crate::{Affine3, Mat3, Vec3};
     use core::fmt;
     use serde::{
         de::{self, Deserialize, Deserializer, SeqAccess, Visitor},
         ser::{Serialize, SerializeTupleStruct, Serializer},
     };
 
-    impl Serialize for Affine3D {
+    impl Serialize for Affine3 {
         fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where
             S: Serializer,
         {
             // Serialize column-wise as 3x4 matrix:
-            let mat3 = self.mat3();
-            let w_axis = self.translation();
-            let mut state = serializer.serialize_tuple_struct("Affine3D", 12)?;
-            state.serialize_field(&mat3.x_axis.x)?;
-            state.serialize_field(&mat3.x_axis.y)?;
-            state.serialize_field(&mat3.x_axis.z)?;
-            state.serialize_field(&mat3.y_axis.x)?;
-            state.serialize_field(&mat3.y_axis.y)?;
-            state.serialize_field(&mat3.y_axis.z)?;
-            state.serialize_field(&mat3.z_axis.x)?;
-            state.serialize_field(&mat3.z_axis.y)?;
-            state.serialize_field(&mat3.z_axis.z)?;
-            state.serialize_field(&w_axis.x)?;
-            state.serialize_field(&w_axis.y)?;
-            state.serialize_field(&w_axis.z)?;
+            let mut state = serializer.serialize_tuple_struct("Affine3", 12)?;
+            state.serialize_field(&self.x_axis.x)?;
+            state.serialize_field(&self.x_axis.y)?;
+            state.serialize_field(&self.x_axis.z)?;
+            state.serialize_field(&self.y_axis.x)?;
+            state.serialize_field(&self.y_axis.y)?;
+            state.serialize_field(&self.y_axis.z)?;
+            state.serialize_field(&self.z_axis.x)?;
+            state.serialize_field(&self.z_axis.y)?;
+            state.serialize_field(&self.z_axis.z)?;
+            state.serialize_field(&self.w_axis.x)?;
+            state.serialize_field(&self.w_axis.y)?;
+            state.serialize_field(&self.w_axis.z)?;
             state.end()
         }
     }
 
-    impl<'de> Deserialize<'de> for Affine3D {
+    impl<'de> Deserialize<'de> for Affine3 {
         fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
         where
             D: Deserializer<'de>,
         {
-            struct Affine3DVisitor;
+            struct Affine3Visitor;
 
-            impl<'de> Visitor<'de> for Affine3DVisitor {
-                type Value = Affine3D;
+            impl<'de> Visitor<'de> for Affine3Visitor {
+                type Value = Affine3;
 
                 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-                    formatter.write_str("struct Affine3D")
+                    formatter.write_str("struct Affine3")
                 }
 
-                fn visit_seq<V>(self, mut seq: V) -> Result<Affine3D, V::Error>
+                fn visit_seq<V>(self, mut seq: V) -> Result<Affine3, V::Error>
                 where
                     V: SeqAccess<'de>,
                 {
@@ -671,19 +669,19 @@ mod affine3d {
                         f[6], f[7], f[8], //
                     ]);
                     let t = Vec3::new(f[9], f[10], f[11]);
-                    Ok(Affine3D::from_mat3_translation(m, t))
+                    Ok(Affine3::from_mat3_translation(m, t))
                 }
             }
 
-            deserializer.deserialize_tuple_struct("Affine3D", 12, Affine3DVisitor)
+            deserializer.deserialize_tuple_struct("Affine3", 12, Affine3Visitor)
         }
     }
 
     #[test]
-    fn test_affine3d_serde() {
+    fn test_affine3_serde() {
         use crate::{Quat, Vec3};
 
-        let a = Affine3D::from_scale_rotation_translation(
+        let a = Affine3::from_scale_rotation_translation(
             Vec3::new(1.0, 2.0, 3.0),
             Quat::IDENTITY,
             Vec3::new(4.0, 5.0, 6.0),
@@ -696,20 +694,20 @@ mod affine3d {
         let deserialized = serde_json::from_str(&serialized).unwrap();
         assert_eq!(a, deserialized);
 
-        let deserialized = serde_json::from_str::<Affine3D>("[]");
+        let deserialized = serde_json::from_str::<Affine3>("[]");
         assert!(deserialized.is_err());
-        let deserialized = serde_json::from_str::<Affine3D>("[1.0]");
+        let deserialized = serde_json::from_str::<Affine3>("[1.0]");
         assert!(deserialized.is_err());
-        let deserialized = serde_json::from_str::<Affine3D>("[1.0,2.0]");
+        let deserialized = serde_json::from_str::<Affine3>("[1.0,2.0]");
         assert!(deserialized.is_err());
-        let deserialized = serde_json::from_str::<Affine3D>("[1.0,2.0,3.0]");
+        let deserialized = serde_json::from_str::<Affine3>("[1.0,2.0,3.0]");
         assert!(deserialized.is_err());
-        let deserialized = serde_json::from_str::<Affine3D>("[1.0,2.0,3.0,4.0,5.0]");
+        let deserialized = serde_json::from_str::<Affine3>("[1.0,2.0,3.0,4.0,5.0]");
         assert!(deserialized.is_err());
         let deserialized =
-            serde_json::from_str::<Affine3D>("[[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]]");
+            serde_json::from_str::<Affine3>("[[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]]");
         assert!(deserialized.is_err());
-        let deserialized = serde_json::from_str::<Affine3D>(
+        let deserialized = serde_json::from_str::<Affine3>(
             "[[1.0,2.0,3.0,4.0],[5.0,6.0,7.0,8.0],[9.0,10.0,11.0,12.0][13.0,14.0,15.0,16.0]]",
         );
         assert!(deserialized.is_err());
diff --git a/src/lib.rs b/src/lib.rs
index 6d9aba9..133195a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -196,7 +196,7 @@ mod vec;
 #[doc(hidden)]
 pub mod cast;
 
-mod affine3d;
+mod affine3;
 mod core;
 mod euler;
 mod mat2;
@@ -226,7 +226,7 @@ pub use self::bool::*;
 
 /** `f32` vector, quaternion and matrix types. */
 pub mod f32 {
-    pub use super::affine3d::Affine3D;
+    pub use super::affine3::Affine3;
     pub use super::mat2::{mat2, Mat2};
     pub use super::mat3::{mat3, Mat3};
     pub use super::mat4::{mat4, Mat4};
@@ -242,6 +242,7 @@ pub use self::f32::*;
 
 /** `f64` vector, quaternion and matrix types. */
 pub mod f64 {
+    pub use super::affine3::DAffine3;
     pub use super::mat2::{dmat2, DMat2};
     pub use super::mat3::{dmat3, DMat3};
     pub use super::mat4::{dmat4, DMat4};
diff --git a/src/mat2.rs b/src/mat2.rs
index 5c85d16..118f585 100644
--- a/src/mat2.rs
+++ b/src/mat2.rs
@@ -1,5 +1,5 @@
 use crate::core::{
-    storage::{Vector2x2, XY},
+    storage::{Columns2, XY},
     traits::matrix::{FloatMatrix2x2, Matrix2x2, MatrixConst},
 };
 use crate::{DMat3, DVec2, Mat3, Vec2};
@@ -328,7 +328,7 @@ macro_rules! impl_mat2_traits {
         }
 
         impl Deref for $mat2 {
-            type Target = Vector2x2<$vec2>;
+            type Target = Columns2<$vec2>;
             #[inline(always)]
             fn deref(&self) -> &Self::Target {
                 unsafe { &*(self as *const Self as *const Self::Target) }
@@ -392,7 +392,7 @@ macro_rules! impl_mat2_traits {
 type InnerF32 = __m128;
 
 #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
-type InnerF32 = crate::core::storage::Vector2x2<XY<f32>>;
+type InnerF32 = crate::core::storage::Columns2<XY<f32>>;
 
 /// A 2x2 column major matrix.
 #[derive(Clone, Copy)]
@@ -409,7 +409,7 @@ impl Mat2 {
 }
 impl_mat2_traits!(f32, mat2, Mat2, Mat3, Vec2);
 
-type InnerF64 = crate::core::storage::Vector2x2<XY<f64>>;
+type InnerF64 = crate::core::storage::Columns2<XY<f64>>;
 
 /// A 2x2 column major matrix.
 #[derive(Clone, Copy)]
diff --git a/src/mat3.rs b/src/mat3.rs
index bfceda0..ba9250a 100644
--- a/src/mat3.rs
+++ b/src/mat3.rs
@@ -1,8 +1,8 @@
 use crate::core::{
-    storage::{Vector3x3, XYZ},
+    storage::{Columns3, XYZ},
     traits::matrix::{FloatMatrix3x3, Matrix3x3, MatrixConst},
 };
-use crate::{DMat2, DMat4, DQuat, DVec2, DVec3, Mat2, Mat4, Quat, Vec2, Vec3, Vec3A, Vec3Swizzles};
+use crate::{DMat2, DMat4, DQuat, DVec2, DVec3, Mat2, Mat4, Quat, Vec2, Vec3, Vec3A};
 #[cfg(not(target_arch = "spirv"))]
 use core::fmt;
 use core::{
@@ -10,6 +10,19 @@ use core::{
     ops::{Add, Deref, DerefMut, Mul, Sub},
 };
 
+#[cfg(all(
+    target_arch = "x86",
+    target_feature = "sse2",
+    not(feature = "scalar-math")
+))]
+use core::arch::x86::*;
+#[cfg(all(
+    target_arch = "x86_64",
+    target_feature = "sse2",
+    not(feature = "scalar-math")
+))]
+use core::arch::x86_64::*;
+
 #[cfg(feature = "std")]
 use std::iter::{Product, Sum};
 
@@ -258,22 +271,6 @@ macro_rules! impl_mat3_methods {
         #[inline(always)]
         pub fn transpose(&self) -> Self {
             Self(self.0.transpose())
-            // {
-            //     #[cfg(target_arch = "x86")]
-            //     use core::arch::x86::*;
-            //     #[cfg(target_arch = "x86_64")]
-            //     use core::arch::x86_64::*;
-            //     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: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b00_00_10_00).into(),
-            //             y_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b01_01_11_01).into(),
-            //             z_axis: _mm_shuffle_ps(tmp1, self.z_axis.0, 0b10_10_10_00).into(),
-            //         }
-            //     }
-            // }
         }
 
         /// Returns the determinant of `self`.
@@ -293,17 +290,13 @@ macro_rules! impl_mat3_methods {
         /// Transforms a 3D vector.
         #[inline(always)]
         pub fn mul_vec3(&self, other: $vec3) -> $vec3 {
-            self.mul_vec3_as_vec3a(other)
+            $vec3::from_simd(self.to_simd().mul_vector(other.to_simd()))
         }
 
         /// Multiplies two 3x3 matrices.
         #[inline]
         pub fn mul_mat3(&self, other: &Self) -> Self {
-            Self::from_cols(
-                self.mul_vec3(other.x_axis),
-                self.mul_vec3(other.y_axis),
-                self.mul_vec3(other.z_axis),
-            )
+            Self::from_simd(self.to_simd().mul_matrix(&other.to_simd()))
         }
 
         /// Adds two 3x3 matrices.
@@ -392,7 +385,7 @@ macro_rules! impl_mat3_traits {
         }
 
         impl Deref for $mat3 {
-            type Target = Vector3x3<$vec3>;
+            type Target = Columns3<$vec3>;
             #[inline(always)]
             fn deref(&self) -> &Self::Target {
                 unsafe { &*(self as *const Self as *const Self::Target) }
@@ -514,7 +507,7 @@ macro_rules! impl_mat3_traits {
     };
 }
 
-type InnerF32 = Vector3x3<XYZ<f32>>;
+type InnerF32 = Columns3<XYZ<f32>>;
 define_mat3_struct!(Mat3, InnerF32);
 
 impl Mat3 {
@@ -523,16 +516,7 @@ impl Mat3 {
     /// Transforms a `Vec3A`.
     #[inline]
     pub fn mul_vec3a(&self, other: Vec3A) -> Vec3A {
-        let mut res = Vec3A::from(self.x_axis) * other.xxx();
-        res = Vec3A::from(self.y_axis).mul_add(other.yyy(), res);
-        res = Vec3A::from(self.z_axis).mul_add(other.zzz(), res);
-        res
-    }
-
-    /// Transforms a `Vec3`.
-    #[inline(always)]
-    fn mul_vec3_as_vec3a(&self, other: Vec3) -> Vec3 {
-        Vec3::from(self.mul_vec3a(Vec3A::from(other)))
+        Vec3A::from_simd(self.to_simd().mul_vector(other.to_simd()))
     }
 
     #[inline(always)]
@@ -543,6 +527,39 @@ impl Mat3 {
             self.z_axis.as_f64(),
         )
     }
+
+    #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+    #[inline(always)]
+    fn to_simd(&self) -> Columns3<__m128> {
+        Columns3 {
+            x_axis: self.x_axis.0.into(),
+            y_axis: self.y_axis.0.into(),
+            z_axis: self.z_axis.0.into(),
+        }
+    }
+
+    #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+    #[allow(dead_code)]
+    #[inline(always)]
+    fn from_simd(m: Columns3<__m128>) -> Self {
+        Self(Matrix3x3::from_cols(
+            m.x_axis.into(),
+            m.y_axis.into(),
+            m.z_axis.into(),
+        ))
+    }
+
+    #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+    #[inline(always)]
+    fn to_simd(&self) -> InnerF32 {
+        self.0
+    }
+
+    #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+    #[inline(always)]
+    fn from_simd(m: InnerF32) -> Self {
+        Self(m)
+    }
 }
 impl_mat3_traits!(f32, mat3, Mat3, Mat4, Vec3);
 
@@ -554,17 +571,12 @@ impl Mul<Vec3A> for Mat3 {
     }
 }
 
-type InnerF64 = Vector3x3<XYZ<f64>>;
+type InnerF64 = Columns3<XYZ<f64>>;
 define_mat3_struct!(DMat3, InnerF64);
 
 impl DMat3 {
     impl_mat3_methods!(f64, DVec3, DVec2, DQuat, DMat2, InnerF64);
 
-    #[inline(always)]
-    fn mul_vec3_as_vec3a(&self, other: DVec3) -> DVec3 {
-        DVec3(self.0.mul_vector(other.0))
-    }
-
     #[inline(always)]
     pub fn as_f32(&self) -> Mat3 {
         Mat3::from_cols(
@@ -573,5 +585,15 @@ impl DMat3 {
             self.z_axis.as_f32(),
         )
     }
+
+    #[inline(always)]
+    fn to_simd(&self) -> InnerF64 {
+        self.0
+    }
+
+    #[inline(always)]
+    fn from_simd(inner: InnerF64) -> Self {
+        Self(inner)
+    }
 }
 impl_mat3_traits!(f64, dmat3, DMat3, DMat4, DVec3);
diff --git a/src/mat4.rs b/src/mat4.rs
index 3e4602c..2655b06 100644
--- a/src/mat4.rs
+++ b/src/mat4.rs
@@ -1,5 +1,5 @@
 use crate::core::{
-    storage::{Vector4x4, XYZW},
+    storage::{Columns4, XYZW},
     traits::{
         matrix::{FloatMatrix4x4, Matrix4x4, MatrixConst},
         projection::ProjectionMatrix,
@@ -127,7 +127,7 @@ macro_rules! impl_mat4_methods {
         #[cfg_attr(docsrs, doc(alias = "scale"))]
         #[inline(always)]
         pub fn from_diagonal(diagonal: $vec4) -> Self {
-            Self($inner::from_diagonal(diagonal.0))
+            Self($inner::from_diagonal(diagonal.0.into()))
         }
 
         /// Creates an affine transformation matrix from the given 3D `scale`, `rotation` and
@@ -661,7 +661,7 @@ macro_rules! impl_mat4_traits {
         }
 
         impl Deref for $mat4 {
-            type Target = Vector4x4<$vec4>;
+            type Target = Columns4<$vec4>;
             #[inline(always)]
             fn deref(&self) -> &Self::Target {
                 unsafe { &*(self as *const Self as *const Self::Target) }
@@ -721,10 +721,10 @@ macro_rules! impl_mat4_traits {
 }
 
 #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
-type InnerF32 = Vector4x4<__m128>;
+type InnerF32 = Columns4<__m128>;
 
 #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
-type InnerF32 = Vector4x4<XYZW<f32>>;
+type InnerF32 = Columns4<XYZW<f32>>;
 
 define_mat4_struct!(Mat4, InnerF32);
 
@@ -759,7 +759,7 @@ impl Mat4 {
 }
 impl_mat4_traits!(f32, mat4, Mat4, Vec4);
 
-type InnerF64 = Vector4x4<XYZW<f64>>;
+type InnerF64 = Columns4<XYZW<f64>>;
 
 define_mat4_struct!(DMat4, InnerF64);
 
diff --git a/src/vec.rs b/src/vec.rs
index 9ab3a54..c4901d8 100644
--- a/src/vec.rs
+++ b/src/vec.rs
@@ -684,3 +684,17 @@ macro_rules! impl_vecn_as_u32 {
         }
     };
 }
+
+macro_rules! impl_vecn_to_simd_noop {
+    ($inner:ident) => {
+        #[inline(always)]
+        pub(crate) fn to_simd(&self) -> $inner {
+            self.0
+        }
+
+        #[inline(always)]
+        pub(crate) fn from_simd(inner: $inner) -> Self {
+            Self(inner)
+        }
+    };
+}
diff --git a/src/vec3.rs b/src/vec3.rs
index 02cae76..ce014a0 100644
--- a/src/vec3.rs
+++ b/src/vec3.rs
@@ -269,6 +269,30 @@ macro_rules! impl_f32_vec3 {
             impl_vecn_as_f64!(DVec3, x, y, z);
             impl_vecn_as_i32!(IVec3, x, y, z);
             impl_vecn_as_u32!(UVec3, x, y, z);
+
+            #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+            #[inline(always)]
+            pub(crate) fn to_simd(&self) -> __m128 {
+                self.0.into()
+            }
+
+            #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+            #[inline(always)]
+            pub(crate) fn from_simd(v: __m128) -> Self {
+                Self(v.into())
+            }
+
+            #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+            #[inline(always)]
+            pub(crate) fn to_simd(&self) -> $inner {
+                self.0
+            }
+
+            #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+            #[inline(always)]
+            pub(crate) fn from_simd(v: $inner) -> Self {
+                Self(v)
+            }
         }
         impl_vec3_float_traits!(f32, $new, $vec2, $vec3, $vec4, $mask, $inner);
     };
@@ -338,6 +362,7 @@ impl DVec3 {
     impl_vecn_as_f32!(Vec3, x, y, z);
     impl_vecn_as_i32!(IVec3, x, y, z);
     impl_vecn_as_u32!(UVec3, x, y, z);
+    impl_vecn_to_simd_noop!(XYZF64);
 }
 impl_vec3_float_traits!(f64, dvec3, DVec2, DVec3, DVec4, BVec3, XYZF64);
 


Test patch:
diff --git a/tests/affine3d.rs b/tests/affine3.rs
similarity index 90%
rename from tests/affine3d.rs
rename to tests/affine3.rs
index b979804..b43f7f1 100644
--- a/tests/affine3d.rs
+++ b/tests/affine3.rs
@@ -233,16 +233,38 @@ macro_rules! impl_affine3d_tests {
     };
 }
 
-mod affine3d {
+mod affine3 {
     use super::support::deg;
-    use glam::{Affine3D, Quat, Vec3};
+    use glam::{Affine3, Quat, Vec3};
 
     #[test]
     fn test_align() {
         use std::mem;
-        assert_eq!(64, mem::size_of::<Affine3D>());
-        assert_eq!(16, mem::align_of::<Affine3D>());
+        #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+        assert_eq!(64, mem::size_of::<Affine3>());
+        #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+        assert_eq!(16, mem::align_of::<Affine3>());
+
+        // TODO: should we force alignment and size to be the same here?
+        #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+        assert_eq!(48, mem::size_of::<Affine3>());
+        #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+        assert_eq!(4, mem::align_of::<Affine3>());
+    }
+
+    impl_affine3d_tests!(f32, Affine3, Quat, Vec3);
+}
+
+mod daffine3 {
+    use super::support::deg;
+    use glam::{DAffine3, DQuat, DVec3};
+
+    #[test]
+    fn test_align() {
+        use std::mem;
+        assert_eq!(96, mem::size_of::<DAffine3>());
+        assert_eq!(8, mem::align_of::<DAffine3>());
     }
 
-    impl_affine3d_tests!(f32, Affine3D, Quat, Vec3);
+    impl_affine3d_tests!(f64, DAffine3, DQuat, DVec3);
 }
diff --git a/tests/support/mod.rs b/tests/support/mod.rs
index 67cc9ab..575f227 100644
--- a/tests/support/mod.rs
+++ b/tests/support/mod.rs
@@ -2,8 +2,8 @@
 mod macros;
 
 use glam::{
-    Affine3D, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2, Mat3, Mat4, Quat, Vec2, Vec3,
-    Vec3A, Vec4,
+    Affine3, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2, Mat3, Mat4, Quat,
+    Vec2, Vec3, Vec3A, Vec4,
 };
 
 #[cfg(feature = "transform-types")]
@@ -145,7 +145,7 @@ impl FloatCompare for DMat4 {
     }
 }
 
-impl FloatCompare for Affine3D {
+impl FloatCompare for Affine3 {
     #[inline]
     fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
@@ -159,6 +159,20 @@ impl FloatCompare for Affine3D {
     }
 }
 
+impl FloatCompare for DAffine3 {
+    #[inline]
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
+        self.abs_diff_eq(*other, max_abs_diff as f64)
+    }
+    #[inline]
+    fn abs_diff(&self, other: &Self) -> Self {
+        Self::from_mat3_translation(
+            self.mat3().abs_diff(&other.mat3()),
+            self.translation().abs_diff(&other.translation()),
+        )
+    }
+}
+
 impl FloatCompare for Mat4 {
     #[inline]
     fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {