Updates 3

This commit is contained in:
sergeypdev 2025-06-28 23:24:03 +04:00
parent d53c1a3ae6
commit bce37a3b7e
7 changed files with 193 additions and 128 deletions

View File

@ -20,7 +20,6 @@ import "core:hash"
import "core:log"
import "core:math"
import "core:math/linalg"
import "core:slice"
import "game:physics"
import "game:physics/bvh"
import "game:render"
@ -484,10 +483,12 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
if true {
for x in 0 ..< 10 {
for y in 0 ..< 10 {
box_name := name.from_string(fmt.tprintf("box[{},{}]", x, y))
physics.immediate_body(
&world.physics_scene,
hash.fnv32a(slice.to_bytes([]int{(x | y << 8)})),
u32(box_name),
physics.Body_Config {
name = box_name,
initial_pos = {-5, 0.5 + f32(y) * 1.1, f32(x) * 3 + 10},
initial_rot = linalg.QUATERNIONF32_IDENTITY,
shapes = {
@ -696,15 +697,20 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
{
level_model := assets.get_model(&g_mem.assetman, "assets/testblend.glb")
for i in 0 ..< level_model.meshCount {
for i in 0 ..< min(level_model.meshCount, 32) {
mesh := &level_model.meshes[i]
if mesh.triangleCount > 0 {
assert(mesh.vertexCount <= i32(max(u16)))
m := level_model.transform
pos := physics.Vec3{m[3][0], m[3][1], m[3][2]}
rotation := linalg.quaternion_from_matrix4_f32(auto_cast level_model.transform)
physics.immediate_level_geom(
&world.physics_scene,
hash.fnv32a(transmute([]byte)(fmt.tprintf("level mesh {}", i))),
{
rotation = linalg.QUATERNIONF32_IDENTITY,
position = pos,
rotation = rotation,
vertices = (cast([^]rl.Vector3)mesh.vertices)[:mesh.vertexCount],
indices = mesh.indices[:mesh.triangleCount * 3],
},
@ -1051,7 +1057,7 @@ draw_world :: proc(world: ^World) {
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_body := physics.get_body(sim_state, world.car_handle)
car_model := assets.get_model(&g_mem.assetman, "assets/ice_cream_truck.glb")
@ -1127,7 +1133,7 @@ draw_world :: proc(world: ^World) {
// .VEC3,
// )
render.draw_model(level_model, {}, 1)
// render.draw_model(level_model, {}, 1)
render.draw_model(car_model, {}, car_matrix)

View File

@ -47,6 +47,12 @@ Mesh_BVH :: struct {
mesh: Mesh,
}
destroy_mesh_bvh :: proc(bvh: ^Mesh_BVH) {
destroy_bvh(&bvh.bvh)
delete(bvh.mesh.vertices)
delete(bvh.mesh.indices)
}
Node :: struct {
aabb: AABB,
// Index of the left child, right child is left_child + 1

View File

@ -14,6 +14,12 @@ import "libs:tracy"
_ :: fmt
_ :: log
@(private = "file")
Traversal :: struct {
node_idx: i32,
should_draw: bool,
}
// Assuming rl.BeginMode3D was called before this
debug_draw_bvh_bounds_mesh :: proc(bvh: ^BVH, mesh: Mesh, pos: rl.Vector3, node_index: int) {
old_width := rlgl.GetLineWidth()
@ -23,10 +29,6 @@ debug_draw_bvh_bounds_mesh :: proc(bvh: ^BVH, mesh: Mesh, pos: rl.Vector3, node_
temp := runtime.default_temp_allocator_temp_begin()
defer runtime.default_temp_allocator_temp_end(temp)
Traversal :: struct {
node_idx: i32,
should_draw: bool,
}
nodes_to_process: queue.Queue(Traversal)
queue.init(&nodes_to_process, queue.DEFAULT_CAPACITY, context.temp_allocator)
@ -100,10 +102,6 @@ debug_draw_bvh_bounds :: proc(bvh: ^BVH, primitive_bounds: []AABB, node_index: i
temp := runtime.default_temp_allocator_temp_begin()
defer runtime.default_temp_allocator_temp_end(temp)
Traversal :: struct {
node_idx: i32,
should_draw: bool,
}
nodes_to_process: queue.Queue(Traversal)
queue.init(&nodes_to_process, queue.DEFAULT_CAPACITY, context.temp_allocator)

View File

@ -54,6 +54,16 @@ draw_debug_scene :: proc(scene: ^Scene) {
sim_state := get_sim_state(scene)
// Static_TLAS
if true && sim_state.static_tlas.built {
bvh.debug_draw_bvh_bounds_mesh(
&sim_state.static_tlas.bvh_tree.bvh,
sim_state.static_tlas.bvh_tree.mesh,
0,
0,
)
}
// Dynamic TLAS
if true && sim_state.dynamic_tlas.built {
bvh.debug_draw_bvh_bounds(
@ -99,20 +109,20 @@ draw_debug_scene :: proc(scene: ^Scene) {
}
}
if true {
for &level_geom, geom_idx in sim_state.level_geoms {
if level_geom.alive {
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
// if true {
// for &level_geom, geom_idx in sim_state.level_geoms {
// if level_geom.alive {
// vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
for i in 0 ..< len(indices) / 3 {
i1, i2, i3 := indices[i * 3 + 0], indices[i * 3 + 1], indices[i * 3 + 2]
v1, v2, v3 := vertices[i1], vertices[i2], vertices[i3]
// for i in 0 ..< len(indices) / 3 {
// i1, i2, i3 := indices[i * 3 + 0], indices[i * 3 + 1], indices[i * 3 + 2]
// v1, v2, v3 := vertices[i1], vertices[i2], vertices[i3]
rl.DrawTriangle3D(v1, v2, v3, debug.int_to_color(i32(geom_idx + 1)))
}
}
}
}
// rl.DrawTriangle3D(v1, v2, v3, debug.int_to_color(i32(geom_idx + 1)))
// }
// }
// }
// }
for _, i in sim_state.suspension_constraints {
wheel := &sim_state.suspension_constraints_slice[i]

View File

@ -5,7 +5,6 @@ import "core:log"
import "core:math"
import lg "core:math/linalg"
import "game:halfedge"
import "game:name"
import "libs:tracy"
_ :: log
@ -264,9 +263,6 @@ body_get_convex_shape_by_linear_index_world :: proc(
mesh: collision.Convex,
) {
tracy.Zone()
if shape_linear_idx == 1 {
log.debugf("linear idx: %v, body %v", shape_linear_idx, name.to_string(body.name))
}
shape_idx := get_shape_by_index(sim_state, body.shape, shape_linear_idx)
return body_get_convex_shape_world(sim_state, body, sim_state.shapes[shape_idx], allocator)
}

View File

@ -1,5 +1,6 @@
package physics
import "bvh"
import "collision"
import lg "core:math/linalg"
import "game:container/spanpool"
@ -23,6 +24,7 @@ Contact_Pair_Bodies :: struct {
}
Contact_Pair_Body_Level :: struct {
a: Body_Handle,
b: Level_Geom_Handle,
shape_a: i32,
tri_idx: i32,
}
@ -47,11 +49,12 @@ make_contact_pair_bodies :: proc(
}
make_contact_pair_body_level :: proc(
body: i32,
body: Body_Handle,
level_geom_handle: Level_Geom_Handle,
shape_a: i32,
tri_idx: i32,
) -> Contact_Pair_Body_Level {
return {index_to_body_handle(int(body)), shape_a, tri_idx}
return {body, level_geom_handle, shape_a, tri_idx}
}
Contact_Container :: struct {
@ -107,6 +110,10 @@ Sim_State :: struct {
geometry_vertices_pool: spanpool.Span_Pool(Vec3),
geometry_indices_pool: spanpool.Span_Pool(u16),
// BLAS for level geom
blas_nodes_pool: spanpool.Span_Pool(bvh.Node),
blas_primitives_pool: spanpool.Span_Pool(u16),
// Temp stuff, kept for one frame, allocated from temp_allocator
static_tlas: Static_TLAS,
dynamic_tlas: Dynamic_TLAS,
@ -154,6 +161,8 @@ copy_sim_state :: proc(dst: ^Sim_State, src: ^Sim_State) {
spanpool.copy(&dst.gear_ratios_pool, src.gear_ratios_pool)
spanpool.copy(&dst.geometry_vertices_pool, src.geometry_vertices_pool)
spanpool.copy(&dst.geometry_indices_pool, src.geometry_indices_pool)
spanpool.copy(&dst.blas_nodes_pool, src.blas_nodes_pool)
spanpool.copy(&dst.blas_primitives_pool, src.blas_primitives_pool)
}
copy_physics_scene :: proc(dst, src: ^Scene) {
@ -377,9 +386,16 @@ Geometry_Handle :: struct {
indices: spanpool.Handle,
}
BLAS_Handle :: struct {
nodes: spanpool.Handle,
primitives: spanpool.Handle,
}
Level_Geom :: struct {
alive: bool,
aabb: AABB,
geometry: Geometry_Handle,
blas: BLAS_Handle,
x: Vec3,
q: Quat,
next_plus_one: i32,
@ -416,19 +432,19 @@ is_handle_valid :: proc {
is_level_geom_handle_valid,
}
index_to_body_handle :: proc(idx: int) -> Body_Handle {
index_to_body_handle :: #force_inline proc(idx: int) -> Body_Handle {
return Body_Handle(idx + 1)
}
index_to_level_geom :: proc(idx: int) -> Level_Geom_Handle {
index_to_level_geom :: #force_inline proc(idx: int) -> Level_Geom_Handle {
return Level_Geom_Handle(idx + 1)
}
body_handle_to_index :: proc(handle: Body_Handle) -> int {
body_handle_to_index :: #force_inline proc(handle: Body_Handle) -> int {
return int(handle) - 1
}
level_geom_handle_to_index :: proc(handle: Level_Geom_Handle) -> int {
level_geom_handle_to_index :: #force_inline proc(handle: Level_Geom_Handle) -> int {
return int(handle) - 1
}
@ -913,6 +929,13 @@ get_level_geom_data :: proc(
return
}
get_level_geom_blas :: proc(sim_state: ^Sim_State, handle: BLAS_Handle) -> (bvh: bvh.BVH) {
bvh.nodes = spanpool.resolve_slice(&sim_state.blas_nodes_pool, handle.nodes)
bvh.primitives = spanpool.resolve_slice(&sim_state.blas_primitives_pool, handle.primitives)
bvh.nodes_used = i32(len(bvh.nodes))
return
}
update_level_geom_from_config :: proc(
sim_state: ^Sim_State,
level_geom: Level_Geom_Ptr,
@ -921,6 +944,40 @@ update_level_geom_from_config :: proc(
level_geom.x = config.position
level_geom.q = config.rotation
// TODO: figure out if asset changed and rebuild only then
}
add_level_geom :: proc(sim_state: ^Sim_State, config: Level_Geom_Config) -> Level_Geom_Handle {
assert(len(config.vertices) > 0)
assert(len(config.vertices) <= int(max(u16)))
sim_state.num_level_geoms += 1
level_geom: Level_Geom
level_geom.alive = true
update_level_geom_from_config(sim_state, &level_geom, config)
blas :=
bvh.build_bvh_from_mesh(bvh.Mesh{vertices = config.vertices, indices = config.indices}, context.temp_allocator).bvh
level_geom.blas.nodes = spanpool.allocate_elems(
&sim_state.blas_nodes_pool,
..blas.nodes[:blas.nodes_used],
)
level_geom.blas.primitives = spanpool.allocate_elems(
&sim_state.blas_primitives_pool,
..blas.primitives,
)
aabb_min, aabb_max: Vec3 = max(f32), min(f32)
for v in config.vertices {
aabb_min = lg.min(aabb_min, v)
aabb_max = lg.max(aabb_max, v)
}
level_geom.aabb.center = (aabb_max + aabb_min) * 0.5
level_geom.aabb.extent = (aabb_max - aabb_min) * 0.5
if spanpool.is_handle_valid(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices) {
spanpool.free(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices)
spanpool.free(&sim_state.geometry_indices_pool, level_geom.geometry.indices)
@ -934,14 +991,6 @@ update_level_geom_from_config :: proc(
&sim_state.geometry_indices_pool,
..config.indices,
)
}
add_level_geom :: proc(sim_state: ^Sim_State, config: Level_Geom_Config) -> Level_Geom_Handle {
sim_state.num_level_geoms += 1
level_geom: Level_Geom
level_geom.alive = true
update_level_geom_from_config(sim_state, &level_geom, config)
if sim_state.first_free_level_geom_plus_one > 0 {
index := sim_state.first_free_level_geom_plus_one
@ -962,6 +1011,9 @@ remove_level_geom :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) {
spanpool.free(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices)
spanpool.free(&sim_state.geometry_indices_pool, level_geom.geometry.indices)
spanpool.free(&sim_state.blas_nodes_pool, level_geom.blas.nodes)
spanpool.free(&sim_state.blas_primitives_pool, level_geom.blas.primitives)
}
_get_first_free_body :: proc(sim_state: ^Sim_State) -> i32 {
@ -983,6 +1035,7 @@ destry_sim_state :: proc(sim_state: ^Sim_State) {
spanpool.destroy_spanpool(&sim_state.geometry_vertices_pool)
spanpool.destroy_spanpool(&sim_state.geometry_indices_pool)
static_tlas_destroy(&sim_state.static_tlas)
dynamic_tlas_destroy(&sim_state.dynamic_tlas)
}

View File

@ -71,8 +71,9 @@ Step_Mode :: enum {
}
Static_TLAS :: struct {
bvh_tree: bvh.Mesh_BVH,
tri_to_level_geom: []Level_Geom_Handle,
bvh_tree: bvh.BVH,
level_geom_aabbs: []bvh.AABB,
built: bool,
}
// Top Level Acceleration Structure
@ -82,70 +83,43 @@ Dynamic_TLAS :: struct {
built: bool,
}
build_static_tlas :: proc(sim_state: ^Sim_State) -> Static_TLAS {
build_static_tlas :: proc(sim_state: ^Sim_State, out_tlas: ^Static_TLAS) {
tracy.Zone()
num_vertices, num_indices: int
for i in 0 ..< len(sim_state.level_geoms) {
level_geom := &sim_state.level_geoms[i]
static_tlas_destroy(out_tlas)
if level_geom.alive {
num_vertices += int(level_geom.geometry.vertices.len)
num_indices += int(level_geom.geometry.indices.len)
}
}
level_geom_aabbs := make([]bvh.AABB, len(sim_state.level_geoms))
level_geom_indices := make([]u16, len(sim_state.level_geoms), context.temp_allocator)
if num_vertices == 0 {
return Static_TLAS{}
}
{
for i in 0 ..< len(sim_state.level_geoms) {
level_geom := &sim_state.level_geoms[i]
vertices := make([]bvh.Vec3, num_vertices, context.temp_allocator)
indices := make([]u16, num_indices, context.temp_allocator)
if level_geom.alive {
aabb := &level_geom_aabbs[i]
level_geom_indices[i] = u16(i)
tri_to_level_geom := make([]Level_Geom_Handle, num_indices / 3, context.temp_allocator)
num_vertices, num_indices = 0, 0
for i in 0 ..< len(sim_state.level_geoms) {
level_geom := &sim_state.level_geoms[i]
if level_geom.alive {
geom_verts, geom_inds := get_level_geom_data(sim_state, level_geom.geometry)
base_vertex := num_vertices
copy_slice(vertices[base_vertex:], geom_verts)
num_vertices += len(geom_verts)
for j in 0 ..< len(geom_inds) {
if num_indices %% 3 == 0 {
tri := num_indices / 3
tri_to_level_geom[tri] = index_to_level_geom(i)
}
ind := geom_inds[j]
indices[num_indices] = u16(base_vertex + int(ind))
num_indices += 1
aabb.min = level_geom.aabb.center - level_geom.aabb.extent
aabb.max = level_geom.aabb.center + level_geom.aabb.extent
}
}
}
log.debugf("num vertices {}, len vertices {}", num_vertices, len(vertices))
assert(num_vertices == len(vertices))
assert(num_indices == len(indices))
sim_state_bvh := bvh.build_bvh_from_aabbs(level_geom_aabbs, level_geom_indices)
// for i in 0 ..< len(indices) / 3 {
// tri := get_triangle(vertices, indices, i)
// rl.DrawTriangle3D(tri[0], tri[1], tri[2], rl.RED)
// }
result := Static_TLAS {
bvh_tree = bvh.build_bvh_from_mesh(
bvh.Mesh{vertices = vertices, indices = indices},
context.temp_allocator,
),
tri_to_level_geom = tri_to_level_geom,
out_tlas^ = Static_TLAS {
built = true,
bvh_tree = sim_state_bvh,
level_geom_aabbs = level_geom_aabbs,
}
}
return result
static_tlas_destroy :: proc(static_tlas: ^Static_TLAS) {
if static_tlas.built {
bvh.destroy_bvh(&static_tlas.bvh_tree)
delete(static_tlas.level_geom_aabbs)
static_tlas^ = {}
}
}
// TODO: free intermediate temp allocs
@ -160,18 +134,16 @@ build_dynamic_tlas :: proc(
dynamic_tlas_destroy(out_tlas)
body_aabbs := make([]bvh.AABB, sim_state.num_bodies, allocator)
body_indices := make([]u16, sim_state.num_bodies, context.temp_allocator)
body_aabbs := make([]bvh.AABB, len(sim_state.bodies_slice), allocator)
body_indices := make([]u16, len(sim_state.bodies_slice), context.temp_allocator)
{
aabb_index := 0
for i in 0 ..< len(sim_state.bodies_slice) {
body := &sim_state.bodies_slice[i]
if body.alive {
aabb := &body_aabbs[aabb_index]
body_indices[aabb_index] = u16(i)
aabb_index += 1
aabb := &body_aabbs[i]
body_indices[i] = u16(i)
phys_aabb := body_get_aabb(body)
@ -221,11 +193,24 @@ raycasts_level :: proc(
ray.origin = origin
ray.dir = dir
ray.dir_inv = 1.0 / dir
it := bvh.iterator_intersect_leaf_ray(&tlas.bvh_tree.bvh, ray, distance)
it := bvh.iterator_intersect_leaf_ray(&tlas.bvh_tree, ray, distance)
for leaf_node in bvh.iterator_intersect_leaf_next(&it) {
for j in 0 ..< leaf_node.prim_len {
tri_idx := tlas.bvh_tree.bvh.primitives[leaf_node.child_or_prim_start + j]
level_geom_handle := index_to_level_geom(
int(tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]),
)
level_geom := get_level_geom(sim_state, level_geom_handle)
blas := get_level_geom_blas(sim_state, level_geom.blas)
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
blas_it := bvh.iterator_intersect_leaf_ray(&blas, ray, distance)
for blas_leaf_node in bvh.iterator_intersect_leaf_next(&it) {
for k in 0 ..< blas_leaf_node.prim_len {
tri_idx := blas.primitives[blas_leaf_node.child_or_prim_start + k]
}
}
tri := get_triangle(
tlas.bvh_tree.mesh.vertices,
tlas.bvh_tree.mesh.indices,
@ -388,8 +373,8 @@ remove_invalid_contacts :: proc(
should_remove |= !get_body(sim_state, Body_Handle(contact.b)).alive
if !should_remove {
aabb_a := dyn_tlas.body_aabbs[int(contact.a) - 1]
aabb_b := dyn_tlas.body_aabbs[int(contact.b) - 1]
aabb_a := dyn_tlas.body_aabbs[body_handle_to_index(contact.a)]
aabb_b := dyn_tlas.body_aabbs[body_handle_to_index(Body_Handle(contact.b))]
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b)
}
@ -398,14 +383,18 @@ remove_invalid_contacts :: proc(
should_remove |= !get_body(sim_state, contact.a).alive
if !should_remove {
should_remove |= int(contact.tri_idx * 3) > len(static_tlas.bvh_tree.mesh.indices)
level_geom_handle := Level_Geom_Handle(contact.b)
tri_offset :=
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 |= (tri_idx * 3) >= len(static_tlas.bvh_tree.mesh.indices)
if !should_remove {
aabb_a := dyn_tlas.body_aabbs[int(contact.a) - 1]
aabb_a := dyn_tlas.body_aabbs[body_handle_to_index(contact.a)]
tri := get_triangle(
static_tlas.bvh_tree.mesh.vertices,
static_tlas.bvh_tree.mesh.indices,
int(contact.tri_idx),
tri_idx,
)
aabb_b := get_triangle_aabb(tri)
@ -424,9 +413,10 @@ remove_invalid_contacts :: proc(
)
} else {
pair = make_contact_pair_body_level(
i32(body_handle_to_index(contact.a)),
Body_Handle(contact.a),
Level_Geom_Handle(contact.b),
contact.shape_a,
contact.tri_idx,
contact.local_tri_idx,
)
}
return
@ -554,11 +544,16 @@ find_new_contacts :: proc(
)
prim_aabb := get_triangle_aabb(tri)
level_geom_handle := static_tlas.tri_to_level_geom[tri_idx]
local_tri_idx :=
int(tri_idx) -
static_tlas.level_geom_tri_offset[level_geom_handle_to_index(level_geom_handle)]
assert(local_tri_idx >= 0)
if bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) {
shapes_it := shapes_iterator(sim_state, body.shape)
for shape, shape_idx in shapes_iterator_next(&shapes_it) {
for shape in shapes_iterator_next(&shapes_it) {
shape_idx := shapes_it.counter - 1
shape_aabb := shape_get_aabb(shape^)
shape_aabb = body_transform_shape_aabb(body, shape_aabb)
bvh_shape_aabb := bvh.AABB {
@ -567,9 +562,10 @@ find_new_contacts :: proc(
}
pair := make_contact_pair_body_level(
i32(body_idx),
index_to_body_handle(int(body_idx)),
level_geom_handle,
shape_idx,
i32(tri_idx),
i32(local_tri_idx),
)
if bvh.test_aabb_vs_aabb(prim_aabb, bvh_shape_aabb) &&
@ -582,11 +578,11 @@ find_new_contacts :: proc(
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),
tri_idx = i32(tri_idx),
type = .Body_vs_Level,
a = index_to_body_handle(i),
shape_a = shape_idx,
b = i32(level_geom_handle),
local_tri_idx = i32(local_tri_idx),
}
sim_state.contact_container.lookup[pair] = i32(new_contact_idx)
@ -665,7 +661,7 @@ Contact :: struct {
b: i32,
// shape index in each body
shape_a, shape_b: i32,
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,
@ -737,17 +733,17 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
case .Body_vs_Body:
m2 = body_get_convex_shape_by_linear_index_world(sim_state, body2, contact.shape_b)
case .Body_vs_Level:
// level_geom := get_level_geom(sim_state, Level_Geom_Handle(contact.b))
level_geom_handle := Level_Geom_Handle(contact.b)
tri_idx :=
static_tlas.level_geom_tri_offset[level_geom_handle_to_index(level_geom_handle)] +
int(contact.local_tri_idx)
tri := get_triangle(
static_tlas.bvh_tree.mesh.vertices,
static_tlas.bvh_tree.mesh.indices,
int(contact.tri_idx),
tri_idx,
)
// rl.DrawTriangle3D(tri[0], tri[1], tri[2], rl.BLUE)
m2 = collision.double_sided_triangle_to_convex(tri, context.temp_allocator)
// m2 = level_geom_get_convex_shape(sim_state, level_geom, int(contact.tri_idx))
// he.debug_draw_mesh_wires(m2, rl.BLUE)
}
// Raw manifold has contact points in world space
@ -818,7 +814,7 @@ pgs_solve_contacts :: proc(
inv_dt: f32,
apply_bias: bool,
) {
bias_rate, mass_coef, impulse_coef := calculate_soft_constraint_params(16, 1, dt)
bias_rate, mass_coef, impulse_coef := calculate_soft_constraint_params(30, 0.8, dt)
if !apply_bias {
mass_coef = 1
bias_rate = 0
@ -1521,7 +1517,7 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi
dt := f32(dt_64)
inv_dt := f32(inv_dt_64)
sim_state.static_tlas = build_static_tlas(sim_state)
build_static_tlas(sim_state, &sim_state.static_tlas)
build_dynamic_tlas(sim_state, config, &sim_state.dynamic_tlas)
remove_invalid_contacts(sim_state, sim_state.static_tlas, sim_state.dynamic_tlas)