run collision callbacks twice. process explosion blast collision in collision callback

This commit is contained in:
jacob 2025-05-16 09:30:39 -05:00
parent 855fa051d0
commit 6ac44c3de9
6 changed files with 115 additions and 117 deletions

View File

@ -139,15 +139,16 @@ void _log(i32 level, struct string msg)
#if LOG_INCLUDE_SOURCE_LOCATION #if LOG_INCLUDE_SOURCE_LOCATION
struct string msg_formatted = string_format( struct string msg_formatted = string_format(
scratch.arena, scratch.arena,
LIT("%F:%F:%F |Thread %F| [%F] <%F:%F> %F"), LIT("[%F:%F:%F.%F] |%F| [%F] <%F:%F> %F"),
/* Time */ /* Time */
FMT_UINT(lt.hour), FMT_UINT_Z(lt.hour, 2),
FMT_UINT(lt.minute), FMT_UINT_Z(lt.minute, 2),
FMT_UINT(lt.second), FMT_UINT_Z(lt.second, 2),
FMT_UINT_Z(lt.milliseconds, 3),
/* TID */ /* TID */
FMT_UINT(tid), FMT_UINT_Z(tid, 5),
/* Level */ /* Level */
FMT_STR(shorthand), FMT_STR(shorthand),
@ -162,15 +163,16 @@ void _log(i32 level, struct string msg)
#else #else
struct string msg_formatted = string_format( struct string msg_formatted = string_format(
scratch.arena, scratch.arena,
LIT("%F:%F:%F |Thread %F| [%F] %F"), LIT("[%F:%F:%F.%F] |%F| [%F] %F"),
/* Time */ /* Time */
FMT_UINT(lt.hour), FMT_UINT_Z(lt.hour, 2),
FMT_UINT(lt.minute), FMT_UINT_Z(lt.minute, 2),
FMT_UINT(lt.second), FMT_UINT_Z(lt.second, 2),
FMT_UINT_Z(lt.milliseconds, 3),
/* TID */ /* TID */
FMT_UINT(tid), FMT_UINT_Z(tid, 5),
/* Level */ /* Level */
FMT_STR(shorthand), FMT_STR(shorthand),

View File

@ -55,7 +55,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
for (u64 check0_index = 0; check0_index < ss->num_ents_reserved; ++check0_index) { for (u64 check0_index = 0; check0_index < ss->num_ents_reserved; ++check0_index) {
struct sim_ent *check0 = &ss->ents[check0_index]; struct sim_ent *check0 = &ss->ents[check0_index];
if (!sim_ent_is_valid_and_active(check0)) continue; if (!sim_ent_is_valid_and_active(check0)) continue;
if (!(sim_ent_has_prop(check0, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(check0, SEPROP_PHYSICAL_KINEMATIC))) continue; if (!(sim_ent_has_prop(check0, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(check0, SEPROP_PHYSICAL_KINEMATIC) || sim_ent_has_prop(check0, SEPROP_SENSOR))) continue;
if (check0->local_collider.count <= 0) continue; if (check0->local_collider.count <= 0) continue;
struct xform check0_xf = sim_ent_get_xform(check0); struct xform check0_xf = sim_ent_get_xform(check0);
@ -68,7 +68,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
struct sim_ent *check1 = sim_ent_from_id(ss, space_entry->ent); struct sim_ent *check1 = sim_ent_from_id(ss, space_entry->ent);
if (check1 == check0) continue; if (check1 == check0) continue;
if (!sim_ent_is_valid_and_active(check1)) continue; if (!sim_ent_is_valid_and_active(check1)) continue;
if (!(sim_ent_has_prop(check1, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(check1, SEPROP_PHYSICAL_KINEMATIC))) continue; if (!(sim_ent_has_prop(check1, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(check1, SEPROP_PHYSICAL_KINEMATIC) || sim_ent_has_prop(check1, SEPROP_SENSOR))) continue;
if (check1->local_collider.count <= 0) continue; if (check1->local_collider.count <= 0) continue;
/* Deterministic order based on entity id */ /* Deterministic order based on entity id */
@ -323,11 +323,11 @@ void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration)
constraint->inv_i0 = inv_i0; constraint->inv_i0 = inv_i0;
constraint->inv_i1 = inv_i1; constraint->inv_i1 = inv_i1;
if (sim_ent_has_prop(e0, SEPROP_PHYSICAL_KINEMATIC)) { if (!sim_ent_has_prop(e0, SEPROP_PHYSICAL_DYNAMIC)) {
constraint->inv_m0 = 0; constraint->inv_m0 = 0;
constraint->inv_i0 = 0; constraint->inv_i0 = 0;
} }
if (sim_ent_has_prop(e1, SEPROP_PHYSICAL_KINEMATIC)) { if (!sim_ent_has_prop(e1, SEPROP_PHYSICAL_DYNAMIC)) {
constraint->inv_m1 = 0; constraint->inv_m1 = 0;
constraint->inv_i1 = 0; constraint->inv_i1 = 0;
} }
@ -381,8 +381,8 @@ void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration)
if (!sim_ent_should_simulate(e0) || !sim_ent_should_simulate(e1) if (!sim_ent_should_simulate(e0) || !sim_ent_should_simulate(e1)
|| !(sim_ent_has_prop(e0, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e0, SEPROP_PHYSICAL_KINEMATIC)) || !(sim_ent_has_prop(e0, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e0, SEPROP_PHYSICAL_KINEMATIC) || !sim_ent_has_prop(e0, SEPROP_SENSOR))
|| !(sim_ent_has_prop(e1, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e1, SEPROP_PHYSICAL_KINEMATIC))) { || !(sim_ent_has_prop(e1, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e1, SEPROP_PHYSICAL_KINEMATIC) || !sim_ent_has_prop(e1, SEPROP_SENSOR))) {
/* Mark dbg ent for removal */ /* Mark dbg ent for removal */
sim_ent_disable_prop(dbg_ent, SEPROP_ACTIVE); sim_ent_disable_prop(dbg_ent, SEPROP_ACTIVE);
sim_ent_enable_prop(dbg_ent, SEPROP_RELEASE); sim_ent_enable_prop(dbg_ent, SEPROP_RELEASE);
@ -1002,7 +1002,7 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_step_ctx *ctx, f32 step_
for (u64 e0_index = 0; e0_index < ss->num_ents_reserved; ++e0_index) { for (u64 e0_index = 0; e0_index < ss->num_ents_reserved; ++e0_index) {
struct sim_ent *e0 = &ss->ents[e0_index]; struct sim_ent *e0 = &ss->ents[e0_index];
if (!sim_ent_should_simulate(e0)) continue; if (!sim_ent_should_simulate(e0)) continue;
if (!(sim_ent_has_prop(e0, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e0, SEPROP_PHYSICAL_KINEMATIC))) continue; if (!(sim_ent_has_prop(e0, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e0, SEPROP_PHYSICAL_KINEMATIC) || sim_ent_has_prop(e0, SEPROP_SENSOR))) continue;
if (!sim_ent_has_prop(e0, SEPROP_BULLET)) continue; if (!sim_ent_has_prop(e0, SEPROP_BULLET)) continue;
if (e0->local_collider.count <= 0) continue; if (e0->local_collider.count <= 0) continue;
@ -1021,7 +1021,7 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_step_ctx *ctx, f32 step_
struct sim_ent *e1 = sim_ent_from_id(ss, entry->ent); struct sim_ent *e1 = sim_ent_from_id(ss, entry->ent);
if (e1 == e0) continue; if (e1 == e0) continue;
if (!sim_ent_should_simulate(e1)) continue; if (!sim_ent_should_simulate(e1)) continue;
if (!(sim_ent_has_prop(e1, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e1, SEPROP_PHYSICAL_KINEMATIC))) continue; if (!(sim_ent_has_prop(e1, SEPROP_PHYSICAL_DYNAMIC) || sim_ent_has_prop(e1, SEPROP_PHYSICAL_KINEMATIC) || sim_ent_has_prop(e1, SEPROP_SENSOR))) continue;
if (e1->local_collider.count <= 0) continue; if (e1->local_collider.count <= 0) continue;
struct collider_shape e1_collider = e1->local_collider; struct collider_shape e1_collider = e1->local_collider;
@ -1068,6 +1068,26 @@ 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)
{
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 */
callback(data, sim_step_ctx);
callback(&data_inverted, sim_step_ctx);
}
}
/* 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)
{ {
@ -1107,7 +1127,7 @@ void phys_step(struct phys_step_ctx *ctx, f32 timestep)
if (ctx->pre_solve_callback) { if (ctx->pre_solve_callback) {
__profscope(pre_solve_callback); __profscope(pre_solve_callback);
ctx->pre_solve_callback(collision_data, ctx->sim_step_ctx); run_collision_callbacks(ctx, ctx->pre_solve_callback, collision_data);
} }
f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS; f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS;
@ -1141,7 +1161,7 @@ void phys_step(struct phys_step_ctx *ctx, f32 timestep)
if (ctx->post_solve_callback) { if (ctx->post_solve_callback) {
__profscope(post_solve_callback); __profscope(post_solve_callback);
ctx->post_solve_callback(collision_data, ctx->sim_step_ctx); run_collision_callbacks(ctx, ctx->post_solve_callback, collision_data);
} }
scratch_end(scratch); scratch_end(scratch);

View File

@ -10,6 +10,7 @@ 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 phys_contact_constraint *constraint;
struct sim_ent_id e0; struct sim_ent_id e0;
struct sim_ent_id e1; struct sim_ent_id e1;
@ -25,7 +26,7 @@ struct phys_collision_data_array {
}; };
struct phys_collision_data; struct phys_collision_data;
#define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg_collision_data, arg_sim_step_ctx) void name(struct phys_collision_data_array 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) void 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);
/* Structure containing data used for a single physics step */ /* Structure containing data used for a single physics step */

View File

@ -291,9 +291,16 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha
} else { } else {
error_str = LIT("Unknown error"); error_str = LIT("Unknown error");
if (error_blob) { if (error_blob) {
u64 error_cstr_len = ID3D10Blob_GetBufferSize(error_blob); u64 error_blob_cstr_len = ID3D10Blob_GetBufferSize(error_blob);
char *error_cstr = (char *)ID3D10Blob_GetBufferPointer(error_blob); char *error_blob_cstr = (char *)ID3D10Blob_GetBufferPointer(error_blob);
error_str = string_copy(arena, string_from_cstr(error_cstr, error_cstr_len)); struct string error_blob_str = string_copy(arena, string_from_cstr(error_blob_cstr, error_blob_cstr_len));
if (string_ends_with(error_blob_str, LIT("\n"))) {
/* Remove trailing newline */
error_blob_str.len -= 1;
}
if (error_blob_str.len > 0) {
error_str = error_blob_str;
}
} }
} }
@ -346,7 +353,7 @@ INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_d
*old_shader = new_shader; *old_shader = new_shader;
} else { } else {
error_msg = string_format(scratch.arena, error_msg = string_format(scratch.arena,
LIT("Failed to compile shader \"%F\":\n\n%F"), LIT("Failed to compile shader \"%F\":\n%F"),
FMT_STR(name), FMT_STR(name),
FMT_STR(comp_error)); FMT_STR(comp_error));
shader_release(&new_shader); shader_release(&new_shader);

View File

@ -48,7 +48,6 @@ enum sim_ent_prop {
SEPROP_TRIGGER_NEXT_TICK, SEPROP_TRIGGER_NEXT_TICK,
SEPROP_EXPLOSION, SEPROP_EXPLOSION,
SEPROP_EXPLOSION_STARTED,
SEPROP_BULLET, SEPROP_BULLET,
SEPROP_TRACER, SEPROP_TRACER,
@ -303,6 +302,7 @@ struct sim_ent {
/* Explosion */ /* Explosion */
f32 explosion_radius; f32 explosion_radius;
u64 explosion_tick;
/* ====================================================================== */ /* ====================================================================== */
/* Tracer */ /* Tracer */

View File

@ -49,7 +49,7 @@ void sim_accel_reset(struct sim_snapshot *ss, struct sim_accel *accel)
* Test * Test
* ========================== */ * ========================== */
/* TODO: Remove this */ /* TODO: Remove this */
INTERNAL struct sim_ent *spawn_test_employee(struct sim_step_ctx *ctx) INTERNAL struct sim_ent *spawn_test_employee(struct sim_step_ctx *ctx)
{ {
@ -153,6 +153,23 @@ INTERNAL struct sim_ent *spawn_test_camera(struct sim_snapshot *world, struct si
return camera_ent; return camera_ent;
} }
INTERNAL struct sim_ent *spawn_test_explosion(struct sim_snapshot *world, struct v2 point, f32 radius)
{
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
struct sim_ent *ent = sim_ent_alloc_sync_src(root);
sim_ent_set_xform(ent, XFORM_POS(point));
sim_ent_enable_prop(ent, SEPROP_EXPLOSION);
ent->explosion_radius = radius;
sim_ent_enable_prop(ent, SEPROP_SENSOR);
ent->local_collider.count = 1;
ent->local_collider.radius = radius;
return ent;
}
INTERNAL void spawn_test_entities(struct sim_step_ctx *ctx, struct v2 offset) INTERNAL void spawn_test_entities(struct sim_step_ctx *ctx, struct v2 offset)
{ {
struct sim_snapshot *world = ctx->world; struct sim_snapshot *world = ctx->world;
@ -257,30 +274,25 @@ INTERNAL void test_clear_level(struct sim_step_ctx *ctx)
* Respond to physics collisions * Respond to physics collisions
* ========================== */ * ========================== */
INTERNAL void on_collision(struct sim_ent *e0, struct sim_ent *e1, struct phys_collision_data *data, struct sim_step_ctx *step_ctx) INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, data, step_ctx)
{ {
struct sim_snapshot *world = step_ctx->world; struct sim_snapshot *world = step_ctx->world;
struct sim_ent *e0 = sim_ent_from_id(world, data->e0);
struct sim_ent *e1 = sim_ent_from_id(world, data->e1);
struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID); struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
struct phys_contact_constraint *constraint = data->constraint;
if (sim_ent_should_simulate(e0) && sim_ent_should_simulate(e1)) { if (sim_ent_should_simulate(e0) && sim_ent_should_simulate(e1)) {
/* Bullet impact */ /* Bullet impact */
if (sim_ent_has_prop(e0, SEPROP_BULLET)) { if (sim_ent_has_prop(e0, SEPROP_BULLET)) {
struct v2 normal = data->normal; /* Impact normal */ struct v2 normal = data->normal; /* Impact normal */
struct v2 vrel = v2_neg(data->vrel); /* Impact velocity */ struct v2 vrel = data->vrel; /* Impact velocity */
struct sim_ent *bullet = e0; struct sim_ent *bullet = e0;
struct sim_ent *target = e1; struct sim_ent *target = e1;
struct sim_ent *src = sim_ent_from_id(world, bullet->bullet_src); struct sim_ent *src = sim_ent_from_id(world, bullet->bullet_src);
if (bullet->bullet_has_hit || sim_ent_id_eq(src->top, target->top)) { /* Process collision if bullet already spent or * target share same top level parent */
/* Ignore collision if bullet already spent or if weapon and if (!bullet->bullet_has_hit && !sim_ent_id_eq(src->top, target->top)) {
* target share same top level parent */
/* NOTE: Since bullet is most likely just a sensor skip_solve is probably already true */
constraint->skip_solve = true;
} else {
struct v2 point = data->point; struct v2 point = data->point;
/* Update tracer */ /* Update tracer */
@ -323,12 +335,7 @@ INTERNAL void on_collision(struct sim_ent *e0, struct sim_ent *e1, struct phys_c
/* Create explosion */ /* Create explosion */
if (bullet->bullet_impact_explosion_radius > 0) { if (bullet->bullet_impact_explosion_radius > 0) {
struct sim_ent *exp = sim_ent_alloc_sync_src(root); spawn_test_explosion(world, point, bullet->bullet_impact_explosion_radius);
sim_ent_set_xform(exp, XFORM_POS(point));
sim_ent_enable_prop(exp, SEPROP_SENSOR);
sim_ent_enable_prop(exp, SEPROP_EXPLOSION);
exp->explosion_radius = bullet->bullet_impact_explosion_radius;
} }
/* Update bullet */ /* Update bullet */
@ -336,20 +343,39 @@ INTERNAL void on_collision(struct sim_ent *e0, struct sim_ent *e1, struct phys_c
sim_ent_enable_prop(bullet, SEPROP_RELEASE); sim_ent_enable_prop(bullet, SEPROP_RELEASE);
} }
} }
}
}
INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(pre_solve_callback, collision_data_array, step_ctx) /* Explosion blast collision */
{ if (sim_ent_has_prop(e0, SEPROP_EXPLOSION) && (e0->explosion_tick == world->tick || e0->explosion_tick == 0)) {
struct sim_snapshot *world = step_ctx->world; struct sim_ent *exp = e0;
for (u64 i = 0; i < collision_data_array.count; ++i) { struct sim_ent *victim = e1;
struct phys_collision_data *data = &collision_data_array.a[i];
struct sim_ent *e0 = sim_ent_from_id(world, data->e0); exp->explosion_tick = world->tick;
struct sim_ent *e1 = sim_ent_from_id(world, data->e1); sim_ent_disable_prop(exp, SEPROP_SENSOR);
if (sim_ent_is_valid_and_active(e0) && sim_ent_is_valid_and_active(e1)) {
on_collision(e0, e1, data, step_ctx); struct xform xf = sim_ent_get_xform(exp);
if (sim_ent_is_valid_and_active(e1) && sim_ent_is_valid_and_active(e0)) {
on_collision(e1, e0, data, step_ctx); f32 radius = exp->explosion_radius;
struct collider_shape origin_collider = ZI;
origin_collider.count = 1;
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 v2 dir = v2_sub(closest_points.p1, closest_points.p0);
f32 distance = v2_len(dir);
if (closest_points.colliding) {
dir = v2_neg(dir);
distance = 0;
} else {
distance = v2_len(dir);
}
/* TODO: Blast obstruction */
if (distance <= radius) {
/* TODO: Exponential decay */
f32 strength = 100;
f32 ratio = (radius - distance) / radius;
struct v2 impulse = v2_with_len(dir, strength * ratio);
sim_ent_apply_linear_impulse(victim, impulse, closest_points.p1);
} }
} }
} }
@ -584,11 +610,7 @@ void sim_step(struct sim_step_ctx *ctx)
if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) { if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) {
if (!(old_control.flags & SIM_CONTROL_FLAG_EXPLODE_TEST)) { if (!(old_control.flags & SIM_CONTROL_FLAG_EXPLODE_TEST)) {
logf_info("Explosion (test)"); logf_info("Explosion (test)");
struct sim_ent *exp = sim_ent_alloc_sync_src(root); spawn_test_explosion(world, player->player_cursor_pos, 1);
sim_ent_set_xform(exp, XFORM_POS(player->player_cursor_pos));
sim_ent_enable_prop(exp, SEPROP_EXPLOSION);
exp->explosion_radius = 50;
} }
} }
@ -1228,64 +1250,10 @@ void sim_step(struct sim_step_ctx *ctx)
{ {
struct phys_step_ctx phys = ZI; struct phys_step_ctx phys = ZI;
phys.sim_step_ctx = ctx; phys.sim_step_ctx = ctx;
phys.pre_solve_callback = pre_solve_callback; phys.pre_solve_callback = on_collision;
phys_step(&phys, sim_dt); phys_step(&phys, sim_dt);
} }
/* ========================== *
* Update explosions
* ========================== */
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
struct sim_ent *ent = &world->ents[ent_index];
if (!sim_ent_should_simulate(ent)) continue;
if (sim_ent_has_prop(ent, SEPROP_EXPLOSION)) {
struct xform xf = sim_ent_get_xform(ent);
if (!sim_ent_has_prop(ent, SEPROP_EXPLOSION_STARTED)) {
f32 radius = ent->explosion_radius;
sim_ent_enable_prop(ent, SEPROP_EXPLOSION_STARTED);
struct collider_shape origin_collider = ZI;
origin_collider.count = 1;
struct aabb aabb = ZI;
{
struct collider_shape radius_collider = ZI;
radius_collider.radius = radius;
radius_collider.count = 1;
ent->local_collider = radius_collider;
aabb = collider_aabb_from_collider(&radius_collider, xf);
}
/* Find ents in blast range */
struct space_iter iter = space_iter_begin_aabb(ctx->accel->space, aabb);
struct space_entry *space_entry;
while ((space_entry = space_iter_next(&iter))) {
struct sim_ent *victim = sim_ent_from_id(world, space_entry->ent);
if (sim_ent_should_simulate(victim) && sim_ent_has_prop(victim, SEPROP_PHYSICAL_DYNAMIC) && !sim_ent_id_eq(ent->id, victim->id)) {
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 v2 dir = v2_sub(closest_points.p1, closest_points.p0);
f32 distance = v2_len(dir);
/* TODO: Blast obstruction */
if (distance <= radius) {
/* TODO: Exponential decay */
//f32 ratio = (radius - distance) / radius;
//f32 strength = 100;
//struct v2 impulse = v2_with_len(dir, strength * ratio);
struct v2 impulse = V2(-100, 0);
//sim_ent_apply_linear_impulse(victim, impulse, closest_points.p1);
sim_ent_apply_linear_impulse_to_center(victim, impulse);
}
}
}
space_iter_end(iter);
}
}
}
/* ========================== * /* ========================== *
* Update tracers * Update tracers
* ========================== */ * ========================== */