use std::f32;
use std::cmp;
use enum_set::EnumSet;
use rand::StdRng;
use light_arena::Allocator;
use scene::Scene;
use linalg::{self, Ray, Vector, Point};
use geometry::{Intersection, Emitter, Instance};
use film::Colorf;
use bxdf::{BSDF, BxDFType};
use light::Light;
use sampler::{Sampler, Sample};
use mc;
pub use self::whitted::Whitted;
pub use self::path::Path;
pub use self::normals_debug::NormalsDebug;
pub mod whitted;
pub mod path;
pub mod normals_debug;
pub trait Integrator {
fn illumination(&self, scene: &Scene, light_list: &[&Emitter], ray: &Ray,
hit: &Intersection, sampler: &mut Sampler, rng: &mut StdRng,
alloc: &Allocator) -> Colorf;
fn specular_reflection(&self, scene: &Scene, light_list: &[&Emitter], ray: &Ray,
bsdf: &BSDF, sampler: &mut Sampler, rng: &mut StdRng,
alloc: &Allocator) -> Colorf {
let w_o = -ray.d;
let mut spec_refl = EnumSet::new();
spec_refl.insert(BxDFType::Specular);
spec_refl.insert(BxDFType::Reflection);
let mut sample_2d = [(0.0, 0.0)];
let mut sample_1d = [0.0];
sampler.get_samples_2d(&mut sample_2d[..], rng);
sampler.get_samples_1d(&mut sample_1d[..], rng);
let sample = Sample::new(&sample_2d[0], sample_1d[0]);
let (f, w_i, pdf, _) = bsdf.sample(&w_o, spec_refl, &sample);
let mut refl = Colorf::broadcast(0.0);
if pdf > 0.0 && !f.is_black() && f32::abs(linalg::dot(&w_i, &bsdf.n)) != 0.0 {
let mut refl_ray = ray.child(&bsdf.p, &w_i);
refl_ray.min_t = 0.001;
if let Some(hit) = scene.intersect(&mut refl_ray) {
let li = self.illumination(scene, light_list, &refl_ray, &hit, sampler, rng, alloc);
refl = f * li * f32::abs(linalg::dot(&w_i, &bsdf.n)) / pdf;
}
}
refl
}
fn specular_transmission(&self, scene: &Scene, light_list: &[&Emitter], ray: &Ray,
bsdf: &BSDF, sampler: &mut Sampler, rng: &mut StdRng,
alloc: &Allocator) -> Colorf {
let w_o = -ray.d;
let mut spec_trans = EnumSet::new();
spec_trans.insert(BxDFType::Specular);
spec_trans.insert(BxDFType::Transmission);
let mut sample_2d = [(0.0, 0.0)];
let mut sample_1d = [0.0];
sampler.get_samples_2d(&mut sample_2d[..], rng);
sampler.get_samples_1d(&mut sample_1d[..], rng);
let sample = Sample::new(&sample_2d[0], sample_1d[0]);
let (f, w_i, pdf, _) = bsdf.sample(&w_o, spec_trans, &sample);
let mut transmit = Colorf::broadcast(0.0);
if pdf > 0.0 && !f.is_black() && f32::abs(linalg::dot(&w_i, &bsdf.n)) != 0.0 {
let mut trans_ray = ray.child(&bsdf.p, &w_i);
trans_ray.min_t = 0.001;
if let Some(hit) = scene.intersect(&mut trans_ray) {
let li = self.illumination(scene, light_list, &trans_ray, &hit, sampler, rng, alloc);
transmit = f * li * f32::abs(linalg::dot(&w_i, &bsdf.n)) / pdf;
}
}
transmit
}
fn sample_one_light(&self, scene: &Scene, light_list: &[&Emitter], w_o: &Vector, p: &Point,
bsdf: &BSDF, light_sample: &Sample, bsdf_sample: &Sample, time: f32) -> Colorf {
let l = cmp::min((light_sample.one_d * light_list.len() as f32) as usize, light_list.len() - 1);
self.estimate_direct(scene, w_o, p, bsdf, light_sample, bsdf_sample, light_list[l],
BxDFType::non_specular(), time)
}
fn estimate_direct(&self, scene: &Scene, w_o: &Vector, p: &Point, bsdf: &BSDF, light_sample: &Sample,
bsdf_sample: &Sample, light: &Light, flags: EnumSet<BxDFType>, time: f32) -> Colorf {
let mut direct_light = Colorf::black();
let (li, w_i, pdf_light, occlusion) = light.sample_incident(&bsdf.p, &light_sample.two_d, time);
if pdf_light > 0.0 && !li.is_black() && !occlusion.occluded(scene) {
let f = bsdf.eval(w_o, &w_i, flags);
if !f.is_black() {
if light.delta_light() {
direct_light = f * li * f32::abs(linalg::dot(&w_i, &bsdf.n)) / pdf_light;
} else {
let pdf_bsdf = bsdf.pdf(w_o, &w_i, flags);
let w = mc::power_heuristic(1.0, pdf_light, 1.0, pdf_bsdf);
direct_light = f * li * f32::abs(linalg::dot(&w_i, &bsdf.n)) * w / pdf_light;
}
}
}
if !light.delta_light() {
let (f, w_i, pdf_bsdf, sampled_type) = bsdf.sample(w_o, flags, bsdf_sample);
if pdf_bsdf > 0.0 && !f.is_black() {
let w = if !sampled_type.contains(&BxDFType::Specular) {
let pdf_light = light.pdf(p, &w_i, time);
if pdf_light == 0.0 {
return direct_light;
}
mc::power_heuristic(1.0, pdf_bsdf, 1.0, pdf_light)
} else {
1.0
};
let mut ray = Ray::segment(p, &w_i, 0.001, f32::INFINITY, time);
let mut li = Colorf::black();
if let Some(h) = scene.intersect(&mut ray) {
if let Instance::Emitter(ref e) = *h.instance {
if e as *const Light == light as *const Light {
li = e.radiance(&-w_i, &h.dg.p, &h.dg.ng, time)
}
}
}
if !li.is_black() {
direct_light = direct_light + f * li * f32::abs(linalg::dot(&w_i, &bsdf.n)) * w / pdf_bsdf;
}
}
}
direct_light
}
}