Use full 4x4 matrix for affine transformation

This commit is contained in:
hal8174 2025-08-24 21:31:13 +02:00
parent ae4dc2c21a
commit c48790f32f
Signed by: hal8174
SSH key fingerprint: SHA256:NN98ZYwnrreQLSOV/g+amY7C3yL/mS1heD7bi5t6PPw
2 changed files with 172 additions and 49 deletions

View file

@ -2,14 +2,40 @@ use std::ops::{Mul, MulAssign};
use crate::prelude::*; use crate::prelude::*;
#[derive(Debug, Copy, Clone)] #[derive(Copy, Clone)]
pub struct AffineTransform { pub struct AffineTransform {
mat: [[Float; 4]; 3], mat: [[Float; 4]; 4],
inv: [[Float; 3]; 3], inv: [[Float; 4]; 4],
}
impl std::fmt::Debug for AffineTransform {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct MatDebug([[Float; 4]; 4]);
struct RowDebug([Float; 4]);
impl std::fmt::Debug for RowDebug {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:.5?}", self.0))
}
}
impl std::fmt::Debug for MatDebug {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list()
.entries(self.0.into_iter().map(RowDebug))
.finish()
}
}
f.debug_struct("AffineTransform")
.field("mat", &MatDebug(self.mat))
.field("inv", &MatDebug(self.inv))
.finish()
}
} }
impl AffineTransform { impl AffineTransform {
pub fn new(mat: [[Float; 4]; 3]) -> Option<Self> { pub fn new(mat: [[Float; 4]; 4]) -> Option<Self> {
let inv = Self::invert(mat)?; let inv = Self::invert(mat)?;
Some(Self { mat, inv }) Some(Self { mat, inv })
} }
@ -20,8 +46,14 @@ impl AffineTransform {
[1.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
],
inv: [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
], ],
inv: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
} }
} }
@ -31,8 +63,14 @@ impl AffineTransform {
[1.0, 0.0, 0.0, pos.x()], [1.0, 0.0, 0.0, pos.x()],
[0.0, 1.0, 0.0, pos.y()], [0.0, 1.0, 0.0, pos.y()],
[0.0, 0.0, 1.0, pos.z()], [0.0, 0.0, 1.0, pos.z()],
[0.0, 0.0, 0.0, 1.0],
],
inv: [
[1.0, 0.0, 0.0, -pos.x()],
[0.0, 1.0, 0.0, -pos.y()],
[0.0, 0.0, 1.0, -pos.z()],
[0.0, 0.0, 0.0, 1.0],
], ],
inv: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
} }
} }
@ -42,11 +80,13 @@ impl AffineTransform {
[sx, 0.0, 0.0, 0.0], [sx, 0.0, 0.0, 0.0],
[0.0, sy, 0.0, 0.0], [0.0, sy, 0.0, 0.0],
[0.0, 0.0, sz, 0.0], [0.0, 0.0, sz, 0.0],
[0.0, 0.0, 0.0, 1.0],
], ],
inv: [ inv: [
[1.0 / sx, 0.0, 0.0], [1.0 / sx, 0.0, 0.0, 0.0],
[0.0, 1.0 / sy, 0.0], [0.0, 1.0 / sy, 0.0, 0.0],
[0.0, 0.0, 1.0 / sz], [0.0, 0.0, 1.0 / sz, 0.0],
[0.0, 0.0, 0.0, 1.0],
], ],
} }
} }
@ -71,13 +111,15 @@ impl AffineTransform {
dir.z() * dir.z() * (1.0 - angle.cos()) + angle.cos(), dir.z() * dir.z() * (1.0 - angle.cos()) + angle.cos(),
0.0, 0.0,
], ],
[0.0, 0.0, 0.0, 1.0],
]; ];
Self { Self {
mat, mat,
inv: [ inv: [
[mat[0][0], mat[1][0], mat[2][0]], [mat[0][0], mat[1][0], mat[2][0], 0.0],
[mat[0][1], mat[1][1], mat[2][1]], [mat[0][1], mat[1][1], mat[2][1], 0.0],
[mat[0][2], mat[1][2], mat[2][2]], [mat[0][2], mat[1][2], mat[2][2], 0.0],
[0.0, 0.0, 0.0, 1.0],
], ],
} }
} }
@ -91,33 +133,46 @@ impl AffineTransform {
[right.x(), new_up.x(), dir.x(), pos.x()], [right.x(), new_up.x(), dir.x(), pos.x()],
[right.y(), new_up.y(), dir.y(), pos.y()], [right.y(), new_up.y(), dir.y(), pos.y()],
[right.z(), new_up.z(), dir.z(), pos.z()], [right.z(), new_up.z(), dir.z(), pos.z()],
[0.0, 0.0, 0.0, 1.0],
]; ];
Self::new(mat) Self::new(mat)
} }
fn invert(mat: [[Float; 4]; 3]) -> Option<[[Float; 3]; 3]> { fn invert(mat: [[Float; 4]; 4]) -> Option<[[Float; 4]; 4]> {
// using cramers rule // using cramers rule
let adj = |y, x| {
let cofactor_indices = |p| match p { let cofactor_indices = |p| match p {
0 => (1, 2), 0 => (1, 2, 3),
1 => (0, 2), 1 => (0, 2, 3),
2 => (0, 1), 2 => (0, 1, 3),
3 => (0, 1, 2),
_ => unreachable!(), _ => unreachable!(),
}; };
let (y1, y2) = cofactor_indices(y); let adj = |y, x| {
let (x1, x2) = cofactor_indices(x); let (y0, y1, y2) = cofactor_indices(y);
let (x0, x1, x2) = cofactor_indices(x);
(if (x + y) % 2 == 0 { 1.0 } else { -1.0 }) (if (x + y) % 2 == 0 { 1.0 } else { -1.0 })
* (mat[y1][x1] * mat[y2][x2] - mat[y1][x2] * mat[y2][x1]) * (mat[y0][x0] * mat[y1][x1] * mat[y2][x2]
+ mat[y0][x1] * mat[y1][x2] * mat[y2][x0]
+ mat[y0][x2] * mat[y1][x0] * mat[y2][x1]
- mat[y0][x2] * mat[y1][x1] * mat[y2][x0]
- mat[y0][x1] * mat[y1][x0] * mat[y2][x2]
- mat[y0][x0] * mat[y1][x2] * mat[y2][x1])
}; };
let det = mat[0][0] * mat[1][1] * mat[2][2] let mut det = 0.0;
+ mat[0][1] * mat[1][2] * mat[2][0] for i in 0..=3 {
+ mat[0][2] * mat[1][0] * mat[2][1] let (x0, x1, x2) = cofactor_indices(i);
- mat[0][2] * mat[1][1] * mat[2][0] det = if i % 2 == 0 { -1.0 } else { 1.0 }
- mat[0][1] * mat[1][0] * mat[2][2] * mat[3][i]
- mat[0][0] * mat[1][2] * mat[2][1]; * (mat[0][x0] * mat[1][x1] * mat[2][x2]
+ mat[0][x1] * mat[1][x2] * mat[2][x0]
+ mat[0][x2] * mat[1][x0] * mat[2][x1]
- mat[0][x2] * mat[1][x1] * mat[2][x0]
- mat[0][x1] * mat[1][x0] * mat[2][x2]
- mat[0][x0] * mat[1][x2] * mat[2][x1]);
}
if det != 0.0 { if det != 0.0 {
let det_frac = 1.0 / det; let det_frac = 1.0 / det;
@ -127,16 +182,25 @@ impl AffineTransform {
adj(0, 0) * det_frac, adj(0, 0) * det_frac,
adj(1, 0) * det_frac, adj(1, 0) * det_frac,
adj(2, 0) * det_frac, adj(2, 0) * det_frac,
adj(3, 0) * det_frac,
], ],
[ [
adj(0, 1) * det_frac, adj(0, 1) * det_frac,
adj(1, 1) * det_frac, adj(1, 1) * det_frac,
adj(2, 1) * det_frac, adj(2, 1) * det_frac,
adj(3, 1) * det_frac,
], ],
[ [
adj(0, 2) * det_frac, adj(0, 2) * det_frac,
adj(1, 2) * det_frac, adj(1, 2) * det_frac,
adj(2, 2) * det_frac, adj(2, 2) * det_frac,
adj(3, 2) * det_frac,
],
[
adj(0, 3) * det_frac,
adj(1, 3) * det_frac,
adj(2, 3) * det_frac,
adj(3, 3) * det_frac,
], ],
]) ])
} else { } else {
@ -194,90 +258,147 @@ impl Mul<AffineTransform> for AffineTransform {
self.mat[0][0] * rhs.mat[0][0] self.mat[0][0] * rhs.mat[0][0]
+ self.mat[0][1] * rhs.mat[1][0] + self.mat[0][1] * rhs.mat[1][0]
+ self.mat[0][2] * rhs.mat[2][0] + self.mat[0][2] * rhs.mat[2][0]
+ self.mat[0][3], + self.mat[0][3] * rhs.mat[3][0],
self.mat[0][0] * rhs.mat[0][1] self.mat[0][0] * rhs.mat[0][1]
+ self.mat[0][1] * rhs.mat[1][1] + self.mat[0][1] * rhs.mat[1][1]
+ self.mat[0][2] * rhs.mat[2][1] + self.mat[0][2] * rhs.mat[2][1]
+ self.mat[0][3], + self.mat[0][3] * rhs.mat[3][1],
self.mat[0][0] * rhs.mat[0][2] self.mat[0][0] * rhs.mat[0][2]
+ self.mat[0][1] * rhs.mat[1][2] + self.mat[0][1] * rhs.mat[1][2]
+ self.mat[0][2] * rhs.mat[2][2] + self.mat[0][2] * rhs.mat[2][2]
+ self.mat[0][3], + self.mat[0][3] * rhs.mat[3][2],
self.mat[0][0] * rhs.mat[0][3] self.mat[0][0] * rhs.mat[0][3]
+ self.mat[0][1] * rhs.mat[1][3] + self.mat[0][1] * rhs.mat[1][3]
+ self.mat[0][2] * rhs.mat[2][3] + self.mat[0][2] * rhs.mat[2][3]
+ self.mat[0][3], + self.mat[0][3] * rhs.mat[3][3],
], ],
[ [
self.mat[1][0] * rhs.mat[0][0] self.mat[1][0] * rhs.mat[0][0]
+ self.mat[1][1] * rhs.mat[1][0] + self.mat[1][1] * rhs.mat[1][0]
+ self.mat[1][2] * rhs.mat[2][0] + self.mat[1][2] * rhs.mat[2][0]
+ self.mat[1][3], + self.mat[1][3] * rhs.mat[3][0],
self.mat[1][0] * rhs.mat[0][1] self.mat[1][0] * rhs.mat[0][1]
+ self.mat[1][1] * rhs.mat[1][1] + self.mat[1][1] * rhs.mat[1][1]
+ self.mat[1][2] * rhs.mat[2][1] + self.mat[1][2] * rhs.mat[2][1]
+ self.mat[1][3], + self.mat[1][3] * rhs.mat[3][1],
self.mat[1][0] * rhs.mat[0][2] self.mat[1][0] * rhs.mat[0][2]
+ self.mat[1][1] * rhs.mat[1][2] + self.mat[1][1] * rhs.mat[1][2]
+ self.mat[1][2] * rhs.mat[2][2] + self.mat[1][2] * rhs.mat[2][2]
+ self.mat[1][3], + self.mat[1][3] * rhs.mat[3][2],
self.mat[1][0] * rhs.mat[0][3] self.mat[1][0] * rhs.mat[0][3]
+ self.mat[1][1] * rhs.mat[1][3] + self.mat[1][1] * rhs.mat[1][3]
+ self.mat[1][2] * rhs.mat[2][3] + self.mat[1][2] * rhs.mat[2][3]
+ self.mat[1][3], + self.mat[1][3] * rhs.mat[3][3],
], ],
[ [
self.mat[2][0] * rhs.mat[0][0] self.mat[2][0] * rhs.mat[0][0]
+ self.mat[2][1] * rhs.mat[1][0] + self.mat[2][1] * rhs.mat[1][0]
+ self.mat[2][2] * rhs.mat[2][0] + self.mat[2][2] * rhs.mat[2][0]
+ self.mat[2][3], + self.mat[2][3] * rhs.mat[3][0],
self.mat[2][0] * rhs.mat[0][1] self.mat[2][0] * rhs.mat[0][1]
+ self.mat[2][1] * rhs.mat[1][1] + self.mat[2][1] * rhs.mat[1][1]
+ self.mat[2][2] * rhs.mat[2][1] + self.mat[2][2] * rhs.mat[2][1]
+ self.mat[2][3], + self.mat[2][3] * rhs.mat[3][1],
self.mat[2][0] * rhs.mat[0][2] self.mat[2][0] * rhs.mat[0][2]
+ self.mat[2][1] * rhs.mat[1][2] + self.mat[2][1] * rhs.mat[1][2]
+ self.mat[2][2] * rhs.mat[2][2] + self.mat[2][2] * rhs.mat[2][2]
+ self.mat[2][3], + self.mat[2][3] * rhs.mat[3][3],
self.mat[2][0] * rhs.mat[0][3] self.mat[2][0] * rhs.mat[0][3]
+ self.mat[2][1] * rhs.mat[1][3] + self.mat[2][1] * rhs.mat[1][3]
+ self.mat[2][2] * rhs.mat[2][3] + self.mat[2][2] * rhs.mat[2][3]
+ self.mat[2][3], + self.mat[2][3] * rhs.mat[3][3],
],
[
self.mat[3][0] * rhs.mat[0][0]
+ self.mat[3][1] * rhs.mat[1][0]
+ self.mat[3][2] * rhs.mat[2][0]
+ self.mat[3][3] * rhs.mat[3][0],
self.mat[3][0] * rhs.mat[0][1]
+ self.mat[3][1] * rhs.mat[1][1]
+ self.mat[3][2] * rhs.mat[2][1]
+ self.mat[3][3] * rhs.mat[3][1],
self.mat[3][0] * rhs.mat[0][2]
+ self.mat[3][1] * rhs.mat[1][2]
+ self.mat[3][2] * rhs.mat[2][2]
+ self.mat[3][3] * rhs.mat[3][2],
self.mat[3][0] * rhs.mat[0][3]
+ self.mat[3][1] * rhs.mat[1][3]
+ self.mat[3][2] * rhs.mat[2][3]
+ self.mat[3][3] * rhs.mat[3][3],
], ],
], ],
inv: [ inv: [
[ [
rhs.inv[0][0] * self.inv[0][0] rhs.inv[0][0] * self.inv[0][0]
+ rhs.inv[0][1] * self.inv[1][0] + rhs.inv[0][1] * self.inv[1][0]
+ rhs.inv[0][2] * self.inv[2][0], + rhs.inv[0][2] * self.inv[2][0]
+ rhs.inv[0][3] * self.inv[3][0],
rhs.inv[0][0] * self.inv[0][1] rhs.inv[0][0] * self.inv[0][1]
+ rhs.inv[0][1] * self.inv[1][1] + rhs.inv[0][1] * self.inv[1][1]
+ rhs.inv[0][2] * self.inv[2][1], + rhs.inv[0][2] * self.inv[2][1]
+ rhs.inv[0][3] * self.inv[3][1],
rhs.inv[0][0] * self.inv[0][2] rhs.inv[0][0] * self.inv[0][2]
+ rhs.inv[0][1] * self.inv[1][2] + rhs.inv[0][1] * self.inv[1][2]
+ rhs.inv[0][2] * self.inv[2][2], + rhs.inv[0][2] * self.inv[2][2]
+ rhs.inv[0][3] * self.inv[3][2],
rhs.inv[0][0] * self.inv[0][3]
+ rhs.inv[0][1] * self.inv[1][3]
+ rhs.inv[0][2] * self.inv[2][3]
+ rhs.inv[0][3] * self.inv[3][3],
], ],
[ [
rhs.inv[1][0] * self.inv[0][0] rhs.inv[1][0] * self.inv[0][0]
+ rhs.inv[1][1] * self.inv[1][0] + rhs.inv[1][1] * self.inv[1][0]
+ rhs.inv[1][2] * self.inv[2][0], + rhs.inv[1][2] * self.inv[2][0]
+ rhs.inv[1][3] * self.inv[3][0],
rhs.inv[1][0] * self.inv[0][1] rhs.inv[1][0] * self.inv[0][1]
+ rhs.inv[1][1] * self.inv[1][1] + rhs.inv[1][1] * self.inv[1][1]
+ rhs.inv[1][2] * self.inv[2][1], + rhs.inv[1][2] * self.inv[2][1]
+ rhs.inv[1][3] * self.inv[3][1],
rhs.inv[1][0] * self.inv[0][2] rhs.inv[1][0] * self.inv[0][2]
+ rhs.inv[1][1] * self.inv[1][2] + rhs.inv[1][1] * self.inv[1][2]
+ rhs.inv[1][2] * self.inv[2][2], + rhs.inv[1][2] * self.inv[2][2]
+ rhs.inv[1][3] * self.inv[3][2],
rhs.inv[1][0] * self.inv[0][3]
+ rhs.inv[1][1] * self.inv[1][3]
+ rhs.inv[1][2] * self.inv[2][3]
+ rhs.inv[1][3] * self.inv[3][3],
], ],
[ [
rhs.inv[2][0] * self.inv[0][0] rhs.inv[2][0] * self.inv[0][0]
+ rhs.inv[2][1] * self.inv[1][0] + rhs.inv[2][1] * self.inv[1][0]
+ rhs.inv[2][2] * self.inv[2][0], + rhs.inv[2][2] * self.inv[2][0]
+ rhs.inv[2][3] * self.inv[3][0],
rhs.inv[2][0] * self.inv[0][1] rhs.inv[2][0] * self.inv[0][1]
+ rhs.inv[2][1] * self.inv[1][1] + rhs.inv[2][1] * self.inv[1][1]
+ rhs.inv[2][2] * self.inv[2][1], + rhs.inv[2][2] * self.inv[2][1]
+ rhs.inv[2][3] * self.inv[3][1],
rhs.inv[2][0] * self.inv[0][2] rhs.inv[2][0] * self.inv[0][2]
+ rhs.inv[2][1] * self.inv[1][2] + rhs.inv[2][1] * self.inv[1][2]
+ rhs.inv[2][2] * self.inv[2][2], + rhs.inv[2][2] * self.inv[2][2]
+ rhs.inv[2][3] * self.inv[3][3],
rhs.inv[2][0] * self.inv[0][3]
+ rhs.inv[2][1] * self.inv[1][3]
+ rhs.inv[2][2] * self.inv[2][3]
+ rhs.inv[2][3] * self.inv[3][3],
],
[
rhs.inv[3][0] * self.inv[0][0]
+ rhs.inv[3][1] * self.inv[1][0]
+ rhs.inv[3][2] * self.inv[2][0]
+ rhs.inv[3][3] * self.inv[3][0],
rhs.inv[3][0] * self.inv[0][1]
+ rhs.inv[3][1] * self.inv[1][1]
+ rhs.inv[3][2] * self.inv[2][1]
+ rhs.inv[3][3] * self.inv[3][1],
rhs.inv[3][0] * self.inv[0][2]
+ rhs.inv[3][1] * self.inv[1][2]
+ rhs.inv[3][2] * self.inv[2][2]
+ rhs.inv[3][3] * self.inv[3][2],
rhs.inv[3][0] * self.inv[0][3]
+ rhs.inv[3][1] * self.inv[1][3]
+ rhs.inv[3][2] * self.inv[2][3]
+ rhs.inv[3][3] * self.inv[3][3],
], ],
], ],
} }

View file

@ -487,6 +487,7 @@ fn parse_transform(input: &mut Tokenizer) -> Result<AffineTransform> {
[v[0], v[4], v[8], v[12]], [v[0], v[4], v[8], v[12]],
[v[1], v[5], v[9], v[13]], [v[1], v[5], v[9], v[13]],
[v[2], v[6], v[10], v[14]], [v[2], v[6], v[10], v[14]],
[v[3], v[7], v[11], v[15]],
]) ])
.ok_or(miette!("Unable to invert transformation")) .ok_or(miette!("Unable to invert transformation"))
} }
@ -757,6 +758,7 @@ fn inner_parse_pbrt(path: impl AsRef<Path> + std::fmt::Debug) -> Result<Pbrt> {
*context.ctm.last_mut().unwrap() = affine_transform *context.ctm.last_mut().unwrap() = affine_transform
} }
Statement::Shape(shape_type, shape_alpha) => { Statement::Shape(shape_type, shape_alpha) => {
dbg!(&context);
pbrt.scene.shapes.push(Shape { pbrt.scene.shapes.push(Shape {
ctm: context.get_ctm(), ctm: context.get_ctm(),
material: Arc::clone( material: Arc::clone(