From 4a9754dfdb68e037f7105b625613ed4a2e57a582 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sun, 16 Sep 2018 20:03:42 -0700 Subject: [PATCH] bvh: add some tests and fmt::Display implementations. --- rtiow/src/aabb.rs | 10 +++- rtiow/src/bin/tracer.rs | 14 +++-- rtiow/src/bvh.rs | 111 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/rtiow/src/aabb.rs b/rtiow/src/aabb.rs index 5cdc825..d28c423 100644 --- a/rtiow/src/aabb.rs +++ b/rtiow/src/aabb.rs @@ -1,12 +1,20 @@ +use std::fmt; + use ray::Ray; use vec3::Vec3; -#[derive(Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct AABB { pub min: Vec3, pub max: Vec3, } +impl fmt::Display for AABB { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({})-({})", self.min, self.max) + } +} + impl AABB { pub fn new(min: Vec3, max: Vec3) -> AABB { AABB { min, max } diff --git a/rtiow/src/bin/tracer.rs b/rtiow/src/bin/tracer.rs index a107838..b74f53f 100644 --- a/rtiow/src/bin/tracer.rs +++ b/rtiow/src/bin/tracer.rs @@ -94,7 +94,7 @@ fn random_scene() -> Vec> { objects } -fn build_scene_book(opt: &Opt) -> Scene { +fn build_scene_book(bvh: bool, opt: &Opt) -> Scene { let lookfrom = Vec3::new(13., 2., 3.); let lookat = Vec3::new(0., 0., 0.); let dist_to_focus = 10.; @@ -112,7 +112,12 @@ fn build_scene_book(opt: &Opt) -> Scene { time_min, time_max, ); - let world: Box = Box::new(HitableList::new(random_scene())); + let world: Box; + if bvh { + world = Box::new(BVH::new(random_scene(), time_min, time_max)); + } else { + world = Box::new(HitableList::new(random_scene())); + } Scene { camera, world, @@ -277,6 +282,7 @@ fn build_scene_cube(opt: &Opt) -> Scene { #[derive(Debug)] pub enum Model { Book, + BookBVH, Tutorial, Cube, BVH, @@ -296,6 +302,7 @@ impl str::FromStr for Model { fn from_str(s: &str) -> std::result::Result { match s { "book" => Ok(Model::Book), + "book_bvh" => Ok(Model::BookBVH), "tutorial" => Ok(Model::Tutorial), "cube" => Ok(Model::Cube), "bvh" => Ok(Model::BVH), @@ -328,7 +335,8 @@ fn main() -> Result<(), std::io::Error> { let start = Instant::now(); let opt = Opt::from_args(); let scene = match opt.model { - Model::Book => build_scene_book(&opt), + Model::Book => build_scene_book(false, &opt), + Model::BookBVH => build_scene_book(true, &opt), Model::Cube => build_scene_cube(&opt), Model::Tutorial => build_scene_tutorial(&opt), Model::BVH => build_scene_bvh(&opt), diff --git a/rtiow/src/bvh.rs b/rtiow/src/bvh.rs index 4489ae0..6f56afa 100644 --- a/rtiow/src/bvh.rs +++ b/rtiow/src/bvh.rs @@ -1,4 +1,5 @@ use std; +use std::fmt; use rand; use rand::Rng; @@ -18,10 +19,30 @@ enum BVHNode { }, } +impl fmt::Display for BVHNode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + BVHNode::Leaf(ref hit) => write!( + f, + "Leaf: {}", + hit.bounding_box(0., 0.) + .map_or("NO BBOX".to_owned(), |bb| bb.to_string()) + ), + BVHNode::Branch { bbox, .. } => write!( + f, + "Branch: {}", + // TODO(wathiede): removing this .clone() results in a complaint about moving out + // of a borrow. + bbox.clone() + .map_or("NO BBOX".to_owned(), |bb| bb.to_string()) + ), + } + } +} + fn box_x_compare(ah: &Box, bh: &Box) -> std::cmp::Ordering { match (ah.bounding_box(0., 0.), bh.bounding_box(0., 0.)) { (Some(box_left), Some(box_right)) => { - eprintln!("box_x_compare {} < {}", box_left.min, box_right.min); return box_left.min.x.partial_cmp(&box_right.min.x).unwrap(); } _ => panic!("hit missing bounding box"), @@ -31,7 +52,6 @@ fn box_x_compare(ah: &Box, bh: &Box) -> std::cmp::Ordering { fn box_y_compare(ah: &Box, bh: &Box) -> std::cmp::Ordering { match (ah.bounding_box(0., 0.), bh.bounding_box(0., 0.)) { (Some(box_left), Some(box_right)) => { - eprintln!("box_y_compare {} < {}", box_left.min, box_right.min); return box_left.min.y.partial_cmp(&box_right.min.y).unwrap(); } _ => panic!("hit missing bounding box"), @@ -41,7 +61,6 @@ fn box_y_compare(ah: &Box, bh: &Box) -> std::cmp::Ordering { fn box_z_compare(ah: &Box, bh: &Box) -> std::cmp::Ordering { match (ah.bounding_box(0., 0.), bh.bounding_box(0., 0.)) { (Some(box_left), Some(box_right)) => { - eprintln!("box_z_compare {} < {}", box_left.min, box_right.min); return box_left.min.z.partial_cmp(&box_right.min.z).unwrap(); } _ => panic!("hit missing bounding box"), @@ -118,6 +137,7 @@ impl Hit for BVHNode { }, } } + fn bounding_box(&self, t_min: f32, t_max: f32) -> Option { match self { BVHNode::Leaf(ref hit) => hit.bounding_box(t_min, t_max), @@ -138,6 +158,23 @@ impl BVH { } } +fn print_tree(f: &mut fmt::Formatter, depth: usize, bvhn: &BVHNode) -> fmt::Result { + // TODO(wathiede): recurse and indent + write!(f, "{}{}\n", " ".repeat(depth * 2), bvhn)?; + if let BVHNode::Branch { left, right, .. } = bvhn { + print_tree(f, depth + 1, left)?; + print_tree(f, depth + 1, right)?; + } + Ok(()) +} + +impl fmt::Display for BVH { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Root\n")?; + print_tree(f, 1, &self.root) + } +} + impl Hit for BVH { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { self.root.hit(r, t_min, t_max) @@ -147,3 +184,71 @@ impl Hit for BVH { self.root.bounding_box(t_min, t_max) } } + +#[cfg(test)] +mod tests { + use aabb::AABB; + use material::Lambertian; + use material::Metal; + use sphere::Sphere; + use vec3::Vec3; + + use super::*; + + #[test] + fn bbox_two_spheres() { + let two_spheres_bvh = BVH::new( + vec![ + Box::new(Sphere::new( + Vec3::new(0., 0., 0.), + 0.5, + Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))), + )), + Box::new(Sphere::new( + Vec3::new(1., 0., 0.), + 0.5, + Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)), + )), + ], + 0., + 1., + ); + assert_eq!( + AABB::new(Vec3::new(-0.5, -0.5, -0.5), Vec3::new(1.5, 0.5, 0.5)), + two_spheres_bvh.bounding_box(0., 1.).unwrap(), + "BVH:\n{}", + two_spheres_bvh + ); + } + + #[test] + fn bbox_three_spheres() { + let three_spheres_bvh = BVH::new( + vec![ + Box::new(Sphere::new( + Vec3::new(0., 0., 0.), + 0.5, + Box::new(Lambertian::new(Vec3::new(0.1, 0.2, 0.5))), + )), + Box::new(Sphere::new( + Vec3::new(1., 0., 0.), + 0.5, + Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)), + )), + Box::new(Sphere::new( + Vec3::new(0., 1., 0.), + 0.5, + Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)), + )), + ], + 0., + 1., + ); + assert_eq!( + AABB::new(Vec3::new(-0.5, -0.5, -0.5), Vec3::new(1.5, 1.5, 0.5)), + three_spheres_bvh.bounding_box(0., 1.).unwrap(), + "BVH:\n{}", + three_spheres_bvh + ); + } +}