use std::ops::{Add, Mul, Neg, Sub};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vec3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vec3 {
pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
pub const fn to_array(self) -> [f32; 3] {
[self.x, self.y, self.z]
}
pub const fn from_array([x, y, z]: [f32; 3]) -> Self {
Self { x, y, z }
}
pub fn dot(self, other: Self) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z
}
pub fn cross(self, other: Self) -> Self {
Self::new(
self.y * other.z - self.z * other.y,
self.z * other.x - self.x * other.z,
self.x * other.y - self.y * other.x,
)
}
pub fn length(self) -> f32 {
self.dot(self).sqrt()
}
pub fn normalized(self) -> Option<Self> {
let length = self.length();
(length.is_normal()).then(|| self * (1.0 / length))
}
pub fn lerp(self, other: Self, t: f32) -> Self {
self + (other - self) * t
}
pub fn min(self, other: Self) -> Self {
Self::new(
self.x.min(other.x),
self.y.min(other.y),
self.z.min(other.z),
)
}
pub fn max(self, other: Self) -> Self {
Self::new(
self.x.max(other.x),
self.y.max(other.y),
self.z.max(other.z),
)
}
}
impl Add for Vec3 {
type Output = Self;
fn add(self, other: Self) -> Self {
Self::new(self.x + other.x, self.y + other.y, self.z + other.z)
}
}
impl Sub for Vec3 {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self::new(self.x - other.x, self.y - other.y, self.z - other.z)
}
}
impl Mul<f32> for Vec3 {
type Output = Self;
fn mul(self, factor: f32) -> Self {
Self::new(self.x * factor, self.y * factor, self.z * factor)
}
}
impl Neg for Vec3 {
type Output = Self;
fn neg(self) -> Self {
Self::new(-self.x, -self.y, -self.z)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Mat4 {
rows: [[f32; 4]; 4],
}
impl Default for Mat4 {
fn default() -> Self {
Self::IDENTITY
}
}
impl Mat4 {
pub const IDENTITY: Self = Self::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],
[0.0, 0.0, 0.0, 1.0],
]);
pub const fn from_rows(rows: [[f32; 4]; 4]) -> Self {
Self { rows }
}
pub const fn rows(&self) -> [[f32; 4]; 4] {
self.rows
}
pub const fn from_axes(x: Vec3, y: Vec3, z: Vec3, origin: Vec3) -> Self {
Self::from_rows([
[x.x, y.x, z.x, origin.x],
[x.y, y.y, z.y, origin.y],
[x.z, y.z, z.z, origin.z],
[0.0, 0.0, 0.0, 1.0],
])
}
pub const fn translation(offset: Vec3) -> Self {
Self::from_axes(
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
offset,
)
}
pub const fn scaling(factors: Vec3) -> Self {
Self::from_axes(
Vec3::new(factors.x, 0.0, 0.0),
Vec3::new(0.0, factors.y, 0.0),
Vec3::new(0.0, 0.0, factors.z),
Vec3::ZERO,
)
}
pub fn rotation_x(radians: f32) -> Self {
let (sin, cos) = radians.sin_cos();
Self::from_axes(
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, cos, sin),
Vec3::new(0.0, -sin, cos),
Vec3::ZERO,
)
}
pub fn rotation_y(radians: f32) -> Self {
let (sin, cos) = radians.sin_cos();
Self::from_axes(
Vec3::new(cos, 0.0, sin),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(-sin, 0.0, cos),
Vec3::ZERO,
)
}
pub fn rotation_z(radians: f32) -> Self {
let (sin, cos) = radians.sin_cos();
Self::from_axes(
Vec3::new(cos, sin, 0.0),
Vec3::new(-sin, cos, 0.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::ZERO,
)
}
pub fn then(&self, next: &Self) -> Self {
let (a, b) = (next.rows, self.rows);
Self::from_rows(std::array::from_fn(|row| {
std::array::from_fn(|column| (0..4).map(|k| a[row][k] * b[k][column]).sum())
}))
}
pub fn transform_point(&self, point: Vec3) -> Vec3 {
let [x, y, z, w] = self.transform_homogeneous(point);
if w == 1.0 || w == 0.0 {
Vec3::new(x, y, z)
} else {
Vec3::new(x / w, y / w, z / w)
}
}
pub fn transform_homogeneous(&self, point: Vec3) -> [f32; 4] {
self.rows
.map(|row| row[0] * point.x + row[1] * point.y + row[2] * point.z + row[3])
}
pub fn transform_vector(&self, vector: Vec3) -> Vec3 {
let [x, y, z, _] = self
.rows
.map(|row| row[0] * vector.x + row[1] * vector.y + row[2] * vector.z);
Vec3::new(x, y, z)
}
pub fn length_scale(&self) -> f32 {
let axis = |axis: Vec3| self.transform_vector(axis).length();
(axis(Vec3::new(1.0, 0.0, 0.0))
* axis(Vec3::new(0.0, 1.0, 0.0))
* axis(Vec3::new(0.0, 0.0, 1.0)))
.cbrt()
}
pub fn transpose(&self) -> Self {
Self::from_rows(std::array::from_fn(|row| {
std::array::from_fn(|column| self.rows[column][row])
}))
}
pub fn inverse(&self) -> Option<Self> {
let m = self.rows;
let cofactor = |row: usize, column: usize| {
let pick = |skip: usize| -> [usize; 3] {
let mut picked = [0; 3];
let mut next = 0;
for index in 0..4 {
if index != skip {
picked[next] = index;
next += 1;
}
}
picked
};
let [r0, r1, r2] = pick(row);
let [c0, c1, c2] = pick(column);
let minor = m[r0][c0] * (m[r1][c1] * m[r2][c2] - m[r1][c2] * m[r2][c1])
- m[r0][c1] * (m[r1][c0] * m[r2][c2] - m[r1][c2] * m[r2][c0])
+ m[r0][c2] * (m[r1][c0] * m[r2][c1] - m[r1][c1] * m[r2][c0]);
if (row + column).is_multiple_of(2) {
minor
} else {
-minor
}
};
let cofactors: [[f32; 4]; 4] =
std::array::from_fn(|row| std::array::from_fn(|column| cofactor(row, column)));
let determinant: f32 = (0..4)
.map(|column| m[0][column] * cofactors[0][column])
.sum();
if !determinant.is_normal() {
return None;
}
let scale = 1.0 / determinant;
Some(Self::from_rows(std::array::from_fn(|row| {
std::array::from_fn(|column| cofactors[column][row] * scale)
})))
}
}