use core::iter::{Product, Sum}; use core::ops::{Add, Div, Index, IndexMut, Mul}; use num_traits::Float as NumFloat; use super::math::{SquareMatrix, radians, safe_acos}; use super::quaternion::Quaternion; use crate::core::color::{RGB, XYZ}; use crate::core::geometry::{ Bounds3f, Frame, Normal, Normal3f, Point, Point3f, Point3fi, Ray, Vector, Vector3f, Vector3fi, VectorLike, }; use crate::core::interaction::{ Interaction, InteractionBase, InteractionTrait, MediumInteraction, SurfaceInteraction, }; use crate::utils::gpu_array_from_fn; use crate::{Float, gamma}; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct TransformGeneric { m: SquareMatrix, m_inv: SquareMatrix, } impl TransformGeneric { pub fn new(m: SquareMatrix, m_inv: SquareMatrix) -> Self { Self { m, m_inv } } pub fn from_matrix(m: SquareMatrix) -> Option { let inv = m.inverse()?; Some(Self { m, m_inv: inv }) } pub fn from_flat(flat: &[T; 16]) -> Option { let m: SquareMatrix = SquareMatrix::from(flat); let inv = m.inverse()?; Some(Self { m, m_inv: inv }) } pub fn identity() -> Self { let m: SquareMatrix = SquareMatrix::identity(); Self { m, m_inv: m } } pub fn is_identity(&self) -> bool { self.m.is_identity() } pub fn inverse(&self) -> Self { Self { m: self.m_inv, m_inv: self.m, } } pub fn apply_inverse(&self, p: Point) -> Point { *self * p } pub fn apply_inverse_vector(&self, v: Vector) -> Vector { let x = v.x(); let y = v.y(); let z = v.z(); Vector::::new( self.m_inv[0][0] * x + self.m_inv[0][1] * y + self.m_inv[0][2] * z, self.m_inv[1][0] * x + self.m_inv[1][1] * y + self.m_inv[1][2] * z, self.m_inv[2][0] * x + self.m_inv[2][1] * y + self.m_inv[2][2] * z, ) } pub fn apply_inverse_normal(&self, n: Normal) -> Normal { *self * n } pub fn swaps_handedness(&self) -> bool { let s = SquareMatrix::::new([ [self.m[0][0], self.m[0][1], self.m[0][2]], [self.m[1][0], self.m[1][1], self.m[1][2]], [self.m[2][0], self.m[2][1], self.m[2][2]], ]); s.determinant() < T::zero() } pub fn get_matrix(&self) -> SquareMatrix { self.m } } impl Default for TransformGeneric { fn default() -> Self { Self::identity() } } pub type Transform = TransformGeneric; impl TransformGeneric { pub fn apply_to_point(&self, p: Point3f) -> Point3f { let x = p.x(); let y = p.y(); let z = p.z(); let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3]; let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3]; let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3]; let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3]; if wp == 1. { Point3f::new(xp, yp, zp) } else { Point3f::new(xp / wp, yp / wp, zp / wp) } } pub fn apply_to_vector(&self, p: Vector3f) -> Vector3f { let x = p.x(); let y = p.y(); let z = p.z(); let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; if wp == 1. { Vector3f::new(xp, yp, zp) } else { Vector3f::new(xp / wp, yp / wp, zp / wp) } } pub fn apply_to_normal(&self, p: Normal3f) -> Normal3f { let x = p.x(); let y = p.y(); let z = p.z(); let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; if wp == 1. { Normal3f::new(xp, yp, zp) } else { Normal3f::new(xp / wp, yp / wp, zp / wp) } } pub fn apply_to_bounds(&self, b: Bounds3f) -> Bounds3f { let mut new_bounds = Bounds3f::default(); for i in 0..8 { let corner = self.apply_to_point(b.corner(i)); new_bounds = new_bounds.union_point(corner); } new_bounds } pub fn apply_to_ray(&self, r: &Ray, t_max: &mut Option) -> Ray { let norm_squared = r.d.norm_squared(); let mut o = Point3fi::new_from_point(r.o); if norm_squared > 0. { let dt = r.d.abs().dot(o.error()) / norm_squared; let offset = Vector3fi::new_from_vector(r.d * dt); o = o + offset; if let Some(t) = t_max.as_mut() { *t -= dt; } } Ray::new(o.into(), r.d, Some(r.time), &*r.medium) } pub fn apply_to_interval(&self, pi: &Point3fi) -> Point3fi { let p = pi.midpoint(); let x = p.x(); let y = p.y(); let z = p.z(); let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; let mut p_error = Vector3f::default(); if pi.is_exact() { p_error[0] = gamma(3) * ((self.m[0][0] * x).abs() + (self.m[0][1] * y).abs() + (self.m[0][2] + z).abs() + self.m[0][3].abs()); p_error[1] = gamma(3) * ((self.m[1][0] * x).abs() + (self.m[1][1] * y).abs() + (self.m[1][2] + z).abs() + self.m[1][3].abs()); p_error[2] = gamma(3) * ((self.m[2][0] * x).abs() + (self.m[2][1] * y).abs() + (self.m[2][2] + z).abs() + self.m[2][3].abs()); } if wp == 1. { Point3fi::new_with_error(Point([xp, yp, zp]), p_error) } else { Point3fi::new_with_error(Point([xp / wp, yp / wp, zp / wp]), p_error) } } pub fn apply_to_vector_interval(&self, vi: &Vector3fi) -> Vector3fi { let v = vi.midpoint(); let x = v.x(); let y = v.y(); let z = v.z(); // Transform the midpoint of the vector interval let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z; let mut v_error = Vector3f::default(); // Propagate the error, ignoring the translational part of the matrix if vi.is_exact() { v_error[0] = gamma(3) * ((self.m[0][0] * x).abs() + (self.m[0][1] * y).abs() + (self.m[0][2] * z).abs()); v_error[1] = gamma(3) * ((self.m[1][0] * x).abs() + (self.m[1][1] * y).abs() + (self.m[1][2] * z).abs()); v_error[2] = gamma(3) * ((self.m[2][0] * x).abs() + (self.m[2][1] * y).abs() + (self.m[2][2] * z).abs()); } if wp == 1. { Vector3fi::new_with_error(Vector3f::new(xp, yp, zp), v_error) } else { Vector3fi::new_with_error(Vector3f::new(xp / wp, yp / wp, zp / wp), v_error) } } pub fn apply_to_interaction(&self, inter: &Interaction) -> Interaction { match inter { Interaction::Surface(si) => { let mut ret = si.clone(); ret.common.pi = self.apply_to_interval(&si.common.pi); let n = self.apply_to_normal(si.common.n); ret.common.wo = self.apply_to_vector(si.common.wo).normalize(); ret.dpdu = self.apply_to_vector(si.dpdu); ret.dpdv = self.apply_to_vector(si.dpdv); ret.dndu = self.apply_to_normal(si.dndu); ret.dndv = self.apply_to_normal(si.dndv); ret.dpdx = self.apply_to_vector(si.dpdx); ret.dpdy = self.apply_to_vector(si.dpdy); let shading_n = self.apply_to_normal(si.shading.n); ret.shading.n = shading_n.normalize(); ret.shading.dpdu = self.apply_to_vector(si.shading.dpdu); ret.shading.dpdv = self.apply_to_vector(si.shading.dpdv); ret.shading.dndu = self.apply_to_normal(si.shading.dndu); ret.shading.dndv = self.apply_to_normal(si.shading.dndv); ret.common.n = n.normalize().face_forward(ret.shading.n); Interaction::Surface(ret) } Interaction::Medium(mi) => { let mut ret = mi.clone(); ret.common.pi = self.apply_to_interval(&mi.common.pi); ret.common.n = self.apply_to_normal(mi.common.n).normalize(); ret.common.wo = self.apply_to_vector(mi.common.wo).normalize(); Interaction::Medium(ret) } Interaction::Simple(sim) => { let mut ret = sim.clone(); ret.common.pi = self.apply_to_interval(&sim.common.pi); if sim.common.n != Default::default() { ret.common.n = self.apply_to_normal(sim.common.n).normalize(); } if sim.common.wo != Default::default() { ret.common.wo = self.apply_to_vector(sim.common.wo).normalize(); } Interaction::Simple(ret) } } } pub fn apply_inverse_interval(&self, p: &Point3fi) -> Point3fi { let x = Float::from(p.x()); let y = Float::from(p.y()); let z = Float::from(p.z()); let m_inv = &self.m_inv; // Compute transformed coordinates from point let xp = (m_inv[0][0] * x + m_inv[0][1] * y) + (m_inv[0][2] * z + m_inv[0][3]); let yp = (m_inv[1][0] * x + m_inv[1][1] * y) + (m_inv[1][2] * z + m_inv[1][3]); let zp = (m_inv[2][0] * x + m_inv[2][1] * y) + (m_inv[2][2] * z + m_inv[2][3]); let wp = (m_inv[3][0] * x + m_inv[3][1] * y) + (m_inv[3][2] * z + m_inv[3][3]); // Compute absolute error for transformed point let g3 = gamma(3); let p_out_error = if p.is_exact() { Vector3f::new( g3 * (m_inv[0][0] * x).abs() + (m_inv[0][1] * y).abs() + (m_inv[0][2] * z).abs(), g3 * (m_inv[1][0] * x).abs() + (m_inv[1][1] * y).abs() + (m_inv[1][2] * z).abs(), g3 * (m_inv[2][0] * x).abs() + (m_inv[2][1] * y).abs() + (m_inv[2][2] * z).abs(), ) } else { let p_in_error = p.error(); let g3_plus_1 = g3 + 1.0; Vector3f::new( g3_plus_1 * (m_inv[0][0].abs() * p_in_error.x() + m_inv[0][1].abs() * p_in_error.y() + m_inv[0][2].abs() * p_in_error.z()) + g3 * ((m_inv[0][0] * x).abs() + (m_inv[0][1] * y).abs() + (m_inv[0][2] * z).abs() + m_inv[0][3].abs()), g3_plus_1 * (m_inv[1][0].abs() * p_in_error.x() + m_inv[1][1].abs() * p_in_error.y() + m_inv[1][2].abs() * p_in_error.z()) + g3 * ((m_inv[1][0] * x).abs() + (m_inv[1][1] * y).abs() + (m_inv[1][2] * z).abs() + m_inv[1][3].abs()), g3_plus_1 * (m_inv[2][0].abs() * p_in_error.x() + m_inv[2][1].abs() * p_in_error.y() + m_inv[2][2].abs() * p_in_error.z()) + g3 * ((m_inv[2][0] * x).abs() + (m_inv[2][1] * y).abs() + (m_inv[2][2] * z).abs() + m_inv[2][3].abs()), ) }; if wp == 1.0 { Point3fi::new_with_error(Point3f::new(xp, yp, zp), p_out_error) } else { Point3fi::new_with_error(Point3f::new(xp / wp, yp / wp, zp / wp), p_out_error) } } pub fn apply_inverse_ray(&self, r: &Ray, t_max: Option) -> (Ray, Float) { let mut o = self.apply_inverse_interval(&Point3fi::new_from_point(r.o)); let d = self.apply_inverse_vector(r.d); // Offset ray origin to edge of error bounds and compute _tMax_ let mut t = 0.; let length_squared = d.norm_squared(); if length_squared > 0. { let o_error = Vector3f::new(o.x().width() / 2., o.y().width() / 2., o.z().width() / 2.); let dt = d.abs().dot(o_error) / length_squared; o = o + Vector3fi::from(d * dt); if let Some(t_max) = t_max { t = t_max - dt; } } (Ray::new(Point3f::from(o), d, Some(r.time), &*r.medium), t) } pub fn to_quaternion(self) -> Quaternion { let trace = self.m.trace(); let mut quat = Quaternion::default(); if trace > 0. { let mut s = (trace + 1.).sqrt(); quat.w = 0.5 * s; s = 0.5 / s; quat.v[0] = (self.m[2][1] - self.m[1][2]) * s; quat.v[1] = (self.m[0][2] - self.m[2][0]) * s; quat.v[2] = (self.m[1][0] - self.m[0][1]) * s; } else { let nxt = [1, 2, 0]; let mut q = [0.; 3]; let mut i = 0; if self.m[1][1] > self.m[0][0] { i = 1; } if self.m[2][2] > self.m[i][i] { i = 2; } let j = nxt[i]; let k = nxt[j]; let mut s = (self.m[i][i] - (self.m[j][j] + self.m[k][k]) + 1.).sqrt(); q[i] = 0.5 * s; if s != 0. { s = 0.5 / s; } quat.w = (self.m[k][j] - self.m[j][k]) * s; q[j] = (self.m[j][i] + self.m[i][j]) * s; q[k] = (self.m[k][i] + self.m[i][k]) * s; quat.v[0] = q[0]; quat.v[1] = q[1]; quat.v[2] = q[2]; } quat } pub fn translate(delta: Vector3f) -> Self { TransformGeneric { m: SquareMatrix::new([ [1.0, 0.0, 0.0, delta.x()], [0.0, 1.0, 0.0, delta.y()], [0.0, 0.0, 1.0, delta.z()], [0.0, 0.0, 0.0, 1.0], ]), m_inv: SquareMatrix::new([ [1.0, 0.0, 0.0, -delta.x()], [0.0, 1.0, 0.0, -delta.y()], [0.0, 0.0, 1.0, -delta.z()], [0.0, 0.0, 0.0, 1.0], ]), } } pub fn scale(x: Float, y: Float, z: Float) -> Self { let m = SquareMatrix::new([ [x, 0., 0., 0.], [0., y, 0., 0.], [0., 0., z, 0.], [0., 0., 0., 1.], ]); let m_inv = SquareMatrix::new([ [1. / x, 0., 0., 0.], [0., 1. / y, 0., 0.], [0., 0., 1. / z, 0.], [0., 0., 0., 1.], ]); Self { m, m_inv } } pub fn perspective(fov: Float, n: Float, f: Float) -> Option> { let persp: SquareMatrix = SquareMatrix::new([ [1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., f / (f - n), -f * n / (f - n)], [0., 0., 1., 0.], ]); let inv_tan_ang = 1. / (radians(fov) / 2.).tan(); let persp_transform = TransformGeneric::from_matrix(persp)?; Some(TransformGeneric::scale(inv_tan_ang, inv_tan_ang, 1.) * persp_transform) } pub fn orthographic(z_near: Float, z_far: Float) -> Self { Self::scale(1., 1., 1. / (z_far - z_near)) * Self::translate(Vector3f::new(0., 0., -z_near)) } pub fn rotate_around_axis(theta: Float, axis: impl Into) -> Self { let sin_theta = theta.to_radians().sin(); let cos_theta = theta.to_radians().cos(); TransformGeneric::rotate(sin_theta, cos_theta, axis) } pub fn rotate(sin_theta: Float, cos_theta: Float, axis: impl Into) -> Self { let vec_axis: Vector3f = axis.into(); let a = vec_axis.normalize(); let mut m: SquareMatrix = SquareMatrix::default(); m[0][0] = a.x() * a.x() + (1. - a.x() * a.x()) * cos_theta; m[0][1] = a.x() * a.y() * (1. - cos_theta) - a.z() * sin_theta; m[0][2] = a.x() * a.z() * (1. - cos_theta) + a.y() * sin_theta; m[0][3] = 0.; m[1][0] = a.x() * a.y() * (1. - cos_theta) + a.z() * sin_theta; m[1][1] = a.y() * a.y() + (1. - a.y() * a.y()) * cos_theta; m[1][2] = a.y() * a.z() * (1. - cos_theta) - a.x() * sin_theta; m[1][3] = 0.; m[2][0] = a.x() * a.z() * (1. - cos_theta) - a.y() * sin_theta; m[2][1] = a.y() * a.z() * (1. - cos_theta) + a.x() * sin_theta; m[2][2] = a.z() * a.z() + (1. - a.z() * a.z()) * cos_theta; m[2][3] = 0.; TransformGeneric::new(m, m.transpose()) } pub fn rotate_from_to(from: Vector3f, to: Vector3f) -> Self { let refl; if from.x().abs() < 0.72 && to.x().abs() < 0.72 { refl = Vector3f::new(1., 0., 0.); } else if from.y().abs() < 0.72 && to.y().abs() < 0.72 { refl = Vector3f::new(0., 1., 0.); } else { refl = Vector3f::new(0., 0., 1.); } let u = refl - from; let v = refl - to; let uu = u.dot(u); let vv = v.dot(v); let uv = u.dot(v); let mut r: SquareMatrix = SquareMatrix::default(); for i in 0..3 { for j in 0..3 { let k = if i == j { 1. } else { 0. }; r[i][j] = k - 2. / uu * u[i] * u[j] - 2. * vv * v[i] * v[j] + 4. * uv / (uu * vv) * v[i] * u[j]; } } TransformGeneric::new(r, r.transpose()) } pub fn rotate_x(theta: Float) -> Self { let sin_theta = theta.to_radians().sin(); let cos_theta = theta.to_radians().cos(); let m = SquareMatrix::new([ [1., 0., 0., 0.], [0., cos_theta, -sin_theta, 0.], [0., sin_theta, cos_theta, 0.], [0., 0., 0., 1.], ]); Self { m, m_inv: m.transpose(), } } pub fn rotate_y(theta: Float) -> Self { let sin_theta = theta.to_radians().sin(); let cos_theta = theta.to_radians().cos(); let m = SquareMatrix::new([ [cos_theta, 0., sin_theta, 0.], [0., 1., 0., 0.], [-sin_theta, 0., cos_theta, 0.], [0., 0., 0., 1.], ]); Self { m, m_inv: m.transpose(), } } pub fn rotate_z(theta: Float) -> Self { let sin_theta = theta.to_radians().sin(); let cos_theta = theta.to_radians().cos(); let m = SquareMatrix::new([ [cos_theta, -sin_theta, 0., 0.], [sin_theta, cos_theta, 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.], ]); Self { m, m_inv: m.transpose(), } } pub fn decompose(&self) -> (Vector3f, SquareMatrix, SquareMatrix) { let t = Vector3f::new(self.m[0][3], self.m[1][3], self.m[2][3]); let mut m = self.m; for i in 0..3 { m[i][3] = 0.; m[3][i] = m[i][3]; } m[3][3] = 1.; let mut norm: Float; let mut r = m; let mut count = 0; loop { let rit = r .transpose() .inverse() .expect("Transform is not decomposable"); let rnext = (r + rit) / 2.; norm = 0.0; for i in 0..3 { let n = (r[i][0] - rnext[i][0]).abs() + (r[i][1] - rnext[i][1]).abs() + (r[i][2] - rnext[i][2]).abs(); norm = norm.max(n); } r = rnext; count += 1; if count > 100 || norm < 0.0001 { break; } } let s = r .transpose() .inverse() .expect("Part of decomposition is singular") * m; (t, r, s) } pub fn has_scale(&self, tolerance: Option) -> bool { let la2 = self .apply_to_vector(Vector3f::new(1., 0., 0.)) .norm_squared(); let lb2 = self .apply_to_vector(Vector3f::new(0., 1., 0.)) .norm_squared(); let lc2 = self .apply_to_vector(Vector3f::new(0., 0., 1.)) .norm_squared(); let tol = tolerance.unwrap_or(1e-3); (la2 - 1.).abs() > tol || (lb2 - 1.).abs() > tol || (lc2 - 1.).abs() > tol } } impl PartialEq for TransformGeneric { fn eq(&self, other: &Self) -> bool { self.m == other.m && self.m_inv == other.m_inv } } impl Mul for TransformGeneric { type Output = TransformGeneric; fn mul(self, rhs: TransformGeneric) -> Self::Output { TransformGeneric { m: self.m * rhs.m, m_inv: rhs.m_inv * self.m_inv, } } } impl<'b, T> Mul<&'b TransformGeneric> for &TransformGeneric where T: NumFloat, { type Output = TransformGeneric; fn mul(self, rhs: &'b TransformGeneric) -> Self::Output { TransformGeneric { m: self.m * rhs.m, m_inv: rhs.m_inv * self.m_inv, } } } impl Mul> for Float { type Output = TransformGeneric; fn mul(self, rhs: TransformGeneric) -> Self::Output { TransformGeneric { m: rhs.m * self, m_inv: rhs.m_inv * self, } } } impl Mul> for TransformGeneric where T: NumFloat, { type Output = Point; fn mul(self, p: Point) -> Self::Output { let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; if wp == T::one() { Point([xp, yp, zp]) } else { Point([xp / wp, yp / wp, zp / wp]) } } } impl Mul> for TransformGeneric where T: NumFloat, { type Output = Vector; fn mul(self, p: Vector) -> Self::Output { let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; if wp == T::one() { Vector([xp, yp, zp]) } else { Vector([xp / wp, yp / wp, zp / wp]) } } } impl Mul> for TransformGeneric where T: NumFloat, { type Output = Normal; fn mul(self, p: Normal) -> Self::Output { let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; if wp == T::one() { Normal([xp, yp, zp]) } else { Normal([xp / wp, yp / wp, zp / wp]) } } } impl From for TransformGeneric { fn from(frame: Frame) -> Self { let values: &[Float; 16] = &[ frame.x.x(), frame.x.y(), frame.x.z(), 0., frame.y.x(), frame.y.y(), frame.y.z(), 0., frame.z.x(), frame.z.y(), frame.z.z(), 0., 0., 0., 0., 1., ]; Self::from_flat(values).expect("Transform must be inversible") } } impl From for TransformGeneric { fn from(q: Quaternion) -> Self { let xx = q.v.x() * q.v.x(); let yy = q.v.y() * q.v.y(); let zz = q.v.z() * q.v.z(); let xy = q.v.x() * q.v.y(); let xz = q.v.x() * q.v.z(); let yz = q.v.y() * q.v.z(); let wx = q.v.x() * q.w; let wy = q.v.y() * q.w; let wz = q.v.z() * q.w; let mut m = [[0.0; 4]; 4]; m[0][0] = 1. - 2. * (yy + zz); m[0][1] = 2. * (xy - wz); m[0][2] = 2. * (xz + wy); m[1][0] = 2. * (xy + wz); m[1][1] = 1. - 2. * (xx + zz); m[1][2] = 2. * (yz - wx); m[2][0] = 2. * (xz - wy); m[2][1] = 2. * (yz + wx); m[2][2] = 1. - 2. * (xx + yy); m[3][3] = 1.; let m_sq = SquareMatrix::new(m); // For a pure rotation, the inverse is the transpose. let m_inv_sq = m_sq.transpose(); TransformGeneric::new(m_sq, m_inv_sq) } } #[repr(C)] #[derive(Default, Debug, Copy, Clone)] pub struct DerivativeTerm { kc: Float, kx: Float, ky: Float, kz: Float, } impl DerivativeTerm { pub fn new(kc: Float, kx: Float, ky: Float, kz: Float) -> Self { Self { kc, kx, ky, kz } } pub fn from_matrix(m: [Float; 4]) -> Self { Self { kc: m[0], kx: m[1], ky: m[2], kz: m[3], } } } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct AnimatedTransform { pub start_transform: Transform, pub end_transform: Transform, pub start_time: Float, pub end_time: Float, actually_animated: bool, t: [Vector3f; 2], r: [Quaternion; 2], s: [SquareMatrix; 2], has_rotation: bool, c1: [DerivativeTerm; 3], c2: [DerivativeTerm; 3], c3: [DerivativeTerm; 3], c4: [DerivativeTerm; 3], c5: [DerivativeTerm; 3], } impl Default for AnimatedTransform { fn default() -> Self { Self { start_transform: Transform::default(), end_transform: Transform::default(), start_time: 0.0, end_time: 0.0, actually_animated: false, t: gpu_array_from_fn(|_| Vector3f::default()), r: gpu_array_from_fn(|_| Quaternion::default()), s: gpu_array_from_fn(|_| SquareMatrix::default()), has_rotation: false, c1: gpu_array_from_fn(|_| DerivativeTerm::default()), c2: gpu_array_from_fn(|_| DerivativeTerm::default()), c3: gpu_array_from_fn(|_| DerivativeTerm::default()), c4: gpu_array_from_fn(|_| DerivativeTerm::default()), c5: gpu_array_from_fn(|_| DerivativeTerm::default()), } } } impl AnimatedTransform { pub fn from_transform(t: &Transform) -> Self { Self::new(t, 0., t, 1.) } pub fn new( start_transform: &Transform, start_time: Float, end_transform: &Transform, end_time: Float, ) -> Self { let actually_animated = start_transform != end_transform; if !actually_animated { return Self::default(); } let (t0, r_temp, s0) = start_transform.decompose(); let mut rm = r_temp; let scale_transform = TransformGeneric::from_matrix(rm).expect("Scaling matrix should invertible"); let r0 = scale_transform.to_quaternion(); let (t1, r_temp, s1) = end_transform.decompose(); rm = r_temp; let scale_transform = TransformGeneric::from_matrix(rm).expect("Scaling matrix should invertible"); let mut r1 = scale_transform.to_quaternion(); if r0.dot(r1) < 0. { r1 = -r1; } let has_rotation = r0.dot(r1) < 0.9995; let t = [t0, t1]; let r = [r0, r1]; let s = [s0, s1]; let (c1, c2, c3, c4, c5) = if has_rotation { let mut c1: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default()); let mut c2: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default()); let mut c3: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default()); let mut c4: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default()); let mut c5: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default()); let cos_theta = r0.dot(r1); let theta = safe_acos(cos_theta); let qperp: Quaternion = (r0 - r1 * cos_theta).normalize(); let t0x = t0.x(); let t0y = t0.y(); let t0z = t0.z(); let t1x = t1.x(); let t1y = t1.y(); let t1z = t1.z(); let q0x = r0.v.x(); let q0y = r0.v.y(); let q0z = r0.v.z(); let q0w = r0.w; let qperpx = qperp.v.x(); let qperpy = qperp.v.y(); let qperpz = qperp.v.z(); let qperpw = qperp.w; let s000 = s0[0][0]; let s001 = s0[0][1]; let s002 = s0[0][2]; let s010 = s0[1][0]; let s011 = s0[1][1]; let s012 = s0[1][2]; let s020 = s0[2][0]; let s021 = s0[2][1]; let s022 = s0[2][2]; let s100 = s1[0][0]; let s101 = s1[0][1]; let s102 = s1[0][2]; let s110 = s1[1][0]; let s111 = s1[1][1]; let s112 = s1[1][2]; let s120 = s1[2][0]; let s121 = s1[2][1]; let s122 = s1[2][2]; c1[0] = DerivativeTerm::new( -t0x + t1x, (-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s000 + q0w * q0z * s010 - qperpx * qperpy * s010 + qperpw * qperpz * s010 - q0w * q0y * s020 - qperpw * qperpy * s020 - qperpx * qperpz * s020 + s100 - q0y * q0y * s100 - q0z * q0z * s100 - qperpy * qperpy * s100 - qperpz * qperpz * s100 - q0w * q0z * s110 + qperpx * qperpy * s110 - qperpw * qperpz * s110 + q0w * q0y * s120 + qperpw * qperpy * s120 + qperpx * qperpz * s120 + q0x * (-(q0y * s010) - q0z * s020 + q0y * s110 + q0z * s120), (-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s001 + q0w * q0z * s011 - qperpx * qperpy * s011 + qperpw * qperpz * s011 - q0w * q0y * s021 - qperpw * qperpy * s021 - qperpx * qperpz * s021 + s101 - q0y * q0y * s101 - q0z * q0z * s101 - qperpy * qperpy * s101 - qperpz * qperpz * s101 - q0w * q0z * s111 + qperpx * qperpy * s111 - qperpw * qperpz * s111 + q0w * q0y * s121 + qperpw * qperpy * s121 + qperpx * qperpz * s121 + q0x * (-(q0y * s011) - q0z * s021 + q0y * s111 + q0z * s121), (-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s002 + q0w * q0z * s012 - qperpx * qperpy * s012 + qperpw * qperpz * s012 - q0w * q0y * s022 - qperpw * qperpy * s022 - qperpx * qperpz * s022 + s102 - q0y * q0y * s102 - q0z * q0z * s102 - qperpy * qperpy * s102 - qperpz * qperpz * s102 - q0w * q0z * s112 + qperpx * qperpy * s112 - qperpw * qperpz * s112 + q0w * q0y * s122 + qperpw * qperpy * s122 + qperpx * qperpz * s122 + q0x * (-(q0y * s012) - q0z * s022 + q0y * s112 + q0z * s122), ); c2[0] = DerivativeTerm::new( 0., -(qperpy * qperpy * s000) - qperpz * qperpz * s000 + qperpx * qperpy * s010 - qperpw * qperpz * s010 + qperpw * qperpy * s020 + qperpx * qperpz * s020 + q0y * q0y * (s000 - s100) + q0z * q0z * (s000 - s100) + qperpy * qperpy * s100 + qperpz * qperpz * s100 - qperpx * qperpy * s110 + qperpw * qperpz * s110 - qperpw * qperpy * s120 - qperpx * qperpz * s120 + 2. * q0x * qperpy * s010 * theta - 2. * q0w * qperpz * s010 * theta + 2. * q0w * qperpy * s020 * theta + 2. * q0x * qperpz * s020 * theta + q0y * (q0x * (-s010 + s110) + q0w * (-s020 + s120) + 2. * (-2. * qperpy * s000 + qperpx * s010 + qperpw * s020) * theta) + q0z * (q0w * (s010 - s110) + q0x * (-s020 + s120) - 2. * (2. * qperpz * s000 + qperpw * s010 - qperpx * s020) * theta), -(qperpy * qperpy * s001) - qperpz * qperpz * s001 + qperpx * qperpy * s011 - qperpw * qperpz * s011 + qperpw * qperpy * s021 + qperpx * qperpz * s021 + q0y * q0y * (s001 - s101) + q0z * q0z * (s001 - s101) + qperpy * qperpy * s101 + qperpz * qperpz * s101 - qperpx * qperpy * s111 + qperpw * qperpz * s111 - qperpw * qperpy * s121 - qperpx * qperpz * s121 + 2. * q0x * qperpy * s011 * theta - 2. * q0w * qperpz * s011 * theta + 2. * q0w * qperpy * s021 * theta + 2. * q0x * qperpz * s021 * theta + q0y * (q0x * (-s011 + s111) + q0w * (-s021 + s121) + 2. * (-2. * qperpy * s001 + qperpx * s011 + qperpw * s021) * theta) + q0z * (q0w * (s011 - s111) + q0x * (-s021 + s121) - 2. * (2. * qperpz * s001 + qperpw * s011 - qperpx * s021) * theta), -(qperpy * qperpy * s002) - qperpz * qperpz * s002 + qperpx * qperpy * s012 - qperpw * qperpz * s012 + qperpw * qperpy * s022 + qperpx * qperpz * s022 + q0y * q0y * (s002 - s102) + q0z * q0z * (s002 - s102) + qperpy * qperpy * s102 + qperpz * qperpz * s102 - qperpx * qperpy * s112 + qperpw * qperpz * s112 - qperpw * qperpy * s122 - qperpx * qperpz * s122 + 2. * q0x * qperpy * s012 * theta - 2. * q0w * qperpz * s012 * theta + 2. * q0w * qperpy * s022 * theta + 2. * q0x * qperpz * s022 * theta + q0y * (q0x * (-s012 + s112) + q0w * (-s022 + s122) + 2. * (-2. * qperpy * s002 + qperpx * s012 + qperpw * s022) * theta) + q0z * (q0w * (s012 - s112) + q0x * (-s022 + s122) - 2. * (2. * qperpz * s002 + qperpw * s012 - qperpx * s022) * theta), ); c3[0] = DerivativeTerm::new( 0., -2. * (q0x * qperpy * s010 - q0w * qperpz * s010 + q0w * qperpy * s020 + q0x * qperpz * s020 - q0x * qperpy * s110 + q0w * qperpz * s110 - q0w * qperpy * s120 - q0x * qperpz * s120 + q0y * (-2. * qperpy * s000 + qperpx * s010 + qperpw * s020 + 2. * qperpy * s100 - qperpx * s110 - qperpw * s120) + q0z * (-2. * qperpz * s000 - qperpw * s010 + qperpx * s020 + 2. * qperpz * s100 + qperpw * s110 - qperpx * s120)) * theta, -2. * (q0x * qperpy * s011 - q0w * qperpz * s011 + q0w * qperpy * s021 + q0x * qperpz * s021 - q0x * qperpy * s111 + q0w * qperpz * s111 - q0w * qperpy * s121 - q0x * qperpz * s121 + q0y * (-2. * qperpy * s001 + qperpx * s011 + qperpw * s021 + 2. * qperpy * s101 - qperpx * s111 - qperpw * s121) + q0z * (-2. * qperpz * s001 - qperpw * s011 + qperpx * s021 + 2. * qperpz * s101 + qperpw * s111 - qperpx * s121)) * theta, -2. * (q0x * qperpy * s012 - q0w * qperpz * s012 + q0w * qperpy * s022 + q0x * qperpz * s022 - q0x * qperpy * s112 + q0w * qperpz * s112 - q0w * qperpy * s122 - q0x * qperpz * s122 + q0y * (-2. * qperpy * s002 + qperpx * s012 + qperpw * s022 + 2. * qperpy * s102 - qperpx * s112 - qperpw * s122) + q0z * (-2. * qperpz * s002 - qperpw * s012 + qperpx * s022 + 2. * qperpz * s102 + qperpw * s112 - qperpx * s122)) * theta, ); c4[0] = DerivativeTerm::new( 0., -(q0x * qperpy * s010) + q0w * qperpz * s010 - q0w * qperpy * s020 - q0x * qperpz * s020 + q0x * qperpy * s110 - q0w * qperpz * s110 + q0w * qperpy * s120 + q0x * qperpz * s120 + 2. * q0y * q0y * s000 * theta + 2. * q0z * q0z * s000 * theta - 2. * qperpy * qperpy * s000 * theta - 2. * qperpz * qperpz * s000 * theta + 2. * qperpx * qperpy * s010 * theta - 2. * qperpw * qperpz * s010 * theta + 2. * qperpw * qperpy * s020 * theta + 2. * qperpx * qperpz * s020 * theta + q0y * (-(qperpx * s010) - qperpw * s020 + 2. * qperpy * (s000 - s100) + qperpx * s110 + qperpw * s120 - 2. * q0x * s010 * theta - 2. * q0w * s020 * theta) + q0z * (2. * qperpz * s000 + qperpw * s010 - qperpx * s020 - 2. * qperpz * s100 - qperpw * s110 + qperpx * s120 + 2. * q0w * s010 * theta - 2. * q0x * s020 * theta), -(q0x * qperpy * s011) + q0w * qperpz * s011 - q0w * qperpy * s021 - q0x * qperpz * s021 + q0x * qperpy * s111 - q0w * qperpz * s111 + q0w * qperpy * s121 + q0x * qperpz * s121 + 2. * q0y * q0y * s001 * theta + 2. * q0z * q0z * s001 * theta - 2. * qperpy * qperpy * s001 * theta - 2. * qperpz * qperpz * s001 * theta + 2. * qperpx * qperpy * s011 * theta - 2. * qperpw * qperpz * s011 * theta + 2. * qperpw * qperpy * s021 * theta + 2. * qperpx * qperpz * s021 * theta + q0y * (-(qperpx * s011) - qperpw * s021 + 2. * qperpy * (s001 - s101) + qperpx * s111 + qperpw * s121 - 2. * q0x * s011 * theta - 2. * q0w * s021 * theta) + q0z * (2. * qperpz * s001 + qperpw * s011 - qperpx * s021 - 2. * qperpz * s101 - qperpw * s111 + qperpx * s121 + 2. * q0w * s011 * theta - 2. * q0x * s021 * theta), -(q0x * qperpy * s012) + q0w * qperpz * s012 - q0w * qperpy * s022 - q0x * qperpz * s022 + q0x * qperpy * s112 - q0w * qperpz * s112 + q0w * qperpy * s122 + q0x * qperpz * s122 + 2. * q0y * q0y * s002 * theta + 2. * q0z * q0z * s002 * theta - 2. * qperpy * qperpy * s002 * theta - 2. * qperpz * qperpz * s002 * theta + 2. * qperpx * qperpy * s012 * theta - 2. * qperpw * qperpz * s012 * theta + 2. * qperpw * qperpy * s022 * theta + 2. * qperpx * qperpz * s022 * theta + q0y * (-(qperpx * s012) - qperpw * s022 + 2. * qperpy * (s002 - s102) + qperpx * s112 + qperpw * s122 - 2. * q0x * s012 * theta - 2. * q0w * s022 * theta) + q0z * (2. * qperpz * s002 + qperpw * s012 - qperpx * s022 - 2. * qperpz * s102 - qperpw * s112 + qperpx * s122 + 2. * q0w * s012 * theta - 2. * q0x * s022 * theta), ); c5[0] = DerivativeTerm::new( 0., 2. * (qperpy * qperpy * s000 + qperpz * qperpz * s000 - qperpx * qperpy * s010 + qperpw * qperpz * s010 - qperpw * qperpy * s020 - qperpx * qperpz * s020 - qperpy * qperpy * s100 - qperpz * qperpz * s100 + q0y * q0y * (-s000 + s100) + q0z * q0z * (-s000 + s100) + qperpx * qperpy * s110 - qperpw * qperpz * s110 + q0y * (q0x * (s010 - s110) + q0w * (s020 - s120)) + qperpw * qperpy * s120 + qperpx * qperpz * s120 + q0z * (-(q0w * s010) + q0x * s020 + q0w * s110 - q0x * s120)) * theta, 2. * (qperpy * qperpy * s001 + qperpz * qperpz * s001 - qperpx * qperpy * s011 + qperpw * qperpz * s011 - qperpw * qperpy * s021 - qperpx * qperpz * s021 - qperpy * qperpy * s101 - qperpz * qperpz * s101 + q0y * q0y * (-s001 + s101) + q0z * q0z * (-s001 + s101) + qperpx * qperpy * s111 - qperpw * qperpz * s111 + q0y * (q0x * (s011 - s111) + q0w * (s021 - s121)) + qperpw * qperpy * s121 + qperpx * qperpz * s121 + q0z * (-(q0w * s011) + q0x * s021 + q0w * s111 - q0x * s121)) * theta, 2. * (qperpy * qperpy * s002 + qperpz * qperpz * s002 - qperpx * qperpy * s012 + qperpw * qperpz * s012 - qperpw * qperpy * s022 - qperpx * qperpz * s022 - qperpy * qperpy * s102 - qperpz * qperpz * s102 + q0y * q0y * (-s002 + s102) + q0z * q0z * (-s002 + s102) + qperpx * qperpy * s112 - qperpw * qperpz * s112 + q0y * (q0x * (s012 - s112) + q0w * (s022 - s122)) + qperpw * qperpy * s122 + qperpx * qperpz * s122 + q0z * (-(q0w * s012) + q0x * s022 + q0w * s112 - q0x * s122)) * theta, ); c1[1] = DerivativeTerm::new( -t0y + t1y, -(qperpx * qperpy * s000) - qperpw * qperpz * s000 - s010 + q0z * q0z * s010 + qperpx * qperpx * s010 + qperpz * qperpz * s010 - q0y * q0z * s020 + qperpw * qperpx * s020 - qperpy * qperpz * s020 + qperpx * qperpy * s100 + qperpw * qperpz * s100 + q0w * q0z * (-s000 + s100) + q0x * q0x * (s010 - s110) + s110 - q0z * q0z * s110 - qperpx * qperpx * s110 - qperpz * qperpz * s110 + q0x * (q0y * (-s000 + s100) + q0w * (s020 - s120)) + q0y * q0z * s120 - qperpw * qperpx * s120 + qperpy * qperpz * s120, -(qperpx * qperpy * s001) - qperpw * qperpz * s001 - s011 + q0z * q0z * s011 + qperpx * qperpx * s011 + qperpz * qperpz * s011 - q0y * q0z * s021 + qperpw * qperpx * s021 - qperpy * qperpz * s021 + qperpx * qperpy * s101 + qperpw * qperpz * s101 + q0w * q0z * (-s001 + s101) + q0x * q0x * (s011 - s111) + s111 - q0z * q0z * s111 - qperpx * qperpx * s111 - qperpz * qperpz * s111 + q0x * (q0y * (-s001 + s101) + q0w * (s021 - s121)) + q0y * q0z * s121 - qperpw * qperpx * s121 + qperpy * qperpz * s121, -(qperpx * qperpy * s002) - qperpw * qperpz * s002 - s012 + q0z * q0z * s012 + qperpx * qperpx * s012 + qperpz * qperpz * s012 - q0y * q0z * s022 + qperpw * qperpx * s022 - qperpy * qperpz * s022 + qperpx * qperpy * s102 + qperpw * qperpz * s102 + q0w * q0z * (-s002 + s102) + q0x * q0x * (s012 - s112) + s112 - q0z * q0z * s112 - qperpx * qperpx * s112 - qperpz * qperpz * s112 + q0x * (q0y * (-s002 + s102) + q0w * (s022 - s122)) + q0y * q0z * s122 - qperpw * qperpx * s122 + qperpy * qperpz * s122, ); c2[1] = DerivativeTerm::new( 0., qperpx * qperpy * s000 + qperpw * qperpz * s000 + q0z * q0z * s010 - qperpx * qperpx * s010 - qperpz * qperpz * s010 - q0y * q0z * s020 - qperpw * qperpx * s020 + qperpy * qperpz * s020 - qperpx * qperpy * s100 - qperpw * qperpz * s100 + q0x * q0x * (s010 - s110) - q0z * q0z * s110 + qperpx * qperpx * s110 + qperpz * qperpz * s110 + q0y * q0z * s120 + qperpw * qperpx * s120 - qperpy * qperpz * s120 + 2. * q0z * qperpw * s000 * theta + 2. * q0y * qperpx * s000 * theta - 4. * q0z * qperpz * s010 * theta + 2. * q0z * qperpy * s020 * theta + 2. * q0y * qperpz * s020 * theta + q0x * (q0w * s020 + q0y * (-s000 + s100) - q0w * s120 + 2. * qperpy * s000 * theta - 4. * qperpx * s010 * theta - 2. * qperpw * s020 * theta) + q0w * (-(q0z * s000) + q0z * s100 + 2. * qperpz * s000 * theta - 2. * qperpx * s020 * theta), qperpx * qperpy * s001 + qperpw * qperpz * s001 + q0z * q0z * s011 - qperpx * qperpx * s011 - qperpz * qperpz * s011 - q0y * q0z * s021 - qperpw * qperpx * s021 + qperpy * qperpz * s021 - qperpx * qperpy * s101 - qperpw * qperpz * s101 + q0x * q0x * (s011 - s111) - q0z * q0z * s111 + qperpx * qperpx * s111 + qperpz * qperpz * s111 + q0y * q0z * s121 + qperpw * qperpx * s121 - qperpy * qperpz * s121 + 2. * q0z * qperpw * s001 * theta + 2. * q0y * qperpx * s001 * theta - 4. * q0z * qperpz * s011 * theta + 2. * q0z * qperpy * s021 * theta + 2. * q0y * qperpz * s021 * theta + q0x * (q0w * s021 + q0y * (-s001 + s101) - q0w * s121 + 2. * qperpy * s001 * theta - 4. * qperpx * s011 * theta - 2. * qperpw * s021 * theta) + q0w * (-(q0z * s001) + q0z * s101 + 2. * qperpz * s001 * theta - 2. * qperpx * s021 * theta), qperpx * qperpy * s002 + qperpw * qperpz * s002 + q0z * q0z * s012 - qperpx * qperpx * s012 - qperpz * qperpz * s012 - q0y * q0z * s022 - qperpw * qperpx * s022 + qperpy * qperpz * s022 - qperpx * qperpy * s102 - qperpw * qperpz * s102 + q0x * q0x * (s012 - s112) - q0z * q0z * s112 + qperpx * qperpx * s112 + qperpz * qperpz * s112 + q0y * q0z * s122 + qperpw * qperpx * s122 - qperpy * qperpz * s122 + 2. * q0z * qperpw * s002 * theta + 2. * q0y * qperpx * s002 * theta - 4. * q0z * qperpz * s012 * theta + 2. * q0z * qperpy * s022 * theta + 2. * q0y * qperpz * s022 * theta + q0x * (q0w * s022 + q0y * (-s002 + s102) - q0w * s122 + 2. * qperpy * s002 * theta - 4. * qperpx * s012 * theta - 2. * qperpw * s022 * theta) + q0w * (-(q0z * s002) + q0z * s102 + 2. * qperpz * s002 * theta - 2. * qperpx * s022 * theta), ); c3[1] = DerivativeTerm::new( 0., 2. * (-(q0x * qperpy * s000) - q0w * qperpz * s000 + 2. * q0x * qperpx * s010 + q0x * qperpw * s020 + q0w * qperpx * s020 + q0x * qperpy * s100 + q0w * qperpz * s100 - 2. * q0x * qperpx * s110 - q0x * qperpw * s120 - q0w * qperpx * s120 + q0z * (2. * qperpz * s010 - qperpy * s020 + qperpw * (-s000 + s100) - 2. * qperpz * s110 + qperpy * s120) + q0y * (-(qperpx * s000) - qperpz * s020 + qperpx * s100 + qperpz * s120)) * theta, 2. * (-(q0x * qperpy * s001) - q0w * qperpz * s001 + 2. * q0x * qperpx * s011 + q0x * qperpw * s021 + q0w * qperpx * s021 + q0x * qperpy * s101 + q0w * qperpz * s101 - 2. * q0x * qperpx * s111 - q0x * qperpw * s121 - q0w * qperpx * s121 + q0z * (2. * qperpz * s011 - qperpy * s021 + qperpw * (-s001 + s101) - 2. * qperpz * s111 + qperpy * s121) + q0y * (-(qperpx * s001) - qperpz * s021 + qperpx * s101 + qperpz * s121)) * theta, 2. * (-(q0x * qperpy * s002) - q0w * qperpz * s002 + 2. * q0x * qperpx * s012 + q0x * qperpw * s022 + q0w * qperpx * s022 + q0x * qperpy * s102 + q0w * qperpz * s102 - 2. * q0x * qperpx * s112 - q0x * qperpw * s122 - q0w * qperpx * s122 + q0z * (2. * qperpz * s012 - qperpy * s022 + qperpw * (-s002 + s102) - 2. * qperpz * s112 + qperpy * s122) + q0y * (-(qperpx * s002) - qperpz * s022 + qperpx * s102 + qperpz * s122)) * theta, ); c4[1] = DerivativeTerm::new( 0., -(q0x * qperpy * s000) - q0w * qperpz * s000 + 2. * q0x * qperpx * s010 + q0x * qperpw * s020 + q0w * qperpx * s020 + q0x * qperpy * s100 + q0w * qperpz * s100 - 2. * q0x * qperpx * s110 - q0x * qperpw * s120 - q0w * qperpx * s120 + 2. * qperpx * qperpy * s000 * theta + 2. * qperpw * qperpz * s000 * theta + 2. * q0x * q0x * s010 * theta + 2. * q0z * q0z * s010 * theta - 2. * qperpx * qperpx * s010 * theta - 2. * qperpz * qperpz * s010 * theta + 2. * q0w * q0x * s020 * theta - 2. * qperpw * qperpx * s020 * theta + 2. * qperpy * qperpz * s020 * theta + q0y * (-(qperpx * s000) - qperpz * s020 + qperpx * s100 + qperpz * s120 - 2. * q0x * s000 * theta) + q0z * (2. * qperpz * s010 - qperpy * s020 + qperpw * (-s000 + s100) - 2. * qperpz * s110 + qperpy * s120 - 2. * q0w * s000 * theta - 2. * q0y * s020 * theta), -(q0x * qperpy * s001) - q0w * qperpz * s001 + 2. * q0x * qperpx * s011 + q0x * qperpw * s021 + q0w * qperpx * s021 + q0x * qperpy * s101 + q0w * qperpz * s101 - 2. * q0x * qperpx * s111 - q0x * qperpw * s121 - q0w * qperpx * s121 + 2. * qperpx * qperpy * s001 * theta + 2. * qperpw * qperpz * s001 * theta + 2. * q0x * q0x * s011 * theta + 2. * q0z * q0z * s011 * theta - 2. * qperpx * qperpx * s011 * theta - 2. * qperpz * qperpz * s011 * theta + 2. * q0w * q0x * s021 * theta - 2. * qperpw * qperpx * s021 * theta + 2. * qperpy * qperpz * s021 * theta + q0y * (-(qperpx * s001) - qperpz * s021 + qperpx * s101 + qperpz * s121 - 2. * q0x * s001 * theta) + q0z * (2. * qperpz * s011 - qperpy * s021 + qperpw * (-s001 + s101) - 2. * qperpz * s111 + qperpy * s121 - 2. * q0w * s001 * theta - 2. * q0y * s021 * theta), -(q0x * qperpy * s002) - q0w * qperpz * s002 + 2. * q0x * qperpx * s012 + q0x * qperpw * s022 + q0w * qperpx * s022 + q0x * qperpy * s102 + q0w * qperpz * s102 - 2. * q0x * qperpx * s112 - q0x * qperpw * s122 - q0w * qperpx * s122 + 2. * qperpx * qperpy * s002 * theta + 2. * qperpw * qperpz * s002 * theta + 2. * q0x * q0x * s012 * theta + 2. * q0z * q0z * s012 * theta - 2. * qperpx * qperpx * s012 * theta - 2. * qperpz * qperpz * s012 * theta + 2. * q0w * q0x * s022 * theta - 2. * qperpw * qperpx * s022 * theta + 2. * qperpy * qperpz * s022 * theta + q0y * (-(qperpx * s002) - qperpz * s022 + qperpx * s102 + qperpz * s122 - 2. * q0x * s002 * theta) + q0z * (2. * qperpz * s012 - qperpy * s022 + qperpw * (-s002 + s102) - 2. * qperpz * s112 + qperpy * s122 - 2. * q0w * s002 * theta - 2. * q0y * s022 * theta), ); c5[1] = DerivativeTerm::new( 0., -2. * (qperpx * qperpy * s000 + qperpw * qperpz * s000 + q0z * q0z * s010 - qperpx * qperpx * s010 - qperpz * qperpz * s010 - q0y * q0z * s020 - qperpw * qperpx * s020 + qperpy * qperpz * s020 - qperpx * qperpy * s100 - qperpw * qperpz * s100 + q0w * q0z * (-s000 + s100) + q0x * q0x * (s010 - s110) - q0z * q0z * s110 + qperpx * qperpx * s110 + qperpz * qperpz * s110 + q0x * (q0y * (-s000 + s100) + q0w * (s020 - s120)) + q0y * q0z * s120 + qperpw * qperpx * s120 - qperpy * qperpz * s120) * theta, -2. * (qperpx * qperpy * s001 + qperpw * qperpz * s001 + q0z * q0z * s011 - qperpx * qperpx * s011 - qperpz * qperpz * s011 - q0y * q0z * s021 - qperpw * qperpx * s021 + qperpy * qperpz * s021 - qperpx * qperpy * s101 - qperpw * qperpz * s101 + q0w * q0z * (-s001 + s101) + q0x * q0x * (s011 - s111) - q0z * q0z * s111 + qperpx * qperpx * s111 + qperpz * qperpz * s111 + q0x * (q0y * (-s001 + s101) + q0w * (s021 - s121)) + q0y * q0z * s121 + qperpw * qperpx * s121 - qperpy * qperpz * s121) * theta, -2. * (qperpx * qperpy * s002 + qperpw * qperpz * s002 + q0z * q0z * s012 - qperpx * qperpx * s012 - qperpz * qperpz * s012 - q0y * q0z * s022 - qperpw * qperpx * s022 + qperpy * qperpz * s022 - qperpx * qperpy * s102 - qperpw * qperpz * s102 + q0w * q0z * (-s002 + s102) + q0x * q0x * (s012 - s112) - q0z * q0z * s112 + qperpx * qperpx * s112 + qperpz * qperpz * s112 + q0x * (q0y * (-s002 + s102) + q0w * (s022 - s122)) + q0y * q0z * s122 + qperpw * qperpx * s122 - qperpy * qperpz * s122) * theta, ); c1[2] = DerivativeTerm::new( -t0z + t1z, qperpw * qperpy * s000 - qperpx * qperpz * s000 - q0y * q0z * s010 - qperpw * qperpx * s010 - qperpy * qperpz * s010 - s020 + q0y * q0y * s020 + qperpx * qperpx * s020 + qperpy * qperpy * s020 - qperpw * qperpy * s100 + qperpx * qperpz * s100 + q0x * q0z * (-s000 + s100) + q0y * q0z * s110 + qperpw * qperpx * s110 + qperpy * qperpz * s110 + q0w * (q0y * (s000 - s100) + q0x * (-s010 + s110)) + q0x * q0x * (s020 - s120) + s120 - q0y * q0y * s120 - qperpx * qperpx * s120 - qperpy * qperpy * s120, qperpw * qperpy * s001 - qperpx * qperpz * s001 - q0y * q0z * s011 - qperpw * qperpx * s011 - qperpy * qperpz * s011 - s021 + q0y * q0y * s021 + qperpx * qperpx * s021 + qperpy * qperpy * s021 - qperpw * qperpy * s101 + qperpx * qperpz * s101 + q0x * q0z * (-s001 + s101) + q0y * q0z * s111 + qperpw * qperpx * s111 + qperpy * qperpz * s111 + q0w * (q0y * (s001 - s101) + q0x * (-s011 + s111)) + q0x * q0x * (s021 - s121) + s121 - q0y * q0y * s121 - qperpx * qperpx * s121 - qperpy * qperpy * s121, qperpw * qperpy * s002 - qperpx * qperpz * s002 - q0y * q0z * s012 - qperpw * qperpx * s012 - qperpy * qperpz * s012 - s022 + q0y * q0y * s022 + qperpx * qperpx * s022 + qperpy * qperpy * s022 - qperpw * qperpy * s102 + qperpx * qperpz * s102 + q0x * q0z * (-s002 + s102) + q0y * q0z * s112 + qperpw * qperpx * s112 + qperpy * qperpz * s112 + q0w * (q0y * (s002 - s102) + q0x * (-s012 + s112)) + q0x * q0x * (s022 - s122) + s122 - q0y * q0y * s122 - qperpx * qperpx * s122 - qperpy * qperpy * s122, ); c2[2] = DerivativeTerm::new( 0., q0w * q0y * s000 - q0x * q0z * s000 - qperpw * qperpy * s000 + qperpx * qperpz * s000 - q0w * q0x * s010 - q0y * q0z * s010 + qperpw * qperpx * s010 + qperpy * qperpz * s010 + q0x * q0x * s020 + q0y * q0y * s020 - qperpx * qperpx * s020 - qperpy * qperpy * s020 - q0w * q0y * s100 + q0x * q0z * s100 + qperpw * qperpy * s100 - qperpx * qperpz * s100 + q0w * q0x * s110 + q0y * q0z * s110 - qperpw * qperpx * s110 - qperpy * qperpz * s110 - q0x * q0x * s120 - q0y * q0y * s120 + qperpx * qperpx * s120 + qperpy * qperpy * s120 - 2. * q0y * qperpw * s000 * theta + 2. * q0z * qperpx * s000 * theta - 2. * q0w * qperpy * s000 * theta + 2. * q0x * qperpz * s000 * theta + 2. * q0x * qperpw * s010 * theta + 2. * q0w * qperpx * s010 * theta + 2. * q0z * qperpy * s010 * theta + 2. * q0y * qperpz * s010 * theta - 4. * q0x * qperpx * s020 * theta - 4. * q0y * qperpy * s020 * theta, q0w * q0y * s001 - q0x * q0z * s001 - qperpw * qperpy * s001 + qperpx * qperpz * s001 - q0w * q0x * s011 - q0y * q0z * s011 + qperpw * qperpx * s011 + qperpy * qperpz * s011 + q0x * q0x * s021 + q0y * q0y * s021 - qperpx * qperpx * s021 - qperpy * qperpy * s021 - q0w * q0y * s101 + q0x * q0z * s101 + qperpw * qperpy * s101 - qperpx * qperpz * s101 + q0w * q0x * s111 + q0y * q0z * s111 - qperpw * qperpx * s111 - qperpy * qperpz * s111 - q0x * q0x * s121 - q0y * q0y * s121 + qperpx * qperpx * s121 + qperpy * qperpy * s121 - 2. * q0y * qperpw * s001 * theta + 2. * q0z * qperpx * s001 * theta - 2. * q0w * qperpy * s001 * theta + 2. * q0x * qperpz * s001 * theta + 2. * q0x * qperpw * s011 * theta + 2. * q0w * qperpx * s011 * theta + 2. * q0z * qperpy * s011 * theta + 2. * q0y * qperpz * s011 * theta - 4. * q0x * qperpx * s021 * theta - 4. * q0y * qperpy * s021 * theta, q0w * q0y * s002 - q0x * q0z * s002 - qperpw * qperpy * s002 + qperpx * qperpz * s002 - q0w * q0x * s012 - q0y * q0z * s012 + qperpw * qperpx * s012 + qperpy * qperpz * s012 + q0x * q0x * s022 + q0y * q0y * s022 - qperpx * qperpx * s022 - qperpy * qperpy * s022 - q0w * q0y * s102 + q0x * q0z * s102 + qperpw * qperpy * s102 - qperpx * qperpz * s102 + q0w * q0x * s112 + q0y * q0z * s112 - qperpw * qperpx * s112 - qperpy * qperpz * s112 - q0x * q0x * s122 - q0y * q0y * s122 + qperpx * qperpx * s122 + qperpy * qperpy * s122 - 2. * q0y * qperpw * s002 * theta + 2. * q0z * qperpx * s002 * theta - 2. * q0w * qperpy * s002 * theta + 2. * q0x * qperpz * s002 * theta + 2. * q0x * qperpw * s012 * theta + 2. * q0w * qperpx * s012 * theta + 2. * q0z * qperpy * s012 * theta + 2. * q0y * qperpz * s012 * theta - 4. * q0x * qperpx * s022 * theta - 4. * q0y * qperpy * s022 * theta, ); c3[2] = DerivativeTerm::new( 0., -2. * (-(q0w * qperpy * s000) + q0x * qperpz * s000 + q0x * qperpw * s010 + q0w * qperpx * s010 - 2. * q0x * qperpx * s020 + q0w * qperpy * s100 - q0x * qperpz * s100 - q0x * qperpw * s110 - q0w * qperpx * s110 + q0z * (qperpx * s000 + qperpy * s010 - qperpx * s100 - qperpy * s110) + 2. * q0x * qperpx * s120 + q0y * (qperpz * s010 - 2. * qperpy * s020 + qperpw * (-s000 + s100) - qperpz * s110 + 2. * qperpy * s120)) * theta, -2. * (-(q0w * qperpy * s001) + q0x * qperpz * s001 + q0x * qperpw * s011 + q0w * qperpx * s011 - 2. * q0x * qperpx * s021 + q0w * qperpy * s101 - q0x * qperpz * s101 - q0x * qperpw * s111 - q0w * qperpx * s111 + q0z * (qperpx * s001 + qperpy * s011 - qperpx * s101 - qperpy * s111) + 2. * q0x * qperpx * s121 + q0y * (qperpz * s011 - 2. * qperpy * s021 + qperpw * (-s001 + s101) - qperpz * s111 + 2. * qperpy * s121)) * theta, -2. * (-(q0w * qperpy * s002) + q0x * qperpz * s002 + q0x * qperpw * s012 + q0w * qperpx * s012 - 2. * q0x * qperpx * s022 + q0w * qperpy * s102 - q0x * qperpz * s102 - q0x * qperpw * s112 - q0w * qperpx * s112 + q0z * (qperpx * s002 + qperpy * s012 - qperpx * s102 - qperpy * s112) + 2. * q0x * qperpx * s122 + q0y * (qperpz * s012 - 2. * qperpy * s022 + qperpw * (-s002 + s102) - qperpz * s112 + 2. * qperpy * s122)) * theta, ); c4[2] = DerivativeTerm::new( 0., q0w * qperpy * s000 - q0x * qperpz * s000 - q0x * qperpw * s010 - q0w * qperpx * s010 + 2. * q0x * qperpx * s020 - q0w * qperpy * s100 + q0x * qperpz * s100 + q0x * qperpw * s110 + q0w * qperpx * s110 - 2. * q0x * qperpx * s120 - 2. * qperpw * qperpy * s000 * theta + 2. * qperpx * qperpz * s000 * theta - 2. * q0w * q0x * s010 * theta + 2. * qperpw * qperpx * s010 * theta + 2. * qperpy * qperpz * s010 * theta + 2. * q0x * q0x * s020 * theta + 2. * q0y * q0y * s020 * theta - 2. * qperpx * qperpx * s020 * theta - 2. * qperpy * qperpy * s020 * theta + q0z * (-(qperpx * s000) - qperpy * s010 + qperpx * s100 + qperpy * s110 - 2. * q0x * s000 * theta) + q0y * (-(qperpz * s010) + 2. * qperpy * s020 + qperpw * (s000 - s100) + qperpz * s110 - 2. * qperpy * s120 + 2. * q0w * s000 * theta - 2. * q0z * s010 * theta), q0w * qperpy * s001 - q0x * qperpz * s001 - q0x * qperpw * s011 - q0w * qperpx * s011 + 2. * q0x * qperpx * s021 - q0w * qperpy * s101 + q0x * qperpz * s101 + q0x * qperpw * s111 + q0w * qperpx * s111 - 2. * q0x * qperpx * s121 - 2. * qperpw * qperpy * s001 * theta + 2. * qperpx * qperpz * s001 * theta - 2. * q0w * q0x * s011 * theta + 2. * qperpw * qperpx * s011 * theta + 2. * qperpy * qperpz * s011 * theta + 2. * q0x * q0x * s021 * theta + 2. * q0y * q0y * s021 * theta - 2. * qperpx * qperpx * s021 * theta - 2. * qperpy * qperpy * s021 * theta + q0z * (-(qperpx * s001) - qperpy * s011 + qperpx * s101 + qperpy * s111 - 2. * q0x * s001 * theta) + q0y * (-(qperpz * s011) + 2. * qperpy * s021 + qperpw * (s001 - s101) + qperpz * s111 - 2. * qperpy * s121 + 2. * q0w * s001 * theta - 2. * q0z * s011 * theta), q0w * qperpy * s002 - q0x * qperpz * s002 - q0x * qperpw * s012 - q0w * qperpx * s012 + 2. * q0x * qperpx * s022 - q0w * qperpy * s102 + q0x * qperpz * s102 + q0x * qperpw * s112 + q0w * qperpx * s112 - 2. * q0x * qperpx * s122 - 2. * qperpw * qperpy * s002 * theta + 2. * qperpx * qperpz * s002 * theta - 2. * q0w * q0x * s012 * theta + 2. * qperpw * qperpx * s012 * theta + 2. * qperpy * qperpz * s012 * theta + 2. * q0x * q0x * s022 * theta + 2. * q0y * q0y * s022 * theta - 2. * qperpx * qperpx * s022 * theta - 2. * qperpy * qperpy * s022 * theta + q0z * (-(qperpx * s002) - qperpy * s012 + qperpx * s102 + qperpy * s112 - 2. * q0x * s002 * theta) + q0y * (-(qperpz * s012) + 2. * qperpy * s022 + qperpw * (s002 - s102) + qperpz * s112 - 2. * qperpy * s122 + 2. * q0w * s002 * theta - 2. * q0z * s012 * theta), ); c5[2] = DerivativeTerm::new( 0., 2. * (qperpw * qperpy * s000 - qperpx * qperpz * s000 + q0y * q0z * s010 - qperpw * qperpx * s010 - qperpy * qperpz * s010 - q0y * q0y * s020 + qperpx * qperpx * s020 + qperpy * qperpy * s020 + q0x * q0z * (s000 - s100) - qperpw * qperpy * s100 + qperpx * qperpz * s100 + q0w * (q0y * (-s000 + s100) + q0x * (s010 - s110)) - q0y * q0z * s110 + qperpw * qperpx * s110 + qperpy * qperpz * s110 + q0y * q0y * s120 - qperpx * qperpx * s120 - qperpy * qperpy * s120 + q0x * q0x * (-s020 + s120)) * theta, 2. * (qperpw * qperpy * s001 - qperpx * qperpz * s001 + q0y * q0z * s011 - qperpw * qperpx * s011 - qperpy * qperpz * s011 - q0y * q0y * s021 + qperpx * qperpx * s021 + qperpy * qperpy * s021 + q0x * q0z * (s001 - s101) - qperpw * qperpy * s101 + qperpx * qperpz * s101 + q0w * (q0y * (-s001 + s101) + q0x * (s011 - s111)) - q0y * q0z * s111 + qperpw * qperpx * s111 + qperpy * qperpz * s111 + q0y * q0y * s121 - qperpx * qperpx * s121 - qperpy * qperpy * s121 + q0x * q0x * (-s021 + s121)) * theta, 2. * (qperpw * qperpy * s002 - qperpx * qperpz * s002 + q0y * q0z * s012 - qperpw * qperpx * s012 - qperpy * qperpz * s012 - q0y * q0y * s022 + qperpx * qperpx * s022 + qperpy * qperpy * s022 + q0x * q0z * (s002 - s102) - qperpw * qperpy * s102 + qperpx * qperpz * s102 + q0w * (q0y * (-s002 + s102) + q0x * (s012 - s112)) - q0y * q0z * s112 + qperpw * qperpx * s112 + qperpy * qperpz * s112 + q0y * q0y * s122 - qperpx * qperpx * s122 - qperpy * qperpy * s122 + q0x * q0x * (-s022 + s122)) * theta, ); (c1, c2, c3, c4, c5) } else { ( gpu_array_from_fn(|_| DerivativeTerm::default()), gpu_array_from_fn(|_| DerivativeTerm::default()), gpu_array_from_fn(|_| DerivativeTerm::default()), gpu_array_from_fn(|_| DerivativeTerm::default()), gpu_array_from_fn(|_| DerivativeTerm::default()), ) }; AnimatedTransform { start_transform: *start_transform, end_transform: *end_transform, start_time, end_time, actually_animated, t, r, s, has_rotation, c1, c2, c3, c4, c5, } } pub fn apply_point(&self, p: Point3f, time: Float) -> Point3f { if !self.actually_animated || time <= self.start_time { return self.start_transform.apply_to_point(p); } else if time >= self.end_time { return self.end_transform.apply_to_point(p); } let t = self.interpolate(time); t.apply_to_point(p) } pub fn apply_vector(&self, v: Vector3f, time: Float) -> Vector3f { if !self.actually_animated || time <= self.start_time { return self.start_transform.apply_to_vector(v); } else if time >= self.end_time { return self.end_transform.apply_to_vector(v); } let t = self.interpolate(time); t.apply_to_vector(v) } pub fn apply_ray(&self, r: &Ray, t_max: &mut Option) -> Ray { if !self.actually_animated || r.time <= self.start_time { return self.start_transform.apply_to_ray(r, t_max); } else if r.time >= self.end_time { return self.end_transform.apply_to_ray(r, t_max); } let t = self.interpolate(r.time); t.apply_to_ray(r, t_max) } pub fn apply_interaction(&self, si: &Interaction) -> Interaction { if !self.actually_animated { return self.start_transform.apply_to_interaction(si); } let t = self.interpolate(si.time()); t.apply_to_interaction(si) } pub fn interpolate(&self, time: Float) -> TransformGeneric { if !self.actually_animated || time <= self.start_time { return self.start_transform; } if time >= self.end_time { return self.end_transform; } let dt = (time - self.start_time) / (self.end_time - self.start_time); let trans = (1.0 - dt) * self.t[0] + dt * self.t[1]; let rotate = Quaternion::slerp(dt, self.r[0], self.r[1]); let scale = (1.0 - dt) * self.s[0] + dt * self.s[1]; let scale_transform = TransformGeneric::from_matrix(scale).expect("Scale matrix is not inversible"); TransformGeneric::translate(trans) * TransformGeneric::from(rotate) * scale_transform } pub fn apply_inverse_point(&self, p: Point3f, time: Float) -> Point3f { if !self.actually_animated { return self.start_transform.apply_inverse(p); } self.interpolate(time).apply_inverse(p) } pub fn apply_inverse_vector(&self, v: Vector3f, time: Float) -> Vector3f { if !self.actually_animated { return self.start_transform.apply_inverse_vector(v); } self.interpolate(time).apply_inverse_vector(v) } pub fn apply_inverse_normal(&self, n: Normal3f, time: Float) -> Normal3f { if !self.actually_animated { return self.start_transform.apply_inverse_normal(n); } self.interpolate(time).apply_inverse_normal(n) } pub fn motion_bounds(&self, b: &Bounds3f) -> Bounds3f { if !self.actually_animated { return self.start_transform.apply_to_bounds(*b); } self.start_transform .apply_to_bounds(*b) .union(self.end_transform.apply_to_bounds(*b)) } pub fn is_animated(&self) -> bool { self.actually_animated } } pub fn look_at( pos: impl Into, look: impl Into, up: impl Into, ) -> Option> { let mut world_from_camera: SquareMatrix = SquareMatrix::default(); // Initialize fourth column of viewing matrix let pos: Point3f = pos.into(); let look: Point3f = look.into(); let up: Point3f = up.into(); world_from_camera[0][3] = pos.x(); world_from_camera[1][3] = pos.y(); world_from_camera[2][3] = pos.z(); world_from_camera[3][3] = 1.; // Initialize first three columns of viewing matrix let dir = (look - pos).normalize(); if Vector3f::from(up).normalize().cross(dir).norm() == 0. { panic!( "LookAt: \"up\" vector ({}, {}, {}) and viewing direction ({}, {}, {}) passed to LookAt are pointing in the same direction.", up.x(), up.y(), up.z(), dir.x(), dir.y(), dir.z() ); } let right = Vector3f::from(up).normalize().cross(dir).normalize(); let new_up = dir.cross(right); world_from_camera[0][0] = right.x(); world_from_camera[1][0] = right.y(); world_from_camera[2][0] = right.z(); world_from_camera[3][0] = 0.; world_from_camera[0][1] = new_up.x(); world_from_camera[1][1] = new_up.y(); world_from_camera[2][1] = new_up.z(); world_from_camera[3][1] = 0.; world_from_camera[0][2] = dir.x(); world_from_camera[1][2] = dir.y(); world_from_camera[2][2] = dir.z(); world_from_camera[3][2] = 0.; let camera_from_world = world_from_camera.inverse()?; Some(TransformGeneric::new(camera_from_world, world_from_camera)) }