Skip to content

Commit

Permalink
Partition planar triangles separately
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Oom committed Mar 25, 2024
1 parent 1293392 commit 2a19ef3
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 47 deletions.
17 changes: 11 additions & 6 deletions kdtree/src/build_median.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,17 @@ impl KdTreeBuilder for MedianKdTreeBuilder {
if planes.is_empty() {
return None;
}
let best = median(&planes);
Some(split_and_partition(
&clipped_triangles,
&parent.boundary,
best,
))
let plane = median(&planes);
let split = split_and_partition(&clipped_triangles, &parent.boundary, plane);
let left = KdBox {
boundary: split.left_aabb,
triangle_indices: [split.left_triangle_indices, split.middle_triangle_indices].concat(),
};
let right = KdBox {
boundary: split.right_aabb,
triangle_indices: split.right_triangle_indices,
};
Some(KdSplit { plane, left, right })
}

fn terminate(&self, _: &KdBox, _: &super::build::KdSplit) -> bool {
Expand Down
64 changes: 43 additions & 21 deletions kdtree/src/build_sah.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use nalgebra::Vector3;
use rayon::prelude::*;

use geometry::{aabb::Aabb, aap::Aap, algorithms::triangles_bounding_box, triangle::Triangle};
use geometry::{aap::Aap, algorithms::triangles_bounding_box, triangle::Triangle};

use crate::split::{clip_triangle, PerfectSplit};

Expand All @@ -19,7 +19,7 @@ pub struct SahKdTreeBuilder {
}

impl SahKdTreeBuilder {
fn calculate_sah_cost_helper(&self, probability: (f32, f32), counts: (usize, usize)) -> f32 {
fn calculate_sah_cost(&self, probability: (f32, f32), counts: (usize, usize)) -> f32 {
debug_assert!(probability.0 >= 0.0 && probability.1 >= 0.0);
debug_assert!(probability.0 > 0.0 || probability.1 > 0.0);
let empty_factor = if counts.0 == 0 || counts.1 == 0 {
Expand All @@ -32,33 +32,43 @@ impl SahKdTreeBuilder {
empty_factor * (self.traverse_cost + intersect_cost)
}

fn calculate_sah_cost(&self, parent: &Aabb, split: &KdSplit) -> f32 {
let probability_left = split.left.boundary.surface_area() / parent.surface_area();
let probability_right = split.right.boundary.surface_area() / parent.surface_area();
let probability = (probability_left, probability_right);
let counts = (
split.left.triangle_indices.len(),
split.right.triangle_indices.len(),
);
self.calculate_sah_cost_helper(probability, counts)
}

fn split_and_calculate_cost(
&self,
parent: &KdBox,
plane: Aap,
clipped: &[ClippedTriangle],
) -> (KdSplit, f32) {
let split = split_and_partition(clipped, &parent.boundary, plane);
let cost = self.calculate_sah_cost(&parent.boundary, &split);
(split, cost)
// TODO: Place planes to the left or to the right depending on what gives best cost.
let probability_left = split.left_aabb.surface_area() / parent.boundary.surface_area();
let probability_right = split.right_aabb.surface_area() / parent.boundary.surface_area();
let probability = (probability_left, probability_right);
let counts = (
split.left_triangle_indices.len() + split.middle_triangle_indices.len(),
split.right_triangle_indices.len() + split.middle_triangle_indices.len(),
);
let cost = self.calculate_sah_cost(probability, counts);
let left = KdBox {
boundary: split.left_aabb,
triangle_indices: [
split.left_triangle_indices,
split.middle_triangle_indices.clone(),
]
.concat(),
};
let right = KdBox {
boundary: split.right_aabb,
triangle_indices: [split.right_triangle_indices, split.middle_triangle_indices]
.concat(),
};
(KdSplit { plane, left, right }, cost)
}
}

impl KdTreeBuilder for SahKdTreeBuilder {
fn starting_box(&self) -> KdBox {
KdBox {
boundary: triangles_bounding_box(&self.triangles).enlarge(&Vector3::new(0.5, 0.5, 0.5)),
boundary: triangles_bounding_box(&self.triangles).enlarge(&Vector3::new(1.0, 1.0, 1.0)),
triangle_indices: (0u32..self.triangles.len() as u32).collect(),
}
}
Expand Down Expand Up @@ -101,8 +111,17 @@ impl KdTreeBuilder for SahKdTreeBuilder {
}

fn terminate(&self, parent: &KdBox, split: &KdSplit) -> bool {
let split_cost = self.calculate_sah_cost(&parent.boundary, split);
split_cost >= self.intersect_cost * parent.triangle_indices.len() as f32
let probability_left = split.left.boundary.surface_area() / parent.boundary.surface_area();
let probability_right =
split.right.boundary.surface_area() / parent.boundary.surface_area();
let probability = (probability_left, probability_right);
let counts = (
split.left.triangle_indices.len(),
split.right.triangle_indices.len(),
);
let split_cost = self.calculate_sah_cost(probability, counts);
let intersect_cost = self.intersect_cost * parent.triangle_indices.len() as f32;
split_cost >= intersect_cost
}

fn make_tree(self, root: Box<KdNode>) -> KdTree {
Expand Down Expand Up @@ -132,7 +151,7 @@ mod tests {
empty_factor: 0.8,
triangles,
};
let tree = build_kdtree(builder, 6);
let tree = build_kdtree(builder, 10);

let expected = KdNode::new_node(
Aap::new_x(0.0),
Expand All @@ -147,7 +166,10 @@ mod tests {
KdNode::empty(),
),
);
dbg!(&tree.root);
assert_eq!(tree.root, expected);
assert_eq!(
tree.root, expected,
"\n actual: {}\n expected: {}",
tree.root, expected
);
}
}
50 changes: 30 additions & 20 deletions kdtree/src/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use geometry::{
triangle::Triangle,
};

use super::build::{KdBox, KdSplit};

#[derive(Debug, PartialEq)]
pub struct PerfectSplit {
pub axis: Axis,
Expand Down Expand Up @@ -105,23 +103,26 @@ mod tests {
pub fn partition_triangles(
clipped_triangles: &[ClippedTriangle],
plane: &Aap,
) -> (Vec<u32>, Vec<u32>) {
) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
let mut left_triangles: Vec<u32> = Vec::new();
let mut middle_triangles: Vec<u32> = Vec::new();
let mut right_triangles: Vec<u32> = Vec::new();
for clipped in clipped_triangles {
let planar =
clipped.min[plane.axis] == plane.distance && clipped.max[plane.axis] == plane.distance;
let left = clipped.min[plane.axis] < plane.distance;
let right = clipped.max[plane.axis] > plane.distance;
// TODO: What to do with planar triangles?
if left || planar {
if left {
left_triangles.push(clipped.index);
}
if right || planar {
if planar {
middle_triangles.push(clipped.index);
}
if right {
right_triangles.push(clipped.index);
}
}
(left_triangles, right_triangles)
(left_triangles, middle_triangles, right_triangles)
}

#[cfg(test)]
Expand Down Expand Up @@ -153,22 +154,31 @@ mod partition_triangles_tests {

let actual = partition_triangles(clipped.as_slice(), &plane);

assert_eq!(actual, (vec![0, 1], vec![1, 2]));
assert_eq!(actual, (vec![0], vec![1], vec![2]));
}
}

pub fn split_and_partition(clipped: &[ClippedTriangle], aabb: &Aabb, plane: Aap) -> KdSplit {
pub struct SplitPartitioning {
pub left_aabb: Aabb,
pub right_aabb: Aabb,
pub left_triangle_indices: Vec<u32>,
pub middle_triangle_indices: Vec<u32>,
pub right_triangle_indices: Vec<u32>,
}

pub fn split_and_partition(
clipped: &[ClippedTriangle],
aabb: &Aabb,
plane: Aap,
) -> SplitPartitioning {
let (left_aabb, right_aabb) = aabb.split(&plane);
let (left_triangles, right_triangles) = partition_triangles(clipped, &plane);
KdSplit {
plane,
left: KdBox {
boundary: left_aabb,
triangle_indices: left_triangles,
},
right: KdBox {
boundary: right_aabb,
triangle_indices: right_triangles,
},
let (left_triangle_indices, middle_triangle_indices, right_triangle_indices) =
partition_triangles(clipped, &plane);
SplitPartitioning {
left_aabb,
right_aabb,
left_triangle_indices,
middle_triangle_indices,
right_triangle_indices,
}
}

0 comments on commit 2a19ef3

Please sign in to comment.