use crate::tokenizer::Tokenizer; use error::SourceFile; use miette::{Diagnostic, IntoDiagnostic, Result, SourceSpan, bail, miette}; use ray_tracing_core::{ affine_transform::AffineTransform, math::{Dir3, Pos3}, prelude::Float, }; use std::path::{Path, PathBuf}; use thiserror::Error; mod tokenizer; mod error; #[derive(Error, Debug, Diagnostic)] #[error("oops!")] #[diagnostic(help("justus ist doof"))] struct MyBad { #[source_code] src: SourceFile, #[label("Here")] bad_bit: SourceSpan, } struct Lexer { input: Tokenizer, } impl Lexer { fn new(path: impl AsRef) -> Result { Ok(Self { input: Tokenizer::new(path)?, }) } } #[derive(Debug)] enum Statement { AttributeBegin, AttributeEnd, Include(String), ConcatTransform(AffineTransform), Shape(ShapeType), Unknown(String, Vec), Transform(AffineTransform), } fn parse_look_at(iter: &mut Tokenizer) -> Result { let eye = 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()?, ); let look_at = 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()?, ); let up = 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::look_at(eye, look_at, up) .ok_or(miette!("Unable to calculate inverse of matrix"))?, )) } fn parse_shape(iter: &mut Tokenizer) -> Result { let shape_type = iter.next().ok_or(miette!("unable to get shape type"))??; match shape_type.as_str() { "\"sphere\"" => { let mut radius = 1.0; let zmin = -radius; let zmax = radius; let phimax = 360.0; while let Some(p) = iter.next_if(|p| p.starts_with('"')).transpose()? { match p.as_str() { "\"float radius\"" => { radius = iter.parse_parameter()?; } _ => { bail!("unknown argument {}", p) } } } Ok(Statement::Shape(ShapeType::Sphere { radius, zmin, zmax, phimax, })) } "\"trianglemesh\"" => { let mut indices = Vec::new(); let mut p = Vec::new(); let mut n = Vec::new(); let mut s = Vec::new(); let mut uv = Vec::new(); while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? { match q.as_str() { "\"integer indices\"" => { iter.parse_list(&mut indices)?; } "\"point3 P\"" => { iter.parse_list_3(&mut p, Pos3::new)?; } "\"normal N\"" => { iter.parse_list_3(&mut n, Dir3::new)?; } "\"vector S\"" => { iter.parse_list_3(&mut s, Dir3::new)?; } "\"point2 uv\"" => { iter.parse_list_2(&mut uv, |u, v| [u, v])?; } _ => { bail!("unknown argument {}", q) } } } if p.len() < 3 { bail!("At least 3 points required.") } if indices.is_empty() && p.len() != 3 { bail!("Indices required for trianglemesh with more than 3 points.") } if indices.len() % 3 != 0 { bail!( "number of indices must be divisible by 3. num indices: {}", indices.len() ) } if !n.is_empty() && n.len() != p.len() { bail!("Number of normals not equal to number of positions.") } if !s.is_empty() && s.len() != p.len() { bail!("Number of tangents 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::TriangleMesh { indices, p, n, s, uv, })) } "\"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.next_if(|p| p.starts_with('"')).transpose()? { match q.as_str() { "\"integer indices\"" => { iter.parse_list(&mut indices)?; } "\"point3 P\"" => { iter.parse_list_3(&mut p, Pos3::new)?; } "\"point2 uv\"" => { iter.parse_list_2(&mut uv, |u, v| [u, v])?; } "\"vector N\"" => { iter.parse_list_3(&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 { indices, p, n, uv, })) } "\"loopsubdiv\"" => { let mut levels = 3; let mut indices = Vec::new(); let mut p = Vec::new(); while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? { match q.as_str() { "\"point3 P\"" => { iter.parse_list_3(&mut p, Pos3::new)?; } "\"integer indices\"" => { iter.parse_list(&mut indices)?; } "\"integer levels\"" => { levels = iter.parse_parameter()?; } _ => { bail!("unknown argument {}", q) } } } if indices.is_empty() { bail!("indices are a required field") } if p.is_empty() { bail!("p is a required field") } Ok(Statement::Shape(ShapeType::LoopSubDiv { levels, indices, 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.starts_with('"')).transpose()? { match q.as_str() { "\"float height\"" => height = iter.parse_parameter()?, "\"float radius\"" => radius = iter.parse_parameter()?, "\"float innerradius\"" => innerradius = iter.parse_parameter()?, "\"float phimax\"" => phimax = iter.parse_parameter()?, _ => { bail!("unknown argument {}", q) } } } Ok(Statement::Shape(ShapeType::Disk { height, radius, innerradius, phimax, })) } _ => Err(miette!("Unknown shape {}", shape_type)), } } impl Lexer { fn next(&mut self, ctm: AffineTransform) -> Option> { match self.input.next() { Some(Ok(s)) => match s.as_str() { "AttributeBegin" => Some(Ok(Statement::AttributeBegin)), "AttributeEnd" => Some(Ok(Statement::AttributeEnd)), "Include" => { let s = self .input .next() .unwrap() .unwrap() .trim_matches('"') .to_string(); Some(Ok(Statement::Include(s))) } "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(Statement::Transform)), "ConcatTransform" => { Some(parse_transform(&mut self.input).map(Statement::ConcatTransform)) } _ => { if s.chars().any(|c| !c.is_ascii_alphabetic()) { Some(Err(miette!("malformed identifier"))) } else { let mut v = Vec::new(); while let Some(p) = self .input .next_if(|s| !s.starts_with(|c: char| c.is_ascii_alphabetic())) { match p { Ok(c) => v.push(c), Err(e) => return Some(Err(e)), } } Some(Ok(Statement::Unknown(s, v))) } } }, Some(Err(e)) => Some(Err(e)), None => None, } } } fn parse_transform(input: &mut Tokenizer) -> 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") } 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 Tokenizer) -> 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 Tokenizer) -> Result { Ok(Statement::ConcatTransform(AffineTransform::scale( iter.parse_next()?, iter.parse_next()?, iter.parse_next()?, ))) } fn parse_rotate(iter: &mut Tokenizer) -> 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 { count: usize, iter: I, } impl BytesToChar { fn new(iter: I) -> Self { Self { count: 0, iter } } } impl>> Iterator for BytesToChar { type Item = Result<(usize, char)>; fn next(&mut self) -> Option { match self.iter.next()? { Ok(a) => { self.count += 1; if a & 0x80 == 0 { Some(Ok((self.count, char::from(a)))) } else { todo!() } } Err(e) => Some(Err(e).into_diagnostic()), } } } struct Parser

{ path: P, inner: Option>>, iter: Lexer, } impl + std::fmt::Debug> Parser

{ fn new(path: P) -> Result { dbg!(&path); Ok(Self { iter: Lexer::new(path.as_ref())?, path, inner: None, }) } } impl> Parser

{ fn next(&mut self, context: &PbrtContext) -> Option> { if let Some(iter) = &mut self.inner { if let Some(statement) = iter.next(context) { return Some(statement); } self.inner = None; } match self.iter.next(context.ctm) { Some(Ok(Statement::Include(s))) => { let path = self.path.as_ref().parent().unwrap().join(s); self.inner = Some(Box::new(Parser::new(path).unwrap())); self.next(context) } Some(s) => Some(s), None => None, } } } #[derive(Debug)] enum ShapeType { Sphere { radius: Float, zmin: Float, zmax: Float, phimax: Float, }, TriangleMesh { indices: Vec, p: Vec, n: Vec, s: Vec, uv: Vec<[Float; 2]>, }, BilinearMesh { 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)] struct Shape { ctm: AffineTransform, material: usize, obj: ShapeType, } #[derive(Debug)] pub struct Pbrt { settings: PbrtWorldSettings, scene: PbrtScene, } impl Pbrt { fn new() -> Self { Self { settings: PbrtWorldSettings {}, scene: PbrtScene { shapes: Vec::new() }, } } } #[derive(Debug)] struct PbrtWorldSettings {} #[derive(Debug)] struct PbrtScene { shapes: Vec, } #[derive(Clone)] struct PbrtContext { ctm: AffineTransform, } impl PbrtContext { fn new() -> Self { Self { ctm: AffineTransform::identity(), } } } fn inner_parse_pbrt( path: impl AsRef + std::fmt::Debug, context: Option, ) -> Result { // unwrap on context.last() ok because context is never empty let mut context = vec![context.unwrap_or(PbrtContext::new())]; let mut parser = Parser::new(path)?; let mut pbrt = Pbrt::new(); while let Some(p) = parser.next(context.last().unwrap()).transpose()? { // dbg!(&p); match p { Statement::AttributeBegin => context.push(context.last().ok_or(miette!(""))?.clone()), Statement::AttributeEnd => { context.pop(); } Statement::Include(_) => unreachable!(), 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, material: 0, obj: shape_type, }); } Statement::Unknown(s, items) => { eprintln!("Unknown statement: {s}") } } } Ok(pbrt) } pub fn parse_pbrt_v4(path: impl AsRef + std::fmt::Debug) -> Result { inner_parse_pbrt(path, None) }