call collision callbacks during contact creation loop

This commit is contained in:
jacob 2025-05-17 07:59:54 -05:00
parent 2c6dc93167
commit 3b19e1260e
3 changed files with 49 additions and 79 deletions

View File

@ -40,14 +40,14 @@ struct phys_startup_receipt phys_startup(void)
* Contact * Contact
* ========================== */ * ========================== */
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration) void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration)
{ {
__prof; __prof;
struct phys_collision_data_array res = ZI; struct sim_step_ctx *sim_step_ctx = ctx->sim_step_ctx;
res.a = arena_dry_push(arena, struct phys_collision_data); struct sim_snapshot *ss = sim_step_ctx->world;
struct sim_snapshot *ss = ctx->sim_step_ctx->world; struct space *space = sim_step_ctx->accel->space;
struct space *space = ctx->sim_step_ctx->accel->space;
struct sim_ent_id local_player = ss->local_player; struct sim_ent_id local_player = ss->local_player;
phys_collision_callback_func *collision_callback = ctx->collision_callback;
struct sim_ent *root = sim_ent_from_id(ss, SIM_ENT_ROOT_ID); struct sim_ent *root = sim_ent_from_id(ss, SIM_ENT_ROOT_ID);
u64 tick = ss->tick; u64 tick = ss->tick;
@ -187,24 +187,21 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
#endif #endif
} }
/* Push collision data */ /* Run collision callback */
/* TODO: Analyze benefits of calling callback here directly rather than deferring */ if (collision_callback) {
if (ctx->collision_callback) { struct phys_collision_data data = ZI;
struct phys_collision_data *data = arena_push(arena, struct phys_collision_data); data.e0 = e0->id;
++res.count; data.e1 = e1->id;
data->constraint = &constraint_ent->contact_constraint_data; data.normal = collider_res.normal;
data->e0 = e0->id; data.is_start = is_start;
data->e1 = e1->id; data.dt = elapsed_dt;
data->normal = collider_res.normal;
data->is_start = is_start;
data->dt = elapsed_dt;
/* Calculate point */ /* Calculate point */
struct v2 point = collider_res.points[0].point; struct v2 point = collider_res.points[0].point;
if (collider_res.num_points > 1) { if (collider_res.num_points > 1) {
point = v2_add(point, v2_mul(v2_sub(collider_res.points[1].point, point), 0.5f)); point = v2_add(point, v2_mul(v2_sub(collider_res.points[1].point, point), 0.5f));
} }
data->point = point; data.point = point;
/* Calculate relative velocity */ /* Calculate relative velocity */
struct v2 vrel; struct v2 vrel;
@ -219,7 +216,21 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
struct v2 vel1 = v2_add(v1, v2_perp_mul(vcp1, w1)); struct v2 vel1 = v2_add(v1, v2_perp_mul(vcp1, w1));
vrel = v2_sub(vel0, vel1); vrel = v2_sub(vel0, vel1);
} }
data->vrel = vrel; data.vrel = vrel;
/* Collision data from e1's perspective */
struct phys_collision_data data_inverted = data;
data_inverted.e0 = data.e1;
data_inverted.e1 = data.e0;
data_inverted.normal = v2_neg(data.normal);
data_inverted.vrel = v2_neg(data.vrel);
/* Run callback twice for both e0 & e1 */
b32 skip_solve0 = collision_callback(&data, sim_step_ctx);
b32 skip_solve1 = collision_callback(&data_inverted, sim_step_ctx);
if (skip_solve0 || skip_solve1) {
constraint->skip_solve = true;
}
} }
} else if (constraint_ent->valid) { } else if (constraint_ent->valid) {
constraint_ent->contact_constraint_data.num_points = 0; constraint_ent->contact_constraint_data.num_points = 0;
@ -263,7 +274,6 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
} }
space_iter_end(&iter); space_iter_end(&iter);
} }
return res;
} }
void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration) void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration)
@ -307,7 +317,7 @@ void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration)
f32 scale = math_fabs(xform_get_determinant(e1_xf)); f32 scale = math_fabs(xform_get_determinant(e1_xf));
f32 scaled_mass = e1->mass_unscaled * scale; f32 scaled_mass = e1->mass_unscaled * scale;
f32 scaled_inertia = e1->inertia_unscaled * scale; f32 scaled_inertia = e1->inertia_unscaled * scale;
inv_m1 = 1.f / scaled_mass; inv_m1 = 1.f / scaled_mass;
inv_i1 = 1.f / scaled_inertia; inv_i1 = 1.f / scaled_inertia;
} }
@ -1044,30 +1054,6 @@ void phys_update_aabbs(struct phys_step_ctx *ctx)
* Step * Step
* ========================== */ * ========================== */
INTERNAL void run_collision_callbacks(struct phys_step_ctx *ctx, phys_collision_callback_func *callback, struct phys_collision_data_array collision_data)
{
__prof;
struct sim_step_ctx *sim_step_ctx = ctx->sim_step_ctx;
for (u64 i = 0; i < collision_data.count; ++i) {
/* Collision data from e0's perspective */
struct phys_collision_data *data = &collision_data.a[i];
/* Collision data from e1's perspective */
struct phys_collision_data data_inverted = *data;
data_inverted.e0 = data->e1;
data_inverted.e1 = data->e0;
data_inverted.normal = v2_neg(data->normal);
data_inverted.vrel = v2_neg(data->vrel);
/* Run callback twice for both e0 & e1 */
b32 skip_solve0 = callback(data, sim_step_ctx);
b32 skip_solve1 = callback(&data_inverted, sim_step_ctx);
if (skip_solve0 || skip_solve1) {
data->constraint->skip_solve = true;
}
}
}
/* Returns phys iteration to be fed into next step. Supplied iteration must be > 0. */ /* Returns phys iteration to be fed into next step. Supplied iteration must be > 0. */
void phys_step(struct phys_step_ctx *ctx, f32 timestep) void phys_step(struct phys_step_ctx *ctx, f32 timestep)
{ {
@ -1099,16 +1085,12 @@ void phys_step(struct phys_step_ctx *ctx, f32 timestep)
} }
remaining_dt -= step_dt; remaining_dt -= step_dt;
struct phys_collision_data_array collision_data = phys_create_and_update_contacts(scratch.arena, ctx, timestep - remaining_dt, phys_iteration); phys_create_and_update_contacts(ctx, timestep - remaining_dt, phys_iteration);
phys_prepare_contacts(ctx, phys_iteration); phys_prepare_contacts(ctx, phys_iteration);
phys_prepare_motor_joints(ctx); phys_prepare_motor_joints(ctx);
phys_prepare_mouse_joints(ctx); phys_prepare_mouse_joints(ctx);
if (ctx->collision_callback) {
run_collision_callbacks(ctx, ctx->collision_callback, collision_data);
}
f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS; f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS;
for (u32 i = 0; i < SIM_PHYSICS_SUBSTEPS; ++i) { for (u32 i = 0; i < SIM_PHYSICS_SUBSTEPS; ++i) {
__profscope(substep); __profscope(substep);
@ -1130,14 +1112,13 @@ void phys_step(struct phys_step_ctx *ctx, f32 timestep)
/* Integrate */ /* Integrate */
phys_integrate_velocities(ctx, substep_dt); phys_integrate_velocities(ctx, substep_dt);
phys_update_aabbs(ctx);
/* Relaxation solve */ /* Relaxation solve */
#if SIM_PHYSICS_ENABLE_COLLISION && SIM_PHYSICS_ENABLE_RELAXATION #if SIM_PHYSICS_ENABLE_COLLISION && SIM_PHYSICS_ENABLE_RELAXATION
phys_solve_contacts(ctx, substep_dt, false); phys_solve_contacts(ctx, substep_dt, false);
#endif #endif
} }
phys_update_aabbs(ctx);
scratch_end(scratch); scratch_end(scratch);
} }

View File

@ -10,8 +10,6 @@ struct sim_step_ctx;
struct phys_contact_constraint; struct phys_contact_constraint;
struct phys_collision_data { struct phys_collision_data {
/* NOTE: e0 & e1 can be opposite of e0 & e1 contained in constraint when passed to collision callbacks */
struct phys_contact_constraint *constraint;
struct sim_ent_id e0; struct sim_ent_id e0;
struct sim_ent_id e1; struct sim_ent_id e1;
struct v2 point; struct v2 point;
@ -21,11 +19,6 @@ struct phys_collision_data {
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 occurred) */ 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 occurred) */
}; };
struct phys_collision_data_array {
struct phys_collision_data *a;
u64 count;
};
/* Callback can return true to prevent the physics system from resolving */ /* Callback can return true to prevent the physics system from resolving */
#define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg_collision_data, arg_sim_step_ctx) b32 name(struct phys_collision_data *arg_collision_data, struct sim_step_ctx *arg_sim_step_ctx) #define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg_collision_data, arg_sim_step_ctx) b32 name(struct phys_collision_data *arg_collision_data, struct sim_step_ctx *arg_sim_step_ctx)
typedef PHYS_COLLISION_CALLBACK_FUNC_DEF(phys_collision_callback_func, collision_data, ctx); typedef PHYS_COLLISION_CALLBACK_FUNC_DEF(phys_collision_callback_func, collision_data, ctx);
@ -102,7 +95,7 @@ struct phys_collision_debug {
struct xform xf1; struct xform xf1;
}; };
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration); void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration);
void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration); void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration);
void phys_warm_start_contacts(struct phys_step_ctx *ctx); void phys_warm_start_contacts(struct phys_step_ctx *ctx);
void phys_solve_contacts(struct phys_step_ctx *ctx, f32 dt, b32 apply_bias); void phys_solve_contacts(struct phys_step_ctx *ctx, f32 dt, b32 apply_bias);

View File

@ -77,8 +77,8 @@ INTERNAL struct sim_ent *spawn_test_chucker(struct sim_ent *parent)
chucker->layer = SIM_LAYER_RELATIVE_WEAPON; chucker->layer = SIM_LAYER_RELATIVE_WEAPON;
sim_ent_enable_prop(chucker, SEPROP_WEAPON_CHUCKER); sim_ent_enable_prop(chucker, SEPROP_WEAPON_CHUCKER);
chucker->primary_fire_delay = 1.0f / 10.0f; //chucker->primary_fire_delay = 1.0f / 10.0f;
chucker->secondary_fire_delay = 1.0f / 10.0f; //chucker->secondary_fire_delay = 1.0f / 10.0f;
/* Chucker zone */ /* Chucker zone */
{ {
@ -378,6 +378,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, data, step_ctx)
struct xform victim_xf = sim_ent_get_xform(victim); struct xform victim_xf = sim_ent_get_xform(victim);
struct collider_closest_points_result closest_points = collider_closest_points(&origin_collider, &victim->local_collider, xf, victim_xf); struct collider_closest_points_result closest_points = collider_closest_points(&origin_collider, &victim->local_collider, xf, victim_xf);
struct v2 dir = v2_sub(closest_points.p1, closest_points.p0); struct v2 dir = v2_sub(closest_points.p1, closest_points.p0);
struct v2 point = closest_points.p1;
f32 distance = v2_len(dir); f32 distance = v2_len(dir);
#if 0 #if 0
if (closest_points.colliding) { if (closest_points.colliding) {
@ -387,6 +388,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, data, step_ctx)
#else #else
if (v2_dot(data->normal, dir) < 0) { if (v2_dot(data->normal, dir) < 0) {
dir = v2_neg(dir); dir = v2_neg(dir);
point = xf.og;
distance = 0; distance = 0;
} }
#endif #endif
@ -398,7 +400,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, data, step_ctx)
const f32 falloff_curve = 3; /* Cubic falloff */ const f32 falloff_curve = 3; /* Cubic falloff */
f32 strength_factor = math_pow(1 - distance/radius, falloff_curve); f32 strength_factor = math_pow(1 - distance/radius, falloff_curve);
struct v2 impulse = v2_with_len(dir, strength_center * strength_factor); struct v2 impulse = v2_with_len(dir, strength_center * strength_factor);
sim_ent_apply_linear_impulse(victim, impulse, closest_points.p1); sim_ent_apply_linear_impulse(victim, impulse, point);
} }
} }
@ -607,9 +609,7 @@ void sim_step(struct sim_step_ctx *ctx)
} }
} }
if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) { if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) {
if (is_master && !(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) { test_clear_level(ctx);
test_clear_level(ctx);
}
} }
if (flags & SIM_CONTROL_FLAG_SPAWN1_TEST) { if (flags & SIM_CONTROL_FLAG_SPAWN1_TEST) {
logf_info("Spawn1 (test)"); logf_info("Spawn1 (test)");
@ -622,22 +622,18 @@ void sim_step(struct sim_step_ctx *ctx)
} }
} }
if (flags & SIM_CONTROL_FLAG_SPAWN2_TEST) { if (flags & SIM_CONTROL_FLAG_SPAWN2_TEST) {
if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN2_TEST)) { logf_info("Spawn1 (test)");
logf_info("Spawn1 (test)"); u32 count = 1;
u32 count = 1; f32 spread = 0;
f32 spread = 0; for (u32 j = 0; j < count; ++j) {
for (u32 j = 0; j < count; ++j) { struct v2 pos = player->player_cursor_pos;
struct v2 pos = player->player_cursor_pos; pos.y += (((f32)j / (f32)count) - 0.5) * spread;
pos.y += (((f32)j / (f32)count) - 0.5) * spread; spawn_test_entities2(root, pos);
spawn_test_entities2(root, pos);
}
} }
} }
if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) { if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) {
if (!(old_control.flags & SIM_CONTROL_FLAG_EXPLODE_TEST)) { logf_info("Explosion (test)");
logf_info("Explosion (test)"); spawn_test_explosion(root, player->player_cursor_pos, 100, 2);
spawn_test_explosion(root, player->player_cursor_pos, 100, 2);
}
} }
} }
@ -1036,7 +1032,7 @@ void sim_step(struct sim_step_ctx *ctx)
if (secondary_triggered) { if (secondary_triggered) {
struct sim_ent *zone = sim_ent_from_id(world, ent->chucker_zone); struct sim_ent *zone = sim_ent_from_id(world, ent->chucker_zone);
struct sim_ent *target = sim_ent_from_id(world, zone->chucker_zone_ent); struct sim_ent *target = sim_ent_from_id(world, zone->chucker_zone_ent);
if (sim_ent_is_valid_and_active(target)) { if (sim_ent_is_valid_and_active(target) && zone->chucker_zone_ent_tick == world->tick - 1) {
sim_ent_enable_prop(target, SEPROP_RELEASE); sim_ent_enable_prop(target, SEPROP_RELEASE);
} }
} }