Parsing more stuff.

This commit is contained in:
hal8174 2025-08-02 22:44:06 +02:00
parent 96a24fcc28
commit 8fcf815dbf
Signed by: hal8174
SSH key fingerprint: SHA256:NN98ZYwnrreQLSOV/g+amY7C3yL/mS1heD7bi5t6PPw
3 changed files with 253 additions and 17 deletions

View file

@ -9,6 +9,11 @@ pub struct AffineTransform {
}
impl AffineTransform {
pub fn new(mat: [[Float; 4]; 3]) -> Option<Self> {
let inv = Self::invert(mat)?;
Some(Self { mat, inv })
}
pub fn identity() -> Self {
Self {
mat: [
@ -77,7 +82,7 @@ impl AffineTransform {
}
}
pub fn look_at(pos: Pos3, look: Pos3, up: Dir3) -> Self {
pub fn look_at(pos: Pos3, look: Pos3, up: Dir3) -> Option<Self> {
let dir = (look - pos).normalize();
let right = Dir3::cross(up.normalize(), dir).normalize();
let new_up = Dir3::cross(dir, right);
@ -88,14 +93,55 @@ impl AffineTransform {
[right.z(), new_up.z(), dir.z(), pos.z()],
];
let inv = Self::invert(mat);
Self { mat, inv }
Self::new(mat)
}
fn invert(mat: [[Float; 4]; 3]) -> [[Float; 3]; 3] {
eprintln!("Matrix inversion not implemented");
[[0.0; 3]; 3]
fn invert(mat: [[Float; 4]; 3]) -> Option<[[Float; 3]; 3]> {
// using cramers rule
let adj = |y, x| {
let cofactor_indices = |p| match p {
0 => (1, 2),
1 => (0, 2),
2 => (0, 1),
_ => unreachable!(),
};
let (y1, y2) = cofactor_indices(y);
let (x1, x2) = cofactor_indices(x);
(if (x + y) % 2 == 0 { 1.0 } else { -1.0 })
* (mat[y1][x1] * mat[y2][x2] - mat[y1][x2] * mat[y2][x1])
};
let det = mat[0][0] * mat[1][1] * mat[2][2]
+ mat[0][1] * mat[1][2] * mat[2][0]
+ mat[0][2] * mat[1][0] * mat[2][1]
- mat[0][2] * mat[1][1] * mat[2][0]
- mat[0][1] * mat[1][0] * mat[2][2]
- mat[0][0] * mat[1][2] * mat[2][1];
if det != 0.0 {
let det_frac = 1.0 / det;
Some([
[
adj(0, 0) * det_frac,
adj(1, 0) * det_frac,
adj(2, 0) * det_frac,
],
[
adj(0, 1) * det_frac,
adj(1, 1) * det_frac,
adj(2, 1) * det_frac,
],
[
adj(0, 2) * det_frac,
adj(1, 2) * det_frac,
adj(2, 2) * det_frac,
],
])
} else {
None
}
}
pub fn transform_pos(&self, pos: Pos3) -> Pos3 {

View file

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

View file

@ -119,6 +119,7 @@ enum Statement {
ConcatTransform(AffineTransform),
Shape(ShapeType),
Unknown(String, Vec<String>),
Transform(AffineTransform),
}
fn parse_look_at(iter: &mut impl Iterator<Item = Result<String>>) -> Result<Statement> {
@ -165,9 +166,10 @@ fn parse_look_at(iter: &mut impl Iterator<Item = Result<String>>) -> Result<Stat
.into_diagnostic()?,
);
Ok(Statement::ConcatTransform(AffineTransform::look_at(
eye, look_at, up,
)))
Ok(Statement::ConcatTransform(
AffineTransform::look_at(eye, look_at, up)
.ok_or(miette!("Unable to calculate inverse of matrix"))?,
))
}
fn parse_parameter<I, T>(iter: &mut Peekable<Tokenizer<I>>) -> Result<T>
@ -383,7 +385,7 @@ fn parse_shape<I: Iterator<Item = Result<char>>>(
"\"normal N\"" => {
parse_list_3(iter, &mut n, Dir3::new)?;
}
"\"vector N\"" => {
"\"vector S\"" => {
parse_list_3(iter, &mut s, Dir3::new)?;
}
"\"point2 uv\"" => {
@ -427,8 +429,9 @@ fn parse_shape<I: Iterator<Item = Result<char>>>(
}))
}
"\"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
@ -436,21 +439,50 @@ fn parse_shape<I: Iterator<Item = Result<char>>>(
.transpose()?
{
match q.as_str() {
"\"integer indices\"" => {
parse_list(iter, &mut indices)?;
}
"\"point3 P\"" => {
parse_list_3(iter, &mut p, Pos3::new)?;
}
"\"point2 uv\"" => {
parse_list_2(iter, &mut uv, |u, v| [u, v])?;
}
"\"vector N\"" => {
parse_list_3(iter, &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 {
p: [p[0], p[1], p[2], p[3]],
uv: [uv[0], uv[1], uv[2], uv[3]],
indices,
p,
n,
uv,
}))
}
@ -494,6 +526,34 @@ fn parse_shape<I: Iterator<Item = Result<char>>>(
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.as_ref().is_ok_and(|p| p.starts_with('"')))
.transpose()?
{
match q.as_str() {
"\"float height\"" => height = parse_parameter(iter)?,
"\"float radius\"" => radius = parse_parameter(iter)?,
"\"float innerradius\"" => innerradius = parse_parameter(iter)?,
"\"float phimax\"" => phimax = parse_parameter(iter)?,
_ => {
bail!("unknown argument {}", q)
}
}
}
Ok(Statement::Shape(ShapeType::Disk {
height,
radius,
innerradius,
phimax,
}))
}
_ => Err(miette!("Unknown shape {}", shape_type)),
}
}
@ -517,7 +577,16 @@ impl<I: Iterator<Item = Result<char>>> Lexer<I> {
}
"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(|a| Statement::Transform(a)))
}
"ConcatTransform" => {
Some(parse_transform(&mut self.input).map(|a| Statement::ConcatTransform(a)))
}
_ => {
if s.chars().any(|c| !c.is_ascii_alphabetic()) {
Some(Err(miette!("malformed identifier")))
@ -544,6 +613,114 @@ impl<I: Iterator<Item = Result<char>>> Lexer<I> {
}
}
fn parse_transform<I: Iterator<Item = Result<char>>>(
input: &mut Peekable<Tokenizer<I>>,
) -> 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")
}
Ok(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<I: Iterator<Item = Result<char>>>(
iter: &mut Peekable<Tokenizer<I>>,
) -> 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<I: Iterator<Item = Result<char>>>(
iter: &mut Peekable<Tokenizer<I>>,
) -> Result<Statement> {
Ok(Statement::ConcatTransform(AffineTransform::scale(
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()?,
)))
}
fn parse_rotate<I: Iterator<Item = Result<char>>>(
iter: &mut Peekable<Tokenizer<I>>,
) -> 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> {
iter: I,
}
@ -623,14 +800,22 @@ enum ShapeType {
uv: Vec<[Float; 2]>,
},
BilinearMesh {
p: [Pos3; 4],
uv: [[Float; 2]; 4],
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)]
@ -698,6 +883,9 @@ fn inner_parse_pbrt(
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,