Initial commit for pbrt file format parsing.
This commit is contained in:
parent
9fa518572e
commit
900fe532b5
6 changed files with 409 additions and 34 deletions
10
ray-tracing-pbrt-scene/Cargo.toml
Normal file
10
ray-tracing-pbrt-scene/Cargo.toml
Normal 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"
|
||||
35
ray-tracing-pbrt-scene/example.pbrt
Normal file
35
ray-tracing-pbrt-scene/example.pbrt
Normal 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
|
||||
|
||||
14
ray-tracing-pbrt-scene/examples/pbrt-test.rs
Normal file
14
ray-tracing-pbrt-scene/examples/pbrt-test.rs
Normal 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)
|
||||
}
|
||||
286
ray-tracing-pbrt-scene/src/lib.rs
Normal file
286
ray-tracing-pbrt-scene/src/lib.rs
Normal 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(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue