Initial commit for pbrt file format parsing.

This commit is contained in:
hal8174 2025-07-29 21:57:29 +02:00
parent 9fa518572e
commit 900fe532b5
Signed by: hal8174
SSH key fingerprint: SHA256:JwuqS+eVfISfKr+DkDQ6NWAbGd1jFAHkPpCM1yCnlTs
6 changed files with 409 additions and 34 deletions

96
Cargo.lock generated
View file

@ -223,7 +223,7 @@ dependencies = [
"anyhow", "anyhow",
"arrayvec", "arrayvec",
"log", "log",
"nom", "nom 7.1.3",
"num-rational", "num-rational",
"v_frame", "v_frame",
] ]
@ -432,9 +432,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.21" version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -442,9 +442,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.21" version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -454,9 +454,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.18" version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@ -466,9 +466,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.3" version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
@ -799,12 +799,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.9" version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -1380,9 +1380,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.164" version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
@ -1437,6 +1437,12 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.7.4" version = "0.7.4"
@ -1513,9 +1519,9 @@ dependencies = [
[[package]] [[package]]
name = "miette" name = "miette"
version = "7.2.0" version = "7.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"backtrace-ext", "backtrace-ext",
@ -1527,15 +1533,14 @@ dependencies = [
"supports-unicode", "supports-unicode",
"terminal_size", "terminal_size",
"textwrap", "textwrap",
"thiserror",
"unicode-width 0.1.14", "unicode-width 0.1.14",
] ]
[[package]] [[package]]
name = "miette-derive" name = "miette-derive"
version = "7.2.0" version = "7.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1646,6 +1651,15 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "noop_proc_macro" name = "noop_proc_macro"
version = "0.3.0" version = "0.3.0"
@ -2271,6 +2285,16 @@ dependencies = [
"rayon", "rayon",
] ]
[[package]]
name = "ray-tracing-pbrt-scene"
version = "0.1.0"
dependencies = [
"clap",
"miette",
"nom 8.0.0",
"ray-tracing-core",
]
[[package]] [[package]]
name = "ray-tracing-renderer" name = "ray-tracing-renderer"
version = "0.1.0" version = "0.1.0"
@ -2283,7 +2307,7 @@ name = "ray-tracing-scene"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"miette", "miette",
"nom", "nom 7.1.3",
"rand", "rand",
"ray-tracing-core", "ray-tracing-core",
"ray-tracing-material", "ray-tracing-material",
@ -2420,10 +2444,23 @@ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys 0.4.14",
"windows-sys 0.52.0", "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]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.18"
@ -2568,12 +2605,6 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "smawk"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
[[package]] [[package]]
name = "smithay-client-toolkit" name = "smithay-client-toolkit"
version = "0.16.1" version = "0.16.1"
@ -2696,12 +2727,12 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "terminal_size" name = "terminal_size"
version = "0.3.0" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
dependencies = [ dependencies = [
"rustix", "rustix 1.0.8",
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -2716,7 +2747,6 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
dependencies = [ dependencies = [
"smawk",
"unicode-linebreak", "unicode-linebreak",
"unicode-width 0.1.14", "unicode-width 0.1.14",
] ]
@ -3624,7 +3654,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12"
dependencies = [ dependencies = [
"gethostname", "gethostname",
"rustix", "rustix 0.38.41",
"x11rb-protocol", "x11rb-protocol",
] ]

View file

@ -1,5 +1,5 @@
[workspace] [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" resolver = "2"
[profile.release] [profile.release]

View file

@ -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"

View file

@ -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

View file

@ -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)
}

View file

@ -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<I: Iterator<Item = Result<char>>> {
input_iterator: Peekable<I>,
}
impl<I: Iterator<Item = Result<char>>> Tokenizer<I> {
fn new(input_iterator: I) -> Self {
Self {
input_iterator: input_iterator.peekable(),
}
}
}
impl<I> Iterator for Tokenizer<I>
where
I: Iterator<Item = Result<char>>,
{
type Item = Result<String>;
fn next(&mut self) -> Option<Self::Item> {
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<I: Iterator<Item = Result<char>>> {
input: Peekable<Tokenizer<I>>,
}
impl<I: Iterator<Item = Result<char>>> Lexer<I> {
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<String>),
}
fn parse_look_at(iter: &mut impl Iterator<Item = Result<String>>) -> Result<Statement> {
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<I: Iterator<Item = Result<char>>> Iterator for Lexer<I> {
type Item = Result<Statement>;
fn next(&mut self) -> Option<Self::Item> {
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<I> {
iter: I,
}
impl<I: Iterator<Item = Result<u8, std::io::Error>>> Iterator for BytesToChar<I> {
type Item = Result<char>;
fn next(&mut self) -> Option<Self::Item> {
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<P> {
path: P,
inner: Option<Box<Parser<PathBuf>>>,
iter: Lexer<BytesToChar<Bytes<BufReader<File>>>>,
}
impl<P: AsRef<Path> + std::fmt::Debug> Parser<P> {
fn new(path: P) -> Result<Self> {
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<P: AsRef<Path>> Iterator for Parser<P> {
type Item = Result<Statement>;
fn next(&mut self) -> Option<Self::Item> {
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<Path> + std::fmt::Debug) -> Result<()> {
let mut tokens = 0;
for token in Parser::new(path).unwrap() {
dbg!(token);
tokens += 1;
}
dbg!(tokens);
Ok(())
}