use crate::{scalar, FloatOps, Mat3, Mat4, Quaternion, Vec3, Vec4};
use num::{
traits::{float::FloatCore, NumAssign},
Signed,
};
pub fn translation<T: Copy + NumAssign>(v: Vec3<T>) -> Mat4<T> {
let mut result = Mat4::identity();
result[(0, 3)] = v[0];
result[(1, 3)] = v[1];
result[(2, 3)] = v[2];
result
}
pub fn scaling<T: Copy + NumAssign>(v: Vec3<T>) -> Mat4<T> {
let mut result = Mat4::identity();
result[(0, 0)] = v[0];
result[(1, 1)] = v[1];
result[(2, 2)] = v[2];
result
}
#[inline]
pub fn rotation<T: Copy + NumAssign>(q: Quaternion<T>) -> Mat4<T> {
Mat3::from(q).into()
}
pub fn transformation<T: Copy + NumAssign>(
translation: Vec3<T>,
rotation: Quaternion<T>,
scaling: Vec3<T>,
) -> Mat4<T> {
let mut result: Mat4<T> = Mat3::from(rotation).into();
for c in 0..3 {
for r in 0..3 {
result[(r, c)] *= scaling[c];
}
}
result[(0, 3)] = translation[0];
result[(1, 3)] = translation[1];
result[(2, 3)] = translation[2];
result
}
pub fn translation_of<T: Copy + NumAssign>(m: Mat4<T>) -> Vec3<T> {
Vec3::new([[m[(0, 3)], m[(1, 3)], m[(2, 3)]]])
}
#[inline]
pub fn scaling_of<T: Copy + FloatOps + NumAssign>(m: Mat4<T>) -> Vec3<T> {
Vec3::new([[
Vec3::new([[m[(0, 0)], m[(1, 0)], m[(2, 0)]]]).len(),
Vec3::new([[m[(0, 1)], m[(1, 1)], m[(2, 1)]]]).len(),
Vec3::new([[m[(0, 2)], m[(1, 2)], m[(2, 2)]]]).len(),
]])
}
pub fn rotation_of<T: Copy + FloatOps + NumAssign + PartialOrd>(m: Mat4<T>) -> Quaternion<T> {
let zero = T::zero();
let one = T::one();
let two = one + one;
let scaling = scaling_of(m);
let m00 = m[(0, 0)] / scaling[0];
let m11 = m[(1, 1)] / scaling[1];
let m22 = m[(2, 2)] / scaling[2];
Quaternion::from_slice(&[
scalar::copysign(
scalar::max(zero, one + m00 - m11 - m22).sqrt() / two,
m[(2, 1)] / scaling[1] - m[(1, 2)] / scaling[2],
),
scalar::copysign(
scalar::max(zero, one - m00 + m11 - m22).sqrt() / two,
m[(0, 2)] / scaling[2] - m[(2, 0)] / scaling[0],
),
scalar::copysign(
scalar::max(zero, one - m00 - m11 + m22).sqrt() / two,
m[(1, 0)] / scaling[0] - m[(0, 1)] / scaling[1],
),
scalar::max(zero, one + m00 + m11 + m22).sqrt() / two,
])
}
pub fn invert_trs<T: Copy + FloatOps + NumAssign>(m: &mut Mat4<T>) -> bool {
let zero = T::zero();
let one = T::one();
let neg = scalar::neg();
if m[(3, 0)] != zero || m[(3, 1)] != zero || m[(3, 2)] != zero || m[(3, 3)] != one {
return false;
}
let scaling = scaling_of(*m);
let translation = translation_of(*m);
m.transpose();
m[(3, 0)] = zero;
m[(3, 1)] = zero;
m[(3, 2)] = zero;
for c in 0..3 {
for r in 0..3 {
let factor = scaling[r] * scaling[r];
if factor == zero {
return false;
}
m[(r, c)] *= one / factor;
}
}
let mut t = Vec4::from_vec3(translation, zero);
t *= neg;
t.mul_assign(*m, t);
m[(0, 3)] = t[0];
m[(1, 3)] = t[1];
m[(2, 3)] = t[2];
true
}
pub fn ortho<T: Copy + NumAssign>(
left: T,
right: T,
bottom: T,
top: T,
znear: T,
zfar: T,
) -> Mat4<T> {
let neg = scalar::neg();
let one = T::one();
let two = one + one;
let x = one / (right - left);
let y = one / (top - bottom);
let z = one / (znear - zfar);
let mut result = Mat4::identity();
result[(0, 0)] = two * x;
result[(1, 1)] = two * y;
result[(2, 2)] = two * z;
result[(0, 3)] = (right + left) * x * neg;
result[(1, 3)] = (top + bottom) * y * neg;
result[(2, 3)] = (znear + zfar) * z;
result
}
#[inline]
pub fn perspective<T: Copy + FloatCore + FloatOps + NumAssign + Signed>(
aspect: T,
yfov: T,
znear: T,
zfar: T,
) -> Mat4<T> {
let two = T::one() + T::one();
let top = znear * (yfov / two).tan();
let right = aspect * top;
perspective_viewport(-right, right, -top, top, znear, zfar)
}
pub fn perspective_viewport<T: Copy + FloatCore + NumAssign + Signed>(
left: T,
right: T,
bottom: T,
top: T,
znear: T,
zfar: T,
) -> Mat4<T> {
let one = T::one();
let two = one + one;
let x = one / (right - left);
let y = one / (top - bottom);
let mut result = Mat4::identity();
result[(0, 0)] = two * znear * x;
result[(1, 1)] = two * znear * y;
result[(0, 2)] = (right + left) * x;
result[(1, 2)] = (top + bottom) * y;
result[(3, 2)] = -one;
result[(3, 3)] = T::zero();
if zfar.is_finite() {
let range_inv = one / (znear - zfar);
result[(2, 2)] = (znear + zfar) * range_inv;
result[(2, 3)] = two * znear * zfar * range_inv;
} else {
result[(2, 2)] = -one;
result[(2, 3)] = -two * znear;
}
result
}
pub fn target_to<T: Copy + FloatOps + NumAssign>(
eye: Vec3<T>,
center: Vec3<T>,
up: Vec3<T>,
) -> Mat4<T> {
let mut v = eye - center; v.normalize();
let mut n = up.cross(v); n.normalize();
let mut u = v.cross(n); u.normalize();
let mut result = Mat4::identity();
for i in 0..3 {
result[(i, 0)] = n[i];
result[(i, 1)] = u[i];
result[(i, 2)] = v[i];
result[(i, 3)] = eye[i];
}
result
}
pub fn look_at<T: Copy + FloatOps + NumAssign + Signed>(
eye: Vec3<T>,
center: Vec3<T>,
up: Vec3<T>,
) -> Mat4<T> {
let mut v = center - eye; v.normalize();
let mut n = v.cross(up); n.normalize();
let mut u = n.cross(v); u.normalize();
let mut result = Mat4::identity();
for i in 0..3 {
result[(0, i)] = n[i];
result[(1, i)] = u[i];
result[(2, i)] = -v[i];
}
result[(0, 3)] = -n.dot(eye);
result[(1, 3)] = -u.dot(eye);
result[(2, 3)] = v.dot(eye);
result
}
pub fn look_at_direction<T: Copy + FloatOps + NumAssign + Signed>(pitch: T, yaw: T) -> Vec3<T> {
let neg_cos_pitch = -pitch.cos();
Vec3::new([[
neg_cos_pitch * yaw.sin(),
pitch.sin(),
neg_cos_pitch * yaw.cos(),
]])
}