diff --git a/src/assembly.rs b/src/assembly.rs index bd2b314..f595ea8 100644 --- a/src/assembly.rs +++ b/src/assembly.rs @@ -1,10 +1,13 @@ use std::collections::HashMap; -use math::Matrix4x4; +use math::{Matrix4x4, Vector}; +use lerp::lerp_slice; use bvh::BVH; +use light_accel::{LightAccel, LightArray}; use boundable::Boundable; -use surface::Surface; +use surface::{Surface, SurfaceIntersection}; use light::LightSource; +use color::SpectralSample; use bbox::{BBox, transform_bbox_slice_from}; @@ -12,6 +15,7 @@ use bbox::{BBox, transform_bbox_slice_from}; pub struct Assembly { // Instance list pub instances: Vec, + pub light_instances: Vec, pub xforms: Vec, // Object list @@ -24,7 +28,51 @@ pub struct Assembly { pub object_accel: BVH, // Light accel - pub light_accel: Vec, + pub light_accel: LightArray, +} + +impl Assembly { + // Returns (light_color, shadow_vector, selection_pdf) + pub fn sample_lights(&self, + n: f32, + uvw: (f32, f32, f32), + wavelength: f32, + time: f32, + intr: &SurfaceIntersection) + -> Option<(SpectralSample, Vector, f32)> { + if let &SurfaceIntersection::Hit { pos, .. } = intr { + if let Some((light_i, sel_pdf, _)) = self.light_accel.select(n) { + let inst = self.light_instances[light_i]; + match inst.instance_type { + InstanceType::Object => { + match &self.objects[inst.data_index] { + &Object::Light(ref light) => { + let xform = if let Some((a, b)) = inst.transform_indices { + lerp_slice(&self.xforms[a..b], time) + } else { + Matrix4x4::new() + }; + let (color, shadow_vec, pdf) = + light.sample(&xform, pos, uvw.0, uvw.1, wavelength, time); + return Some((color, shadow_vec, pdf * sel_pdf)); + } + + _ => unimplemented!(), + } + } + + InstanceType::Assembly => { + // TODO: recursive light selection inside assemblies + unimplemented!() + } + } + } else { + None + } + } else { + None + } + } } impl Boundable for Assembly { @@ -151,21 +199,24 @@ impl AssemblyBuilder { println!("Assembly BVH Depth: {}", object_accel.tree_depth()); // Build light accel - // TODO: build light tree instead of stupid vec - let light_accel = { - let mut light_accel = Vec::new(); - for inst in self.instances.iter() { - if let InstanceType::Object = inst.instance_type { + let mut light_instances = self.instances.clone(); + let light_accel = LightArray::new(&mut light_instances[..], |inst| { + match inst.instance_type { + InstanceType::Object => { if let Object::Light(_) = self.objects[inst.data_index] { - light_accel.push(*inst); + Some((&bbs[bis[inst.id]..bis[inst.id + 1]], 1.0)) + } else { + None } } + + _ => None, } - light_accel - }; + }); Assembly { instances: self.instances, + light_instances: light_instances, xforms: self.xforms, objects: self.objects, assemblies: self.assemblies, diff --git a/src/light_accel/mod.rs b/src/light_accel/mod.rs new file mode 100644 index 0000000..633427d --- /dev/null +++ b/src/light_accel/mod.rs @@ -0,0 +1,50 @@ +use bbox::BBox; + +pub trait LightAccel { + /// Returns (index_of_light, selection_pdf, whittled_n) + fn select(&self, n: f32) -> Option<(usize, f32, f32)>; +} + +#[derive(Debug, Clone)] +pub struct LightArray { + indices: Vec, +} + +impl LightArray { + pub fn new<'a, T, F>(things: &mut [T], q: F) -> LightArray + where F: 'a + Fn(&T) -> Option<(&'a [BBox], f32)> + { + let mut indices = Vec::new(); + for (i, thing) in things.iter().enumerate() { + if let Some((_, power)) = q(thing) { + if power > 0.0 { + indices.push(i); + } + } + } + + LightArray { indices: indices } + } +} + +impl LightAccel for LightArray { + fn select(&self, n: f32) -> Option<(usize, f32, f32)> { + assert!(n >= 0.0 && n <= 1.0); + + if self.indices.len() == 0 { + return None; + } + + let n2 = n * self.indices.len() as f32; + let i = if n == 1.0 { + *self.indices.last().unwrap() + } else { + self.indices[n2 as usize] + }; + + let whittled_n = n2 - i as f32; + let pdf = 1.0 / self.indices.len() as f32; + + Some((i, pdf, whittled_n)) + } +} diff --git a/src/main.rs b/src/main.rs index c43e670..666f8b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ mod triangle; mod surface; mod light; mod bvh; +mod light_accel; mod scene; mod assembly; mod halton; diff --git a/src/parse/data_tree.rs b/src/parse/data_tree.rs index 305996d..6ad9579 100644 --- a/src/parse/data_tree.rs +++ b/src/parse/data_tree.rs @@ -1,7 +1,6 @@ #![allow(dead_code)] use std::result::Result; -use std::cmp::Eq; use std::iter::Iterator; use std::slice; diff --git a/src/renderer.rs b/src/renderer.rs index 295ef29..15a7d73 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -2,23 +2,19 @@ use std::path::Path; use std::cmp::min; -use std::iter::Iterator; use std::sync::RwLock; use scoped_threadpool::Pool; use crossbeam::sync::MsQueue; use algorithm::partition_pair; -use lerp::lerp_slice; use ray::Ray; -use assembly::Object; use tracer::Tracer; use halton; -use math::{Matrix4x4, fast_logit}; +use math::fast_logit; use image::Image; use surface; use scene::Scene; use color::{Color, XYZ, SpectralSample, map_0_1_to_wavelength}; -use shading::surface_closure::{SurfaceClosure, LambertClosure}; #[derive(Debug)] pub struct Renderer { @@ -239,31 +235,10 @@ impl LightPath { self.interaction = *isect; // Store interaction for use in next phase // Prepare light ray - if scene.root.light_accel.len() > 0 { - // Get the light and the mapping to its local space - let (light, space) = { - let l1 = &scene.root.objects[scene.root.light_accel[0].data_index]; - let light = if let &Object::Light(ref light) = l1 { - light - } else { - panic!() - }; - let space = if let Some((start, end)) = scene.root.light_accel[0] - .transform_indices { - lerp_slice(&scene.root.xforms[start..end], self.time) - } else { - Matrix4x4::new() - }; - (light, space) - }; - - // Sample the light - let (light_color, shadow_vec, light_pdf) = { - let lu = self.next_lds_samp(); - let lv = self.next_lds_samp(); - light.sample(&space, pos, lu, lv, self.wavelength, self.time) - }; - + let light_n = self.next_lds_samp(); + let light_uvw = (self.next_lds_samp(), self.next_lds_samp(), self.next_lds_samp()); + if let Some((light_color, shadow_vec, light_pdf)) = scene.root + .sample_lights(light_n, light_uvw, self.wavelength, self.time, isect) { // Calculate and store the light that will be contributed // to the film plane if the light is not in shadow. self.pending_color_addition = {