Skip to content

Commit

Permalink
test: Draw spheres and triangles
Browse files Browse the repository at this point in the history
Add a sphere to the cube over plane test scene, and fix bounding boxes
spheres by transformationing their center point from model to world.
  • Loading branch information
Fahien committed Nov 19, 2024
1 parent 6a115fe commit 8e5b04b
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 36 deletions.
23 changes: 16 additions & 7 deletions src/bvh/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,33 @@ impl BvhPrimitive {
}
}

pub fn centroid(&self) -> &Point3 {
pub fn centroid(&self, model: &Model) -> Point3 {
match &self.geometry {
BvhGeometry::Triangle(triangle) => &triangle.centroid,
BvhGeometry::Sphere(sphere) => &sphere.center,
BvhGeometry::Triangle(triangle) => triangle.centroid,
BvhGeometry::Sphere(sphere) => {
let trs = model.solved_trs.get(&self.node).unwrap();
&trs.trs * sphere.center
}
}
}

pub fn min(&self) -> Point3 {
pub fn min(&self, model: &Model) -> Point3 {
match &self.geometry {
BvhGeometry::Triangle(triangle) => triangle.min(),
BvhGeometry::Sphere(sphere) => sphere.min(),
BvhGeometry::Sphere(sphere) => {
let trs = model.solved_trs.get(&self.node).unwrap();
&trs.trs * sphere.min()
}
}
}

pub fn max(&self) -> Point3 {
pub fn max(&self, model: &Model) -> Point3 {
match &self.geometry {
BvhGeometry::Triangle(triangle) => triangle.max(),
BvhGeometry::Sphere(sphere) => sphere.max(),
BvhGeometry::Sphere(sphere) => {
let trs = model.solved_trs.get(&self.node).unwrap();
&trs.trs * sphere.max()
}
}
}

Expand Down
66 changes: 38 additions & 28 deletions src/bvh/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,25 @@ impl AABB {
self.grow(&triangle.vertices[2].pos);
}

fn grow_sphere(&mut self, sphere: &BvhSphere) {
fn grow_sphere(&mut self, sphere: &BvhSphere, trs: &Trs) {
let radius = sphere.get_radius();
self.grow(&(sphere.center + Vec3::new(-radius, 0.0, 0.0)));
self.grow(&(sphere.center + Vec3::new(radius, 0.0, 0.0)));
self.grow(&(sphere.center + Vec3::new(0.0, -radius, 0.0)));
self.grow(&(sphere.center + Vec3::new(0.0, radius, 0.0)));
self.grow(&(sphere.center + Vec3::new(0.0, 0.0, -radius)));
self.grow(&(sphere.center + Vec3::new(0.0, 0.0, radius)));
let center = trs * sphere.center;
self.grow(&(center + Vec3::new(-radius, 0.0, 0.0)));
self.grow(&(center + Vec3::new(radius, 0.0, 0.0)));
self.grow(&(center + Vec3::new(0.0, -radius, 0.0)));
self.grow(&(center + Vec3::new(0.0, radius, 0.0)));
self.grow(&(center + Vec3::new(0.0, 0.0, -radius)));
self.grow(&(center + Vec3::new(0.0, 0.0, radius)));
}

fn grow_primitive(&mut self, primitive: &BvhPrimitive) {
fn grow_primitive(&mut self, model: &Model, primitive: &BvhPrimitive) {
match &primitive.geometry {
BvhGeometry::Triangle(triangle) => {
self.grow_triangle(triangle);
}
BvhGeometry::Sphere(sphere) => {
self.grow_sphere(sphere);
let trs = model.solved_trs.get(&primitive.node).unwrap();
self.grow_sphere(sphere, &trs.trs);
}
}
}
Expand Down Expand Up @@ -98,33 +100,34 @@ impl BvhNode {

pub fn set_primitives(
&mut self,
model: &Model,
primitives: Vec<BvhPrimitive>,
max_depth: usize,
nodes: &mut Pack<BvhNode>,
) {
let mut timer = Timer::new();
self.set_primitives_recursive(primitives, max_depth, 0, nodes);
self.set_primitives_recursive(model, primitives, max_depth, 0, nodes);
print_success!("BVH", "built in {:.2}ms", timer.get_delta().as_millis());
}

/// Surface Area Heuristics:
/// The cost of a split is proportional to the summed cost of intersecting the two
/// resulting boxes, including the triangles they store.
fn evaluate_sah(&self, axis: Axis3, pos: f32) -> f32 {
fn evaluate_sah(&self, model: &Model, axis: Axis3, pos: f32) -> f32 {
// determine triangle counts and bounds for this split candidate
let mut left_box = AABB::default();
let mut right_box = AABB::default();
let mut left_count = 0;
let mut right_count = 0;

for pri in &self.primitives {
let centroid = pri.centroid();
let centroid = pri.centroid(model);
if centroid[axis] < pos {
left_count += 1;
left_box.grow_primitive(pri);
left_box.grow_primitive(model, pri);
} else {
right_count += 1;
right_box.grow_primitive(pri);
right_box.grow_primitive(model, pri);
}
}

Expand All @@ -138,7 +141,7 @@ impl BvhNode {

/// Finds the optimal split plane position and axis
/// - Returns (split axis, split pos, split cost)
fn find_best_split_plane(&self) -> (Axis3, f32, f32) {
fn find_best_split_plane(&self, model: &Model) -> (Axis3, f32, f32) {
const ALL_AXIS: [Axis3; 3] = [Axis3::X, Axis3::Y, Axis3::Z];

let mut best_cost = f32::MAX;
Expand All @@ -158,7 +161,7 @@ impl BvhNode {

for i in 1..AREA_COUNT {
let candidate_pos = bounds_min + i as f32 * scale;
let cost = self.evaluate_sah(axis, candidate_pos);
let cost = self.evaluate_sah(model, axis, candidate_pos);
if cost < best_cost {
best_cost = cost;
best_axis = axis;
Expand All @@ -176,6 +179,7 @@ impl BvhNode {

fn set_primitives_recursive(
&mut self,
model: &Model,
primitives: Vec<BvhPrimitive>,
max_depth: usize,
level: usize,
Expand All @@ -189,16 +193,16 @@ impl BvhNode {

// Visit each vertex of the primitives to find the lowest and highest x, y, and z
for pri in self.primitives.iter() {
self.bounds.a = self.bounds.a.min(&pri.min());
self.bounds.b = self.bounds.b.max(&pri.max());
self.bounds.a = self.bounds.a.min(&pri.min(model));
self.bounds.b = self.bounds.b.max(&pri.max(model));
}

if level >= max_depth {
return;
}

// Surface Area Heuristics
let (split_axis, split_pos, split_cost) = self.find_best_split_plane();
let (split_axis, split_pos, split_cost) = self.find_best_split_plane(model);

let no_split_cost = self.calculate_cost();
if split_cost > no_split_cost {
Expand All @@ -209,7 +213,7 @@ impl BvhNode {
let mut i = 0;
let mut j = self.primitives.len();
while i < j {
let centroid = self.primitives[i].centroid();
let centroid = self.primitives[i].centroid(model);
if centroid[split_axis] < split_pos {
i += 1;
} else {
Expand All @@ -227,10 +231,16 @@ impl BvhNode {

// Create two nodes
let mut left_child = BvhNode::new();
left_child.set_primitives_recursive(left_triangles, max_depth, level + 1, nodes);
left_child.set_primitives_recursive(model, left_triangles, max_depth, level + 1, nodes);

let mut right_child = BvhNode::new();
right_child.set_primitives_recursive(right_triangles, max_depth, level + 1, nodes);
right_child.set_primitives_recursive(
model,
right_triangles,
max_depth,
level + 1,
nodes,
);

self.left = nodes.push(left_child);
self.right = nodes.push(right_child);
Expand Down Expand Up @@ -316,8 +326,8 @@ impl BvhBuilder {
self
}

pub fn build(self) -> Bvh {
Bvh::new(self.primitives, self.max_depth)
pub fn build(self, model: &Model) -> Bvh {
Bvh::new(model, self.primitives, self.max_depth)
}
}

Expand All @@ -332,15 +342,15 @@ impl Bvh {
BvhBuilder::new()
}

pub fn new(primitives: Vec<BvhPrimitive>, max_depth: usize) -> Self {
pub fn new(model: &Model, primitives: Vec<BvhPrimitive>, max_depth: usize) -> Self {
let mut nodes = Pack::new();

let mut root = BvhNode::new();
root.bounds = AABB::new(
Point3::new(f32::MAX, f32::MAX, f32::MAX),
Point3::new(f32::MIN, f32::MIN, f32::MIN),
);
root.set_primitives(primitives, max_depth, &mut nodes);
root.set_primitives(model, primitives, max_depth, &mut nodes);

Self {
root,
Expand Down Expand Up @@ -429,7 +439,7 @@ mod test {
let node = model.nodes.push(Node::default());
let triangles = triangle_prim.primitives(node, Handle::none(), &model);

let bvh = Bvh::builder().primitives(triangles).build();
let bvh = Bvh::builder().primitives(triangles).build(&model);
assert!(bvh.nodes.is_empty());
assert!(!bvh.root.left.valid());
assert!(!bvh.root.right.valid());
Expand All @@ -456,7 +466,7 @@ mod test {
left_primitives.append(&mut right_primitives);
let primitives = left_primitives;

let bvh = Bvh::builder().primitives(primitives).build();
let bvh = Bvh::builder().primitives(primitives).build(&model);
assert!(!bvh.nodes.is_empty());
assert!(bvh.root.left.valid());
assert!(bvh.root.right.valid());
Expand Down
2 changes: 1 addition & 1 deletion src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl Draw for Scene {
if !self.config.bvh {
bvh_builder = bvh_builder.max_depth(0);
}
let bvh = bvh_builder.build();
let bvh = bvh_builder.build(&self.model);

let mut timer = Timer::new();

Expand Down
13 changes: 13 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ fn cube_over_plane() {
scene.load("tests/model/box/box.gltf").unwrap();
scene.push_default_model();

let mut model = Model::default();
let sphere_prim = Primitive::unit_sphere();
let prim_handle = model.primitives.push(sphere_prim);
let sphere_mesh = model.meshes.push(Mesh::new(vec![prim_handle]));
let sphere_node = model.nodes.push(
Node::builder()
.translation(Vec3::new(-0.5, 2.0, -3.0))
.mesh(sphere_mesh)
.build(),
);
model.root.children.push(sphere_node);
scene.push(model);

let root0 = scene.model.nodes.get_mut(1.into()).unwrap();
root0.trs.scale = Vec3::new(16.0, 16.0, 0.125);
root0.trs.translation.translate(&Vec3::new(0.0, -1.0, 0.0));
Expand Down

0 comments on commit 8e5b04b

Please sign in to comment.