Reformat code with rustfmt 0.9

This commit is contained in:
Nathan Vegdahl 2017-06-15 22:00:31 -07:00
parent 46247ec9aa
commit f649bec585
32 changed files with 1257 additions and 926 deletions

View File

@ -1,6 +0,0 @@
max_width = 1024
error_on_line_overflow = false
array_layout = "Block"
chain_indent = "Block"
fn_args_layout = "Block"
fn_call_style = "Block"

View File

@ -38,8 +38,14 @@ pub enum BVHNode<'a> {
} }
impl<'a> BVH<'a> { impl<'a> BVH<'a> {
pub fn from_objects<'b, T, F>(arena: &'a MemArena, objects: &mut [T], objects_per_leaf: usize, bounder: F) -> BVH<'a> pub fn from_objects<'b, T, F>(
where F: 'b + Fn(&T) -> &'b [BBox] arena: &'a MemArena,
objects: &mut [T],
objects_per_leaf: usize,
bounder: F,
) -> BVH<'a>
where
F: 'b + Fn(&T) -> &'b [BBox],
{ {
if objects.len() == 0 { if objects.len() == 0 {
BVH { BVH {
@ -50,7 +56,11 @@ impl<'a> BVH<'a> {
let base = BVHBase::from_objects(objects, objects_per_leaf, bounder); let base = BVHBase::from_objects(objects, objects_per_leaf, bounder);
BVH { BVH {
root: Some(BVH::construct_from_base(arena, &base, base.root_node_index())), root: Some(BVH::construct_from_base(
arena,
&base,
base.root_node_index(),
)),
depth: base.depth, depth: base.depth,
} }
} }
@ -61,7 +71,8 @@ impl<'a> BVH<'a> {
} }
pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F) pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F)
where F: FnMut(&T, &mut [AccelRay]) where
F: FnMut(&T, &mut [AccelRay]),
{ {
if self.root.is_none() { if self.root.is_none() {
return; return;
@ -89,11 +100,11 @@ impl<'a> BVH<'a> {
bounds_len, bounds_len,
split_axis, split_axis,
} => { } => {
let bounds = unsafe { std::slice::from_raw_parts(bounds_start, bounds_len as usize) }; let bounds =
let part = partition( unsafe { std::slice::from_raw_parts(bounds_start, bounds_len as usize) };
&mut rays[..ray_i_stack[stack_ptr]], let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
|r| (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r), (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r)
); });
if part > 0 { if part > 0 {
ray_i_stack[stack_ptr] = part; ray_i_stack[stack_ptr] = part;
ray_i_stack[stack_ptr + 1] = part; ray_i_stack[stack_ptr + 1] = part;
@ -115,11 +126,11 @@ impl<'a> BVH<'a> {
bounds_start, bounds_start,
bounds_len, bounds_len,
} => { } => {
let bounds = unsafe { std::slice::from_raw_parts(bounds_start, bounds_len as usize) }; let bounds =
let part = partition( unsafe { std::slice::from_raw_parts(bounds_start, bounds_len as usize) };
&mut rays[..ray_i_stack[stack_ptr]], let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
|r| (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r), (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r)
); });
trav_time += timer.tick() as f64; trav_time += timer.tick() as f64;
@ -137,15 +148,17 @@ impl<'a> BVH<'a> {
} }
trav_time += timer.tick() as f64; trav_time += timer.tick() as f64;
ACCEL_TRAV_TIME.with( ACCEL_TRAV_TIME.with(|att| {
|att| {
let v = att.get(); let v = att.get();
att.set(v + trav_time); att.set(v + trav_time);
} });
);
} }
fn construct_from_base(arena: &'a MemArena, base: &BVHBase, node_index: usize) -> &'a mut BVHNode<'a> { fn construct_from_base(
arena: &'a MemArena,
base: &BVHBase,
node_index: usize,
) -> &'a mut BVHNode<'a> {
match &base.nodes[node_index] { match &base.nodes[node_index] {
&BVHBaseNode::Internal { &BVHBaseNode::Internal {
bounds_range, bounds_range,
@ -154,7 +167,10 @@ impl<'a> BVH<'a> {
} => { } => {
let mut node = unsafe { arena.alloc_uninitialized_with_alignment::<BVHNode>(32) }; let mut node = unsafe { arena.alloc_uninitialized_with_alignment::<BVHNode>(32) };
let bounds = arena.copy_slice_with_alignment(&base.bounds[bounds_range.0..bounds_range.1], 32); let bounds = arena.copy_slice_with_alignment(
&base.bounds[bounds_range.0..bounds_range.1],
32,
);
let child1 = BVH::construct_from_base(arena, base, children_indices.0); let child1 = BVH::construct_from_base(arena, base, children_indices.0);
let child2 = BVH::construct_from_base(arena, base, children_indices.1); let child2 = BVH::construct_from_base(arena, base, children_indices.1);

View File

@ -58,7 +58,8 @@ impl BVHBase {
} }
pub fn from_objects<'b, T, F>(objects: &mut [T], objects_per_leaf: usize, bounder: F) -> BVHBase pub fn from_objects<'b, T, F>(objects: &mut [T], objects_per_leaf: usize, bounder: F) -> BVHBase
where F: 'b + Fn(&T) -> &'b [BBox] where
F: 'b + Fn(&T) -> &'b [BBox],
{ {
let mut bvh = BVHBase::new(); let mut bvh = BVHBase::new();
bvh.recursive_build(0, 0, objects_per_leaf, objects, &bounder); bvh.recursive_build(0, 0, objects_per_leaf, objects, &bounder);
@ -70,7 +71,8 @@ impl BVHBase {
} }
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],
{ {
// TODO: do all of this without the temporary cache // TODO: do all of this without the temporary cache
let max_len = objects.iter().map(|obj| bounder(obj).len()).max().unwrap(); let max_len = objects.iter().map(|obj| bounder(obj).len()).max().unwrap();
@ -94,8 +96,16 @@ impl BVHBase {
} }
} }
fn recursive_build<'a, T, F>(&mut self, offset: usize, depth: usize, objects_per_leaf: usize, objects: &mut [T], bounder: &F) -> (usize, (usize, usize)) fn recursive_build<'a, T, F>(
where F: 'a + Fn(&T) -> &'a [BBox] &mut self,
offset: usize,
depth: usize,
objects_per_leaf: usize,
objects: &mut [T],
bounder: &F,
) -> (usize, (usize, usize))
where
F: 'a + Fn(&T) -> &'a [BBox],
{ {
let me = self.nodes.len(); let me = self.nodes.len();
@ -109,12 +119,13 @@ impl BVHBase {
// We make sure that it's worth having multiple time samples, and if not // We make sure that it's worth having multiple time samples, and if not
// we reduce to the union of the time samples. // we reduce to the union of the time samples.
self.acc_bounds(objects, bounder); self.acc_bounds(objects, bounder);
let union_bounds = self.bounds_cache let union_bounds = self.bounds_cache.iter().fold(
.iter() BBox::new(),
.fold(BBox::new(), |b1, b2| (b1 | *b2)); |b1, b2| (b1 | *b2),
let average_area = self.bounds_cache );
.iter() let average_area = self.bounds_cache.iter().fold(0.0, |area, bb| {
.fold(0.0, |area, bb| area + bb.surface_area()) / self.bounds_cache.len() as f32; area + bb.surface_area()
}) / self.bounds_cache.len() as f32;
if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) { if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) {
self.bounds.push(union_bounds); self.bounds.push(union_bounds);
} else { } else {
@ -123,13 +134,10 @@ impl BVHBase {
} }
// Create node // Create node
self.nodes self.nodes.push(BVHBaseNode::Leaf {
.push(
BVHBaseNode::Leaf {
bounds_range: (bi, self.bounds.len()), bounds_range: (bi, self.bounds.len()),
object_range: (offset, offset + objects.len()), object_range: (offset, offset + objects.len()),
} });
);
if self.depth < depth { if self.depth < depth {
self.depth = depth; self.depth = depth;
@ -138,20 +146,18 @@ impl BVHBase {
return (me, (bi, self.bounds.len())); return (me, (bi, self.bounds.len()));
} else { } else {
// Not a leaf node // Not a leaf node
self.nodes self.nodes.push(BVHBaseNode::Internal {
.push(
BVHBaseNode::Internal {
bounds_range: (0, 0), bounds_range: (0, 0),
children_indices: (0, 0), children_indices: (0, 0),
split_axis: 0, split_axis: 0,
} });
);
// Partition objects. // Partition objects.
// If we're too near the max depth, we do balanced building to // If we're too near the max depth, we do balanced building to
// avoid exceeding max depth. // avoid exceeding max depth.
// Otherwise we do SAH splitting to build better trees. // Otherwise we do SAH splitting to build better trees.
let (split_index, split_axis) = if (log2_64(objects.len() as u64) as usize) < (BVH_MAX_DEPTH - depth) { let (split_index, split_axis) =
if (log2_64(objects.len() as u64) as usize) < (BVH_MAX_DEPTH - depth) {
// SAH splitting, when we have room to play // SAH splitting, when we have room to play
sah_split(objects, &bounder) sah_split(objects, &bounder)
} else { } else {
@ -189,7 +195,8 @@ impl BVHBase {
// We make sure that it's worth having multiple time samples, and if not // We make sure that it's worth having multiple time samples, and if not
// we reduce to the union of the time samples. // we reduce to the union of the time samples.
let union_bounds = merged.iter().fold(BBox::new(), |b1, b2| (b1 | *b2)); let union_bounds = merged.iter().fold(BBox::new(), |b1, b2| (b1 | *b2));
let average_area = merged.iter().fold(0.0, |area, bb| area + bb.surface_area()) / merged.len() as f32; let average_area = merged.iter().fold(0.0, |area, bb| area + bb.surface_area()) /
merged.len() as f32;
if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) { if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) {
self.bounds.push(union_bounds); self.bounds.push(union_bounds);
} else { } else {

View File

@ -13,7 +13,8 @@ pub struct LightArray {
impl LightArray { impl LightArray {
#[allow(dead_code)] #[allow(dead_code)]
pub fn new<'a, T, F>(things: &mut [T], q: F) -> LightArray pub fn new<'a, T, F>(things: &mut [T], q: F) -> LightArray
where F: 'a + Fn(&T) -> Option<(&'a [BBox], f32)> where
F: 'a + Fn(&T) -> Option<(&'a [BBox], f32)>,
{ {
let mut indices = Vec::new(); let mut indices = Vec::new();
let mut aprx_energy = 0.0; let mut aprx_energy = 0.0;
@ -34,7 +35,15 @@ impl LightArray {
} }
impl LightAccel for LightArray { impl LightAccel for LightArray {
fn select(&self, inc: Vector, pos: Point, nor: Normal, sc: &SurfaceClosure, time: f32, n: f32) -> Option<(usize, f32, f32)> { fn select(
&self,
inc: Vector,
pos: Point,
nor: Normal,
sc: &SurfaceClosure,
time: f32,
n: f32,
) -> Option<(usize, f32, f32)> {
let _ = (inc, pos, nor, sc, time); // Not using these, silence warnings let _ = (inc, pos, nor, sc, time); // Not using these, silence warnings
assert!(n >= 0.0 && n <= 1.0); assert!(n >= 0.0 && n <= 1.0);

View File

@ -26,8 +26,13 @@ struct Node {
} }
impl<'a> LightTree<'a> { impl<'a> LightTree<'a> {
pub fn from_objects<'b, T, F>(arena: &'a MemArena, objects: &mut [T], info_getter: F) -> LightTree<'a> pub fn from_objects<'b, T, F>(
where F: 'b + Fn(&T) -> (&'b [BBox], f32) arena: &'a MemArena,
objects: &mut [T],
info_getter: F,
) -> LightTree<'a>
where
F: 'b + Fn(&T) -> (&'b [BBox], f32),
{ {
let mut builder = LightTreeBuilder::new(); let mut builder = LightTreeBuilder::new();
builder.recursive_build(0, 0, objects, &info_getter); builder.recursive_build(0, 0, objects, &info_getter);
@ -42,7 +47,15 @@ impl<'a> LightTree<'a> {
impl<'a> LightAccel for LightTree<'a> { impl<'a> LightAccel for LightTree<'a> {
fn select(&self, inc: Vector, pos: Point, nor: Normal, sc: &SurfaceClosure, time: f32, n: f32) -> Option<(usize, f32, f32)> { fn select(
&self,
inc: Vector,
pos: Point,
nor: Normal,
sc: &SurfaceClosure,
time: f32,
n: f32,
) -> Option<(usize, f32, f32)> {
if self.nodes.len() == 0 { if self.nodes.len() == 0 {
return None; return None;
} }
@ -141,8 +154,15 @@ impl LightTreeBuilder {
} }
} }
fn recursive_build<'a, T, F>(&mut self, offset: usize, depth: usize, objects: &mut [T], info_getter: &F) -> (usize, (usize, usize)) fn recursive_build<'a, T, F>(
where F: 'a + Fn(&T) -> (&'a [BBox], f32) &mut self,
offset: usize,
depth: usize,
objects: &mut [T],
info_getter: &F,
) -> (usize, (usize, usize))
where
F: 'a + Fn(&T) -> (&'a [BBox], f32),
{ {
let me_index = self.nodes.len(); let me_index = self.nodes.len();
@ -153,15 +173,12 @@ impl LightTreeBuilder {
let bi = self.bounds.len(); let bi = self.bounds.len();
let (obj_bounds, energy) = info_getter(&objects[0]); let (obj_bounds, energy) = info_getter(&objects[0]);
self.bounds.extend(obj_bounds); self.bounds.extend(obj_bounds);
self.nodes self.nodes.push(Node {
.push(
Node {
is_leaf: true, is_leaf: true,
bounds_range: (bi, self.bounds.len()), bounds_range: (bi, self.bounds.len()),
energy: energy, energy: energy,
child_index: offset, child_index: offset,
} });
);
if self.depth < depth { if self.depth < depth {
self.depth = depth; self.depth = depth;
@ -170,21 +187,19 @@ impl LightTreeBuilder {
return (me_index, (bi, self.bounds.len())); return (me_index, (bi, self.bounds.len()));
} else { } else {
// Not a leaf node // Not a leaf node
self.nodes self.nodes.push(Node {
.push(
Node {
is_leaf: false, is_leaf: false,
bounds_range: (0, 0), bounds_range: (0, 0),
energy: 0.0, energy: 0.0,
child_index: 0, child_index: 0,
} });
);
// Partition objects. // Partition objects.
let (split_index, _) = sah_split(objects, &|obj_ref| info_getter(obj_ref).0); let (split_index, _) = sah_split(objects, &|obj_ref| info_getter(obj_ref).0);
// Create child nodes // Create child nodes
let (_, c1_bounds) = self.recursive_build(offset, depth + 1, &mut objects[..split_index], info_getter); let (_, c1_bounds) =
self.recursive_build(offset, depth + 1, &mut objects[..split_index], info_getter);
let (c2_index, c2_bounds) = self.recursive_build( let (c2_index, c2_bounds) = self.recursive_build(
offset + split_index, offset + split_index,
depth + 1, depth + 1,

View File

@ -19,7 +19,15 @@ thread_local! {
pub trait LightAccel { pub trait LightAccel {
/// Returns (index_of_light, selection_pdf, whittled_n) /// Returns (index_of_light, selection_pdf, whittled_n)
fn select(&self, inc: Vector, pos: Point, nor: Normal, sc: &SurfaceClosure, time: f32, n: f32) -> Option<(usize, f32, f32)>; fn select(
&self,
inc: Vector,
pos: Point,
nor: Normal,
sc: &SurfaceClosure,
time: f32,
n: f32,
) -> Option<(usize, f32, f32)>;
fn approximate_energy(&self) -> f32; fn approximate_energy(&self) -> f32;
} }

View File

@ -22,7 +22,8 @@ const SPLIT_PLANE_COUNT: usize = 5;
/// Returns the index of the partition boundary and the axis that it split on /// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z). /// (0 = x, 1 = y, 2 = z).
pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (usize, usize) pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (usize, usize)
where F: Fn(&T) -> &'a [BBox] where
F: Fn(&T) -> &'a [BBox],
{ {
// Generate the planes for splitting // Generate the planes for splitting
let planes = { let planes = {
@ -65,7 +66,8 @@ pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (u
// Build SAH bins // Build SAH bins
let sah_bins = { let sah_bins = {
let mut sah_bins = [[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1]; SPLIT_PLANE_COUNT]; let mut sah_bins = [[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1];
SPLIT_PLANE_COUNT];
for obj in objects.iter() { for obj in objects.iter() {
let tb = lerp_slice(bounder(obj), 0.5); let tb = lerp_slice(bounder(obj), 0.5);
let centroid = tb.center().into_vector(); let centroid = tb.center().into_vector();
@ -131,13 +133,11 @@ pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (u
}; };
// Partition // Partition
let mut split_i = partition( let mut split_i = partition(&mut objects[..], |obj| {
&mut objects[..], |obj| {
let centroid = lerp_slice(bounder(obj), 0.5).center().into_vector(); let centroid = lerp_slice(bounder(obj), 0.5).center().into_vector();
let dist = dot(centroid, plane); let dist = dot(centroid, plane);
dist < div dist < div
} });
);
if split_i < 1 { if split_i < 1 {
split_i = 1; split_i = 1;
@ -155,7 +155,8 @@ pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (u
/// Returns the index of the partition boundary and the axis that it split on /// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z). /// (0 = x, 1 = y, 2 = z).
pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize) pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
where F: Fn(&T) -> &'a [BBox] where
F: Fn(&T) -> &'a [BBox],
{ {
// Get combined object centroid extents // Get combined object centroid extents
let bounds = { let bounds = {
@ -224,13 +225,11 @@ pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
}; };
// Partition // Partition
let mut split_i = partition( let mut split_i = partition(&mut objects[..], |obj| {
&mut objects[..], |obj| {
let tb = lerp_slice(bounder(obj), 0.5); let tb = lerp_slice(bounder(obj), 0.5);
let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5; let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5;
centroid < div centroid < div
} });
);
if split_i < 1 { if split_i < 1 {
split_i = 1; split_i = 1;
} else if split_i >= objects.len() { } else if split_i >= objects.len() {
@ -245,7 +244,8 @@ pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
/// Returns the index of the partition boundary and the axis that it split on /// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z). /// (0 = x, 1 = y, 2 = z).
pub fn bounds_mean_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize) pub fn bounds_mean_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
where F: Fn(&T) -> &'a [BBox] where
F: Fn(&T) -> &'a [BBox],
{ {
// Get combined object bounds // Get combined object bounds
let bounds = { let bounds = {
@ -272,13 +272,11 @@ pub fn bounds_mean_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, us
let div = (bounds.min.get_n(split_axis) + bounds.max.get_n(split_axis)) * 0.5; let div = (bounds.min.get_n(split_axis) + bounds.max.get_n(split_axis)) * 0.5;
// Partition // Partition
let mut split_i = partition( let mut split_i = partition(&mut objects[..], |obj| {
&mut objects[..], |obj| {
let tb = lerp_slice(bounder(obj), 0.5); let tb = lerp_slice(bounder(obj), 0.5);
let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5; let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5;
centroid < div centroid < div
} });
);
if split_i < 1 { if split_i < 1 {
split_i = 1; split_i = 1;
} else if split_i >= objects.len() { } else if split_i >= objects.len() {
@ -294,7 +292,8 @@ pub fn bounds_mean_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, us
/// Returns the index of the partition boundary and the axis that it split on /// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z). /// (0 = x, 1 = y, 2 = z).
pub fn median_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize) pub fn median_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
where F: Fn(&T) -> &'a [BBox] where
F: Fn(&T) -> &'a [BBox],
{ {
// Get combined object bounds // Get combined object bounds
let bounds = { let bounds = {
@ -322,8 +321,7 @@ pub fn median_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
let place = objects.len() / 2; let place = objects.len() / 2;
if place > 0 { place } else { 1 } if place > 0 { place } else { 1 }
}; };
quick_select( quick_select(objects, place, |a, b| {
objects, place, |a, b| {
let tb_a = lerp_slice(bounder(a), 0.5); let tb_a = lerp_slice(bounder(a), 0.5);
let tb_b = lerp_slice(bounder(b), 0.5); let tb_b = lerp_slice(bounder(b), 0.5);
let centroid_a = (tb_a.min.get_n(split_axis) + tb_a.max.get_n(split_axis)) * 0.5; let centroid_a = (tb_a.min.get_n(split_axis) + tb_a.max.get_n(split_axis)) * 0.5;
@ -336,8 +334,7 @@ pub fn median_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
} else { } else {
Ordering::Greater Ordering::Greater
} }
} });
);
(place, split_axis) (place, split_axis)
} }

View File

@ -13,7 +13,8 @@ use lerp::{Lerp, lerp_slice};
/// item and the probability that it would have been selected with a /// item and the probability that it would have been selected with a
/// random n. /// random n.
pub fn weighted_choice<T, F>(slc: &[T], n: f32, weight: F) -> (usize, f32) pub fn weighted_choice<T, F>(slc: &[T], n: f32, weight: F) -> (usize, f32)
where F: Fn(&T) -> f32 where
F: Fn(&T) -> f32,
{ {
assert!(slc.len() > 0); assert!(slc.len() > 0);
@ -40,7 +41,8 @@ pub fn weighted_choice<T, F>(slc: &[T], n: f32, weight: F) -> (usize, f32)
/// The predicate is executed precisely once on every element in /// The predicate is executed precisely once on every element in
/// the slice, and is allowed to modify the elements. /// the slice, and is allowed to modify the elements.
pub fn partition<T, F>(slc: &mut [T], mut pred: F) -> usize pub fn partition<T, F>(slc: &mut [T], mut pred: F) -> usize
where F: FnMut(&mut T) -> bool where
F: FnMut(&mut T) -> bool,
{ {
// This version uses raw pointers and pointer arithmetic to squeeze more // This version uses raw pointers and pointer arithmetic to squeeze more
// performance out of the code. // performance out of the code.
@ -89,7 +91,8 @@ pub fn partition<T, F>(slc: &mut [T], mut pred: F) -> usize
/// of the array we're currently on: left or right. False means left, /// of the array we're currently on: left or right. False means left,
/// True means right. /// True means right.
pub fn partition_with_side<T, F>(slc: &mut [T], mut pred: F) -> usize pub fn partition_with_side<T, F>(slc: &mut [T], mut pred: F) -> usize
where F: FnMut(&mut T, bool) -> bool where
F: FnMut(&mut T, bool) -> bool,
{ {
// This version uses raw pointers and pointer arithmetic to squeeze more // This version uses raw pointers and pointer arithmetic to squeeze more
// performance out of the code. // performance out of the code.
@ -141,7 +144,8 @@ pub fn partition_with_side<T, F>(slc: &mut [T], mut pred: F) -> usize
/// The predicate is executed precisely once on every element in /// The predicate is executed precisely once on every element in
/// the slices, and is allowed to modify the elements. /// the slices, and is allowed to modify the elements.
pub fn partition_pair<A, B, F>(slc1: &mut [A], slc2: &mut [B], mut pred: F) -> usize pub fn partition_pair<A, B, F>(slc1: &mut [A], slc2: &mut [B], mut pred: F) -> usize
where F: FnMut(usize, &mut A, &mut B) -> bool where
F: FnMut(usize, &mut A, &mut B) -> bool,
{ {
assert!(slc1.len() == slc2.len()); assert!(slc1.len() == slc2.len());
@ -163,7 +167,8 @@ pub fn partition_pair<A, B, F>(slc1: &mut [A], slc2: &mut [B], mut pred: F) -> u
((a1 as usize) - start) / std::mem::size_of::<A>(), ((a1 as usize) - start) / std::mem::size_of::<A>(),
&mut *a1, &mut *a1,
&mut *a2, &mut *a2,
) { )
{
break; break;
} }
a1 = a1.offset(1); a1 = a1.offset(1);
@ -180,7 +185,8 @@ pub fn partition_pair<A, B, F>(slc1: &mut [A], slc2: &mut [B], mut pred: F) -> u
((b1 as usize) - start) / std::mem::size_of::<A>(), ((b1 as usize) - start) / std::mem::size_of::<A>(),
&mut *b1, &mut *b1,
&mut *b2, &mut *b2,
) { )
{
break; break;
} }
} }
@ -197,7 +203,8 @@ pub fn partition_pair<A, B, F>(slc1: &mut [A], slc2: &mut [B], mut pred: F) -> u
/// Partitions the slice of items to place the nth-ordered item in the nth place, /// Partitions the slice of items to place the nth-ordered item in the nth place,
/// and the items less than it before and the items more than it after. /// and the items less than it before and the items more than it after.
pub fn quick_select<T, F>(slc: &mut [T], n: usize, mut order: F) pub fn quick_select<T, F>(slc: &mut [T], n: usize, mut order: F)
where F: FnMut(&T, &T) -> Ordering where
F: FnMut(&T, &T) -> Ordering,
{ {
let mut left = 0; let mut left = 0;
let mut right = slc.len(); let mut right = slc.len();
@ -227,8 +234,13 @@ pub fn quick_select<T, F>(slc: &mut [T], n: usize, mut order: F)
} }
/// Merges two slices of things, appending the result to vec_out /// Merges two slices of things, appending the result to vec_out
pub fn merge_slices_append<T: Lerp + Copy, F>(slice1: &[T], slice2: &[T], vec_out: &mut Vec<T>, merge: F) pub fn merge_slices_append<T: Lerp + Copy, F>(
where F: Fn(&T, &T) -> T slice1: &[T],
slice2: &[T],
vec_out: &mut Vec<T>,
merge: F,
) where
F: Fn(&T, &T) -> T,
{ {
// Transform the bounding boxes // Transform the bounding boxes
if slice1.len() == 0 || slice2.len() == 0 { if slice1.len() == 0 || slice2.len() == 0 {
@ -255,7 +267,8 @@ pub fn merge_slices_append<T: Lerp + Copy, F>(slice1: &[T], slice2: &[T], vec_ou
/// Merges two slices of things, storing the result in slice_out. /// Merges two slices of things, storing the result in slice_out.
/// Panics if slice_out is not the right size. /// Panics if slice_out is not the right size.
pub fn merge_slices_to<T: Lerp + Copy, F>(slice1: &[T], slice2: &[T], slice_out: &mut [T], merge: F) pub fn merge_slices_to<T: Lerp + Copy, F>(slice1: &[T], slice2: &[T], slice_out: &mut [T], merge: F)
where F: Fn(&T, &T) -> T where
F: Fn(&T, &T) -> T,
{ {
assert!(slice_out.len() == cmp::max(slice1.len(), slice2.len())); assert!(slice_out.len() == cmp::max(slice1.len(), slice2.len()));
@ -267,7 +280,8 @@ pub fn merge_slices_to<T: Lerp + Copy, F>(slice1: &[T], slice2: &[T], slice_out:
Iterator::zip( Iterator::zip(
slice_out.iter_mut(), slice_out.iter_mut(),
Iterator::zip(slice1.iter(), slice2.iter()), Iterator::zip(slice1.iter(), slice2.iter()),
) { )
{
*xfo = merge(xf1, xf2); *xfo = merge(xf1, xf2);
} }
} else if slice1.len() > slice2.len() { } else if slice1.len() > slice2.len() {
@ -291,15 +305,13 @@ mod tests {
use super::*; use super::*;
fn quick_select_ints(list: &mut [i32], i: usize) { fn quick_select_ints(list: &mut [i32], i: usize) {
quick_select( quick_select(list, i, |a, b| if a < b {
list, i, |a, b| if a < b {
Ordering::Less Ordering::Less
} else if a == b { } else if a == b {
Ordering::Equal Ordering::Equal
} else { } else {
Ordering::Greater Ordering::Greater
} });
);
} }
#[test] #[test]

View File

@ -18,7 +18,13 @@ pub struct Camera<'a> {
} }
impl<'a> Camera<'a> { impl<'a> Camera<'a> {
pub fn new(arena: &'a MemArena, transforms: Vec<Matrix4x4>, fovs: Vec<f32>, mut aperture_radii: Vec<f32>, mut focus_distances: Vec<f32>) -> Camera<'a> { pub fn new(
arena: &'a MemArena,
transforms: Vec<Matrix4x4>,
fovs: Vec<f32>,
mut aperture_radii: Vec<f32>,
mut focus_distances: Vec<f32>,
) -> Camera<'a> {
assert!(transforms.len() != 0, "Camera has no transform(s)!"); assert!(transforms.len() != 0, "Camera has no transform(s)!");
assert!(fovs.len() != 0, "Camera has no fov(s)!"); assert!(fovs.len() != 0, "Camera has no fov(s)!");
@ -81,8 +87,7 @@ impl<'a> Camera<'a> {
(x * tfov) - (orig.x() / focus_distance), (x * tfov) - (orig.x() / focus_distance),
(y * tfov) - (orig.y() / focus_distance), (y * tfov) - (orig.y() / focus_distance),
1.0, 1.0,
) ).normalized();
.normalized();
Ray::new(orig * transform, dir * transform, time, false) Ray::new(orig * transform, dir * transform, time, false)
} }

View File

@ -278,7 +278,8 @@ pub fn x_1931(wavelength: f32) -> f32 {
let t1 = (wavelength - 442.0) * (if wavelength < 442.0 { 0.0624 } else { 0.0374 }); let t1 = (wavelength - 442.0) * (if wavelength < 442.0 { 0.0624 } else { 0.0374 });
let t2 = (wavelength - 599.8) * (if wavelength < 599.8 { 0.0264 } else { 0.0323 }); let t2 = (wavelength - 599.8) * (if wavelength < 599.8 { 0.0264 } else { 0.0323 });
let t3 = (wavelength - 501.1) * (if wavelength < 501.1 { 0.0490 } else { 0.0382 }); let t3 = (wavelength - 501.1) * (if wavelength < 501.1 { 0.0490 } else { 0.0382 });
(0.362 * faster_exp(-0.5 * t1 * t1)) + (1.056 * faster_exp(-0.5 * t2 * t2)) - (0.065 * faster_exp(-0.5 * t3 * t3)) (0.362 * faster_exp(-0.5 * t1 * t1)) + (1.056 * faster_exp(-0.5 * t2 * t2)) -
(0.065 * faster_exp(-0.5 * t3 * t3))
} }
pub fn y_1931(wavelength: f32) -> f32 { pub fn y_1931(wavelength: f32) -> f32 {

View File

@ -77,7 +77,10 @@ impl Image {
} }
// Clip bucket to image // Clip bucket to image
let max = (cmp::min(max.0, self.res.0 as u32), cmp::min(max.1, self.res.1 as u32)); let max = (
cmp::min(max.0, self.res.0 as u32),
cmp::min(max.1, self.res.1 as u32),
);
// Push bucket onto list // Push bucket onto list
bucket_list.push((min, max)); bucket_list.push((min, max));
@ -138,7 +141,8 @@ impl Image {
let res_y = self.res.1; let res_y = self.res.1;
for y in 0..res_y { for y in 0..res_y {
for x in 0..res_x { for x in 0..res_x {
let (r, g, b) = quantize_tri_255(xyz_to_srgbe(self.get(x, res_y - 1 - y).to_tuple())); let (r, g, b) =
quantize_tri_255(xyz_to_srgbe(self.get(x, res_y - 1 - y).to_tuple()));
image.push(r); image.push(r);
image.push(g); image.push(g);
image.push(b); image.push(b);
@ -178,8 +182,7 @@ impl Image {
.add_channel("G", openexr::PixelType::HALF) .add_channel("G", openexr::PixelType::HALF)
.add_channel("B", openexr::PixelType::HALF) .add_channel("B", openexr::PixelType::HALF)
.set_compression(openexr::Compression::PIZ_COMPRESSION), .set_compression(openexr::Compression::PIZ_COMPRESSION),
) ).unwrap();
.unwrap();
let mut fb = { let mut fb = {
// Create the frame buffer // Create the frame buffer
@ -230,11 +233,14 @@ impl<'a> Bucket<'a> {
/// encoding to base64. The fourth channel is alpha, and is set to 1.0 for /// encoding to base64. The fourth channel is alpha, and is set to 1.0 for
/// all pixels. /// all pixels.
pub fn rgba_base64<F>(&mut self, color_convert: F) -> String pub fn rgba_base64<F>(&mut self, color_convert: F) -> String
where F: Fn((f32, f32, f32)) -> (f32, f32, f32) where
F: Fn((f32, f32, f32)) -> (f32, f32, f32),
{ {
use base64; use base64;
use std::slice; use std::slice;
let mut data = Vec::with_capacity((4 * (self.max.0 - self.min.0) * (self.max.1 - self.min.1)) as usize); let mut data = Vec::with_capacity(
(4 * (self.max.0 - self.min.0) * (self.max.1 - self.min.1)) as usize,
);
for y in self.min.1..self.max.1 { for y in self.min.1..self.max.1 {
for x in self.min.0..self.max.0 { for x in self.min.0..self.max.0 {
let color = color_convert(self.get(x, y).to_tuple()); let color = color_convert(self.get(x, y).to_tuple());
@ -244,7 +250,8 @@ impl<'a> Bucket<'a> {
data.push(1.0); data.push(1.0);
} }
} }
let data_u8 = unsafe { slice::from_raw_parts(&data[0] as *const f32 as *const u8, data.len() * 4) }; let data_u8 =
unsafe { slice::from_raw_parts(&data[0] as *const f32 as *const u8, data.len() * 4) };
base64::encode(data_u8) base64::encode(data_u8)
} }
} }
@ -256,9 +263,10 @@ impl<'a> Drop for Bucket<'a> {
let mut bucket_list = tmp.borrow_mut(); let mut bucket_list = tmp.borrow_mut();
// Find matching bucket and remove it // Find matching bucket and remove it
let i = bucket_list let i = bucket_list.iter().position(|bucket| {
.iter() (bucket.0).0 == self.min.0 && (bucket.0).1 == self.min.1 &&
.position(|bucket| (bucket.0).0 == self.min.0 && (bucket.0).1 == self.min.1 && (bucket.1).0 == self.max.0 && (bucket.1).1 == self.max.1); (bucket.1).0 == self.max.0 && (bucket.1).1 == self.max.1
});
bucket_list.swap_remove(i.unwrap()); bucket_list.swap_remove(i.unwrap());
} }
} }

View File

@ -38,8 +38,9 @@ pub fn lerp_slice<T: Lerp + Copy>(s: &[T], alpha: f32) -> T {
} }
pub fn lerp_slice_with<T, F>(s: &[T], alpha: f32, f: F) -> T pub fn lerp_slice_with<T, F>(s: &[T], alpha: f32, f: F) -> T
where T: Copy, where
F: Fn(T, T, f32) -> T T: Copy,
F: Fn(T, T, f32) -> T,
{ {
debug_assert!(s.len() > 0); debug_assert!(s.len() > 0);
debug_assert!(alpha >= 0.0); debug_assert!(alpha >= 0.0);

View File

@ -19,7 +19,12 @@ pub struct DistantDiskLight<'a> {
} }
impl<'a> DistantDiskLight<'a> { impl<'a> DistantDiskLight<'a> {
pub fn new(arena: &'a MemArena, radii: Vec<f32>, directions: Vec<Vector>, colors: Vec<XYZ>) -> DistantDiskLight<'a> { pub fn new(
arena: &'a MemArena,
radii: Vec<f32>,
directions: Vec<Vector>,
colors: Vec<XYZ>,
) -> DistantDiskLight<'a> {
DistantDiskLight { DistantDiskLight {
radii: arena.copy_slice(&radii), radii: arena.copy_slice(&radii),
directions: arena.copy_slice(&directions), directions: arena.copy_slice(&directions),
@ -75,9 +80,10 @@ impl<'a> WorldLightSource for DistantDiskLight<'a> {
} }
fn approximate_energy(&self) -> f32 { fn approximate_energy(&self) -> f32 {
let color: XYZ = self.colors let color: XYZ = self.colors.iter().fold(
.iter() XYZ::new(0.0, 0.0, 0.0),
.fold(XYZ::new(0.0, 0.0, 0.0), |a, &b| a + b) / self.colors.len() as f32; |a, &b| a + b,
) / self.colors.len() as f32;
color.y color.y
} }
} }

View File

@ -26,7 +26,15 @@ pub trait LightSource: Boundable + Debug + Sync {
/// ///
/// Returns: The light arriving at the point arr, the vector to use for /// Returns: The light arriving at the point arr, the vector to use for
/// shadow testing, and the pdf of the sample. /// shadow testing, and the pdf of the sample.
fn sample(&self, space: &Matrix4x4, arr: Point, u: f32, v: f32, wavelength: f32, time: f32) -> (SpectralSample, Vector, f32); fn sample(
&self,
space: &Matrix4x4,
arr: Point,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> (SpectralSample, Vector, f32);
/// Calculates the pdf of sampling the given /// Calculates the pdf of sampling the given
@ -37,7 +45,16 @@ pub trait LightSource: Boundable + Debug + Sync {
/// are a valid sample for the light source (i.e. hits/lies on the light /// are a valid sample for the light source (i.e. hits/lies on the light
/// source). No guarantees are made about the correctness of the return /// source). No guarantees are made about the correctness of the return
/// value if they are not valid. /// value if they are not valid.
fn sample_pdf(&self, space: &Matrix4x4, arr: Point, sample_dir: Vector, sample_u: f32, sample_v: f32, wavelength: f32, time: f32) -> f32; fn sample_pdf(
&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
sample_v: f32,
wavelength: f32,
time: f32,
) -> f32;
/// Returns the color emitted in the given direction from the /// Returns the color emitted in the given direction from the
@ -48,7 +65,15 @@ pub trait LightSource: Boundable + Debug + Sync {
/// - v: Random parameter V. /// - v: Random parameter V.
/// - wavelength: The hero wavelength of light to sample at. /// - wavelength: The hero wavelength of light to sample at.
/// - time: The time to sample at. /// - time: The time to sample at.
fn outgoing(&self, space: &Matrix4x4, dir: Vector, u: f32, v: f32, wavelength: f32, time: f32) -> SpectralSample; fn outgoing(
&self,
space: &Matrix4x4,
dir: Vector,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> SpectralSample;
/// Returns whether the light has a delta distribution. /// Returns whether the light has a delta distribution.

View File

@ -18,17 +18,19 @@ pub struct RectangleLight<'a> {
} }
impl<'a> RectangleLight<'a> { impl<'a> RectangleLight<'a> {
pub fn new<'b>(arena: &'b MemArena, dimensions: Vec<(f32, f32)>, colors: Vec<XYZ>) -> RectangleLight<'b> { pub fn new<'b>(
arena: &'b MemArena,
dimensions: Vec<(f32, f32)>,
colors: Vec<XYZ>,
) -> RectangleLight<'b> {
let bbs: Vec<_> = dimensions let bbs: Vec<_> = dimensions
.iter() .iter()
.map( .map(|d| {
|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),
max: Point::new(d.0 * 0.5, d.1 * 0.5, 0.0), max: Point::new(d.0 * 0.5, d.1 * 0.5, 0.0),
} }
} })
)
.collect(); .collect();
RectangleLight { RectangleLight {
dimensions: arena.copy_slice(&dimensions), dimensions: arena.copy_slice(&dimensions),
@ -39,7 +41,15 @@ impl<'a> RectangleLight<'a> {
} }
impl<'a> LightSource for RectangleLight<'a> { impl<'a> LightSource for RectangleLight<'a> {
fn sample(&self, space: &Matrix4x4, arr: Point, u: f32, v: f32, wavelength: f32, time: f32) -> (SpectralSample, Vector, f32) { fn sample(
&self,
space: &Matrix4x4,
arr: Point,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> (SpectralSample, Vector, f32) {
// Calculate time interpolated values // Calculate time interpolated values
let dim = lerp_slice(&self.dimensions, time); let dim = lerp_slice(&self.dimensions, time);
let col = lerp_slice(&self.colors, time); let col = lerp_slice(&self.colors, time);
@ -98,7 +108,16 @@ impl<'a> LightSource for RectangleLight<'a> {
return (spectral_sample, shadow_vec, pdf as f32); return (spectral_sample, shadow_vec, pdf as f32);
} }
fn sample_pdf(&self, space: &Matrix4x4, arr: Point, sample_dir: Vector, sample_u: f32, sample_v: f32, wavelength: f32, time: f32) -> f32 { fn sample_pdf(
&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
sample_v: f32,
wavelength: f32,
time: f32,
) -> f32 {
// We're not using these, silence warnings // We're not using these, silence warnings
let _ = (sample_dir, sample_u, sample_v, wavelength); let _ = (sample_dir, sample_u, sample_v, wavelength);
@ -125,7 +144,15 @@ impl<'a> LightSource for RectangleLight<'a> {
1.0 / (area_1 + area_2) 1.0 / (area_1 + area_2)
} }
fn outgoing(&self, space: &Matrix4x4, dir: Vector, u: f32, v: f32, wavelength: f32, time: f32) -> SpectralSample { fn outgoing(
&self,
space: &Matrix4x4,
dir: Vector,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> SpectralSample {
// We're not using these, silence warnings // We're not using these, silence warnings
let _ = (space, dir, u, v); let _ = (space, dir, u, v);
@ -143,9 +170,10 @@ impl<'a> LightSource for RectangleLight<'a> {
} }
fn approximate_energy(&self) -> f32 { fn approximate_energy(&self) -> f32 {
let color: XYZ = self.colors let color: XYZ = self.colors.iter().fold(
.iter() XYZ::new(0.0, 0.0, 0.0),
.fold(XYZ::new(0.0, 0.0, 0.0), |a, &b| a + b) / self.colors.len() as f32; |a, &b| a + b,
) / self.colors.len() as f32;
color.y color.y
} }
} }

View File

@ -24,14 +24,12 @@ impl<'a> SphereLight<'a> {
pub fn new<'b>(arena: &'b MemArena, radii: Vec<f32>, colors: Vec<XYZ>) -> SphereLight<'b> { pub fn new<'b>(arena: &'b MemArena, radii: Vec<f32>, colors: Vec<XYZ>) -> SphereLight<'b> {
let bbs: Vec<_> = radii let bbs: Vec<_> = radii
.iter() .iter()
.map( .map(|r| {
|r| {
BBox { BBox {
min: Point::new(-*r, -*r, -*r), min: Point::new(-*r, -*r, -*r),
max: Point::new(*r, *r, *r), max: Point::new(*r, *r, *r),
} }
} })
)
.collect(); .collect();
SphereLight { SphereLight {
radii: arena.copy_slice(&radii), radii: arena.copy_slice(&radii),
@ -42,7 +40,15 @@ impl<'a> SphereLight<'a> {
} }
impl<'a> LightSource for SphereLight<'a> { impl<'a> LightSource for SphereLight<'a> {
fn sample(&self, space: &Matrix4x4, arr: Point, u: f32, v: f32, wavelength: f32, time: f32) -> (SpectralSample, Vector, f32) { fn sample(
&self,
space: &Matrix4x4,
arr: Point,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> (SpectralSample, Vector, f32) {
// TODO: track fp error due to transforms // TODO: track fp error due to transforms
let arr = arr * *space; let arr = arr * *space;
let pos = Point::new(0.0, 0.0, 0.0); let pos = Point::new(0.0, 0.0, 0.0);
@ -96,7 +102,8 @@ impl<'a> LightSource for SphereLight<'a> {
); );
// Calculate the final values and return everything. // Calculate the final values and return everything.
let shadow_vec = ((x * sample.x()) + (y * sample.y()) + (z * sample.z())) * space.inverse(); let shadow_vec = ((x * sample.x()) + (y * sample.y()) + (z * sample.z())) *
space.inverse();
let pdf = uniform_sample_cone_pdf(cos_theta_max); let pdf = uniform_sample_cone_pdf(cos_theta_max);
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength); let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
return (spectral_sample, shadow_vec, pdf as f32); return (spectral_sample, shadow_vec, pdf as f32);
@ -109,7 +116,16 @@ impl<'a> LightSource for SphereLight<'a> {
} }
} }
fn sample_pdf(&self, space: &Matrix4x4, arr: Point, sample_dir: Vector, sample_u: f32, sample_v: f32, wavelength: f32, time: f32) -> f32 { fn sample_pdf(
&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
sample_v: f32,
wavelength: f32,
time: f32,
) -> f32 {
// We're not using these, silence warnings // We're not using these, silence warnings
let _ = (sample_dir, sample_u, sample_v, wavelength); let _ = (sample_dir, sample_u, sample_v, wavelength);
@ -132,7 +148,15 @@ impl<'a> LightSource for SphereLight<'a> {
} }
} }
fn outgoing(&self, space: &Matrix4x4, dir: Vector, u: f32, v: f32, wavelength: f32, time: f32) -> SpectralSample { fn outgoing(
&self,
space: &Matrix4x4,
dir: Vector,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> SpectralSample {
// We're not using these, silence warnings // We're not using these, silence warnings
let _ = (space, dir, u, v); let _ = (space, dir, u, v);
@ -148,9 +172,10 @@ impl<'a> LightSource for SphereLight<'a> {
} }
fn approximate_energy(&self) -> f32 { fn approximate_energy(&self) -> f32 {
let color: XYZ = self.colors let color: XYZ = self.colors.iter().fold(
.iter() XYZ::new(0.0, 0.0, 0.0),
.fold(XYZ::new(0.0, 0.0, 0.0), |a, &b| a + b) / self.colors.len() as f32; |a, &b| a + b,
) / self.colors.len() as f32;
color.y color.y
} }
} }

View File

@ -83,7 +83,7 @@ fn main() {
.value_name("FILE") .value_name("FILE")
.help("Input .psy file") .help("Input .psy file")
.takes_value(true) .takes_value(true)
.required_unless_one(&["dev", "use_stdin"]) .required_unless_one(&["dev", "use_stdin"]),
) )
.arg( .arg(
Arg::with_name("spp") Arg::with_name("spp")
@ -92,43 +92,45 @@ fn main() {
.value_name("N") .value_name("N")
.help("Number of samples per pixel") .help("Number of samples per pixel")
.takes_value(true) .takes_value(true)
.validator( .validator(|s| {
|s| { usize::from_str(&s).and(Ok(())).or(Err(
usize::from_str(&s) "must be an integer"
.and(Ok(())) .to_string(),
.or(Err("must be an integer".to_string())) ))
} }),
)
) )
.arg( .arg(
Arg::with_name("max_bucket_samples") Arg::with_name("max_bucket_samples")
.short("b") .short("b")
.long("spb") .long("spb")
.value_name("N") .value_name("N")
.help("Target number of samples per bucket (determines bucket size)") .help(
.takes_value(true) "Target number of samples per bucket (determines bucket size)",
.validator(
|s| {
usize::from_str(&s)
.and(Ok(()))
.or(Err("must be an integer".to_string()))
}
) )
.takes_value(true)
.validator(|s| {
usize::from_str(&s).and(Ok(())).or(Err(
"must be an integer"
.to_string(),
))
}),
) )
.arg( .arg(
Arg::with_name("crop") Arg::with_name("crop")
.long("crop") .long("crop")
.value_name("X1 Y1 X2 Y2") .value_name("X1 Y1 X2 Y2")
.help("Only render the image between pixel coordinates (X1, Y1) and (X2, Y2). Coordinates are zero-indexed and inclusive.") .help(
"Only render the image between pixel coordinates (X1, Y1) \
and (X2, Y2). Coordinates are zero-indexed and inclusive.",
)
.takes_value(true) .takes_value(true)
.number_of_values(4) .number_of_values(4)
.validator( .validator(|s| {
|s| { usize::from_str(&s).and(Ok(())).or(Err(
usize::from_str(&s) "must be four integers"
.and(Ok(())) .to_string(),
.or(Err("must be four integers".to_string())) ))
} }),
)
) )
.arg( .arg(
Arg::with_name("threads") Arg::with_name("threads")
@ -137,38 +139,33 @@ fn main() {
.value_name("N") .value_name("N")
.help( .help(
"Number of threads to render with. Defaults to the number of logical \ "Number of threads to render with. Defaults to the number of logical \
cores on the system." cores on the system.",
) )
.takes_value(true) .takes_value(true)
.validator( .validator(|s| {
|s| { usize::from_str(&s).and(Ok(())).or(Err(
usize::from_str(&s) "must be an integer"
.and(Ok(())) .to_string(),
.or(Err("must be an integer".to_string())) ))
} }),
)
)
.arg(
Arg::with_name("stats")
.long("stats")
.help("Print additional statistics about rendering")
)
.arg(
Arg::with_name("dev")
.long("dev")
.help("Show useful dev/debug info.")
) )
.arg(Arg::with_name("stats").long("stats").help(
"Print additional statistics about rendering",
))
.arg(Arg::with_name("dev").long("dev").help(
"Show useful dev/debug info.",
))
.arg( .arg(
Arg::with_name("serialized_output") Arg::with_name("serialized_output")
.long("serialized_output") .long("serialized_output")
.help("Serialize and send render output to standard output.") .help("Serialize and send render output to standard output.")
.hidden(true) .hidden(true),
) )
.arg( .arg(
Arg::with_name("use_stdin") Arg::with_name("use_stdin")
.long("use_stdin") .long("use_stdin")
.help("Take scene file in from stdin instead of a file path.") .help("Take scene file in from stdin instead of a file path.")
.hidden(true) .hidden(true),
) )
.get_matches(); .get_matches();
@ -186,10 +183,13 @@ fn main() {
return; return;
} }
let crop = args.values_of("crop") let crop = args.values_of("crop").map(|mut vals| {
.map( let coords = (
|mut vals| { u32::from_str(vals.next().unwrap()).unwrap(),
let coords = (u32::from_str(vals.next().unwrap()).unwrap(), u32::from_str(vals.next().unwrap()).unwrap(), u32::from_str(vals.next().unwrap()).unwrap(), u32::from_str(vals.next().unwrap()).unwrap()); u32::from_str(vals.next().unwrap()).unwrap(),
u32::from_str(vals.next().unwrap()).unwrap(),
u32::from_str(vals.next().unwrap()).unwrap(),
);
if coords.0 > coords.2 { if coords.0 > coords.2 {
panic!("Argument '--crop': X1 must be less than or equal to X2"); panic!("Argument '--crop': X1 must be less than or equal to X2");
} }
@ -197,8 +197,7 @@ fn main() {
panic!("Argument '--crop': Y1 must be less than or equal to Y2"); panic!("Argument '--crop': Y1 must be less than or equal to Y2");
} }
coords coords
} });
);
// Parse data tree of scene file // Parse data tree of scene file
if !args.is_present("serialized_output") { if !args.is_present("serialized_output") {
@ -214,9 +213,9 @@ fn main() {
let mut stdin = tmp.lock(); let mut stdin = tmp.lock();
let mut buf = vec![0u8; 4096]; let mut buf = vec![0u8; 4096];
loop { loop {
let count = stdin let count = stdin.read(&mut buf).expect(
.read(&mut buf) "Unexpected end of scene input.",
.expect("Unexpected end of scene input."); );
let start = if input.len() < 11 { let start = if input.len() < 11 {
0 0
} else { } else {
@ -227,7 +226,9 @@ fn main() {
let mut done = false; let mut done = false;
let mut trunc_len = 0; let mut trunc_len = 0;
if let nom::IResult::Done(remaining, _) = take_until!(&input[start..end], "__PSY_EOF__") { if let nom::IResult::Done(remaining, _) =
take_until!(&input[start..end], "__PSY_EOF__")
{
done = true; done = true;
trunc_len = input.len() - remaining.len(); trunc_len = input.len() - remaining.len();
} }
@ -261,12 +262,10 @@ fn main() {
} }
let arena = MemArena::with_min_block_size((1 << 20) * 4); let arena = MemArena::with_min_block_size((1 << 20) * 4);
let mut r = parse_scene(&arena, child).unwrap_or_else( let mut r = parse_scene(&arena, child).unwrap_or_else(|e| {
|e| {
e.print(&psy_contents); e.print(&psy_contents);
panic!("Parse error."); panic!("Parse error.");
} });
);
if let Some(spp) = args.value_of("spp") { if let Some(spp) = args.value_of("spp") {
if !args.is_present("serialized_output") { if !args.is_present("serialized_output") {
@ -275,7 +274,8 @@ fn main() {
r.spp = usize::from_str(&spp).unwrap(); r.spp = usize::from_str(&spp).unwrap();
} }
let max_samples_per_bucket = if let Some(max_samples_per_bucket) = args.value_of("max_bucket_samples") { let max_samples_per_bucket =
if let Some(max_samples_per_bucket) = args.value_of("max_bucket_samples") {
u32::from_str(&max_samples_per_bucket).unwrap() u32::from_str(&max_samples_per_bucket).unwrap()
} else { } else {
4096 4096
@ -331,7 +331,9 @@ fn main() {
if !args.is_present("serialized_output") { if !args.is_present("serialized_output") {
println!("Writing image to disk into '{}'...", r.output_file); println!("Writing image to disk into '{}'...", r.output_file);
if r.output_file.ends_with(".png") { if r.output_file.ends_with(".png") {
image.write_png(Path::new(&r.output_file)).expect("Failed to write png..."); image.write_png(Path::new(&r.output_file)).expect(
"Failed to write png...",
);
} else if r.output_file.ends_with(".exr") { } else if r.output_file.ends_with(".exr") {
image.write_exr(Path::new(&r.output_file)); image.write_exr(Path::new(&r.output_file));
} else { } else {

View File

@ -135,7 +135,9 @@ pub fn fast_pow2(p: f32) -> f32 {
let w: i32 = clipp as i32; let w: i32 = clipp as i32;
let z: f32 = clipp - w as f32 + offset; let z: f32 = clipp - w as f32 + offset;
let i: u32 = ((1 << 23) as f32 * (clipp + 121.2740575 + 27.7280233 / (4.84252568 - z) - 1.49012907 * z)) as u32; let i: u32 = ((1 << 23) as f32 *
(clipp + 121.2740575 + 27.7280233 / (4.84252568 - z) - 1.49012907 * z)) as
u32;
unsafe { transmute_copy::<u32, f32>(&i) } unsafe { transmute_copy::<u32, f32>(&i) }
} }

View File

@ -35,14 +35,12 @@ impl<'a> DataTree<'a> {
remaining_text = skip_ws_and_comments(remaining_text); remaining_text = skip_ws_and_comments(remaining_text);
if remaining_text.1.len() == 0 { if remaining_text.1.len() == 0 {
return Ok( return Ok(DataTree::Internal {
DataTree::Internal {
type_name: "ROOT", type_name: "ROOT",
ident: None, ident: None,
children: items, children: items,
byte_offset: 0, byte_offset: 0,
} });
);
} else { } else {
// If the whole text wasn't parsed, something went wrong. // If the whole text wasn't parsed, something went wrong.
return Err(ParseError::Other((0, "Failed to parse the entire string."))); return Err(ParseError::Other((0, "Failed to parse the entire string.")));
@ -106,7 +104,10 @@ impl<'a> DataTree<'a> {
} }
} }
pub fn iter_internal_children_with_type(&'a self, type_name: &'static str) -> DataTreeFilterInternalIter<'a> { pub fn iter_internal_children_with_type(
&'a self,
type_name: &'static str,
) -> DataTreeFilterInternalIter<'a> {
if let &DataTree::Internal { ref children, .. } = self { if let &DataTree::Internal { ref children, .. } = self {
DataTreeFilterInternalIter { DataTreeFilterInternalIter {
type_name: type_name, type_name: type_name,
@ -120,7 +121,10 @@ impl<'a> DataTree<'a> {
} }
} }
pub fn iter_leaf_children_with_type(&'a self, type_name: &'static str) -> DataTreeFilterLeafIter<'a> { pub fn iter_leaf_children_with_type(
&'a self,
type_name: &'static str,
) -> DataTreeFilterLeafIter<'a> {
if let &DataTree::Internal { ref children, .. } = self { if let &DataTree::Internal { ref children, .. } = self {
DataTreeFilterLeafIter { DataTreeFilterLeafIter {
type_name: type_name, type_name: type_name,
@ -141,7 +145,8 @@ impl<'a> DataTree<'a> {
ident, ident,
ref children, ref children,
byte_offset: _, byte_offset: _,
} = *self { } = *self
{
(type_name, ident, children) (type_name, ident, children)
} else { } else {
panic!("Expected DataTree::Internal, found DataTree::Leaf") panic!("Expected DataTree::Internal, found DataTree::Leaf")
@ -152,7 +157,8 @@ impl<'a> DataTree<'a> {
type_name, type_name,
contents, contents,
byte_offset: _, byte_offset: _,
} = *self { } = *self
{
(type_name, contents) (type_name, contents)
} else { } else {
panic!("Expected DataTree::Leaf, found DataTree::Internal") panic!("Expected DataTree::Leaf, found DataTree::Internal")
@ -312,17 +318,15 @@ fn parse_node<'a>(source_text: (usize, &'a str)) -> ParseResult<'a> {
children.push(node); children.push(node);
} }
if let (Token::CloseInner, text4) = next_token(text_remaining) { if let (Token::CloseInner, text4) = next_token(text_remaining) {
return Ok( return Ok(Some((
Some( DataTree::Internal {
(DataTree::Internal {
type_name: type_name, type_name: type_name,
ident: Some(n), ident: Some(n),
children: children, children: children,
byte_offset: text1.0, byte_offset: text1.0,
}, },
text4) text4,
) )));
);
} else { } else {
return Err(ParseError::MissingCloseInternal(text_remaining.0)); return Err(ParseError::MissingCloseInternal(text_remaining.0));
} }
@ -341,17 +345,15 @@ fn parse_node<'a>(source_text: (usize, &'a str)) -> ParseResult<'a> {
} }
if let (Token::CloseInner, text3) = next_token(text_remaining) { if let (Token::CloseInner, text3) = next_token(text_remaining) {
return Ok( return Ok(Some((
Some( DataTree::Internal {
(DataTree::Internal {
type_name: type_name, type_name: type_name,
ident: None, ident: None,
children: children, children: children,
byte_offset: text1.0, byte_offset: text1.0,
}, },
text3) text3,
) )));
);
} else { } else {
return Err(ParseError::MissingCloseInternal(text_remaining.0)); return Err(ParseError::MissingCloseInternal(text_remaining.0));
} }
@ -361,16 +363,14 @@ fn parse_node<'a>(source_text: (usize, &'a str)) -> ParseResult<'a> {
(Token::OpenLeaf, text2) => { (Token::OpenLeaf, text2) => {
let (contents, text3) = parse_leaf_content(text2); let (contents, text3) = parse_leaf_content(text2);
if let (Token::CloseLeaf, text4) = next_token(text3) { if let (Token::CloseLeaf, text4) = next_token(text3) {
return Ok( return Ok(Some((
Some( DataTree::Leaf {
(DataTree::Leaf {
type_name: type_name, type_name: type_name,
contents: contents, contents: contents,
byte_offset: text1.0, byte_offset: text1.0,
}, },
text4) text4,
) )));
);
} else { } else {
return Err(ParseError::MissingCloseLeaf(text3.0)); return Err(ParseError::MissingCloseLeaf(text3.0));
} }
@ -407,7 +407,10 @@ fn parse_leaf_content<'a>(source_text: (usize, &'a str)) -> (&'a str, (usize, &'
si = source_text.1.len(); si = source_text.1.len();
} }
return (&source_text.1[0..si], (source_text.0 + si, &source_text.1[si..])); return (&source_text.1[0..si], (
source_text.0 + si,
&source_text.1[si..],
));
} }
@ -454,7 +457,10 @@ fn next_token<'a>(source_text: (usize, &'a str)) -> (Token<'a>, (usize, &'a str)
si = text1.1.len(); si = text1.1.len();
} }
return (Token::Ident(&text1.1[0..si]), (text1.0 + si, &text1.1[si..])); return (
Token::Ident(&text1.1[0..si]),
(text1.0 + si, &text1.1[si..]),
);
} }
_ => { _ => {
@ -474,7 +480,10 @@ fn next_token<'a>(source_text: (usize, &'a str)) -> (Token<'a>, (usize, &'a str)
si = text1.1.len(); si = text1.1.len();
} }
return (Token::TypeName(&text1.1[0..si]), (text1.0 + si, &text1.1[si..])); return (Token::TypeName(&text1.1[0..si]), (
text1.0 + si,
&text1.1[si..],
));
} }
} }
@ -614,10 +623,10 @@ mod tests {
fn tokenize_5() { fn tokenize_5() {
let input = (0, " $hi\\ t\\#he\\[re "); let input = (0, " $hi\\ t\\#he\\[re ");
assert_eq!( assert_eq!(next_token(input), (
next_token(input), Token::Ident("$hi\\ t\\#he\\[re"),
(Token::Ident("$hi\\ t\\#he\\[re"), (15, " ")) (15, " "),
); ));
} }
#[test] #[test]
@ -648,18 +657,18 @@ mod tests {
let (token7, input8) = next_token(input7); let (token7, input8) = next_token(input7);
let (token8, input9) = next_token(input8); let (token8, input9) = next_token(input8);
assert_eq!( assert_eq!((token1, input2), (Token::TypeName("Thing"), (
(token1, input2), 5,
(Token::TypeName("Thing"), (5, " $yar { # A comment\n\tThing2 []\n}")) " $yar { # A comment\n\tThing2 []\n}",
); )));
assert_eq!( assert_eq!((token2, input3), (Token::Ident("$yar"), (
(token2, input3), 10,
(Token::Ident("$yar"), (10, " { # A comment\n\tThing2 []\n}")) " { # A comment\n\tThing2 []\n}",
); )));
assert_eq!( assert_eq!((token3, input4), (Token::OpenInner, (
(token3, input4), 12,
(Token::OpenInner, (12, " # A comment\n\tThing2 []\n}")) " # A comment\n\tThing2 []\n}",
); )));
assert_eq!( assert_eq!(
(token4, input5), (token4, input5),
(Token::TypeName("Thing2"), (32, " []\n}")) (Token::TypeName("Thing2"), (32, " []\n}"))
@ -700,9 +709,8 @@ mod tests {
A [] A []
A {} A {}
B {} B {}
"# "#,
) ).unwrap();
.unwrap();
let i = dt.iter_children_with_type("A"); let i = dt.iter_children_with_type("A");
assert_eq!(i.count(), 3); assert_eq!(i.count(), 3);
@ -717,9 +725,8 @@ mod tests {
A [] A []
A {} A {}
B {} B {}
"# "#,
) ).unwrap();
.unwrap();
let i = dt.iter_internal_children_with_type("A"); let i = dt.iter_internal_children_with_type("A");
assert_eq!(i.count(), 2); assert_eq!(i.count(), 2);
@ -734,9 +741,8 @@ mod tests {
A {} A {}
A [] A []
B {} B {}
"# "#,
) ).unwrap();
.unwrap();
let i = dt.iter_leaf_children_with_type("A"); let i = dt.iter_leaf_children_with_type("A");
assert_eq!(i.count(), 2); assert_eq!(i.count(), 2);

View File

@ -91,61 +91,54 @@ fn line_count_to_byte_offset(text: &str, offset: usize) -> usize {
/// Takes in a DataTree representing a Scene node and returns /// Takes in a DataTree representing a Scene node and returns
pub fn parse_scene<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Renderer<'a>, PsyParseError> { pub fn parse_scene<'a>(
arena: &'a MemArena,
tree: &'a DataTree,
) -> Result<Renderer<'a>, PsyParseError> {
// Verify we have the right number of each section // Verify we have the right number of each section
if tree.iter_children_with_type("Output").count() != 1 { if tree.iter_children_with_type("Output").count() != 1 {
let count = tree.iter_children_with_type("Output").count(); let count = tree.iter_children_with_type("Output").count();
return Err( return Err(PsyParseError::WrongNodeCount(
PsyParseError::WrongNodeCount(
tree.byte_offset(), tree.byte_offset(),
"Scene should have precisely one Output \ "Scene should have precisely one Output \
section.", section.",
count, count,
) ));
);
} }
if tree.iter_children_with_type("RenderSettings").count() != 1 { if tree.iter_children_with_type("RenderSettings").count() != 1 {
let count = tree.iter_children_with_type("RenderSettings").count(); let count = tree.iter_children_with_type("RenderSettings").count();
return Err( return Err(PsyParseError::WrongNodeCount(
PsyParseError::WrongNodeCount(
tree.byte_offset(), tree.byte_offset(),
"Scene should have precisely one \ "Scene should have precisely one \
RenderSettings section.", RenderSettings section.",
count, count,
) ));
);
} }
if tree.iter_children_with_type("Camera").count() != 1 { if tree.iter_children_with_type("Camera").count() != 1 {
let count = tree.iter_children_with_type("Camera").count(); let count = tree.iter_children_with_type("Camera").count();
return Err( return Err(PsyParseError::WrongNodeCount(
PsyParseError::WrongNodeCount(
tree.byte_offset(), tree.byte_offset(),
"Scene should have precisely one Camera \ "Scene should have precisely one Camera \
section.", section.",
count, count,
) ));
);
} }
if tree.iter_children_with_type("World").count() != 1 { if tree.iter_children_with_type("World").count() != 1 {
let count = tree.iter_children_with_type("World").count(); let count = tree.iter_children_with_type("World").count();
return Err( return Err(PsyParseError::WrongNodeCount(
PsyParseError::WrongNodeCount(
tree.byte_offset(), tree.byte_offset(),
"Scene should have precisely one World section.", "Scene should have precisely one World section.",
count, count,
) ));
);
} }
if tree.iter_children_with_type("Assembly").count() != 1 { if tree.iter_children_with_type("Assembly").count() != 1 {
let count = tree.iter_children_with_type("Assembly").count(); let count = tree.iter_children_with_type("Assembly").count();
return Err( return Err(PsyParseError::WrongNodeCount(
PsyParseError::WrongNodeCount(
tree.byte_offset(), tree.byte_offset(),
"Scene should have precisely one Root Assembly \ "Scene should have precisely one Root Assembly \
section.", section.",
count, count,
) ));
);
} }
// Parse output info // Parse output info
@ -155,7 +148,7 @@ pub fn parse_scene<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Render
let render_settings = parse_render_settings( let render_settings = parse_render_settings(
tree.iter_children_with_type("RenderSettings") tree.iter_children_with_type("RenderSettings")
.nth(0) .nth(0)
.unwrap() .unwrap(),
)?; )?;
// Parse camera // Parse camera
@ -193,7 +186,10 @@ pub fn parse_scene<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Render
// Put renderer together // Put renderer together
let renderer = Renderer { let renderer = Renderer {
output_file: output_info.clone(), output_file: output_info.clone(),
resolution: ((render_settings.0).0 as usize, (render_settings.0).1 as usize), resolution: (
(render_settings.0).0 as usize,
(render_settings.0).1 as usize,
),
spp: render_settings.1 as usize, spp: render_settings.1 as usize,
seed: render_settings.2, seed: render_settings.2,
scene: scene, scene: scene,
@ -220,22 +216,18 @@ fn parse_output_info(tree: &DataTree) -> Result<String, PsyParseError> {
// Trim and validate // Trim and validate
let tc = contents.trim(); let tc = contents.trim();
if tc.chars().count() < 2 { if tc.chars().count() < 2 {
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"File path format is \ "File path format is \
incorrect.", incorrect.",
) ));
);
} }
if tc.chars().nth(0).unwrap() != '"' || tc.chars().last().unwrap() != '"' { if tc.chars().nth(0).unwrap() != '"' || tc.chars().last().unwrap() != '"' {
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"File paths must be \ "File paths must be \
surrounded by quotes.", surrounded by quotes.",
) ));
);
} }
let len = tc.len(); let len = tc.len();
let tc = &tc[1..len - 1]; let tc = &tc[1..len - 1];
@ -253,16 +245,17 @@ fn parse_output_info(tree: &DataTree) -> Result<String, PsyParseError> {
if found_path { if found_path {
return Ok((path)); return Ok((path));
} else { } else {
return Err(PsyParseError::MissingNode(tree.byte_offset(), "Output section must contain a Path.")); return Err(PsyParseError::MissingNode(
tree.byte_offset(),
"Output section must contain a Path.",
));
} }
} else { } else {
return Err( return Err(PsyParseError::ExpectedInternalNode(
PsyParseError::ExpectedInternalNode(
tree.byte_offset(), tree.byte_offset(),
"Output section should be an internal \ "Output section should be an internal \
node, containing at least a Path.", node, containing at least a Path.",
) ));
);
}; };
} }
@ -285,18 +278,18 @@ fn parse_render_settings(tree: &DataTree) -> Result<((u32, u32), u32, u32), PsyP
contents, contents,
byte_offset, byte_offset,
} if type_name == "Resolution" => { } if type_name == "Resolution" => {
if let IResult::Done(_, (w, h)) = closure!(terminated!(tuple!(ws_u32, ws_u32), nom::eof))(contents.as_bytes()) { if let IResult::Done(_, (w, h)) =
closure!(terminated!(tuple!(ws_u32, ws_u32), nom::eof))(contents.as_bytes())
{
found_res = true; found_res = true;
res = (w, h); res = (w, h);
} else { } else {
// Found Resolution, but its contents is not in the right format // Found Resolution, but its contents is not in the right format
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"Resolution should be specified with two \ "Resolution should be specified with two \
integers in the form '[width height]'.", integers in the form '[width height]'.",
) ));
);
} }
} }
@ -311,14 +304,12 @@ fn parse_render_settings(tree: &DataTree) -> Result<((u32, u32), u32, u32), PsyP
spp = n; spp = n;
} else { } else {
// Found SamplesPerPixel, but its contents is not in the right format // Found SamplesPerPixel, but its contents is not in the right format
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"SamplesPerPixel should be \ "SamplesPerPixel should be \
an integer specified in \ an integer specified in \
the form '[samples]'.", the form '[samples]'.",
) ));
);
} }
} }
@ -332,14 +323,12 @@ fn parse_render_settings(tree: &DataTree) -> Result<((u32, u32), u32, u32), PsyP
seed = n; seed = n;
} else { } else {
// Found Seed, but its contents is not in the right format // Found Seed, but its contents is not in the right format
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"Seed should be an integer \ "Seed should be an integer \
specified in the form \ specified in the form \
'[samples]'.", '[samples]'.",
) ));
);
} }
} }
@ -350,23 +339,19 @@ fn parse_render_settings(tree: &DataTree) -> Result<((u32, u32), u32, u32), PsyP
if found_res && found_spp { if found_res && found_spp {
return Ok((res, spp, seed)); return Ok((res, spp, seed));
} else { } else {
return Err( return Err(PsyParseError::MissingNode(
PsyParseError::MissingNode(
tree.byte_offset(), tree.byte_offset(),
"RenderSettings must have both Resolution and \ "RenderSettings must have both Resolution and \
SamplesPerPixel specified.", SamplesPerPixel specified.",
) ));
);
} }
} else { } else {
return Err( return Err(PsyParseError::ExpectedInternalNode(
PsyParseError::ExpectedInternalNode(
tree.byte_offset(), tree.byte_offset(),
"RenderSettings section should be an \ "RenderSettings section should be an \
internal node, containing at least \ internal node, containing at least \
Resolution and SamplesPerPixel.", Resolution and SamplesPerPixel.",
) ));
);
}; };
} }
@ -393,14 +378,12 @@ fn parse_camera<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Camera<'a
fovs.push(fov * (3.1415926536 / 180.0)); fovs.push(fov * (3.1415926536 / 180.0));
} else { } else {
// Found Fov, but its contents is not in the right format // Found Fov, but its contents is not in the right format
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"Fov should be a decimal \ "Fov should be a decimal \
number specified in the \ number specified in the \
form '[fov]'.", form '[fov]'.",
) ));
);
} }
} }
@ -414,14 +397,12 @@ fn parse_camera<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Camera<'a
focus_distances.push(fd); focus_distances.push(fd);
} else { } else {
// Found FocalDistance, but its contents is not in the right format // Found FocalDistance, but its contents is not in the right format
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"FocalDistance should be a \ "FocalDistance should be a \
decimal number specified \ decimal number specified \
in the form '[fov]'.", in the form '[fov]'.",
) ));
);
} }
} }
@ -435,14 +416,12 @@ fn parse_camera<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Camera<'a
aperture_radii.push(ar); aperture_radii.push(ar);
} else { } else {
// Found ApertureRadius, but its contents is not in the right format // Found ApertureRadius, but its contents is not in the right format
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"ApertureRadius should be a \ "ApertureRadius should be a \
decimal number specified \ decimal number specified \
in the form '[fov]'.", in the form '[fov]'.",
) ));
);
} }
} }
@ -464,16 +443,20 @@ fn parse_camera<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Camera<'a
} }
} }
return Ok(Camera::new(arena, mats, fovs, aperture_radii, focus_distances)); return Ok(Camera::new(
arena,
mats,
fovs,
aperture_radii,
focus_distances,
));
} else { } else {
return Err( return Err(PsyParseError::ExpectedInternalNode(
PsyParseError::ExpectedInternalNode(
tree.byte_offset(), tree.byte_offset(),
"Camera section should be an internal \ "Camera section should be an internal \
node, containing at least Fov and \ node, containing at least Fov and \
Transform.", Transform.",
) ));
);
} }
} }
@ -488,13 +471,11 @@ fn parse_world<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<World<'a>,
// Parse background shader // Parse background shader
let bgs = { let bgs = {
if tree.iter_children_with_type("BackgroundShader").count() != 1 { if tree.iter_children_with_type("BackgroundShader").count() != 1 {
return Err( return Err(PsyParseError::WrongNodeCount(
PsyParseError::WrongNodeCount(
tree.byte_offset(), tree.byte_offset(),
"World should have precisely one BackgroundShader section.", "World should have precisely one BackgroundShader section.",
tree.iter_children_with_type("BackgroundShader").count(), tree.iter_children_with_type("BackgroundShader").count(),
) ));
);
} }
tree.iter_children_with_type("BackgroundShader") tree.iter_children_with_type("BackgroundShader")
.nth(0) .nth(0)
@ -502,25 +483,23 @@ fn parse_world<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<World<'a>,
}; };
let bgs_type = { let bgs_type = {
if bgs.iter_children_with_type("Type").count() != 1 { if bgs.iter_children_with_type("Type").count() != 1 {
return Err( return Err(PsyParseError::WrongNodeCount(
PsyParseError::WrongNodeCount(
bgs.byte_offset(), bgs.byte_offset(),
"BackgroundShader should have \ "BackgroundShader should have \
precisely one Type specified.", precisely one Type specified.",
bgs.iter_children_with_type("Type").count(), bgs.iter_children_with_type("Type").count(),
) ));
);
} }
if let &DataTree::Leaf { contents, .. } = bgs.iter_children_with_type("Type").nth(0).unwrap() { if let &DataTree::Leaf { contents, .. } =
bgs.iter_children_with_type("Type").nth(0).unwrap()
{
contents.trim() contents.trim()
} else { } else {
return Err( return Err(PsyParseError::ExpectedLeafNode(
PsyParseError::ExpectedLeafNode(
bgs.byte_offset(), bgs.byte_offset(),
"BackgroundShader's Type should be a \ "BackgroundShader's Type should be a \
leaf node.", leaf node.",
) ));
);
} }
}; };
match bgs_type { match bgs_type {
@ -529,40 +508,37 @@ fn parse_world<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<World<'a>,
contents, contents,
byte_offset, byte_offset,
.. ..
}) = bgs.iter_children_with_type("Color").nth(0) { }) = bgs.iter_children_with_type("Color").nth(0)
if let IResult::Done(_, color) = closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.trim().as_bytes()) { {
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.trim().as_bytes())
{
// TODO: proper color space management, not just assuming // TODO: proper color space management, not just assuming
// rec.709. // rec.709.
background_color = XYZ::from_tuple(rec709_e_to_xyz(color)); background_color = XYZ::from_tuple(rec709_e_to_xyz(color));
} else { } else {
return Err( return Err(PsyParseError::IncorrectLeafData(
PsyParseError::IncorrectLeafData(
byte_offset, byte_offset,
"Color should be specified \ "Color should be specified \
with three decimal numbers \ with three decimal numbers \
in the form '[R G B]'.", in the form '[R G B]'.",
) ));
);
} }
} else { } else {
return Err( return Err(PsyParseError::MissingNode(
PsyParseError::MissingNode(
bgs.byte_offset(), bgs.byte_offset(),
"BackgroundShader's Type is Color, \ "BackgroundShader's Type is Color, \
but no Color is specified.", but no Color is specified.",
) ));
);
} }
} }
_ => { _ => {
return Err( return Err(PsyParseError::UnknownVariant(
PsyParseError::UnknownVariant(
bgs.byte_offset(), bgs.byte_offset(),
"The specified BackgroundShader Type \ "The specified BackgroundShader Type \
isn't a recognized type.", isn't a recognized type.",
) ))
)
} }
} }
@ -578,21 +554,17 @@ fn parse_world<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<World<'a>,
} }
// Build and return the world // Build and return the world
return Ok( return Ok(World {
World {
background_color: background_color, background_color: background_color,
lights: arena.copy_slice(&lights), lights: arena.copy_slice(&lights),
} });
);
} else { } else {
return Err( return Err(PsyParseError::ExpectedInternalNode(
PsyParseError::ExpectedInternalNode(
tree.byte_offset(), tree.byte_offset(),
"World section should be an internal \ "World section should be an internal \
node, containing at least a \ node, containing at least a \
BackgroundShader.", BackgroundShader.",
) ));
);
} }
} }
@ -601,8 +573,7 @@ fn parse_world<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<World<'a>,
pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> { pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> {
if let IResult::Done(_, ns) = if let IResult::Done(_, ns) =
closure!( closure!(terminated!(
terminated!(
tuple!( tuple!(
ws_f32, ws_f32,
ws_f32, ws_f32,
@ -622,10 +593,9 @@ pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> {
ws_f32 ws_f32
), ),
nom::eof nom::eof
) ))(contents.as_bytes())
)(contents.as_bytes()) { {
return Ok( return Ok(Matrix4x4::new_from_values(
Matrix4x4::new_from_values(
ns.0, ns.0,
ns.4, ns.4,
ns.8, ns.8,
@ -642,8 +612,7 @@ pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> {
ns.7, ns.7,
ns.11, ns.11,
ns.15, ns.15,
) ));
);
} else { } else {
return Err(PsyParseError::UnknownError(0)); return Err(PsyParseError::UnknownError(0));
} }

View File

@ -12,7 +12,10 @@ use super::psy_mesh_surface::parse_mesh_surface;
use super::psy::{parse_matrix, PsyParseError}; use super::psy::{parse_matrix, PsyParseError};
pub fn parse_assembly<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Assembly<'a>, PsyParseError> { pub fn parse_assembly<'a>(
arena: &'a MemArena,
tree: &'a DataTree,
) -> Result<Assembly<'a>, PsyParseError> {
let mut builder = AssemblyBuilder::new(arena); let mut builder = AssemblyBuilder::new(arena);
if tree.is_internal() { if tree.is_internal() {
@ -52,16 +55,14 @@ pub fn parse_assembly<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<Ass
if builder.name_exists(name) { if builder.name_exists(name) {
builder.add_instance(name, Some(&xforms)); builder.add_instance(name, Some(&xforms));
} else { } else {
return Err( return Err(PsyParseError::InstancedMissingData(
PsyParseError::InstancedMissingData(
child.iter_leaf_children_with_type("Data").nth(0).unwrap().2, child.iter_leaf_children_with_type("Data").nth(0).unwrap().2,
"Attempted to add \ "Attempted to add \
instance for data with \ instance for data with \
a name that doesn't \ a name that doesn't \
exist.", exist.",
name.to_string(), name.to_string(),
) ));
);
} }
} }

View File

@ -15,7 +15,10 @@ use super::DataTree;
use super::psy::PsyParseError; use super::psy::PsyParseError;
pub fn parse_distant_disk_light<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<DistantDiskLight<'a>, PsyParseError> { pub fn parse_distant_disk_light<'a>(
arena: &'a MemArena,
tree: &'a DataTree,
) -> Result<DistantDiskLight<'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 directions = Vec::new(); let mut directions = Vec::new();
@ -44,7 +47,9 @@ pub fn parse_distant_disk_light<'a>(arena: &'a MemArena, tree: &'a DataTree) ->
contents, contents,
byte_offset, byte_offset,
} if type_name == "Direction" => { } if type_name == "Direction" => {
if let IResult::Done(_, direction) = closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) { if let IResult::Done(_, direction) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
directions.push(Vector::new(direction.0, direction.1, direction.2)); directions.push(Vector::new(direction.0, direction.1, direction.2));
} else { } else {
// Found direction, but its contents is not in the right format // Found direction, but its contents is not in the right format
@ -58,7 +63,9 @@ pub fn parse_distant_disk_light<'a>(arena: &'a MemArena, tree: &'a DataTree) ->
contents, contents,
byte_offset, byte_offset,
} if type_name == "Color" => { } if type_name == "Color" => {
if let IResult::Done(_, color) = closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) { if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly. // TODO: handle color space conversions properly.
// Probably will need a special color type with its // Probably will need a special color type with its
// own parser...? // own parser...?
@ -80,7 +87,10 @@ pub fn parse_distant_disk_light<'a>(arena: &'a MemArena, tree: &'a DataTree) ->
} }
pub fn parse_sphere_light<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<SphereLight<'a>, 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,7 +118,9 @@ pub fn parse_sphere_light<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result
contents, contents,
byte_offset, byte_offset,
} if type_name == "Color" => { } if type_name == "Color" => {
if let IResult::Done(_, color) = closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) { if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly. // TODO: handle color space conversions properly.
// Probably will need a special color type with its // Probably will need a special color type with its
// own parser...? // own parser...?
@ -129,7 +141,10 @@ pub fn parse_sphere_light<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result
} }
} }
pub fn parse_rectangle_light<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<RectangleLight<'a>, 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();
@ -143,7 +158,9 @@ pub fn parse_rectangle_light<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Res
contents, contents,
byte_offset, byte_offset,
} if type_name == "Dimensions" => { } if type_name == "Dimensions" => {
if let IResult::Done(_, radius) = closure!(tuple!(ws_f32, ws_f32))(contents.as_bytes()) { if let IResult::Done(_, radius) =
closure!(tuple!(ws_f32, ws_f32))(contents.as_bytes())
{
dimensions.push(radius); dimensions.push(radius);
} else { } else {
// Found dimensions, but its contents is not in the right format // Found dimensions, but its contents is not in the right format
@ -157,7 +174,9 @@ pub fn parse_rectangle_light<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Res
contents, contents,
byte_offset, byte_offset,
} if type_name == "Color" => { } if type_name == "Color" => {
if let IResult::Done(_, color) = closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) { if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes())
{
// TODO: handle color space conversions properly. // TODO: handle color space conversions properly.
// Probably will need a special color type with its // Probably will need a special color type with its
// own parser...? // own parser...?

View File

@ -21,7 +21,10 @@ use super::psy::PsyParseError;
// accel: BVH, // accel: BVH,
// } // }
pub fn parse_mesh_surface<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result<TriangleMesh<'a>, 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();
@ -37,7 +40,9 @@ pub fn parse_mesh_surface<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result
// Collect verts for this time sample // Collect verts for this time sample
let mut vert_count = 0; let mut vert_count = 0;
while let IResult::Done(remaining, vert) = closure!(tuple!(ws_f32, ws_f32, ws_f32))(raw_text) { while let IResult::Done(remaining, vert) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(raw_text)
{
raw_text = remaining; raw_text = remaining;
verts.push(Point::new(vert.0, vert.1, vert.2)); verts.push(Point::new(vert.0, vert.1, vert.2));
@ -88,7 +93,11 @@ pub fn parse_mesh_surface<'a>(arena: &'a MemArena, tree: &'a DataTree) -> Result
// Store all the time samples of each triangle contiguously // Store all the time samples of each triangle contiguously
for time_sample in 0..time_samples { for time_sample in 0..time_samples {
let start_vi = vert_count * time_sample; let start_vi = vert_count * time_sample;
triangles.push((verts[start_vi + face_vert_indices[v1]], verts[start_vi + face_vert_indices[v1 + vi + 1]], verts[start_vi + face_vert_indices[v1 + vi + 2]])); triangles.push((
verts[start_vi + face_vert_indices[v1]],
verts[start_vi + face_vert_indices[v1 + vi + 1]],
verts[start_vi + face_vert_indices[v1 + vi + 2]],
));
} }
} }
} else { } else {

View File

@ -68,7 +68,13 @@ impl RenderStats {
} }
impl<'a> Renderer<'a> { impl<'a> Renderer<'a> {
pub fn render(&self, max_samples_per_bucket: u32, crop: Option<(u32, u32, u32, u32)>, thread_count: u32, do_blender_output: bool) -> (Image, RenderStats) { pub fn render(
&self,
max_samples_per_bucket: u32,
crop: Option<(u32, u32, u32, u32)>,
thread_count: u32,
do_blender_output: bool,
) -> (Image, RenderStats) {
let mut tpool = Pool::new(thread_count); let mut tpool = Pool::new(thread_count);
let image = Image::new(self.resolution.0, self.resolution.1); let image = Image::new(self.resolution.0, self.resolution.1);
@ -97,8 +103,7 @@ impl<'a> Renderer<'a> {
}; };
// Render // Render
tpool.scoped( tpool.scoped(|scope| {
|scope| {
// Spawn worker tasks // Spawn worker tasks
for _ in 0..thread_count { for _ in 0..thread_count {
let jq = &job_queue; let jq = &job_queue;
@ -106,8 +111,7 @@ impl<'a> Renderer<'a> {
let img = &image; let img = &image;
let pixrenref = &pixels_rendered; let pixrenref = &pixels_rendered;
let cstats = &collective_stats; let cstats = &collective_stats;
scope.execute( scope.execute(move || {
move || {
self.render_job( self.render_job(
jq, jq,
ajq, ajq,
@ -117,8 +121,7 @@ impl<'a> Renderer<'a> {
cstats, cstats,
do_blender_output, do_blender_output,
) )
} });
);
} }
// Print initial 0.00% progress // Print initial 0.00% progress
@ -162,21 +165,18 @@ impl<'a> Renderer<'a> {
bucket_h bucket_h
}; };
if x < width && y < height && w > 0 && h > 0 { if x < width && y < height && w > 0 && h > 0 {
job_queue.push( job_queue.push(BucketJob {
BucketJob {
x: (start_x + x) as u32, x: (start_x + x) as u32,
y: (start_y + y) as u32, y: (start_y + y) as u32,
w: w as u32, w: w as u32,
h: h as u32, h: h as u32,
} });
);
} }
} }
// Mark done queuing jobs // Mark done queuing jobs
*all_jobs_queued.write().unwrap() = true; *all_jobs_queued.write().unwrap() = true;
} });
);
// Clear percentage progress print // Clear percentage progress print
print!( print!(
@ -188,7 +188,16 @@ impl<'a> Renderer<'a> {
} }
/// Waits for buckets in the job queue to render and renders them when available. /// Waits for buckets in the job queue to render and renders them when available.
fn render_job(&self, job_queue: &MsQueue<BucketJob>, all_jobs_queued: &RwLock<bool>, image: &Image, total_pixels: usize, pixels_rendered: &Mutex<Cell<usize>>, collected_stats: &RwLock<RenderStats>, do_blender_output: bool) { fn render_job(
&self,
job_queue: &MsQueue<BucketJob>,
all_jobs_queued: &RwLock<bool>,
image: &Image,
total_pixels: usize,
pixels_rendered: &Mutex<Cell<usize>>,
collected_stats: &RwLock<RenderStats>,
do_blender_output: bool,
) {
let mut stats = RenderStats::new(); let mut stats = RenderStats::new();
let mut timer = Timer::new(); let mut timer = Timer::new();
let mut total_timer = Timer::new(); let mut total_timer = Timer::new();
@ -246,7 +255,10 @@ impl<'a> Renderer<'a> {
&self.scene, &self.scene,
(x, y), (x, y),
(img_x, img_y), (img_x, img_y),
(get_sample(0, offset + si as u32), get_sample(1, offset + si as u32)), (
get_sample(0, offset + si as u32),
get_sample(1, offset + si as u32),
),
get_sample(2, offset + si as u32), get_sample(2, offset + si as u32),
map_0_1_to_wavelength(get_sample(3, offset + si as u32)), map_0_1_to_wavelength(get_sample(3, offset + si as u32)),
offset + si as u32, offset + si as u32,
@ -266,11 +278,9 @@ impl<'a> Renderer<'a> {
stats.trace_time += timer.tick() as f64; stats.trace_time += timer.tick() as f64;
// Determine next rays to shoot based on result // Determine next rays to shoot based on result
pi = partition_pair( pi = partition_pair(&mut paths[..pi], &mut rays[..pi], |i, path, ray| {
&mut paths[..pi], path.next(&mut xform_stack, &self.scene, &isects[i], &mut *ray)
&mut rays[..pi], });
|i, path, ray| path.next(&mut xform_stack, &self.scene, &isects[i], &mut *ray),
);
stats.ray_generation_time += timer.tick() as f64; stats.ray_generation_time += timer.tick() as f64;
} }
@ -326,12 +336,10 @@ impl<'a> Renderer<'a> {
} }
stats.total_time += total_timer.tick() as f64; stats.total_time += total_timer.tick() as f64;
ACCEL_TRAV_TIME.with( ACCEL_TRAV_TIME.with(|att| {
|att| {
stats.accel_traversal_time = att.get(); stats.accel_traversal_time = att.get();
att.set(0.0); att.set(0.0);
} });
);
// Collect stats // Collect stats
collected_stats.write().unwrap().collect(stats); collected_stats.write().unwrap().collect(stats);
@ -366,8 +374,17 @@ pub struct LightPath {
} }
impl LightPath { impl LightPath {
fn new(scene: &Scene, pixel_co: (u32, u32), image_plane_co: (f32, f32), lens_uv: (f32, f32), time: f32, wavelength: f32, lds_offset: u32) -> (LightPath, Ray) { fn new(
(LightPath { scene: &Scene,
pixel_co: (u32, u32),
image_plane_co: (f32, f32),
lens_uv: (f32, f32),
time: f32,
wavelength: f32,
lds_offset: u32,
) -> (LightPath, Ray) {
(
LightPath {
event: LightPathEvent::CameraRay, event: LightPathEvent::CameraRay,
bounce_count: 0, bounce_count: 0,
@ -385,15 +402,14 @@ impl LightPath {
color: Float4::splat(0.0), color: Float4::splat(0.0),
}, },
scene scene.camera.generate_ray(
.camera
.generate_ray(
image_plane_co.0, image_plane_co.0,
image_plane_co.1, image_plane_co.1,
time, time,
lens_uv.0, lens_uv.0,
lens_uv.1, lens_uv.1,
)) ),
)
} }
fn next_lds_samp(&self) -> f32 { fn next_lds_samp(&self) -> f32 {
@ -402,7 +418,13 @@ impl LightPath {
get_sample(dimension, self.lds_offset) get_sample(dimension, self.lds_offset)
} }
fn next(&mut self, xform_stack: &mut TransformStack, scene: &Scene, isect: &surface::SurfaceIntersection, ray: &mut Ray) -> bool { fn next(
&mut self,
xform_stack: &mut TransformStack,
scene: &Scene,
isect: &surface::SurfaceIntersection,
ray: &mut Ray,
) -> bool {
match self.event { match self.event {
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// Result of Camera or bounce ray, prepare next bounce and light rays // Result of Camera or bounce ray, prepare next bounce and light rays
@ -411,14 +433,23 @@ impl LightPath {
if let &surface::SurfaceIntersection::Hit { if let &surface::SurfaceIntersection::Hit {
intersection_data: ref idata, intersection_data: ref idata,
ref closure, ref closure,
} = isect { } = isect
{
// Hit something! Do the stuff // Hit something! Do the stuff
// Prepare light ray // Prepare light ray
let light_n = self.next_lds_samp(); let light_n = self.next_lds_samp();
let light_uvw = (self.next_lds_samp(), self.next_lds_samp(), self.next_lds_samp()); let light_uvw = (
self.next_lds_samp(),
self.next_lds_samp(),
self.next_lds_samp(),
);
xform_stack.clear(); xform_stack.clear();
let found_light = if let Some((light_color, shadow_vec, light_pdf, light_sel_pdf, is_infinite)) = let found_light = if let Some((light_color,
shadow_vec,
light_pdf,
light_sel_pdf,
is_infinite)) =
scene.sample_lights( scene.sample_lights(
xform_stack, xform_stack,
light_n, light_n,
@ -426,15 +457,22 @@ impl LightPath {
self.wavelength, self.wavelength,
self.time, self.time,
isect, isect,
) { )
{
// Check if pdf is zero, to avoid NaN's. // Check if pdf is zero, to avoid NaN's.
if light_pdf > 0.0 { if light_pdf > 0.0 {
// Calculate and store the light that will be contributed // Calculate and store the light that will be contributed
// to the film plane if the light is not in shadow. // to the film plane if the light is not in shadow.
self.pending_color_addition = { self.pending_color_addition = {
let material = closure.as_surface_closure(); let material = closure.as_surface_closure();
let la = material.evaluate(ray.dir, shadow_vec, idata.nor, self.wavelength); let la = material.evaluate(
light_color.e * la.e * self.light_attenuation / (light_pdf * light_sel_pdf) ray.dir,
shadow_vec,
idata.nor,
self.wavelength,
);
light_color.e * la.e * self.light_attenuation /
(light_pdf * light_sel_pdf)
}; };
// Calculate the shadow ray for testing if the light is // Calculate the shadow ray for testing if the light is
@ -480,7 +518,12 @@ impl LightPath {
self.next_attentuation_fac = filter.e / pdf; self.next_attentuation_fac = filter.e / pdf;
// Calculate the ray for this bounce // Calculate the ray for this bounce
self.next_bounce_ray = Some(Ray::new(idata.pos + dir.normalized() * 0.0001, dir, self.time, false)); self.next_bounce_ray = Some(Ray::new(
idata.pos + dir.normalized() * 0.0001,
dir,
self.time,
false,
));
true true
} else { } else {

View File

@ -1,3 +1,5 @@
mod monte_carlo; mod monte_carlo;
pub use self::monte_carlo::{square_to_circle, cosine_sample_hemisphere, uniform_sample_hemisphere, uniform_sample_sphere, uniform_sample_cone, uniform_sample_cone_pdf, spherical_triangle_solid_angle, uniform_sample_spherical_triangle}; pub use self::monte_carlo::{square_to_circle, cosine_sample_hemisphere, uniform_sample_hemisphere,
uniform_sample_sphere, uniform_sample_cone, uniform_sample_cone_pdf,
spherical_triangle_solid_angle, uniform_sample_spherical_triangle};

View File

@ -120,7 +120,13 @@ pub fn spherical_triangle_solid_angle(va: Vector, vb: Vector, vc: Vector) -> f32
/// Generates a uniform sample on a spherical triangle given two uniform /// Generates a uniform sample on a spherical triangle given two uniform
/// random variables i and j in [0, 1]. /// random variables i and j in [0, 1].
pub fn uniform_sample_spherical_triangle(va: Vector, vb: Vector, vc: Vector, i: f32, j: f32) -> Vector { pub fn uniform_sample_spherical_triangle(
va: Vector,
vb: Vector,
vc: Vector,
i: f32,
j: f32,
) -> Vector {
// Calculate sines and cosines of the spherical triangle's edge lengths // Calculate sines and cosines of the spherical triangle's edge lengths
let cos_a: f64 = dot(vb, vc).max(-1.0).min(1.0) as f64; let cos_a: f64 = dot(vb, vc).max(-1.0).min(1.0) as f64;
let cos_b: f64 = dot(vc, va).max(-1.0).min(1.0) as f64; let cos_b: f64 = dot(vc, va).max(-1.0).min(1.0) as f64;
@ -172,7 +178,8 @@ pub fn uniform_sample_spherical_triangle(va: Vector, vb: Vector, vc: Vector, i:
let q_bottom = ((v * s) + (u * t)) * sin_va; let q_bottom = ((v * s) + (u * t)) * sin_va;
let q = q_top / q_bottom; let q = q_top / q_bottom;
let vc_2 = (va * q as f32) + ((vc - (va * dot(vc, va))).normalized() * (1.0 - (q * q)).sqrt() as f32); let vc_2 = (va * q as f32) +
((vc - (va * dot(vc, va))).normalized() * (1.0 - (q * q)).sqrt() as f32);
let z = 1.0 - (j * (1.0 - dot(vc_2, vb))); let z = 1.0 - (j * (1.0 - dot(vc_2, vb)));

View File

@ -36,26 +36,35 @@ pub struct Assembly<'a> {
impl<'a> Assembly<'a> { impl<'a> Assembly<'a> {
// Returns (light_color, shadow_vector, pdf, selection_pdf) // Returns (light_color, shadow_vector, pdf, selection_pdf)
pub fn sample_lights(&self, xform_stack: &mut TransformStack, n: f32, uvw: (f32, f32, f32), wavelength: f32, time: f32, intr: &SurfaceIntersection) -> Option<(SpectralSample, Vector, f32, f32)> { pub fn sample_lights(
&self,
xform_stack: &mut TransformStack,
n: f32,
uvw: (f32, f32, f32),
wavelength: f32,
time: f32,
intr: &SurfaceIntersection,
) -> Option<(SpectralSample, Vector, f32, f32)> {
if let &SurfaceIntersection::Hit { if let &SurfaceIntersection::Hit {
intersection_data: idata, intersection_data: idata,
closure, closure,
} = intr { } = intr
{
let sel_xform = if xform_stack.top().len() > 0 { let sel_xform = if xform_stack.top().len() > 0 {
lerp_slice(xform_stack.top(), time) lerp_slice(xform_stack.top(), time)
} else { } else {
Matrix4x4::new() Matrix4x4::new()
}; };
if let Some((light_i, sel_pdf, whittled_n)) = if let Some((light_i, sel_pdf, whittled_n)) =
self.light_accel self.light_accel.select(
.select(
idata.incoming * sel_xform, idata.incoming * sel_xform,
idata.pos * sel_xform, idata.pos * sel_xform,
idata.nor * sel_xform, idata.nor * sel_xform,
closure.as_surface_closure(), closure.as_surface_closure(),
time, time,
n, n,
) { )
{
let inst = self.light_instances[light_i]; let inst = self.light_instances[light_i];
match inst.instance_type { match inst.instance_type {
@ -81,7 +90,8 @@ impl<'a> Assembly<'a> {
}; };
// Sample the light // Sample the light
let (color, shadow_vec, pdf) = light.sample(&xform, idata.pos, uvw.0, uvw.1, wavelength, time); let (color, shadow_vec, pdf) =
light.sample(&xform, idata.pos, uvw.0, uvw.1, wavelength, time);
return Some((color, shadow_vec, pdf, sel_pdf)); return Some((color, shadow_vec, pdf, sel_pdf));
} }
@ -97,7 +107,14 @@ impl<'a> Assembly<'a> {
} }
// Sample sub-assembly lights // Sample sub-assembly lights
let sample = self.assemblies[inst.data_index].sample_lights(xform_stack, whittled_n, uvw, wavelength, time, intr); let sample = self.assemblies[inst.data_index].sample_lights(
xform_stack,
whittled_n,
uvw,
wavelength,
time,
intr,
);
// Pop the assembly's transforms off the transform stack. // Pop the assembly's transforms off the transform stack.
if let Some(_) = inst.transform_indices { if let Some(_) = inst.transform_indices {
@ -176,8 +193,10 @@ impl<'a> AssemblyBuilder<'a> {
} }
// Add assembly // Add assembly
self.assembly_map self.assembly_map.insert(
.insert(name.to_string(), self.assemblies.len()); name.to_string(),
self.assemblies.len(),
);
self.assemblies.push(asmb); self.assemblies.push(asmb);
} }
@ -200,14 +219,18 @@ impl<'a> AssemblyBuilder<'a> {
instance_type: InstanceType::Object, instance_type: InstanceType::Object,
data_index: self.object_map[name], data_index: self.object_map[name],
id: self.instances.len(), id: self.instances.len(),
transform_indices: xforms.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())), transform_indices: xforms.map(
|xf| (self.xforms.len(), self.xforms.len() + xf.len()),
),
} }
} else { } else {
Instance { Instance {
instance_type: InstanceType::Assembly, instance_type: InstanceType::Assembly,
data_index: self.assembly_map[name], data_index: self.assembly_map[name],
id: self.instances.len(), id: self.instances.len(),
transform_indices: xforms.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())), transform_indices: xforms.map(
|xf| (self.xforms.len(), self.xforms.len() + xf.len()),
),
} }
}; };
@ -228,19 +251,15 @@ 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( let object_accel = BVH::from_objects(self.arena, &mut self.instances[..], 1, |inst| {
self.arena, &bbs[bis[inst.id]..bis[inst.id + 1]]
&mut self.instances[..], });
1,
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]],
);
// Get list of instances that are for light sources or assemblies that contain light // Get list of instances that are for light sources or assemblies that contain light
// sources. // sources.
let mut light_instances: Vec<_> = self.instances let mut light_instances: Vec<_> = self.instances
.iter() .iter()
.filter( .filter(|inst| match inst.instance_type {
|inst| match inst.instance_type {
InstanceType::Object => { InstanceType::Object => {
if let Object::Light(_) = self.objects[inst.data_index] { if let Object::Light(_) = self.objects[inst.data_index] {
true true
@ -254,14 +273,12 @@ impl<'a> AssemblyBuilder<'a> {
.light_accel .light_accel
.approximate_energy() > 0.0 .approximate_energy() > 0.0
} }
} })
)
.map(|&a| a) .map(|&a| a)
.collect(); .collect();
// Build light accel // Build light accel
let light_accel = LightTree::from_objects( let light_accel = LightTree::from_objects(self.arena, &mut light_instances[..], |inst| {
self.arena, &mut light_instances[..], |inst| {
let bounds = &bbs[bis[inst.id]..bis[inst.id + 1]]; let bounds = &bbs[bis[inst.id]..bis[inst.id + 1]];
let energy = match inst.instance_type { let energy = match inst.instance_type {
InstanceType::Object => { InstanceType::Object => {
@ -279,8 +296,7 @@ impl<'a> AssemblyBuilder<'a> {
} }
}; };
(bounds, energy) (bounds, energy)
} });
);
Assembly { Assembly {
instances: self.arena.copy_slice(&self.instances), instances: self.arena.copy_slice(&self.instances),

View File

@ -19,7 +19,15 @@ pub struct Scene<'a> {
} }
impl<'a> Scene<'a> { impl<'a> Scene<'a> {
pub fn sample_lights(&self, xform_stack: &mut TransformStack, n: f32, uvw: (f32, f32, f32), wavelength: f32, time: f32, intr: &SurfaceIntersection) -> Option<(SpectralSample, Vector, f32, f32, bool)> { pub fn sample_lights(
&self,
xform_stack: &mut TransformStack,
n: f32,
uvw: (f32, f32, f32),
wavelength: f32,
time: f32,
intr: &SurfaceIntersection,
) -> Option<(SpectralSample, Vector, f32, f32, bool)> {
// TODO: this just selects between world lights and local lights // TODO: this just selects between world lights and local lights
// with a 50/50 chance. We should do something more sophisticated // with a 50/50 chance. We should do something more sophisticated
// than this, accounting for the estimated impact of the lights // than this, accounting for the estimated impact of the lights
@ -27,10 +35,10 @@ impl<'a> Scene<'a> {
// Calculate relative probabilities of traversing into world lights // Calculate relative probabilities of traversing into world lights
// or local lights. // or local lights.
let wl_energy = if self.world let wl_energy = if self.world.lights.iter().fold(0.0, |energy, light| {
.lights energy + light.approximate_energy()
.iter() }) <= 0.0
.fold(0.0, |energy, light| energy + light.approximate_energy()) <= 0.0 { {
0.0 0.0
} else { } else {
1.0 1.0
@ -59,8 +67,15 @@ impl<'a> Scene<'a> {
let n = (n - wl_prob) / (1.0 - wl_prob); let n = (n - wl_prob) / (1.0 - wl_prob);
if let Some((ss, sv, pdf, spdf)) = if let Some((ss, sv, pdf, spdf)) =
self.root self.root.sample_lights(
.sample_lights(xform_stack, n, uvw, wavelength, time, intr) { xform_stack,
n,
uvw,
wavelength,
time,
intr,
)
{
return Some((ss, sv, pdf, spdf * (1.0 - wl_prob), false)); return Some((ss, sv, pdf, spdf * (1.0 - wl_prob), false));
} else { } else {
return None; return None;

View File

@ -42,7 +42,13 @@ pub trait SurfaceClosure {
/// wavelength: The wavelength of light to sample at. /// wavelength: The wavelength of light to sample at.
/// ///
/// Returns a tuple with the generated outgoing light direction, color filter, and pdf. /// Returns a tuple with the generated outgoing light direction, color filter, and pdf.
fn sample(&self, inc: Vector, nor: Normal, uv: (f32, f32), wavelength: f32) -> (Vector, SpectralSample, f32); fn sample(
&self,
inc: Vector,
nor: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32);
/// Evaluates the closure for the given incoming and outgoing rays. /// Evaluates the closure for the given incoming and outgoing rays.
/// ///
@ -67,7 +73,13 @@ pub trait SurfaceClosure {
/// This is used for importance sampling, so does not need to be exact, /// This is used for importance sampling, so does not need to be exact,
/// but it does need to be non-zero anywhere that an exact solution would /// but it does need to be non-zero anywhere that an exact solution would
/// be non-zero. /// be non-zero.
fn estimate_eval_over_solid_angle(&self, inc: Vector, out: Vector, nor: Normal, cos_theta: f32) -> f32; fn estimate_eval_over_solid_angle(
&self,
inc: Vector,
out: Vector,
nor: Normal,
cos_theta: f32,
) -> f32;
} }
@ -163,10 +175,20 @@ impl SurfaceClosure for EmitClosure {
false false
} }
fn sample(&self, inc: Vector, nor: Normal, uv: (f32, f32), wavelength: f32) -> (Vector, SpectralSample, f32) { fn sample(
&self,
inc: Vector,
nor: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32) {
let _ = (inc, nor, uv); // Not using these, silence warning let _ = (inc, nor, uv); // Not using these, silence warning
(Vector::new(0.0, 0.0, 0.0), SpectralSample::new(wavelength), 1.0) (
Vector::new(0.0, 0.0, 0.0),
SpectralSample::new(wavelength),
1.0,
)
} }
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample { fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample {
@ -181,7 +203,13 @@ impl SurfaceClosure for EmitClosure {
1.0 1.0
} }
fn estimate_eval_over_solid_angle(&self, inc: Vector, out: Vector, nor: Normal, cos_theta: f32) -> f32 { fn estimate_eval_over_solid_angle(
&self,
inc: Vector,
out: Vector,
nor: Normal,
cos_theta: f32,
) -> f32 {
let _ = (inc, out, nor, cos_theta); // Not using these, silence warning let _ = (inc, out, nor, cos_theta); // Not using these, silence warning
// TODO: what to do here? // TODO: what to do here?
@ -207,13 +235,18 @@ impl SurfaceClosure for LambertClosure {
false false
} }
fn sample(&self, inc: Vector, nor: Normal, uv: (f32, f32), wavelength: f32) -> (Vector, SpectralSample, f32) { fn sample(
&self,
inc: Vector,
nor: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32) {
let nn = if dot(nor.into_vector(), inc) <= 0.0 { let nn = if dot(nor.into_vector(), inc) <= 0.0 {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() -nor.normalized()
} }.into_vector();
.into_vector();
// Generate a random ray direction in the hemisphere // Generate a random ray direction in the hemisphere
// of the surface. // of the surface.
@ -231,8 +264,7 @@ impl SurfaceClosure for LambertClosure {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() -nor.normalized()
} }.into_vector();
.into_vector();
let fac = dot(nn, v).max(0.0) * INV_PI; let fac = dot(nn, v).max(0.0) * INV_PI;
self.col.to_spectral_sample(wavelength) * fac self.col.to_spectral_sample(wavelength) * fac
@ -244,13 +276,18 @@ impl SurfaceClosure for LambertClosure {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() -nor.normalized()
} }.into_vector();
.into_vector();
dot(nn, v).max(0.0) * INV_PI dot(nn, v).max(0.0) * INV_PI
} }
fn estimate_eval_over_solid_angle(&self, inc: Vector, out: Vector, nor: Normal, cos_theta: f32) -> f32 { fn estimate_eval_over_solid_angle(
&self,
inc: Vector,
out: Vector,
nor: Normal,
cos_theta: f32,
) -> f32 {
assert!(cos_theta >= -1.0 && cos_theta <= 1.0); assert!(cos_theta >= -1.0 && cos_theta <= 1.0);
// Analytically calculates lambert shading from a uniform light source // Analytically calculates lambert shading from a uniform light source
@ -295,8 +332,7 @@ impl SurfaceClosure for LambertClosure {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() -nor.normalized()
} }.into_vector();
.into_vector();
let cos_nv = dot(nn, v).max(-1.0).min(1.0); let cos_nv = dot(nn, v).max(-1.0).min(1.0);
@ -375,7 +411,9 @@ impl GTRClosure {
let roughness2 = self.roughness * self.roughness; let roughness2 = self.roughness * self.roughness;
// Calculate top half of equation // Calculate top half of equation
let top = 1.0 - ((roughness2.powf(1.0 - self.tail_shape) * (1.0 - u)) + u).powf(1.0 / (1.0 - self.tail_shape)); let top = 1.0 -
((roughness2.powf(1.0 - self.tail_shape) * (1.0 - u)) + u)
.powf(1.0 / (1.0 - self.tail_shape));
// Calculate bottom half of equation // Calculate bottom half of equation
let bottom = 1.0 - roughness2; let bottom = 1.0 - roughness2;
@ -408,14 +446,19 @@ impl SurfaceClosure for GTRClosure {
} }
fn sample(&self, inc: Vector, nor: Normal, uv: (f32, f32), wavelength: f32) -> (Vector, SpectralSample, f32) { fn sample(
&self,
inc: Vector,
nor: Normal,
uv: (f32, f32),
wavelength: f32,
) -> (Vector, SpectralSample, f32) {
// Get normalized surface normal // Get normalized surface normal
let nn = if dot(nor.into_vector(), inc) < 0.0 { let nn = if dot(nor.into_vector(), inc) < 0.0 {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() // If back-facing, flip normal -nor.normalized() // If back-facing, flip normal
} }.into_vector();
.into_vector();
// Generate a random ray direction in the hemisphere // Generate a random ray direction in the hemisphere
// of the surface. // of the surface.
@ -444,8 +487,7 @@ impl SurfaceClosure for GTRClosure {
-nor.normalized() // If back-facing, flip normal -nor.normalized() // If back-facing, flip normal
} else { } else {
nor.normalized() nor.normalized()
} }.into_vector();
.into_vector();
// Calculate needed dot products // Calculate needed dot products
let na = clamp(dot(nn, aa), -1.0, 1.0); let na = clamp(dot(nn, aa), -1.0, 1.0);
@ -541,8 +583,7 @@ impl SurfaceClosure for GTRClosure {
-nor.normalized() // If back-facing, flip normal -nor.normalized() // If back-facing, flip normal
} else { } else {
nor.normalized() nor.normalized()
} }.into_vector();
.into_vector();
// Calculate needed dot products // Calculate needed dot products
let nh = clamp(dot(nn, hh), -1.0, 1.0); let nh = clamp(dot(nn, hh), -1.0, 1.0);
@ -551,7 +592,13 @@ impl SurfaceClosure for GTRClosure {
} }
fn estimate_eval_over_solid_angle(&self, inc: Vector, out: Vector, nor: Normal, cos_theta: f32) -> f32 { fn estimate_eval_over_solid_angle(
&self,
inc: Vector,
out: Vector,
nor: Normal,
cos_theta: f32,
) -> f32 {
// TODO: all of the stuff in this function is horribly hacky. // TODO: all of the stuff in this function is horribly hacky.
// Find a proper way to approximate the light contribution from a // Find a proper way to approximate the light contribution from a
// solid angle. // solid angle.
@ -563,8 +610,7 @@ impl SurfaceClosure for GTRClosure {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() // If back-facing, flip normal -nor.normalized() // If back-facing, flip normal
} }.into_vector();
.into_vector();
let aa = -inc.normalized(); // Vector pointing to where "in" came from let aa = -inc.normalized(); // Vector pointing to where "in" came from
let bb = out.normalized(); // Out let bb = out.normalized(); // Out

View File

@ -12,7 +12,13 @@ use shading::surface_closure::SurfaceClosureUnion;
pub trait Surface: Boundable + Debug + Sync { pub trait Surface: Boundable + Debug + Sync {
fn intersect_rays(&self, accel_rays: &mut [AccelRay], wrays: &[Ray], isects: &mut [SurfaceIntersection], space: &[Matrix4x4]); fn intersect_rays(
&self,
accel_rays: &mut [AccelRay],
wrays: &[Ray],
isects: &mut [SurfaceIntersection],
space: &[Matrix4x4],
);
} }

View File

@ -24,7 +24,11 @@ pub struct TriangleMesh<'a> {
} }
impl<'a> TriangleMesh<'a> { impl<'a> TriangleMesh<'a> {
pub fn from_triangles<'b>(arena: &'b MemArena, time_samples: usize, triangles: Vec<(Point, Point, Point)>) -> TriangleMesh<'b> { pub fn from_triangles<'b>(
arena: &'b MemArena,
time_samples: usize,
triangles: Vec<(Point, Point, Point)>,
) -> 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,12 +45,9 @@ impl<'a> TriangleMesh<'a> {
bounds bounds
}; };
let accel = BVH::from_objects( let accel = BVH::from_objects(arena, &mut indices[..], 3, |tri_i| {
arena, &bounds[*tri_i..(*tri_i + time_samples)]
&mut indices[..], });
3,
|tri_i| &bounds[*tri_i..(*tri_i + time_samples)],
);
TriangleMesh { TriangleMesh {
time_samples: time_samples, time_samples: time_samples,
@ -65,7 +66,13 @@ impl<'a> Boundable for TriangleMesh<'a> {
impl<'a> Surface for TriangleMesh<'a> { impl<'a> Surface for TriangleMesh<'a> {
fn intersect_rays(&self, accel_rays: &mut [AccelRay], wrays: &[Ray], isects: &mut [SurfaceIntersection], space: &[Matrix4x4]) { fn intersect_rays(
&self,
accel_rays: &mut [AccelRay],
wrays: &[Ray],
isects: &mut [SurfaceIntersection],
space: &[Matrix4x4],
) {
self.accel self.accel
.traverse( .traverse(
&mut accel_rays[..], &self.indices, |tri_i, rs| { &mut accel_rays[..], &self.indices, |tri_i, rs| {
@ -96,13 +103,17 @@ impl<'a> Surface for TriangleMesh<'a> {
incoming: wr.dir, incoming: wr.dir,
t: t, t: t,
pos: wr.orig + (wr.dir * t), pos: wr.orig + (wr.dir * t),
nor: cross(tri.0 - tri.1, tri.0 - tri.2).into_normal(), // TODO nor: cross(tri.0 - tri.1, tri.0 - tri.2)
nor_g: cross(tri.0 - tri.1, tri.0 - tri.2).into_normal(), .into_normal(), // TODO
nor_g: cross(tri.0 - tri.1, tri.0 - tri.2)
.into_normal(),
uv: (0.0, 0.0), // TODO uv: (0.0, 0.0), // TODO
local_space: mat_space, local_space: mat_space,
}, },
// TODO: get surface closure from surface shader. // TODO: get surface closure from surface shader.
closure: SurfaceClosureUnion::LambertClosure(LambertClosure::new(XYZ::new(0.8, 0.8, 0.8))), closure: SurfaceClosureUnion::LambertClosure(
LambertClosure::new(XYZ::new(0.8, 0.8, 0.8))
),
// closure: // closure:
// SurfaceClosureUnion::GTRClosure( // SurfaceClosureUnion::GTRClosure(
// GTRClosure::new(XYZ::new(0.8, 0.8, 0.8), // GTRClosure::new(XYZ::new(0.8, 0.8, 0.8),

View File

@ -29,12 +29,9 @@ impl<'a> Tracer<'a> {
self.rays.clear(); self.rays.clear();
self.rays.reserve(wrays.len()); self.rays.reserve(wrays.len());
let mut ids = 0..(wrays.len() as u32); let mut ids = 0..(wrays.len() as u32);
self.rays self.rays.extend(wrays.iter().map(
.extend( |wr| AccelRay::new(wr, ids.next().unwrap()),
wrays ));
.iter()
.map(|wr| AccelRay::new(wr, ids.next().unwrap()))
);
return self.inner.trace(wrays, &mut self.rays[..]); return self.inner.trace(wrays, &mut self.rays[..]);
} }
@ -51,8 +48,12 @@ impl<'a> TracerInner<'a> {
// Ready the isects // Ready the isects
self.isects.clear(); self.isects.clear();
self.isects.reserve(wrays.len()); self.isects.reserve(wrays.len());
self.isects self.isects.extend(
.extend(iter::repeat(SurfaceIntersection::Miss).take(wrays.len())); iter::repeat(SurfaceIntersection::Miss).take(
wrays
.len(),
),
);
let mut ray_sets = split_rays_by_direction(&mut rays[..]); let mut ray_sets = split_rays_by_direction(&mut rays[..]);
for ray_set in ray_sets.iter_mut().filter(|ray_set| ray_set.len() > 0) { for ray_set in ray_sets.iter_mut().filter(|ray_set| ray_set.len() > 0) {
@ -62,11 +63,16 @@ impl<'a> TracerInner<'a> {
return &self.isects; return &self.isects;
} }
fn trace_assembly<'b>(&'b mut self, assembly: &Assembly, wrays: &[Ray], accel_rays: &mut [AccelRay]) { fn trace_assembly<'b>(
assembly &'b mut self,
.object_accel assembly: &Assembly,
.traverse( wrays: &[Ray],
&mut accel_rays[..], &assembly.instances[..], |inst, rs| { accel_rays: &mut [AccelRay],
) {
assembly.object_accel.traverse(
&mut accel_rays[..],
&assembly.instances[..],
|inst, rs| {
// Transform rays if needed // Transform rays if needed
if let Some((xstart, xend)) = inst.transform_indices { if let Some((xstart, xend)) = inst.transform_indices {
// Push transforms to stack // Push transforms to stack
@ -77,7 +83,10 @@ impl<'a> TracerInner<'a> {
for ray in &mut rs[..] { for ray in &mut rs[..] {
let id = ray.id; let id = ray.id;
let t = ray.time; let t = ray.time;
ray.update_from_xformed_world_ray(&wrays[id as usize], &lerp_slice(xforms, t)); ray.update_from_xformed_world_ray(
&wrays[id as usize],
&lerp_slice(xforms, t),
);
} }
} }
@ -117,11 +126,19 @@ impl<'a> TracerInner<'a> {
for ray_set in ray_sets.iter_mut().filter(|ray_set| ray_set.len() > 0) { for ray_set in ray_sets.iter_mut().filter(|ray_set| ray_set.len() > 0) {
match inst.instance_type { match inst.instance_type {
InstanceType::Object => { InstanceType::Object => {
self.trace_object(&assembly.objects[inst.data_index], wrays, ray_set); self.trace_object(
&assembly.objects[inst.data_index],
wrays,
ray_set,
);
} }
InstanceType::Assembly => { InstanceType::Assembly => {
self.trace_assembly(&assembly.assemblies[inst.data_index], wrays, ray_set); self.trace_assembly(
&assembly.assemblies[inst.data_index],
wrays,
ray_set,
);
} }
} }
} }
@ -138,7 +155,10 @@ impl<'a> TracerInner<'a> {
for ray in &mut rs[..] { for ray in &mut rs[..] {
let id = ray.id; let id = ray.id;
let t = ray.time; let t = ray.time;
ray.update_from_xformed_world_ray(&wrays[id as usize], &lerp_slice(xforms, t)); ray.update_from_xformed_world_ray(
&wrays[id as usize],
&lerp_slice(xforms, t),
);
} }
} else { } else {
for ray in &mut rs[..] { for ray in &mut rs[..] {
@ -147,7 +167,7 @@ impl<'a> TracerInner<'a> {
} }
} }
} }
} },
); );
} }