From 900fe532b529c44fddd795a030b7a6371a3184f3 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Tue, 29 Jul 2025 21:57:29 +0200 Subject: [PATCH] Initial commit for pbrt file format parsing. --- Cargo.lock | 96 ++++--- Cargo.toml | 2 +- ray-tracing-pbrt-scene/Cargo.toml | 10 + ray-tracing-pbrt-scene/example.pbrt | 35 +++ ray-tracing-pbrt-scene/examples/pbrt-test.rs | 14 + ray-tracing-pbrt-scene/src/lib.rs | 286 +++++++++++++++++++ 6 files changed, 409 insertions(+), 34 deletions(-) create mode 100644 ray-tracing-pbrt-scene/Cargo.toml create mode 100644 ray-tracing-pbrt-scene/example.pbrt create mode 100644 ray-tracing-pbrt-scene/examples/pbrt-test.rs create mode 100644 ray-tracing-pbrt-scene/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 5cab906..4788244 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,7 +223,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom", + "nom 7.1.3", "num-rational", "v_frame", ] @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" dependencies = [ "clap_builder", "clap_derive", @@ -442,9 +442,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" dependencies = [ "anstream", "anstyle", @@ -454,9 +454,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clipboard-win" @@ -799,12 +799,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -1380,9 +1380,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.164" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libfuzzer-sys" @@ -1437,6 +1437,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.7.4" @@ -1513,9 +1519,9 @@ dependencies = [ [[package]] name = "miette" -version = "7.2.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "backtrace", "backtrace-ext", @@ -1527,15 +1533,14 @@ dependencies = [ "supports-unicode", "terminal_size", "textwrap", - "thiserror", "unicode-width 0.1.14", ] [[package]] name = "miette-derive" -version = "7.2.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", @@ -1646,6 +1651,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "noop_proc_macro" version = "0.3.0" @@ -2271,6 +2285,16 @@ dependencies = [ "rayon", ] +[[package]] +name = "ray-tracing-pbrt-scene" +version = "0.1.0" +dependencies = [ + "clap", + "miette", + "nom 8.0.0", + "ray-tracing-core", +] + [[package]] name = "ray-tracing-renderer" version = "0.1.0" @@ -2283,7 +2307,7 @@ name = "ray-tracing-scene" version = "0.1.0" dependencies = [ "miette", - "nom", + "nom 7.1.3", "rand", "ray-tracing-core", "ray-tracing-material", @@ -2420,10 +2444,23 @@ dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.60.2", +] + [[package]] name = "ryu" version = "1.0.18" @@ -2568,12 +2605,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "smithay-client-toolkit" version = "0.16.1" @@ -2696,12 +2727,12 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix", - "windows-sys 0.48.0", + "rustix 1.0.8", + "windows-sys 0.59.0", ] [[package]] @@ -2716,7 +2747,6 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ - "smawk", "unicode-linebreak", "unicode-width 0.1.14", ] @@ -3624,7 +3654,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" dependencies = [ "gethostname", - "rustix", + "rustix 0.38.41", "x11rb-protocol", ] diff --git a/Cargo.toml b/Cargo.toml index 33e632d..ef274c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [ "ray-tracing-core", "ray-tracing-image", "ray-tracing-material", "ray-tracing-renderer", "ray-tracing-scene", "ray-tracing-tev", "ray-tracing-egui", "ray-tracing-material-visualizer"] +members = [ "ray-tracing-core", "ray-tracing-image", "ray-tracing-material", "ray-tracing-renderer", "ray-tracing-scene", "ray-tracing-tev", "ray-tracing-egui", "ray-tracing-material-visualizer", "ray-tracing-pbrt-scene"] resolver = "2" [profile.release] diff --git a/ray-tracing-pbrt-scene/Cargo.toml b/ray-tracing-pbrt-scene/Cargo.toml new file mode 100644 index 0000000..e3ecbf3 --- /dev/null +++ b/ray-tracing-pbrt-scene/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ray-tracing-pbrt-scene" +version = "0.1.0" +edition = "2024" + +[dependencies] +ray-tracing-core = { path = "../ray-tracing-core" } +clap = { version = "4.5.41", features = ["derive"] } +miette = { version = "7.6.0", features = ["fancy"] } +nom = "8.0.0" diff --git a/ray-tracing-pbrt-scene/example.pbrt b/ray-tracing-pbrt-scene/example.pbrt new file mode 100644 index 0000000..2b5ca95 --- /dev/null +++ b/ray-tracing-pbrt-scene/example.pbrt @@ -0,0 +1,35 @@ +LookAt 3 4 1.5 # eye + .5 .5 0 # look at point + 0 0 1 # up vector +Camera "perspective" "float fov" 45 + +Sampler "halton" "integer pixelsamples" 128 +Integrator "volpath" +Film "rgb" "string filename" "simple.png" + "integer xresolution" [400] "integer yresolution" [400] + +WorldBegin + +# uniform blue-ish illumination from all directions +LightSource "infinite" "rgb L" [ .4 .45 .5 ] + +# approximate the sun +LightSource "distant" "point3 from" [ -30 40 100 ] + "blackbody L" 3000 "float scale" 1.5 + +AttributeBegin + Material "dielectric" + Shape "sphere" "float radius" 1 +AttributeEnd + +AttributeBegin + Texture "checks" "spectrum" "checkerboard" + "float uscale" [16] "float vscale" [16] + "rgb tex1" [.1 .1 .1] "rgb tex2" [.8 .8 .8] + Material "diffuse" "texture reflectance" "checks" + Translate 0 0 -1 + Shape "bilinearmesh" + "point3 P" [ -20 -20 0 20 -20 0 -20 20 0 20 20 0 ] + "point2 uv" [ 0 0 1 0 1 1 0 1 ] +AttributeEnd + diff --git a/ray-tracing-pbrt-scene/examples/pbrt-test.rs b/ray-tracing-pbrt-scene/examples/pbrt-test.rs new file mode 100644 index 0000000..e654fb5 --- /dev/null +++ b/ray-tracing-pbrt-scene/examples/pbrt-test.rs @@ -0,0 +1,14 @@ +use clap::Parser; +use ray_tracing_pbrt_scene::parse_pbrt_v4; +use std::path::PathBuf; + +#[derive(Parser)] +struct Args { + filename: PathBuf, +} + +fn main() -> Result<(), miette::Error> { + let args = Args::parse(); + + parse_pbrt_v4(args.filename) +} diff --git a/ray-tracing-pbrt-scene/src/lib.rs b/ray-tracing-pbrt-scene/src/lib.rs new file mode 100644 index 0000000..3c2b772 --- /dev/null +++ b/ray-tracing-pbrt-scene/src/lib.rs @@ -0,0 +1,286 @@ +use std::{ + fs::File, + io::{BufRead, BufReader, Bytes, Read}, + iter::Peekable, + path::{Path, PathBuf}, +}; + +use miette::{Diagnostic, Error, IntoDiagnostic, Result, bail, miette}; +use nom::IResult; +use ray_tracing_core::math::{Dir3, Pos3}; + +struct Tokenizer>> { + input_iterator: Peekable, +} + +impl>> Tokenizer { + fn new(input_iterator: I) -> Self { + Self { + input_iterator: input_iterator.peekable(), + } + } +} + +impl Iterator for Tokenizer +where + I: Iterator>, +{ + type Item = Result; + + fn next(&mut self) -> Option { + while self + .input_iterator + .peek() + .is_some_and(|c| c.as_ref().is_ok_and(|&c| c.is_whitespace() || c == '#')) + { + if self + .input_iterator + .peek() + .is_some_and(|c| c.as_ref().is_ok_and(|&c| c == '#')) + { + while self + .input_iterator + .next() + .is_some_and(|c| c.is_ok_and(|c| c != '\n')) + {} + } else { + match self.input_iterator.next()? { + Ok(_) => (), + Err(e) => return Some(Err(e)), + } + } + } + + match self.input_iterator.next() { + Some(Ok('[')) => Some(Ok(String::from('['))), + Some(Ok(']')) => Some(Ok(String::from(']'))), + Some(Ok('"')) => { + let mut r = String::from('"'); + while let Some(p) = self + .input_iterator + .next_if(|c| c.as_ref().is_ok_and(|&c| c != '"')) + { + match p { + Ok(c) => r.push(c), + Err(e) => return Some(Err(e)), + } + } + if self + .input_iterator + .next() + .is_none_or(|c| !c.is_ok_and(|c| c == '"')) + { + return Some(Err(miette::miette!("unfinished string"))); + }; + r.push('"'); + Some(Ok(r)) + } + Some(Ok(c)) => { + let mut r = String::new(); + r.push(c); + while let Some(p) = self.input_iterator.next_if(|c| { + c.as_ref() + .is_ok_and(|&c| c != '#' && c != '[' && c != ']' && !c.is_whitespace()) + }) { + match p { + Ok(c) => r.push(c), + Err(e) => return Some(Err(e)), + } + } + Some(Ok(r)) + } + Some(Err(e)) => Some(Err(e)), + None => None, + } + } +} + +struct Lexer>> { + input: Peekable>, +} + +impl>> Lexer { + fn new(iter: I) -> Self { + Self { + input: Tokenizer::new(iter).peekable(), + } + } +} + +#[derive(Debug)] +enum Statement { + AttributeStart, + AttributeEnd, + Include(String), + LookAt { eye: Pos3, look_at: Pos3, up: Dir3 }, + Unknown(String, Vec), +} + +fn parse_look_at(iter: &mut impl Iterator>) -> 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::LookAt { eye, look_at, up }) +} + +impl>> Iterator for Lexer { + type Item = Result; + + fn next(&mut self) -> Option { + match self.input.next() { + Some(Ok(s)) => match s.as_str() { + "AttributeStart" => Some(Ok(Statement::AttributeStart)), + "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)), + _ => { + 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.as_ref() + .is_ok_and(|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, + } + } +} + +struct BytesToChar { + iter: I, +} + +impl>> Iterator for BytesToChar { + type Item = Result; + + fn next(&mut self) -> Option { + match self.iter.next()? { + Ok(a) => { + if a & 0x80 == 0 { + Some(Ok(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); + let bufreader = BufReader::new(std::fs::File::open(&path).into_diagnostic()?); + Ok(Self { + path, + inner: None, + iter: Lexer::new(BytesToChar { + iter: bufreader.bytes(), + }), + }) + } +} + +impl> Iterator for Parser

{ + type Item = Result; + + fn next(&mut self) -> Option { + if let Some(iter) = &mut self.inner { + if let Some(statement) = iter.next() { + return Some(statement); + } + self.inner = None; + } + + match self.iter.next() { + 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() + } + Some(s) => Some(s), + None => None, + } + } +} + +pub fn parse_pbrt_v4(path: impl AsRef + std::fmt::Debug) -> Result<()> { + let mut tokens = 0; + for token in Parser::new(path).unwrap() { + dbg!(token); + tokens += 1; + } + dbg!(tokens); + + Ok(()) +}