642 lines
18 KiB
Rust
642 lines
18 KiB
Rust
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<Path>) -> Result<Self> {
|
|
Ok(Self {
|
|
input: Tokenizer::new(path)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Statement {
|
|
AttributeBegin,
|
|
AttributeEnd,
|
|
Include(String),
|
|
ConcatTransform(AffineTransform),
|
|
Shape(ShapeType),
|
|
Unknown(String, Vec<String>),
|
|
Transform(AffineTransform),
|
|
}
|
|
|
|
fn parse_look_at(iter: &mut Tokenizer) -> 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::ConcatTransform(
|
|
AffineTransform::look_at(eye, look_at, up)
|
|
.ok_or(miette!("Unable to calculate inverse of matrix"))?,
|
|
))
|
|
}
|
|
|
|
fn parse_shape(iter: &mut Tokenizer) -> Result<Statement> {
|
|
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<Result<Statement>> {
|
|
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<AffineTransform> {
|
|
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::<Float>()
|
|
.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<Statement> {
|
|
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<Statement> {
|
|
Ok(Statement::ConcatTransform(AffineTransform::scale(
|
|
iter.parse_next()?,
|
|
iter.parse_next()?,
|
|
iter.parse_next()?,
|
|
)))
|
|
}
|
|
|
|
fn parse_rotate(iter: &mut Tokenizer) -> Result<Statement> {
|
|
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<I> {
|
|
count: usize,
|
|
iter: I,
|
|
}
|
|
|
|
impl<I> BytesToChar<I> {
|
|
fn new(iter: I) -> Self {
|
|
Self { count: 0, iter }
|
|
}
|
|
}
|
|
|
|
impl<I: Iterator<Item = Result<u8, std::io::Error>>> Iterator for BytesToChar<I> {
|
|
type Item = Result<(usize, char)>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
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<P> {
|
|
path: P,
|
|
inner: Option<Box<Parser<PathBuf>>>,
|
|
iter: Lexer,
|
|
}
|
|
|
|
impl<P: AsRef<Path> + std::fmt::Debug> Parser<P> {
|
|
fn new(path: P) -> Result<Self> {
|
|
dbg!(&path);
|
|
Ok(Self {
|
|
iter: Lexer::new(path.as_ref())?,
|
|
path,
|
|
inner: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<P: AsRef<Path>> Parser<P> {
|
|
fn next(&mut self, context: &PbrtContext) -> Option<Result<Statement>> {
|
|
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<usize>,
|
|
p: Vec<Pos3>,
|
|
n: Vec<Dir3>,
|
|
s: Vec<Dir3>,
|
|
uv: Vec<[Float; 2]>,
|
|
},
|
|
BilinearMesh {
|
|
indices: Vec<usize>,
|
|
p: Vec<Pos3>,
|
|
n: Vec<Dir3>,
|
|
uv: Vec<[Float; 2]>,
|
|
},
|
|
LoopSubDiv {
|
|
levels: u32,
|
|
indices: Vec<usize>,
|
|
p: Vec<Pos3>,
|
|
},
|
|
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<Shape>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct PbrtContext {
|
|
ctm: AffineTransform,
|
|
}
|
|
|
|
impl PbrtContext {
|
|
fn new() -> Self {
|
|
Self {
|
|
ctm: AffineTransform::identity(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn inner_parse_pbrt(
|
|
path: impl AsRef<Path> + std::fmt::Debug,
|
|
context: Option<PbrtContext>,
|
|
) -> Result<Pbrt> {
|
|
// 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<Path> + std::fmt::Debug) -> Result<Pbrt> {
|
|
inner_parse_pbrt(path, None)
|
|
}
|