From 7a77d1c4097773826e63f16025fd81886500ec90 Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Tue, 7 Jan 2025 00:14:52 +0400 Subject: [PATCH] Working BVH --- game/assets/assets.odin | 4 +- game/game.odin | 62 ++++++++++++++++++++++------ game/physics/bvh/bvh.odin | 86 ++++++++------------------------------- 3 files changed, 68 insertions(+), 84 deletions(-) diff --git a/game/assets/assets.odin b/game/assets/assets.odin index 6a572e3..ab5eda8 100644 --- a/game/assets/assets.odin +++ b/game/assets/assets.odin @@ -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 { diff --git a/game/game.odin b/game/game.odin index ac6f35c..87d72b6 100644 --- a/game/game.odin +++ b/game/game.odin @@ -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: diff --git a/game/physics/bvh/bvh.odin b/game/physics/bvh/bvh.odin index 853a391..297d149 100644 --- a/game/physics/bvh/bvh.odin +++ b/game/physics/bvh/bvh.odin @@ -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öller–Trumbore 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 - //} }