// Copyright 2013-2014 The CGMath Developers. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Modified for the gltf crate by the gltf library developers. use std::ops; #[cfg(test)] mod test { use super::*; impl approx::AbsDiffEq for Vector4 { type Epsilon = f32; fn default_epsilon() -> f32 { f32::default_epsilon() } fn abs_diff_eq(&self, other: &Vector4, epsilon: Self::Epsilon) -> bool { f32::abs_diff_eq(&self.x, &other.x, epsilon) && f32::abs_diff_eq(&self.y, &other.y, epsilon) && f32::abs_diff_eq(&self.z, &other.z, epsilon) && f32::abs_diff_eq(&self.w, &other.w, epsilon) } } impl approx::RelativeEq for Vector4 { fn default_max_relative() -> f32 { f32::default_max_relative() } fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool { f32::relative_eq(&self.x, &other.x, epsilon, max_relative) && f32::relative_eq(&self.y, &other.y, epsilon, max_relative) && f32::relative_eq(&self.z, &other.z, epsilon, max_relative) && f32::relative_eq(&self.w, &other.w, epsilon, max_relative) } } impl approx::UlpsEq for Vector4 { fn default_max_ulps() -> u32 { f32::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool { f32::ulps_eq(&self.x, &other.x, epsilon, max_ulps) && f32::ulps_eq(&self.y, &other.y, epsilon, max_ulps) && f32::ulps_eq(&self.z, &other.z, epsilon, max_ulps) && f32::ulps_eq(&self.w, &other.w, epsilon, max_ulps) } } impl approx::AbsDiffEq for Matrix4 { type Epsilon = f32; fn default_epsilon() -> f32 { f32::default_epsilon() } fn abs_diff_eq(&self, other: &Matrix4, epsilon: Self::Epsilon) -> bool { Vector4::abs_diff_eq(&self.x, &other.x, epsilon) && Vector4::abs_diff_eq(&self.y, &other.y, epsilon) && Vector4::abs_diff_eq(&self.z, &other.z, epsilon) && Vector4::abs_diff_eq(&self.w, &other.w, epsilon) } } impl approx::RelativeEq for Matrix4 { fn default_max_relative() -> f32 { f32::default_max_relative() } fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool { Vector4::relative_eq(&self.x, &other.x, epsilon, max_relative) && Vector4::relative_eq(&self.y, &other.y, epsilon, max_relative) && Vector4::relative_eq(&self.z, &other.z, epsilon, max_relative) && Vector4::relative_eq(&self.w, &other.w, epsilon, max_relative) } } impl approx::UlpsEq for Matrix4 { fn default_max_ulps() -> u32 { f32::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool { Vector4::ulps_eq(&self.x, &other.x, epsilon, max_ulps) && Vector4::ulps_eq(&self.y, &other.y, epsilon, max_ulps) && Vector4::ulps_eq(&self.z, &other.z, epsilon, max_ulps) && Vector4::ulps_eq(&self.w, &other.w, epsilon, max_ulps) } } } #[derive(Copy, Clone, Debug, PartialEq)] #[repr(C)] pub struct Vector3 { pub x: f32, pub y: f32, pub z: f32, } impl Vector3 { pub fn new(x: f32, y: f32, z: f32) -> Self { Vector3 { x, y, z } } pub fn magnitude(&self) -> f32 { (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() } pub fn multiply(&mut self, s: f32) { self.x *= s; self.y *= s; self.z *= s; } #[cfg(test)] pub fn normalize(self) -> Vector3 { self * (1.0 / self.magnitude()) } } impl ops::Mul for Vector3 { type Output = Vector3; fn mul(mut self, rhs: f32) -> Self::Output { self.multiply(rhs); self } } #[derive(Copy, Clone, Debug, PartialEq)] #[repr(C)] pub struct Vector4 { pub x: f32, pub y: f32, pub z: f32, pub w: f32, } impl Vector4 { pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self { Vector4 { x, y, z, w } } pub fn multiply(&mut self, s: f32) { self.x *= s; self.y *= s; self.z *= s; self.w *= s; } pub fn as_array(&self) -> [f32; 4] { [self.x, self.y, self.z, self.w] } #[cfg(test)] pub fn from_array([x, y, z, w]: [f32; 4]) -> Self { Self { x, y, z, w } } } impl ops::Add for Vector4 { type Output = Self; fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y, z: self.z + other.z, w: self.w + other.w, } } } impl ops::Mul for Vector4 { type Output = Vector4; fn mul(mut self, rhs: f32) -> Self::Output { self.multiply(rhs); self } } #[derive(Copy, Clone, Debug, PartialEq)] #[repr(C)] pub struct Matrix3 { pub x: Vector3, pub y: Vector3, pub z: Vector3, } impl Matrix3 { #[rustfmt::skip] #[allow(clippy::too_many_arguments)] pub fn new( c0r0: f32, c0r1: f32, c0r2: f32, c1r0: f32, c1r1: f32, c1r2: f32, c2r0: f32, c2r1: f32, c2r2: f32, ) -> Matrix3 { Matrix3 { x: Vector3::new(c0r0, c0r1, c0r2), y: Vector3::new(c1r0, c1r1, c1r2), z: Vector3::new(c2r0, c2r1, c2r2), } } pub fn determinant(&self) -> f32 { self.x.x * (self.y.y * self.z.z - self.z.y * self.y.z) - self.y.x * (self.x.y * self.z.z - self.z.y * self.x.z) + self.z.x * (self.x.y * self.y.z - self.y.y * self.x.z) } pub fn trace(&self) -> f32 { self.x.x + self.y.y + self.z.z } } #[derive(Copy, Clone, Debug, PartialEq)] #[repr(C)] pub struct Matrix4 { pub x: Vector4, pub y: Vector4, pub z: Vector4, pub w: Vector4, } impl Matrix4 { #[rustfmt::skip] #[allow(clippy::too_many_arguments)] pub fn new( c0r0: f32, c0r1: f32, c0r2: f32, c0r3: f32, c1r0: f32, c1r1: f32, c1r2: f32, c1r3: f32, c2r0: f32, c2r1: f32, c2r2: f32, c2r3: f32, c3r0: f32, c3r1: f32, c3r2: f32, c3r3: f32, ) -> Matrix4 { Matrix4 { x: Vector4::new(c0r0, c0r1, c0r2, c0r3), y: Vector4::new(c1r0, c1r1, c1r2, c1r3), z: Vector4::new(c2r0, c2r1, c2r2, c2r3), w: Vector4::new(c3r0, c3r1, c3r2, c3r3), } } #[cfg(test)] pub fn from_array([x, y, z, w]: [[f32; 4]; 4]) -> Matrix4 { Matrix4 { x: Vector4::from_array(x), y: Vector4::from_array(y), z: Vector4::from_array(z), w: Vector4::from_array(w), } } /// Create a homogeneous transformation matrix from a translation vector. #[rustfmt::skip] pub fn from_translation(v: Vector3) -> Matrix4 { Matrix4::new( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, v.x, v.y, v.z, 1.0, ) } /// Create a homogeneous transformation matrix from a set of scale values. #[rustfmt::skip] pub fn from_nonuniform_scale(x: f32, y: f32, z: f32) -> Matrix4 { Matrix4::new( x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, 1.0, ) } /// Convert the quaternion to a 4 x 4 rotation matrix. pub fn from_quaternion(q: Quaternion) -> Matrix4 { let x2 = q.v.x + q.v.x; let y2 = q.v.y + q.v.y; let z2 = q.v.z + q.v.z; let xx2 = x2 * q.v.x; let xy2 = x2 * q.v.y; let xz2 = x2 * q.v.z; let yy2 = y2 * q.v.y; let yz2 = y2 * q.v.z; let zz2 = z2 * q.v.z; let sy2 = y2 * q.s; let sz2 = z2 * q.s; let sx2 = x2 * q.s; Matrix4 { x: Vector4::new(1.0 - yy2 - zz2, xy2 + sz2, xz2 - sy2, 0.0), y: Vector4::new(xy2 - sz2, 1.0 - xx2 - zz2, yz2 + sx2, 0.0), z: Vector4::new(xz2 + sy2, yz2 - sx2, 1.0 - xx2 - yy2, 0.0), w: Vector4::new(0.0, 0.0, 0.0, 1.0), } } pub fn as_array(&self) -> [[f32; 4]; 4] { [ self.x.as_array(), self.y.as_array(), self.z.as_array(), self.w.as_array(), ] } } impl ops::Mul for Matrix4 { type Output = Matrix4; fn mul(self, rhs: Matrix4) -> Self::Output { let a = self.x; let b = self.y; let c = self.z; let d = self.w; Matrix4 { x: a * rhs.x.x + b * rhs.x.y + c * rhs.x.z + d * rhs.x.w, y: a * rhs.y.x + b * rhs.y.y + c * rhs.y.z + d * rhs.y.w, z: a * rhs.z.x + b * rhs.z.y + c * rhs.z.z + d * rhs.z.w, w: a * rhs.w.x + b * rhs.w.y + c * rhs.w.z + d * rhs.w.w, } } } #[derive(Copy, Clone, Debug, PartialEq)] #[repr(C)] pub struct Quaternion { pub s: f32, pub v: Vector3, } impl Quaternion { pub fn new(w: f32, xi: f32, yj: f32, zk: f32) -> Quaternion { Quaternion { s: w, v: Vector3::new(xi, yj, zk), } } #[cfg(test)] pub fn from_axis_angle(axis: Vector3, radians: f32) -> Quaternion { Quaternion { s: (0.5 * radians).cos(), v: axis * (0.5 * radians).sin(), } } /// Convert a rotation matrix to an equivalent quaternion. pub fn from_matrix(m: Matrix3) -> Quaternion { let trace = m.trace(); if trace >= 0.0 { let s = (1.0 + trace).sqrt(); let w = 0.5 * s; let s = 0.5 / s; let x = (m.y.z - m.z.y) * s; let y = (m.z.x - m.x.z) * s; let z = (m.x.y - m.y.x) * s; Quaternion::new(w, x, y, z) } else if (m.x.x > m.y.y) && (m.x.x > m.z.z) { let s = ((m.x.x - m.y.y - m.z.z) + 1.0).sqrt(); let x = 0.5 * s; let s = 0.5 / s; let y = (m.y.x + m.x.y) * s; let z = (m.x.z + m.z.x) * s; let w = (m.y.z - m.z.y) * s; Quaternion::new(w, x, y, z) } else if m.y.y > m.z.z { let s = ((m.y.y - m.x.x - m.z.z) + 1.0).sqrt(); let y = 0.5 * s; let s = 0.5 / s; let z = (m.z.y + m.y.z) * s; let x = (m.y.x + m.x.y) * s; let w = (m.z.x - m.x.z) * s; Quaternion::new(w, x, y, z) } else { let s = ((m.z.z - m.x.x - m.y.y) + 1.0).sqrt(); let z = 0.5 * s; let s = 0.5 / s; let x = (m.x.z + m.z.x) * s; let y = (m.z.y + m.y.z) * s; let w = (m.x.y - m.y.x) * s; Quaternion::new(w, x, y, z) } } }