Change to using a dedicated affine transform type.
This lets certain operations, especially matrix inversion, be quite a bit faster. And we don't need anything beyond affine transformations anyway.
This commit is contained in:
parent
e6f9af9336
commit
e0ee0d6dff
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
lerp::{lerp, lerp_slice, Lerp},
|
lerp::{lerp, lerp_slice, Lerp},
|
||||||
math::{fast_minf32, Matrix4x4, Point, Vector},
|
math::{fast_minf32, Point, Transform, Vector},
|
||||||
};
|
};
|
||||||
|
|
||||||
const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;
|
const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;
|
||||||
|
@ -55,7 +55,7 @@ impl BBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new BBox transformed into a different space.
|
// Creates a new BBox transformed into a different space.
|
||||||
pub fn transformed(&self, xform: Matrix4x4) -> BBox {
|
pub fn transformed(&self, xform: Transform) -> BBox {
|
||||||
// BBox corners
|
// BBox corners
|
||||||
let vs = [
|
let vs = [
|
||||||
Point::new(self.min.x(), self.min.y(), self.min.z()),
|
Point::new(self.min.x(), self.min.y(), self.min.z()),
|
||||||
|
@ -150,7 +150,7 @@ impl Lerp for BBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_bbox_slice_from(bbs_in: &[BBox], xforms: &[Matrix4x4], bbs_out: &mut Vec<BBox>) {
|
pub fn transform_bbox_slice_from(bbs_in: &[BBox], xforms: &[Transform], bbs_out: &mut Vec<BBox>) {
|
||||||
bbs_out.clear();
|
bbs_out.clear();
|
||||||
|
|
||||||
// Transform the bounding boxes
|
// Transform the bounding boxes
|
||||||
|
|
|
@ -4,14 +4,14 @@ use kioku::Arena;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
math::{Matrix4x4, Point, Vector},
|
math::{Point, Transform, Vector},
|
||||||
ray::Ray,
|
ray::Ray,
|
||||||
sampling::square_to_circle,
|
sampling::square_to_circle,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Camera<'a> {
|
pub struct Camera<'a> {
|
||||||
transforms: &'a [Matrix4x4],
|
transforms: &'a [Transform],
|
||||||
fovs: &'a [f32],
|
fovs: &'a [f32],
|
||||||
tfovs: &'a [f32],
|
tfovs: &'a [f32],
|
||||||
aperture_radii: &'a [f32],
|
aperture_radii: &'a [f32],
|
||||||
|
@ -21,7 +21,7 @@ pub struct Camera<'a> {
|
||||||
impl<'a> Camera<'a> {
|
impl<'a> Camera<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
arena: &'a Arena,
|
arena: &'a Arena,
|
||||||
transforms: &[Matrix4x4],
|
transforms: &[Transform],
|
||||||
fovs: &[f32],
|
fovs: &[f32],
|
||||||
mut aperture_radii: &[f32],
|
mut aperture_radii: &[f32],
|
||||||
mut focus_distances: &[f32],
|
mut focus_distances: &[f32],
|
||||||
|
|
28
src/lerp.rs
28
src/lerp.rs
|
@ -1,6 +1,6 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use math3d::{Matrix4x4, Normal, Point, Vector};
|
use math3d::{Normal, Point, Transform, Vector};
|
||||||
|
|
||||||
/// Trait for allowing a type to be linearly interpolated.
|
/// Trait for allowing a type to be linearly interpolated.
|
||||||
pub trait Lerp: Copy {
|
pub trait Lerp: Copy {
|
||||||
|
@ -106,8 +106,8 @@ impl Lerp for glam::Vec4 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lerp for Matrix4x4 {
|
impl Lerp for Transform {
|
||||||
fn lerp(self, other: Matrix4x4, alpha: f32) -> Matrix4x4 {
|
fn lerp(self, other: Transform, alpha: f32) -> Transform {
|
||||||
(self * (1.0 - alpha)) + (other * alpha)
|
(self * (1.0 - alpha)) + (other * alpha)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,23 +215,21 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lerp_matrix() {
|
fn lerp_matrix() {
|
||||||
let a = Matrix4x4::new_from_values(
|
let a = Transform::new_from_values(
|
||||||
0.0, 2.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0,
|
0.0, 2.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
|
||||||
);
|
);
|
||||||
let b = Matrix4x4::new_from_values(
|
let b = Transform::new_from_values(
|
||||||
-1.0, 1.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
-1.0, 1.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let c1 = Matrix4x4::new_from_values(
|
let c1 = Transform::new_from_values(
|
||||||
-0.25, 1.75, 2.25, 3.25, 4.25, 5.25, 6.25, 7.25, 8.25, 9.25, 10.25, 11.25, 12.25,
|
-0.25, 1.75, 2.25, 3.25, 4.25, 5.25, 6.25, 7.25, 8.25, 9.25, 10.25, 11.25,
|
||||||
13.25, 14.25, 15.25,
|
|
||||||
);
|
);
|
||||||
let c2 = Matrix4x4::new_from_values(
|
let c2 = Transform::new_from_values(
|
||||||
-0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5,
|
-0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5,
|
||||||
);
|
);
|
||||||
let c3 = Matrix4x4::new_from_values(
|
let c3 = Transform::new_from_values(
|
||||||
-0.75, 1.25, 2.75, 3.75, 4.75, 5.75, 6.75, 7.75, 8.75, 9.75, 10.75, 11.75, 12.75,
|
-0.75, 1.25, 2.75, 3.75, 4.75, 5.75, 6.75, 7.75, 8.75, 9.75, 10.75, 11.75,
|
||||||
13.75, 14.75, 15.75,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(a.lerp(b, 0.0), a);
|
assert_eq!(a.lerp(b, 0.0), a);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
color::SpectralSample,
|
color::SpectralSample,
|
||||||
math::{Matrix4x4, Normal, Point, Vector},
|
math::{Normal, Point, Transform, Vector},
|
||||||
surface::Surface,
|
surface::Surface,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ pub trait SurfaceLight: Surface {
|
||||||
/// - The pdf of the sample.
|
/// - The pdf of the sample.
|
||||||
fn sample_from_point(
|
fn sample_from_point(
|
||||||
&self,
|
&self,
|
||||||
space: &Matrix4x4,
|
space: &Transform,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
u: f32,
|
u: f32,
|
||||||
v: f32,
|
v: f32,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
boundable::Boundable,
|
boundable::Boundable,
|
||||||
color::{Color, SpectralSample},
|
color::{Color, SpectralSample},
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
math::{cross, dot, Matrix4x4, Normal, Point, Vector},
|
math::{cross, dot, Normal, Point, Transform, Vector},
|
||||||
ray::{RayBatch, RayStack},
|
ray::{RayBatch, RayStack},
|
||||||
sampling::{
|
sampling::{
|
||||||
spherical_triangle_solid_angle, triangle_surface_area, uniform_sample_spherical_triangle,
|
spherical_triangle_solid_angle, triangle_surface_area, uniform_sample_spherical_triangle,
|
||||||
|
@ -51,7 +51,7 @@ impl<'a> RectangleLight<'a> {
|
||||||
// more efficiently by inlining it there.
|
// more efficiently by inlining it there.
|
||||||
fn sample_pdf(
|
fn sample_pdf(
|
||||||
&self,
|
&self,
|
||||||
space: &Matrix4x4,
|
space: &Transform,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
sample_dir: Vector,
|
sample_dir: Vector,
|
||||||
hit_point: Point,
|
hit_point: Point,
|
||||||
|
@ -97,7 +97,7 @@ impl<'a> RectangleLight<'a> {
|
||||||
|
|
||||||
// fn outgoing(
|
// fn outgoing(
|
||||||
// &self,
|
// &self,
|
||||||
// space: &Matrix4x4,
|
// space: &Transform,
|
||||||
// dir: Vector,
|
// dir: Vector,
|
||||||
// u: f32,
|
// u: f32,
|
||||||
// v: f32,
|
// v: f32,
|
||||||
|
@ -120,7 +120,7 @@ impl<'a> RectangleLight<'a> {
|
||||||
impl<'a> SurfaceLight for RectangleLight<'a> {
|
impl<'a> SurfaceLight for RectangleLight<'a> {
|
||||||
fn sample_from_point(
|
fn sample_from_point(
|
||||||
&self,
|
&self,
|
||||||
space: &Matrix4x4,
|
space: &Transform,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
u: f32,
|
u: f32,
|
||||||
v: f32,
|
v: f32,
|
||||||
|
@ -261,7 +261,7 @@ impl<'a> Surface for RectangleLight<'a> {
|
||||||
ray_stack: &mut RayStack,
|
ray_stack: &mut RayStack,
|
||||||
isects: &mut [SurfaceIntersection],
|
isects: &mut [SurfaceIntersection],
|
||||||
shader: &dyn SurfaceShader,
|
shader: &dyn SurfaceShader,
|
||||||
space: &[Matrix4x4],
|
space: &[Transform],
|
||||||
) {
|
) {
|
||||||
let _ = shader; // Silence 'unused' warning
|
let _ = shader; // Silence 'unused' warning
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
boundable::Boundable,
|
boundable::Boundable,
|
||||||
color::{Color, SpectralSample},
|
color::{Color, SpectralSample},
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
math::{coordinate_system_from_vector, dot, Matrix4x4, Normal, Point, Vector},
|
math::{coordinate_system_from_vector, dot, Normal, Point, Transform, Vector},
|
||||||
ray::{RayBatch, RayStack},
|
ray::{RayBatch, RayStack},
|
||||||
sampling::{uniform_sample_cone, uniform_sample_cone_pdf, uniform_sample_sphere},
|
sampling::{uniform_sample_cone, uniform_sample_cone_pdf, uniform_sample_sphere},
|
||||||
shading::surface_closure::SurfaceClosure,
|
shading::surface_closure::SurfaceClosure,
|
||||||
|
@ -50,7 +50,7 @@ impl<'a> SphereLight<'a> {
|
||||||
// more efficiently by inlining it there.
|
// more efficiently by inlining it there.
|
||||||
fn sample_pdf(
|
fn sample_pdf(
|
||||||
&self,
|
&self,
|
||||||
space: &Matrix4x4,
|
space: &Transform,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
sample_dir: Vector,
|
sample_dir: Vector,
|
||||||
sample_u: f32,
|
sample_u: f32,
|
||||||
|
@ -84,7 +84,7 @@ impl<'a> SphereLight<'a> {
|
||||||
impl<'a> SurfaceLight for SphereLight<'a> {
|
impl<'a> SurfaceLight for SphereLight<'a> {
|
||||||
fn sample_from_point(
|
fn sample_from_point(
|
||||||
&self,
|
&self,
|
||||||
space: &Matrix4x4,
|
space: &Transform,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
u: f32,
|
u: f32,
|
||||||
v: f32,
|
v: f32,
|
||||||
|
@ -210,7 +210,7 @@ impl<'a> Surface for SphereLight<'a> {
|
||||||
ray_stack: &mut RayStack,
|
ray_stack: &mut RayStack,
|
||||||
isects: &mut [SurfaceIntersection],
|
isects: &mut [SurfaceIntersection],
|
||||||
shader: &dyn SurfaceShader,
|
shader: &dyn SurfaceShader,
|
||||||
space: &[Matrix4x4],
|
space: &[Transform],
|
||||||
) {
|
) {
|
||||||
let _ = shader; // Silence 'unused' warning
|
let _ = shader; // Silence 'unused' warning
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
pub use math3d::{cross, dot, CrossProduct, DotProduct, Matrix4x4, Normal, Point, Vector};
|
pub use math3d::{cross, dot, CrossProduct, DotProduct, Normal, Point, Transform, Vector};
|
||||||
|
|
||||||
/// Clamps a value between a min and max.
|
/// Clamps a value between a min and max.
|
||||||
pub fn clamp<T: PartialOrd>(v: T, lower: T, upper: T) -> T {
|
pub fn clamp<T: PartialOrd>(v: T, lower: T, upper: T) -> T {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
color::{rec709_e_to_xyz, Color},
|
color::{rec709_e_to_xyz, Color},
|
||||||
light::WorldLightSource,
|
light::WorldLightSource,
|
||||||
math::Matrix4x4,
|
math::Transform,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scene::Scene,
|
scene::Scene,
|
||||||
scene::World,
|
scene::World,
|
||||||
|
@ -553,16 +553,17 @@ fn parse_world<'a>(arena: &'a Arena, tree: &'a DataTree) -> Result<World<'a>, Ps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> {
|
pub fn parse_matrix(contents: &str) -> Result<Transform, PsyParseError> {
|
||||||
if let IResult::Ok((leftover, ns)) = all_consuming(tuple((
|
if let IResult::Ok((leftover, ns)) = all_consuming(tuple((
|
||||||
ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32,
|
ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32,
|
||||||
ws_f32, ws_f32, ws_f32, ws_f32, ws_f32,
|
ws_f32, ws_f32, ws_f32, ws_f32, ws_f32,
|
||||||
)))(contents)
|
)))(contents)
|
||||||
{
|
{
|
||||||
if leftover.is_empty() {
|
if leftover.is_empty() {
|
||||||
return Ok(Matrix4x4::new_from_values(
|
return Ok(Transform::new_from_values(
|
||||||
ns.0, ns.4, ns.8, ns.12, ns.1, ns.5, ns.9, ns.13, ns.2, ns.6, ns.10, ns.14, ns.3,
|
// We throw away the last row, since it's not necessarily affine.
|
||||||
ns.7, ns.11, ns.15,
|
// TODO: is there a more correct way to handle this?
|
||||||
|
ns.0, ns.4, ns.8, ns.12, ns.1, ns.5, ns.9, ns.13, ns.2, ns.6, ns.10, ns.14,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use glam::BVec4A;
|
use glam::BVec4A;
|
||||||
|
|
||||||
use crate::math::{Matrix4x4, Point, Vector};
|
use crate::math::{Point, Transform, Vector};
|
||||||
|
|
||||||
type RayIndexType = u16;
|
type RayIndexType = u16;
|
||||||
type FlagType = u8;
|
type FlagType = u8;
|
||||||
|
@ -119,7 +119,7 @@ impl RayBatch {
|
||||||
///
|
///
|
||||||
/// This should be called when entering (and exiting) traversal of a
|
/// This should be called when entering (and exiting) traversal of a
|
||||||
/// new transform space.
|
/// new transform space.
|
||||||
pub fn update_local(&mut self, idx: usize, xform: &Matrix4x4) {
|
pub fn update_local(&mut self, idx: usize, xform: &Transform) {
|
||||||
self.hot[idx].orig_local = self.cold[idx].orig * *xform;
|
self.hot[idx].orig_local = self.cold[idx].orig * *xform;
|
||||||
self.hot[idx].dir_inv_local = Vector {
|
self.hot[idx].dir_inv_local = Vector {
|
||||||
co: (self.cold[idx].dir * *xform).co.recip(),
|
co: (self.cold[idx].dir * *xform).co.recip(),
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
color::SpectralSample,
|
color::SpectralSample,
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
light::SurfaceLight,
|
light::SurfaceLight,
|
||||||
math::{Matrix4x4, Normal, Point},
|
math::{Normal, Point, Transform},
|
||||||
shading::SurfaceShader,
|
shading::SurfaceShader,
|
||||||
surface::{Surface, SurfaceIntersection},
|
surface::{Surface, SurfaceIntersection},
|
||||||
transform_stack::TransformStack,
|
transform_stack::TransformStack,
|
||||||
|
@ -21,7 +21,7 @@ pub struct Assembly<'a> {
|
||||||
// Instance list
|
// Instance list
|
||||||
pub instances: &'a [Instance],
|
pub instances: &'a [Instance],
|
||||||
pub light_instances: &'a [Instance],
|
pub light_instances: &'a [Instance],
|
||||||
pub xforms: &'a [Matrix4x4],
|
pub xforms: &'a [Transform],
|
||||||
|
|
||||||
// Surface shader list
|
// Surface shader list
|
||||||
pub surface_shaders: &'a [&'a dyn SurfaceShader],
|
pub surface_shaders: &'a [&'a dyn SurfaceShader],
|
||||||
|
@ -60,7 +60,7 @@ impl<'a> Assembly<'a> {
|
||||||
let sel_xform = if !xform_stack.top().is_empty() {
|
let sel_xform = if !xform_stack.top().is_empty() {
|
||||||
lerp_slice(xform_stack.top(), time)
|
lerp_slice(xform_stack.top(), time)
|
||||||
} else {
|
} else {
|
||||||
Matrix4x4::new()
|
Transform::new()
|
||||||
};
|
};
|
||||||
if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel.select(
|
if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel.select(
|
||||||
idata.incoming * sel_xform,
|
idata.incoming * sel_xform,
|
||||||
|
@ -90,7 +90,7 @@ impl<'a> Assembly<'a> {
|
||||||
if !pxforms.is_empty() {
|
if !pxforms.is_empty() {
|
||||||
lerp_slice(pxforms, time)
|
lerp_slice(pxforms, time)
|
||||||
} else {
|
} else {
|
||||||
Matrix4x4::new()
|
Transform::new()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ pub struct AssemblyBuilder<'a> {
|
||||||
|
|
||||||
// Instance list
|
// Instance list
|
||||||
instances: Vec<Instance>,
|
instances: Vec<Instance>,
|
||||||
xforms: Vec<Matrix4x4>,
|
xforms: Vec<Transform>,
|
||||||
|
|
||||||
// Shader list
|
// Shader list
|
||||||
surface_shaders: Vec<&'a dyn SurfaceShader>,
|
surface_shaders: Vec<&'a dyn SurfaceShader>,
|
||||||
|
@ -224,7 +224,7 @@ impl<'a> AssemblyBuilder<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
surface_shader_name: Option<&str>,
|
surface_shader_name: Option<&str>,
|
||||||
xforms: Option<&[Matrix4x4]>,
|
xforms: Option<&[Transform]>,
|
||||||
) {
|
) {
|
||||||
// Make sure name exists
|
// Make sure name exists
|
||||||
if !self.name_exists(name) {
|
if !self.name_exists(name) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
bbox::BBox,
|
bbox::BBox,
|
||||||
boundable::Boundable,
|
boundable::Boundable,
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
math::{cross, dot, Matrix4x4, Normal, Point},
|
math::{cross, dot, Normal, Point, Transform},
|
||||||
ray::{RayBatch, RayStack},
|
ray::{RayBatch, RayStack},
|
||||||
shading::SurfaceClosure,
|
shading::SurfaceClosure,
|
||||||
};
|
};
|
||||||
|
@ -150,13 +150,13 @@ impl<'a> MicropolyBatch<'a> {
|
||||||
rays: &mut RayBatch,
|
rays: &mut RayBatch,
|
||||||
ray_stack: &mut RayStack,
|
ray_stack: &mut RayStack,
|
||||||
isects: &mut [SurfaceIntersection],
|
isects: &mut [SurfaceIntersection],
|
||||||
space: &[Matrix4x4],
|
space: &[Transform],
|
||||||
) {
|
) {
|
||||||
// Precalculate transform for non-motion blur cases
|
// Precalculate transform for non-motion blur cases
|
||||||
let static_mat_space = if space.len() == 1 {
|
let static_mat_space = if space.len() == 1 {
|
||||||
lerp_slice(space, 0.0).inverse()
|
lerp_slice(space, 0.0).inverse()
|
||||||
} else {
|
} else {
|
||||||
Matrix4x4::new()
|
Transform::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.accel
|
self.accel
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
boundable::Boundable,
|
boundable::Boundable,
|
||||||
math::{Matrix4x4, Normal, Point, Vector},
|
math::{Normal, Point, Transform, Vector},
|
||||||
ray::{RayBatch, RayStack},
|
ray::{RayBatch, RayStack},
|
||||||
shading::surface_closure::SurfaceClosure,
|
shading::surface_closure::SurfaceClosure,
|
||||||
shading::SurfaceShader,
|
shading::SurfaceShader,
|
||||||
|
@ -25,7 +25,7 @@ pub trait Surface: Boundable + Debug + Sync {
|
||||||
ray_stack: &mut RayStack,
|
ray_stack: &mut RayStack,
|
||||||
isects: &mut [SurfaceIntersection],
|
isects: &mut [SurfaceIntersection],
|
||||||
shader: &dyn SurfaceShader,
|
shader: &dyn SurfaceShader,
|
||||||
space: &[Matrix4x4],
|
space: &[Transform],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ pub struct SurfaceIntersectionData {
|
||||||
// a cube centered around `pos` with dimensions of `2 * pos_err`.
|
// a cube centered around `pos` with dimensions of `2 * pos_err`.
|
||||||
pub nor: Normal, // Shading normal
|
pub nor: Normal, // Shading normal
|
||||||
pub nor_g: Normal, // True geometric normal
|
pub nor_g: Normal, // True geometric normal
|
||||||
pub local_space: Matrix4x4, // Matrix from global space to local space
|
pub local_space: Transform, // Matrix from global space to local space
|
||||||
pub t: f32, // Ray t-value at the intersection point
|
pub t: f32, // Ray t-value at the intersection point
|
||||||
pub sample_pdf: f32, // The PDF of getting this point by explicitly sampling the surface
|
pub sample_pdf: f32, // The PDF of getting this point by explicitly sampling the surface
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
bbox::BBox,
|
bbox::BBox,
|
||||||
boundable::Boundable,
|
boundable::Boundable,
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
math::{cross, dot, Matrix4x4, Normal, Point},
|
math::{cross, dot, Normal, Point, Transform},
|
||||||
ray::{RayBatch, RayStack},
|
ray::{RayBatch, RayStack},
|
||||||
shading::SurfaceShader,
|
shading::SurfaceShader,
|
||||||
};
|
};
|
||||||
|
@ -128,13 +128,13 @@ impl<'a> Surface for TriangleMesh<'a> {
|
||||||
ray_stack: &mut RayStack,
|
ray_stack: &mut RayStack,
|
||||||
isects: &mut [SurfaceIntersection],
|
isects: &mut [SurfaceIntersection],
|
||||||
shader: &dyn SurfaceShader,
|
shader: &dyn SurfaceShader,
|
||||||
space: &[Matrix4x4],
|
space: &[Transform],
|
||||||
) {
|
) {
|
||||||
// Precalculate transform for non-motion blur cases
|
// Precalculate transform for non-motion blur cases
|
||||||
let static_mat_space = if space.len() == 1 {
|
let static_mat_space = if space.len() == 1 {
|
||||||
lerp_slice(space, 0.0).inverse()
|
lerp_slice(space, 0.0).inverse()
|
||||||
} else {
|
} else {
|
||||||
Matrix4x4::new()
|
Transform::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.accel
|
self.accel
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
accel::ray_code,
|
accel::ray_code,
|
||||||
color::{rec709_to_xyz, Color},
|
color::{rec709_to_xyz, Color},
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
math::Matrix4x4,
|
math::Transform,
|
||||||
ray::{RayBatch, RayStack},
|
ray::{RayBatch, RayStack},
|
||||||
scene::{Assembly, InstanceType, Object},
|
scene::{Assembly, InstanceType, Object},
|
||||||
shading::{SimpleSurfaceShader, SurfaceShader},
|
shading::{SimpleSurfaceShader, SurfaceShader},
|
||||||
|
@ -63,7 +63,7 @@ impl<'a> TracerInner<'a> {
|
||||||
|
|
||||||
// Prep the accel part of the rays.
|
// Prep the accel part of the rays.
|
||||||
{
|
{
|
||||||
let ident = Matrix4x4::new();
|
let ident = Transform::new();
|
||||||
for i in 0..rays.len() {
|
for i in 0..rays.len() {
|
||||||
rays.update_local(i, &ident);
|
rays.update_local(i, &ident);
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ impl<'a> TracerInner<'a> {
|
||||||
rays.update_local(ray_idx, &lerp_slice(xforms, t));
|
rays.update_local(ray_idx, &lerp_slice(xforms, t));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let ident = Matrix4x4::new();
|
let ident = Transform::new();
|
||||||
ray_stack.pop_do_next_task(|ray_idx| {
|
ray_stack.pop_do_next_task(|ray_idx| {
|
||||||
rays.update_local(ray_idx, &ident);
|
rays.update_local(ray_idx, &ident);
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,10 +3,10 @@ use std::{
|
||||||
mem::{transmute, MaybeUninit},
|
mem::{transmute, MaybeUninit},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{algorithm::merge_slices_to, math::Matrix4x4};
|
use crate::{algorithm::merge_slices_to, math::Transform};
|
||||||
|
|
||||||
pub struct TransformStack {
|
pub struct TransformStack {
|
||||||
stack: Vec<MaybeUninit<Matrix4x4>>,
|
stack: Vec<MaybeUninit<Transform>>,
|
||||||
stack_indices: Vec<usize>,
|
stack_indices: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,11 @@ impl TransformStack {
|
||||||
self.stack_indices.push(0);
|
self.stack_indices.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, xforms: &[Matrix4x4]) {
|
pub fn push(&mut self, xforms: &[Transform]) {
|
||||||
assert!(!xforms.is_empty());
|
assert!(!xforms.is_empty());
|
||||||
|
|
||||||
if self.stack.is_empty() {
|
if self.stack.is_empty() {
|
||||||
let xforms: &[MaybeUninit<Matrix4x4>] = unsafe { transmute(xforms) };
|
let xforms: &[MaybeUninit<Transform>] = unsafe { transmute(xforms) };
|
||||||
self.stack.extend(xforms);
|
self.stack.extend(xforms);
|
||||||
} else {
|
} else {
|
||||||
let sil = self.stack_indices.len();
|
let sil = self.stack_indices.len();
|
||||||
|
@ -73,7 +73,7 @@ impl TransformStack {
|
||||||
self.stack_indices.pop();
|
self.stack_indices.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn top(&self) -> &[Matrix4x4] {
|
pub fn top(&self) -> &[Transform] {
|
||||||
let sil = self.stack_indices.len();
|
let sil = self.stack_indices.len();
|
||||||
let i1 = self.stack_indices[sil - 2];
|
let i1 = self.stack_indices[sil - 2];
|
||||||
let i2 = self.stack_indices[sil - 1];
|
let i2 = self.stack_indices[sil - 1];
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
mod matrix;
|
|
||||||
mod normal;
|
mod normal;
|
||||||
mod point;
|
mod point;
|
||||||
|
mod transform;
|
||||||
mod vector;
|
mod vector;
|
||||||
|
|
||||||
pub use self::{matrix::Matrix4x4, normal::Normal, point::Point, vector::Vector};
|
pub use self::{normal::Normal, point::Point, transform::Transform, vector::Vector};
|
||||||
|
|
||||||
/// Trait for calculating dot products.
|
/// Trait for calculating dot products.
|
||||||
pub trait DotProduct {
|
pub trait DotProduct {
|
||||||
|
|
|
@ -1,201 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::ops::{Add, Mul};
|
|
||||||
|
|
||||||
use approx::relative_eq;
|
|
||||||
use glam::{Mat4, Vec4};
|
|
||||||
|
|
||||||
use super::Point;
|
|
||||||
|
|
||||||
/// A 4x4 matrix, used for transforms
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
||||||
pub struct Matrix4x4(pub Mat4);
|
|
||||||
|
|
||||||
impl Matrix4x4 {
|
|
||||||
/// Creates a new identity matrix
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Matrix4x4 {
|
|
||||||
Matrix4x4(Mat4::IDENTITY)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new matrix with the specified values:
|
|
||||||
/// a b c d
|
|
||||||
/// e f g h
|
|
||||||
/// i j k l
|
|
||||||
/// m n o p
|
|
||||||
#[inline]
|
|
||||||
#[allow(clippy::many_single_char_names)]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new_from_values(
|
|
||||||
a: f32,
|
|
||||||
b: f32,
|
|
||||||
c: f32,
|
|
||||||
d: f32,
|
|
||||||
e: f32,
|
|
||||||
f: f32,
|
|
||||||
g: f32,
|
|
||||||
h: f32,
|
|
||||||
i: f32,
|
|
||||||
j: f32,
|
|
||||||
k: f32,
|
|
||||||
l: f32,
|
|
||||||
m: f32,
|
|
||||||
n: f32,
|
|
||||||
o: f32,
|
|
||||||
p: f32,
|
|
||||||
) -> Matrix4x4 {
|
|
||||||
Matrix4x4(Mat4::from_cols(
|
|
||||||
Vec4::new(a, e, i, m),
|
|
||||||
Vec4::new(b, f, j, n),
|
|
||||||
Vec4::new(c, g, k, o),
|
|
||||||
Vec4::new(d, h, l, p),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn from_location(loc: Point) -> Matrix4x4 {
|
|
||||||
Matrix4x4(Mat4::from_translation(loc.co.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the matrices are approximately equal to each other.
|
|
||||||
/// Each corresponding element in the matrices cannot have a relative
|
|
||||||
/// error exceeding epsilon.
|
|
||||||
#[inline]
|
|
||||||
pub fn aprx_eq(&self, other: Matrix4x4, epsilon: f32) -> bool {
|
|
||||||
let mut eq = true;
|
|
||||||
for c in 0..4 {
|
|
||||||
for r in 0..4 {
|
|
||||||
let a = self.0.col(c)[r];
|
|
||||||
let b = other.0.col(c)[r];
|
|
||||||
eq &= relative_eq!(a, b, epsilon = epsilon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eq
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the transpose of the matrix
|
|
||||||
#[inline]
|
|
||||||
pub fn transposed(&self) -> Matrix4x4 {
|
|
||||||
Matrix4x4(self.0.transpose())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inverse of the Matrix
|
|
||||||
#[inline]
|
|
||||||
pub fn inverse(&self) -> Matrix4x4 {
|
|
||||||
Matrix4x4(self.0.inverse())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Matrix4x4 {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiply two matrices together
|
|
||||||
impl Mul for Matrix4x4 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Self) -> Self {
|
|
||||||
Self(other.0.mul_mat4(&self.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiply a matrix by a f32
|
|
||||||
impl Mul<f32> for Matrix4x4 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: f32) -> Self {
|
|
||||||
Self(self.0 * other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add two matrices together
|
|
||||||
impl Add for Matrix4x4 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add(self, other: Self) -> Self {
|
|
||||||
Self(self.0 + other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn equality_test() {
|
|
||||||
let a = Matrix4x4::new();
|
|
||||||
let b = Matrix4x4::new();
|
|
||||||
let c = Matrix4x4::new_from_values(
|
|
||||||
1.1, 0.0, 0.0, 0.0, 0.0, 1.1, 0.0, 0.0, 0.0, 0.0, 1.1, 0.0, 0.0, 0.0, 0.0, 1.1,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(a, b);
|
|
||||||
assert!(a != c);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn approximate_equality_test() {
|
|
||||||
let a = Matrix4x4::new();
|
|
||||||
let b = Matrix4x4::new_from_values(
|
|
||||||
1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0,
|
|
||||||
0.0, 1.000001,
|
|
||||||
);
|
|
||||||
let c = Matrix4x4::new_from_values(
|
|
||||||
1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0,
|
|
||||||
0.0, 1.000003,
|
|
||||||
);
|
|
||||||
let d = Matrix4x4::new_from_values(
|
|
||||||
-1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0,
|
|
||||||
0.0, -1.000001,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(a.aprx_eq(b, 0.000001));
|
|
||||||
assert!(!a.aprx_eq(c, 0.000001));
|
|
||||||
assert!(!a.aprx_eq(d, 0.000001));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn multiply_test() {
|
|
||||||
let a = Matrix4x4::new_from_values(
|
|
||||||
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
|
|
||||||
);
|
|
||||||
let b = Matrix4x4::new_from_values(
|
|
||||||
1.0, 5.0, 9.0, 13.0, 2.0, 6.0, 10.0, 14.0, 3.0, 7.0, 11.0, 15.0, 4.0, 8.0, 12.0, 16.0,
|
|
||||||
);
|
|
||||||
let c = Matrix4x4::new_from_values(
|
|
||||||
266.0, 141.0, 331.0, 188.5, 292.0, 158.0, 366.0, 213.0, 318.0, 175.0, 401.0, 237.5,
|
|
||||||
344.0, 192.0, 436.0, 262.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(a * b, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn inverse_test() {
|
|
||||||
let a = Matrix4x4::new_from_values(
|
|
||||||
1.0, 0.33, 0.0, -2.0, 0.0, 1.0, 0.0, 0.0, 2.1, 0.7, 1.3, 0.0, 0.0, 0.0, 0.0, -1.0,
|
|
||||||
);
|
|
||||||
let b = a.inverse();
|
|
||||||
let c = Matrix4x4::new();
|
|
||||||
|
|
||||||
assert!((dbg!(a * b)).aprx_eq(dbg!(c), 0.0000001));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn transpose_test() {
|
|
||||||
let a = Matrix4x4::new_from_values(
|
|
||||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
|
||||||
);
|
|
||||||
let b = Matrix4x4::new_from_values(
|
|
||||||
1.0, 5.0, 9.0, 13.0, 2.0, 6.0, 10.0, 14.0, 3.0, 7.0, 11.0, 15.0, 4.0, 8.0, 12.0, 16.0,
|
|
||||||
);
|
|
||||||
let c = a.transposed();
|
|
||||||
|
|
||||||
assert_eq!(b, c);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use glam::Vec3A;
|
use glam::Vec3A;
|
||||||
|
|
||||||
use super::{CrossProduct, DotProduct, Matrix4x4, Vector};
|
use super::{CrossProduct, DotProduct, Transform, Vector};
|
||||||
|
|
||||||
/// A surface normal in 3d homogeneous space.
|
/// A surface normal in 3d homogeneous space.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -126,14 +126,13 @@ impl Mul<f32> for Normal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<Matrix4x4> for Normal {
|
impl Mul<Transform> for Normal {
|
||||||
type Output = Normal;
|
type Output = Normal;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mul(self, other: Matrix4x4) -> Normal {
|
fn mul(self, other: Transform) -> Normal {
|
||||||
let mat = other.0.inverse().transpose();
|
|
||||||
Normal {
|
Normal {
|
||||||
co: mat.transform_vector3a(self.co),
|
co: other.0.matrix3.inverse().transpose().mul_vec3a(self.co),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,9 +175,9 @@ impl CrossProduct for Normal {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::{CrossProduct, DotProduct, Matrix4x4};
|
use super::super::{CrossProduct, DotProduct, Transform};
|
||||||
use super::*;
|
use super::*;
|
||||||
use approx::UlpsEq;
|
use approx::assert_ulps_eq;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
|
@ -210,12 +209,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn mul_matrix_1() {
|
fn mul_matrix_1() {
|
||||||
let n = Normal::new(1.0, 2.5, 4.0);
|
let n = Normal::new(1.0, 2.5, 4.0);
|
||||||
let m = Matrix4x4::new_from_values(
|
let m = Transform::new_from_values(
|
||||||
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
|
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0,
|
||||||
);
|
);
|
||||||
let nm = n * m;
|
let nm = n * m;
|
||||||
let nm2 = Normal::new(-19.258825, 5.717648, -1.770588);
|
let nm2 = Normal::new(-4.0625, 1.78125, -0.03125);
|
||||||
assert!(nm.co.ulps_eq(&nm2.co, 0.0, 4));
|
for i in 0..3 {
|
||||||
|
assert_ulps_eq!(nm.co[i], nm2.co[i], max_ulps = 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use glam::Vec3A;
|
use glam::Vec3A;
|
||||||
|
|
||||||
use super::{Matrix4x4, Vector};
|
use super::{Transform, Vector};
|
||||||
|
|
||||||
/// A position in 3d homogeneous space.
|
/// A position in 3d homogeneous space.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -23,15 +23,6 @@ impl Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// Returns the point in standardized coordinates, where the
|
|
||||||
// /// fourth homogeneous component has been normalized to 1.0.
|
|
||||||
// #[inline(always)]
|
|
||||||
// pub fn norm(&self) -> Point {
|
|
||||||
// Point {
|
|
||||||
// co: self.co / self.co[3],
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn min(&self, other: Point) -> Point {
|
pub fn min(&self, other: Point) -> Point {
|
||||||
let n1 = self;
|
let n1 = self;
|
||||||
|
@ -138,11 +129,11 @@ impl Sub<Vector> for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<Matrix4x4> for Point {
|
impl Mul<Transform> for Point {
|
||||||
type Output = Point;
|
type Output = Point;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mul(self, other: Matrix4x4) -> Point {
|
fn mul(self, other: Transform) -> Point {
|
||||||
Point {
|
Point {
|
||||||
co: other.0.transform_point3a(self.co),
|
co: other.0.transform_point3a(self.co),
|
||||||
}
|
}
|
||||||
|
@ -151,18 +142,9 @@ impl Mul<Matrix4x4> for Point {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::{Matrix4x4, Vector};
|
use super::super::{Transform, Vector};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn norm() {
|
|
||||||
let mut p1 = Point::new(1.0, 2.0, 3.0);
|
|
||||||
let p2 = Point::new(2.0, 4.0, 6.0);
|
|
||||||
p1.co.set_w(0.5);
|
|
||||||
|
|
||||||
assert_eq!(p2, p1.norm());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let p1 = Point::new(1.0, 2.0, 3.0);
|
let p1 = Point::new(1.0, 2.0, 3.0);
|
||||||
|
@ -184,8 +166,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn mul_matrix_1() {
|
fn mul_matrix_1() {
|
||||||
let p = Point::new(1.0, 2.5, 4.0);
|
let p = Point::new(1.0, 2.5, 4.0);
|
||||||
let m = Matrix4x4::new_from_values(
|
let m = Transform::new_from_values(
|
||||||
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 0.0, 0.0, 0.0, 1.0,
|
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0,
|
||||||
);
|
);
|
||||||
let pm = Point::new(15.5, 54.0, 70.0);
|
let pm = Point::new(15.5, 54.0, 70.0);
|
||||||
assert_eq!(p * m, pm);
|
assert_eq!(p * m, pm);
|
||||||
|
@ -194,11 +176,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn mul_matrix_2() {
|
fn mul_matrix_2() {
|
||||||
let p = Point::new(1.0, 2.5, 4.0);
|
let p = Point::new(1.0, 2.5, 4.0);
|
||||||
let m = Matrix4x4::new_from_values(
|
let m = Transform::new_from_values(
|
||||||
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 2.0, 3.0, 1.0, 5.0,
|
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0,
|
||||||
);
|
);
|
||||||
let mut pm = Point::new(15.5, 54.0, 70.0);
|
let pm = Point::new(15.5, 54.0, 70.0);
|
||||||
pm.co.set_w(18.5);
|
|
||||||
assert_eq!(p * m, pm);
|
assert_eq!(p * m, pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,12 +187,11 @@ mod tests {
|
||||||
fn mul_matrix_3() {
|
fn mul_matrix_3() {
|
||||||
// Make sure matrix multiplication composes the way one would expect
|
// Make sure matrix multiplication composes the way one would expect
|
||||||
let p = Point::new(1.0, 2.5, 4.0);
|
let p = Point::new(1.0, 2.5, 4.0);
|
||||||
let m1 = Matrix4x4::new_from_values(
|
let m1 = Transform::new_from_values(
|
||||||
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
|
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0,
|
||||||
);
|
|
||||||
let m2 = Matrix4x4::new_from_values(
|
|
||||||
4.0, 1.0, 2.0, 3.5, 3.0, 6.0, 5.0, 2.0, 2.0, 2.0, 4.0, 12.0, 5.0, 7.0, 8.0, 11.0,
|
|
||||||
);
|
);
|
||||||
|
let m2 =
|
||||||
|
Transform::new_from_values(4.0, 1.0, 2.0, 3.5, 3.0, 6.0, 5.0, 2.0, 2.0, 2.0, 4.0, 12.0);
|
||||||
println!("{:?}", m1 * m2);
|
println!("{:?}", m1 * m2);
|
||||||
|
|
||||||
let pmm1 = p * (m1 * m2);
|
let pmm1 = p * (m1 * m2);
|
||||||
|
|
178
sub_crates/math3d/src/transform.rs
Normal file
178
sub_crates/math3d/src/transform.rs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::ops::{Add, Mul};
|
||||||
|
|
||||||
|
use approx::relative_eq;
|
||||||
|
use glam::{Affine3A, Mat3, Mat4, Vec3};
|
||||||
|
|
||||||
|
use super::Point;
|
||||||
|
|
||||||
|
/// A 4x3 affine transform matrix, used for transforms.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct Transform(pub Affine3A);
|
||||||
|
|
||||||
|
impl Transform {
|
||||||
|
/// Creates a new identity matrix
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Transform {
|
||||||
|
Transform(Affine3A::IDENTITY)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new matrix with the specified values:
|
||||||
|
/// a b c d
|
||||||
|
/// e f g h
|
||||||
|
/// i j k l
|
||||||
|
/// m n o p
|
||||||
|
#[inline]
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn new_from_values(
|
||||||
|
a: f32,
|
||||||
|
b: f32,
|
||||||
|
c: f32,
|
||||||
|
d: f32,
|
||||||
|
e: f32,
|
||||||
|
f: f32,
|
||||||
|
g: f32,
|
||||||
|
h: f32,
|
||||||
|
i: f32,
|
||||||
|
j: f32,
|
||||||
|
k: f32,
|
||||||
|
l: f32,
|
||||||
|
) -> Transform {
|
||||||
|
Transform(Affine3A::from_mat3_translation(
|
||||||
|
Mat3::from_cols(Vec3::new(a, e, i), Vec3::new(b, f, j), Vec3::new(c, g, k)),
|
||||||
|
Vec3::new(d, h, l),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_location(loc: Point) -> Transform {
|
||||||
|
Transform(Affine3A::from_translation(loc.co.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the matrices are approximately equal to each other.
|
||||||
|
/// Each corresponding element in the matrices cannot have a relative
|
||||||
|
/// error exceeding epsilon.
|
||||||
|
#[inline]
|
||||||
|
pub fn aprx_eq(&self, other: Transform, epsilon: f32) -> bool {
|
||||||
|
let mut eq = true;
|
||||||
|
for c in 0..3 {
|
||||||
|
for r in 0..3 {
|
||||||
|
let a = self.0.matrix3.col(c)[r];
|
||||||
|
let b = other.0.matrix3.col(c)[r];
|
||||||
|
eq &= relative_eq!(a, b, epsilon = epsilon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 0..3 {
|
||||||
|
let a = self.0.translation[i];
|
||||||
|
let b = other.0.translation[i];
|
||||||
|
eq &= relative_eq!(a, b, epsilon = epsilon);
|
||||||
|
}
|
||||||
|
eq
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the inverse of the Matrix
|
||||||
|
#[inline]
|
||||||
|
pub fn inverse(&self) -> Transform {
|
||||||
|
Transform(self.0.inverse())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Transform {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply two matrices together
|
||||||
|
impl Mul for Transform {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mul(self, other: Self) -> Self {
|
||||||
|
Self(other.0 * self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply a matrix by a f32
|
||||||
|
impl Mul<f32> for Transform {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mul(self, other: f32) -> Self {
|
||||||
|
Self(Affine3A::from_mat4(Mat4::from(self.0) * other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add two matrices together
|
||||||
|
impl Add for Transform {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self(Affine3A::from_mat4(
|
||||||
|
Mat4::from(self.0) + Mat4::from(other.0),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn equality_test() {
|
||||||
|
let a = Transform::new();
|
||||||
|
let b = Transform::new();
|
||||||
|
let c =
|
||||||
|
Transform::new_from_values(1.1, 0.0, 0.0, 0.0, 0.0, 1.1, 0.0, 0.0, 0.0, 0.0, 1.1, 0.0);
|
||||||
|
|
||||||
|
assert_eq!(a, b);
|
||||||
|
assert!(a != c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn approximate_equality_test() {
|
||||||
|
let a = Transform::new();
|
||||||
|
let b = Transform::new_from_values(
|
||||||
|
1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0,
|
||||||
|
);
|
||||||
|
let c = Transform::new_from_values(
|
||||||
|
1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0,
|
||||||
|
);
|
||||||
|
let d = Transform::new_from_values(
|
||||||
|
-1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(a.aprx_eq(b, 0.000001));
|
||||||
|
assert!(!a.aprx_eq(c, 0.000001));
|
||||||
|
assert!(!a.aprx_eq(d, 0.000001));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply_test() {
|
||||||
|
let a = Transform::new_from_values(
|
||||||
|
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0,
|
||||||
|
);
|
||||||
|
let b = Transform::new_from_values(
|
||||||
|
1.0, 5.0, 9.0, 13.0, 2.0, 6.0, 10.0, 14.0, 3.0, 7.0, 11.0, 15.0,
|
||||||
|
);
|
||||||
|
let c = Transform::new_from_values(
|
||||||
|
97.0, 50.0, 136.0, 162.5, 110.0, 60.0, 156.0, 185.0, 123.0, 70.0, 176.0, 207.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(a * b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inverse_test() {
|
||||||
|
let a = Transform::new_from_values(
|
||||||
|
1.0, 0.33, 0.0, -2.0, 0.0, 1.0, 0.0, 0.0, 2.1, 0.7, 1.3, 0.0,
|
||||||
|
);
|
||||||
|
let b = a.inverse();
|
||||||
|
let c = Transform::new();
|
||||||
|
|
||||||
|
assert!((dbg!(a * b)).aprx_eq(dbg!(c), 0.0000001));
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use glam::Vec3A;
|
use glam::Vec3A;
|
||||||
|
|
||||||
use super::{CrossProduct, DotProduct, Matrix4x4, Normal, Point};
|
use super::{CrossProduct, DotProduct, Normal, Point, Transform};
|
||||||
|
|
||||||
/// A direction vector in 3d homogeneous space.
|
/// A direction vector in 3d homogeneous space.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -138,11 +138,11 @@ impl Mul<f32> for Vector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<Matrix4x4> for Vector {
|
impl Mul<Transform> for Vector {
|
||||||
type Output = Vector;
|
type Output = Vector;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mul(self, other: Matrix4x4) -> Vector {
|
fn mul(self, other: Transform) -> Vector {
|
||||||
Vector {
|
Vector {
|
||||||
co: other.0.transform_vector3a(self.co),
|
co: other.0.transform_vector3a(self.co),
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ impl CrossProduct for Vector {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::{CrossProduct, DotProduct, Matrix4x4};
|
use super::super::{CrossProduct, DotProduct, Transform};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -220,8 +220,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn mul_matrix_1() {
|
fn mul_matrix_1() {
|
||||||
let v = Vector::new(1.0, 2.5, 4.0);
|
let v = Vector::new(1.0, 2.5, 4.0);
|
||||||
let m = Matrix4x4::new_from_values(
|
let m = Transform::new_from_values(
|
||||||
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
|
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0,
|
||||||
);
|
);
|
||||||
assert_eq!(v * m, Vector::new(14.0, 46.0, 58.0));
|
assert_eq!(v * m, Vector::new(14.0, 46.0, 58.0));
|
||||||
}
|
}
|
||||||
|
@ -229,8 +229,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn mul_matrix_2() {
|
fn mul_matrix_2() {
|
||||||
let v = Vector::new(1.0, 2.5, 4.0);
|
let v = Vector::new(1.0, 2.5, 4.0);
|
||||||
let m = Matrix4x4::new_from_values(
|
let m = Transform::new_from_values(
|
||||||
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 0.0, 0.0, 0.0, 1.0,
|
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0,
|
||||||
);
|
);
|
||||||
assert_eq!(v * m, Vector::new(14.0, 46.0, 58.0));
|
assert_eq!(v * m, Vector::new(14.0, 46.0, 58.0));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user