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
pub objects: Vec<Object>,
object_map: HashMap<String, usize>, // map Name -> Index
// Assembly list
pub assemblies: Vec<Assembly>,
assembly_map: HashMap<String, usize>, // map Name -> Index
// Object accel
pub object_accel: BVH,
}
impl Assembly {
pub fn new() -> Assembly {
Assembly {
impl Boundable for Assembly {
fn bounds<'a>(&'a self) -> &'a [BBox] {
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(),
xforms: Vec::new(),
objects: Vec::new(),
object_map: HashMap::new(),
assemblies: Vec::new(),
assembly_map: HashMap::new(),
object_accel: BVH::new_empty(),
}
}
pub fn add_object(&mut self, name: &str, obj: Object) {
self.object_map.insert(name.to_string(), self.objects.len());
self.objects.push(obj);
@ -81,31 +99,29 @@ impl Assembly {
}
}
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();
pub fn build(mut self) -> Assembly {
// 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[..],
let object_accel = BVH::from_objects(&mut self.instances[..],
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.
@ -121,16 +137,17 @@ impl Assembly {
// Get bounding boxes
match inst.instance_type {
InstanceType::Object => {
// Push bounds onto bbs
let obj = &self.objects[inst.data_index];
// TODO: push bounds onto bbs
match obj {
&Object::Surface(ref s) => bbs.extend(s.bounds()),
}
}
InstanceType::Assembly => {
// Push bounds onto bbs
let asmb = &self.assemblies[inst.data_index];
// TODO: push bounds onto bbs
bbs.extend(asmb.bounds());
}
}
@ -153,6 +170,7 @@ impl Assembly {
}
#[derive(Debug)]
pub enum Object {
Surface(Box<Surface>),

View File

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

View File

@ -4,7 +4,7 @@ use std;
use std::ops::{Index, IndexMut, Mul};
use float4::Float4;
use lerp::Lerp;
use lerp::{Lerp, lerp_slice};
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)]
mod tests {

View File

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

View File

@ -2,7 +2,8 @@ use std::iter;
use std::slice;
use std::cell::UnsafeCell;
use math::Matrix4x4;
use math::{Matrix4x4, multiply_matrix_slices};
use lerp::lerp_slice;
use assembly::{Assembly, Object, Instance, InstanceType};
use ray::Ray;
use surface::SurfaceIntersection;
@ -11,6 +12,7 @@ pub struct Tracer<'a> {
root: &'a Assembly,
rays: UnsafeCell<Vec<Ray>>, // Should only be used from trace(), not any other methods
xform_stack: Vec<Matrix4x4>,
xform_stack_indices: Vec<usize>,
isects: Vec<SurfaceIntersection>,
}
@ -18,8 +20,9 @@ impl<'a> Tracer<'a> {
pub fn from_assembly(assembly: &'a Assembly) -> Tracer<'a> {
Tracer {
root: assembly,
xform_stack: Vec::new(),
rays: UnsafeCell::new(Vec::new()),
xform_stack: Vec::new(),
xform_stack_indices: vec![0],
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]) {
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 {
InstanceType::Object => {
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);
}
}
// 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.
- Unit tests for scene parsing.