Completed first attempt at implementing BVH4.
Completely untested, so almost certainly non-functional. But it compiles!
This commit is contained in:
parent
e0c6dfbd76
commit
53a14996c9
|
@ -21,6 +21,309 @@ use super::bvh_base::{BVHBase, BVHBaseNode, BVH_MAX_DEPTH};
|
|||
// TRAVERSAL_TABLE
|
||||
include!("bvh4_table.inc");
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BVH4<'a> {
|
||||
root: Option<&'a BVH4Node<'a>>,
|
||||
depth: usize,
|
||||
_bounds: Option<&'a [BBox]>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum BVH4Node<'a> {
|
||||
Internal {
|
||||
bounds: &'a [BBox4],
|
||||
children: &'a [BVH4Node<'a>],
|
||||
traversal_code: u8,
|
||||
},
|
||||
|
||||
Leaf { object_range: (usize, usize) },
|
||||
}
|
||||
|
||||
impl<'a> BVH4<'a> {
|
||||
pub fn from_objects<'b, T, F>(arena: &'a MemArena,
|
||||
objects: &mut [T],
|
||||
objects_per_leaf: usize,
|
||||
bounder: F)
|
||||
-> BVH4<'a>
|
||||
where F: 'b + Fn(&T) -> &'b [BBox]
|
||||
{
|
||||
if objects.len() == 0 {
|
||||
BVH4 {
|
||||
root: None,
|
||||
depth: 0,
|
||||
_bounds: None,
|
||||
}
|
||||
} else {
|
||||
let base = BVHBase::from_objects(objects, objects_per_leaf, bounder);
|
||||
|
||||
let mut fill_node = unsafe { arena.alloc_uninitialized::<BVH4Node>() };
|
||||
BVH4::construct_from_base(arena, &base, &base.nodes[base.root_node_index()], fill_node);
|
||||
|
||||
BVH4 {
|
||||
root: Some(fill_node),
|
||||
depth: (base.depth / 2) + 1,
|
||||
_bounds: {
|
||||
let range = base.nodes[base.root_node_index()].bounds_range();
|
||||
Some(arena.copy_slice(&base.bounds[range.0..range.1]))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tree_depth(&self) -> usize {
|
||||
self.depth
|
||||
}
|
||||
|
||||
pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F)
|
||||
where F: FnMut(&T, &mut [AccelRay])
|
||||
{
|
||||
match self.root {
|
||||
None => {}
|
||||
|
||||
Some(root) => {
|
||||
// +2 of max depth for root and last child
|
||||
let mut node_stack = [Some(root); BVH_MAX_DEPTH + 2];
|
||||
let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 2];
|
||||
let mut stack_ptr = 1;
|
||||
let mut first_loop = true;
|
||||
|
||||
let ray_code = (rays[0].dir_inv.x().is_sign_negative() as u8) |
|
||||
((rays[0].dir_inv.y().is_sign_negative() as u8) << 1) |
|
||||
((rays[0].dir_inv.z().is_sign_negative() as u8) << 2);
|
||||
|
||||
while stack_ptr > 0 {
|
||||
match node_stack[stack_ptr] {
|
||||
Some(&BVH4Node::Internal { bounds, children, traversal_code }) => {
|
||||
let node_order_code = {
|
||||
TRAVERSAL_TABLE[ray_code as usize][traversal_code as usize]
|
||||
};
|
||||
|
||||
let mut all_hits = 0; // Accumulate
|
||||
let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
|
||||
if (!r.is_done()) && (first_loop || r.trav_stack.pop()) {
|
||||
let hits = lerp_slice(bounds, r.time)
|
||||
.intersect_accel_ray(r)
|
||||
.to_bitmask();
|
||||
all_hits |= hits;
|
||||
|
||||
// Push hit bits onto ray's traversal stack
|
||||
let mut shuffled_hits = 0;
|
||||
for i in 0..3 {
|
||||
let ii = (node_order_code >> (i * 2)) & 3;
|
||||
shuffled_hits |= ((hits >> ii) & 1) << i;
|
||||
}
|
||||
r.trav_stack.push_3(shuffled_hits);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
if part > 0 {
|
||||
node_stack[stack_ptr] = {
|
||||
Some(&children[(node_order_code & 3) as usize])
|
||||
};
|
||||
node_stack[stack_ptr + 1] = {
|
||||
Some(&children[((node_order_code >> 2) & 3) as usize])
|
||||
};
|
||||
node_stack[stack_ptr + 2] = if children.len() > 2 {
|
||||
Some(&children[((node_order_code >> 4) & 3) as usize])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
node_stack[stack_ptr + 3] = if children.len() > 3 {
|
||||
Some(&children[((node_order_code >> 6) & 3) as usize])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
ray_i_stack[stack_ptr] = part;
|
||||
ray_i_stack[stack_ptr + 1] = part;
|
||||
stack_ptr += children.len() - 1;
|
||||
} else {
|
||||
stack_ptr -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
Some(&BVH4Node::Leaf { object_range }) => {
|
||||
let part = if !first_loop {
|
||||
partition(&mut rays[..ray_i_stack[stack_ptr]],
|
||||
|r| r.trav_stack.pop())
|
||||
} else {
|
||||
ray_i_stack[stack_ptr]
|
||||
};
|
||||
|
||||
for obj in &objects[object_range.0..object_range.1] {
|
||||
obj_ray_test(obj, &mut rays[..part]);
|
||||
}
|
||||
|
||||
stack_ptr -= 1;
|
||||
}
|
||||
|
||||
None => {
|
||||
if !first_loop {
|
||||
for r in (&mut rays[..ray_i_stack[stack_ptr]]).iter_mut() {
|
||||
r.trav_stack.pop();
|
||||
}
|
||||
}
|
||||
stack_ptr -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
first_loop = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_from_base(arena: &'a MemArena,
|
||||
base: &BVHBase,
|
||||
node: &BVHBaseNode,
|
||||
fill_node: &mut BVH4Node<'a>) {
|
||||
match node {
|
||||
// Create internal node
|
||||
&BVHBaseNode::Internal { bounds_range: _, children_indices, split_axis } => {
|
||||
let child_l = &base.nodes[children_indices.0];
|
||||
let child_r = &base.nodes[children_indices.1];
|
||||
|
||||
// Prepare convenient access to the stuff we need.
|
||||
let child_count;
|
||||
let children; // [Optional, Optional, Optional, Optional]
|
||||
let split_axis_l; // Optional
|
||||
let split_axis_r; // Optional
|
||||
match child_l {
|
||||
&BVHBaseNode::Internal { children_indices: i_l, split_axis: s_l, .. } => {
|
||||
match child_r {
|
||||
&BVHBaseNode::Internal { children_indices: i_r,
|
||||
split_axis: s_r,
|
||||
.. } => {
|
||||
// Four nodes
|
||||
child_count = 4;
|
||||
children = [Some(&base.nodes[i_l.0]),
|
||||
Some(&base.nodes[i_l.1]),
|
||||
Some(&base.nodes[i_r.0]),
|
||||
Some(&base.nodes[i_r.1])];
|
||||
split_axis_l = Some(s_l);
|
||||
split_axis_r = Some(s_r);
|
||||
}
|
||||
&BVHBaseNode::Leaf { .. } => {
|
||||
// Three nodes with left split
|
||||
child_count = 3;
|
||||
children = [Some(&base.nodes[i_l.0]),
|
||||
Some(&base.nodes[i_l.1]),
|
||||
Some(child_r),
|
||||
None];
|
||||
split_axis_l = Some(s_l);
|
||||
split_axis_r = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
&BVHBaseNode::Leaf { .. } => {
|
||||
match child_r {
|
||||
&BVHBaseNode::Internal { children_indices: i_r,
|
||||
split_axis: s_r,
|
||||
.. } => {
|
||||
// Three nodes with right split
|
||||
child_count = 3;
|
||||
children = [Some(child_l),
|
||||
Some(&base.nodes[i_r.0]),
|
||||
Some(&base.nodes[i_r.1]),
|
||||
None];
|
||||
split_axis_l = None;
|
||||
split_axis_r = Some(s_r);
|
||||
}
|
||||
&BVHBaseNode::Leaf { .. } => {
|
||||
// Two nodes
|
||||
child_count = 2;
|
||||
children = [Some(child_l), Some(child_r), None, None];
|
||||
split_axis_l = None;
|
||||
split_axis_r = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct bounds
|
||||
let bounds = {
|
||||
let bounds_len = children.iter()
|
||||
.map(|c| if let &Some(n) = c {
|
||||
n.bounds_range().1 - n.bounds_range().0
|
||||
} else {
|
||||
0
|
||||
})
|
||||
.max()
|
||||
.unwrap();
|
||||
let mut bounds = unsafe { arena.alloc_array_uninitialized(bounds_len) };
|
||||
for (i, b) in bounds.iter_mut().enumerate() {
|
||||
let time = i as f32 / (bounds_len - 1) as f32;
|
||||
|
||||
let b1 = children[0].map_or(BBox::new(), |c| {
|
||||
let (x, y) = c.bounds_range();
|
||||
lerp_slice(&base.bounds[x..y], time)
|
||||
});
|
||||
let b2 = children[1].map_or(BBox::new(), |c| {
|
||||
let (x, y) = c.bounds_range();
|
||||
lerp_slice(&base.bounds[x..y], time)
|
||||
});
|
||||
let b3 = children[2].map_or(BBox::new(), |c| {
|
||||
let (x, y) = c.bounds_range();
|
||||
lerp_slice(&base.bounds[x..y], time)
|
||||
});
|
||||
let b4 = children[3].map_or(BBox::new(), |c| {
|
||||
let (x, y) = c.bounds_range();
|
||||
lerp_slice(&base.bounds[x..y], time)
|
||||
});
|
||||
*b = BBox4::from_bboxes(b1, b2, b3, b4);
|
||||
}
|
||||
bounds
|
||||
};
|
||||
|
||||
// Construct child nodes
|
||||
let mut child_nodes =
|
||||
unsafe { arena.alloc_array_uninitialized::<BVH4Node>(child_count) };
|
||||
for (i, c) in children[0..child_count].iter().enumerate() {
|
||||
BVH4::construct_from_base(arena, base, c.unwrap(), &mut child_nodes[i]);
|
||||
}
|
||||
|
||||
// Build this node
|
||||
let traversal_code = {
|
||||
let topology_code = if child_count == 4 {
|
||||
0
|
||||
} else if child_count == 2 {
|
||||
3
|
||||
} else if split_axis_l.is_some() {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
};
|
||||
calc_traversal_code(split_axis,
|
||||
split_axis_l.unwrap_or(split_axis_r.unwrap_or(0)),
|
||||
split_axis_r.unwrap_or(0),
|
||||
topology_code)
|
||||
};
|
||||
*fill_node = BVH4Node::Internal {
|
||||
bounds: bounds,
|
||||
children: child_nodes,
|
||||
traversal_code: traversal_code,
|
||||
};
|
||||
}
|
||||
|
||||
// Create internal node
|
||||
&BVHBaseNode::Leaf { object_range, .. } => {
|
||||
*fill_node = BVH4Node::Leaf { object_range: object_range };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Boundable for BVH4<'a> {
|
||||
fn bounds<'b>(&'b self) -> &'b [BBox] {
|
||||
self._bounds.unwrap_or(&[])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculates the traversal code for a BVH4 node based on the splits and topology
|
||||
// of its children.
|
||||
//
|
||||
|
@ -44,161 +347,3 @@ fn calc_traversal_code(split_1: u8, split_2: u8, split_3: u8, topology: u8) -> u
|
|||
static T_TABLE: [u8; 4] = [0, 27, 27 + 9, 27 + 9 + 9];
|
||||
split_1 + (split_2 * 3) + (split_3 * 9) + T_TABLE[topology as usize]
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BVH4<'a> {
|
||||
root: Option<&'a BVH4Node<'a>>,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum BVH4Node<'a> {
|
||||
// Internal {
|
||||
// bounds: &'a [BBox4],
|
||||
// children: [&'a BVH4Node<'a>; 4],
|
||||
// children_count: u8,
|
||||
// traversal_code: u8,
|
||||
// },
|
||||
Internal {
|
||||
bounds: &'a [BBox],
|
||||
children: (&'a BVH4Node<'a>, &'a BVH4Node<'a>),
|
||||
split_axis: u8,
|
||||
},
|
||||
|
||||
Leaf {
|
||||
bounds: &'a [BBox],
|
||||
object_range: (usize, usize),
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> BVH4<'a> {
|
||||
pub fn from_objects<'b, T, F>(arena: &'a MemArena,
|
||||
objects: &mut [T],
|
||||
objects_per_leaf: usize,
|
||||
bounder: F)
|
||||
-> BVH4<'a>
|
||||
where F: 'b + Fn(&T) -> &'b [BBox]
|
||||
{
|
||||
if objects.len() == 0 {
|
||||
BVH4 {
|
||||
root: None,
|
||||
depth: 0,
|
||||
}
|
||||
} else {
|
||||
let base = BVHBase::from_objects(objects, objects_per_leaf, bounder);
|
||||
|
||||
BVH4 {
|
||||
root: Some(BVH4::construct_from_base(arena, &base, base.root_node_index())),
|
||||
depth: base.depth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tree_depth(&self) -> usize {
|
||||
self.depth
|
||||
}
|
||||
|
||||
pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F)
|
||||
where F: FnMut(&T, &mut [AccelRay])
|
||||
{
|
||||
match self.root {
|
||||
None => {}
|
||||
|
||||
Some(root) => {
|
||||
// +2 of max depth for root and last child
|
||||
let mut node_stack = [root; BVH_MAX_DEPTH + 2];
|
||||
let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 2];
|
||||
let mut stack_ptr = 1;
|
||||
|
||||
while stack_ptr > 0 {
|
||||
match node_stack[stack_ptr] {
|
||||
&BVH4Node::Internal { bounds, children, split_axis } => {
|
||||
let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
|
||||
(!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r)
|
||||
});
|
||||
if part > 0 {
|
||||
node_stack[stack_ptr] = children.0;
|
||||
node_stack[stack_ptr + 1] = children.1;
|
||||
ray_i_stack[stack_ptr] = part;
|
||||
ray_i_stack[stack_ptr + 1] = part;
|
||||
if rays[0].dir_inv.get_n(split_axis as usize).is_sign_positive() {
|
||||
node_stack.swap(stack_ptr, stack_ptr + 1);
|
||||
}
|
||||
stack_ptr += 1;
|
||||
} else {
|
||||
stack_ptr -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
&BVH4Node::Leaf { bounds, object_range } => {
|
||||
let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
|
||||
(!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r)
|
||||
});
|
||||
if part > 0 {
|
||||
for obj in &objects[object_range.0..object_range.1] {
|
||||
obj_ray_test(obj, &mut rays[..part]);
|
||||
}
|
||||
}
|
||||
|
||||
stack_ptr -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_from_base(arena: &'a MemArena,
|
||||
base: &BVHBase,
|
||||
node_index: usize)
|
||||
-> &'a mut BVH4Node<'a> {
|
||||
match &base.nodes[node_index] {
|
||||
&BVHBaseNode::Internal { bounds_range, children_indices, split_axis } => {
|
||||
let mut node = unsafe { arena.alloc_uninitialized::<BVH4Node>() };
|
||||
|
||||
let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]);
|
||||
let child1 = BVH4::construct_from_base(arena, base, children_indices.0);
|
||||
let child2 = BVH4::construct_from_base(arena, base, children_indices.1);
|
||||
|
||||
*node = BVH4Node::Internal {
|
||||
bounds: bounds,
|
||||
children: (child1, child2),
|
||||
split_axis: split_axis,
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
&BVHBaseNode::Leaf { bounds_range, object_range } => {
|
||||
let mut node = unsafe { arena.alloc_uninitialized::<BVH4Node>() };
|
||||
let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]);
|
||||
|
||||
*node = BVH4Node::Leaf {
|
||||
bounds: bounds,
|
||||
object_range: object_range,
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref DEGENERATE_BOUNDS: [BBox; 1] = [BBox::new()];
|
||||
}
|
||||
|
||||
impl<'a> Boundable for BVH4<'a> {
|
||||
fn bounds<'b>(&'b self) -> &'b [BBox] {
|
||||
match self.root {
|
||||
None => &DEGENERATE_BOUNDS[..],
|
||||
Some(root) => {
|
||||
match root {
|
||||
&BVH4Node::Internal { bounds, .. } => bounds,
|
||||
|
||||
&BVH4Node::Leaf { bounds, .. } => bounds,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,15 @@ pub enum BVHBaseNode {
|
|||
},
|
||||
}
|
||||
|
||||
impl BVHBaseNode {
|
||||
pub fn bounds_range(&self) -> (usize, usize) {
|
||||
match self {
|
||||
&BVHBaseNode::Internal { bounds_range, .. } => bounds_range,
|
||||
&BVHBaseNode::Leaf { bounds_range, .. } => bounds_range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BVHBase {
|
||||
fn new() -> BVHBase {
|
||||
BVHBase {
|
||||
|
|
|
@ -12,6 +12,10 @@ impl BitStack128 {
|
|||
BitStack128 { data: (0, 0) }
|
||||
}
|
||||
|
||||
pub fn new_with_1() -> BitStack128 {
|
||||
BitStack128 { data: (1, 0) }
|
||||
}
|
||||
|
||||
/// Push a bit onto the top of the stack.
|
||||
pub fn push(&mut self, value: bool) {
|
||||
debug_assert!((self.data.1 >> (size_of::<u64>() - 1)) == 0); // Verify no stack overflow
|
||||
|
@ -57,6 +61,16 @@ impl BitStack128 {
|
|||
return b;
|
||||
}
|
||||
|
||||
/// Pop the top n bits off the stack, returning only the first
|
||||
/// one.
|
||||
pub fn pop_1_remove_n(&mut self, n: usize) -> bool {
|
||||
debug_assert!(n < size_of::<BitStack128>()); // Can't pop more than we have
|
||||
let b = (self.data.0 & 1) != 0;
|
||||
self.data.0 = (self.data.0 >> n) | (self.data.1 << (size_of::<u64>() - n));
|
||||
self.data.1 >>= n;
|
||||
return b;
|
||||
}
|
||||
|
||||
/// Read the top bit of the stack without popping it.
|
||||
pub fn peek(&self) -> bool {
|
||||
(self.data.0 & 1) != 0
|
||||
|
|
|
@ -17,7 +17,6 @@ pub struct Ray {
|
|||
pub max_t: f32,
|
||||
pub time: f32,
|
||||
pub flags: u32,
|
||||
pub trav_stack: BitStack128,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
|
@ -29,7 +28,6 @@ impl Ray {
|
|||
max_t: std::f32::INFINITY,
|
||||
time: time,
|
||||
flags: 0,
|
||||
trav_stack: BitStack128::new(),
|
||||
}
|
||||
} else {
|
||||
Ray {
|
||||
|
@ -38,7 +36,6 @@ impl Ray {
|
|||
max_t: 1.0,
|
||||
time: time,
|
||||
flags: OCCLUSION_FLAG,
|
||||
trav_stack: BitStack128::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +59,7 @@ pub struct AccelRay {
|
|||
pub time: f32,
|
||||
pub flags: u32,
|
||||
pub id: u32,
|
||||
pub trav_stack: BitStack128,
|
||||
}
|
||||
|
||||
impl AccelRay {
|
||||
|
@ -73,6 +71,7 @@ impl AccelRay {
|
|||
time: ray.time,
|
||||
flags: ray.flags,
|
||||
id: id,
|
||||
trav_stack: BitStack128::new_with_1(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user