diff --git a/Cargo.lock b/Cargo.lock index 99e403b..a5916ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ dependencies = [ "lodepng 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "nom 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quickersort 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "scoped_threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -76,6 +77,11 @@ name = "nom" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "num-traits" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "num_cpus" version = "0.2.12" @@ -84,6 +90,15 @@ dependencies = [ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quickersort" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "0.1.71" @@ -138,11 +153,24 @@ dependencies = [ "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unreachable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "utf8-ranges" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.2.7" diff --git a/Cargo.toml b/Cargo.toml index 8172713..0afa579 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ nom = "1.2" scoped_threadpool = "0.1" crossbeam = "0.2" num_cpus = "0.2" +quickersort = "2.0" lodepng = "0.8" \ No newline at end of file diff --git a/src/bvh.rs b/src/bvh.rs index bc03b93..2fe8081 100644 --- a/src/bvh.rs +++ b/src/bvh.rs @@ -1,10 +1,15 @@ #![allow(dead_code)] +use std::cmp::Ordering; +use quickersort::sort_by; use lerp::lerp_slice; use bbox::BBox; use boundable::Boundable; use ray::AccelRay; use algorithm::{partition, merge_slices_append}; +use math::log2_64; + +const BVH_MAX_DEPTH: usize = 64; #[derive(Debug)] pub struct BVH { @@ -129,10 +134,15 @@ impl BVH { 2 } }; - let split_pos = (bounds.min[split_axis] + bounds.max[split_axis]) * 0.5; - // Partition objects based on split - let split_index = { + // Partition objects based on split. + // If we're too near the max depth, we do balanced building to + // avoid exceeding max depth. + // Otherwise we do cooler clever stuff to build better trees. + let split_index = if (log2_64(objects.len() as u64) as usize) < + (BVH_MAX_DEPTH - depth) { + // Clever splitting, when we have room to play + let split_pos = (bounds.min[split_axis] + bounds.max[split_axis]) * 0.5; let mut split_i = partition(&mut objects[..], |obj| { let tb = lerp_slice(bounder(obj), 0.5); let centroid = (tb.min[split_axis] + tb.max[split_axis]) * 0.5; @@ -143,6 +153,25 @@ impl BVH { } split_i + } else { + // Balanced splitting, when we don't have room to play + sort_by(objects, + &|a, b| { + let tb_a = lerp_slice(bounder(a), 0.5); + let tb_b = lerp_slice(bounder(b), 0.5); + let centroid_a = (tb_a.min[split_axis] + tb_a.max[split_axis]) * 0.5; + let centroid_b = (tb_b.min[split_axis] + tb_b.max[split_axis]) * 0.5; + + if centroid_a < centroid_b { + Ordering::Less + } else if centroid_a == centroid_b { + Ordering::Equal + } else { + Ordering::Greater + } + }); + + objects.len() / 2 }; // Create child nodes @@ -186,8 +215,8 @@ impl BVH { return; } - let mut i_stack = [0; 256]; - let mut ray_i_stack = [rays.len(); 256]; + let mut i_stack = [0; BVH_MAX_DEPTH + 1]; + let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 1]; let mut stack_ptr = 1; while stack_ptr > 0 { diff --git a/src/main.rs b/src/main.rs index 8764420..6567e40 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ extern crate docopt; extern crate scoped_threadpool; extern crate crossbeam; extern crate num_cpus; +extern crate quickersort; extern crate lodepng; #[macro_use] extern crate nom; diff --git a/src/math/mod.rs b/src/math/mod.rs index efd6b44..6c22d85 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -40,6 +40,24 @@ pub fn fast_ln(x: f32) -> f32 { +pub fn log2_64(value: u64) -> u64 { + const TAB64: [u64; 64] = [63, 0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3, 61, 51, + 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4, 62, 57, 46, + 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21, 56, 45, 25, 31, + 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5]; + + let value = value | (value >> 1); + let value = value | (value >> 2); + let value = value | (value >> 4); + let value = value | (value >> 8); + let value = value | (value >> 16); + let value = value | (value >> 32); + + TAB64[((((value - (value >> 1)) * 0x07EDD5E59A4E28C2)) >> 58) as usize] +} + + + /// Creates a coordinate system from a single vector. pub fn coordinate_system_from_vector(v: Vector) -> (Vector, Vector, Vector) { let v2 = if v[0].abs() > v[1].abs() {