cleanup collision debug leak & merge structure with contact lookup into entity lookup
This commit is contained in:
parent
6268e012f6
commit
02290601c3
@ -35,6 +35,7 @@
|
||||
#define GAME_PHYSICS_SUBSTEPS 4
|
||||
#define GAME_PHYSICS_ENABLE_WARM_STARTING 1
|
||||
#define GAME_PHYSICS_ENABLE_RELAXATION 1
|
||||
#define GAME_PHYSICS_ENABLE_TOI 1
|
||||
|
||||
#define USER_DRAW_MENKOWSKI 0
|
||||
#define GAME_PHYSICS_ENABLE_COLLISION 1
|
||||
|
||||
224
src/game.c
224
src/game.c
@ -12,32 +12,29 @@
|
||||
#include "log.h"
|
||||
#include "collider.h"
|
||||
|
||||
struct contact_lookup_entry {
|
||||
struct entity_lookup_key {
|
||||
u64 hash;
|
||||
struct entity_handle contact_ent_handle;
|
||||
struct contact_lookup_entry *next;
|
||||
struct contact_lookup_entry *prev;
|
||||
};
|
||||
|
||||
struct contact_lookup_bucket {
|
||||
struct contact_lookup_entry *first;
|
||||
struct contact_lookup_entry *last;
|
||||
struct entity_lookup_entry {
|
||||
struct entity_lookup_key key;
|
||||
struct entity_handle entity;
|
||||
struct entity_lookup_entry *next;
|
||||
struct entity_lookup_entry *prev;
|
||||
};
|
||||
|
||||
struct contact_lookup {
|
||||
struct entity_lookup_bucket {
|
||||
struct entity_lookup_entry *first;
|
||||
struct entity_lookup_entry *last;
|
||||
};
|
||||
|
||||
struct entity_lookup {
|
||||
struct arena arena;
|
||||
struct contact_lookup_bucket buckets[4096];
|
||||
struct contact_lookup_entry *first_free_entry;
|
||||
struct entity_lookup_bucket *buckets;
|
||||
u64 num_buckets;
|
||||
struct entity_lookup_entry *first_free_entry;
|
||||
};
|
||||
|
||||
#if COLLIDER_DEBUG
|
||||
/* TODO: Remove this (debugging) */
|
||||
struct collision_debug_lookup {
|
||||
struct arena arena;
|
||||
struct fixed_dict dict;
|
||||
};
|
||||
#endif
|
||||
|
||||
GLOBAL struct {
|
||||
struct atomic_i32 game_thread_shutdown;
|
||||
struct sys_thread game_thread;
|
||||
@ -64,9 +61,9 @@ GLOBAL struct {
|
||||
f32 mouse_joint_max_force;
|
||||
|
||||
/* Bookkeeping structures */
|
||||
struct contact_lookup contact_lookup;
|
||||
struct entity_lookup contact_lookup;
|
||||
#if COLLIDER_DEBUG
|
||||
struct collision_debug_lookup collision_debug_lookup;
|
||||
struct entity_lookup collision_debug_lookup;
|
||||
#endif
|
||||
|
||||
/* Ticks */
|
||||
@ -176,36 +173,31 @@ INTERNAL void activate_now(struct entity *ent)
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Contact lookup
|
||||
* Entity lookup
|
||||
* ========================== */
|
||||
|
||||
INTERNAL void contact_lookup_alloc(struct contact_lookup *l)
|
||||
INTERNAL struct entity_lookup entity_lookup_alloc(u64 num_buckets)
|
||||
{
|
||||
MEMZERO_STRUCT(l);
|
||||
l->arena = arena_alloc(GIGABYTE(64));
|
||||
ASSERT(num_buckets > 0);
|
||||
struct entity_lookup l = ZI;
|
||||
l.arena = arena_alloc(GIGABYTE(64));
|
||||
l.buckets = arena_push_array_zero(&l.arena, struct entity_lookup_bucket, num_buckets);
|
||||
l.num_buckets = num_buckets;
|
||||
return l;
|
||||
}
|
||||
|
||||
INTERNAL void contact_lookup_release(struct contact_lookup *l)
|
||||
INTERNAL void entity_lookup_release(struct entity_lookup *l)
|
||||
{
|
||||
arena_release(&l->arena);
|
||||
}
|
||||
|
||||
INTERNAL u64 contact_lookup_hash_from_entities(struct entity_handle h0, struct entity_handle h1)
|
||||
INTERNAL struct entity_lookup_entry *entity_lookup_get(struct entity_lookup *l, struct entity_lookup_key key)
|
||||
{
|
||||
struct buffer b0 = BUFFER_FROM_STRUCT(&h0);
|
||||
struct buffer b1 = BUFFER_FROM_STRUCT(&h1);
|
||||
u64 hash = hash_fnv64(HASH_FNV64_BASIS, b0);
|
||||
hash = hash_fnv64(hash, b1);
|
||||
return hash;
|
||||
}
|
||||
|
||||
INTERNAL struct contact_lookup_entry *contact_lookup_get(struct contact_lookup *l, u64 hash)
|
||||
{
|
||||
u64 index = hash % ARRAY_COUNT(l->buckets);
|
||||
struct contact_lookup_bucket *bucket = &l->buckets[index];
|
||||
struct contact_lookup_entry *res = NULL;
|
||||
for (struct contact_lookup_entry *e = bucket->first; e; e = e->next) {
|
||||
if (e->hash == hash) {
|
||||
u64 index = key.hash % l->num_buckets;
|
||||
struct entity_lookup_bucket *bucket = &l->buckets[index];
|
||||
struct entity_lookup_entry *res = NULL;
|
||||
for (struct entity_lookup_entry *e = bucket->first; e; e = e->next) {
|
||||
if (e->key.hash == key.hash) {
|
||||
res = e;
|
||||
break;
|
||||
}
|
||||
@ -213,25 +205,25 @@ INTERNAL struct contact_lookup_entry *contact_lookup_get(struct contact_lookup *
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERNAL void contact_lookup_set(struct contact_lookup *l, u64 hash, struct entity_handle handle)
|
||||
INTERNAL void entity_lookup_set(struct entity_lookup *l, struct entity_lookup_key key, struct entity_handle handle)
|
||||
{
|
||||
u64 index = hash % ARRAY_COUNT(l->buckets);
|
||||
struct contact_lookup_bucket *bucket = &l->buckets[index];
|
||||
u64 index = key.hash % l->num_buckets;
|
||||
struct entity_lookup_bucket *bucket = &l->buckets[index];
|
||||
|
||||
struct contact_lookup_entry *prev = NULL;
|
||||
struct contact_lookup_entry **slot = &bucket->first;
|
||||
struct entity_lookup_entry *prev = NULL;
|
||||
struct entity_lookup_entry **slot = &bucket->first;
|
||||
while (*slot) {
|
||||
if ((*slot)->hash == hash) {
|
||||
if ((*slot)->key.hash == key.hash) {
|
||||
break;
|
||||
}
|
||||
prev = *slot;
|
||||
slot = &(*slot)->next;
|
||||
}
|
||||
|
||||
struct contact_lookup_entry *entry = *slot;
|
||||
struct entity_lookup_entry *entry = *slot;
|
||||
if (entry) {
|
||||
/* Set existing entry */
|
||||
entry->contact_ent_handle = handle;
|
||||
entry->entity = handle;
|
||||
} else {
|
||||
/* Allocate entry */
|
||||
if (l->first_free_entry) {
|
||||
@ -239,12 +231,12 @@ INTERNAL void contact_lookup_set(struct contact_lookup *l, u64 hash, struct enti
|
||||
l->first_free_entry->prev = NULL;
|
||||
l->first_free_entry = entry->next;
|
||||
} else {
|
||||
entry = arena_push(&l->arena, struct contact_lookup_entry);
|
||||
entry = arena_push(&l->arena, struct entity_lookup_entry);
|
||||
}
|
||||
MEMZERO_STRUCT(entry);
|
||||
|
||||
entry->hash = hash;
|
||||
entry->contact_ent_handle = handle;
|
||||
entry->key = key;
|
||||
entry->entity = handle;
|
||||
if (prev) {
|
||||
entry->prev = prev;
|
||||
prev->next = entry;
|
||||
@ -255,11 +247,11 @@ INTERNAL void contact_lookup_set(struct contact_lookup *l, u64 hash, struct enti
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void contact_lookup_remove(struct contact_lookup *l, struct contact_lookup_entry *entry)
|
||||
INTERNAL void entity_lookup_remove(struct entity_lookup *l, struct entity_lookup_entry *entry)
|
||||
{
|
||||
struct contact_lookup_bucket *bucket = &l->buckets[entry->hash % ARRAY_COUNT(l->buckets)];
|
||||
struct contact_lookup_entry *prev = entry->prev;
|
||||
struct contact_lookup_entry *next = entry->next;
|
||||
struct entity_lookup_bucket *bucket = &l->buckets[entry->key.hash % l->num_buckets];
|
||||
struct entity_lookup_entry *prev = entry->prev;
|
||||
struct entity_lookup_entry *next = entry->next;
|
||||
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
@ -281,6 +273,16 @@ INTERNAL void contact_lookup_remove(struct contact_lookup *l, struct contact_loo
|
||||
l->first_free_entry = entry;
|
||||
}
|
||||
|
||||
INTERNAL struct entity_lookup_key entity_lookup_key_from_two_handles(struct entity_handle h0, struct entity_handle h1)
|
||||
{
|
||||
struct entity_lookup_key key = ZI;
|
||||
struct buffer b0 = BUFFER_FROM_STRUCT(&h0);
|
||||
struct buffer b1 = BUFFER_FROM_STRUCT(&h1);
|
||||
key.hash = hash_fnv64(HASH_FNV64_BASIS, b0);
|
||||
key.hash = hash_fnv64(key.hash, b1);
|
||||
return key;
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Reset
|
||||
* ========================== */
|
||||
@ -291,17 +293,14 @@ INTERNAL void reset_world(void)
|
||||
/* Release world */
|
||||
world_release(&G.tick);
|
||||
/* Release bookkeeping */
|
||||
#if COLLIDER_DEBUG
|
||||
arena_release(&G.collision_debug_lookup.arena);
|
||||
#endif
|
||||
contact_lookup_release(&G.contact_lookup);
|
||||
entity_lookup_release(&G.collision_debug_lookup);
|
||||
entity_lookup_release(&G.contact_lookup);
|
||||
}
|
||||
|
||||
/* Create bookkeeping */
|
||||
contact_lookup_alloc(&G.contact_lookup);
|
||||
G.contact_lookup = entity_lookup_alloc(4096);
|
||||
#if COLLIDER_DEBUG
|
||||
G.collision_debug_lookup.arena = arena_alloc(GIGABYTE(64));
|
||||
G.collision_debug_lookup.dict = fixed_dict_init(&G.collision_debug_lookup.arena, 4096);
|
||||
G.collision_debug_lookup = entity_lookup_alloc(4096);
|
||||
#endif
|
||||
|
||||
/* Re-create world */
|
||||
@ -507,12 +506,12 @@ INTERNAL void create_contacts(void)
|
||||
e1_collider = check0_collider;
|
||||
}
|
||||
|
||||
u64 lookup_hash = contact_lookup_hash_from_entities(e0->handle, e1->handle);
|
||||
struct contact_lookup_entry *entry = contact_lookup_get(&G.contact_lookup, lookup_hash);
|
||||
struct entity_lookup_key key = entity_lookup_key_from_two_handles(e0->handle, e1->handle);
|
||||
struct entity_lookup_entry *entry = entity_lookup_get(&G.contact_lookup, key);
|
||||
|
||||
struct entity *constraint_ent = entity_nil();
|
||||
if (entry) {
|
||||
constraint_ent = entity_from_handle(store, entry->contact_ent_handle);
|
||||
constraint_ent = entity_from_handle(store, entry->entity);
|
||||
if (entity_is_valid_and_active(constraint_ent)) {
|
||||
if (constraint_ent->contact_constraint_data.last_iteration >= G.tick.tick_id) {
|
||||
/* Already processed constraint this iteration */
|
||||
@ -522,7 +521,7 @@ INTERNAL void create_contacts(void)
|
||||
}
|
||||
} else {
|
||||
/* Constraint ent no longer valid, delete entry */
|
||||
contact_lookup_remove(&G.contact_lookup, entry);
|
||||
entity_lookup_remove(&G.contact_lookup, entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
@ -583,7 +582,7 @@ INTERNAL void create_contacts(void)
|
||||
entity_enable_prop(constraint_ent, ENTITY_PROP_CONTACT_CONSTRAINT);
|
||||
activate_now(constraint_ent);
|
||||
ASSERT(!entry); /* Existing entry should never be present here */
|
||||
contact_lookup_set(&G.contact_lookup, lookup_hash, constraint_ent->handle);
|
||||
entity_lookup_set(&G.contact_lookup, key, constraint_ent->handle);
|
||||
}
|
||||
}
|
||||
constraint = &constraint_ent->contact_constraint_data;
|
||||
@ -648,21 +647,17 @@ INTERNAL void create_contacts(void)
|
||||
/* TODO: Remove this (debugging) */
|
||||
#if COLLIDER_DEBUG
|
||||
{
|
||||
struct string fdkey = STRING_FROM_BUFFER(BUFFER_FROM_STRUCT(&lookup_hash));
|
||||
struct entity_handle *dbg_ent_handle = fixed_dict_get(&G.collision_debug_lookup.dict, fdkey);
|
||||
if (!dbg_ent_handle) {
|
||||
/* FIXME: Handle never released */
|
||||
dbg_ent_handle = arena_push_zero(&G.collision_debug_lookup.arena, struct entity_handle);
|
||||
struct entity *dbg_ent = entity_nil();
|
||||
struct entity_lookup_entry *dbg_entry = entity_lookup_get(&G.collision_debug_lookup, key);
|
||||
if (dbg_entry) {
|
||||
dbg_ent = entity_from_handle(store, dbg_entry->entity);
|
||||
}
|
||||
|
||||
struct entity *dbg_ent = entity_from_handle(store, *dbg_ent_handle);
|
||||
|
||||
if (!dbg_ent->valid) {
|
||||
/* FIXME: Entity never released */
|
||||
dbg_ent = entity_alloc(root);
|
||||
entity_enable_prop(dbg_ent, ENTITY_PROP_COLLISION_DEBUG);
|
||||
*dbg_ent_handle = dbg_ent->handle;
|
||||
fixed_dict_set(&G.collision_debug_lookup.arena, &G.collision_debug_lookup.dict, fdkey, dbg_ent_handle);
|
||||
entity_lookup_set(&G.collision_debug_lookup, key, dbg_ent->handle);
|
||||
}
|
||||
|
||||
struct collision_debug *dbg = &dbg_ent->collision_debug_data;
|
||||
@ -785,15 +780,53 @@ INTERNAL void prepare_contacts(void)
|
||||
entity_disable_prop(constraint_ent, ENTITY_PROP_ACTIVE);
|
||||
entity_enable_prop(constraint_ent, ENTITY_PROP_RELEASE_AT_END_OF_FRAME);
|
||||
/* Remove from lookup */
|
||||
u64 hash = contact_lookup_hash_from_entities(constraint->e0, constraint->e1);
|
||||
struct contact_lookup_entry *entry = contact_lookup_get(&G.contact_lookup, hash);
|
||||
struct entity_lookup_key key = entity_lookup_key_from_two_handles(constraint->e0, constraint->e1);
|
||||
struct entity_lookup_entry *entry = entity_lookup_get(&G.contact_lookup, key);
|
||||
if (entry) {
|
||||
contact_lookup_remove(&G.contact_lookup, entry);
|
||||
entity_lookup_remove(&G.contact_lookup, entry);
|
||||
} else {
|
||||
ASSERT(false); /* This should always exist */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if COLLIDER_DEBUG
|
||||
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
|
||||
struct entity *dbg_ent = &store->entities[entity_index];
|
||||
if (!entity_is_valid_and_active(dbg_ent)) continue;
|
||||
if (!entity_has_prop(dbg_ent, ENTITY_PROP_COLLISION_DEBUG)) continue;
|
||||
|
||||
struct collision_debug *dbg = &dbg_ent->collision_debug_data;
|
||||
struct entity *e0 = entity_from_handle(store, dbg->e0);
|
||||
struct entity *e1 = entity_from_handle(store, dbg->e1);
|
||||
|
||||
|
||||
if (!entity_is_valid_and_active(e0) || !entity_is_valid_and_active(e1)
|
||||
|| !(entity_has_prop(e0, ENTITY_PROP_PHYSICAL_DYNAMIC) || entity_has_prop(e0, ENTITY_PROP_PHYSICAL_KINEMATIC))
|
||||
|| !(entity_has_prop(e1, ENTITY_PROP_PHYSICAL_DYNAMIC) || entity_has_prop(e1, ENTITY_PROP_PHYSICAL_KINEMATIC))) {
|
||||
/* Mark dbg ent for removal */
|
||||
entity_disable_prop(dbg_ent, ENTITY_PROP_ACTIVE);
|
||||
entity_enable_prop(dbg_ent, ENTITY_PROP_RELEASE_AT_END_OF_FRAME);
|
||||
|
||||
/* Remove from lookup */
|
||||
struct entity_lookup_key key = entity_lookup_key_from_two_handles(dbg->e0, dbg->e1);
|
||||
struct entity_lookup_entry *entry = entity_lookup_get(&G.collision_debug_lookup, key);
|
||||
|
||||
if (e0->valid) {
|
||||
--e0->colliding;
|
||||
}
|
||||
if (e1->valid) {
|
||||
--e0->colliding;
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
entity_lookup_remove(&G.collision_debug_lookup, entry);
|
||||
} else {
|
||||
ASSERT(false); /* This should always exist */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
INTERNAL void warm_start_contacts(void)
|
||||
@ -1563,7 +1596,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations)
|
||||
struct entity_store *store = G.tick.entity_store;
|
||||
//struct entity *root = G.root;
|
||||
|
||||
|
||||
for (u64 e0_index = 0; e0_index < store->reserved; ++e0_index) {
|
||||
struct entity *e0 = &store->entities[e0_index];
|
||||
if (!entity_is_valid_and_active(e0)) continue;
|
||||
@ -1583,7 +1615,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations)
|
||||
e0_xf_t1 = xform_basis_rotated_world(e0_xf_t1, tick_angular_velocity);
|
||||
}
|
||||
|
||||
|
||||
/* Start e1 index at e0 index + 1 to prevent redundant checks */
|
||||
for (u64 e1_index = e0_index + 1; e1_index < store->reserved; ++e1_index) {
|
||||
struct entity *e1 = &store->entities[e1_index];
|
||||
@ -1609,7 +1640,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations)
|
||||
if (t != 0 && t < smallest_t) {
|
||||
smallest_t = t;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1617,24 +1647,6 @@ INTERNAL f32 determine_earliest_toi(f32 dt, f32 tolerance, u32 max_iterations)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ========================== *
|
||||
* Update
|
||||
@ -2254,11 +2266,21 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
|
||||
f32 remaining_dt = dt;
|
||||
integrate_velocities_from_forces(dt);
|
||||
while (remaining_dt > 0) {
|
||||
f32 earliest_toi = 1;
|
||||
{
|
||||
#if GAME_PHYSICS_ENABLE_TOI
|
||||
const f32 min_toi = 0.000001f;
|
||||
const f32 tolerance = 0.00001f;
|
||||
const u32 max_iterations = 128;
|
||||
f32 earliest_toi = max_f32(determine_earliest_toi(remaining_dt, tolerance, max_iterations), min_toi);
|
||||
earliest_toi = max_f32(determine_earliest_toi(remaining_dt, tolerance, max_iterations), min_toi);
|
||||
#else
|
||||
(UNUSED)toi;
|
||||
(UNUSED)determine_earliest_toi;
|
||||
#endif
|
||||
}
|
||||
|
||||
f32 step_dt = remaining_dt * earliest_toi;
|
||||
|
||||
create_contacts();
|
||||
create_mouse_joints(game_cmds);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user