BVH and objects now use MemArena.

This commit is contained in:
Nathan Vegdahl 2017-04-09 23:33:36 -07:00
parent e9e202933f
commit c82c821b31
8 changed files with 188 additions and 146 deletions

View File

@ -1,5 +1,7 @@
#![allow(dead_code)] #![allow(dead_code)]
use mem_arena::MemArena;
use algorithm::{partition, merge_slices_append}; use algorithm::{partition, merge_slices_append};
use bbox::BBox; use bbox::BBox;
use boundable::Boundable; use boundable::Boundable;
@ -12,15 +14,14 @@ use super::objects_split::{sah_split, median_split};
const BVH_MAX_DEPTH: usize = 64; const BVH_MAX_DEPTH: usize = 64;
#[derive(Debug)] #[derive(Copy, Clone, Debug)]
pub struct BVH { pub struct BVH<'a> {
nodes: Vec<BVHNode>, nodes: &'a [BVHNode],
bounds: Vec<BBox>, bounds: &'a [BBox],
depth: usize, depth: usize,
bounds_cache: Vec<BBox>,
} }
#[derive(Debug)] #[derive(Copy, Clone, Debug)]
enum BVHNode { enum BVHNode {
Internal { Internal {
bounds_range: (usize, usize), bounds_range: (usize, usize),
@ -34,9 +35,102 @@ enum BVHNode {
}, },
} }
impl BVH { impl<'a> BVH<'a> {
pub fn new_empty() -> BVH { pub fn from_objects<'b, T, F>(arena: &'a MemArena,
objects: &mut [T],
objects_per_leaf: usize,
bounder: F)
-> BVH<'a>
where F: 'b + Fn(&T) -> &'b [BBox]
{
let mut builder = BVHBuilder::new_empty();
builder.recursive_build(0, 0, objects_per_leaf, objects, &bounder);
BVH { BVH {
nodes: arena.copy_slice(&builder.nodes),
bounds: arena.copy_slice(&builder.bounds),
depth: builder.depth,
}
}
pub fn tree_depth(&self) -> usize {
self.depth
}
pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F)
where F: FnMut(&T, &mut [AccelRay])
{
if self.nodes.len() == 0 {
return;
}
// +2 of max depth for root and last child
let mut i_stack = [0; BVH_MAX_DEPTH + 2];
let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 2];
let mut stack_ptr = 1;
while stack_ptr > 0 {
match self.nodes[i_stack[stack_ptr]] {
BVHNode::Internal { bounds_range: br, second_child_index, split_axis } => {
let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
(!r.is_done()) &&
lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r)
});
if part > 0 {
i_stack[stack_ptr] += 1;
i_stack[stack_ptr + 1] = second_child_index;
ray_i_stack[stack_ptr] = part;
ray_i_stack[stack_ptr + 1] = part;
if rays[0].dir_inv.get_n(split_axis as usize).is_sign_positive() {
i_stack.swap(stack_ptr, stack_ptr + 1);
}
stack_ptr += 1;
} else {
stack_ptr -= 1;
}
}
BVHNode::Leaf { bounds_range: br, object_range } => {
let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
(!r.is_done()) &&
lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r)
});
if part > 0 {
for obj in &objects[object_range.0..object_range.1] {
obj_ray_test(obj, &mut rays[..part]);
}
}
stack_ptr -= 1;
}
}
}
}
}
impl<'a> Boundable for BVH<'a> {
fn bounds<'b>(&'b self) -> &'b [BBox] {
match self.nodes[0] {
BVHNode::Internal { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1],
BVHNode::Leaf { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1],
}
}
}
#[derive(Debug)]
struct BVHBuilder {
nodes: Vec<BVHNode>,
bounds: Vec<BBox>,
depth: usize,
bounds_cache: Vec<BBox>,
}
impl BVHBuilder {
fn new_empty() -> BVHBuilder {
BVHBuilder {
nodes: Vec::new(), nodes: Vec::new(),
bounds: Vec::new(), bounds: Vec::new(),
depth: 0, depth: 0,
@ -44,22 +138,6 @@ impl BVH {
} }
} }
pub fn from_objects<'a, T, F>(objects: &mut [T], objects_per_leaf: usize, bounder: F) -> BVH
where F: 'a + Fn(&T) -> &'a [BBox]
{
let mut bvh = BVH::new_empty();
bvh.recursive_build(0, 0, objects_per_leaf, objects, &bounder);
bvh.bounds_cache.clear();
bvh.bounds_cache.shrink_to_fit();
bvh
}
pub fn tree_depth(&self) -> usize {
self.depth
}
fn acc_bounds<'a, T, F>(&mut self, objects: &mut [T], bounder: &F) fn acc_bounds<'a, T, F>(&mut self, objects: &mut [T], bounder: &F)
where F: 'a + Fn(&T) -> &'a [BBox] where F: 'a + Fn(&T) -> &'a [BBox]
{ {
@ -168,66 +246,4 @@ impl BVH {
return (me, (bi, self.bounds.len())); return (me, (bi, self.bounds.len()));
} }
} }
pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F)
where F: FnMut(&T, &mut [AccelRay])
{
if self.nodes.len() == 0 {
return;
}
// +2 of max depth for root and last child
let mut i_stack = [0; BVH_MAX_DEPTH + 2];
let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 2];
let mut stack_ptr = 1;
while stack_ptr > 0 {
match self.nodes[i_stack[stack_ptr]] {
BVHNode::Internal { bounds_range: br, second_child_index, split_axis } => {
let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
(!r.is_done()) &&
lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r)
});
if part > 0 {
i_stack[stack_ptr] += 1;
i_stack[stack_ptr + 1] = second_child_index;
ray_i_stack[stack_ptr] = part;
ray_i_stack[stack_ptr + 1] = part;
if rays[0].dir_inv.get_n(split_axis as usize).is_sign_positive() {
i_stack.swap(stack_ptr, stack_ptr + 1);
}
stack_ptr += 1;
} else {
stack_ptr -= 1;
}
}
BVHNode::Leaf { bounds_range: br, object_range } => {
let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
(!r.is_done()) &&
lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r)
});
if part > 0 {
for obj in &objects[object_range.0..object_range.1] {
obj_ray_test(obj, &mut rays[..part]);
}
}
stack_ptr -= 1;
}
}
}
}
}
impl Boundable for BVH {
fn bounds<'a>(&'a self) -> &'a [BBox] {
match self.nodes[0] {
BVHNode::Internal { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1],
BVHNode::Leaf { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1],
}
}
} }

View File

@ -1,3 +1,5 @@
use mem_arena::MemArena;
use bbox::BBox; use bbox::BBox;
use boundable::Boundable; use boundable::Boundable;
use color::{XYZ, SpectralSample, Color}; use color::{XYZ, SpectralSample, Color};
@ -8,16 +10,19 @@ use sampling::{spherical_triangle_solid_angle, uniform_sample_spherical_triangle
use super::LightSource; use super::LightSource;
#[derive(Debug)] #[derive(Copy, Clone, Debug)]
pub struct RectangleLight { pub struct RectangleLight<'a> {
dimensions: Vec<(f32, f32)>, dimensions: &'a [(f32, f32)],
colors: Vec<XYZ>, colors: &'a [XYZ],
bounds_: Vec<BBox>, bounds_: &'a [BBox],
} }
impl RectangleLight { impl<'a> RectangleLight<'a> {
pub fn new(dimensions: Vec<(f32, f32)>, colors: Vec<XYZ>) -> RectangleLight { pub fn new<'b>(arena: &'b MemArena,
let bbs = dimensions.iter() dimensions: Vec<(f32, f32)>,
colors: Vec<XYZ>)
-> RectangleLight<'b> {
let bbs: Vec<_> = dimensions.iter()
.map(|d| { .map(|d| {
BBox { BBox {
min: Point::new(d.0 * -0.5, d.1 * -0.5, 0.0), min: Point::new(d.0 * -0.5, d.1 * -0.5, 0.0),
@ -26,14 +31,14 @@ impl RectangleLight {
}) })
.collect(); .collect();
RectangleLight { RectangleLight {
dimensions: dimensions, dimensions: arena.copy_slice(&dimensions),
colors: colors, colors: arena.copy_slice(&colors),
bounds_: bbs, bounds_: arena.copy_slice(&bbs),
} }
} }
} }
impl LightSource for RectangleLight { impl<'a> LightSource for RectangleLight<'a> {
fn sample(&self, fn sample(&self,
space: &Matrix4x4, space: &Matrix4x4,
arr: Point, arr: Point,
@ -166,8 +171,8 @@ impl LightSource for RectangleLight {
} }
} }
impl Boundable for RectangleLight { impl<'a> Boundable for RectangleLight<'a> {
fn bounds<'a>(&'a self) -> &'a [BBox] { fn bounds<'b>(&'b self) -> &'b [BBox] {
&self.bounds_ &self.bounds_
} }
} }

View File

@ -1,5 +1,7 @@
use std::f64::consts::PI as PI_64; use std::f64::consts::PI as PI_64;
use mem_arena::MemArena;
use bbox::BBox; use bbox::BBox;
use boundable::Boundable; use boundable::Boundable;
use color::{XYZ, SpectralSample, Color}; use color::{XYZ, SpectralSample, Color};
@ -11,16 +13,16 @@ use super::LightSource;
// TODO: handle case where radius = 0.0. // TODO: handle case where radius = 0.0.
#[derive(Debug)] #[derive(Copy, Clone, Debug)]
pub struct SphereLight { pub struct SphereLight<'a> {
radii: Vec<f32>, radii: &'a [f32],
colors: Vec<XYZ>, colors: &'a [XYZ],
bounds_: Vec<BBox>, bounds_: &'a [BBox],
} }
impl SphereLight { impl<'a> SphereLight<'a> {
pub fn new(radii: Vec<f32>, colors: Vec<XYZ>) -> SphereLight { pub fn new<'b>(arena: &'b MemArena, radii: Vec<f32>, colors: Vec<XYZ>) -> SphereLight<'b> {
let bbs = radii.iter() let bbs: Vec<_> = radii.iter()
.map(|r| { .map(|r| {
BBox { BBox {
min: Point::new(-*r, -*r, -*r), min: Point::new(-*r, -*r, -*r),
@ -29,14 +31,14 @@ impl SphereLight {
}) })
.collect(); .collect();
SphereLight { SphereLight {
radii: radii, radii: arena.copy_slice(&radii),
colors: colors, colors: arena.copy_slice(&colors),
bounds_: bbs, bounds_: arena.copy_slice(&bbs),
} }
} }
} }
impl LightSource for SphereLight { impl<'a> LightSource for SphereLight<'a> {
fn sample(&self, fn sample(&self,
space: &Matrix4x4, space: &Matrix4x4,
arr: Point, arr: Point,
@ -170,8 +172,8 @@ impl LightSource for SphereLight {
} }
} }
impl Boundable for SphereLight { impl<'a> Boundable for SphereLight<'a> {
fn bounds<'a>(&'a self) -> &'a [BBox] { fn bounds<'b>(&'b self) -> &'b [BBox] {
&self.bounds_ &self.bounds_
} }
} }

View File

@ -67,7 +67,9 @@ pub fn parse_assembly<'a>(arena: &'a MemArena,
"MeshSurface" => { "MeshSurface" => {
if let &DataTree::Internal { ident: Some(ident), .. } = child { if let &DataTree::Internal { ident: Some(ident), .. } = child {
builder.add_object(ident, builder.add_object(ident,
Object::Surface(Box::new(parse_mesh_surface(&child)?))); Object::Surface(arena.alloc(
parse_mesh_surface(arena, &child)?
)));
} else { } else {
// TODO: error condition of some kind, because no ident // TODO: error condition of some kind, because no ident
panic!(); panic!();
@ -78,7 +80,9 @@ pub fn parse_assembly<'a>(arena: &'a MemArena,
"SphereLight" => { "SphereLight" => {
if let &DataTree::Internal { ident: Some(ident), .. } = child { if let &DataTree::Internal { ident: Some(ident), .. } = child {
builder.add_object(ident, builder.add_object(ident,
Object::Light(Box::new(parse_sphere_light(&child)?))); Object::Light(arena.alloc(
parse_sphere_light(arena, &child)?
)));
} else { } else {
// TODO: error condition of some kind, because no ident // TODO: error condition of some kind, because no ident
panic!(); panic!();
@ -89,7 +93,9 @@ pub fn parse_assembly<'a>(arena: &'a MemArena,
"RectangleLight" => { "RectangleLight" => {
if let &DataTree::Internal { ident: Some(ident), .. } = child { if let &DataTree::Internal { ident: Some(ident), .. } = child {
builder.add_object(ident, builder.add_object(ident,
Object::Light(Box::new(parse_rectangle_light(&child)?))); Object::Light(arena.alloc(
parse_rectangle_light(arena, &child)?
)));
} else { } else {
// TODO: error condition of some kind, because no ident // TODO: error condition of some kind, because no ident
panic!(); panic!();

View File

@ -72,7 +72,9 @@ pub fn parse_distant_disk_light<'a>(arena: &'a MemArena,
} }
pub fn parse_sphere_light(tree: &DataTree) -> Result<SphereLight, PsyParseError> { pub fn parse_sphere_light<'a>(arena: &'a MemArena,
tree: &'a DataTree)
-> Result<SphereLight<'a>, PsyParseError> {
if let &DataTree::Internal { ref children, .. } = tree { if let &DataTree::Internal { ref children, .. } = tree {
let mut radii = Vec::new(); let mut radii = Vec::new();
let mut colors = Vec::new(); let mut colors = Vec::new();
@ -108,13 +110,15 @@ pub fn parse_sphere_light(tree: &DataTree) -> Result<SphereLight, PsyParseError>
} }
} }
return Ok(SphereLight::new(radii, colors)); return Ok(SphereLight::new(arena, radii, colors));
} else { } else {
return Err(PsyParseError::UnknownError); return Err(PsyParseError::UnknownError);
} }
} }
pub fn parse_rectangle_light(tree: &DataTree) -> Result<RectangleLight, PsyParseError> { pub fn parse_rectangle_light<'a>(arena: &'a MemArena,
tree: &'a DataTree)
-> Result<RectangleLight<'a>, PsyParseError> {
if let &DataTree::Internal { ref children, .. } = tree { if let &DataTree::Internal { ref children, .. } = tree {
let mut dimensions = Vec::new(); let mut dimensions = Vec::new();
let mut colors = Vec::new(); let mut colors = Vec::new();
@ -151,7 +155,7 @@ pub fn parse_rectangle_light(tree: &DataTree) -> Result<RectangleLight, PsyParse
} }
} }
return Ok(RectangleLight::new(dimensions, colors)); return Ok(RectangleLight::new(arena, dimensions, colors));
} else { } else {
return Err(PsyParseError::UnknownError); return Err(PsyParseError::UnknownError);
} }

View File

@ -4,6 +4,8 @@ use std::result::Result;
use nom::IResult; use nom::IResult;
use mem_arena::MemArena;
use math::Point; use math::Point;
use surface::triangle_mesh::TriangleMesh; use surface::triangle_mesh::TriangleMesh;
@ -19,7 +21,9 @@ use super::psy::PsyParseError;
// accel: BVH, // accel: BVH,
// } // }
pub fn parse_mesh_surface(tree: &DataTree) -> Result<TriangleMesh, PsyParseError> { pub fn parse_mesh_surface<'a>(arena: &'a MemArena,
tree: &'a DataTree)
-> Result<TriangleMesh<'a>, PsyParseError> {
let mut verts = Vec::new(); let mut verts = Vec::new();
let mut face_vert_counts = Vec::new(); let mut face_vert_counts = Vec::new();
let mut face_vert_indices = Vec::new(); let mut face_vert_indices = Vec::new();
@ -100,5 +104,5 @@ pub fn parse_mesh_surface(tree: &DataTree) -> Result<TriangleMesh, PsyParseError
ii += *fvc; ii += *fvc;
} }
Ok(TriangleMesh::from_triangles(time_samples, triangles)) Ok(TriangleMesh::from_triangles(arena, time_samples, triangles))
} }

View File

@ -22,13 +22,13 @@ pub struct Assembly<'a> {
pub xforms: &'a [Matrix4x4], pub xforms: &'a [Matrix4x4],
// Object list // Object list
pub objects: Vec<Object>, pub objects: Vec<Object<'a>>,
// Assembly list // Assembly list
pub assemblies: Vec<Assembly<'a>>, pub assemblies: Vec<Assembly<'a>>,
// Object accel // Object accel
pub object_accel: BVH, pub object_accel: BVH<'a>,
// Light accel // Light accel
pub light_accel: LightTree, pub light_accel: LightTree,
@ -137,7 +137,7 @@ pub struct AssemblyBuilder<'a> {
xforms: Vec<Matrix4x4>, xforms: Vec<Matrix4x4>,
// Object list // Object list
objects: Vec<Object>, objects: Vec<Object<'a>>,
object_map: HashMap<String, usize>, // map Name -> Index object_map: HashMap<String, usize>, // map Name -> Index
// Assembly list // Assembly list
@ -159,7 +159,7 @@ impl<'a> AssemblyBuilder<'a> {
} }
} }
pub fn add_object(&mut self, name: &str, obj: Object) { pub fn add_object(&mut self, name: &str, obj: Object<'a>) {
// Make sure the name hasn't already been used. // Make sure the name hasn't already been used.
if self.name_exists(name) { if self.name_exists(name) {
panic!("Attempted to add object to assembly with a name that already exists."); panic!("Attempted to add object to assembly with a name that already exists.");
@ -231,7 +231,8 @@ impl<'a> AssemblyBuilder<'a> {
let (bis, bbs) = self.instance_bounds(); let (bis, bbs) = self.instance_bounds();
// Build object accel // Build object accel
let object_accel = BVH::from_objects(&mut self.instances[..], let object_accel = BVH::from_objects(self.arena,
&mut self.instances[..],
1, 1,
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
@ -335,9 +336,9 @@ impl<'a> AssemblyBuilder<'a> {
#[derive(Debug)] #[derive(Debug)]
pub enum Object { pub enum Object<'a> {
Surface(Box<Surface>), Surface(&'a Surface),
Light(Box<LightSource>), Light(&'a LightSource),
} }

View File

@ -1,5 +1,7 @@
#![allow(dead_code)] #![allow(dead_code)]
use mem_arena::MemArena;
use accel::BVH; use accel::BVH;
use bbox::BBox; use bbox::BBox;
use boundable::Boundable; use boundable::Boundable;
@ -13,18 +15,19 @@ use super::{Surface, SurfaceIntersection, SurfaceIntersectionData};
use super::triangle; use super::triangle;
#[derive(Debug)] #[derive(Copy, Clone, Debug)]
pub struct TriangleMesh { pub struct TriangleMesh<'a> {
time_samples: usize, time_samples: usize,
geo: Vec<(Point, Point, Point)>, geo: &'a [(Point, Point, Point)],
indices: Vec<usize>, indices: &'a [usize],
accel: BVH, accel: BVH<'a>,
} }
impl TriangleMesh { impl<'a> TriangleMesh<'a> {
pub fn from_triangles(time_samples: usize, pub fn from_triangles<'b>(arena: &'b MemArena,
time_samples: usize,
triangles: Vec<(Point, Point, Point)>) triangles: Vec<(Point, Point, Point)>)
-> TriangleMesh { -> TriangleMesh<'b> {
assert!(triangles.len() % time_samples == 0); assert!(triangles.len() % time_samples == 0);
let mut indices: Vec<usize> = (0..(triangles.len() / time_samples)) let mut indices: Vec<usize> = (0..(triangles.len() / time_samples))
@ -41,27 +44,28 @@ impl TriangleMesh {
bounds bounds
}; };
let accel = BVH::from_objects(&mut indices[..], let accel = BVH::from_objects(arena,
&mut indices[..],
3, 3,
|tri_i| &bounds[*tri_i..(*tri_i + time_samples)]); |tri_i| &bounds[*tri_i..(*tri_i + time_samples)]);
TriangleMesh { TriangleMesh {
time_samples: time_samples, time_samples: time_samples,
geo: triangles, geo: arena.copy_slice(&triangles),
indices: indices, indices: arena.copy_slice(&indices),
accel: accel, accel: accel,
} }
} }
} }
impl Boundable for TriangleMesh { impl<'a> Boundable for TriangleMesh<'a> {
fn bounds<'a>(&'a self) -> &'a [BBox] { fn bounds<'b>(&'b self) -> &'b [BBox] {
self.accel.bounds() self.accel.bounds()
} }
} }
impl Surface for TriangleMesh { impl<'a> Surface for TriangleMesh<'a> {
fn intersect_rays(&self, fn intersect_rays(&self,
accel_rays: &mut [AccelRay], accel_rays: &mut [AccelRay],
wrays: &[Ray], wrays: &[Ray],