diff --git a/ray-tracing-pbrt-scene/src/either.rs b/ray-tracing-pbrt-scene/src/either.rs index 1ee8cfa..a950439 100644 --- a/ray-tracing-pbrt-scene/src/either.rs +++ b/ray-tracing-pbrt-scene/src/either.rs @@ -17,6 +17,20 @@ impl Either { Either::B(b) => Either::B(f(b)), } } + + pub fn get_a(&self) -> Option<&A> { + match self { + Either::A(a) => Some(a), + Either::B(_) => None, + } + } + + pub fn get_b(&self) -> Option<&B> { + match self { + Either::A(_) => None, + Either::B(b) => Some(b), + } + } } impl Either> { diff --git a/ray-tracing-pbrt-scene/src/lib.rs b/ray-tracing-pbrt-scene/src/lib.rs index 95d6e8b..a3b3f69 100644 --- a/ray-tracing-pbrt-scene/src/lib.rs +++ b/ray-tracing-pbrt-scene/src/lib.rs @@ -1,4 +1,5 @@ use crate::{ + either::Either, scene::PbrtScene, shape::{Shape, ShapeAlpha, ShapeType}, tokenizer::{Token, Tokenizer}, @@ -8,6 +9,7 @@ use miette::{IntoDiagnostic, Result, bail, miette}; use ray_tracing_core::{ affine_transform::AffineTransform, color::Color, + light::Light, math::{Dir3, Pos3}, prelude::{Float, Rng}, }; @@ -83,6 +85,22 @@ enum Statement { Material(Arc>), MakeNamedMaterial(String, Arc>), NamedMaterial(String), + AreaLight(AreaLight), +} + +#[derive(Debug, Clone)] +pub struct AreaLight { + pub color: Color, +} + +impl Light for AreaLight { + fn emit(&self, w_in: Dir3, _rng: &mut R) -> Color { + if w_in.y() > 0.0 { + self.color + } else { + Color::black() + } + } } fn parse_look_at(iter: &mut Tokenizer) -> Result> { @@ -417,7 +435,9 @@ impl Lexer { material::parse_make_named_material(&mut self.input, context) .map(|(name, material)| Statement::MakeNamedMaterial(name, material)), ), - "NamedMaterial" => Some(self.input.parse_parameter().map(Statement::NamedMaterial)), + "NamedMaterial" => { + Some(self.input.next_string_value().map(Statement::NamedMaterial)) + } "ConcatTransform" => { Some(parse_transform(&mut self.input).map(Statement::ConcatTransform)) } @@ -429,6 +449,8 @@ impl Lexer { Ok(s) => Ok(Statement::CoordSysTransform(s)), Err(e) => Err(e), }), + + "AreaLightSource" => Some(parse_area_light(&mut self.input)), "WorldBegin" => Some(Ok(Statement::WorldBegin)), _ => { if s.chars().any(|c| !c.is_ascii_alphabetic()) { @@ -459,6 +481,22 @@ impl Lexer { } } +fn parse_area_light(input: &mut Tokenizer) -> Result> { + let s = input.next_string_value()?; + + if s.as_str() != "diffuse" { + return Err(miette!( + labels = vec![input.last_span_labeled(Some("here"))], + "Only diffuse area light supported." + ) + .with_source_code(input.get_src())); + } + + Ok(Statement::AreaLight(parse_dict2!(input, AreaLight; + color, Color::white(), ["rgb L", texture::parse_rgb(input)?] + ))) +} + fn parse_transform(input: &mut Tokenizer) -> Result { input.next_expect_bracket_open()?; let mut v = [0.0; 16]; @@ -615,6 +653,7 @@ pub struct PbrtContext { ctm: Vec, textures: HashMap>, material: Vec>>, + area_light: Vec, materials: HashMap>>, } @@ -623,6 +662,7 @@ impl PbrtContext { Self { ctm: vec![AffineTransform::identity()], textures: HashMap::new(), + area_light: Vec::new(), material: Vec::new(), materials: HashMap::new(), } @@ -651,6 +691,11 @@ impl PbrtContext { self.material .push(Arc::clone(self.material.last().unwrap())); } + + if !self.area_light.is_empty() { + self.area_light + .push(self.area_light.last().unwrap().clone()); + } } fn pop(&mut self) -> Result<()> { @@ -661,6 +706,7 @@ impl PbrtContext { } self.material.pop(); + self.area_light.pop(); Ok(()) } @@ -747,17 +793,26 @@ fn inner_parse_pbrt( } Statement::Shape(shape_type, shape_alpha) => { dbg!(&context); - pbrt.scene.shapes.push(Shape { - ctm: context.get_ctm(), - material: Arc::clone( - context - .material - .last() - .ok_or_else(|| miette!("No material specified"))?, - ), - obj: shape_type, - alpha: shape_alpha, - }); + if context.area_light.is_empty() { + pbrt.scene.shapes.push(Shape { + ctm: context.get_ctm(), + material: Either::A(Arc::clone( + context + .material + .last() + .ok_or_else(|| miette!("No material specified"))?, + )), + obj: shape_type, + alpha: shape_alpha, + }); + } else { + pbrt.scene.shapes.push(Shape { + ctm: context.get_ctm(), + material: Either::B(context.area_light.last().unwrap().clone()), + obj: shape_type, + alpha: shape_alpha, + }); + } } Statement::CoordinateSystem(s) => { named_transforms.insert(s, context.get_ctm()); @@ -774,6 +829,13 @@ fn inner_parse_pbrt( *context.material.last_mut().unwrap() = m; } } + Statement::AreaLight(l) => { + if context.area_light.is_empty() { + context.area_light.push(l); + } else { + *context.area_light.last_mut().unwrap() = l; + } + } Statement::MakeNamedMaterial(n, m) => { context.materials.insert(n, m); } @@ -801,7 +863,7 @@ fn inner_parse_pbrt( // dbg!(context); - Ok(pbrt) + Ok(dbg!(pbrt)) } pub fn parse_pbrt_v4( diff --git a/ray-tracing-pbrt-scene/src/shape.rs b/ray-tracing-pbrt-scene/src/shape.rs index 8fc787f..ab69922 100644 --- a/ray-tracing-pbrt-scene/src/shape.rs +++ b/ray-tracing-pbrt-scene/src/shape.rs @@ -2,13 +2,13 @@ use std::sync::Arc; use ray_tracing_core::{affine_transform::AffineTransform, prelude::*, scene::Intersection}; -use crate::{material::PbrtMaterial, scene::UVMaterial}; +use crate::{AreaLight, either::Either, material::PbrtMaterial, scene::UVMaterial}; #[derive(Debug)] #[allow(dead_code)] pub(crate) struct Shape { pub(crate) ctm: AffineTransform, - pub(crate) material: Arc>, + pub(crate) material: Either>, AreaLight>, pub(crate) obj: ShapeType, pub(crate) alpha: ShapeAlpha, } @@ -21,7 +21,7 @@ pub(crate) enum ShapeAlpha { Texture(String), } -#[derive(Debug)] +// #[derive(Debug)] #[allow(dead_code)] pub(crate) enum ShapeType { Sphere { @@ -219,12 +219,14 @@ impl Shape { t, self.ctm .inverse_transform_normal((ray.at(t).as_dir()).normalize()), - Some(UVMaterial { + self.material.get_a().map(|m| UVMaterial { u: phi, v: Float::acos(p.z() / radius), - material: self.material.as_ref(), + material: m.as_ref(), }), - None, + self.material + .get_b() + .map(|l| l as &dyn ray_tracing_core::light::Light), 0.0, )); } @@ -244,12 +246,14 @@ impl Shape { return Some(Intersection::new( t, Dir3::new(0.0, 0.0, 1.0), - Some(UVMaterial { + self.material.get_a().map(|m| UVMaterial { u, v, - material: self.material.as_ref(), + material: m.as_ref(), }), - None, + self.material + .get_b() + .map(|l| l as &dyn ray_tracing_core::light::Light), 0.0, )); }