diff --git a/Cargo.lock b/Cargo.lock index aa85c37..05972c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,10 @@ name = "bitflags" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bvh_order" +version = "0.1.0" + [[package]] name = "byteorder" version = "1.0.0" @@ -158,6 +162,7 @@ name = "psychopath" version = "0.1.0" dependencies = [ "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bvh_order 0.1.0", "clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)", "color 0.1.0", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 088dc73..9615bf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "sub_crates/bvh_order", "sub_crates/color", "sub_crates/float4", "sub_crates/halton", @@ -36,6 +37,9 @@ scoped_threadpool = "0.1" time = "0.1" # Local crate dependencies +[dependencies.bvh_order] +path = "sub_crates/bvh_order" + [dependencies.color] path = "sub_crates/color" diff --git a/sub_crates/bvh_order/Cargo.toml b/sub_crates/bvh_order/Cargo.toml new file mode 100644 index 0000000..623ca09 --- /dev/null +++ b/sub_crates/bvh_order/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bvh_order" +version = "0.1.0" +authors = ["Nathan Vegdahl "] +license = "MIT" +build = "build.rs" + +[lib] +name = "bvh_order" +path = "src/lib.rs" diff --git a/sub_crates/bvh_order/build.rs b/sub_crates/bvh_order/build.rs new file mode 100644 index 0000000..0f46941 --- /dev/null +++ b/sub_crates/bvh_order/build.rs @@ -0,0 +1,105 @@ +// Generate table for traversal order of quad BVHs. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +fn main() { + // Build the traversal table. + let mut traversal_table = [ + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + ]; + for raydir in 0..8 { + let ray = [raydir & 1, (raydir >> 1) & 1, (raydir >> 2) & 1]; + + for s2 in 0..3 { + for s1 in 0..3 { + for s0 in 0..3 { + let mut perm = [0, 1, 2, 3]; + if ray[s1] == 1 { + perm.swap(0, 1); + } + if ray[s2] == 1 { + perm.swap(2, 3); + } + if ray[s0] == 1 { + perm.swap(0, 2); + perm.swap(1, 3); + } + traversal_table[raydir].push( + perm[0] + (perm[1] << 2) + (perm[2] << 4) + + (perm[3] << 6), + ); + } + } + } + + for s1 in 0..3 { + for s0 in 0..3 { + let mut perm = [0, 1, 2]; + if ray[s1] == 1 { + perm.swap(0, 1); + } + if ray[s0] == 1 { + perm.swap(0, 1); + perm.swap(0, 2); + } + traversal_table[raydir].push(perm[0] + (perm[1] << 2) + (perm[2] << 4)); + } + } + + for s1 in 0..3 { + for s0 in 0..3 { + let mut perm = [0, 1, 2]; + if ray[s1] == 1 { + perm.swap(1, 2); + } + if ray[s0] == 1 { + perm.swap(0, 2); + perm.swap(0, 1); + } + traversal_table[raydir].push(perm[0] + (perm[1] << 2) + (perm[2] << 4)); + } + } + + for s0 in 0..3 { + let mut perm = [0, 1]; + if ray[s0] == 1 { + perm.swap(0, 1); + } + traversal_table[raydir].push(perm[0] + (perm[1] << 2)); + } + } + + // Write traversal table to Rust file + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("table_inc.rs"); + let mut f = File::create(&dest_path).unwrap(); + + f.write_all("pub static TRAVERSAL_TABLE: [[u8; 48]; 8] = [".as_bytes()) + .unwrap(); + + for sub_table in traversal_table.iter() { + f.write_all("\n [".as_bytes()).unwrap(); + for (i, n) in sub_table.iter().enumerate() { + if i == 27 || i == 36 || i == 45 { + f.write_all("\n ".as_bytes()).unwrap(); + } + f.write_all(format!("{}", n).as_bytes()).unwrap(); + if i != 47 { + f.write_all(", ".as_bytes()).unwrap(); + } + } + f.write_all("],".as_bytes()).unwrap(); + } + + f.write_all("\n];".as_bytes()).unwrap(); +} diff --git a/sub_crates/bvh_order/src/lib.rs b/sub_crates/bvh_order/src/lib.rs new file mode 100644 index 0000000..fc52a5e --- /dev/null +++ b/sub_crates/bvh_order/src/lib.rs @@ -0,0 +1,34 @@ +#![allow(dead_code)] + +// Include the file generated by the build.rs script +include!(concat!(env!("OUT_DIR"), "/table_inc.rs")); + +/// Represents the split axes of the BVH2 node(s) that a BVH4 node was created +/// from. +/// +/// * `Full` means four nodes from three splits: top, left, and right. +/// * `Left` is three nodes from two splits: top and left. +/// * `Right` is three nodes from two splits: top and right. +/// * `TopOnly` is two nodes from one split (in other words, the BVH4 node is +/// identical to the single BVH2 node that it was created from). +/// +/// Left in this case means the node whose coordinate on the top split-axis is +/// lower. For example, if the top split is on the x axis, then `left.x <= right.x`. +#[derive(Debug, Copy, Clone)] +pub enum SplitAxes { + Full((u8, u8, u8)), // top, left, right + Left((u8, u8)), // top, left + Right((u8, u8)), // top, right + TopOnly(u8), // top +} + +/// Calculates the traversal code for a BVH4 node based on the splits and +/// topology of its children. +pub fn calc_traversal_code(split: SplitAxes) -> u8 { + match split { + SplitAxes::Full((top, left, right)) => top + (left * 3) + (right * 9), + SplitAxes::Left((top, left)) => top + (left * 3) + 27, + SplitAxes::Right((top, right)) => top + (right * 3) + (27 + 9), + SplitAxes::TopOnly(top) => top + (27 + 9 + 9), + } +}