Working BVH

This commit is contained in:
sergeypdev 2025-01-07 00:14:52 +04:00
parent 493f311ad0
commit 7a77d1c409
3 changed files with 68 additions and 84 deletions

View File

@ -112,7 +112,7 @@ get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> Loaded_BVH {
loaded_bvh, ok := assetman.bvhs[path]
model, modtime, reloaded := get_model_ex(assetman, path, loaded_bvh.modtime)
should_recreate := reloaded || !ok || true
should_recreate := reloaded || !ok
if ok && should_recreate {
destroy_loaded_bvh(loaded_bvh)
@ -124,7 +124,7 @@ get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> Loaded_BVH {
outer_aabb := bvh.AABB {
min = math.F32_MAX,
max = math.F32_MIN,
max = -math.F32_MAX,
}
for i in 0 ..< model.meshCount {

View File

@ -73,6 +73,8 @@ Game_Memory :: struct {
es: Editor_State,
editor: bool,
preview_bvh: int,
preview_node: int,
draw_car: bool,
}
Track_Edit_State :: enum {
@ -362,11 +364,37 @@ update :: proc() {
dt := rl.GetFrameTime()
if rl.IsKeyReleased(.LEFT_BRACKET) {
g_mem.preview_bvh -= 1
// Debug BVH traversal
mesh_bvh := assets.get_bvh(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb")
if rl.IsKeyDown(.LEFT_SHIFT) {
if g_mem.preview_bvh >= 0 && g_mem.preview_bvh < len(mesh_bvh.bvhs) {
b := mesh_bvh.bvhs[g_mem.preview_bvh]
node := &b.nodes[g_mem.preview_node]
if !bvh.is_leaf_node(node^) {
if rl.IsKeyPressed(.LEFT_BRACKET) {
g_mem.preview_node = int(node.child_or_prim_start)
} else if rl.IsKeyPressed(.RIGHT_BRACKET) {
g_mem.preview_node = int(node.child_or_prim_start + 1)
} else if rl.IsKeyPressed(.P) {
g_mem.preview_node = 0
}
}
}
} else {
if rl.IsKeyPressed(.LEFT_BRACKET) {
g_mem.preview_bvh -= 1
g_mem.preview_node = 0
}
if rl.IsKeyPressed(.RIGHT_BRACKET) {
g_mem.preview_bvh += 1
g_mem.preview_node = 0
}
}
if rl.IsKeyReleased(.RIGHT_BRACKET) {
g_mem.preview_bvh += 1
if rl.IsKeyPressed(.SPACE) {
g_mem.draw_car = !g_mem.draw_car
}
if g_mem.editor {
@ -447,12 +475,12 @@ draw :: proc() {
for &blas, i in mesh_bvh.bvhs {
mesh := car_model.meshes[i]
if i == -1 {
if i == g_mem.preview_bvh {
bvh.debug_draw_bvh_bounds(
&blas,
bvh.bvh_mesh_from_rl_mesh(mesh),
0,
g_mem.preview_bvh,
g_mem.preview_node,
)
}
@ -469,7 +497,7 @@ draw :: proc() {
}
if mesh_col.hit {
rl.DrawSphereWires(ray.origin + ray.dir * mesh_col.t, 1, 8, 8, rl.RED)
rl.DrawSphereWires(ray.origin + ray.dir * mesh_col.t, 0.1, 8, 8, rl.RED)
}
}
@ -479,7 +507,9 @@ draw :: proc() {
rl.DrawModel(car_model, car_body.x - runtime_world.car_com, 1, rl.WHITE)
} else {
// rl.DrawModel(car_model, 0, 1, rl.WHITE)
if g_mem.draw_car {
rl.DrawModel(car_model, 0, 1, rl.WHITE)
}
}
physics.draw_debug_scene(&world.physics_scene)
@ -534,11 +564,10 @@ draw :: proc() {
rl.DrawText(
fmt.ctprintf(
"mesh: %v, tri: %v, bary: %v, idx: %v",
"mesh: %v, aabb tests: %v, tri tests: %v",
hit_mesh_idx,
mesh_col.prim,
mesh_col.bary,
g_mem.preview_bvh,
mesh_col.aabb_tests,
mesh_col.triangle_tests,
),
5,
32,
@ -546,6 +575,15 @@ draw :: proc() {
rl.ORANGE,
)
rl.DrawText(
fmt.ctprintf("bvh: %v, node: %v", g_mem.preview_bvh, g_mem.preview_node),
5,
48,
8,
rl.ORANGE,
)
switch g_mem.es.track_edit_state {
case .Select:
case .Move:

View File

@ -144,10 +144,11 @@ update_node_bounds :: proc(bvh: ^BVH, node_idx: i32, prim_aabbs: []AABB) {
node := &bvh.nodes[node_idx]
node.aabb.min = math.F32_MAX
node.aabb.max = math.F32_MIN
node.aabb.max = -math.F32_MAX
for i in node.child_or_prim_start ..< node.child_or_prim_start + node.prim_len {
prim_aabb := prim_aabbs[bvh.primitives[i]]
node.aabb.min.x = min(node.aabb.min.x, prim_aabb.min.x)
node.aabb.min.y = min(node.aabb.min.y, prim_aabb.min.y)
node.aabb.min.z = min(node.aabb.min.z, prim_aabb.min.z)
@ -156,11 +157,6 @@ update_node_bounds :: proc(bvh: ^BVH, node_idx: i32, prim_aabbs: []AABB) {
node.aabb.max.y = max(node.aabb.max.y, prim_aabb.max.y)
node.aabb.max.z = max(node.aabb.max.z, prim_aabb.max.z)
}
size := node.aabb.max - node.aabb.min
assert(size.x >= 0)
assert(size.y >= 0)
assert(size.z >= 0)
}
subdivide :: proc(bvh: ^BVH, node_idx: i32, centroids: []Vec3, aabbs: []AABB) {
@ -235,12 +231,15 @@ Ray :: struct {
}
Collision :: struct {
hit: bool,
t: f32,
hit: bool,
t: f32,
// which primitive we hit
prim: u16,
prim: u16,
// Barycentric coords of the hit triangle
bary: Bary,
bary: Bary,
// jStats
aabb_tests: int,
triangle_tests: int,
}
traverse_bvh_ray_mesh :: proc(bvh: ^BVH, mesh: Mesh, ray: Ray, out_collision: ^Collision) -> bool {
@ -279,8 +278,9 @@ internal_traverse_bvh_ray_triangles :: proc(
node := &bvh.nodes[node_idx]
out_collision.aabb_tests += 1
if !internal_ray_aabb_test(ray, node.aabb, out_collision.t) {
return
continue
}
rl.DrawBoundingBox(
@ -300,26 +300,9 @@ internal_traverse_bvh_ray_triangles :: proc(
}
}
// https://tavianator.com/2022/ray_box_boundary.html
internal_ray_aabb_test :: proc(ray: Ray, box: AABB, min_t: f32) -> bool {
_, ok := collision.intersect_ray_aabb(ray.origin, ray.dir, collision.Aabb{box.min, box.max})
return ok
// t1 := (box.min[0] - ray.origin[0]) * ray.dir_inv[0]
// t2 := (box.max[0] - ray.origin[0]) * ray.dir_inv[0]
// tmin := min(t1, t2)
// tmax := max(t1, t2)
// for i in 1 ..< 3 {
// t1 = (box.min[i] - ray.origin[i]) * ray.dir_inv[i]
// t2 = (box.max[i] - ray.origin[i]) * ray.dir_inv[i]
// tmin = max(tmin, min(t1, t2))
// tmax = min(tmax, max(t1, t2))
// }
// return tmax > max(tmin, 0.0)
ts, ok := collision.intersect_ray_aabb(ray.origin, ray.dir, collision.Aabb{box.min, box.max})
return ok && ts[0] < min_t
}
// MöllerTrumbore intersection algorithm
@ -328,6 +311,9 @@ internal_ray_tri_test :: proc(ray: Ray, mesh: Mesh, tri: u16, col: ^Collision) {
i1, i2, i3 := mesh.indices[tri * 3], mesh.indices[tri * 3 + 1], mesh.indices[tri * 3 + 2]
v1, v2, v3 := mesh.vertices[i1], mesh.vertices[i2], mesh.vertices[i3]
col.triangle_tests += 1
rl.DrawTriangle3D(v1, v2, v3, debug_int_to_color(i32(tri) + 1))
t, _, barycentric, ok := collision.intersect_segment_triangle(
{ray.origin, ray.origin + ray.dir},
{v1, v2, v3},
@ -339,44 +325,4 @@ internal_ray_tri_test :: proc(ray: Ray, mesh: Mesh, tri: u16, col: ^Collision) {
col.prim = tri
col.bary = barycentric
}
// rl.DrawTriangle3D(v1, v2, v3, debug_int_to_color(i32(tri)))
// rl_col := rl.GetRayCollisionTriangle(rl.Ray{ray.origin, ray.dir}, v1, v2, v3)
// if rl_col.hit && rl_col.distance < col.t {
// col.hit = true
// col.t = lg.distance(ray.origin, rl_col.point)
// }
return
//e1, e2 := v2 - v1, v3 - v1
//h := lg.cross(ray.dir, e2)
//a := lg.dot(e1, h)
//// ray parallel to triangle
//if a > -0.0001 || a < 0.0001 {
// return
//}
//f: f32 = 1.0 / a
//s := ray.origin - v1
//u := f * lg.dot(s, h)
//if u < 0 || u > 1 {
// return
//}
//q := lg.cross(s, e1)
//v := f * lg.dot(ray.dir, q)
//if v < 0 || u + v > 1 {
// return
//}
//t := f * lg.dot(e2, q)
//if t > 0.0001 && t < col.t {
// col.hit = true
// col.t = t
// col.prim = tri
// col.bary = Vec3{u, v, 0} // TODO: calc W
//}
}