Skip to content

Commit

Permalink
Split Aabb without algebra
Browse files Browse the repository at this point in the history
Allows KdTree construction without rounding errors in the bounding
boxes.
  • Loading branch information
Daniel Oom committed Mar 27, 2024
1 parent 1ef4249 commit a697e64
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 87 deletions.
110 changes: 49 additions & 61 deletions geometry/src/aabb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,110 +4,98 @@ use super::aap::Aap;

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Aabb {
center: Vector3<f32>,
half_size: Vector3<f32>,
min: Vector3<f32>,
max: Vector3<f32>,
}

impl Aabb {
pub fn from_extents(min: &Vector3<f32>, max: &Vector3<f32>) -> Aabb {
let size = max - min;
let half_size = size / 2.;
Aabb {
center: min + half_size,
half_size,
}
pub fn from_extents(min: Vector3<f32>, max: Vector3<f32>) -> Aabb {
debug_assert!(min <= max);
Aabb { min, max }
}

pub fn empty() -> Aabb {
Aabb {
center: Vector3::zeros(),
half_size: Vector3::zeros(),
min: Vector3::zeros(),
max: Vector3::zeros(),
}
}

pub fn unit() -> Aabb {
Aabb {
center: Vector3::new(0., 0., 0.),
half_size: Vector3::new(0.5, 0.5, 0.5),
min: Vector3::new(0., 0., 0.),
max: Vector3::new(1., 1., 1.),
}
}

pub fn is_empty(&self) -> bool {
self.min == self.max
}

pub fn center(&self) -> Vector3<f32> {
self.center
self.min + self.half_size()
}

pub fn half_size(&self) -> Vector3<f32> {
self.half_size
self.size() / 2.0
}

pub fn size(&self) -> Vector3<f32> {
2.0 * self.half_size
self.max - self.min
}

pub fn min(&self) -> Vector3<f32> {
self.center - self.half_size
self.min
}

pub fn max(&self) -> Vector3<f32> {
self.center + self.half_size
self.max
}

pub fn surface_area(&self) -> f32 {
8. * (self.half_size.x * self.half_size.y
+ self.half_size.x * self.half_size.z
+ self.half_size.y * self.half_size.z)
let size = self.size();
2. * (size.x * size.y + size.x * size.z + size.y * size.z)
}

pub fn volume(&self) -> f32 {
8. * self.half_size.x * self.half_size.y * self.half_size.z
}

#[must_use]
pub fn translate(&self, delta: &Vector3<f32>) -> Aabb {
Aabb {
center: self.center + delta,
half_size: self.half_size,
}
let size = self.size();
size.x * size.y * size.z
}

#[must_use]
pub fn enlarge(&self, delta: &Vector3<f32>) -> Aabb {
let half_delta = delta / 2.0;
Aabb {
center: self.center,
half_size: self.half_size + delta,
min: self.min - half_delta,
max: self.max + half_delta,
}
}

pub fn split(&self, plane: &Aap) -> (Aabb, Aabb) {
let fst_half_axis = (plane.distance - self.min()[plane.axis]) / 2.;
let snd_half_axis = (self.max()[plane.axis] - plane.distance) / 2.;
debug_assert!(
fst_half_axis >= 0.0,
"fst_half_axis is negative {fst_half_axis}",
);
debug_assert!(
snd_half_axis >= 0.0,
"snd_half_axis is negative {snd_half_axis}",
);

let mut fst_center = self.center;
let mut fst_half_size = self.half_size;
fst_center[plane.axis] = plane.distance - fst_half_axis;
fst_half_size[plane.axis] = fst_half_axis;

let mut snd_center = self.center;
let mut snd_half_size = self.half_size;
snd_center[plane.axis] = plane.distance + snd_half_axis;
snd_half_size[plane.axis] = snd_half_axis;

let fst = Aabb {
center: fst_center,
half_size: fst_half_size,
};
let snd = Aabb {
center: snd_center,
half_size: snd_half_size,
};
let mut new_max = self.max;
new_max[plane.axis] = plane.distance;

let mut new_min = self.min;
new_min[plane.axis] = plane.distance;

let fst = Aabb::from_extents(self.min, new_max);
let snd = Aabb::from_extents(new_min, self.max);
(fst, snd)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn split_in_half_halves_the_volume() {
let aabb = Aabb::unit();

let actual = aabb.split(&Aap::new_x(0.5));

assert_eq!(aabb.volume(), 1.0);
assert_eq!(actual.0.volume(), 0.5);
assert_eq!(actual.1.volume(), 0.5);
}
}
18 changes: 9 additions & 9 deletions geometry/src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ mod tests_intersect_triangle_aabb {
v1: Vector3::new(2., 1., 1.),
v2: Vector3::new(1., 2., 1.),
};
let aabb = Aabb::from_extents(&Vector3::new(0., 0., 0.), &Vector3::new(2., 2., 2.));
let aabb = Aabb::from_extents(Vector3::new(0., 0., 0.), Vector3::new(2., 2., 2.));

assert_eq!(intersect_triangle_aabb(&triangle, &aabb), true);
}
Expand All @@ -352,7 +352,7 @@ mod tests_intersect_triangle_aabb {
v1: Vector3::new(2., 1., 2.),
v2: Vector3::new(1., 2., 2.),
};
let aabb = Aabb::from_extents(&Vector3::new(0., 0., 0.), &Vector3::new(2., 2., 2.));
let aabb = Aabb::from_extents(Vector3::new(0., 0., 0.), Vector3::new(2., 2., 2.));

assert_eq!(intersect_triangle_aabb(&triangle, &aabb), true);
}
Expand All @@ -364,7 +364,7 @@ mod tests_intersect_triangle_aabb {
v1: Vector3::new(11., 10., 10.),
v2: Vector3::new(10., 11., 10.),
};
let aabb = Aabb::from_extents(&Vector3::new(0., 0., 0.), &Vector3::new(2., 2., 2.));
let aabb = Aabb::from_extents(Vector3::new(0., 0., 0.), Vector3::new(2., 2., 2.));

assert_eq!(intersect_triangle_aabb(&triangle, &aabb), false);
}
Expand All @@ -380,7 +380,7 @@ pub fn triangles_bounding_box(triangles: &[Triangle]) -> Aabb {
a = a.inf(&triangle.min());
b = b.sup(&triangle.max());
}
Aabb::from_extents(&a, &b)
Aabb::from_extents(a, b)
}

#[cfg(test)]
Expand All @@ -404,7 +404,7 @@ mod tests {

let actual = triangles_bounding_box(&triangles);

let expected = Aabb::from_extents(&Vector3::new(-1., -1., -1.), &Vector3::new(1., 1., 1.));
let expected = Aabb::from_extents(Vector3::new(-1., -1., -1.), Vector3::new(1., 1., 1.));
assert_eq!(actual, expected);
}
}
Expand Down Expand Up @@ -586,7 +586,7 @@ mod tests_clip_triangle_aabb {
v1: Vector3::new(2.0, 1.0, 1.0),
v2: Vector3::new(2.0, 2.0, 1.0),
};
let aabb = Aabb::from_extents(&Vector3::new(0.0, 0.0, 0.0), &Vector3::new(3.0, 3.0, 3.0));
let aabb = Aabb::from_extents(Vector3::new(0.0, 0.0, 0.0), Vector3::new(3.0, 3.0, 3.0));

let actual = clip_triangle_aabb(&triangle, &aabb);

Expand All @@ -601,7 +601,7 @@ mod tests_clip_triangle_aabb {
v1: Vector3::new(1.0, 2.0, 0.0),
v2: Vector3::new(1.0, 2.0, 1.0),
};
let aabb = Aabb::from_extents(&Vector3::new(0.0, 0.0, 0.0), &Vector3::new(1.0, 1.0, 1.0));
let aabb = Aabb::from_extents(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));

let actual = clip_triangle_aabb(&triangle, &aabb);

Expand All @@ -616,7 +616,7 @@ mod tests_clip_triangle_aabb {
v1: Vector3::new(1.0, -1.0, 0.0),
v2: Vector3::new(1.0, -1.0, 1.0),
};
let aabb = Aabb::from_extents(&Vector3::new(0.0, 0.0, 0.0), &Vector3::new(1.0, 1.0, 1.0));
let aabb = Aabb::from_extents(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));

let actual = clip_triangle_aabb(&triangle, &aabb);

Expand All @@ -631,7 +631,7 @@ mod tests_clip_triangle_aabb {
v1: Vector3::new(12.0, 0.0, 0.0),
v2: Vector3::new(6.0, 6.0, 0.0),
};
let aabb = Aabb::from_extents(&Vector3::new(2.0, -1.0, 0.0), &Vector3::new(10.0, 4.0, 0.0));
let aabb = Aabb::from_extents(Vector3::new(2.0, -1.0, 0.0), Vector3::new(10.0, 4.0, 0.0));

let actual = clip_triangle_aabb(&triangle, &aabb);

Expand Down
25 changes: 9 additions & 16 deletions kdtree/src/build_sah.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,9 @@ impl KdTreeBuilder for SahKdTreeBuilder {
}

fn find_best_split(&self, _: u32, parent: &KdBox) -> Option<KdSplit> {
debug_assert!(parent.boundary.volume() > 0.0);
debug_assert!(!parent.boundary.is_empty());
debug_assert!(!parent.triangle_indices.is_empty());

let min = parent.boundary.min();
let max = parent.boundary.max();

let min_by_snd = |a: (_, f32), b: (_, f32)| if a.1 <= b.1 { a } else { b };

let clipped_triangles = parent
Expand All @@ -95,16 +92,12 @@ impl KdTreeBuilder for SahKdTreeBuilder {
splits.dedup();
splits
.par_iter()
.filter_map(|s| {
if s.distance > min[s.axis] && s.distance < max[s.axis] {
let plane = Aap {
axis: s.axis,
distance: s.distance,
};
Some(self.split_and_calculate_cost(parent, plane, &clipped_triangles))
} else {
None
}
.map(|s| {
let plane = Aap {
axis: s.axis,
distance: s.distance,
};
self.split_and_calculate_cost(parent, plane, &clipped_triangles)
})
.reduce_with(min_by_snd)
.map(|a| a.0)
Expand Down Expand Up @@ -151,7 +144,7 @@ mod tests {
empty_factor: 0.8,
triangles,
};
let tree = build_kdtree(builder, 10);
let tree = build_kdtree(builder, 6);

let expected = KdNode::new_node(
Aap::new_x(0.0),
Expand Down Expand Up @@ -198,7 +191,7 @@ mod tests {
empty_factor: 0.8,
triangles,
};
let tree = build_kdtree(builder, 10);
let tree = build_kdtree(builder, 4);

let expected = KdNode::new_node(
Aap::new_x(0.0),
Expand Down
2 changes: 1 addition & 1 deletion kdtree/src/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ mod clip_triangle_tests {
v1: Vector3::new(1.0, 0.0, 0.0),
v2: Vector3::new(1.0, 1.0, 0.0),
};
let aabb = Aabb::from_extents(&Vector3::new(0.0, 0.0, 0.0), &Vector3::new(2.0, 1.0, 1.0));
let aabb = Aabb::from_extents(Vector3::new(0.0, 0.0, 0.0), Vector3::new(2.0, 1.0, 1.0));

let actual = clip_triangle(&[triangle], &aabb, 0);

Expand Down

0 comments on commit a697e64

Please sign in to comment.