First implementation of track collision (non convex triangle level geometry)
This commit is contained in:
parent
890ac2494a
commit
735c61fd05
@ -28,9 +28,17 @@ destroy_spanpool :: proc(s: ^$T/Span_Pool($E)) {
|
||||
delete(s.free_spans)
|
||||
}
|
||||
|
||||
// TODO: use everywhere
|
||||
is_handle_valid :: proc(s: ^$T/Span_Pool($E), handle: Handle) -> bool {
|
||||
return(
|
||||
handle.gen != 0 &&
|
||||
int(handle.first + handle.len) <= len(s.elems) &&
|
||||
s.generations[handle.first] == handle.gen \
|
||||
)
|
||||
}
|
||||
|
||||
resolve_slice :: proc(s: ^$T/Span_Pool($E), handle: Handle, loc := #caller_location) -> []E {
|
||||
assert(int(handle.first + handle.len) <= len(s.elems), "invalid spanpool handle", loc)
|
||||
assert(s.generations[handle.first] == handle.gen, "invalid spanpool handle", loc)
|
||||
assert(is_handle_valid(s, handle), "invalid spanpool handle", loc)
|
||||
|
||||
return s.elems[handle.first:handle.first + handle.len]
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ Car :: struct {
|
||||
SOLVER_CONFIG :: physics.Solver_Config {
|
||||
timestep = 1.0 / 60,
|
||||
gravity = rl.Vector3{0, -9.8, 0},
|
||||
substreps_minus_one = 8 - 1,
|
||||
substreps_minus_one = 4 - 1,
|
||||
}
|
||||
|
||||
Game_Memory :: struct {
|
||||
@ -359,18 +359,21 @@ World_Update_Config :: struct {
|
||||
}
|
||||
|
||||
update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
rl.BeginDrawing()
|
||||
rl.BeginMode3D(game_camera_3d())
|
||||
if !world.pause {
|
||||
car_model := assets.get_model(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb")
|
||||
car_bounds := rl.GetModelBoundingBox(car_model)
|
||||
world.car_com = (car_bounds.min + car_bounds.max) / 2
|
||||
|
||||
if true {
|
||||
physics.immediate_body(
|
||||
&world.physics_scene,
|
||||
#hash("floor", "fnv32a"),
|
||||
physics.Body_Config {
|
||||
initial_pos = {0, -0.5, 0},
|
||||
initial_rot = linalg.QUATERNIONF32_IDENTITY,
|
||||
shape = physics.Shape_Box{size = {1000, 1, 1000}},
|
||||
shape = physics.Shape_Box{size = {100, 1, 100}},
|
||||
},
|
||||
)
|
||||
|
||||
@ -383,6 +386,7 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
shape = physics.Shape_Box{size = {5, 1, 100}},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
car_convex := assets.get_convex(&g_mem.assetman, "assets/car_convex.obj")
|
||||
|
||||
@ -614,6 +618,26 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
wheel.turn_angle = TURN_ANGLE * turn_vel_correction * turn_input
|
||||
}
|
||||
|
||||
if len(world.track.points) > 1 {
|
||||
interpolated_points := calculate_spline_interpolated_points(
|
||||
world.track.points[:],
|
||||
context.temp_allocator,
|
||||
)
|
||||
track_verts, track_inds := spline_generate_mesh(
|
||||
interpolated_points,
|
||||
context.temp_allocator,
|
||||
)
|
||||
physics.immediate_level_geom(
|
||||
&world.physics_scene,
|
||||
#hash("track", "fnv32a"),
|
||||
{
|
||||
rotation = linalg.QUATERNIONF32_IDENTITY,
|
||||
vertices = track_verts,
|
||||
indices = track_inds,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
physics.simulate(
|
||||
&world.physics_scene,
|
||||
SOLVER_CONFIG,
|
||||
@ -1019,7 +1043,6 @@ draw_world :: proc(world: ^World) {
|
||||
draw :: proc() {
|
||||
tracy.Zone()
|
||||
|
||||
rl.BeginDrawing()
|
||||
defer rl.EndDrawing()
|
||||
rl.ClearBackground(rl.GRAY)
|
||||
render.clear_stencil()
|
||||
@ -1038,14 +1061,13 @@ draw :: proc() {
|
||||
// )
|
||||
|
||||
{
|
||||
rl.BeginMode3D(camera)
|
||||
defer rl.EndMode3D()
|
||||
|
||||
draw_world(world)
|
||||
|
||||
{
|
||||
// Debug draw spline road
|
||||
{
|
||||
if false {
|
||||
rlgl.EnableWireMode()
|
||||
defer rlgl.DisableWireMode()
|
||||
|
||||
|
@ -102,8 +102,8 @@ mesh_from_vertex_index_list :: proc(
|
||||
faces[f].edge = Edge_Index(e)
|
||||
}
|
||||
|
||||
next_edge := f * vertices_per_face + ((i + 1) % vertices_per_face)
|
||||
prev_edge := f * vertices_per_face + ((i - 1) % vertices_per_face)
|
||||
next_edge := f * vertices_per_face + ((i + 1) %% vertices_per_face)
|
||||
prev_edge := f * vertices_per_face + ((i - 1) %% vertices_per_face)
|
||||
|
||||
edges[e] = {
|
||||
origin = Vertex_Index(index),
|
||||
|
@ -8,8 +8,8 @@ import "core:math"
|
||||
import lg "core:math/linalg"
|
||||
import "core:mem"
|
||||
import "game:debug"
|
||||
import "libs:tracy"
|
||||
import rl "libs:raylib"
|
||||
import "libs:tracy"
|
||||
|
||||
_ :: log
|
||||
_ :: rl
|
||||
@ -70,6 +70,7 @@ build_bvh_from_mesh :: proc(mesh: Mesh, allocator := context.allocator) -> (mesh
|
||||
assert(len(indices) % 3 == 0)
|
||||
|
||||
bvh := &mesh_bvh.bvh
|
||||
mesh_bvh.mesh = mesh
|
||||
|
||||
num_triangles := len(indices) / 3
|
||||
|
||||
@ -93,18 +94,10 @@ build_bvh_from_mesh :: proc(mesh: Mesh, allocator := context.allocator) -> (mesh
|
||||
i1, i2, i3 := indices[i * 3], indices[i * 3 + 1], indices[i * 3 + 2]
|
||||
v1, v2, v3 := vertices[i1], vertices[i2], vertices[i3]
|
||||
|
||||
centroids[i] = (v1 + v2 + v3) * 0.33333333333
|
||||
centroids[i] = (v1 + v2 + v3) / 3.0
|
||||
|
||||
aabbs[i].min = Vec3 {
|
||||
min(v1.x, v2.x, v3.x),
|
||||
min(v1.y, v2.y, v3.y),
|
||||
min(v1.z, v2.z, v3.z),
|
||||
}
|
||||
aabbs[i].max = Vec3 {
|
||||
max(v1.x, v2.x, v3.x),
|
||||
max(v1.y, v2.y, v3.y),
|
||||
max(v1.z, v2.z, v3.z),
|
||||
}
|
||||
aabbs[i].min = lg.min_triple(v1, v2, v3)
|
||||
aabbs[i].max = lg.max_triple(v1, v2, v3)
|
||||
|
||||
size := aabbs[i].max - aabbs[i].min
|
||||
assert(size.x >= 0)
|
||||
@ -286,8 +279,10 @@ iterator_intersect_leaf_aabb :: proc(
|
||||
) {
|
||||
it.bvh = bvh
|
||||
it.bounds = bounds
|
||||
if len(it.bvh.nodes) > 0 {
|
||||
queue.init(&it.nodes_to_process, queue.DEFAULT_CAPACITY, context.temp_allocator)
|
||||
queue.push_back(&it.nodes_to_process, 0)
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
@ -302,8 +297,10 @@ iterator_intersect_leaf_ray :: proc(
|
||||
it.bvh = bvh
|
||||
it.ray = ray
|
||||
it.min_t = distance
|
||||
if len(it.bvh.nodes) > 0 {
|
||||
queue.init(&it.nodes_to_process, queue.DEFAULT_CAPACITY, context.temp_allocator)
|
||||
queue.push_back(&it.nodes_to_process, 0)
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
|
@ -41,6 +41,26 @@ box_to_convex :: proc(box: Box, allocator := context.allocator) -> (convex: Conv
|
||||
return
|
||||
}
|
||||
|
||||
double_sided_triangle_indices := [6]u16{0, 1, 2, 0, 2, 1}
|
||||
|
||||
double_sided_triangle_to_convex :: proc(
|
||||
tri: [3]Vec3,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
convex: Convex,
|
||||
) {
|
||||
tri := tri
|
||||
convex = Convex(
|
||||
halfedge.mesh_from_vertex_index_list(
|
||||
tri[:],
|
||||
double_sided_triangle_indices[:],
|
||||
3,
|
||||
allocator,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
Contact_Type_Face :: struct {
|
||||
face_idx_a: halfedge.Face_Index,
|
||||
face_idx_b: halfedge.Face_Index,
|
||||
@ -85,8 +105,8 @@ convex_vs_convex_sat :: proc(a, b: Convex) -> (manifold: Contact_Manifold, colli
|
||||
return
|
||||
}
|
||||
|
||||
is_face_a_contact := (face_query_a.separation + 0.2) > edge_separation
|
||||
is_face_b_contact := (face_query_b.separation + 0.2) > edge_separation
|
||||
is_face_a_contact := (face_query_a.separation + 0.001) > edge_separation
|
||||
is_face_b_contact := (face_query_b.separation + 0.001) > edge_separation
|
||||
|
||||
if is_face_a_contact || is_face_b_contact {
|
||||
manifold = create_face_contact_manifold(face_query_a, a, face_query_b, b)
|
||||
@ -287,7 +307,7 @@ create_face_contact_manifold :: proc(
|
||||
) {
|
||||
tracy.Zone()
|
||||
|
||||
is_ref_a := (face_query_a.separation + 0.1) > face_query_b.separation
|
||||
is_ref_a := (face_query_a.separation + 0.01) > face_query_b.separation
|
||||
ref_face_query := is_ref_a ? face_query_a : face_query_b
|
||||
ref_convex := is_ref_a ? a : b
|
||||
inc_convex := is_ref_a ? b : a
|
||||
@ -400,7 +420,18 @@ create_face_contact_manifold :: proc(
|
||||
clipping_plane_center /= f32(num)
|
||||
}
|
||||
|
||||
clipping_plane := plane_from_point_normal(clipping_face_vert, clipping_face.normal)
|
||||
clipping_normal := clipping_face.normal
|
||||
|
||||
// If adjacent face is facing backwards from us (like a double sided triangle)
|
||||
// use the edge normal
|
||||
if lg.dot(ref_face.normal, clipping_face.normal) < -0.99 {
|
||||
e1, e2 := halfedge.get_edge_points(ref_convex, edge)
|
||||
|
||||
clipping_normal = lg.cross(e2 - e1, clipping_face_vert - clipping_plane_center)
|
||||
clipping_normal = -lg.normalize0(lg.cross(e2 - e1, clipping_normal))
|
||||
}
|
||||
|
||||
clipping_plane := plane_from_point_normal(clipping_face_vert, clipping_normal)
|
||||
|
||||
// Actual clipping
|
||||
{
|
||||
@ -578,7 +609,7 @@ create_edge_contact_manifold :: proc(
|
||||
edge_idx_b = edge_b,
|
||||
}
|
||||
manifold.normal = separating_plane.normal
|
||||
manifold.separation = separation
|
||||
manifold.separation = lg.dot(ps[0] - ps[1], separating_plane.normal)
|
||||
manifold.points_a[0] = ps[0]
|
||||
manifold.points_b[0] = ps[1]
|
||||
manifold.points_len = 1
|
||||
|
@ -69,6 +69,21 @@ draw_debug_scene :: proc(scene: ^Scene) {
|
||||
}
|
||||
}
|
||||
|
||||
if false {
|
||||
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]
|
||||
|
||||
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]
|
||||
if wheel.alive {
|
||||
@ -130,7 +145,8 @@ draw_debug_scene :: proc(scene: ^Scene) {
|
||||
points_a_slice, points_b_slice :=
|
||||
points_a[:contact.manifold.points_len], points_b[:contact.manifold.points_len]
|
||||
debug_transform_points_local_to_world(get_body(sim_state, contact.a), points_a_slice)
|
||||
debug_transform_points_local_to_world(get_body(sim_state, contact.b), points_b_slice)
|
||||
b_handle := Body_Handle(contact.b) if contact.type == .Body_vs_Body else INVALID_BODY
|
||||
debug_transform_points_local_to_world(get_body(sim_state, b_handle), points_b_slice)
|
||||
debug_draw_manifold_points(
|
||||
contact,
|
||||
-1,
|
||||
|
@ -125,6 +125,21 @@ body_get_convex_shape_world :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
level_geom_get_convex_shape :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
level_geom: Level_Geom_Ptr,
|
||||
tri_idx: int,
|
||||
allocator := context.temp_allocator,
|
||||
) -> (
|
||||
mesh: collision.Convex,
|
||||
) {
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
|
||||
return collision.double_sided_triangle_to_convex(
|
||||
get_transformed_triangle(vertices, indices, tri_idx, level_geom.x, level_geom.q),
|
||||
allocator,
|
||||
)
|
||||
}
|
||||
|
||||
shape_get_aabb :: proc(shape: Collision_Shape) -> (aabb: AABB) {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
|
@ -170,6 +170,29 @@ immediate_engine :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
immediate_level_geom :: proc(
|
||||
scene: ^Scene,
|
||||
id: u32,
|
||||
config: Level_Geom_Config,
|
||||
) -> (
|
||||
handle: Level_Geom_Handle,
|
||||
) {
|
||||
state := &scene.solver_state
|
||||
sim_state := get_sim_state(scene)
|
||||
h, ok := immediate_container_find_or_add(
|
||||
&state.immediate_level_geoms,
|
||||
state.simulation_frame,
|
||||
id,
|
||||
)
|
||||
|
||||
if ok {
|
||||
update_level_geom_from_config(sim_state, get_level_geom(sim_state, h^), config)
|
||||
} else {
|
||||
h^ = add_level_geom(sim_state, config)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
prune_immediate :: proc(scene: ^Scene) {
|
||||
tracy.Zone()
|
||||
|
||||
@ -181,6 +204,10 @@ prune_immediate :: proc(scene: ^Scene) {
|
||||
state.simulation_frame,
|
||||
)
|
||||
removed_engines := immediate_container_prune(&state.immediate_engines, state.simulation_frame)
|
||||
removed_level_geoms := immediate_container_prune(
|
||||
&state.immediate_level_geoms,
|
||||
state.simulation_frame,
|
||||
)
|
||||
for handle in removed_bodies {
|
||||
remove_body(sim_state, handle)
|
||||
}
|
||||
@ -190,4 +217,7 @@ prune_immediate :: proc(scene: ^Scene) {
|
||||
for handle in removed_engines {
|
||||
remove_engine(sim_state, handle)
|
||||
}
|
||||
for handle in removed_level_geoms {
|
||||
remove_level_geom(sim_state, handle)
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,28 @@ AABB :: struct {
|
||||
extent: Vec3,
|
||||
}
|
||||
|
||||
Contact_Pair :: [2]i32
|
||||
Contact_Pair_Bodies :: struct {
|
||||
a, b: Body_Handle,
|
||||
}
|
||||
Contact_Pair_Body_Level :: struct {
|
||||
a: Body_Handle,
|
||||
tri_idx: i32,
|
||||
}
|
||||
|
||||
make_contact_pair :: proc(body_a: i32, body_b: i32) -> Contact_Pair {
|
||||
return {min(body_a, body_b), max(body_a, body_b)}
|
||||
Contact_Pair :: union {
|
||||
Contact_Pair_Bodies,
|
||||
Contact_Pair_Body_Level,
|
||||
}
|
||||
|
||||
make_contact_pair_bodies :: proc(body_a: i32, body_b: i32) -> Contact_Pair_Bodies {
|
||||
return {
|
||||
index_to_body_handle(int(min(body_a, body_b))),
|
||||
index_to_body_handle(int(max(body_a, body_b))),
|
||||
}
|
||||
}
|
||||
|
||||
make_contact_pair_body_level :: proc(body: i32, tri_idx: i32) -> Contact_Pair_Body_Level {
|
||||
return {index_to_body_handle(int(body)), tri_idx}
|
||||
}
|
||||
|
||||
Contact_Container :: struct {
|
||||
@ -46,9 +64,11 @@ Sim_State :: struct {
|
||||
bodies: #soa[dynamic]Body,
|
||||
suspension_constraints: #soa[dynamic]Suspension_Constraint,
|
||||
engines: [dynamic]Engine,
|
||||
level_geoms: [dynamic]Level_Geom,
|
||||
// Number of alive bodies
|
||||
num_bodies: i32,
|
||||
num_engines: i32,
|
||||
num_level_geoms: i32,
|
||||
|
||||
// Slices. When you call get_body or get_suspension_constraint you will get a pointer to an element in this slice
|
||||
bodies_slice: #soa[]Body,
|
||||
@ -56,6 +76,7 @@ Sim_State :: struct {
|
||||
first_free_body_plus_one: i32,
|
||||
first_free_suspension_constraint_plus_one: i32,
|
||||
first_free_engine_plus_one: i32,
|
||||
first_free_level_geom_plus_one: i32,
|
||||
|
||||
// Persistent stuff for simulation
|
||||
contact_container: Contact_Container,
|
||||
@ -65,6 +86,10 @@ Sim_State :: struct {
|
||||
// Engine array data
|
||||
rpm_torque_curves_pool: spanpool.Span_Pool([2]f32),
|
||||
gear_ratios_pool: spanpool.Span_Pool(f32),
|
||||
|
||||
// Level geometry
|
||||
geometry_vertices_pool: spanpool.Span_Pool(Vec3),
|
||||
geometry_indices_pool: spanpool.Span_Pool(u16),
|
||||
}
|
||||
|
||||
Scene :: struct {
|
||||
@ -86,6 +111,7 @@ copy_sim_state :: proc(dst: ^Sim_State, src: ^Sim_State) {
|
||||
resize(&dst.bodies, len(src.bodies))
|
||||
resize(&dst.suspension_constraints, len(src.suspension_constraints))
|
||||
resize(&dst.engines, len(src.engines))
|
||||
resize(&dst.level_geoms, len(src.level_geoms))
|
||||
|
||||
dst.bodies_slice = dst.bodies[:]
|
||||
dst.suspension_constraints_slice = dst.suspension_constraints[:]
|
||||
@ -97,11 +123,14 @@ copy_sim_state :: proc(dst: ^Sim_State, src: ^Sim_State) {
|
||||
dst.suspension_constraints[i] = src.suspension_constraints[i]
|
||||
}
|
||||
copy(dst.engines[:], src.engines[:])
|
||||
copy(dst.level_geoms[:], src.level_geoms[:])
|
||||
|
||||
contact_container_copy(&dst.contact_container, src.contact_container)
|
||||
convex_container_copy(&dst.convex_container, src.convex_container)
|
||||
spanpool.copy(&dst.rpm_torque_curves_pool, src.rpm_torque_curves_pool)
|
||||
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)
|
||||
}
|
||||
|
||||
copy_physics_scene :: proc(dst, src: ^Scene) {
|
||||
@ -299,14 +328,30 @@ Engine :: struct {
|
||||
next_plus_one: i32,
|
||||
}
|
||||
|
||||
Geometry_Handle :: struct {
|
||||
vertices: spanpool.Handle,
|
||||
indices: spanpool.Handle,
|
||||
}
|
||||
|
||||
Level_Geom :: struct {
|
||||
alive: bool,
|
||||
geometry: Geometry_Handle,
|
||||
x: Vec3,
|
||||
q: Quat,
|
||||
next_plus_one: i32,
|
||||
}
|
||||
|
||||
// Index plus one, so handle 0 maps to invalid body
|
||||
Body_Handle :: distinct i32
|
||||
Suspension_Constraint_Handle :: distinct i32
|
||||
Engine_Handle :: distinct i32
|
||||
// Handle for static geometry
|
||||
Level_Geom_Handle :: distinct i32
|
||||
|
||||
INVALID_BODY :: Body_Handle(0)
|
||||
INVALID_SUSPENSION_CONSTRAINT :: Suspension_Constraint_Handle(0)
|
||||
INVALID_ENGINE :: Engine_Handle(0)
|
||||
INVALID_LEVEL_GEOM :: Level_Geom_Handle(0)
|
||||
|
||||
is_body_handle_valid :: proc(handle: Body_Handle) -> bool {
|
||||
return i32(handle) > 0
|
||||
@ -317,27 +362,41 @@ is_suspension_constraint_handle_valid :: proc(handle: Suspension_Constraint_Hand
|
||||
is_engine_handle_valid :: proc(handle: Engine_Handle) -> bool {
|
||||
return i32(handle) > 0
|
||||
}
|
||||
is_level_geom_handle_valid :: proc(handle: Level_Geom_Handle) -> bool {
|
||||
return i32(handle) > 0
|
||||
}
|
||||
is_handle_valid :: proc {
|
||||
is_body_handle_valid,
|
||||
is_suspension_constraint_handle_valid,
|
||||
is_engine_handle_valid,
|
||||
is_level_geom_handle_valid,
|
||||
}
|
||||
|
||||
index_to_body_handle :: proc(idx: int) -> Body_Handle {
|
||||
return Body_Handle(idx + 1)
|
||||
}
|
||||
|
||||
index_to_level_geom :: proc(idx: int) -> Level_Geom_Handle {
|
||||
return Level_Geom_Handle(idx + 1)
|
||||
}
|
||||
|
||||
body_handle_to_index :: proc(handle: Body_Handle) -> int {
|
||||
return int(handle) - 1
|
||||
}
|
||||
|
||||
level_geom_handle_to_index :: proc(handle: Level_Geom_Handle) -> int {
|
||||
return int(handle) - 1
|
||||
}
|
||||
|
||||
handle_to_index :: proc {
|
||||
body_handle_to_index,
|
||||
level_geom_handle_to_index,
|
||||
}
|
||||
|
||||
Body_Ptr :: #soa^#soa[]Body
|
||||
Suspension_Constraint_Ptr :: #soa^#soa[]Suspension_Constraint
|
||||
Engine_Ptr :: ^Engine
|
||||
Level_Geom_Ptr :: ^Level_Geom
|
||||
|
||||
_invalid_body: #soa[1]Body
|
||||
_invalid_body_slice := _invalid_body[:]
|
||||
@ -372,6 +431,11 @@ flip_sim_state :: proc(scene: ^Scene) {
|
||||
get_body :: proc(sim_state: ^Sim_State, handle: Body_Handle) -> Body_Ptr {
|
||||
index := int(handle) - 1
|
||||
if index < 0 || index >= len(sim_state.bodies_slice) {
|
||||
_invalid_body_slice[0] = {
|
||||
alive = true,
|
||||
q = lg.QUATERNIONF32_IDENTITY,
|
||||
prev_q = lg.QUATERNIONF32_IDENTITY,
|
||||
}
|
||||
return &_invalid_body_slice[0]
|
||||
}
|
||||
|
||||
@ -438,6 +502,13 @@ Engine_Config :: struct {
|
||||
axle: Drive_Axle_Config,
|
||||
}
|
||||
|
||||
Level_Geom_Config :: struct {
|
||||
position: Vec3,
|
||||
rotation: Quat,
|
||||
vertices: []Vec3,
|
||||
indices: []u16,
|
||||
}
|
||||
|
||||
calculate_body_params_from_config :: proc(
|
||||
config: Body_Config,
|
||||
) -> (
|
||||
@ -701,6 +772,79 @@ remove_engine :: proc(sim_state: ^Sim_State, handle: Engine_Handle) {
|
||||
}
|
||||
}
|
||||
|
||||
invalid_level_geom: Level_Geom
|
||||
|
||||
get_level_geom :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) -> Level_Geom_Ptr {
|
||||
index := int(handle) - 1
|
||||
if index < 0 || index >= len(sim_state.bodies_slice) {
|
||||
return &invalid_level_geom
|
||||
}
|
||||
|
||||
return &sim_state.level_geoms[index]
|
||||
}
|
||||
|
||||
get_level_geom_data :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
handle: Geometry_Handle,
|
||||
) -> (
|
||||
vertices: []Vec3,
|
||||
indices: []u16,
|
||||
) {
|
||||
vertices = spanpool.resolve_slice(&sim_state.geometry_vertices_pool, handle.vertices)
|
||||
indices = spanpool.resolve_slice(&sim_state.geometry_indices_pool, handle.indices)
|
||||
return
|
||||
}
|
||||
|
||||
update_level_geom_from_config :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
level_geom: Level_Geom_Ptr,
|
||||
config: Level_Geom_Config,
|
||||
) {
|
||||
level_geom.x = config.position
|
||||
level_geom.q = config.rotation
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
level_geom.geometry.vertices = spanpool.allocate_elems(
|
||||
&sim_state.geometry_vertices_pool,
|
||||
..config.vertices,
|
||||
)
|
||||
level_geom.geometry.indices = spanpool.allocate_elems(
|
||||
&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
|
||||
new_level_geom := get_level_geom(sim_state, Level_Geom_Handle(index))
|
||||
next_plus_one := new_level_geom.next_plus_one
|
||||
new_level_geom^ = level_geom
|
||||
sim_state.first_free_level_geom_plus_one = next_plus_one
|
||||
return Level_Geom_Handle(index)
|
||||
}
|
||||
|
||||
append(&sim_state.level_geoms, level_geom)
|
||||
index := len(sim_state.level_geoms)
|
||||
return Level_Geom_Handle(index)
|
||||
}
|
||||
|
||||
remove_level_geom :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) {
|
||||
level_geom := get_level_geom(sim_state, handle)
|
||||
|
||||
spanpool.free(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices)
|
||||
spanpool.free(&sim_state.geometry_indices_pool, level_geom.geometry.indices)
|
||||
}
|
||||
|
||||
_get_first_free_body :: proc(sim_state: ^Sim_State) -> i32 {
|
||||
return sim_state.first_free_body_plus_one - 1
|
||||
@ -709,12 +853,16 @@ _get_first_free_body :: proc(sim_state: ^Sim_State) -> i32 {
|
||||
destry_sim_state :: proc(sim_state: ^Sim_State) {
|
||||
delete_soa(sim_state.bodies)
|
||||
delete_soa(sim_state.suspension_constraints)
|
||||
delete(sim_state.engines)
|
||||
delete(sim_state.level_geoms)
|
||||
|
||||
delete_soa(sim_state.contact_container.contacts)
|
||||
delete_map(sim_state.contact_container.lookup)
|
||||
delete(sim_state.engines)
|
||||
convex_container_destroy(&sim_state.convex_container)
|
||||
spanpool.destroy_spanpool(&sim_state.rpm_torque_curves_pool)
|
||||
spanpool.destroy_spanpool(&sim_state.gear_ratios_pool)
|
||||
spanpool.destroy_spanpool(&sim_state.geometry_vertices_pool)
|
||||
spanpool.destroy_spanpool(&sim_state.geometry_indices_pool)
|
||||
}
|
||||
|
||||
destroy_physics_scene :: proc(scene: ^Scene) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package physics
|
||||
|
||||
import rl " libs:raylib"
|
||||
import "base:runtime"
|
||||
import "bvh"
|
||||
import "collision"
|
||||
@ -38,6 +39,7 @@ Solver_State :: struct {
|
||||
immediate_bodies: Immediate_Container(Body_Handle),
|
||||
immediate_suspension_constraints: Immediate_Container(Suspension_Constraint_Handle),
|
||||
immediate_engines: Immediate_Container(Engine_Handle),
|
||||
immediate_level_geoms: Immediate_Container(Level_Geom_Handle),
|
||||
}
|
||||
|
||||
copy_solver_state :: proc(dst, src: ^Solver_State) {
|
||||
@ -50,12 +52,14 @@ copy_solver_state :: proc(dst, src: ^Solver_State) {
|
||||
&src.immediate_suspension_constraints,
|
||||
)
|
||||
immediate_container_copy(&dst.immediate_engines, &src.immediate_engines)
|
||||
immediate_container_copy(&dst.immediate_level_geoms, &src.immediate_level_geoms)
|
||||
}
|
||||
|
||||
destroy_solver_state :: proc(state: ^Solver_State) {
|
||||
immediate_container_destroy(&state.immediate_bodies)
|
||||
immediate_container_destroy(&state.immediate_suspension_constraints)
|
||||
immediate_container_destroy(&state.immediate_engines)
|
||||
immediate_container_destroy(&state.immediate_level_geoms)
|
||||
}
|
||||
|
||||
MAX_STEPS :: 10
|
||||
@ -65,15 +69,85 @@ Step_Mode :: enum {
|
||||
Single,
|
||||
}
|
||||
|
||||
Static_TLAS :: struct {
|
||||
bvh_tree: bvh.Mesh_BVH,
|
||||
tri_to_level_geom: []Level_Geom_Handle,
|
||||
}
|
||||
|
||||
// Top Level Acceleration Structure
|
||||
TLAS :: struct {
|
||||
Dynamic_TLAS :: struct {
|
||||
bvh_tree: bvh.BVH,
|
||||
body_aabbs: []bvh.AABB,
|
||||
}
|
||||
|
||||
build_static_tlas :: proc(sim_state: ^Sim_State) -> 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]
|
||||
|
||||
if level_geom.alive {
|
||||
num_vertices += int(level_geom.geometry.vertices.len)
|
||||
num_indices += int(level_geom.geometry.indices.len)
|
||||
}
|
||||
}
|
||||
|
||||
if num_vertices == 0 {
|
||||
return Static_TLAS{}
|
||||
}
|
||||
|
||||
vertices := make([]bvh.Vec3, num_vertices, context.temp_allocator)
|
||||
indices := make([]u16, num_indices, context.temp_allocator)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(num_vertices == len(vertices))
|
||||
assert(num_indices == len(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,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO: free intermediate temp allocs
|
||||
// Creates TLAS using temp allocator
|
||||
build_tlas :: proc(sim_state: ^Sim_State, config: Solver_Config) -> TLAS {
|
||||
build_dynamic_tlas :: proc(sim_state: ^Sim_State, config: Solver_Config) -> Dynamic_TLAS {
|
||||
tracy.Zone()
|
||||
|
||||
body_aabbs := make([]bvh.AABB, sim_state.num_bodies, context.temp_allocator)
|
||||
@ -92,7 +166,7 @@ build_tlas :: proc(sim_state: ^Sim_State, config: Solver_Config) -> TLAS {
|
||||
phys_aabb := body_get_aabb(body)
|
||||
|
||||
EXPAND_K :: 2
|
||||
expand := lg.abs(EXPAND_K * config.timestep * body.v)
|
||||
expand := lg.abs(EXPAND_K * config.timestep * body.v) + 0.1
|
||||
phys_aabb.extent += expand * 0.5
|
||||
|
||||
aabb.min = phys_aabb.center - phys_aabb.extent
|
||||
@ -103,12 +177,12 @@ build_tlas :: proc(sim_state: ^Sim_State, config: Solver_Config) -> TLAS {
|
||||
|
||||
sim_state_bvh := bvh.build_bvh_from_aabbs(body_aabbs, body_indices, context.temp_allocator)
|
||||
|
||||
return TLAS{bvh_tree = sim_state_bvh, body_aabbs = body_aabbs}
|
||||
return Dynamic_TLAS{bvh_tree = sim_state_bvh, body_aabbs = body_aabbs}
|
||||
}
|
||||
|
||||
raycast_bodies :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
tlas: ^TLAS,
|
||||
tlas: ^Dynamic_TLAS,
|
||||
origin, dir: Vec3,
|
||||
distance := max(f32),
|
||||
) -> (
|
||||
@ -152,8 +226,115 @@ raycast_bodies :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
get_triangle :: proc(vertices: []Vec3, indices: []u16, tri_idx: int) -> (tri: [3]Vec3) {
|
||||
i1, i2, i3 := indices[tri_idx * 3 + 0], indices[tri_idx * 3 + 1], indices[tri_idx * 3 + 2]
|
||||
tri[0], tri[1], tri[2] = vertices[i1], vertices[i2], vertices[i3]
|
||||
return
|
||||
}
|
||||
|
||||
get_transformed_triangle :: proc(
|
||||
vertices: []Vec3,
|
||||
indices: []u16,
|
||||
tri_idx: int,
|
||||
position: Vec3,
|
||||
rotation: Quat,
|
||||
) -> (
|
||||
tri: [3]Vec3,
|
||||
) {
|
||||
tri = get_triangle(vertices, indices, tri_idx)
|
||||
|
||||
tri[0] = lg.quaternion_mul_vector3(rotation, tri[0]) + position
|
||||
tri[1] = lg.quaternion_mul_vector3(rotation, tri[1]) + position
|
||||
tri[2] = lg.quaternion_mul_vector3(rotation, tri[2]) + position
|
||||
return
|
||||
}
|
||||
|
||||
get_triangle_aabb :: proc(tri: [3]Vec3) -> (aabb: bvh.AABB) {
|
||||
aabb.min = lg.min(tri[0], lg.min(tri[1], tri[2]))
|
||||
aabb.max = lg.max(tri[0], lg.max(tri[1], tri[2]))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
remove_invalid_contacts :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
static_tlas: Static_TLAS,
|
||||
dyn_tlas: Dynamic_TLAS,
|
||||
) {
|
||||
tracy.Zone()
|
||||
|
||||
i := 0
|
||||
for i < len(sim_state.contact_container.contacts) {
|
||||
contact := sim_state.contact_container.contacts[i]
|
||||
|
||||
should_remove := false
|
||||
|
||||
if contact.type == .Body_vs_Body {
|
||||
should_remove |= !get_body(sim_state, contact.a).alive
|
||||
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]
|
||||
|
||||
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b)
|
||||
}
|
||||
} else {
|
||||
// a is a body, b is a triangle index
|
||||
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)
|
||||
|
||||
if !should_remove {
|
||||
aabb_a := dyn_tlas.body_aabbs[int(contact.a) - 1]
|
||||
tri := get_triangle(
|
||||
static_tlas.bvh_tree.mesh.vertices,
|
||||
static_tlas.bvh_tree.mesh.indices,
|
||||
int(contact.tri_idx),
|
||||
)
|
||||
aabb_b := get_triangle_aabb(tri)
|
||||
|
||||
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pair_from_contact :: proc(contact: Contact) -> (pair: Contact_Pair) {
|
||||
if contact.type == .Body_vs_Body {
|
||||
pair = make_contact_pair_bodies(i32(contact.a) - 1, i32(contact.b) - 1)
|
||||
} else {
|
||||
pair = make_contact_pair_body_level(
|
||||
i32(body_handle_to_index(contact.a)),
|
||||
contact.tri_idx,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if should_remove {
|
||||
removed_pair := pair_from_contact(contact)
|
||||
delete_key(&sim_state.contact_container.lookup, removed_pair)
|
||||
|
||||
unordered_remove_soa(&sim_state.contact_container.contacts, i)
|
||||
|
||||
if i < len(sim_state.contact_container.contacts) {
|
||||
moved_contact := &sim_state.contact_container.contacts[i]
|
||||
moved_pair := pair_from_contact(moved_contact^)
|
||||
sim_state.contact_container.lookup[moved_pair] = i32(i)
|
||||
}
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: free intermediate temp allocs
|
||||
find_new_contacts :: proc(sim_state: ^Sim_State, tlas: ^TLAS) {
|
||||
find_new_contacts :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
static_tlas: ^Static_TLAS,
|
||||
dyn_tlas: ^Dynamic_TLAS,
|
||||
) {
|
||||
tracy.Zone()
|
||||
|
||||
for i in 0 ..< len(sim_state.bodies_slice) {
|
||||
@ -162,15 +343,17 @@ find_new_contacts :: proc(sim_state: ^Sim_State, tlas: ^TLAS) {
|
||||
body := &sim_state.bodies_slice[i]
|
||||
|
||||
if body.alive {
|
||||
body_aabb := tlas.body_aabbs[i]
|
||||
it := bvh.iterator_intersect_leaf_aabb(&tlas.bvh_tree, body_aabb)
|
||||
body_aabb := dyn_tlas.body_aabbs[i]
|
||||
{
|
||||
it := bvh.iterator_intersect_leaf_aabb(&dyn_tlas.bvh_tree, body_aabb)
|
||||
|
||||
for leaf_node in bvh.iterator_intersect_leaf_next(&it) {
|
||||
for j in 0 ..< leaf_node.prim_len {
|
||||
other_body_idx := tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]
|
||||
prim_aabb := tlas.body_aabbs[other_body_idx]
|
||||
other_body_idx :=
|
||||
dyn_tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]
|
||||
prim_aabb := dyn_tlas.body_aabbs[other_body_idx]
|
||||
|
||||
pair := make_contact_pair(i32(body_idx), i32(other_body_idx))
|
||||
pair := make_contact_pair_bodies(i32(body_idx), i32(other_body_idx))
|
||||
if body_idx != other_body_idx &&
|
||||
(bvh.test_aabb_vs_aabb(body_aabb, prim_aabb)) &&
|
||||
!(pair in sim_state.contact_container.lookup) {
|
||||
@ -180,8 +363,9 @@ find_new_contacts :: proc(sim_state: ^Sim_State, tlas: ^TLAS) {
|
||||
contact := &sim_state.contact_container.contacts[new_contact_idx]
|
||||
|
||||
contact^ = Contact {
|
||||
type = .Body_vs_Body,
|
||||
a = Body_Handle(i + 1),
|
||||
b = Body_Handle(other_body_idx + 1),
|
||||
b = i32(other_body_idx + 1),
|
||||
}
|
||||
|
||||
sim_state.contact_container.lookup[pair] = i32(new_contact_idx)
|
||||
@ -189,6 +373,42 @@ find_new_contacts :: proc(sim_state: ^Sim_State, tlas: ^TLAS) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
it := bvh.iterator_intersect_leaf_aabb(&static_tlas.bvh_tree.bvh, body_aabb)
|
||||
|
||||
for leaf_node in bvh.iterator_intersect_leaf_next(&it) {
|
||||
for j in 0 ..< leaf_node.prim_len {
|
||||
tri_idx :=
|
||||
static_tlas.bvh_tree.bvh.primitives[leaf_node.child_or_prim_start + j]
|
||||
tri := get_triangle(
|
||||
static_tlas.bvh_tree.mesh.vertices,
|
||||
static_tlas.bvh_tree.mesh.indices,
|
||||
int(tri_idx),
|
||||
)
|
||||
prim_aabb := get_triangle_aabb(tri)
|
||||
level_geom_handle := static_tlas.tri_to_level_geom[tri_idx]
|
||||
|
||||
pair := make_contact_pair_body_level(i32(body_idx), i32(tri_idx))
|
||||
aabb_overlaps := bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) || true
|
||||
if aabb_overlaps && !(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]
|
||||
|
||||
contact^ = Contact {
|
||||
type = .Body_vs_Level,
|
||||
a = index_to_body_handle(i),
|
||||
b = i32(level_geom_handle),
|
||||
tri_idx = i32(tri_idx),
|
||||
}
|
||||
|
||||
sim_state.contact_container.lookup[pair] = i32(new_contact_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,6 +457,7 @@ simulate :: proc(
|
||||
state.immediate_bodies.num_items = 0
|
||||
state.immediate_suspension_constraints.num_items = 0
|
||||
state.immediate_engines.num_items = 0
|
||||
state.immediate_level_geoms.num_items = 0
|
||||
}
|
||||
|
||||
GLOBAL_PLANE :: collision.Plane {
|
||||
@ -244,8 +465,17 @@ GLOBAL_PLANE :: collision.Plane {
|
||||
dist = 0,
|
||||
}
|
||||
|
||||
Contact_Type :: enum {
|
||||
Body_vs_Body,
|
||||
Body_vs_Level,
|
||||
}
|
||||
|
||||
Contact :: struct {
|
||||
a, b: Body_Handle,
|
||||
type: Contact_Type,
|
||||
a: Body_Handle,
|
||||
// Body_vs_Body - Body_Handle, Body_vs_Level - Level_Geom_Handle
|
||||
b: i32,
|
||||
tri_idx: i32,
|
||||
prev_x_a, prev_x_b: Vec3,
|
||||
prev_q_a, prev_q_b: Quat,
|
||||
manifold: collision.Contact_Manifold,
|
||||
@ -263,7 +493,7 @@ Contact :: struct {
|
||||
applied_normal_correction: [4]f32,
|
||||
}
|
||||
|
||||
update_contacts :: proc(sim_state: ^Sim_State) {
|
||||
update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
|
||||
tracy.Zone()
|
||||
|
||||
graph_color_bitmask: [4]bit_array.Bit_Array
|
||||
@ -279,12 +509,10 @@ update_contacts :: proc(sim_state: ^Sim_State) {
|
||||
for contact_idx in 0 ..< len(sim_state.contact_container.contacts) {
|
||||
contact := &sim_state.contact_container.contacts[contact_idx]
|
||||
|
||||
i, j := i32(contact.a) - 1, i32(contact.b) - 1
|
||||
|
||||
body, body2 := &sim_state.bodies_slice[i], &sim_state.bodies_slice[j]
|
||||
b_handle := Body_Handle(contact.b) if contact.type == .Body_vs_Body else INVALID_BODY
|
||||
body, body2 := get_body(sim_state, contact.a), get_body(sim_state, b_handle)
|
||||
|
||||
assert(body.alive)
|
||||
assert(body2.alive)
|
||||
|
||||
old_manifold := contact.manifold
|
||||
old_total_normal_impulse := contact.total_normal_impulse
|
||||
@ -302,19 +530,35 @@ update_contacts :: proc(sim_state: ^Sim_State) {
|
||||
contact.applied_static_friction = false
|
||||
contact.applied_normal_correction = 0
|
||||
|
||||
aabb1, aabb2 := body_get_aabb(body), body_get_aabb(body2)
|
||||
// aabb1, aabb2 := body_get_aabb(body), body_get_aabb(body2)
|
||||
|
||||
// TODO: extract common math functions into a sane place
|
||||
if !collision.test_aabb_vs_aabb(
|
||||
{min = aabb1.center - aabb1.extent, max = aabb1.center + aabb1.extent},
|
||||
{min = aabb2.center - aabb2.extent, max = aabb2.center + aabb2.extent},
|
||||
) {
|
||||
continue
|
||||
}
|
||||
// if !collision.test_aabb_vs_aabb(
|
||||
// {min = aabb1.center - aabb1.extent, max = aabb1.center + aabb1.extent},
|
||||
// {min = aabb2.center - aabb2.extent, max = aabb2.center + aabb2.extent},
|
||||
// ) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
m1, m2 :=
|
||||
body_get_convex_shape_world(sim_state, body),
|
||||
body_get_convex_shape_world(sim_state, body2)
|
||||
m1 := body_get_convex_shape_world(sim_state, body)
|
||||
m2: collision.Convex
|
||||
|
||||
switch contact.type {
|
||||
case .Body_vs_Body:
|
||||
m2 = body_get_convex_shape_world(sim_state, body2)
|
||||
case .Body_vs_Level:
|
||||
// level_geom := get_level_geom(sim_state, Level_Geom_Handle(contact.b))
|
||||
tri := get_triangle(
|
||||
static_tlas.bvh_tree.mesh.vertices,
|
||||
static_tlas.bvh_tree.mesh.indices,
|
||||
int(contact.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
|
||||
raw_manifold, collision := collision.convex_vs_convex_sat(m1, m2)
|
||||
@ -384,7 +628,7 @@ pgs_solve_contacts :: proc(
|
||||
inv_dt: f32,
|
||||
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(16, 1, dt)
|
||||
if !apply_bias {
|
||||
mass_coef = 1
|
||||
bias_rate = 0
|
||||
@ -397,17 +641,18 @@ pgs_solve_contacts :: proc(
|
||||
random_order[i] = i32(i)
|
||||
}
|
||||
|
||||
// for i in 0 ..< len(random_order) - 1 {
|
||||
// j := rand.int_max(len(random_order))
|
||||
// slice.swap(random_order, i, j)
|
||||
// }
|
||||
for i in 0 ..< len(random_order) - 1 {
|
||||
j := rand.int_max(len(random_order))
|
||||
slice.swap(random_order, i, j)
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0 ..< len(sim_state.contact_container.contacts) {
|
||||
contact := &sim_state.contact_container.contacts[random_order[i]]
|
||||
manifold := &contact.manifold
|
||||
|
||||
body1, body2 := get_body(sim_state, contact.a), get_body(sim_state, contact.b)
|
||||
b_handle := Body_Handle(contact.b) if contact.type == .Body_vs_Body else INVALID_BODY
|
||||
body1, body2 := get_body(sim_state, contact.a), get_body(sim_state, b_handle)
|
||||
|
||||
for point_idx in 0 ..< manifold.points_len {
|
||||
p1, p2 :=
|
||||
@ -710,7 +955,7 @@ calculate_ground_vel :: proc(
|
||||
|
||||
pgs_solve_suspension :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
tlas: ^TLAS,
|
||||
tlas: ^Dynamic_TLAS,
|
||||
config: Solver_Config,
|
||||
dt: f32,
|
||||
inv_dt: f32,
|
||||
@ -914,7 +1159,7 @@ pgs_solve_suspension :: proc(
|
||||
|
||||
pgs_substep :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
tlas: ^TLAS,
|
||||
tlas: ^Dynamic_TLAS,
|
||||
config: Solver_Config,
|
||||
dt: f32,
|
||||
inv_dt: f32,
|
||||
@ -933,7 +1178,8 @@ pgs_substep :: proc(
|
||||
contact := &sim_state.contact_container.contacts[i]
|
||||
manifold := &contact.manifold
|
||||
|
||||
body1, body2 := get_body(sim_state, contact.a), get_body(sim_state, contact.b)
|
||||
body1, body2 :=
|
||||
get_body(sim_state, contact.a), get_body(sim_state, Body_Handle(contact.b))
|
||||
|
||||
for point_idx in 0 ..< manifold.points_len {
|
||||
p1, p2 :=
|
||||
@ -1078,49 +1324,12 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi
|
||||
dt := f32(dt_64)
|
||||
inv_dt := f32(inv_dt_64)
|
||||
|
||||
tlas := build_tlas(sim_state, config)
|
||||
static_tlas := build_static_tlas(sim_state)
|
||||
dyn_tlas := build_dynamic_tlas(sim_state, config)
|
||||
|
||||
{
|
||||
tracy.ZoneN("simulate_step::remove_invalid_contacts")
|
||||
i := 0
|
||||
for i < len(sim_state.contact_container.contacts) {
|
||||
contact := sim_state.contact_container.contacts[i]
|
||||
|
||||
should_remove := false
|
||||
|
||||
should_remove |= !get_body(sim_state, contact.a).alive
|
||||
should_remove |= !get_body(sim_state, contact.b).alive
|
||||
|
||||
if !should_remove {
|
||||
aabb_a := tlas.body_aabbs[int(contact.a) - 1]
|
||||
aabb_b := tlas.body_aabbs[int(contact.b) - 1]
|
||||
|
||||
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b)
|
||||
}
|
||||
|
||||
if should_remove {
|
||||
removed_pair := make_contact_pair(i32(contact.a) - 1, i32(contact.b) - 1)
|
||||
delete_key(&sim_state.contact_container.lookup, removed_pair)
|
||||
|
||||
unordered_remove_soa(&sim_state.contact_container.contacts, i)
|
||||
|
||||
if i < len(sim_state.contact_container.contacts) {
|
||||
moved_contact := &sim_state.contact_container.contacts[i]
|
||||
moved_pair := make_contact_pair(
|
||||
i32(moved_contact.a) - 1,
|
||||
i32(moved_contact.b) - 1,
|
||||
)
|
||||
sim_state.contact_container.lookup[moved_pair] = i32(i)
|
||||
}
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
find_new_contacts(sim_state, &tlas)
|
||||
|
||||
update_contacts(sim_state)
|
||||
remove_invalid_contacts(sim_state, static_tlas, dyn_tlas)
|
||||
find_new_contacts(sim_state, &static_tlas, &dyn_tlas)
|
||||
update_contacts(sim_state, &static_tlas)
|
||||
|
||||
Solver :: enum {
|
||||
XPBD,
|
||||
@ -1136,7 +1345,7 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi
|
||||
}
|
||||
case .PGS:
|
||||
for _ in 0 ..< substeps {
|
||||
pgs_substep(sim_state, &tlas, config, dt, inv_dt)
|
||||
pgs_substep(sim_state, &dyn_tlas, config, dt, inv_dt)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,9 @@ xpbd_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_
|
||||
tracy.ZoneN("simulate_step::solve_collisions")
|
||||
for i in 0 ..< len(sim_state.contact_container.contacts) {
|
||||
contact := &sim_state.contact_container.contacts[i]
|
||||
body, body2 := get_body(sim_state, contact.a), get_body(sim_state, contact.b)
|
||||
|
||||
body, body2 :=
|
||||
get_body(sim_state, contact.a), get_body(sim_state, Body_Handle(contact.b))
|
||||
|
||||
contact^ = Contact {
|
||||
a = contact.a,
|
||||
@ -137,7 +139,8 @@ xpbd_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_
|
||||
|
||||
for &contact in sim_state.contact_container.contacts {
|
||||
manifold := contact.manifold
|
||||
body1, body2 := get_body(sim_state, contact.a), get_body(sim_state, contact.b)
|
||||
body1, body2 :=
|
||||
get_body(sim_state, contact.a), get_body(sim_state, Body_Handle(contact.b))
|
||||
|
||||
prev_p1, prev_p2: Vec3
|
||||
p1, p2: Vec3
|
||||
@ -227,7 +230,8 @@ xpbd_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_
|
||||
for &contact in sim_state.contact_container.contacts {
|
||||
manifold := &contact.manifold
|
||||
|
||||
body, body2 := get_body(sim_state, contact.a), get_body(sim_state, contact.b)
|
||||
body, body2 :=
|
||||
get_body(sim_state, contact.a), get_body(sim_state, Body_Handle(contact.b))
|
||||
prev_q1, prev_q2 := body.prev_q, body2.prev_q
|
||||
|
||||
for point_idx in 0 ..< manifold.points_len {
|
||||
@ -285,7 +289,7 @@ xpbd_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_
|
||||
|
||||
manifold := &contact.manifold
|
||||
body1 := get_body(sim_state, contact.a)
|
||||
body2 := get_body(sim_state, contact.b)
|
||||
body2 := get_body(sim_state, Body_Handle(contact.b))
|
||||
|
||||
friction_p1, friction_p2: Vec3
|
||||
total_lambda_normal := f32(0)
|
||||
|
145
game/render/debug.odin
Normal file
145
game/render/debug.odin
Normal file
@ -0,0 +1,145 @@
|
||||
package render
|
||||
|
||||
import rl "libs:raylib"
|
||||
import gl "vendor:OpenGL"
|
||||
|
||||
MAX_VERTICES :: 4096
|
||||
MAX_INDICES :: 4096
|
||||
MAX_DRAWS :: 4096
|
||||
|
||||
Vec3 :: [3]f32
|
||||
|
||||
Draw_Call :: struct {
|
||||
color: rl.Color,
|
||||
first_index, num_indices: i32,
|
||||
base_vertex: i32,
|
||||
}
|
||||
|
||||
Immediate_Buffer :: enum {
|
||||
Vertices = 0,
|
||||
Indices,
|
||||
}
|
||||
|
||||
Immediate_Draw_State :: struct {
|
||||
vertices: [MAX_VERTICES]Vec3,
|
||||
indices: [MAX_INDICES]u16,
|
||||
draws: [MAX_DRAWS]Draw_Call,
|
||||
num_vertices, num_indices, num_draws: int,
|
||||
vbo_ids: [Immediate_Buffer]u32,
|
||||
vao_id: u32,
|
||||
}
|
||||
|
||||
push_mesh :: proc(ds: ^Immediate_Draw_State, vertices: []Vec3, indices: []u16, color: rl.Color) {
|
||||
copy(ds.vertices[ds.num_vertices:][:len(vertices)], vertices)
|
||||
copy(ds.indices[ds.num_indices:][:len(indices)], indices)
|
||||
ds.draws[ds.num_draws] = {
|
||||
color = color,
|
||||
first_index = i32(ds.num_indices),
|
||||
num_indices = i32(len(indices)),
|
||||
base_vertex = i32(ds.num_vertices),
|
||||
}
|
||||
ds.num_vertices += len(vertices)
|
||||
ds.num_indices += len(indices)
|
||||
ds.num_draws += 1
|
||||
}
|
||||
|
||||
// rudimentary batching
|
||||
find_or_push_cmd :: proc(ds: ^Immediate_Draw_State, color: rl.Color) -> (cmd: ^Draw_Call) {
|
||||
if ds.num_draws > 0 && ds.draws[ds.num_draws - 1].color == color {
|
||||
cmd = &ds.draws[ds.num_draws - 1]
|
||||
assert(i32(ds.num_indices) == cmd.first_index + cmd.num_indices)
|
||||
} else {
|
||||
ds.draws[ds.num_draws] = {
|
||||
color = color,
|
||||
first_index = i32(ds.num_indices),
|
||||
num_indices = 0,
|
||||
base_vertex = i32(ds.num_vertices),
|
||||
}
|
||||
cmd = &ds.draws[ds.num_draws]
|
||||
ds.num_draws += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
push_tri :: proc(ds: ^Immediate_Draw_State, v1, v2, v3: Vec3, color: rl.Color) {
|
||||
draw := find_or_push_cmd(ds, color)
|
||||
|
||||
ds.vertices[ds.num_vertices + 0] = v1
|
||||
ds.vertices[ds.num_vertices + 1] = v2
|
||||
ds.vertices[ds.num_vertices + 2] = v3
|
||||
ds.indices[ds.num_indices + 0] = u16(ds.num_vertices + 0)
|
||||
ds.indices[ds.num_indices + 1] = u16(ds.num_vertices + 1)
|
||||
ds.indices[ds.num_indices + 2] = u16(ds.num_vertices + 2)
|
||||
ds.num_vertices += 3
|
||||
ds.num_indices += 3
|
||||
draw.num_indices += 3
|
||||
}
|
||||
|
||||
push_quad :: proc(ds: ^Immediate_Draw_State, v1, v2, v3, v4: Vec3, color: rl.Color) {
|
||||
push_tri(ds, v1, v2, v3, color)
|
||||
push_tri(ds, v1, v3, v4, color)
|
||||
}
|
||||
|
||||
draw_batch :: proc(ds: ^Immediate_Draw_State) {
|
||||
if ds.vbo_ids == {} {
|
||||
gl.GenBuffers(len(ds.vbo_ids), &ds.vbo_ids[.Vertices])
|
||||
gl.GenVertexArrays(1, &ds.vao_id)
|
||||
|
||||
gl.BindVertexArray(ds.vao_id)
|
||||
gl.EnableVertexAttribArray(u32(rl.ShaderLocationIndex.VERTEX_POSITION))
|
||||
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, ds.vbo_ids[.Vertices])
|
||||
gl.VertexAttribPointer(
|
||||
u32(rl.ShaderLocationIndex.VERTEX_POSITION),
|
||||
3,
|
||||
gl.FLOAT,
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ds.vbo_ids[.Indices])
|
||||
|
||||
} else {
|
||||
gl.BindVertexArray(ds.vao_id)
|
||||
}
|
||||
gl.BindVertexArray(0)
|
||||
|
||||
defer gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
||||
|
||||
gl.BufferData(gl.ARRAY_BUFFER, MAX_VERTICES * size_of(Vec3), nil, gl.STREAM_DRAW)
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, 0, ds.num_vertices * size_of(Vec3), raw_data(ds.vertices[:]))
|
||||
gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 0, 0)
|
||||
gl.EnableVertexAttribArray(0)
|
||||
defer gl.DisableVertexAttribArray(0)
|
||||
|
||||
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ds.vbo_ids[.Indices])
|
||||
defer gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
|
||||
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, MAX_INDICES * size_of(u16), nil, gl.STREAM_DRAW)
|
||||
gl.BufferSubData(
|
||||
gl.ELEMENT_ARRAY_BUFFER,
|
||||
0,
|
||||
ds.num_indices * size_of(u16),
|
||||
raw_data(ds.indices[:]),
|
||||
)
|
||||
|
||||
shader := rl.LoadMaterialDefault().shader
|
||||
rl.BeginShaderMode(shader)
|
||||
defer rl.EndShaderMode()
|
||||
|
||||
for draw in ds.draws[:ds.num_draws] {
|
||||
color: [4]f32 = {
|
||||
f32(draw.color.r) / 255.0,
|
||||
f32(draw.color.g) / 255.0,
|
||||
f32(draw.color.b) / 255.0,
|
||||
f32(draw.color.a) / 255.0,
|
||||
}
|
||||
gl.Uniform4fv(shader.locs[rl.ShaderLocationIndex.COLOR_DIFFUSE], 1, raw_data(color[:]))
|
||||
gl.DrawElementsBaseVertex(
|
||||
gl.TRIANGLES,
|
||||
i32(ds.num_indices / 3),
|
||||
gl.UNSIGNED_SHORT,
|
||||
rawptr(uintptr(draw.first_index) * size_of(u16)),
|
||||
draw.base_vertex,
|
||||
)
|
||||
}
|
||||
}
|
@ -258,6 +258,70 @@ calculate_spline_interpolated_points :: proc(
|
||||
return nil
|
||||
}
|
||||
|
||||
spline_generate_mesh :: proc(
|
||||
interpolated_points: #soa[]Interpolated_Point,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
vertices: []rl.Vector3,
|
||||
indices: []u16,
|
||||
) {
|
||||
vertices = make([]rl.Vector3, (len(interpolated_points) - 1) * 4, allocator)
|
||||
indices = make([]u16, (len(interpolated_points) - 1) * 6, allocator)
|
||||
|
||||
for i in 0 ..< len(interpolated_points) - 1 {
|
||||
cur, next := interpolated_points[i], interpolated_points[i + 1]
|
||||
|
||||
tangent := lg.normalize0(next.pos - cur.pos)
|
||||
normal := interpolated_points[i].normal
|
||||
bitangent := lg.normalize0(lg.cross(tangent, normal))
|
||||
|
||||
next_tangent: rl.Vector3
|
||||
if i < len(interpolated_points) - 2 {
|
||||
next2 := interpolated_points[i + 2]
|
||||
next_tangent = next2.pos - next.pos
|
||||
} else {
|
||||
next_tangent = tangent
|
||||
}
|
||||
|
||||
next_normal := interpolated_points[i + 1].normal
|
||||
next_bitangent := lg.normalize0(lg.cross(next_tangent, next_normal))
|
||||
|
||||
base_vert, base_ind := i * 4, i * 6
|
||||
|
||||
u_dt := 1.0 / f32(SPLINE_SUBDIVS_U)
|
||||
for u in 0 ..< SPLINE_SUBDIVS_U {
|
||||
u_t := u_dt * f32(u)
|
||||
u_t2 := u_t + u_dt
|
||||
|
||||
// [-1, 1]
|
||||
u_t = u_t * 2 - 1
|
||||
u_t2 = u_t2 * 2 - 1
|
||||
u_t *= ROAD_WIDTH
|
||||
u_t2 *= ROAD_WIDTH
|
||||
|
||||
p1 := cur.pos + bitangent * u_t
|
||||
p2 := cur.pos + bitangent * u_t2
|
||||
p3 := next.pos + next_bitangent * u_t
|
||||
p4 := next.pos + next_bitangent * u_t2
|
||||
|
||||
vertices[base_vert + 0] = p1
|
||||
vertices[base_vert + 1] = p2
|
||||
vertices[base_vert + 2] = p3
|
||||
vertices[base_vert + 3] = p4
|
||||
|
||||
indices[base_ind + 0] = u16(base_vert + 0)
|
||||
indices[base_ind + 1] = u16(base_vert + 1)
|
||||
indices[base_ind + 2] = u16(base_vert + 2)
|
||||
|
||||
indices[base_ind + 3] = u16(base_vert + 1)
|
||||
indices[base_ind + 4] = u16(base_vert + 3)
|
||||
indices[base_ind + 5] = u16(base_vert + 2)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
debug_draw_spline :: proc(interpolated_points: #soa[]Interpolated_Point) {
|
||||
rlgl.Begin(rlgl.LINES)
|
||||
defer rlgl.End()
|
||||
|
Loading…
x
Reference in New Issue
Block a user