respond to collisions using callbacks rather than events

This commit is contained in:
jacob 2025-01-12 09:11:30 -06:00
parent 0ef72e1b23
commit b66f130e9c
4 changed files with 101 additions and 106 deletions

View File

@ -22,8 +22,6 @@ enum entity_prop {
ENTITY_PROP_MOUSE_JOINT,
ENTITY_PROP_SENSOR,
ENTITY_PROP_HIT_EVENT,
ENTITY_PROP_PLAYER_CONTROLLED,
ENTITY_PROP_CAMERA,
ENTITY_PROP_CAMERA_ACTIVE,
@ -128,11 +126,6 @@ struct entity {
/* ENTITY_PROP_MOUSE_JOINT */
struct phys_mouse_joint mouse_joint_data;
/* ====================================================================== */
/* Hit event */
struct phys_hit_event hit_event;
/* ====================================================================== */
/* Activation */

View File

@ -323,6 +323,50 @@ INTERNAL void release_entities_with_prop(enum entity_prop prop)
scratch_end(scratch);
}
/* ========================== *
* Respond to physics collisions
* ========================== */
INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, array)
{
struct entity_store *store = G.tick.entity_store;
struct entity *root = entity_from_handle(store, store->root);
for (u64 i = 0; i < array.count; ++i) {
struct phys_collision_data *data = &array.a[i];
struct entity *e0 = entity_from_handle(store, data->e0);
struct entity *e1 = entity_from_handle(store, data->e1);
if (entity_is_valid_and_active(e0) && entity_is_valid_and_active(e1)) {
/* Bullet hit entity */
if (entity_has_prop(e0, ENTITY_PROP_BULLET) || entity_has_prop(e1, ENTITY_PROP_BULLET)) {
struct entity *bullet = entity_has_prop(e0, ENTITY_PROP_BULLET) ? e0 : e1;
struct entity *target = e0 == bullet ? e1 : e0;
(UNUSED)bullet;
(UNUSED)target;
/* Disable bullet */
entity_enable_prop(bullet, ENTITY_PROP_RELEASE_BEFORE_PUBLISH);
entity_disable_prop(bullet, ENTITY_PROP_ACTIVE);
/* Create test blood */
/* TODO: Remove this */
{
struct xform xf = XFORM_TRS(.t = data->point);
struct entity *decal = entity_alloc(root);
decal->sprite = sprite_tag_from_path(STR("res/graphics/blood.ase"));
entity_set_xform(decal, xf);
entity_enable_prop(decal, ENTITY_PROP_PHYSICAL_KINEMATIC);
decal->linear_velocity = v2_mul(v2_norm(data->normal), 0.5f);
decal->angular_velocity = 1 - (((f32)sys_rand_u32() / (f32)U32_MAX) * 2);
}
}
}
}
}
/* ========================== *
* Update
* ========================== */
@ -925,6 +969,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
ctx.tick_id = G.tick.tick_id;
ctx.store = store;
ctx.contact_lookup = &G.contact_lookup;
ctx.pre_solve_callback = on_collision;
#if COLLIDER_DEBUG
ctx.debug_lookup = &G.collision_debug_lookup;
#endif
@ -945,74 +990,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
phys_step(&ctx, dt);
}
/* ========================== *
* Respond to hit events
* ========================== */
{
struct temp_arena temp = arena_temp_begin(scratch.arena);
/* Sort hit events by dt (to ensure early time of impact collisions are processed first) */
struct phys_hit_event_node *first_sorted = NULL;
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
struct entity *ent = &store->entities[entity_index];
if (!entity_is_valid_and_active(ent)) continue;
if (!entity_has_prop(ent, ENTITY_PROP_HIT_EVENT)) continue;
struct phys_hit_event *e = &ent->hit_event;
struct phys_hit_event_node *next = first_sorted;
struct phys_hit_event_node *prev = NULL;
while (next) {
if (e->dt < next->e->dt) {
break;
}
prev = next;
next = next->next;
}
struct phys_hit_event_node *n = arena_push_zero(temp.arena, struct phys_hit_event_node);
n->e = e;
n->next = next;
if (prev) {
prev->next = n;
} else {
first_sorted = n;
}
}
/* Process events */
for (struct phys_hit_event_node *n = first_sorted; n; n = n->next) {
struct phys_hit_event *event = n->e;
struct entity *e0 = entity_from_handle(store, event->e0);
struct entity *e1 = entity_from_handle(store, event->e1);
if (entity_is_valid_and_active(e0) && entity_is_valid_and_active(e1)) {
/* Bullet hit entity */
if (entity_has_prop(e0, ENTITY_PROP_BULLET) || entity_has_prop(e1, ENTITY_PROP_BULLET)) {
struct entity *bullet = entity_has_prop(e0, ENTITY_PROP_BULLET) ? e0 : e1;
struct entity *target = e0 == bullet ? e1 : e0;
(UNUSED)bullet;
(UNUSED)target;
entity_enable_prop(bullet, ENTITY_PROP_RELEASE_BEFORE_PUBLISH);
entity_disable_prop(bullet, ENTITY_PROP_ACTIVE);
/* Create test blood */
/* TODO: Remove this */
{
struct xform xf = XFORM_TRS(.t = event->point);
struct entity *decal = entity_alloc(root);
decal->sprite = sprite_tag_from_path(STR("res/graphics/blood.ase"));
entity_set_xform(decal, xf);
entity_enable_prop(decal, ENTITY_PROP_PHYSICAL_KINEMATIC);
decal->linear_velocity = v2_mul(v2_norm(event->normal), 0.5f);
decal->angular_velocity = 1 - (((f32)sys_rand_u32() / (f32)U32_MAX) * 2);
}
}
}
}
arena_temp_end(temp);
}
/* ========================== *
* Initialize bullet kinematics from sources
* ========================== */

View File

@ -1,4 +1,6 @@
#include "phys.h"
#include "math.h"
#include "scratch.h"
#include "entity.h"
GLOBAL struct {
@ -37,9 +39,11 @@ struct phys_startup_receipt phys_startup(void)
* Contact
* ========================== */
void phys_create_contacts(struct phys_ctx *ctx, f32 elapsed_dt)
struct phys_collision_data_array phys_create_contacts(struct arena *arena, struct phys_ctx *ctx, f32 elapsed_dt)
{
__prof;
struct phys_collision_data_array res = ZI;
res.a = arena_dry_push(arena, struct phys_collision_data);
u64 tick_id = ctx->tick_id;
struct entity_lookup *contact_lookup = ctx->contact_lookup;
struct entity_lookup *debug_lookup = ctx->debug_lookup;
@ -115,23 +119,21 @@ void phys_create_contacts(struct phys_ctx *ctx, f32 elapsed_dt)
struct phys_contact_constraint *constraint = NULL;
if (collider_res.num_points > 0) {
if (!entity_is_valid_and_active(constraint_ent)) {
/* Create hit event */
{
struct entity *event = entity_alloc(root);
entity_enable_prop(event, ENTITY_PROP_HIT_EVENT);
event->hit_event.e0 = e0->handle;
event->hit_event.e1 = e1->handle;
event->hit_event.normal = collider_res.normal;
event->hit_event.dt = elapsed_dt;
entity_enable_prop(event, ENTITY_PROP_RELEASE_BEFORE_PUBLISH);
entity_enable_prop(event, ENTITY_PROP_ACTIVE);
/* Push collision data */
if (ctx->pre_solve_callback || ctx->post_solve_callback) {
struct phys_collision_data *data = arena_push_zero(arena, struct phys_collision_data);
++res.count;
data->e0 = e0->handle;
data->e1 = e1->handle;
data->normal = collider_res.normal;
data->dt = elapsed_dt;
/* Calculate point */
struct v2 point = collider_res.points[0].point;
if (collider_res.num_points > 1) {
point = v2_add(point, v2_mul(v2_sub(collider_res.points[1].point, point), 0.5f));
}
event->hit_event.point = point;
data->point = point;
/* Calculate relative velocity */
struct v2 vrel;
@ -146,7 +148,7 @@ void phys_create_contacts(struct phys_ctx *ctx, f32 elapsed_dt)
struct v2 vel1 = v2_add(v1, v2_perp_mul(vcp1, w1));
vrel = v2_sub(vel0, vel1);
}
event->hit_event.vrel = vrel;
data->vrel = vrel;
}
/* Create constraint */
@ -275,6 +277,7 @@ void phys_create_contacts(struct phys_ctx *ctx, f32 elapsed_dt)
#endif
}
}
return res;
}
void phys_prepare_contacts(struct phys_ctx *ctx)
@ -1125,6 +1128,7 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
f32 remaining_dt = timestep;
while (remaining_dt > 0) {
__profscope(step);
struct temp_arena scratch = scratch_begin_no_conflict();
/* TOI */
f32 step_dt = remaining_dt;
{
@ -1141,13 +1145,18 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
#endif
}
phys_create_contacts(ctx, timestep - remaining_dt);
struct phys_collision_data_array collision_data = phys_create_contacts(scratch.arena, ctx, timestep - remaining_dt);
phys_create_mouse_joints(ctx);
phys_prepare_contacts(ctx);
phys_prepare_motor_joints(ctx);
phys_prepare_mouse_joints(ctx);
if (ctx->pre_solve_callback) {
__profscope(pre_solve_callback);
ctx->pre_solve_callback(collision_data);
}
f32 substep_dt = step_dt / GAME_PHYSICS_SUBSTEPS;
for (u32 i = 0; i < GAME_PHYSICS_SUBSTEPS; ++i) {
__profscope(substep);
@ -1169,5 +1178,12 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
phys_solve_contacts(ctx, substep_dt, false); /* Relaxation */
#endif
}
if (ctx->post_solve_callback) {
__profscope(post_solve_callback);
ctx->post_solve_callback(collision_data);
}
scratch_end(scratch);
}
}

View File

@ -7,19 +7,8 @@
struct entity_store;
struct entity_lookup;
/* Structure containing data used for a single physics step */
struct phys_ctx {
u64 tick_id;
struct entity_store *store;
struct entity_lookup *contact_lookup;
struct entity_lookup *debug_lookup;
struct v2 dbg_cursor_pos;
b32 dbg_start_dragging;
b32 dbg_stop_dragging;
};
struct phys_hit_event {
struct phys_collision_data {
struct entity_handle constraint_ent;
struct entity_handle e0;
struct entity_handle e1;
struct v2 point;
@ -28,9 +17,29 @@ struct phys_hit_event {
f32 dt; /* How much time elapsed in the step when this event occurred (this will equal the physics timestep unless an early time of impact collision occurred) */
};
struct phys_hit_event_node {
struct phys_hit_event *e;
struct phys_hit_event_node *next;
struct phys_collision_data_array {
struct phys_collision_data *a;
u64 count;
};
struct phys_collision_data;
#define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg) void name(struct phys_collision_data_array arg)
typedef PHYS_COLLISION_CALLBACK_FUNC_DEF(phys_collision_callback_func, data);
/* Structure containing data used for a single physics step */
struct phys_ctx {
u64 tick_id;
struct entity_store *store;
struct entity_lookup *contact_lookup;
phys_collision_callback_func *pre_solve_callback;
phys_collision_callback_func *post_solve_callback;
struct entity_lookup *debug_lookup;
struct v2 dbg_cursor_pos;
b32 dbg_start_dragging;
b32 dbg_stop_dragging;
};
/* ========================== *
@ -97,7 +106,7 @@ struct phys_collision_debug {
struct xform xf1;
};
void phys_create_contacts(struct phys_ctx *ctx, f32 elapsed_dt);
struct phys_collision_data_array phys_create_contacts(struct arena *arena, struct phys_ctx *ctx, f32 elapsed_dt);
void phys_prepare_contacts(struct phys_ctx *ctx);
void phys_warm_start_contacts(struct phys_ctx *ctx);
void phys_solve_contacts(struct phys_ctx *ctx, f32 dt, b32 apply_bias);