Added AssemblyBuilder and got instancing working (sans testing...).

The AssemblyBuilder is responsible for collecting the data needed
to actually create an Assembly.  AssemblyBuilders are now the
only way to create an Assembly, which guarantees that Assemblies
aren't half-baked.

Also got instancing working with transforms and such.  It may not
be _really_ working because I don't have a complex test case for
it yet.  But that will come later.
This commit is contained in:
Nathan Vegdahl 2016-04-23 20:14:10 -07:00
parent 901fc88f63
commit 4988a6d1e6
6 changed files with 139 additions and 37 deletions

View File

@ -16,31 +16,49 @@ pub struct Assembly {
// Object list // Object list
pub objects: Vec<Object>, pub objects: Vec<Object>,
object_map: HashMap<String, usize>, // map Name -> Index
// Assembly list // Assembly list
pub assemblies: Vec<Assembly>, pub assemblies: Vec<Assembly>,
assembly_map: HashMap<String, usize>, // map Name -> Index
// Object accel // Object accel
pub object_accel: BVH, pub object_accel: BVH,
} }
impl Assembly { impl Boundable for Assembly {
pub fn new() -> Assembly { fn bounds<'a>(&'a self) -> &'a [BBox] {
Assembly { self.object_accel.bounds()
}
}
#[derive(Debug)]
pub struct AssemblyBuilder {
// Instance list
instances: Vec<Instance>,
xforms: Vec<Matrix4x4>,
// Object list
objects: Vec<Object>,
object_map: HashMap<String, usize>, // map Name -> Index
// Assembly list
assemblies: Vec<Assembly>,
assembly_map: HashMap<String, usize>, // map Name -> Index
}
impl AssemblyBuilder {
pub fn new() -> AssemblyBuilder {
AssemblyBuilder {
instances: Vec::new(), instances: Vec::new(),
xforms: Vec::new(), xforms: Vec::new(),
objects: Vec::new(), objects: Vec::new(),
object_map: HashMap::new(), object_map: HashMap::new(),
assemblies: Vec::new(), assemblies: Vec::new(),
assembly_map: HashMap::new(), assembly_map: HashMap::new(),
object_accel: BVH::new_empty(),
} }
} }
pub fn add_object(&mut self, name: &str, obj: Object) { pub fn add_object(&mut self, name: &str, obj: Object) {
self.object_map.insert(name.to_string(), self.objects.len()); self.object_map.insert(name.to_string(), self.objects.len());
self.objects.push(obj); self.objects.push(obj);
@ -81,31 +99,29 @@ impl Assembly {
} }
} }
pub fn finalize(&mut self) { pub fn build(mut self) -> Assembly {
// 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. // Shrink storage to minimum.
// However, don't shrink shader storage, because there are pointers to // However, don't shrink shader storage, because there are pointers to
// that data that could get invalidated. // that data that could get invalidated.
self.instances.shrink_to_fit(); self.instances.shrink_to_fit();
self.xforms.shrink_to_fit(); self.xforms.shrink_to_fit();
self.objects.shrink_to_fit(); self.objects.shrink_to_fit();
self.object_map.shrink_to_fit();
self.assemblies.shrink_to_fit(); self.assemblies.shrink_to_fit();
self.assembly_map.shrink_to_fit();
// Build object accel // Build object accel
let (bis, bbs) = self.instance_bounds(); let (bis, bbs) = self.instance_bounds();
println!("Len: {}, {}", bis.len(), bbs.len()); let object_accel = BVH::from_objects(&mut self.instances[..],
self.object_accel = BVH::from_objects(&mut self.instances[..], 1,
1, |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
}
Assembly {
instances: self.instances,
xforms: self.xforms,
objects: self.objects,
assemblies: self.assemblies,
object_accel: object_accel,
}
}
/// Returns a pair of vectors with the bounds of all instances. /// Returns a pair of vectors with the bounds of all instances.
@ -121,16 +137,17 @@ impl Assembly {
// Get bounding boxes // Get bounding boxes
match inst.instance_type { match inst.instance_type {
InstanceType::Object => { InstanceType::Object => {
// Push bounds onto bbs
let obj = &self.objects[inst.data_index]; let obj = &self.objects[inst.data_index];
// TODO: push bounds onto bbs
match obj { match obj {
&Object::Surface(ref s) => bbs.extend(s.bounds()), &Object::Surface(ref s) => bbs.extend(s.bounds()),
} }
} }
InstanceType::Assembly => { InstanceType::Assembly => {
// Push bounds onto bbs
let asmb = &self.assemblies[inst.data_index]; let asmb = &self.assemblies[inst.data_index];
// TODO: push bounds onto bbs bbs.extend(asmb.bounds());
} }
} }
@ -153,6 +170,7 @@ impl Assembly {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Object { pub enum Object {
Surface(Box<Surface>), Surface(Box<Surface>),

View File

@ -33,7 +33,7 @@ use math::{Point, Matrix4x4};
use ray::Ray; use ray::Ray;
use camera::Camera; use camera::Camera;
use scene::Scene; use scene::Scene;
use assembly::{Assembly, Object}; use assembly::{AssemblyBuilder, Object};
use renderer::Renderer; use renderer::Renderer;
use surface::triangle_mesh::TriangleMesh; use surface::triangle_mesh::TriangleMesh;
use parse::DataTree; use parse::DataTree;
@ -132,11 +132,11 @@ fn main() {
vec![20.0], vec![20.0],
vec![1026.0]); vec![1026.0]);
let mut assembly = Assembly::new(); let mut assembly_b = AssemblyBuilder::new();
assembly.add_object("yar", Object::Surface(Box::new(mesh))); assembly_b.add_object("yar", Object::Surface(Box::new(mesh)));
assembly.add_object_instance("yar", assembly_b.add_object_instance("yar",
Some(&[Matrix4x4::from_location(Point::new(0.0, 0.0, 0.0))])); Some(&[Matrix4x4::from_location(Point::new(25.0, 0.0, 0.0))]));
assembly.finalize(); let assembly = assembly_b.build();
let scene = Scene { let scene = Scene {
name: None, name: None,

View File

@ -4,7 +4,7 @@ use std;
use std::ops::{Index, IndexMut, Mul}; use std::ops::{Index, IndexMut, Mul};
use float4::Float4; use float4::Float4;
use lerp::Lerp; use lerp::{Lerp, lerp_slice};
use super::Point; use super::Point;
@ -248,6 +248,32 @@ impl Lerp for Matrix4x4 {
} }
pub fn multiply_matrix_slices(xforms1: &[Matrix4x4],
xforms2: &[Matrix4x4],
xforms_out: &mut Vec<Matrix4x4>) {
xforms_out.clear();
// Transform the bounding boxes
if xforms1.len() == 0 || xforms2.len() == 0 {
return;
} else if xforms1.len() == xforms2.len() {
for (xf1, xf2) in Iterator::zip(xforms1.iter(), xforms2.iter()) {
xforms_out.push(*xf1 * *xf2);
}
} else if xforms1.len() > xforms2.len() {
let s = (xforms1.len() - 1) as f32;
for (i, xf) in xforms1.iter().enumerate() {
xforms_out.push(*xf * lerp_slice(xforms2, i as f32 / s));
}
} else if xforms1.len() < xforms2.len() {
let s = (xforms2.len() - 1) as f32;
for (i, xf) in xforms2.iter().enumerate() {
xforms_out.push(lerp_slice(xforms1, i as f32 / s) * *xf);
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -8,7 +8,7 @@ mod matrix;
pub use self::vector::Vector; pub use self::vector::Vector;
pub use self::normal::Normal; pub use self::normal::Normal;
pub use self::point::Point; pub use self::point::Point;
pub use self::matrix::Matrix4x4; pub use self::matrix::{Matrix4x4, multiply_matrix_slices};
/// Trait for calculating dot products. /// Trait for calculating dot products.
pub trait DotProduct pub trait DotProduct

View File

@ -2,7 +2,8 @@ use std::iter;
use std::slice; use std::slice;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use math::Matrix4x4; use math::{Matrix4x4, multiply_matrix_slices};
use lerp::lerp_slice;
use assembly::{Assembly, Object, Instance, InstanceType}; use assembly::{Assembly, Object, Instance, InstanceType};
use ray::Ray; use ray::Ray;
use surface::SurfaceIntersection; use surface::SurfaceIntersection;
@ -11,6 +12,7 @@ pub struct Tracer<'a> {
root: &'a Assembly, root: &'a Assembly,
rays: UnsafeCell<Vec<Ray>>, // Should only be used from trace(), not any other methods rays: UnsafeCell<Vec<Ray>>, // Should only be used from trace(), not any other methods
xform_stack: Vec<Matrix4x4>, xform_stack: Vec<Matrix4x4>,
xform_stack_indices: Vec<usize>,
isects: Vec<SurfaceIntersection>, isects: Vec<SurfaceIntersection>,
} }
@ -18,8 +20,9 @@ impl<'a> Tracer<'a> {
pub fn from_assembly(assembly: &'a Assembly) -> Tracer<'a> { pub fn from_assembly(assembly: &'a Assembly) -> Tracer<'a> {
Tracer { Tracer {
root: assembly, root: assembly,
xform_stack: Vec::new(),
rays: UnsafeCell::new(Vec::new()), rays: UnsafeCell::new(Vec::new()),
xform_stack: Vec::new(),
xform_stack_indices: vec![0],
isects: Vec::new(), isects: Vec::new(),
} }
} }
@ -57,7 +60,35 @@ impl<'a> Tracer<'a> {
fn trace_assembly<'b>(&'b mut self, assembly: &Assembly, wrays: &[Ray], rays: &mut [Ray]) { fn trace_assembly<'b>(&'b mut self, assembly: &Assembly, wrays: &[Ray], rays: &mut [Ray]) {
assembly.object_accel.traverse(&mut rays[..], &assembly.instances[..], |inst, rs| { assembly.object_accel.traverse(&mut rays[..], &assembly.instances[..], |inst, rs| {
// TODO: transform rays // Transform rays if needed
if let Some((xstart, xend)) = inst.transform_indices {
// Push transforms to stack
let mut combined = Vec::new();
if self.xform_stack.len() == 0 {
self.xform_stack.extend(&assembly.xforms[xstart..xend]);
} else {
let x2start = self.xform_stack_indices[self.xform_stack_indices.len() - 2];
let x2end = self.xform_stack_indices[self.xform_stack_indices.len() - 1];
multiply_matrix_slices(&self.xform_stack[x2start..x2end],
&assembly.xforms[xstart..xend],
&mut combined);
self.xform_stack.extend(&combined);
}
self.xform_stack_indices.push(self.xform_stack.len());
// Do transforms
let xstart = self.xform_stack_indices[self.xform_stack_indices.len() - 2];
let xend = self.xform_stack_indices[self.xform_stack_indices.len() - 1];
let xforms = &self.xform_stack[xstart..xend];
for ray in &mut rs[..] {
let id = ray.id;
let t = ray.time;
*ray = wrays[id as usize];
ray.transform(&lerp_slice(xforms, t));
}
}
// Trace rays
match inst.instance_type { match inst.instance_type {
InstanceType::Object => { InstanceType::Object => {
self.trace_object(&assembly.objects[inst.data_index], wrays, rs); self.trace_object(&assembly.objects[inst.data_index], wrays, rs);
@ -67,6 +98,35 @@ impl<'a> Tracer<'a> {
self.trace_assembly(&assembly.assemblies[inst.data_index], wrays, rs); self.trace_assembly(&assembly.assemblies[inst.data_index], wrays, rs);
} }
} }
// Un-transform rays if needed
if let Some(_) = inst.transform_indices {
// Pop transforms off stack
let xstart = self.xform_stack_indices[self.xform_stack_indices.len() - 2];
let xend = self.xform_stack_indices[self.xform_stack_indices.len() - 1];
let l = self.xform_stack.len();
self.xform_stack.resize(l - (xend - xstart), Matrix4x4::new());
self.xform_stack_indices.pop();
// Undo transforms
if self.xform_stack.len() > 0 {
let xstart = self.xform_stack_indices[self.xform_stack_indices.len() - 2];
let xend = self.xform_stack_indices[self.xform_stack_indices.len() - 1];
let xforms = &self.xform_stack[xstart..xend];
for ray in &mut rs[..] {
let id = ray.id;
let t = ray.time;
*ray = wrays[id as usize];
ray.transform(&lerp_slice(xforms, t));
}
} else {
for ray in &mut rs[..] {
let id = ray.id;
let t = ray.time;
*ray = wrays[id as usize];
}
}
}
}); });
} }

View File

@ -1,4 +1,2 @@
- Implement a Tracer to handle ray tracing responsibilities, with hierarchical
instancing, etc.
- Basic scene parsing with triangle meshes. - Basic scene parsing with triangle meshes.
- Unit tests for scene parsing. - Unit tests for scene parsing.