diff --git a/src/entity.h b/src/entity.h index 206bf7ab..0cdb2750 100644 --- a/src/entity.h +++ b/src/entity.h @@ -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 */ diff --git a/src/game.c b/src/game.c index 8391158b..bb7bd18d 100644 --- a/src/game.c +++ b/src/game.c @@ -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 * ========================== */ diff --git a/src/phys.c b/src/phys.c index cc367f67..5576d934 100644 --- a/src/phys.c +++ b/src/phys.c @@ -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); } } diff --git a/src/phys.h b/src/phys.h index c572ff18..cfdb9469 100644 --- a/src/phys.h +++ b/src/phys.h @@ -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);