From 3ee87ba81c68e436be2a6dd6b7bff86c5aa8ca70 Mon Sep 17 00:00:00 2001 From: Joshua Simmons Date: Tue, 6 Sep 2022 23:43:38 +0200 Subject: [PATCH] Rework maths library Split into multiple files. Add `Affine2` and `Affine3` transformation types. Add `Point2` and `Point3` types. Use macro instead of repeating basic `Point` and `Vec` implementation. Change `Mat4 * Point` to use homogeneous coordinates where `w=1`. Change `Mat4 * Vec` to use homogeneous coordinates where `w=0`. --- narcissus-maths/src/affine2.rs | 8 ++ narcissus-maths/src/affine3.rs | 8 ++ narcissus-maths/src/lib.rs | 92 +++++++++++++++ narcissus-maths/src/mat2.rs | 3 + narcissus-maths/src/mat3.rs | 3 + narcissus-maths/src/mat4.rs | 182 ++++++++++++++++++++++-------- narcissus-maths/src/point2.rs | 84 ++++++++++++++ narcissus-maths/src/point3.rs | 109 ++++++++++++++++++ narcissus-maths/src/quat.rs | 23 ++++ narcissus-maths/src/vec2.rs | 74 +++---------- narcissus-maths/src/vec3.rs | 86 ++++---------- narcissus-maths/src/vec4.rs | 197 +++++++++------------------------ 12 files changed, 554 insertions(+), 315 deletions(-) create mode 100644 narcissus-maths/src/affine2.rs create mode 100644 narcissus-maths/src/affine3.rs create mode 100644 narcissus-maths/src/mat2.rs create mode 100644 narcissus-maths/src/mat3.rs create mode 100644 narcissus-maths/src/point2.rs create mode 100644 narcissus-maths/src/point3.rs diff --git a/narcissus-maths/src/affine2.rs b/narcissus-maths/src/affine2.rs new file mode 100644 index 0000000..42a0a2c --- /dev/null +++ b/narcissus-maths/src/affine2.rs @@ -0,0 +1,8 @@ +use crate::{Mat2, Vec2}; + +#[derive(Clone, Copy, PartialEq)] +#[repr(C)] +pub struct Affine2 { + matrix: Mat2, + translate: Vec2, +} diff --git a/narcissus-maths/src/affine3.rs b/narcissus-maths/src/affine3.rs new file mode 100644 index 0000000..f13a653 --- /dev/null +++ b/narcissus-maths/src/affine3.rs @@ -0,0 +1,8 @@ +use crate::{Mat3, Vec3}; + +#[derive(Clone, Copy, PartialEq)] +#[repr(C)] +pub struct Affine3 { + matrix: Mat3, + translate: Vec3, +} diff --git a/narcissus-maths/src/lib.rs b/narcissus-maths/src/lib.rs index 4c499a0..4331246 100644 --- a/narcissus-maths/src/lib.rs +++ b/narcissus-maths/src/lib.rs @@ -1,11 +1,103 @@ +mod affine2; +mod affine3; +mod mat2; +mod mat3; mod mat4; +mod point2; +mod point3; mod quat; mod vec2; mod vec3; mod vec4; +pub use affine2::Affine2; +pub use affine3::Affine3; +pub use mat2::Mat2; +pub use mat3::Mat3; pub use mat4::Mat4; +pub use point2::Point2; +pub use point3::Point3; pub use quat::Quat; pub use vec2::Vec2; pub use vec3::Vec3; pub use vec4::Vec4; + +#[macro_export] +macro_rules! impl_shared { + ($name:ty, $t:ty, $n:expr) => { + impl $name { + pub const ZERO: Self = Self::splat(0.0); + pub const ONE: Self = Self::splat(1.0); + pub const NAN: Self = Self::splat(0.0 / 0.0); + + #[inline(always)] + pub const fn splat(value: $t) -> Self { + // we have to transmute here because we can't make `into()` const. + unsafe { std::mem::transmute([value; $n]) } + } + + #[inline(always)] + pub fn ceil(self) -> Self { + self.map(|x| x.ceil()) + } + + #[inline(always)] + pub fn floor(self) -> Self { + self.map(|x| x.floor()) + } + + #[inline(always)] + pub fn round(self) -> Self { + self.map(|x| x.round()) + } + } + + impl From<[$t; $n]> for $name { + #[inline(always)] + fn from(x: [$t; $n]) -> Self { + unsafe { std::mem::transmute(x) } + } + } + + impl From<$name> for [$t; $n] { + #[inline(always)] + fn from(x: $name) -> [$t; $n] { + unsafe { std::mem::transmute(x) } + } + } + }; +} + +#[macro_export] +macro_rules! impl_affine { + ($name:ty, $t:ty, $n:expr) => { + impl $name { + #[inline] + pub fn distance(a: Self, b: Self) -> $t { + (a - b).length() + } + + #[inline] + pub fn distance_sq(a: Self, b: Self) -> $t { + (a - b).length_sq() + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector { + ($name:ty, $t:ty, $n:expr) => { + impl $name { + #[inline] + pub fn length(self) -> $t { + self.length_sq().sqrt() + } + + #[inline] + pub fn length_sq(self) -> $t { + Self::dot(self, self) + } + } + }; +} diff --git a/narcissus-maths/src/mat2.rs b/narcissus-maths/src/mat2.rs new file mode 100644 index 0000000..8b21e7b --- /dev/null +++ b/narcissus-maths/src/mat2.rs @@ -0,0 +1,3 @@ +#[derive(Clone, Copy, PartialEq)] +#[repr(C)] +pub struct Mat2(pub [f32; 4]); diff --git a/narcissus-maths/src/mat3.rs b/narcissus-maths/src/mat3.rs new file mode 100644 index 0000000..93a772a --- /dev/null +++ b/narcissus-maths/src/mat3.rs @@ -0,0 +1,3 @@ +#[derive(Clone, Copy, PartialEq)] +#[repr(C)] +pub struct Mat3(pub [f32; 9]); diff --git a/narcissus-maths/src/mat4.rs b/narcissus-maths/src/mat4.rs index 9c7311b..074eb2a 100644 --- a/narcissus-maths/src/mat4.rs +++ b/narcissus-maths/src/mat4.rs @@ -1,4 +1,4 @@ -use crate::{Vec3, Vec4}; +use crate::{Point2, Point3, Vec2, Vec3, Vec4}; #[derive(Clone, Copy, PartialEq)] #[repr(C)] @@ -6,35 +6,27 @@ pub struct Mat4(pub [f32; 16]); impl std::fmt::Debug for Mat4 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Mat4 [")?; if f.alternate() { - writeln!(f)?; - for row in self.as_rows_array() { - f.write_str("\t")?; - for value in row { - f.write_fmt(format_args!("{value}, "))?; - } - f.write_str("\n")?; + writeln!(f, "Mat4 [")?; + for row in self.as_rows() { + writeln!(f, "\t{:?}", row)?; } + writeln!(f, "]") } else { - for value in &self.0[..15] { - f.write_fmt(format_args!("{value}, "))?; - } - f.write_fmt(format_args!("{}", self.0[15]))?; + writeln!(f, "Mat4 {:?}", self.as_rows()) } - f.write_str("]") } } impl Mat4 { - pub const ZERO: Mat4 = Mat4::from_rows_array([ + pub const ZERO: Mat4 = Mat4::from_rows([ [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], ]); - pub const IDENTITY: Mat4 = Mat4::from_rows_array([ + pub const IDENTITY: Mat4 = Mat4::from_rows([ [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], @@ -42,17 +34,17 @@ impl Mat4 { ]); #[inline(always)] - pub fn as_rows_array(&self) -> &[[f32; 4]; 4] { + pub fn as_rows(&self) -> &[[f32; 4]; 4] { unsafe { std::mem::transmute(&self.0) } } #[inline(always)] - pub fn as_rows_array_mut(&mut self) -> &mut [[f32; 4]; 4] { + pub fn as_rows_mut(&mut self) -> &mut [[f32; 4]; 4] { unsafe { std::mem::transmute(&mut self.0) } } #[inline(always)] - pub const fn from_rows_array(rows: [[f32; 4]; 4]) -> Self { + pub const fn from_rows(rows: [[f32; 4]; 4]) -> Self { unsafe { std::mem::transmute(rows) } } @@ -85,8 +77,26 @@ impl Mat4 { result } + pub const fn from_diagonal(diagonal: Vec4) -> Mat4 { + Mat4::from_rows([ + [diagonal.x, 0.0, 0.0, 0.0], + [0.0, diagonal.y, 0.0, 0.0], + [0.0, 0.0, diagonal.z, 0.0], + [0.0, 0.0, 0.0, diagonal.w], + ]) + } + + pub const fn from_rotation(scale: Vec3) -> Mat4 { + Mat4::from_rows([ + [scale.x, 0.0, 0.0, 0.0], + [0.0, scale.y, 0.0, 0.0], + [0.0, 0.0, scale.z, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]) + } + pub const fn from_scale(scale: Vec3) -> Mat4 { - Mat4::from_rows_array([ + Mat4::from_rows([ [scale.x, 0.0, 0.0, 0.0], [0.0, scale.y, 0.0, 0.0], [0.0, 0.0, scale.z, 0.0], @@ -95,7 +105,7 @@ impl Mat4 { } pub const fn from_translation(translation: Vec3) -> Mat4 { - Mat4::from_rows_array([ + Mat4::from_rows([ [1.0, 0.0, 0.0, translation.x], [0.0, 1.0, 0.0, translation.y], [0.0, 0.0, 1.0, translation.z], @@ -107,7 +117,7 @@ impl Mat4 { #[inline(always)] fn transpose_base(self) -> Mat4 { let m = &self.0; - Mat4::from_rows_array([ + Mat4::from_rows([ [m[0x0], m[0x4], m[0x8], m[0xc]], [m[0x1], m[0x5], m[0x9], m[0xd]], [m[0x2], m[0x6], m[0xa], m[0xe]], @@ -137,22 +147,46 @@ impl Mat4 { } } + #[must_use] + #[inline] + pub fn mul_vec2(&self, vec: Vec2) -> Vec2 { + let vec = Vec4::new(vec.x, vec.y, 0.0, 0.0); + let vec = self.mul_vec4(vec); + Vec2::new(vec.x, vec.y) + } + + #[must_use] + #[inline] + pub fn mul_point2(&self, point: Point2) -> Point2 { + let vec = Vec4::new(point.x, point.y, 0.0, 1.0); + let vec = self.mul_vec4(vec); + Point2::new(vec.x, vec.y) + } + #[must_use] #[inline] pub fn mul_vec3(&self, vec: Vec3) -> Vec3 { - let vec = Vec4::new(vec.x, vec.y, vec.z, 1.0); + let vec = Vec4::new(vec.x, vec.y, vec.z, 0.0); + let vec = self.mul_vec4(vec); + [vec.x, vec.y, vec.z].into() + } + + #[must_use] + #[inline] + pub fn mul_point3(&self, point: Point3) -> Point3 { + let vec = Vec4::new(point.x, point.y, point.z, 1.0); let vec = self.mul_vec4(vec); - Vec3::new(vec.x, vec.y, vec.z) + Point3::new(vec.x, vec.y, vec.z) } #[inline(always)] fn mul_vec4_base(&self, vec: Vec4) -> Vec4 { - let rows = self.as_rows_array(); + let rows = self.as_rows(); Vec4::new( - Vec4::dot(Vec4::from_array(rows[0]), vec), - Vec4::dot(Vec4::from_array(rows[1]), vec), - Vec4::dot(Vec4::from_array(rows[2]), vec), - Vec4::dot(Vec4::from_array(rows[3]), vec), + Vec4::dot(rows[0].into(), vec), + Vec4::dot(rows[1].into(), vec), + Vec4::dot(rows[2].into(), vec), + Vec4::dot(rows[3].into(), vec), ) } @@ -162,7 +196,7 @@ impl Mat4 { unsafe fn mul_vec4_sse41(&self, vec: Vec4) -> Vec4 { use std::arch::x86_64::{_mm_hadd_ps, _mm_mul_ps}; - let vec = vec.as_m128(); + let vec = vec.into(); let rows = self.as_m128_array(); let values = _mm_hadd_ps( @@ -170,7 +204,7 @@ impl Mat4 { _mm_hadd_ps(_mm_mul_ps(rows[2], vec), _mm_mul_ps(rows[3], vec)), ); - Vec4::from_m128(values) + values.into() } #[must_use] @@ -192,9 +226,9 @@ impl Mat4 { fn mul_mat4_base(self: &Mat4, rhs: Mat4) -> Mat4 { let mut result = Mat4::IDENTITY; { - let result = result.as_rows_array_mut(); - let lhs = self.as_rows_array(); - let rhs = rhs.as_rows_array(); + let result = result.as_rows_mut(); + let lhs = self.as_rows(); + let rhs = rhs.as_rows(); for i in 0..4 { for j in 0..4 { result[i][j] = lhs[i][0] * rhs[0][j] @@ -244,19 +278,19 @@ impl Mat4 { }; #[inline(always)] - unsafe fn two_linear_combine(a: __m256, mat: &[__m128; 4]) -> __m256 { - let r = _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x00), _mm256_broadcast_ps(&mat[0])); + unsafe fn two_linear_combine(a: __m256, m: &[__m128; 4]) -> __m256 { + let r = _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x00), _mm256_broadcast_ps(&m[0])); let r = _mm256_add_ps( r, - _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x55), _mm256_broadcast_ps(&mat[1])), + _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x55), _mm256_broadcast_ps(&m[1])), ); let r = _mm256_add_ps( r, - _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xaa), _mm256_broadcast_ps(&mat[2])), + _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xaa), _mm256_broadcast_ps(&m[2])), ); _mm256_add_ps( r, - _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xff), _mm256_broadcast_ps(&mat[3])), + _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xff), _mm256_broadcast_ps(&m[3])), ) } @@ -321,6 +355,33 @@ impl std::ops::Mul for Mat4 { } } +impl std::ops::Mul for Mat4 { + type Output = Point3; + + #[inline(always)] + fn mul(self, rhs: Point3) -> Self::Output { + self.mul_point3(rhs) + } +} + +impl std::ops::Mul for Mat4 { + type Output = Vec2; + + #[inline(always)] + fn mul(self, rhs: Vec2) -> Self::Output { + self.mul_vec2(rhs) + } +} + +impl std::ops::Mul for Mat4 { + type Output = Point2; + + #[inline(always)] + fn mul(self, rhs: Point2) -> Self::Output { + self.mul_point2(rhs) + } +} + #[cfg(test)] mod tests { use super::*; @@ -328,20 +389,24 @@ mod tests { const IDENTITY: Mat4 = Mat4::IDENTITY; const SCALE: Mat4 = Mat4::from_scale(Vec3::splat(2.0)); const TRANSLATE: Mat4 = Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0)); - const M: Mat4 = Mat4::from_rows_array([ + const M: Mat4 = Mat4::from_rows([ [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], ]); - const T: Mat4 = Mat4::from_rows_array([ + const T: Mat4 = Mat4::from_rows([ [1.0, 5.0, 9.0, 13.0], [2.0, 6.0, 10.0, 14.0], [3.0, 7.0, 11.0, 15.0], [4.0, 8.0, 12.0, 16.0], ]); + + const V2: Vec2 = Vec2::new(1.0, 2.0); const V3: Vec3 = Vec3::new(1.0, 2.0, 3.0); - const V: Vec4 = Vec4::new(1.0, 2.0, 3.0, 4.0); + const V4: Vec4 = Vec4::new(1.0, 2.0, 3.0, 4.0); + const P2: Point2 = Point2::new(1.0, 2.0); + const P3: Point3 = Point3::new(1.0, 2.0, 3.0); #[test] fn transpose() { @@ -373,19 +438,46 @@ mod tests { } } + #[test] + fn mul_vec2() { + assert_eq!(IDENTITY * Vec2::ZERO, Vec2::ZERO); + assert_eq!(IDENTITY * V2, V2); + assert_eq!(SCALE * Vec2::ZERO, Vec2::ZERO); + assert_eq!(SCALE * Vec2::ONE, Vec2::splat(2.0)); + assert_eq!(TRANSLATE * Vec2::ZERO, Vec2::ZERO); + } + + #[test] + fn mul_point2() { + assert_eq!(IDENTITY * Point2::ZERO, Point2::ZERO); + assert_eq!(IDENTITY * P2, P2); + assert_eq!(SCALE * Point2::ZERO, Point2::ZERO); + assert_eq!(SCALE * Point2::ONE, Point2::splat(2.0)); + assert_eq!(TRANSLATE * Point2::ZERO, P2); + } + #[test] fn mul_vec3() { assert_eq!(IDENTITY * Vec3::ZERO, Vec3::ZERO); assert_eq!(IDENTITY * V3, V3); assert_eq!(SCALE * Vec3::ZERO, Vec3::ZERO); assert_eq!(SCALE * Vec3::ONE, Vec3::splat(2.0)); - assert_eq!(TRANSLATE * Vec3::ZERO, V3); + assert_eq!(TRANSLATE * Vec3::ZERO, Vec3::ZERO); + } + + #[test] + fn mul_point3() { + assert_eq!(IDENTITY * Point3::ZERO, Point3::ZERO); + assert_eq!(IDENTITY * P3, P3); + assert_eq!(SCALE * Point3::ZERO, Point3::ZERO); + assert_eq!(SCALE * Point3::ONE, Point3::splat(2.0)); + assert_eq!(TRANSLATE * Point3::ZERO, P3); } #[test] fn mul_vec4() { assert_eq!(IDENTITY * Vec4::ZERO, Vec4::ZERO); - assert_eq!(IDENTITY * V, V); + assert_eq!(IDENTITY * V4, V4); assert_eq!(SCALE * Vec4::ZERO, Vec4::ZERO); assert_eq!(SCALE * Vec4::ONE, Vec4::new(2.0, 2.0, 2.0, 1.0)); assert_eq!( @@ -396,7 +488,7 @@ mod tests { if std::is_x86_feature_detected!("sse4.1") { unsafe { assert_eq!(IDENTITY.mul_vec4_sse41(Vec4::ZERO), Vec4::ZERO); - assert_eq!(IDENTITY.mul_vec4_sse41(V), V); + assert_eq!(IDENTITY.mul_vec4_sse41(V4), V4); assert_eq!(SCALE.mul_vec4_sse41(Vec4::ZERO), Vec4::ZERO); assert_eq!( SCALE.mul_vec4_sse41(Vec4::ONE), diff --git a/narcissus-maths/src/point2.rs b/narcissus-maths/src/point2.rs new file mode 100644 index 0000000..109d197 --- /dev/null +++ b/narcissus-maths/src/point2.rs @@ -0,0 +1,84 @@ +use crate::{impl_affine, impl_shared, Vec2}; + +/// Type representing a point in a 2d affine space. +#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)] +#[repr(C)] +pub struct Point2 { + pub x: f32, + pub y: f32, +} + +impl_shared!(Point2, f32, 2); +impl_affine!(Point2, f32, 2); + +impl Point2 { + pub const X: Self = Self::new(1.0, 0.0); + pub const Y: Self = Self::new(0.0, 1.0); + + /// Creates a new `Point2` with the given `x` and `y` coordinates. + #[inline(always)] + pub const fn new(x: f32, y: f32) -> Self { + Self { x, y } + } + + /// Returns a new `Point2` with the function `f` applied to each coordinate in order. + #[inline(always)] + pub fn map(self, mut f: F) -> Self + where + F: FnMut(f32) -> f32, + { + Self { + x: f(self.x), + y: f(self.y), + } + } +} + +impl std::ops::Sub for Point2 { + type Output = Vec2; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self::Output { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} + +impl std::ops::Add for Point2 { + type Output = Point2; + #[inline] + fn add(self, rhs: Vec2) -> Self::Output { + Self::Output { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} + +impl std::ops::Sub for Point2 { + type Output = Point2; + #[inline] + fn sub(self, rhs: Vec2) -> Self::Output { + Self::Output { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} + +impl std::ops::AddAssign for Point2 { + #[inline] + fn add_assign(&mut self, rhs: Vec2) { + self.x += rhs.x; + self.y += rhs.y; + } +} + +impl std::ops::SubAssign for Point2 { + #[inline] + fn sub_assign(&mut self, rhs: Vec2) { + self.x -= rhs.x; + self.y -= rhs.y; + } +} diff --git a/narcissus-maths/src/point3.rs b/narcissus-maths/src/point3.rs new file mode 100644 index 0000000..721e6c0 --- /dev/null +++ b/narcissus-maths/src/point3.rs @@ -0,0 +1,109 @@ +use crate::{impl_affine, impl_shared, Vec3}; + +/// Type representing a point in a 3d affine space. +#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)] +#[repr(C)] +pub struct Point3 { + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl_shared!(Point3, f32, 3); +impl_affine!(Point3, f32, 3); + +impl Point3 { + pub const X: Self = Self::new(1.0, 0.0, 0.0); + pub const Y: Self = Self::new(0.0, 1.0, 0.0); + pub const Z: Self = Self::new(0.0, 0.0, 1.0); + + /// Creates a new point in 3d space with the given `x`, `y` and `z` coordinates. + #[inline(always)] + pub const fn new(x: f32, y: f32, z: f32) -> Self { + Self { x, y, z } + } + + /// Returns a new point in 3d space with the function `f` applied to each coordinate in order. + #[inline(always)] + pub fn map(self, mut f: F) -> Self + where + F: FnMut(f32) -> f32, + { + Self { + x: f(self.x), + y: f(self.y), + z: f(self.z), + } + } +} + +impl std::ops::Sub for Point3 { + type Output = Vec3; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Self::Output { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, + } + } +} + +impl std::ops::Add for Point3 { + type Output = Point3; + #[inline] + fn add(self, rhs: Vec3) -> Self::Output { + Self::Output { + x: self.x + rhs.x, + y: self.y + rhs.y, + z: self.z + rhs.z, + } + } +} + +impl std::ops::Sub for Point3 { + type Output = Point3; + #[inline] + fn sub(self, rhs: Vec3) -> Self::Output { + Self::Output { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, + } + } +} + +impl std::ops::AddAssign for Point3 { + #[inline] + fn add_assign(&mut self, rhs: Vec3) { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + } +} + +impl std::ops::SubAssign for Point3 { + #[inline] + fn sub_assign(&mut self, rhs: Vec3) { + self.x -= rhs.x; + self.y -= rhs.y; + self.z -= rhs.z; + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + pub fn distance() { + // nice triangle + assert_eq!( + Point3::distance_sq(Point3::ZERO, Point3::new(5.0, 12.0, 0.0)), + 169.0 + ); + assert_eq!( + Point3::distance(Point3::ZERO, Point3::new(5.0, 12.0, 0.0)), + 13.0 + ); + } +} diff --git a/narcissus-maths/src/quat.rs b/narcissus-maths/src/quat.rs index a7d2b9c..df374dc 100644 --- a/narcissus-maths/src/quat.rs +++ b/narcissus-maths/src/quat.rs @@ -6,3 +6,26 @@ pub struct Quat { pub c: f32, pub d: f32, } + +impl Quat { + pub const ZERO: Self = Self { + a: 0.0, + b: 0.0, + c: 0.0, + d: 0.0, + }; + + pub const IDENTITY: Self = Self { + a: 0.0, + b: 0.0, + c: 0.0, + d: 1.0, + }; + + pub const NAN: Self = Self { + a: std::f32::NAN, + b: std::f32::NAN, + c: std::f32::NAN, + d: std::f32::NAN, + }; +} diff --git a/narcissus-maths/src/vec2.rs b/narcissus-maths/src/vec2.rs index 3828a61..8056dbe 100644 --- a/narcissus-maths/src/vec2.rs +++ b/narcissus-maths/src/vec2.rs @@ -1,3 +1,5 @@ +use crate::{impl_shared, impl_vector}; + #[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)] #[repr(C)] pub struct Vec2 { @@ -5,80 +7,34 @@ pub struct Vec2 { pub y: f32, } -impl Vec2 { - pub const ZERO: Self = Self::splat(0.0); - pub const ONE: Self = Self::splat(1.0); +impl_shared!(Vec2, f32, 2); +impl_vector!(Vec2, f32, 2); +impl Vec2 { pub const X: Self = Self::new(1.0, 0.0); pub const Y: Self = Self::new(0.0, 1.0); + /// Creates a new 2d vector with the given `x` and `y` components. #[inline(always)] pub const fn new(x: f32, y: f32) -> Self { Self { x, y } } + /// Returns a new 2d vector with the function `f` applied to each component in order. #[inline(always)] - pub const fn splat(value: f32) -> Self { - Self { x: value, y: value } - } - - #[inline(always)] - pub fn as_array(self) -> [f32; 2] { - unsafe { std::mem::transmute(self) } - } - - #[inline(always)] - pub fn from_array(values: [f32; 2]) -> Self { - unsafe { std::mem::transmute(values) } - } - - #[inline] - pub fn distance(a: Self, b: Self) -> f32 { - (a - b).length() - } - - #[inline] - pub fn distance_sq(a: Self, b: Self) -> f32 { - (a - b).length_sq() - } - - #[inline] - pub fn dot(a: Self, b: Self) -> f32 { - a.x * b.x + a.y * b.y - } - - #[inline] - pub fn length(self) -> f32 { - self.length_sq().sqrt() - } - - #[inline] - pub fn length_sq(self) -> f32 { - Self::dot(self, self) - } - - #[inline] - pub fn ceil(self) -> Self { + pub fn map(self, mut f: F) -> Self + where + F: FnMut(f32) -> f32, + { Self { - x: self.x.ceil(), - y: self.y.ceil(), + x: f(self.x), + y: f(self.y), } } #[inline] - pub fn floor(self) -> Self { - Self { - x: self.x.floor(), - y: self.y.floor(), - } - } - - #[inline] - pub fn round(self) -> Self { - Self { - x: self.x.round(), - y: self.y.round(), - } + pub fn dot(a: Self, b: Self) -> f32 { + a.x * b.x + a.y * b.y } } diff --git a/narcissus-maths/src/vec3.rs b/narcissus-maths/src/vec3.rs index dc5fb60..c5a3665 100644 --- a/narcissus-maths/src/vec3.rs +++ b/narcissus-maths/src/vec3.rs @@ -1,3 +1,5 @@ +use crate::{impl_shared, impl_vector}; + #[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)] #[repr(C)] pub struct Vec3 { @@ -6,88 +8,42 @@ pub struct Vec3 { pub z: f32, } -impl Vec3 { - pub const ZERO: Self = Self::splat(0.0); - pub const ONE: Self = Self::splat(1.0); - - pub const X: Self = Self::new(1.0, 0.0, 0.0); - pub const Y: Self = Self::new(0.0, 1.0, 0.0); - pub const Z: Self = Self::new(0.0, 0.0, 1.0); +impl_shared!(Vec3, f32, 3); +impl_vector!(Vec3, f32, 3); +impl Vec3 { + /// Creates a new 3d vector with the given `x`, `y` and `z` components. #[inline(always)] pub const fn new(x: f32, y: f32, z: f32) -> Self { Self { x, y, z } } + /// Returns a new 3d vector with the function `f` applied to each component in order. #[inline(always)] - pub const fn splat(value: f32) -> Self { + pub fn map(self, mut f: F) -> Self + where + F: FnMut(f32) -> f32, + { Self { - x: value, - y: value, - z: value, + x: f(self.x), + y: f(self.y), + z: f(self.z), } } - #[inline(always)] - pub fn as_array(self) -> [f32; 3] { - unsafe { std::mem::transmute(self) } - } - - #[inline(always)] - pub fn from_array(values: [f32; 3]) -> Self { - unsafe { std::mem::transmute(values) } - } - - #[inline] - pub fn distance(a: Self, b: Self) -> f32 { - (a - b).length() - } - - #[inline] - pub fn distance_sq(a: Self, b: Self) -> f32 { - (a - b).length_sq() - } - #[inline] pub fn dot(a: Self, b: Self) -> f32 { a.x * b.x + a.y * b.y + a.z * b.z } #[inline] - pub fn length(self) -> f32 { - self.length_sq().sqrt() - } - - #[inline] - pub fn length_sq(self) -> f32 { - Self::dot(self, self) - } - - #[inline] - pub fn ceil(self) -> Self { - Self { - x: self.x.ceil(), - y: self.y.ceil(), - z: self.z.ceil(), - } - } - - #[inline] - pub fn floor(self) -> Self { - Self { - x: self.x.floor(), - y: self.y.floor(), - z: self.z.floor(), - } - } - - #[inline] - pub fn round(self) -> Self { - Self { - x: self.x.round(), - y: self.y.round(), - z: self.z.round(), - } + pub fn cross(a: Self, b: Self) -> Vec3 { + [ + a.y * b.z - a.z * b.y, + -(a.x * b.z - a.z * b.x), + a.x * b.y - a.y * b.x, + ] + .into() } } diff --git a/narcissus-maths/src/vec4.rs b/narcissus-maths/src/vec4.rs index 4953317..f0f2392 100644 --- a/narcissus-maths/src/vec4.rs +++ b/narcissus-maths/src/vec4.rs @@ -1,3 +1,5 @@ +use crate::{impl_shared, impl_vector}; + #[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)] #[repr(C)] pub struct Vec4 { @@ -7,111 +9,75 @@ pub struct Vec4 { pub w: f32, } -impl Vec4 { - pub const ZERO: Self = Self::splat(0.0); - pub const ONE: Self = Self::splat(1.0); +impl_shared!(Vec4, f32, 4); +impl_vector!(Vec4, f32, 4); +impl Vec4 { pub const X: Self = Self::new(1.0, 0.0, 0.0, 0.0); pub const Y: Self = Self::new(0.0, 1.0, 0.0, 0.0); pub const Z: Self = Self::new(0.0, 0.0, 1.0, 0.0); pub const W: Self = Self::new(0.0, 0.0, 0.0, 1.0); + /// Creates a new 4d vector with the given `x`, `y`, `z` and `w` components. #[inline(always)] pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self { Self { x, y, z, w } } + /// Returns a new 4d vector with the function `f` applied to each component in order. #[inline(always)] - pub const fn splat(value: f32) -> Self { + pub fn map(self, mut f: F) -> Self + where + F: FnMut(f32) -> f32, + { Self { - x: value, - y: value, - z: value, - w: value, + x: f(self.x), + y: f(self.y), + z: f(self.z), + w: f(self.w), } } - #[inline(always)] - pub fn as_array(self) -> [f32; 4] { - unsafe { std::mem::transmute(self) } - } - - #[inline(always)] - pub fn from_array(values: [f32; 4]) -> Self { - unsafe { std::mem::transmute(values) } - } - - #[cfg(target_feature = "sse2")] - #[inline(always)] - pub(crate) fn as_m128(self) -> std::arch::x86_64::__m128 { - unsafe { std::arch::x86_64::_mm_loadu_ps(&self.x) } + #[inline] + pub fn dot(a: Self, b: Self) -> f32 { + a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w } +} - #[cfg(target_feature = "sse2")] +#[cfg(target_feature = "sse2")] +impl From for Vec4 { #[inline(always)] - pub(crate) fn from_m128(values: std::arch::x86_64::__m128) -> Self { + fn from(values: std::arch::x86_64::__m128) -> Self { use std::arch::x86_64::_mm_storeu_ps; let mut result = Vec4::ZERO; unsafe { _mm_storeu_ps(&mut result.x, values) } result } +} - #[inline] - pub fn distance(a: Self, b: Self) -> f32 { - (a - b).length() - } - - #[inline] - pub fn distance_sq(a: Self, b: Self) -> f32 { - (a - b).length_sq() - } - - #[inline] - pub fn dot(a: Self, b: Self) -> f32 { - a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w - } - - #[inline] - pub fn length(self) -> f32 { - self.length_sq().sqrt() - } - - #[inline] - pub fn length_sq(self) -> f32 { - Self::dot(self, self) - } - - #[inline] - pub fn ceil(self) -> Self { - Self { - x: self.x.ceil(), - y: self.y.ceil(), - z: self.z.ceil(), - w: self.w.ceil(), - } - } - - #[inline] - pub fn floor(self) -> Self { - Self { - x: self.x.floor(), - y: self.y.floor(), - z: self.z.floor(), - w: self.w.floor(), - } - } - - #[inline] - pub fn round(self) -> Self { - Self { - x: self.x.round(), - y: self.y.round(), - z: self.z.round(), - w: self.w.round(), - } +#[cfg(target_feature = "sse2")] +impl From for std::arch::x86_64::__m128 { + #[inline(always)] + fn from(x: Vec4) -> Self { + unsafe { std::arch::x86_64::_mm_loadu_ps(&x.x) } } } +// +// #[inline(always)] +// pub(crate) fn as_m128(self) -> std::arch::x86_64::__m128 { +// +// } + +// #[cfg(target_feature = "sse2")] +// #[inline(always)] +// pub(crate) fn from_m128(values: std::arch::x86_64::__m128) -> Self { +// use std::arch::x86_64::_mm_storeu_ps; +// let mut result = Vec4::ZERO; +// unsafe { _mm_storeu_ps(&mut result.x, values) } +// result +// } + impl std::ops::Add for Vec4 { type Output = Vec4; @@ -131,7 +97,7 @@ impl std::ops::Add for Vec4 { fn add(self, rhs: Self) -> Self::Output { unsafe { use std::arch::x86_64::_mm_add_ps; - Vec4::from_m128(_mm_add_ps(self.as_m128(), rhs.as_m128())) + _mm_add_ps(self.into(), rhs.into()).into() } } } @@ -153,10 +119,7 @@ impl std::ops::Sub for Vec4 { #[cfg(target_feature = "sse2")] #[inline(always)] fn sub(self, rhs: Self) -> Self::Output { - unsafe { - use std::arch::x86_64::_mm_sub_ps; - Vec4::from_m128(_mm_sub_ps(self.as_m128(), rhs.as_m128())) - } + unsafe { std::arch::x86_64::_mm_sub_ps(self.into(), rhs.into()).into() } } } @@ -177,10 +140,7 @@ impl std::ops::Mul for Vec4 { #[cfg(target_feature = "sse2")] #[inline(always)] fn mul(self, rhs: Self) -> Self::Output { - unsafe { - use std::arch::x86_64::_mm_mul_ps; - Vec4::from_m128(_mm_mul_ps(self.as_m128(), rhs.as_m128())) - } + unsafe { std::arch::x86_64::_mm_mul_ps(self.into(), rhs.into()).into() } } } @@ -201,87 +161,35 @@ impl std::ops::Div for Vec4 { #[cfg(target_feature = "sse2")] #[inline(always)] fn div(self, rhs: Self) -> Self::Output { - unsafe { - use std::arch::x86_64::_mm_div_ps; - Vec4::from_m128(_mm_div_ps(self.as_m128(), rhs.as_m128())) - } + unsafe { std::arch::x86_64::_mm_div_ps(self.into(), rhs.into()).into() } } } impl std::ops::AddAssign for Vec4 { - #[cfg(not(target_feature = "sse2"))] #[inline(always)] fn add_assign(&mut self, rhs: Self) { - self.x += rhs.x; - self.y += rhs.y; - self.z += rhs.z; - self.w += rhs.w; - } - - #[cfg(target_feature = "sse2")] - #[inline(always)] - fn add_assign(&mut self, rhs: Self) { - use std::arch::x86_64::_mm_add_ps; - unsafe { - *self = Vec4::from_m128(_mm_add_ps(self.as_m128(), rhs.as_m128())); - } + *self = *self + rhs; } } impl std::ops::SubAssign for Vec4 { - #[cfg(not(target_feature = "sse2"))] - #[inline(always)] - fn sub_assign(&mut self, rhs: Self) { - self.x -= rhs.x; - self.y -= rhs.y; - self.z -= rhs.z; - self.w -= rhs.w; - } - - #[cfg(target_feature = "sse2")] #[inline(always)] fn sub_assign(&mut self, rhs: Self) { - unsafe { - *self = Vec4::from_m128(std::arch::x86_64::_mm_sub_ps(self.as_m128(), rhs.as_m128())); - } + *self = *self - rhs; } } impl std::ops::MulAssign for Vec4 { - #[cfg(not(target_feature = "sse2"))] - #[inline(always)] - fn mul_assign(&mut self, rhs: Self) { - self.x *= rhs.x; - self.y *= rhs.y; - self.z *= rhs.z; - self.w *= rhs.w; - } - - #[cfg(target_feature = "sse2")] #[inline(always)] fn mul_assign(&mut self, rhs: Self) { - unsafe { - *self = Vec4::from_m128(std::arch::x86_64::_mm_mul_ps(self.as_m128(), rhs.as_m128())); - } + *self = *self * rhs; } } impl std::ops::DivAssign for Vec4 { - #[cfg(not(target_feature = "sse2"))] #[inline(always)] fn div_assign(&mut self, rhs: Self) { - self.x /= rhs.x; - self.y /= rhs.y; - self.z /= rhs.z; - self.w /= rhs.w; - } - - #[cfg(target_feature = "sse2")] - #[inline(always)] - fn div_assign(&mut self, rhs: Self) { - unsafe { - *self = Vec4::from_m128(std::arch::x86_64::_mm_div_ps(self.as_m128(), rhs.as_m128())); - } + *self = *self / rhs; } } @@ -310,8 +218,5 @@ mod tests { assert_eq!(Vec4::new(2.0, 2.0, 2.0, 2.0).length_sq(), 16.0); assert_eq!(Vec4::new(2.0, 2.0, 2.0, 2.0).length(), 4.0); - - assert_eq!(Vec4::distance_sq(Vec4::splat(-1.0), Vec4::splat(1.0)), 16.0); - assert_eq!(Vec4::distance(Vec4::splat(-1.0), Vec4::splat(1.0)), 4.0); } } -- 2.49.0