ray-tracing2/ray-tracing-pbrt-scene/src/lib.rs
2025-08-06 21:03:04 +02:00

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