From 8fcf815dbfe7184972b1524fb90d91e901371aea Mon Sep 17 00:00:00 2001 From: hal8174 Date: Sat, 2 Aug 2025 22:44:06 +0200 Subject: [PATCH] Parsing more stuff. --- ray-tracing-core/src/affine_transform.rs | 60 +++++- ray-tracing-pbrt-scene/examples/pbrt-test.rs | 4 +- ray-tracing-pbrt-scene/src/lib.rs | 206 ++++++++++++++++++- 3 files changed, 253 insertions(+), 17 deletions(-) diff --git a/ray-tracing-core/src/affine_transform.rs b/ray-tracing-core/src/affine_transform.rs index 859f10f..00f754c 100644 --- a/ray-tracing-core/src/affine_transform.rs +++ b/ray-tracing-core/src/affine_transform.rs @@ -9,6 +9,11 @@ pub struct AffineTransform { } impl AffineTransform { + pub fn new(mat: [[Float; 4]; 3]) -> Option { + let inv = Self::invert(mat)?; + Some(Self { mat, inv }) + } + pub fn identity() -> Self { Self { mat: [ @@ -77,7 +82,7 @@ impl AffineTransform { } } - pub fn look_at(pos: Pos3, look: Pos3, up: Dir3) -> Self { + pub fn look_at(pos: Pos3, look: Pos3, up: Dir3) -> Option { let dir = (look - pos).normalize(); let right = Dir3::cross(up.normalize(), dir).normalize(); let new_up = Dir3::cross(dir, right); @@ -88,14 +93,55 @@ impl AffineTransform { [right.z(), new_up.z(), dir.z(), pos.z()], ]; - let inv = Self::invert(mat); - - Self { mat, inv } + Self::new(mat) } - fn invert(mat: [[Float; 4]; 3]) -> [[Float; 3]; 3] { - eprintln!("Matrix inversion not implemented"); - [[0.0; 3]; 3] + fn invert(mat: [[Float; 4]; 3]) -> Option<[[Float; 3]; 3]> { + // using cramers rule + let adj = |y, x| { + let cofactor_indices = |p| match p { + 0 => (1, 2), + 1 => (0, 2), + 2 => (0, 1), + _ => unreachable!(), + }; + let (y1, y2) = cofactor_indices(y); + let (x1, x2) = cofactor_indices(x); + + (if (x + y) % 2 == 0 { 1.0 } else { -1.0 }) + * (mat[y1][x1] * mat[y2][x2] - mat[y1][x2] * mat[y2][x1]) + }; + + let det = mat[0][0] * mat[1][1] * mat[2][2] + + mat[0][1] * mat[1][2] * mat[2][0] + + mat[0][2] * mat[1][0] * mat[2][1] + - mat[0][2] * mat[1][1] * mat[2][0] + - mat[0][1] * mat[1][0] * mat[2][2] + - mat[0][0] * mat[1][2] * mat[2][1]; + + if det != 0.0 { + let det_frac = 1.0 / det; + + Some([ + [ + adj(0, 0) * det_frac, + adj(1, 0) * det_frac, + adj(2, 0) * det_frac, + ], + [ + adj(0, 1) * det_frac, + adj(1, 1) * det_frac, + adj(2, 1) * det_frac, + ], + [ + adj(0, 2) * det_frac, + adj(1, 2) * det_frac, + adj(2, 2) * det_frac, + ], + ]) + } else { + None + } } pub fn transform_pos(&self, pos: Pos3) -> Pos3 { diff --git a/ray-tracing-pbrt-scene/examples/pbrt-test.rs b/ray-tracing-pbrt-scene/examples/pbrt-test.rs index 3ee80b2..a49cfc6 100644 --- a/ray-tracing-pbrt-scene/examples/pbrt-test.rs +++ b/ray-tracing-pbrt-scene/examples/pbrt-test.rs @@ -10,7 +10,9 @@ struct Args { fn main() -> Result<(), miette::Error> { let args = Args::parse(); - dbg!(parse_pbrt_v4(args.filename)?); + let t = parse_pbrt_v4(args.filename)?; + + // dbg!(t); Ok(()) } diff --git a/ray-tracing-pbrt-scene/src/lib.rs b/ray-tracing-pbrt-scene/src/lib.rs index a17e59e..43482c1 100644 --- a/ray-tracing-pbrt-scene/src/lib.rs +++ b/ray-tracing-pbrt-scene/src/lib.rs @@ -119,6 +119,7 @@ enum Statement { ConcatTransform(AffineTransform), Shape(ShapeType), Unknown(String, Vec), + Transform(AffineTransform), } fn parse_look_at(iter: &mut impl Iterator>) -> Result { @@ -165,9 +166,10 @@ fn parse_look_at(iter: &mut impl Iterator>) -> Result(iter: &mut Peekable>) -> Result @@ -383,7 +385,7 @@ fn parse_shape>>( "\"normal N\"" => { parse_list_3(iter, &mut n, Dir3::new)?; } - "\"vector N\"" => { + "\"vector S\"" => { parse_list_3(iter, &mut s, Dir3::new)?; } "\"point2 uv\"" => { @@ -427,8 +429,9 @@ fn parse_shape>>( })) } "\"bilinearmesh\"" => { + let mut indices = Vec::new(); let mut p = Vec::new(); - + let mut n = Vec::new(); let mut uv = Vec::new(); while let Some(q) = iter @@ -436,21 +439,50 @@ fn parse_shape>>( .transpose()? { match q.as_str() { + "\"integer indices\"" => { + parse_list(iter, &mut indices)?; + } "\"point3 P\"" => { parse_list_3(iter, &mut p, Pos3::new)?; } "\"point2 uv\"" => { parse_list_2(iter, &mut uv, |u, v| [u, v])?; } + "\"vector N\"" => { + parse_list_3(iter, &mut n, Dir3::new)?; + } _ => { bail!("unknown argument {}", q) } } } + if p.len() < 4 { + bail!("At least 3 points required.") + } + if indices.is_empty() && p.len() != 4 { + bail!("Indices required for trianglemesh with more than 3 points.") + } + + if indices.len() % 4 != 0 { + bail!( + "number of indices must be divisible by 4. num indices: {}", + indices.len() + ) + } + + if !n.is_empty() && n.len() != p.len() { + bail!("Number of normals not equal to number of positions.") + } + + if !uv.is_empty() && uv.len() != p.len() { + bail!("Number of uvs not equal to number of positions.") + } Ok(Statement::Shape(ShapeType::BilinearMesh { - p: [p[0], p[1], p[2], p[3]], - uv: [uv[0], uv[1], uv[2], uv[3]], + indices, + p, + n, + uv, })) } @@ -494,6 +526,34 @@ fn parse_shape>>( p, })) } + "\"disk\"" => { + let mut height = 0.0; + let mut radius = 1.0; + let mut innerradius = 0.0; + let mut phimax = 360.0; + + while let Some(q) = iter + .next_if(|p| p.as_ref().is_ok_and(|p| p.starts_with('"'))) + .transpose()? + { + match q.as_str() { + "\"float height\"" => height = parse_parameter(iter)?, + "\"float radius\"" => radius = parse_parameter(iter)?, + "\"float innerradius\"" => innerradius = parse_parameter(iter)?, + "\"float phimax\"" => phimax = parse_parameter(iter)?, + _ => { + bail!("unknown argument {}", q) + } + } + } + + Ok(Statement::Shape(ShapeType::Disk { + height, + radius, + innerradius, + phimax, + })) + } _ => Err(miette!("Unknown shape {}", shape_type)), } } @@ -517,7 +577,16 @@ impl>> Lexer { } "LookAt" => Some(parse_look_at(&mut self.input)), "Identity" => Some(Ok(Statement::ConcatTransform(AffineTransform::identity()))), + "Translate" => Some(parse_translate(&mut self.input)), + "Scale" => Some(parse_scale(&mut self.input)), "Shape" => Some(parse_shape(&mut self.input)), + "Rotate" => Some(parse_rotate(&mut self.input)), + "Transform" => { + Some(parse_transform(&mut self.input).map(|a| Statement::Transform(a))) + } + "ConcatTransform" => { + Some(parse_transform(&mut self.input).map(|a| Statement::ConcatTransform(a))) + } _ => { if s.chars().any(|c| !c.is_ascii_alphabetic()) { Some(Err(miette!("malformed identifier"))) @@ -544,6 +613,114 @@ impl>> Lexer { } } +fn parse_transform>>( + input: &mut Peekable>, +) -> Result { + if !input + .next() + .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "[")) + { + bail!("expected list.") + } + + let mut v = [0.0; 16]; + + for i in 0..16 { + v[i] = input + .next() + .ok_or(miette!("value expected"))?? + .parse::() + .into_diagnostic()?; + } + + if !input + .next() + .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) + { + bail!("expected list end.") + } + + if v[3] != 0.0 || v[7] != 0.0 || v[11] != 0.0 || v[15] != 1.0 { + bail!("invalid transform entry") + } + + Ok(AffineTransform::new([ + [v[0], v[4], v[8], v[12]], + [v[1], v[5], v[9], v[13]], + [v[2], v[6], v[10], v[14]], + ]) + .ok_or(miette!("Unable to invert transformation"))?) +} + +fn parse_translate>>( + iter: &mut Peekable>, +) -> Result { + let pos = Pos3::new( + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + ); + + Ok(Statement::ConcatTransform(AffineTransform::translation( + pos, + ))) +} + +fn parse_scale>>( + iter: &mut Peekable>, +) -> Result { + Ok(Statement::ConcatTransform(AffineTransform::scale( + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + ))) +} + +fn parse_rotate>>( + iter: &mut Peekable>, +) -> Result { + let angle = iter + .next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?; + let dir = Dir3::new( + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + iter.next() + .ok_or(miette!("missing argument"))?? + .parse() + .into_diagnostic()?, + ); + + Ok(Statement::ConcatTransform(AffineTransform::rotation( + angle, dir, + ))) +} struct BytesToChar { iter: I, } @@ -623,14 +800,22 @@ enum ShapeType { uv: Vec<[Float; 2]>, }, BilinearMesh { - p: [Pos3; 4], - uv: [[Float; 2]; 4], + indices: Vec, + p: Vec, + n: Vec, + uv: Vec<[Float; 2]>, }, LoopSubDiv { levels: u32, indices: Vec, p: Vec, }, + Disk { + height: f64, + radius: f64, + innerradius: f64, + phimax: f64, + }, } #[derive(Debug)] @@ -698,6 +883,9 @@ fn inner_parse_pbrt( Statement::ConcatTransform(affine_transform) => { context.last_mut().ok_or(miette!(""))?.ctm *= affine_transform } + Statement::Transform(affine_transform) => { + context.last_mut().ok_or(miette!(""))?.ctm = dbg!(affine_transform) + } Statement::Shape(shape_type) => { pbrt.scene.shapes.push(Shape { ctm: context.last().unwrap().ctm,