Reformat code with rustfmt 0.9
This commit is contained in:
parent
46247ec9aa
commit
f649bec585
|
@ -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"
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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(
|
bounds_range: (bi, self.bounds.len()),
|
||||||
BVHBaseNode::Leaf {
|
object_range: (offset, offset + objects.len()),
|
||||||
bounds_range: (bi, self.bounds.len()),
|
});
|
||||||
object_range: (offset, offset + objects.len()),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.depth < depth {
|
if self.depth < depth {
|
||||||
self.depth = depth;
|
self.depth = depth;
|
||||||
|
@ -138,26 +146,24 @@ 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(
|
bounds_range: (0, 0),
|
||||||
BVHBaseNode::Internal {
|
children_indices: (0, 0),
|
||||||
bounds_range: (0, 0),
|
split_axis: 0,
|
||||||
children_indices: (0, 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) =
|
||||||
// SAH splitting, when we have room to play
|
if (log2_64(objects.len() as u64) as usize) < (BVH_MAX_DEPTH - depth) {
|
||||||
sah_split(objects, &bounder)
|
// SAH splitting, when we have room to play
|
||||||
} else {
|
sah_split(objects, &bounder)
|
||||||
// Balanced splitting, when we don't have room to play
|
} else {
|
||||||
median_split(objects, &bounder)
|
// Balanced splitting, when we don't have room to play
|
||||||
};
|
median_split(objects, &bounder)
|
||||||
|
};
|
||||||
|
|
||||||
// Create child nodes
|
// Create child nodes
|
||||||
let (c1_index, c1_bounds) = self.recursive_build(
|
let (c1_index, c1_bounds) = self.recursive_build(
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(
|
is_leaf: true,
|
||||||
Node {
|
bounds_range: (bi, self.bounds.len()),
|
||||||
is_leaf: true,
|
energy: energy,
|
||||||
bounds_range: (bi, self.bounds.len()),
|
child_index: offset,
|
||||||
energy: energy,
|
});
|
||||||
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(
|
is_leaf: false,
|
||||||
Node {
|
bounds_range: (0, 0),
|
||||||
is_leaf: false,
|
energy: 0.0,
|
||||||
bounds_range: (0, 0),
|
child_index: 0,
|
||||||
energy: 0.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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,22 +321,20 @@ 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;
|
let centroid_b = (tb_b.min.get_n(split_axis) + tb_b.max.get_n(split_axis)) * 0.5;
|
||||||
let centroid_b = (tb_b.min.get_n(split_axis) + tb_b.max.get_n(split_axis)) * 0.5;
|
|
||||||
|
|
||||||
if centroid_a < centroid_b {
|
if centroid_a < centroid_b {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
} else if centroid_a == centroid_b {
|
} else if centroid_a == centroid_b {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else {
|
} else {
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
(place, split_axis)
|
(place, split_axis)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
@ -208,10 +215,10 @@ pub fn quick_select<T, F>(slc: &mut [T], n: usize, mut order: F)
|
||||||
|
|
||||||
slc.swap(i, right - 1);
|
slc.swap(i, right - 1);
|
||||||
let ii = left +
|
let ii = left +
|
||||||
{
|
{
|
||||||
let (val, list) = (&mut slc[left..right]).split_last_mut().unwrap();
|
let (val, list) = (&mut slc[left..right]).split_last_mut().unwrap();
|
||||||
partition(list, |n| order(n, val) == Ordering::Less)
|
partition(list, |n| order(n, val) == Ordering::Less)
|
||||||
};
|
};
|
||||||
slc.swap(ii, right - 1);
|
slc.swap(ii, right - 1);
|
||||||
|
|
||||||
if ii == n {
|
if ii == n {
|
||||||
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
28
src/image.rs
28
src/image.rs
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
146
src/main.rs
146
src/main.rs
|
@ -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,19 +183,21 @@ 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(),
|
||||||
if coords.0 > coords.2 {
|
u32::from_str(vals.next().unwrap()).unwrap(),
|
||||||
panic!("Argument '--crop': X1 must be less than or equal to X2");
|
u32::from_str(vals.next().unwrap()).unwrap(),
|
||||||
}
|
|
||||||
if coords.1 > coords.3 {
|
|
||||||
panic!("Argument '--crop': Y1 must be less than or equal to Y2");
|
|
||||||
}
|
|
||||||
coords
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
if coords.0 > coords.2 {
|
||||||
|
panic!("Argument '--crop': X1 must be less than or equal to X2");
|
||||||
|
}
|
||||||
|
if coords.1 > coords.3 {
|
||||||
|
panic!("Argument '--crop': Y1 must be less than or equal to Y2");
|
||||||
|
}
|
||||||
|
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,11 +274,12 @@ 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 =
|
||||||
u32::from_str(&max_samples_per_bucket).unwrap()
|
if let Some(max_samples_per_bucket) = args.value_of("max_bucket_samples") {
|
||||||
} else {
|
u32::from_str(&max_samples_per_bucket).unwrap()
|
||||||
4096
|
} else {
|
||||||
};
|
4096
|
||||||
|
};
|
||||||
|
|
||||||
let thread_count = if let Some(threads) = args.value_of("threads") {
|
let thread_count = if let Some(threads) = args.value_of("threads") {
|
||||||
u32::from_str(&threads).unwrap()
|
u32::from_str(&threads).unwrap()
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -137,11 +141,12 @@ impl<'a> DataTree<'a> {
|
||||||
// For unit tests
|
// For unit tests
|
||||||
fn internal_data_or_panic(&'a self) -> (&'a str, Option<&'a str>, &'a Vec<DataTree<'a>>) {
|
fn internal_data_or_panic(&'a self) -> (&'a str, Option<&'a str>, &'a Vec<DataTree<'a>>) {
|
||||||
if let DataTree::Internal {
|
if let DataTree::Internal {
|
||||||
type_name,
|
type_name,
|
||||||
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")
|
||||||
|
@ -149,10 +154,11 @@ impl<'a> DataTree<'a> {
|
||||||
}
|
}
|
||||||
fn leaf_data_or_panic(&'a self) -> (&'a str, &'a str) {
|
fn leaf_data_or_panic(&'a self) -> (&'a str, &'a str) {
|
||||||
if let DataTree::Leaf {
|
if let DataTree::Leaf {
|
||||||
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);
|
||||||
|
|
387
src/parse/psy.rs
387
src/parse/psy.rs
|
@ -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,49 +573,46 @@ 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,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32,
|
||||||
ws_f32,
|
ws_f32
|
||||||
ws_f32
|
),
|
||||||
),
|
nom::eof
|
||||||
nom::eof
|
))(contents.as_bytes())
|
||||||
)
|
{
|
||||||
)(contents.as_bytes()) {
|
return Ok(Matrix4x4::new_from_values(
|
||||||
return Ok(
|
ns.0,
|
||||||
Matrix4x4::new_from_values(
|
ns.4,
|
||||||
ns.0,
|
ns.8,
|
||||||
ns.4,
|
ns.12,
|
||||||
ns.8,
|
ns.1,
|
||||||
ns.12,
|
ns.5,
|
||||||
ns.1,
|
ns.9,
|
||||||
ns.5,
|
ns.13,
|
||||||
ns.9,
|
ns.2,
|
||||||
ns.13,
|
ns.6,
|
||||||
ns.2,
|
ns.10,
|
||||||
ns.6,
|
ns.14,
|
||||||
ns.10,
|
ns.3,
|
||||||
ns.14,
|
ns.7,
|
||||||
ns.3,
|
ns.11,
|
||||||
ns.7,
|
ns.15,
|
||||||
ns.11,
|
));
|
||||||
ns.15,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return Err(PsyParseError::UnknownError(0));
|
return Err(PsyParseError::UnknownError(0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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...?
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
297
src/renderer.rs
297
src/renderer.rs
|
@ -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,86 +103,80 @@ 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;
|
let ajq = &all_jobs_queued;
|
||||||
let ajq = &all_jobs_queued;
|
let img = ℑ
|
||||||
let img = ℑ
|
let pixrenref = &pixels_rendered;
|
||||||
let pixrenref = &pixels_rendered;
|
let cstats = &collective_stats;
|
||||||
let cstats = &collective_stats;
|
scope.execute(move || {
|
||||||
scope.execute(
|
self.render_job(
|
||||||
move || {
|
jq,
|
||||||
self.render_job(
|
ajq,
|
||||||
jq,
|
img,
|
||||||
ajq,
|
width * height,
|
||||||
img,
|
pixrenref,
|
||||||
width * height,
|
cstats,
|
||||||
pixrenref,
|
do_blender_output,
|
||||||
cstats,
|
)
|
||||||
do_blender_output,
|
});
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print initial 0.00% progress
|
|
||||||
print!("0.00%");
|
|
||||||
let _ = io::stdout().flush();
|
|
||||||
|
|
||||||
// Determine bucket size based on the per-thread maximum number of samples to
|
|
||||||
// calculate at a time.
|
|
||||||
let (bucket_w, bucket_h) = {
|
|
||||||
let target_pixels_per_bucket = max_samples_per_bucket as f64 / self.spp as f64;
|
|
||||||
let target_bucket_dim = if target_pixels_per_bucket.sqrt() < 1.0 {
|
|
||||||
1usize
|
|
||||||
} else {
|
|
||||||
target_pixels_per_bucket.sqrt() as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
(target_bucket_dim, target_bucket_dim)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Populate job queue
|
|
||||||
let bucket_n = {
|
|
||||||
let bucket_count_x = ((width / bucket_w) + 1) as u32;
|
|
||||||
let bucket_count_y = ((height / bucket_h) + 1) as u32;
|
|
||||||
let larger = cmp::max(bucket_count_x, bucket_count_y);
|
|
||||||
let pow2 = upper_power_of_two(larger);
|
|
||||||
pow2 * pow2
|
|
||||||
};
|
|
||||||
for hilbert_d in 0..bucket_n {
|
|
||||||
let (bx, by) = hilbert::d2xy(hilbert_d);
|
|
||||||
|
|
||||||
let x = bx as usize * bucket_w;
|
|
||||||
let y = by as usize * bucket_h;
|
|
||||||
let w = if width >= x {
|
|
||||||
min(bucket_w, width - x)
|
|
||||||
} else {
|
|
||||||
bucket_w
|
|
||||||
};
|
|
||||||
let h = if height >= y {
|
|
||||||
min(bucket_h, height - y)
|
|
||||||
} else {
|
|
||||||
bucket_h
|
|
||||||
};
|
|
||||||
if x < width && y < height && w > 0 && h > 0 {
|
|
||||||
job_queue.push(
|
|
||||||
BucketJob {
|
|
||||||
x: (start_x + x) as u32,
|
|
||||||
y: (start_y + y) as u32,
|
|
||||||
w: w as u32,
|
|
||||||
h: h as u32,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark done queuing jobs
|
|
||||||
*all_jobs_queued.write().unwrap() = true;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
// Print initial 0.00% progress
|
||||||
|
print!("0.00%");
|
||||||
|
let _ = io::stdout().flush();
|
||||||
|
|
||||||
|
// Determine bucket size based on the per-thread maximum number of samples to
|
||||||
|
// calculate at a time.
|
||||||
|
let (bucket_w, bucket_h) = {
|
||||||
|
let target_pixels_per_bucket = max_samples_per_bucket as f64 / self.spp as f64;
|
||||||
|
let target_bucket_dim = if target_pixels_per_bucket.sqrt() < 1.0 {
|
||||||
|
1usize
|
||||||
|
} else {
|
||||||
|
target_pixels_per_bucket.sqrt() as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
(target_bucket_dim, target_bucket_dim)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Populate job queue
|
||||||
|
let bucket_n = {
|
||||||
|
let bucket_count_x = ((width / bucket_w) + 1) as u32;
|
||||||
|
let bucket_count_y = ((height / bucket_h) + 1) as u32;
|
||||||
|
let larger = cmp::max(bucket_count_x, bucket_count_y);
|
||||||
|
let pow2 = upper_power_of_two(larger);
|
||||||
|
pow2 * pow2
|
||||||
|
};
|
||||||
|
for hilbert_d in 0..bucket_n {
|
||||||
|
let (bx, by) = hilbert::d2xy(hilbert_d);
|
||||||
|
|
||||||
|
let x = bx as usize * bucket_w;
|
||||||
|
let y = by as usize * bucket_h;
|
||||||
|
let w = if width >= x {
|
||||||
|
min(bucket_w, width - x)
|
||||||
|
} else {
|
||||||
|
bucket_w
|
||||||
|
};
|
||||||
|
let h = if height >= y {
|
||||||
|
min(bucket_h, height - y)
|
||||||
|
} else {
|
||||||
|
bucket_h
|
||||||
|
};
|
||||||
|
if x < width && y < height && w > 0 && h > 0 {
|
||||||
|
job_queue.push(BucketJob {
|
||||||
|
x: (start_x + x) as u32,
|
||||||
|
y: (start_y + y) as u32,
|
||||||
|
w: w as u32,
|
||||||
|
h: h as u32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark done queuing jobs
|
||||||
|
*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,34 +374,42 @@ 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,
|
||||||
event: LightPathEvent::CameraRay,
|
pixel_co: (u32, u32),
|
||||||
bounce_count: 0,
|
image_plane_co: (f32, f32),
|
||||||
|
lens_uv: (f32, f32),
|
||||||
|
time: f32,
|
||||||
|
wavelength: f32,
|
||||||
|
lds_offset: u32,
|
||||||
|
) -> (LightPath, Ray) {
|
||||||
|
(
|
||||||
|
LightPath {
|
||||||
|
event: LightPathEvent::CameraRay,
|
||||||
|
bounce_count: 0,
|
||||||
|
|
||||||
pixel_co: pixel_co,
|
pixel_co: pixel_co,
|
||||||
lds_offset: lds_offset,
|
lds_offset: lds_offset,
|
||||||
dim_offset: Cell::new(6),
|
dim_offset: Cell::new(6),
|
||||||
time: time,
|
time: time,
|
||||||
wavelength: wavelength,
|
wavelength: wavelength,
|
||||||
|
|
||||||
next_bounce_ray: None,
|
next_bounce_ray: None,
|
||||||
next_attentuation_fac: Float4::splat(1.0),
|
next_attentuation_fac: Float4::splat(1.0),
|
||||||
|
|
||||||
light_attenuation: Float4::splat(1.0),
|
light_attenuation: Float4::splat(1.0),
|
||||||
pending_color_addition: Float4::splat(0.0),
|
pending_color_addition: Float4::splat(0.0),
|
||||||
color: Float4::splat(0.0),
|
color: Float4::splat(0.0),
|
||||||
},
|
},
|
||||||
|
|
||||||
scene
|
scene.camera.generate_ray(
|
||||||
.camera
|
image_plane_co.0,
|
||||||
.generate_ray(
|
image_plane_co.1,
|
||||||
image_plane_co.0,
|
time,
|
||||||
image_plane_co.1,
|
lens_uv.0,
|
||||||
time,
|
lens_uv.1,
|
||||||
lens_uv.0,
|
),
|
||||||
lens_uv.1,
|
)
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_lds_samp(&self) -> f32 {
|
fn next_lds_samp(&self) -> f32 {
|
||||||
|
@ -402,23 +418,38 @@ 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
|
||||||
LightPathEvent::CameraRay |
|
LightPathEvent::CameraRay |
|
||||||
LightPathEvent::BounceRay => {
|
LightPathEvent::BounceRay => {
|
||||||
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 {
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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)));
|
||||||
|
|
||||||
|
|
|
@ -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,59 +251,52 @@ 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
|
} else {
|
||||||
} else {
|
false
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceType::Assembly => {
|
|
||||||
self.assemblies[inst.data_index]
|
|
||||||
.light_accel
|
|
||||||
.approximate_energy() > 0.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
InstanceType::Assembly => {
|
||||||
|
self.assemblies[inst.data_index]
|
||||||
|
.light_accel
|
||||||
|
.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 => {
|
if let Object::Light(ref light) = self.objects[inst.data_index] {
|
||||||
if let Object::Light(ref light) = self.objects[inst.data_index] {
|
light.approximate_energy()
|
||||||
light.approximate_energy()
|
} else {
|
||||||
} else {
|
0.0
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InstanceType::Assembly => {
|
InstanceType::Assembly => {
|
||||||
self.assemblies[inst.data_index]
|
self.assemblies[inst.data_index]
|
||||||
.light_accel
|
.light_accel
|
||||||
.approximate_energy()
|
.approximate_energy()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(bounds, energy)
|
(bounds, energy)
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
Assembly {
|
Assembly {
|
||||||
instances: self.arena.copy_slice(&self.instances),
|
instances: self.arena.copy_slice(&self.instances),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -228,11 +261,10 @@ impl SurfaceClosure for LambertClosure {
|
||||||
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample {
|
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, wavelength: f32) -> SpectralSample {
|
||||||
let v = out.normalized();
|
let v = out.normalized();
|
||||||
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();
|
|
||||||
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
|
||||||
|
@ -241,16 +273,21 @@ impl SurfaceClosure for LambertClosure {
|
||||||
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal) -> f32 {
|
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal) -> f32 {
|
||||||
let v = out.normalized();
|
let v = out.normalized();
|
||||||
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();
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -292,11 +329,10 @@ impl SurfaceClosure for LambertClosure {
|
||||||
} else {
|
} else {
|
||||||
let v = out.normalized();
|
let v = out.normalized();
|
||||||
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();
|
|
||||||
|
|
||||||
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.
|
||||||
|
@ -441,11 +484,10 @@ impl SurfaceClosure for GTRClosure {
|
||||||
|
|
||||||
// Surface normal
|
// Surface normal
|
||||||
let nn = if dot(nor.into_vector(), hh) < 0.0 {
|
let nn = if dot(nor.into_vector(), hh) < 0.0 {
|
||||||
-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);
|
||||||
|
@ -538,11 +580,10 @@ impl SurfaceClosure for GTRClosure {
|
||||||
|
|
||||||
// Surface normal
|
// Surface normal
|
||||||
let nn = if dot(nor.into_vector(), hh) < 0.0 {
|
let nn = if dot(nor.into_vector(), hh) < 0.0 {
|
||||||
-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.
|
||||||
|
@ -560,11 +607,10 @@ impl SurfaceClosure for GTRClosure {
|
||||||
|
|
||||||
// Surface normal
|
// 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();
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -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],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
190
src/tracer.rs
190
src/tracer.rs
|
@ -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,93 +63,112 @@ 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],
|
||||||
// Transform rays if needed
|
) {
|
||||||
if let Some((xstart, xend)) = inst.transform_indices {
|
assembly.object_accel.traverse(
|
||||||
// Push transforms to stack
|
&mut accel_rays[..],
|
||||||
self.xform_stack.push(&assembly.xforms[xstart..xend]);
|
&assembly.instances[..],
|
||||||
|
|inst, rs| {
|
||||||
|
// Transform rays if needed
|
||||||
|
if let Some((xstart, xend)) = inst.transform_indices {
|
||||||
|
// Push transforms to stack
|
||||||
|
self.xform_stack.push(&assembly.xforms[xstart..xend]);
|
||||||
|
|
||||||
// Do transforms
|
// Do transforms
|
||||||
let xforms = self.xform_stack.top();
|
let xforms = self.xform_stack.top();
|
||||||
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),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Trace rays
|
// Trace rays
|
||||||
{
|
{
|
||||||
// This is kind of weird looking, but what we're doing here is
|
// This is kind of weird looking, but what we're doing here is
|
||||||
// splitting the rays up based on direction if they were
|
// splitting the rays up based on direction if they were
|
||||||
// transformed, and not splitting them up if they weren't
|
// transformed, and not splitting them up if they weren't
|
||||||
// transformed.
|
// transformed.
|
||||||
// But to keep the actual tracing code in one place (DRY),
|
// But to keep the actual tracing code in one place (DRY),
|
||||||
// we map both cases to an array slice that contains slices of
|
// we map both cases to an array slice that contains slices of
|
||||||
// ray arrays. Gah... that's confusing even when explained.
|
// ray arrays. Gah... that's confusing even when explained.
|
||||||
// TODO: do this in a way that's less confusing. Probably split
|
// TODO: do this in a way that's less confusing. Probably split
|
||||||
// the tracing code out into a trace_instance() method or
|
// the tracing code out into a trace_instance() method or
|
||||||
// something.
|
// something.
|
||||||
let mut tmp = if let Some(_) = inst.transform_indices {
|
let mut tmp = if let Some(_) = inst.transform_indices {
|
||||||
split_rays_by_direction(rs)
|
split_rays_by_direction(rs)
|
||||||
} else {
|
} else {
|
||||||
[
|
[
|
||||||
&mut rs[..],
|
&mut rs[..],
|
||||||
&mut [],
|
&mut [],
|
||||||
&mut [],
|
&mut [],
|
||||||
&mut [],
|
&mut [],
|
||||||
&mut [],
|
&mut [],
|
||||||
&mut [],
|
&mut [],
|
||||||
&mut [],
|
&mut [],
|
||||||
&mut [],
|
&mut [],
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
let mut ray_sets = if let Some(_) = inst.transform_indices {
|
let mut ray_sets = if let Some(_) = inst.transform_indices {
|
||||||
&mut tmp[..]
|
&mut tmp[..]
|
||||||
} else {
|
} else {
|
||||||
&mut tmp[..1]
|
&mut tmp[..1]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Loop through the split ray slices and trace them
|
// Loop through the split ray slices and trace them
|
||||||
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,
|
||||||
InstanceType::Assembly => {
|
ray_set,
|
||||||
self.trace_assembly(&assembly.assemblies[inst.data_index], wrays, ray_set);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Un-transform rays if needed
|
InstanceType::Assembly => {
|
||||||
if let Some(_) = inst.transform_indices {
|
self.trace_assembly(
|
||||||
// Pop transforms off stack
|
&assembly.assemblies[inst.data_index],
|
||||||
self.xform_stack.pop();
|
wrays,
|
||||||
|
ray_set,
|
||||||
// Undo transforms
|
);
|
||||||
let xforms = self.xform_stack.top();
|
|
||||||
if xforms.len() > 0 {
|
|
||||||
for ray in &mut rs[..] {
|
|
||||||
let id = ray.id;
|
|
||||||
let t = ray.time;
|
|
||||||
ray.update_from_xformed_world_ray(&wrays[id as usize], &lerp_slice(xforms, t));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for ray in &mut rs[..] {
|
|
||||||
let id = ray.id;
|
|
||||||
ray.update_from_world_ray(&wrays[id as usize]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
// Un-transform rays if needed
|
||||||
|
if let Some(_) = inst.transform_indices {
|
||||||
|
// Pop transforms off stack
|
||||||
|
self.xform_stack.pop();
|
||||||
|
|
||||||
|
// Undo transforms
|
||||||
|
let xforms = self.xform_stack.top();
|
||||||
|
if xforms.len() > 0 {
|
||||||
|
for ray in &mut rs[..] {
|
||||||
|
let id = ray.id;
|
||||||
|
let t = ray.time;
|
||||||
|
ray.update_from_xformed_world_ray(
|
||||||
|
&wrays[id as usize],
|
||||||
|
&lerp_slice(xforms, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for ray in &mut rs[..] {
|
||||||
|
let id = ray.id;
|
||||||
|
ray.update_from_world_ray(&wrays[id as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_object<'b>(&'b mut self, obj: &Object, wrays: &[Ray], rays: &mut [AccelRay]) {
|
fn trace_object<'b>(&'b mut self, obj: &Object, wrays: &[Ray], rays: &mut [AccelRay]) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user