Optimize edge collision checking by sorting so they always follow in pairs
Fix suspension constraint force being weird
This commit is contained in:
parent
56f1eb1d15
commit
44a401344f
@ -565,8 +565,6 @@ assetman_fetch_or_load_internal :: proc(
|
|||||||
modtime: physfs.sint64,
|
modtime: physfs.sint64,
|
||||||
result: Asset_Cache_Result,
|
result: Asset_Cache_Result,
|
||||||
) {
|
) {
|
||||||
tracy.Zone()
|
|
||||||
|
|
||||||
existing, has_existing := assetman.assets[key]
|
existing, has_existing := assetman.assets[key]
|
||||||
|
|
||||||
if has_existing {
|
if has_existing {
|
||||||
@ -636,7 +634,6 @@ assetman_fetch_or_load :: proc(
|
|||||||
modtime: physfs.sint64,
|
modtime: physfs.sint64,
|
||||||
result: Asset_Cache_Result,
|
result: Asset_Cache_Result,
|
||||||
) {
|
) {
|
||||||
tracy.Zone()
|
|
||||||
value, modtime, result = assetman_fetch_or_load_internal(
|
value, modtime, result = assetman_fetch_or_load_internal(
|
||||||
assetman,
|
assetman,
|
||||||
key,
|
key,
|
||||||
|
@ -338,7 +338,6 @@ parse_convex :: proc(bytes: []byte, allocator := context.allocator) -> (Loaded_C
|
|||||||
final_edges := make([]halfedge.Half_Edge, len(edges), allocator)
|
final_edges := make([]halfedge.Half_Edge, len(edges), allocator)
|
||||||
final_faces := make([]halfedge.Face, len(faces), allocator)
|
final_faces := make([]halfedge.Face, len(faces), allocator)
|
||||||
copy(final_vertices, vertices[:])
|
copy(final_vertices, vertices[:])
|
||||||
copy(final_edges, edges[:])
|
|
||||||
copy(final_faces, faces[:])
|
copy(final_faces, faces[:])
|
||||||
|
|
||||||
mesh := halfedge.Half_Edge_Mesh {
|
mesh := halfedge.Half_Edge_Mesh {
|
||||||
@ -349,6 +348,8 @@ parse_convex :: proc(bytes: []byte, allocator := context.allocator) -> (Loaded_C
|
|||||||
extent = extent,
|
extent = extent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
halfedge.sort_edges(mesh, edges[:])
|
||||||
|
|
||||||
// Center of mass calculation
|
// Center of mass calculation
|
||||||
total_volume := f32(0.0)
|
total_volume := f32(0.0)
|
||||||
{
|
{
|
||||||
|
@ -51,11 +51,10 @@ mesh_from_vertex_index_list :: proc(
|
|||||||
mesh: Half_Edge_Mesh
|
mesh: Half_Edge_Mesh
|
||||||
verts := make([]Vertex, len(vertices), allocator)
|
verts := make([]Vertex, len(vertices), allocator)
|
||||||
faces := make([]Face, num_faces, allocator)
|
faces := make([]Face, num_faces, allocator)
|
||||||
edges := make([]Half_Edge, len(indices), allocator)
|
edges := make([]Half_Edge, len(indices), context.temp_allocator)
|
||||||
|
|
||||||
mesh.vertices = verts
|
mesh.vertices = verts
|
||||||
mesh.faces = faces
|
mesh.faces = faces
|
||||||
mesh.edges = edges
|
|
||||||
|
|
||||||
min_pos, max_pos: Vec3 = max(f32), min(f32)
|
min_pos, max_pos: Vec3 = max(f32), min(f32)
|
||||||
|
|
||||||
@ -128,9 +127,52 @@ mesh_from_vertex_index_list :: proc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mesh.edges = make([]Half_Edge, len(edges), allocator)
|
||||||
|
|
||||||
|
sort_edges(mesh, edges)
|
||||||
|
|
||||||
return mesh
|
return mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort_edges :: proc(dst_mesh: Half_Edge_Mesh, unsorted_edges: []Half_Edge) {
|
||||||
|
assert(len(dst_mesh.edges) == len(unsorted_edges))
|
||||||
|
|
||||||
|
keys := make([]u32, len(unsorted_edges), context.temp_allocator)
|
||||||
|
|
||||||
|
for &e, i in unsorted_edges {
|
||||||
|
v0 := e.origin
|
||||||
|
v1 := unsorted_edges[e.next].origin
|
||||||
|
|
||||||
|
min_v := min(v0, v1)
|
||||||
|
max_v := max(v0, v1)
|
||||||
|
|
||||||
|
keys[i] = (u32(max_v) << 16) | u32(min_v)
|
||||||
|
}
|
||||||
|
|
||||||
|
sorted_edges_indices := slice.sort_with_indices(keys, context.temp_allocator)
|
||||||
|
|
||||||
|
unsorted_to_sorted_lookup := make([]Edge_Index, len(unsorted_edges), context.temp_allocator)
|
||||||
|
for i in 0 ..< len(unsorted_edges) {
|
||||||
|
unsorted_to_sorted_lookup[sorted_edges_indices[i]] = Edge_Index(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0 ..< len(unsorted_edges) {
|
||||||
|
sorted := &dst_mesh.edges[i]
|
||||||
|
sorted^ = unsorted_edges[sorted_edges_indices[i]]
|
||||||
|
sorted.twin = unsorted_to_sorted_lookup[sorted.twin]
|
||||||
|
sorted.next = unsorted_to_sorted_lookup[sorted.next]
|
||||||
|
sorted.prev = unsorted_to_sorted_lookup[sorted.prev]
|
||||||
|
}
|
||||||
|
|
||||||
|
for &v in dst_mesh.vertices {
|
||||||
|
v.edge = unsorted_to_sorted_lookup[v.edge]
|
||||||
|
}
|
||||||
|
|
||||||
|
for &f in dst_mesh.faces {
|
||||||
|
f.edge = unsorted_to_sorted_lookup[f.edge]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get_edge_points :: #force_inline proc(
|
get_edge_points :: #force_inline proc(
|
||||||
mesh: Half_Edge_Mesh,
|
mesh: Half_Edge_Mesh,
|
||||||
edge: Half_Edge,
|
edge: Half_Edge,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package collision
|
package collision
|
||||||
|
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:container/bit_array"
|
|
||||||
import "core:log"
|
import "core:log"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import lg "core:math/linalg"
|
import lg "core:math/linalg"
|
||||||
@ -274,29 +273,34 @@ query_separation_edges :: proc(
|
|||||||
|
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
|
|
||||||
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 :: #force_inline 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
|
||||||
}
|
}
|
||||||
|
|
||||||
for edge_a, edge_a_idx in a.edges {
|
assert(len(a.edges) % 2 == 0)
|
||||||
for edge_b, edge_b_idx in b.edges {
|
assert(len(b.edges) % 2 == 0)
|
||||||
pair_idx := calc_pair_index(edge_a_idx, edge_b_idx, a_len)
|
|
||||||
if bit_array.get(&checked_pairs, pair_idx) {
|
for edge_a_idx := 0; edge_a_idx < len(a.edges); edge_a_idx += 2 {
|
||||||
continue
|
edge_a := a.edges[edge_a_idx]
|
||||||
}
|
for edge_b_idx := 0; edge_b_idx < len(b.edges); edge_b_idx += 2 {
|
||||||
|
edge_b := b.edges[edge_b_idx]
|
||||||
|
// pair_idx := calc_pair_index(edge_a_idx, edge_b_idx, a_len)
|
||||||
|
// if bit_array.get(&checked_pairs, pair_idx) {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
// TODO: sort edges so twins are next to each other, then can just iterate with step = 2 and skip this bitfield
|
// TODO: sort edges so twins are next to each other, then can just iterate with step = 2 and skip this bitfield
|
||||||
bit_array.set(&checked_pairs, pair_idx)
|
// bit_array.set(&checked_pairs, pair_idx)
|
||||||
bit_array.set(&checked_pairs, calc_pair_index(int(edge_a.twin), edge_b_idx, a_len))
|
// bit_array.set(&checked_pairs, calc_pair_index(int(edge_a.twin), edge_b_idx, a_len))
|
||||||
bit_array.set(&checked_pairs, calc_pair_index(edge_a_idx, int(edge_b.twin), a_len))
|
// bit_array.set(&checked_pairs, calc_pair_index(edge_a_idx, int(edge_b.twin), a_len))
|
||||||
bit_array.set(
|
// bit_array.set(
|
||||||
&checked_pairs,
|
// &checked_pairs,
|
||||||
calc_pair_index(int(edge_a.twin), int(edge_b.twin), a_len),
|
// calc_pair_index(int(edge_a.twin), int(edge_b.twin), a_len),
|
||||||
)
|
// )
|
||||||
|
|
||||||
if build_minkowski_face(a, b, edge_a, edge_b) {
|
if build_minkowski_face(a, b, edge_a, edge_b) {
|
||||||
edge_a1, edge_a2 := halfedge.get_edge_points(a, edge_a)
|
edge_a1, edge_a2 := halfedge.get_edge_points(a, edge_a)
|
||||||
|
@ -148,7 +148,7 @@ build_dynamic_tlas :: proc(
|
|||||||
|
|
||||||
phys_aabb := body_get_aabb(body)
|
phys_aabb := body_get_aabb(body)
|
||||||
|
|
||||||
EXPAND_K :: 2
|
EXPAND_K :: 10
|
||||||
expand := lg.abs(EXPAND_K * config.timestep * body.v) + 0.1
|
expand := lg.abs(EXPAND_K * config.timestep * body.v) + 0.1
|
||||||
phys_aabb.extent += expand * 0.5
|
phys_aabb.extent += expand * 0.5
|
||||||
|
|
||||||
@ -329,6 +329,9 @@ raycast :: proc(
|
|||||||
normal = normal2
|
normal = normal2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: raycast_level and raycast_bodies should return a normalized vec
|
||||||
|
normal = lg.normalize0(normal)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,6 +475,7 @@ find_new_contacts :: proc(
|
|||||||
sim_cache: ^Sim_Cache,
|
sim_cache: ^Sim_Cache,
|
||||||
static_tlas: ^Static_TLAS,
|
static_tlas: ^Static_TLAS,
|
||||||
dyn_tlas: ^Dynamic_TLAS,
|
dyn_tlas: ^Dynamic_TLAS,
|
||||||
|
config: Solver_Config,
|
||||||
) {
|
) {
|
||||||
tracy.Zone()
|
tracy.Zone()
|
||||||
|
|
||||||
@ -503,6 +507,9 @@ find_new_contacts :: proc(
|
|||||||
|
|
||||||
shape_a_aabb := shape_get_aabb(shape_a^)
|
shape_a_aabb := shape_get_aabb(shape_a^)
|
||||||
shape_a_aabb = body_transform_shape_aabb(body, shape_a_aabb)
|
shape_a_aabb = body_transform_shape_aabb(body, shape_a_aabb)
|
||||||
|
EXPAND_K :: 2
|
||||||
|
expand := lg.abs(EXPAND_K * config.timestep * body.v) + 0.1
|
||||||
|
shape_a_aabb.extent += expand * 0.5
|
||||||
|
|
||||||
shapes_b_it := shapes_iterator(sim_state, other_body.shape)
|
shapes_b_it := shapes_iterator(sim_state, other_body.shape)
|
||||||
|
|
||||||
@ -598,16 +605,18 @@ find_new_contacts :: proc(
|
|||||||
)
|
)
|
||||||
tri := get_triangle(vertices, indices, tri_idx)
|
tri := get_triangle(vertices, indices, tri_idx)
|
||||||
prim_aabb := get_triangle_aabb(tri)
|
prim_aabb := get_triangle_aabb(tri)
|
||||||
|
prim_aabb.min -= 0.1
|
||||||
|
prim_aabb.max += 0.1
|
||||||
|
|
||||||
if bvh.test_aabb_vs_aabb(
|
if bvh.test_aabb_vs_aabb(
|
||||||
body_aabb_in_Level_geom_space,
|
body_aabb_in_Level_geom_space,
|
||||||
prim_aabb,
|
prim_aabb,
|
||||||
) {
|
) {
|
||||||
tracy.ZoneN("body_vs_level_geom")
|
// tracy.ZoneN("body_vs_triangle", false)
|
||||||
|
|
||||||
shapes_it := shapes_iterator(sim_state, body.shape)
|
shapes_it := shapes_iterator(sim_state, body.shape)
|
||||||
for shape in shapes_iterator_next(&shapes_it) {
|
for shape in shapes_iterator_next(&shapes_it) {
|
||||||
tracy.ZoneN("body_shape_vs_level_geom")
|
// tracy.ZoneN("body_shape_vs_triangle")
|
||||||
|
|
||||||
shape_idx := shapes_it.counter - 1
|
shape_idx := shapes_it.counter - 1
|
||||||
shape_aabb := shape_get_aabb(shape^)
|
shape_aabb := shape_get_aabb(shape^)
|
||||||
@ -920,7 +929,7 @@ update_contacts :: proc(sim_state: ^Sim_State, sim_cache: ^Sim_Cache, static_tla
|
|||||||
}
|
}
|
||||||
|
|
||||||
calculate_soft_constraint_params :: proc(
|
calculate_soft_constraint_params :: proc(
|
||||||
natural_freq, damping_ratio, dt: f32,
|
natural_freq, damping_ratio, dt: f64,
|
||||||
) -> (
|
) -> (
|
||||||
bias_rate: f32,
|
bias_rate: f32,
|
||||||
mass_coef: f32,
|
mass_coef: f32,
|
||||||
@ -930,9 +939,9 @@ calculate_soft_constraint_params :: proc(
|
|||||||
a1 := 2.0 * damping_ratio + omega * dt
|
a1 := 2.0 * damping_ratio + omega * dt
|
||||||
a2 := dt * omega * a1
|
a2 := dt * omega * a1
|
||||||
a3 := 1.0 / (1.0 + a2)
|
a3 := 1.0 / (1.0 + a2)
|
||||||
bias_rate = omega / a1
|
bias_rate = f32(omega / a1)
|
||||||
mass_coef = a2 * a3
|
mass_coef = f32(a2 * a3)
|
||||||
impulse_coef = a3
|
impulse_coef = f32(a3)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -944,7 +953,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(40, 1.0, dt)
|
bias_rate, mass_coef, impulse_coef := calculate_soft_constraint_params(30, 0.8, f64(dt))
|
||||||
if !apply_bias {
|
if !apply_bias {
|
||||||
mass_coef = 1
|
mass_coef = 1
|
||||||
bias_rate = 0
|
bias_rate = 0
|
||||||
@ -1303,7 +1312,7 @@ pgs_solve_suspension :: proc(
|
|||||||
forward := wheel_get_forward_vec(body, v)
|
forward := wheel_get_forward_vec(body, v)
|
||||||
right := wheel_get_right_vec(body, v)
|
right := wheel_get_right_vec(body, v)
|
||||||
|
|
||||||
w_normal := get_body_angular_inverse_mass(body, dir)
|
w_normal := get_body_angular_inverse_mass(body, -v.hit_normal)
|
||||||
inv_w_normal := 1.0 / w_normal
|
inv_w_normal := 1.0 / w_normal
|
||||||
|
|
||||||
// Drive force
|
// Drive force
|
||||||
@ -1343,12 +1352,12 @@ pgs_solve_suspension :: proc(
|
|||||||
// Spring force
|
// Spring force
|
||||||
{
|
{
|
||||||
bias_coef, mass_coef, impulse_coef := calculate_soft_constraint_params(
|
bias_coef, mass_coef, impulse_coef := calculate_soft_constraint_params(
|
||||||
v.natural_frequency,
|
f64(v.natural_frequency),
|
||||||
v.damping,
|
f64(v.damping),
|
||||||
dt,
|
f64(dt),
|
||||||
)
|
)
|
||||||
|
|
||||||
vel := lg.dot(body_velocity_at_point(body, wheel_world_pos), dir)
|
vel := lg.dot(body_velocity_at_point(body, v.hit_point), -v.hit_normal)
|
||||||
x := v.hit_t
|
x := v.hit_t
|
||||||
separation := v.rest - x
|
separation := v.rest - x
|
||||||
|
|
||||||
@ -1357,7 +1366,11 @@ pgs_solve_suspension :: proc(
|
|||||||
impulse_coef * v.spring_impulse
|
impulse_coef * v.spring_impulse
|
||||||
v.spring_impulse += incremental_impulse
|
v.spring_impulse += incremental_impulse
|
||||||
|
|
||||||
apply_velocity_correction(body, incremental_impulse * dir, wheel_world_pos)
|
apply_velocity_correction(
|
||||||
|
body,
|
||||||
|
incremental_impulse * v.hit_normal,
|
||||||
|
v.hit_point,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Positive means spinning forward
|
// Positive means spinning forward
|
||||||
@ -1584,11 +1597,7 @@ pgs_substep :: proc(
|
|||||||
forward := wheel_get_forward_vec(body, s)
|
forward := wheel_get_forward_vec(body, s)
|
||||||
right := wheel_get_right_vec(body, s)
|
right := wheel_get_right_vec(body, s)
|
||||||
|
|
||||||
apply_velocity_correction(
|
apply_velocity_correction(body, s.spring_impulse * -s.hit_normal, hit_p)
|
||||||
body,
|
|
||||||
s.spring_impulse * body_local_to_world_vec(body, s.rel_dir),
|
|
||||||
p,
|
|
||||||
)
|
|
||||||
apply_velocity_correction(body, s.lateral_impulse * right, p)
|
apply_velocity_correction(body, s.lateral_impulse * right, p)
|
||||||
|
|
||||||
apply_velocity_correction(body, s.longitudinal_impulse * forward, hit_p)
|
apply_velocity_correction(body, s.longitudinal_impulse * forward, hit_p)
|
||||||
@ -1663,7 +1672,13 @@ simulate_step :: proc(
|
|||||||
build_dynamic_tlas(sim_state, config, &sim_state.dynamic_tlas)
|
build_dynamic_tlas(sim_state, config, &sim_state.dynamic_tlas)
|
||||||
|
|
||||||
remove_invalid_contacts(sim_state, sim_cache, sim_state.static_tlas, sim_state.dynamic_tlas)
|
remove_invalid_contacts(sim_state, sim_cache, sim_state.static_tlas, sim_state.dynamic_tlas)
|
||||||
find_new_contacts(sim_state, sim_cache, &sim_state.static_tlas, &sim_state.dynamic_tlas)
|
find_new_contacts(
|
||||||
|
sim_state,
|
||||||
|
sim_cache,
|
||||||
|
&sim_state.static_tlas,
|
||||||
|
&sim_state.dynamic_tlas,
|
||||||
|
config,
|
||||||
|
)
|
||||||
update_contacts(sim_state, sim_cache, &sim_state.static_tlas)
|
update_contacts(sim_state, sim_cache, &sim_state.static_tlas)
|
||||||
|
|
||||||
Solver :: enum {
|
Solver :: enum {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user