Assemblies now use BVH accel and actually utilize instances.

This commit is contained in:
Nathan Vegdahl 2016-04-23 13:37:38 -07:00
parent b58ba10f9b
commit 901fc88f63
9 changed files with 220 additions and 22 deletions

View File

@ -2,8 +2,10 @@ use std::collections::HashMap;
use math::Matrix4x4;
use bvh::BVH;
use boundable::Boundable;
use surface::{Surface, SurfaceIntersection};
use ray::Ray;
use bbox::{BBox, transform_bbox_slice_from};
#[derive(Debug)]
@ -43,6 +45,111 @@ impl Assembly {
self.object_map.insert(name.to_string(), self.objects.len());
self.objects.push(obj);
}
pub fn add_assembly(&mut self, name: &str, asmb: Assembly) {
self.assembly_map.insert(name.to_string(), self.assemblies.len());
self.assemblies.push(asmb);
}
pub fn add_object_instance(&mut self, name: &str, xforms: Option<&[Matrix4x4]>) {
let instance = Instance {
instance_type: InstanceType::Object,
data_index: self.object_map[name],
id: self.instances.len(),
transform_indices: xforms.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())),
};
self.instances.push(instance);
if let Some(xf) = xforms {
self.xforms.extend(xf);
}
}
pub fn add_assembly_instance(&mut self, name: &str, xforms: Option<&[Matrix4x4]>) {
let instance = Instance {
instance_type: InstanceType::Assembly,
data_index: self.object_map[name],
id: self.instances.len(),
transform_indices: xforms.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())),
};
self.instances.push(instance);
if let Some(xf) = xforms {
self.xforms.extend(xf);
}
}
pub fn finalize(&mut self) {
// Clear maps (no longer needed).
// However, don't clear shader maps, as they are still used by
// get_surface_shader() et al.
self.object_map.clear();
self.assembly_map.clear();
// Shrink storage to minimum.
// However, don't shrink shader storage, because there are pointers to
// that data that could get invalidated.
self.instances.shrink_to_fit();
self.xforms.shrink_to_fit();
self.objects.shrink_to_fit();
self.object_map.shrink_to_fit();
self.assemblies.shrink_to_fit();
self.assembly_map.shrink_to_fit();
// Build object accel
let (bis, bbs) = self.instance_bounds();
println!("Len: {}, {}", bis.len(), bbs.len());
self.object_accel = BVH::from_objects(&mut self.instances[..],
1,
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
}
/// Returns a pair of vectors with the bounds of all instances.
/// This is used for building the assembly's BVH.
fn instance_bounds(&self) -> (Vec<usize>, Vec<BBox>) {
let mut indices = vec![0];
let mut bounds = Vec::new();
for inst in self.instances.iter() {
let mut bbs = Vec::new();
let mut bbs2 = Vec::new();
// Get bounding boxes
match inst.instance_type {
InstanceType::Object => {
let obj = &self.objects[inst.data_index];
// TODO: push bounds onto bbs
match obj {
&Object::Surface(ref s) => bbs.extend(s.bounds()),
}
}
InstanceType::Assembly => {
let asmb = &self.assemblies[inst.data_index];
// TODO: push bounds onto bbs
}
}
// Transform the bounding boxes, if necessary
if let Some((xstart, xend)) = inst.transform_indices {
let xf = &self.xforms[xstart..xend];
transform_bbox_slice_from(&bbs, &xf, &mut bbs2);
} else {
bbs2.clear();
bbs2.extend(bbs);
}
// Push transformed bounds onto vec
bounds.extend(bbs2);
indices.push(bounds.len());
}
return (indices, bounds);
}
}
@ -53,15 +160,15 @@ pub enum Object {
#[derive(Debug, Copy, Clone)]
pub enum Instance {
Object {
data_index: usize,
transform_indices: (usize, usize),
shader_index: usize,
},
Assembly {
data_index: usize,
transform_indices: (usize, usize),
},
pub struct Instance {
pub instance_type: InstanceType,
pub data_index: usize,
pub id: usize,
pub transform_indices: Option<(usize, usize)>,
}
#[derive(Debug, Copy, Clone)]
pub enum InstanceType {
Object,
Assembly,
}

View File

@ -2,9 +2,10 @@
use std;
use std::ops::BitOr;
use std::iter::Iterator;
use math::Point;
use lerp::{lerp, Lerp};
use math::{Point, Matrix4x4};
use lerp::{lerp, lerp_slice, Lerp};
use ray::Ray;
const BBOX_MAXT_ADJUST: f32 = 1.00000024;
@ -53,6 +54,29 @@ impl BBox {
// Did we hit?
return hitt0.max(0.0) <= hitt1.min(ray.max_t);
}
// Creates a new BBox transformed into a different space.
pub fn transformed(&self, xform: Matrix4x4) -> BBox {
// BBox corners
let vs = [Point::new(self.min[0], self.min[1], self.min[2]),
Point::new(self.min[0], self.min[1], self.max[2]),
Point::new(self.min[0], self.max[1], self.min[2]),
Point::new(self.min[0], self.max[1], self.max[2]),
Point::new(self.max[0], self.min[1], self.min[2]),
Point::new(self.max[0], self.min[1], self.max[2]),
Point::new(self.max[0], self.max[1], self.min[2]),
Point::new(self.max[0], self.max[1], self.max[2])];
// Transform BBox corners and make new bbox
let mut b = BBox::new();
for v in vs.iter() {
let v = *v * xform;
b.min = v.min(b.min);
b.max = v.max(b.max);
}
return b;
}
}
@ -75,3 +99,27 @@ impl Lerp for BBox {
}
}
}
pub fn transform_bbox_slice_from(bbs_in: &[BBox], xforms: &[Matrix4x4], bbs_out: &mut Vec<BBox>) {
bbs_out.clear();
// Transform the bounding boxes
if xforms.len() == 0 {
return;
} else if bbs_in.len() == xforms.len() {
for (bb, xf) in Iterator::zip(bbs_in.iter(), xforms.iter()) {
bbs_out.push(bb.transformed(xf.inverse()));
}
} else if bbs_in.len() > xforms.len() {
let s = (bbs_in.len() - 1) as f32;
for (i, bb) in bbs_in.iter().enumerate() {
bbs_out.push(bb.transformed(lerp_slice(xforms, i as f32 / s).inverse()));
}
} else if bbs_in.len() < xforms.len() {
let s = (xforms.len() - 1) as f32;
for (i, xf) in xforms.iter().enumerate() {
bbs_out.push(lerp_slice(bbs_in, i as f32 / s).transformed(xf.inverse()));
}
}
}

7
src/boundable.rs Normal file
View File

@ -0,0 +1,7 @@
#![allow(dead_code)]
use bbox::BBox;
pub trait Boundable {
fn bounds<'a>(&'a self) -> &'a [BBox];
}

View File

@ -2,6 +2,7 @@
use lerp::lerp_slice;
use bbox::BBox;
use boundable::Boundable;
use ray::Ray;
use algorithm::partition;
@ -217,3 +218,14 @@ impl BVH {
}
}
}
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

@ -66,9 +66,8 @@ impl Camera {
// Ray origin
let orig = {
let (u, v) = square_to_circle(aperture_radius * ((u * 2.0) - 1.0),
aperture_radius * ((v * 2.0) - 1.0));
Point::new(u, v, 0.0)
let (u, v) = square_to_circle((u * 2.0) - 1.0, (v * 2.0) - 1.0);
Point::new(aperture_radius * u, aperture_radius * v, 0.0)
};
// Ray direction

View File

@ -14,6 +14,7 @@ mod parse;
mod renderer;
mod tracer;
mod image;
mod boundable;
mod triangle;
mod surface;
mod bvh;
@ -133,6 +134,9 @@ fn main() {
let mut assembly = Assembly::new();
assembly.add_object("yar", Object::Surface(Box::new(mesh)));
assembly.add_object_instance("yar",
Some(&[Matrix4x4::from_location(Point::new(0.0, 0.0, 0.0))]));
assembly.finalize();
let scene = Scene {
name: None,

View File

@ -6,6 +6,7 @@ pub mod triangle_mesh;
use ray::Ray;
use math::{Point, Normal, Matrix4x4};
use boundable::Boundable;
#[derive(Debug, Clone)]
@ -21,6 +22,6 @@ pub enum SurfaceIntersection {
},
}
pub trait Surface: Debug {
pub trait Surface: Boundable + Debug {
fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]);
}

View File

@ -5,6 +5,7 @@ use math::{Point, Normal, Matrix4x4};
use ray::Ray;
use triangle;
use bbox::BBox;
use boundable::Boundable;
use bvh::BVH;
use super::{Surface, SurfaceIntersection};
@ -50,6 +51,12 @@ impl TriangleMesh {
}
}
impl Boundable for TriangleMesh {
fn bounds<'a>(&'a self) -> &'a [BBox] {
self.accel.bounds()
}
}
impl Surface for TriangleMesh {
fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]) {

View File

@ -3,7 +3,7 @@ use std::slice;
use std::cell::UnsafeCell;
use math::Matrix4x4;
use assembly::{Assembly, Object};
use assembly::{Assembly, Object, Instance, InstanceType};
use ray::Ray;
use surface::SurfaceIntersection;
@ -56,7 +56,21 @@ impl<'a> Tracer<'a> {
}
fn trace_assembly<'b>(&'b mut self, assembly: &Assembly, wrays: &[Ray], rays: &mut [Ray]) {
for obj in assembly.objects.iter() {
assembly.object_accel.traverse(&mut rays[..], &assembly.instances[..], |inst, rs| {
// TODO: transform rays
match inst.instance_type {
InstanceType::Object => {
self.trace_object(&assembly.objects[inst.data_index], wrays, rs);
}
InstanceType::Assembly => {
self.trace_assembly(&assembly.assemblies[inst.data_index], wrays, rs);
}
}
});
}
fn trace_object<'b>(&'b mut self, obj: &Object, wrays: &[Ray], rays: &mut [Ray]) {
match obj {
&Object::Surface(ref surface) => {
surface.intersect_rays(rays, &mut self.isects);
@ -64,4 +78,3 @@ impl<'a> Tracer<'a> {
}
}
}
}