Lots of optimizations and bug fixes for static geometry, improved debugging

This commit is contained in:
sergeypdev 2025-07-01 00:09:10 +04:00
parent bce37a3b7e
commit 00190a45fc
10 changed files with 335 additions and 180 deletions

BIN
assets/testblend.glb (Stored with Git LFS)

Binary file not shown.

View File

@ -697,7 +697,7 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
{ {
level_model := assets.get_model(&g_mem.assetman, "assets/testblend.glb") level_model := assets.get_model(&g_mem.assetman, "assets/testblend.glb")
for i in 0 ..< min(level_model.meshCount, 32) { for i in 0 ..< level_model.meshCount {
mesh := &level_model.meshes[i] mesh := &level_model.meshes[i]
if mesh.triangleCount > 0 { if mesh.triangleCount > 0 {
@ -1051,16 +1051,15 @@ catmull_rom :: proc(a, b, c, d: rl.Vector3, t: f32) -> rl.Vector3 {
draw_world :: proc(world: ^World) { draw_world :: proc(world: ^World) {
tracy.Zone() tracy.Zone()
if world.debug_state.draw_physics_scene {
physics.draw_debug_scene(&world.physics_scene)
}
sim_state := physics.get_sim_state(&world.physics_scene) sim_state := physics.get_sim_state(&world.physics_scene)
// level_model := assets.get_model(&g_mem.assetman, "assets/testblend.glb") level_model := assets.get_model(&g_mem.assetman, "assets/testblend.glb")
car_model := assets.get_model(&g_mem.assetman, "assets/ice_cream_truck.glb")
phys_debug_state: physics.Debug_State
physics.init_debug_state(&phys_debug_state)
car_body := physics.get_body(sim_state, world.car_handle) car_body := physics.get_body(sim_state, world.car_handle)
car_model := assets.get_model(&g_mem.assetman, "assets/ice_cream_truck.glb")
engine := physics.get_engine(sim_state, world.engine_handle) engine := physics.get_engine(sim_state, world.engine_handle)
{ {
@ -1102,7 +1101,12 @@ draw_world :: proc(world: ^World) {
} }
if .ACTIVE in ui.header(ui_ctx, "Physics") { if .ACTIVE in ui.header(ui_ctx, "Physics") {
physics.draw_debug_ui(ui_ctx, &world.physics_scene, SOLVER_CONFIG) physics.draw_debug_ui(
&phys_debug_state,
ui_ctx,
&world.physics_scene,
SOLVER_CONFIG,
)
} }
} else { } else {
log.infof("Window closed") log.infof("Window closed")
@ -1111,6 +1115,11 @@ draw_world :: proc(world: ^World) {
} }
} }
if world.debug_state.draw_physics_scene {
physics.draw_debug_scene(&world.physics_scene, &phys_debug_state)
}
car_matrix := rl.QuaternionToMatrix(car_body.q) car_matrix := rl.QuaternionToMatrix(car_body.q)
car_matrix = car_matrix =
(auto_cast linalg.matrix4_translate_f32(physics.body_get_shape_pos(car_body))) * car_matrix (auto_cast linalg.matrix4_translate_f32(physics.body_get_shape_pos(car_body))) * car_matrix
@ -1133,7 +1142,7 @@ draw_world :: proc(world: ^World) {
// .VEC3, // .VEC3,
// ) // )
// render.draw_model(level_model, {}, 1) render.draw_model(level_model, {}, 1)
render.draw_model(car_model, {}, car_matrix) render.draw_model(car_model, {}, car_matrix)

View File

@ -1,5 +1,6 @@
package collision package collision
import "base:runtime"
import "core:container/bit_array" import "core:container/bit_array"
import "core:log" import "core:log"
import "core:math" import "core:math"
@ -28,13 +29,25 @@ box_corners_norm := [8]Vec3 {
} }
box_indices := [6 * 4]u16{0, 4, 6, 2, 3, 2, 6, 7, 7, 6, 4, 5, 5, 1, 3, 7, 1, 0, 2, 3, 5, 4, 0, 1} box_indices := [6 * 4]u16{0, 4, 6, 2, 3, 2, 6, 7, 7, 6, 4, 5, 5, 1, 3, 7, 1, 0, 2, 3, 5, 4, 0, 1}
double_sided_triangle_indices := [6]u16{0, 1, 2, 0, 2, 1}
@(private = "file") @(private = "file")
box_mesh: Convex box_mesh: Convex
@(private = "file")
triangle_mesh: Convex
@(init) @(init)
init_box_mesh :: proc() { init_box_mesh :: proc() {
box_mesh = Convex(halfedge.mesh_from_vertex_index_list(box_corners_norm[:], box_indices[:], 4)) box_mesh = Convex(halfedge.mesh_from_vertex_index_list(box_corners_norm[:], box_indices[:], 4))
triangle_mesh = Convex(
halfedge.mesh_from_vertex_index_list(
[]Vec3{{0, 0, 0}, {1, 0, 0}, {0, 1, 0}},
double_sided_triangle_indices[:],
3,
),
)
} }
@(fini) @(fini)
@ -54,23 +67,16 @@ box_to_convex :: proc(box: Box, allocator := context.allocator) -> (convex: Conv
return return
} }
double_sided_triangle_indices := [6]u16{0, 1, 2, 0, 2, 1}
double_sided_triangle_to_convex :: proc( double_sided_triangle_to_convex :: proc(
tri: [3]Vec3, tri: [3]Vec3,
allocator := context.allocator, allocator := context.allocator,
) -> ( ) -> (
convex: Convex, convex: Convex,
) { ) {
tri := tri convex = halfedge.copy_mesh(triangle_mesh, allocator)
convex = Convex( convex.vertices[0].pos = tri[0]
halfedge.mesh_from_vertex_index_list( convex.vertices[1].pos = tri[1]
tri[:], convex.vertices[2].pos = tri[2]
double_sided_triangle_indices[:],
3,
allocator,
),
)
return return
} }
@ -257,11 +263,14 @@ query_separation_edges :: proc(
a_edge = -1 a_edge = -1
b_edge = -1 b_edge = -1
temp := runtime.default_temp_allocator_temp_begin()
defer runtime.default_temp_allocator_temp_end(temp)
checked_pairs: bit_array.Bit_Array checked_pairs: bit_array.Bit_Array
bit_array.init(&checked_pairs, len(a.edges) * len(b.edges), 0, context.temp_allocator) bit_array.init(&checked_pairs, len(a.edges) * len(b.edges), 0, context.temp_allocator)
a_len := len(a.edges) a_len := len(a.edges)
calc_pair_index :: proc(a, b, a_len: int) -> int { calc_pair_index :: #force_inline proc(a, b, a_len: int) -> int {
return (b * a_len) + a return (b * a_len) + a
} }
@ -320,7 +329,7 @@ create_face_contact_manifold :: proc(
) { ) {
tracy.Zone() tracy.Zone()
is_ref_a := (face_query_a.separation + 0.01) > face_query_b.separation is_ref_a := (face_query_a.separation + 0.001) > face_query_b.separation
ref_face_query := is_ref_a ? face_query_a : face_query_b ref_face_query := is_ref_a ? face_query_a : face_query_b
ref_convex := is_ref_a ? a : b ref_convex := is_ref_a ? a : b
inc_convex := is_ref_a ? b : a inc_convex := is_ref_a ? b : a

View File

@ -6,6 +6,7 @@ import "core:log"
import "core:math" import "core:math"
import lg "core:math/linalg" import lg "core:math/linalg"
import "core:mem" import "core:mem"
import "core:slice"
import "core:strings" import "core:strings"
import "game:debug" import "game:debug"
import he "game:halfedge" import he "game:halfedge"
@ -49,23 +50,30 @@ draw_debug_shape :: proc(
} }
} }
draw_debug_scene :: proc(scene: ^Scene) { Debug_State :: struct {
selected_contacts: map[int]struct {},
}
init_debug_state :: proc(debug_state: ^Debug_State) {
debug_state.selected_contacts = make(map[int]struct {}, context.temp_allocator)
}
draw_debug_scene :: proc(scene: ^Scene, debug_state: ^Debug_State) {
tracy.Zone() tracy.Zone()
sim_state := get_sim_state(scene) sim_state := get_sim_state(scene)
// Static_TLAS // Static_TLAS
if true && sim_state.static_tlas.built { if true && sim_state.static_tlas.built {
bvh.debug_draw_bvh_bounds_mesh( bvh.debug_draw_bvh_bounds(
&sim_state.static_tlas.bvh_tree.bvh, &sim_state.static_tlas.bvh_tree,
sim_state.static_tlas.bvh_tree.mesh, sim_state.static_tlas.level_geom_aabbs,
0,
0, 0,
) )
} }
// Dynamic TLAS // Dynamic TLAS
if true && sim_state.dynamic_tlas.built { if false && sim_state.dynamic_tlas.built {
bvh.debug_draw_bvh_bounds( bvh.debug_draw_bvh_bounds(
&sim_state.dynamic_tlas.bvh_tree, &sim_state.dynamic_tlas.bvh_tree,
sim_state.dynamic_tlas.body_aabbs, sim_state.dynamic_tlas.body_aabbs,
@ -182,32 +190,47 @@ draw_debug_scene :: proc(scene: ^Scene) {
} }
} }
if false { if true {
for &contact, contact_idx in sim_state.contact_container.contacts { for &contact, contact_idx in sim_state.contact_container.contacts {
points_a := contact.manifold.points_a if contact_idx in debug_state.selected_contacts ||
points_b := contact.manifold.points_b len(debug_state.selected_contacts) == 0 {
points_a_slice, points_b_slice := points_a := contact.manifold.points_a
points_a[:contact.manifold.points_len], points_b[:contact.manifold.points_len] points_b := contact.manifold.points_b
debug_transform_points_local_to_world(get_body(sim_state, contact.a), points_a_slice) points_a_slice, points_b_slice :=
b_handle := Body_Handle(contact.b) if contact.type == .Body_vs_Body else INVALID_BODY points_a[:contact.manifold.points_len], points_b[:contact.manifold.points_len]
debug_transform_points_local_to_world(get_body(sim_state, b_handle), points_b_slice) debug_transform_points_local_to_world(
debug_draw_manifold_points( get_body(sim_state, contact.a),
contact, points_a_slice,
-1, )
points_a_slice, b_handle :=
color = debug.int_to_color(i32(contact_idx * 2 + 0)), Body_Handle(contact.b) if contact.type == .Body_vs_Body else INVALID_BODY
) debug_transform_points_local_to_world(
debug_draw_manifold_points( get_body(sim_state, b_handle),
contact, points_b_slice,
1, )
points_b_slice, debug_draw_manifold_points(
color = debug.int_to_color(i32(contact_idx * 2 + 1)), contact,
) -1,
points_a_slice,
color = debug.int_to_color(i32(contact_idx * 2 + 0)),
)
debug_draw_manifold_points(
contact,
1,
points_b_slice,
color = debug.int_to_color(i32(contact_idx * 2 + 1)),
)
}
} }
} }
} }
draw_debug_ui :: proc(ctx: ^ui.Context, scene: ^Scene, config: Solver_Config) { draw_debug_ui :: proc(
debug_state: ^Debug_State,
ctx: ^ui.Context,
scene: ^Scene,
config: Solver_Config,
) {
tracy.Zone() tracy.Zone()
sim_state := get_sim_state(scene) sim_state := get_sim_state(scene)
@ -233,7 +256,29 @@ draw_debug_ui :: proc(ctx: ^ui.Context, scene: ^Scene, config: Solver_Config) {
@(static) search_len: int @(static) search_len: int
ui.textbox(ctx, search_buf[:], &search_len) ui.textbox(ctx, search_buf[:], &search_len)
search_str := string(search_buf[0:search_len]) search_str := string(search_buf[0:search_len])
Contact_And_Index :: struct {
contact: Contact,
idx: int,
}
sorted_contacts := make(
[]Contact_And_Index,
len(sim_state.contact_container.contacts),
context.temp_allocator,
)
for &contact, i in sim_state.contact_container.contacts { for &contact, i in sim_state.contact_container.contacts {
sorted_contacts[i] = Contact_And_Index {
contact = contact,
idx = i,
}
}
slice.reverse_sort_by_key(
sorted_contacts,
#force_inline proc(contact_and_index: Contact_And_Index) -> f32 {
return lg.max(contact_and_index.contact.total_normal_impulse)
},
)
for &contact_and_index, i in sorted_contacts {
contact := contact_and_index.contact
title: string title: string
ui.push_id(ctx, mem.any_to_bytes(i)) ui.push_id(ctx, mem.any_to_bytes(i))
@ -241,23 +286,38 @@ draw_debug_ui :: proc(ctx: ^ui.Context, scene: ^Scene, config: Solver_Config) {
if contact.type == .Body_vs_Body { if contact.type == .Body_vs_Body {
title = fmt.tprintf( title = fmt.tprintf(
"%v v %v", "%d %v v %v",
contact_and_index.idx,
name.to_string(get_body(sim_state, contact.a).name), name.to_string(get_body(sim_state, contact.a).name),
name.to_string(get_body(sim_state, Body_Handle(contact.b)).name), name.to_string(get_body(sim_state, Body_Handle(contact.b)).name),
) )
} else { } else {
title = fmt.tprintf( title = fmt.tprintf(
"%v v lvl", "%d %v v lvl",
contact_and_index.idx,
name.to_string(get_body(sim_state, contact.a).name), name.to_string(get_body(sim_state, contact.a).name),
) )
} }
if strings.contains(title, search_str) { if strings.contains(title, search_str) {
ui.inspect_value(ctx, title, contact) if ui.inspect_value(ctx, title, contact) {
debug_state.selected_contacts[contact_and_index.idx] = {}
}
} }
} }
} }
if .ACTIVE in ui.treenode(ctx, "Static TLAS") {
if sim_state.static_tlas.built {
bvh.debug_ui_bvh_node_recursive(
ctx,
sim_state.static_tlas.bvh_tree,
sim_state.static_tlas.level_geom_aabbs,
0,
)
}
}
if .ACTIVE in ui.treenode(ctx, "Dynamic TLAS") { if .ACTIVE in ui.treenode(ctx, "Dynamic TLAS") {
if sim_state.dynamic_tlas.built { if sim_state.dynamic_tlas.built {
bvh.debug_ui_bvh_node_recursive( bvh.debug_ui_bvh_node_recursive(

View File

@ -35,14 +35,14 @@ Contact_Pair :: union {
} }
make_contact_pair_bodies :: proc( make_contact_pair_bodies :: proc(
body_a: i32, body_a: int,
body_b: i32, body_b: int,
shape_a: i32, shape_a: i32,
shape_b: i32, shape_b: i32,
) -> Contact_Pair_Bodies { ) -> Contact_Pair_Bodies {
return { return {
index_to_body_handle(int(min(body_a, body_b))), index_to_body_handle(min(body_a, body_b)),
index_to_body_handle(int(max(body_a, body_b))), index_to_body_handle(max(body_a, body_b)),
shape_a, shape_a,
shape_b, shape_b,
} }

View File

@ -204,29 +204,27 @@ raycasts_level :: proc(
blas := get_level_geom_blas(sim_state, level_geom.blas) blas := get_level_geom_blas(sim_state, level_geom.blas)
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry) vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
// TODO: transform ray into blas space and back
blas_it := bvh.iterator_intersect_leaf_ray(&blas, ray, distance) blas_it := bvh.iterator_intersect_leaf_ray(&blas, ray, distance)
for blas_leaf_node in bvh.iterator_intersect_leaf_next(&it) { for blas_leaf_node in bvh.iterator_intersect_leaf_next(&blas_it) {
for k in 0 ..< blas_leaf_node.prim_len { for k in 0 ..< blas_leaf_node.prim_len {
tri_idx := blas.primitives[blas_leaf_node.child_or_prim_start + k] tri_idx := int(blas.primitives[blas_leaf_node.child_or_prim_start + k])
tri := get_triangle(vertices, indices, tri_idx)
hit_t, tmp_normal, _, ok := collision.intersect_ray_triangle(
{origin, origin + dir},
tri,
)
if ok && (!hit || hit_t < t) {
t = hit_t
normal = tmp_normal
hit = true
}
} }
} }
tri := get_triangle(
tlas.bvh_tree.mesh.vertices,
tlas.bvh_tree.mesh.indices,
int(tri_idx),
)
hit_t, tmp_normal, _, ok := collision.intersect_ray_triangle(
{origin, origin + dir},
tri,
)
if ok && (!hit || hit_t < t) {
t = hit_t
normal = tmp_normal
hit = true
}
} }
} }
@ -384,21 +382,22 @@ remove_invalid_contacts :: proc(
if !should_remove { if !should_remove {
level_geom_handle := Level_Geom_Handle(contact.b) level_geom_handle := Level_Geom_Handle(contact.b)
tri_offset := level_geom := get_level_geom(sim_state, level_geom_handle)
static_tlas.level_geom_tri_offset[level_geom_handle_to_index(level_geom_handle)]
tri_idx := tri_offset + int(contact.local_tri_idx) should_remove |= !level_geom.alive
should_remove |= (tri_idx * 3) >= len(static_tlas.bvh_tree.mesh.indices)
if !should_remove { if !should_remove {
aabb_a := dyn_tlas.body_aabbs[body_handle_to_index(contact.a)] tri_idx := int(contact.local_tri_idx)
tri := get_triangle( vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
static_tlas.bvh_tree.mesh.vertices, should_remove |= tri_idx * 3 >= len(indices)
static_tlas.bvh_tree.mesh.indices,
tri_idx,
)
aabb_b := get_triangle_aabb(tri)
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b) if !should_remove {
aabb_a := dyn_tlas.body_aabbs[body_handle_to_index(contact.a)]
tri := get_triangle(vertices, indices, tri_idx)
aabb_b := get_triangle_aabb(tri)
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b)
}
} }
} }
} }
@ -406,8 +405,8 @@ remove_invalid_contacts :: proc(
pair_from_contact :: proc(contact: Contact) -> (pair: Contact_Pair) { pair_from_contact :: proc(contact: Contact) -> (pair: Contact_Pair) {
if contact.type == .Body_vs_Body { if contact.type == .Body_vs_Body {
pair = make_contact_pair_bodies( pair = make_contact_pair_bodies(
i32(contact.a) - 1, body_handle_to_index(contact.a),
i32(contact.b) - 1, body_handle_to_index(Body_Handle(contact.b)),
contact.shape_a, contact.shape_a,
contact.shape_b, contact.shape_b,
) )
@ -452,9 +451,12 @@ find_new_contacts :: proc(
body_idx := u16(i) body_idx := u16(i)
body := &sim_state.bodies_slice[i] body := &sim_state.bodies_slice[i]
if body.alive { if body.alive && body.inv_mass != 0 {
tracy.ZoneN(name.to_string(body.name))
body_aabb := dyn_tlas.body_aabbs[i] body_aabb := dyn_tlas.body_aabbs[i]
{ {
tracy.ZoneN("find_new_contacts::dynamic_vs_dynamic")
it := bvh.iterator_intersect_leaf_aabb(&dyn_tlas.bvh_tree, body_aabb) it := bvh.iterator_intersect_leaf_aabb(&dyn_tlas.bvh_tree, body_aabb)
for leaf_node in bvh.iterator_intersect_leaf_next(&it) { for leaf_node in bvh.iterator_intersect_leaf_next(&it) {
@ -485,8 +487,8 @@ find_new_contacts :: proc(
) )
pair := make_contact_pair_bodies( pair := make_contact_pair_bodies(
i32(body_idx), int(body_idx),
i32(other_body_idx), int(other_body_idx),
shape_a_idx, shape_a_idx,
shape_b_idx, shape_b_idx,
) )
@ -531,66 +533,90 @@ find_new_contacts :: proc(
} }
{ {
it := bvh.iterator_intersect_leaf_aabb(&static_tlas.bvh_tree.bvh, body_aabb) tracy_ctx := tracy.ZoneN("find_new_contacts::dynamic_vs_static")
it := bvh.iterator_intersect_leaf_aabb(&static_tlas.bvh_tree, body_aabb)
num_contacts_found := 0
for leaf_node in bvh.iterator_intersect_leaf_next(&it) { for leaf_node in bvh.iterator_intersect_leaf_next(&it) {
for j in 0 ..< leaf_node.prim_len { for j in 0 ..< leaf_node.prim_len {
tri_idx := level_geom_handle := index_to_level_geom(
static_tlas.bvh_tree.bvh.primitives[leaf_node.child_or_prim_start + j] int(
tri := get_triangle( static_tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j],
static_tlas.bvh_tree.mesh.vertices, ),
static_tlas.bvh_tree.mesh.indices,
int(tri_idx),
) )
prim_aabb := get_triangle_aabb(tri) level_geom := get_level_geom(sim_state, level_geom_handle)
level_geom_handle := static_tlas.tri_to_level_geom[tri_idx] if level_geom.alive {
local_tri_idx := blas := get_level_geom_blas(sim_state, level_geom.blas)
int(tri_idx) - vertices, indices := get_level_geom_data(
static_tlas.level_geom_tri_offset[level_geom_handle_to_index(level_geom_handle)] sim_state,
assert(local_tri_idx >= 0) level_geom.geometry,
)
if bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) { blas_it := bvh.iterator_intersect_leaf_aabb(&blas, body_aabb)
for blas_leaf_node in bvh.iterator_intersect_leaf_next(&blas_it) {
shapes_it := shapes_iterator(sim_state, body.shape) for k in 0 ..< blas_leaf_node.prim_len {
for shape in shapes_iterator_next(&shapes_it) { tri_idx := int(
shape_idx := shapes_it.counter - 1 blas.primitives[blas_leaf_node.child_or_prim_start + k],
shape_aabb := shape_get_aabb(shape^)
shape_aabb = body_transform_shape_aabb(body, shape_aabb)
bvh_shape_aabb := bvh.AABB {
min = shape_aabb.center - shape_aabb.extent,
max = shape_aabb.center + shape_aabb.extent,
}
pair := make_contact_pair_body_level(
index_to_body_handle(int(body_idx)),
level_geom_handle,
shape_idx,
i32(local_tri_idx),
)
if bvh.test_aabb_vs_aabb(prim_aabb, bvh_shape_aabb) &&
!(pair in sim_state.contact_container.lookup) {
new_contact_idx := len(sim_state.contact_container.contacts)
resize_soa(
&sim_state.contact_container.contacts,
new_contact_idx + 1,
) )
contact := &sim_state.contact_container.contacts[new_contact_idx] tri := get_triangle(vertices, indices, tri_idx)
prim_aabb := get_triangle_aabb(tri)
contact^ = Contact { if bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) {
type = .Body_vs_Level,
a = index_to_body_handle(i), shapes_it := shapes_iterator(sim_state, body.shape)
shape_a = shape_idx, for shape in shapes_iterator_next(&shapes_it) {
b = i32(level_geom_handle), shape_idx := shapes_it.counter - 1
local_tri_idx = i32(local_tri_idx), shape_aabb := shape_get_aabb(shape^)
shape_aabb = body_transform_shape_aabb(
body,
shape_aabb,
)
bvh_shape_aabb := bvh.AABB {
min = shape_aabb.center - shape_aabb.extent,
max = shape_aabb.center + shape_aabb.extent,
}
pair := make_contact_pair_body_level(
index_to_body_handle(int(body_idx)),
level_geom_handle,
shape_idx,
i32(tri_idx),
)
if bvh.test_aabb_vs_aabb(prim_aabb, bvh_shape_aabb) &&
!(pair in sim_state.contact_container.lookup) {
num_contacts_found += 1
new_contact_idx := len(
sim_state.contact_container.contacts,
)
resize_soa(
&sim_state.contact_container.contacts,
new_contact_idx + 1,
)
contact := &sim_state.contact_container.contacts[new_contact_idx]
contact^ = Contact {
type = .Body_vs_Level,
a = index_to_body_handle(i),
shape_a = shape_idx,
b = i32(level_geom_handle),
local_tri_idx = i32(tri_idx),
}
sim_state.contact_container.lookup[pair] = i32(
new_contact_idx,
)
}
}
} }
sim_state.contact_container.lookup[pair] = i32(new_contact_idx)
} }
} }
} }
} }
} }
tracy.ZoneValue(tracy_ctx, u64(num_contacts_found))
} }
} }
} }
@ -662,8 +688,6 @@ Contact :: struct {
// shape index in each body // shape index in each body
shape_a, shape_b: i32, shape_a, shape_b: i32,
local_tri_idx: i32, local_tri_idx: i32,
prev_x_a, prev_x_b: Vec3,
prev_q_a, prev_q_b: Quat,
manifold: collision.Contact_Manifold, manifold: collision.Contact_Manifold,
applied_corrections: int, applied_corrections: int,
@ -682,6 +706,9 @@ Contact :: struct {
update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) { update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
tracy.Zone() tracy.Zone()
temp := runtime.default_temp_allocator_temp_begin()
defer runtime.default_temp_allocator_temp_end(temp)
graph_color_bitmask: [4]bit_array.Bit_Array graph_color_bitmask: [4]bit_array.Bit_Array
for i in 0 ..< len(graph_color_bitmask) { for i in 0 ..< len(graph_color_bitmask) {
bit_array.init( bit_array.init(
@ -692,6 +719,38 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
) )
} }
Body_And_Shape_Index :: struct {
body_index: i32,
shape_index: i32,
}
body_mesh_cache := make(map[Body_And_Shape_Index]collision.Convex, context.temp_allocator)
get_body_mesh :: #force_inline proc(
body_mesh_cache: ^map[Body_And_Shape_Index]collision.Convex,
sim_state: ^Sim_State,
body_idx: int,
shape: i32,
) -> (
convex: collision.Convex,
) {
key := Body_And_Shape_Index {
body_index = i32(body_idx),
shape_index = shape,
}
ok: bool
convex, ok = body_mesh_cache[key]
if !ok {
convex = body_get_convex_shape_by_linear_index_world(
sim_state,
&sim_state.bodies_slice[body_idx],
shape,
)
body_mesh_cache[key] = convex
}
return convex
}
for contact_idx in 0 ..< len(sim_state.contact_container.contacts) { for contact_idx in 0 ..< len(sim_state.contact_container.contacts) {
contact := &sim_state.contact_container.contacts[contact_idx] contact := &sim_state.contact_container.contacts[contact_idx]
@ -704,10 +763,6 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
old_total_normal_impulse := contact.total_normal_impulse old_total_normal_impulse := contact.total_normal_impulse
old_total_friction_impulse := contact.total_friction_impulse old_total_friction_impulse := contact.total_friction_impulse
contact.prev_x_a = body.x
contact.prev_x_b = body2.x
contact.prev_q_a = body.q
contact.prev_q_b = body2.q
contact.manifold = {} contact.manifold = {}
contact.total_normal_impulse = 0 contact.total_normal_impulse = 0
contact.total_friction_impulse = 0 contact.total_friction_impulse = 0
@ -716,9 +771,18 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
contact.applied_static_friction = false contact.applied_static_friction = false
contact.applied_normal_correction = 0 contact.applied_normal_correction = 0
// aabb1, aabb2 := body_get_aabb(body), body_get_aabb(body2) // aabb1 := body_get_aabb(body)
// aabb2: AABB
// switch contact.type {
// case .Body_vs_Body:
// aabb2 = body_get_aabb(body2)
// case .Body_vs_Level:
// level_geom_handle := Level_Geom_Handle(contact.b)
// level_geom := get_level_geom(sim_state, level_geom_handle)
// aabb2 = level_geom.aabb
// }
// TODO: extract common math functions into a sane place // // TODO: extract common math functions into a sane place
// if !collision.test_aabb_vs_aabb( // if !collision.test_aabb_vs_aabb(
// {min = aabb1.center - aabb1.extent, max = aabb1.center + aabb1.extent}, // {min = aabb1.center - aabb1.extent, max = aabb1.center + aabb1.extent},
// {min = aabb2.center - aabb2.extent, max = aabb2.center + aabb2.extent}, // {min = aabb2.center - aabb2.extent, max = aabb2.center + aabb2.extent},
@ -726,22 +790,27 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
// continue // continue
// } // }
m1 := body_get_convex_shape_by_linear_index_world(sim_state, body, contact.shape_a) m1 := get_body_mesh(
&body_mesh_cache,
sim_state,
body_handle_to_index(contact.a),
contact.shape_a,
)
m2: collision.Convex m2: collision.Convex
switch contact.type { switch contact.type {
case .Body_vs_Body: case .Body_vs_Body:
m2 = body_get_convex_shape_by_linear_index_world(sim_state, body2, contact.shape_b) m2 = get_body_mesh(
&body_mesh_cache,
sim_state,
body_handle_to_index(b_handle),
contact.shape_b,
)
case .Body_vs_Level: case .Body_vs_Level:
level_geom_handle := Level_Geom_Handle(contact.b) level_geom_handle := Level_Geom_Handle(contact.b)
tri_idx := level_geom := get_level_geom(sim_state, level_geom_handle)
static_tlas.level_geom_tri_offset[level_geom_handle_to_index(level_geom_handle)] + vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
int(contact.local_tri_idx) tri := get_triangle(vertices, indices, int(contact.local_tri_idx))
tri := get_triangle(
static_tlas.bvh_tree.mesh.vertices,
static_tlas.bvh_tree.mesh.indices,
tri_idx,
)
m2 = collision.double_sided_triangle_to_convex(tri, context.temp_allocator) m2 = collision.double_sided_triangle_to_convex(tri, context.temp_allocator)
} }
@ -814,7 +883,7 @@ pgs_solve_contacts :: proc(
inv_dt: f32, inv_dt: f32,
apply_bias: bool, apply_bias: bool,
) { ) {
bias_rate, mass_coef, impulse_coef := calculate_soft_constraint_params(30, 0.8, dt) bias_rate, mass_coef, impulse_coef := calculate_soft_constraint_params(40, 1.0, dt)
if !apply_bias { if !apply_bias {
mass_coef = 1 mass_coef = 1
bias_rate = 0 bias_rate = 0
@ -827,10 +896,10 @@ pgs_solve_contacts :: proc(
random_order[i] = i32(i) random_order[i] = i32(i)
} }
for i in 0 ..< len(random_order) - 1 { // for i in 0 ..< len(random_order) - 1 {
j := rand.int_max(len(random_order)) // j := rand.int_max(len(random_order))
slice.swap(random_order, i, j) // slice.swap(random_order, i, j)
} // }
} }
for i in 0 ..< len(sim_state.contact_container.contacts) { for i in 0 ..< len(sim_state.contact_container.contacts) {
@ -1369,7 +1438,11 @@ pgs_substep :: proc(
manifold := &contact.manifold manifold := &contact.manifold
body1, body2 := body1, body2 :=
get_body(sim_state, contact.a), get_body(sim_state, Body_Handle(contact.b)) get_body(sim_state, contact.a),
get_body(
sim_state,
contact.type == .Body_vs_Body ? Body_Handle(contact.b) : INVALID_BODY,
)
for point_idx in 0 ..< manifold.points_len { for point_idx in 0 ..< manifold.points_len {
p1, p2 := p1, p2 :=

View File

@ -50,10 +50,6 @@ xpbd_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_
contact^ = Contact { contact^ = Contact {
a = contact.a, a = contact.a,
b = contact.b, b = contact.b,
prev_x_a = body.x,
prev_x_b = body2.x,
prev_q_a = body.q,
prev_q_b = body2.q,
manifold = contact.manifold, manifold = contact.manifold,
} }

View File

@ -1845,7 +1845,7 @@ inspect_struct :: proc(
v: any, v: any,
info: runtime.Type_Info_Struct, info: runtime.Type_Info_Struct,
type_name: string, type_name: string,
) { ) -> bool {
if .ACTIVE in treenode(ctx, name) { if .ACTIVE in treenode(ctx, name) {
if .raw_union in info.flags { if .raw_union in info.flags {
if type_name == "" { if type_name == "" {
@ -1853,7 +1853,7 @@ inspect_struct :: proc(
} else { } else {
keyval(ctx, name, fmt.tprintf("%v{}", type_name)) keyval(ctx, name, fmt.tprintf("%v{}", type_name))
} }
return return true
} }
is_soa := info.soa_kind != .None is_soa := info.soa_kind != .None
@ -1923,7 +1923,11 @@ inspect_struct :: proc(
} }
} }
} }
return true
} }
return false
} }
is_type_numeric :: proc(type_info: ^runtime.Type_Info) -> bool { is_type_numeric :: proc(type_info: ^runtime.Type_Info) -> bool {
@ -1934,10 +1938,10 @@ is_type_numeric :: proc(type_info: ^runtime.Type_Info) -> bool {
return false return false
} }
inspect_value :: proc(ctx: ^Context, name: string, v: any, type_name: string = "") { inspect_value :: proc(ctx: ^Context, name: string, v: any, type_name: string = "") -> bool {
if v.data == nil || v.id == nil { if v.data == nil || v.id == nil {
label(ctx, "<nil>") label(ctx, "<nil>")
return return true
} }
type_info := type_info_of(v.id) type_info := type_info_of(v.id)
@ -1945,7 +1949,7 @@ inspect_value :: proc(ctx: ^Context, name: string, v: any, type_name: string = "
case runtime.Type_Info_Any: // Ignore case runtime.Type_Info_Any: // Ignore
case runtime.Type_Info_Parameters: // Ignore case runtime.Type_Info_Parameters: // Ignore
case runtime.Type_Info_Named: case runtime.Type_Info_Named:
inspect_value(ctx, name, any{v.data, info.base.id}, info.name) return inspect_value(ctx, name, any{v.data, info.base.id}, info.name)
case runtime.Type_Info_Boolean, case runtime.Type_Info_Boolean,
runtime.Type_Info_Integer, runtime.Type_Info_Integer,
@ -2157,7 +2161,7 @@ inspect_value :: proc(ctx: ^Context, name: string, v: any, type_name: string = "
m := (^mem.Raw_Map)(v.data) m := (^mem.Raw_Map)(v.data)
if m != nil { if m != nil {
if info.map_info == nil { if info.map_info == nil {
return return false
} }
map_cap := uintptr(runtime.map_cap(m^)) map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info) ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
@ -2178,15 +2182,19 @@ inspect_value :: proc(ctx: ^Context, name: string, v: any, type_name: string = "
) )
} }
} }
} else {
return false
} }
case runtime.Type_Info_Struct: case runtime.Type_Info_Struct:
inspect_struct(ctx, name, v, info, "") return inspect_struct(ctx, name, v, info, "")
case runtime.Type_Info_Union: case runtime.Type_Info_Union:
// fmt_union(fi, v, verb, info, type_info.size) // fmt_union(fi, v, verb, info, type_info.size)
} }
return true
} }
@(private) @(private)

BIN
src_assets/testblend.blend (Stored with Git LFS)

Binary file not shown.

BIN
src_assets/testblend.blend1 (Stored with Git LFS)

Binary file not shown.