398 lines
11 KiB
Rust
398 lines
11 KiB
Rust
// 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<f32> 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<f32> 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<Matrix4> 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)
|
|
}
|
|
}
|
|
}
|