Parsing some stuff.

This commit is contained in:
hal8174 2025-08-01 22:17:33 +02:00
parent 16b9c32632
commit 96a24fcc28
Signed by: hal8174
SSH key fingerprint: SHA256:NN98ZYwnrreQLSOV/g+amY7C3yL/mS1heD7bi5t6PPw
2 changed files with 371 additions and 37 deletions

View file

@ -10,5 +10,7 @@ struct Args {
fn main() -> Result<(), miette::Error> {
let args = Args::parse();
parse_pbrt_v4(args.filename)
dbg!(parse_pbrt_v4(args.filename)?);
Ok(())
}

View file

@ -117,6 +117,7 @@ enum Statement {
AttributeEnd,
Include(String),
ConcatTransform(AffineTransform),
Shape(ShapeType),
Unknown(String, Vec<String>),
}
@ -169,14 +170,331 @@ fn parse_look_at(iter: &mut impl Iterator<Item = Result<String>>) -> Result<Stat
)))
}
fn parse_parameter<I, T>(iter: &mut Peekable<Tokenizer<I>>) -> Result<T>
where
I: Iterator<Item = Result<char>>,
T: std::str::FromStr,
<T as std::str::FromStr>::Err:
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
{
let p = iter.next().ok_or(miette!("value expected"))??;
match p.as_str() {
"[" => {
let s = iter.next().ok_or(miette!("value expected"))??;
let d = s.parse::<T>().into_diagnostic()?;
if !iter
.next()
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]"))
{
bail!("expected closing bracket.")
}
Ok(d)
}
s => Ok(s.parse::<T>().into_diagnostic()?),
}
}
fn parse_list<I, T>(iter: &mut Peekable<Tokenizer<I>>, data: &mut Vec<T>) -> Result<()>
where
I: Iterator<Item = Result<char>>,
T: std::str::FromStr,
<T as std::str::FromStr>::Err:
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
{
if !iter
.next()
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "["))
{
bail!("expected list.")
}
while let Some(p) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p != "]"))
.transpose()?
{
data.push(p.parse().into_diagnostic()?);
}
if !iter
.next()
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]"))
{
bail!("expected list end.")
}
Ok(())
}
fn parse_list_2<I, T, P, F>(
iter: &mut Peekable<Tokenizer<I>>,
data: &mut Vec<P>,
f: F,
) -> Result<()>
where
I: Iterator<Item = Result<char>>,
T: std::str::FromStr,
<T as std::str::FromStr>::Err:
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
F: Fn(T, T) -> P,
{
if !iter
.next()
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "["))
{
bail!("expected list.")
}
while let Some(pa) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p != "]"))
.transpose()?
{
if let Some(pb) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p != "]"))
.transpose()?
{
data.push(f(
pa.parse().into_diagnostic()?,
pb.parse().into_diagnostic()?,
));
} else {
bail!("Unfinished group")
}
}
if !iter
.next()
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]"))
{
bail!("expected list end.")
}
Ok(())
}
fn parse_list_3<I, T, P, F>(
iter: &mut Peekable<Tokenizer<I>>,
data: &mut Vec<P>,
f: F,
) -> Result<()>
where
I: Iterator<Item = Result<char>>,
T: std::str::FromStr,
<T as std::str::FromStr>::Err:
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
F: Fn(T, T, T) -> P,
{
if !iter
.next()
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "["))
{
bail!("expected list.")
}
while let Some(pa) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p != "]"))
.transpose()?
{
if let Some(pb) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p != "]"))
.transpose()?
{
if let Some(pc) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p != "]"))
.transpose()?
{
data.push(f(
pa.parse().into_diagnostic()?,
pb.parse().into_diagnostic()?,
pc.parse().into_diagnostic()?,
));
} else {
bail!("Unfinished group")
}
} else {
bail!("Unfinished group")
}
}
if !iter
.next()
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]"))
{
bail!("expected list end.")
}
Ok(())
}
fn parse_shape<I: Iterator<Item = Result<char>>>(
iter: &mut Peekable<Tokenizer<I>>,
) -> std::result::Result<Statement, Error> {
let shape_type = iter.next().ok_or(miette!(""))??;
let shape_type = iter.next().ok_or(miette!("unable to get shape type"))??;
match shape_type.as_str() {
"sphere" => {}
_ => Err(miette!("")),
"\"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.as_ref().is_ok_and(|p| p.starts_with('"')))
.transpose()?
{
match p.as_str() {
"\"float radius\"" => {
radius = parse_parameter(iter)?;
}
_ => {
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.as_ref().is_ok_and(|p| p.starts_with('"')))
.transpose()?
{
match q.as_str() {
"\"integer indices\"" => {
parse_list(iter, &mut indices)?;
}
"\"point3 P\"" => {
parse_list_3(iter, &mut p, Pos3::new)?;
}
"\"normal N\"" => {
parse_list_3(iter, &mut n, Dir3::new)?;
}
"\"vector N\"" => {
parse_list_3(iter, &mut s, Dir3::new)?;
}
"\"point2 uv\"" => {
parse_list_2(iter, &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 p = Vec::new();
let mut uv = Vec::new();
while let Some(q) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p.starts_with('"')))
.transpose()?
{
match q.as_str() {
"\"point3 P\"" => {
parse_list_3(iter, &mut p, Pos3::new)?;
}
"\"point2 uv\"" => {
parse_list_2(iter, &mut uv, |u, v| [u, v])?;
}
_ => {
bail!("unknown argument {}", q)
}
}
}
Ok(Statement::Shape(ShapeType::BilinearMesh {
p: [p[0], p[1], p[2], p[3]],
uv: [uv[0], uv[1], uv[2], uv[3]],
}))
}
"\"loopsubdiv\"" => {
let mut levels = 3;
let mut indices = Vec::new();
let mut p = Vec::new();
while let Some(q) = iter
.next_if(|p| p.as_ref().is_ok_and(|p| p.starts_with('"')))
.transpose()?
{
match q.as_str() {
"\"point3 P\"" => {
parse_list_3(iter, &mut p, Pos3::new)?;
}
"\"integer indices\"" => {
parse_list(iter, &mut indices)?;
}
"\"integer levels\"" => {
levels = parse_parameter(iter)?;
}
_ => {
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,
}))
}
_ => Err(miette!("Unknown shape {}", shape_type)),
}
}
@ -199,6 +517,7 @@ impl<I: Iterator<Item = Result<char>>> Lexer<I> {
}
"LookAt" => Some(parse_look_at(&mut self.input)),
"Identity" => Some(Ok(Statement::ConcatTransform(AffineTransform::identity()))),
"Shape" => Some(parse_shape(&mut self.input)),
_ => {
if s.chars().any(|c| !c.is_ascii_alphabetic()) {
Some(Err(miette!("malformed identifier")))
@ -218,7 +537,6 @@ impl<I: Iterator<Item = Result<char>>> Lexer<I> {
Some(Ok(Statement::Unknown(s, v)))
}
}
"Shape" => Some(parse_shape(&mut self.input)),
},
Some(Err(e)) => Some(Err(e)),
None => None,
@ -251,7 +569,6 @@ struct Parser<P> {
path: P,
inner: Option<Box<Parser<PathBuf>>>,
iter: Lexer<BytesToChar<Bytes<BufReader<File>>>>,
ctm: AffineTransform,
}
impl<P: AsRef<Path> + std::fmt::Debug> Parser<P> {
@ -260,40 +577,29 @@ impl<P: AsRef<Path> + std::fmt::Debug> Parser<P> {
let bufreader = BufReader::new(std::fs::File::open(&path).into_diagnostic()?);
Ok(Self {
path,
ctm: AffineTransform::identity(),
inner: None,
iter: Lexer::new(BytesToChar {
iter: bufreader.bytes(),
}),
})
}
fn apply_transform(&mut self, transform: AffineTransform) {
self.ctm *= transform;
}
fn set_transform(&mut self, transform: AffineTransform) {
self.ctm = transform;
}
}
impl<P: AsRef<Path>> Iterator for Parser<P> {
type Item = Result<Statement>;
fn next(&mut self) -> Option<Self::Item> {
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() {
if let Some(statement) = iter.next(context) {
return Some(statement);
}
self.inner = None;
}
match self.iter.next(self.ctm) {
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()
self.next(context)
}
Some(s) => Some(s),
None => None,
@ -301,6 +607,7 @@ impl<P: AsRef<Path>> Iterator for Parser<P> {
}
}
#[derive(Debug)]
enum ShapeType {
Sphere {
radius: Float,
@ -308,29 +615,50 @@ enum ShapeType {
zmax: Float,
phimax: Float,
},
TriangleMesh {
indices: Vec<usize>,
p: Vec<Pos3>,
n: Vec<Dir3>,
s: Vec<Dir3>,
uv: Vec<[Float; 2]>,
},
BilinearMesh {
p: [Pos3; 4],
uv: [[Float; 2]; 4],
},
LoopSubDiv {
levels: u32,
indices: Vec<usize>,
p: Vec<Pos3>,
},
}
#[derive(Debug)]
struct Shape {
ctm: AffineTransform,
material: usize,
obj: ShapeType,
}
struct Pbrt {
#[derive(Debug)]
pub struct Pbrt {
settings: PbrtWorldSettings,
scene: PbrtScene,
}
impl Pbrt {
fn new() -> Self {
Self {
settings: todo!(),
scene: todo!(),
settings: PbrtWorldSettings {},
scene: PbrtScene { shapes: Vec::new() },
}
}
}
#[derive(Debug)]
struct PbrtWorldSettings {}
#[derive(Debug)]
struct PbrtScene {
shapes: Vec<Shape>,
}
@ -352,13 +680,15 @@ 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().transpose()? {
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 => {
@ -368,20 +698,22 @@ fn inner_parse_pbrt(
Statement::ConcatTransform(affine_transform) => {
context.last_mut().ok_or(miette!(""))?.ctm *= affine_transform
}
Statement::Unknown(_, items) => todo!(),
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}")
}
}
}
todo!()
Ok(pbrt)
}
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(())
pub fn parse_pbrt_v4(path: impl AsRef<Path> + std::fmt::Debug) -> Result<Pbrt> {
inner_parse_pbrt(path, None)
}