From 855fa051d0b16f75ba0cd8ec776f0efc401c2fcb Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 16 May 2025 08:15:21 -0500 Subject: [PATCH] start explosion work --- src/config.h | 4 +- src/phys.c | 19 ++-- src/sim.h | 1 + src/sim_ent.h | 9 ++ src/sim_step.c | 243 +++++++++++++++++++++++++++++++++---------------- src/user.c | 7 +- src/user.h | 1 + 7 files changed, 192 insertions(+), 92 deletions(-) diff --git a/src/config.h b/src/config.h index 2971cbf7..d60584f3 100644 --- a/src/config.h +++ b/src/config.h @@ -37,10 +37,10 @@ /* 64^2 = 4096 bins */ #define SPACE_CELL_BINS_SQRT (64) -#define SPACE_CELL_SIZE (1.0f) +#define SPACE_CELL_SIZE (4) #define SIM_TILES_PER_UNIT_SQRT (2) -#define SIM_TILES_PER_CHUNK_SQRT (16) +#define SIM_TILES_PER_CHUNK_SQRT (1) #define SIM_TICKS_PER_SECOND 50 //#define SIM_TIMESCALE 1 diff --git a/src/phys.c b/src/phys.c index fd0a1e60..f8b0f93c 100644 --- a/src/phys.c +++ b/src/phys.c @@ -1045,21 +1045,22 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_step_ctx *ctx, f32 step_ void phys_update_aabbs(struct phys_step_ctx *ctx) { + __prof; struct sim_snapshot *ss = ctx->sim_step_ctx->world; struct space *space = ctx->sim_step_ctx->accel->space; for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) { struct sim_ent *ent = &ss->ents[sim_ent_index]; if (!sim_ent_is_valid_and_active(ent)) continue; - if (ent->local_collider.count <= 0) continue; - - struct xform xf = sim_ent_get_xform(ent); - struct space_entry *space_entry = space_entry_from_handle(space, ent->space_handle); - if (!space_entry->valid) { - space_entry = space_entry_alloc(space, ent->id); - ent->space_handle = space_entry->handle; + if (ent->local_collider.count > 0) { + struct xform xf = sim_ent_get_xform(ent); + struct space_entry *space_entry = space_entry_from_handle(space, ent->space_handle); + if (!space_entry->valid) { + space_entry = space_entry_alloc(space, ent->id); + ent->space_handle = space_entry->handle; + } + struct aabb aabb = collider_aabb_from_collider(&ent->local_collider, xf); + space_entry_update_aabb(space_entry, aabb); } - struct aabb aabb = collider_aabb_from_collider(&ent->local_collider, xf); - space_entry_update_aabb(space_entry, aabb); } } diff --git a/src/sim.h b/src/sim.h index c102193b..1eea46d8 100644 --- a/src/sim.h +++ b/src/sim.h @@ -141,6 +141,7 @@ enum sim_control_flag { SIM_CONTROL_FLAG_CLEAR_ALL = 1 << 2, SIM_CONTROL_FLAG_SPAWN_TEST = 1 << 3, SIM_CONTROL_FLAG_TILE_TEST = 1 << 4, + SIM_CONTROL_FLAG_EXPLODE_TEST = 1 << 5, }; struct sim_control { diff --git a/src/sim_ent.h b/src/sim_ent.h index dcc3e393..640dcb59 100644 --- a/src/sim_ent.h +++ b/src/sim_ent.h @@ -47,6 +47,9 @@ enum sim_ent_prop { SEPROP_TRIGGERED_THIS_TICK, SEPROP_TRIGGER_NEXT_TICK, + SEPROP_EXPLOSION, + SEPROP_EXPLOSION_STARTED, + SEPROP_BULLET, SEPROP_TRACER, @@ -293,8 +296,14 @@ struct sim_ent { struct v2 bullet_src_dir; f32 bullet_impulse; f32 bullet_knockback; + f32 bullet_impact_explosion_radius; b32 bullet_has_hit; + /* ====================================================================== */ + /* Explosion */ + + f32 explosion_radius; + /* ====================================================================== */ /* Tracer */ diff --git a/src/sim_step.c b/src/sim_step.c index e48e11f1..4a8c15df 100644 --- a/src/sim_step.c +++ b/src/sim_step.c @@ -23,7 +23,7 @@ struct sim_accel sim_accel_alloc(void) { struct sim_accel accel = ZI; - accel.space = space_alloc(1, 256); + accel.space = space_alloc(SPACE_CELL_SIZE, SPACE_CELL_BINS_SQRT); return accel; } @@ -94,8 +94,10 @@ INTERNAL struct sim_ent *spawn_test_employee(struct sim_step_ctx *ctx) sim_ent_set_xform(e, xf); +#if 0 e->linear_ground_friction = 250; e->angular_ground_friction = 200; +#endif e->friction = 0; @@ -187,7 +189,9 @@ INTERNAL void spawn_test_entities(struct sim_step_ctx *ctx, struct v2 offset) /* Enemy */ if (ctx->is_master) { struct sim_ent *e = spawn_test_employee(ctx); - (UNUSED)e; + struct xform xf = sim_ent_get_xform(e); + xf.og = offset; + sim_ent_set_xform(e, xf); } /* Big box */ @@ -253,88 +257,99 @@ INTERNAL void test_clear_level(struct sim_step_ctx *ctx) * Respond to physics collisions * ========================== */ -INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, step_ctx) +INTERNAL void on_collision(struct sim_ent *e0, struct sim_ent *e1, struct phys_collision_data *data, struct sim_step_ctx *step_ctx) { + struct sim_snapshot *world = step_ctx->world; 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)) { + + /* Bullet impact */ + if (sim_ent_has_prop(e0, SEPROP_BULLET)) { + struct v2 normal = data->normal; /* Impact normal */ + struct v2 vrel = v2_neg(data->vrel); /* Impact velocity */ + + struct sim_ent *bullet = e0; + struct sim_ent *target = e1; + 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)) { + /* Ignore collision if bullet already spent or if weapon and + * 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; + + /* Update tracer */ + struct sim_ent *tracer = sim_ent_from_id(world, bullet->bullet_tracer); + if (sim_ent_should_simulate(tracer)) { + struct xform xf = sim_ent_get_xform(tracer); + xf.og = point; + sim_ent_set_xform(tracer, xf); + sim_ent_set_linear_velocity(tracer, V2(0, 0)); + } + + /* Update target */ + struct v2 knockback = v2_mul(v2_norm(vrel), bullet->bullet_knockback); + sim_ent_apply_linear_impulse(target, knockback, point); + + /* Create test blood */ + /* TODO: Remove this */ + { + struct xform xf = XFORM_TRS(.t = point, .r = rand_f64_from_state(&step_ctx->rand, 0, TAU)); + struct sim_ent *decal = sim_ent_alloc_sync_src(root); + decal->sprite = sprite_tag_from_path(LIT("res/graphics/blood.ase")); + decal->sprite_tint = RGBA_32_F(1, 1, 1, 0.25f); + decal->layer = SIM_LAYER_FLOOR_DECALS; + sim_ent_set_xform(decal, xf); + + f32 perp_range = 0.5; + struct v2 linear_velocity = v2_mul(normal, 0.5); + linear_velocity = v2_add(linear_velocity, v2_mul(v2_perp(normal), rand_f64_from_state(&step_ctx->rand, -perp_range, perp_range))); + + f32 angular_velocity_range = 5; + f32 angular_velocity = rand_f64_from_seed(&step_ctx->rand, -angular_velocity_range, angular_velocity_range); + + sim_ent_enable_prop(decal, SEPROP_PHYSICAL_KINEMATIC); + sim_ent_set_linear_velocity(decal, linear_velocity); + sim_ent_set_angular_velocity(decal, angular_velocity); + + decal->linear_damping = 5.0f; + decal->angular_damping = 5.0f; + } + + /* Create explosion */ + if (bullet->bullet_impact_explosion_radius > 0) { + struct sim_ent *exp = sim_ent_alloc_sync_src(root); + 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 */ + bullet->bullet_has_hit = true; + sim_ent_enable_prop(bullet, SEPROP_RELEASE); + } + } + } +} + +INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(pre_solve_callback, collision_data_array, step_ctx) +{ + struct sim_snapshot *world = step_ctx->world; for (u64 i = 0; i < collision_data_array.count; ++i) { struct phys_collision_data *data = &collision_data_array.a[i]; - - struct phys_contact_constraint *constraint = data->constraint; struct sim_ent *e0 = sim_ent_from_id(world, data->e0); struct sim_ent *e1 = sim_ent_from_id(world, data->e1); - - if (sim_ent_should_simulate(e0) && sim_ent_should_simulate(e1)) { - /* Bullet hit entity */ - if (sim_ent_has_prop(e0, SEPROP_BULLET) || sim_ent_has_prop(e1, SEPROP_BULLET)) { - struct v2 normal = data->normal; /* Impact normal */ - struct v2 vrel = v2_neg(data->vrel); /* Impact velocity */ - - struct sim_ent *target = e0; - struct sim_ent *bullet = e1; - if (sim_ent_has_prop(e0, SEPROP_BULLET)) { - target = e1; - bullet = e0; - normal = v2_neg(normal); - vrel = v2_neg(vrel); - } - 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)) { - /* Ignore collision if bullet already spent or if weapon and - * 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; - - /* Update tracer */ - struct sim_ent *tracer = sim_ent_from_id(world, bullet->bullet_tracer); - if (sim_ent_should_simulate(tracer)) { - struct xform xf = sim_ent_get_xform(tracer); - xf.og = point; - sim_ent_set_xform(tracer, xf); - sim_ent_set_linear_velocity(tracer, V2(0, 0)); - } - - /* Update target */ - struct v2 knockback = v2_mul(v2_norm(vrel), bullet->bullet_knockback); - sim_ent_apply_linear_impulse(target, knockback, point); - - /* Explode */ - //if (sim_ent_has_prop(bullet, SEPROP_EXPLODE_ON_IMPACT)) { - //} - - /* Create test blood */ - /* TODO: Remove this */ - { - struct xform xf = XFORM_TRS(.t = point, .r = rand_f64_from_state(&step_ctx->rand, 0, TAU)); - struct sim_ent *decal = sim_ent_alloc_sync_src(root); - decal->sprite = sprite_tag_from_path(LIT("res/graphics/blood.ase")); - decal->sprite_tint = RGBA_32_F(1, 1, 1, 0.25f); - decal->layer = SIM_LAYER_FLOOR_DECALS; - sim_ent_set_xform(decal, xf); - - f32 perp_range = 0.5; - struct v2 linear_velocity = v2_mul(normal, 0.5); - linear_velocity = v2_add(linear_velocity, v2_mul(v2_perp(normal), rand_f64_from_state(&step_ctx->rand, -perp_range, perp_range))); - - f32 angular_velocity_range = 5; - f32 angular_velocity = rand_f64_from_seed(&step_ctx->rand, -angular_velocity_range, angular_velocity_range); - - sim_ent_enable_prop(decal, SEPROP_PHYSICAL_KINEMATIC); - sim_ent_set_linear_velocity(decal, linear_velocity); - sim_ent_set_angular_velocity(decal, angular_velocity); - - decal->linear_damping = 5.0f; - decal->angular_damping = 5.0f; - } - - /* Update bullet */ - bullet->bullet_has_hit = true; - //sim_ent_enable_prop(bullet, SEPROP_RELEASE); - } + if (sim_ent_is_valid_and_active(e0) && sim_ent_is_valid_and_active(e1)) { + on_collision(e0, e1, data, step_ctx); + if (sim_ent_is_valid_and_active(e1) && sim_ent_is_valid_and_active(e0)) { + on_collision(e1, e0, data, step_ctx); } } } @@ -558,12 +573,25 @@ void sim_step(struct sim_step_ctx *ctx) if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN_TEST)) { logf_info("Spawning (test)"); u32 count = 1; - f32 spread = 1; + f32 spread = 0; for (u32 j = 0; j < count; ++j) { - spawn_test_entities(ctx, V2(0, (((f32)j / (f32)count) - 0.5) * spread)); + struct v2 pos = player->player_cursor_pos; + pos.y += (((f32)j / (f32)count) - 0.5) * spread; + spawn_test_entities(ctx, pos); } } } + if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) { + if (!(old_control.flags & SIM_CONTROL_FLAG_EXPLODE_TEST)) { + logf_info("Explosion (test)"); + struct sim_ent *exp = sim_ent_alloc_sync_src(root); + sim_ent_set_xform(exp, XFORM_POS(player->player_cursor_pos)); + + sim_ent_enable_prop(exp, SEPROP_EXPLOSION); + exp->explosion_radius = 50; + } + } + if (flags & SIM_CONTROL_FLAG_TILE_TEST) { #if 0 if (is_master) { @@ -901,7 +929,7 @@ void sim_step(struct sim_step_ctx *ctx) bullet->bullet_src_dir = rel_dir; //bullet->bullet_impulse = 0.75f; bullet->bullet_impulse = 2.0f; - bullet->bullet_knockback = 10; + //bullet->bullet_knockback = 10; bullet->mass_unscaled = 0.04f; bullet->inertia_unscaled = 0.00001f; bullet->layer = SIM_LAYER_BULLETS; @@ -951,6 +979,7 @@ void sim_step(struct sim_step_ctx *ctx) //bullet->bullet_impulse = 0.75f; bullet->bullet_impulse = 0.75; bullet->bullet_knockback = 10; + bullet->bullet_impact_explosion_radius = 1.0; bullet->mass_unscaled = 0.04f; bullet->inertia_unscaled = 0.00001f; bullet->layer = SIM_LAYER_BULLETS; @@ -1199,10 +1228,64 @@ void sim_step(struct sim_step_ctx *ctx) { struct phys_step_ctx phys = ZI; phys.sim_step_ctx = ctx; - phys.pre_solve_callback = on_collision; + phys.pre_solve_callback = pre_solve_callback; 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 * ========================== */ diff --git a/src/user.c b/src/user.c index 3ac06bf9..c9b3afeb 100644 --- a/src/user.c +++ b/src/user.c @@ -145,6 +145,7 @@ GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = { [SYS_BTN_M4] = USER_BIND_KIND_TILE_TEST, [SYS_BTN_M2] = USER_BIND_KIND_DEBUG_DRAG, + [SYS_BTN_F] = USER_BIND_KIND_DEBUG_EXPLODE, [SYS_BTN_C] = USER_BIND_KIND_DEBUG_CLEAR, [SYS_BTN_V] = USER_BIND_KIND_DEBUG_SPAWN, [SYS_BTN_N] = USER_BIND_KIND_DEBUG_STEP, @@ -1590,9 +1591,10 @@ INTERNAL void user_update(void) struct bind_state drag_state = G.bind_states[USER_BIND_KIND_DEBUG_DRAG]; struct bind_state clear_state = G.bind_states[USER_BIND_KIND_DEBUG_CLEAR]; struct bind_state spawn_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN]; - struct bind_state tile_state = G.bind_states[USER_BIND_KIND_TILE_TEST]; struct bind_state pause_state = G.bind_states[USER_BIND_KIND_DEBUG_PAUSE]; struct bind_state step_state = G.bind_states[USER_BIND_KIND_DEBUG_STEP]; + struct bind_state tile_state = G.bind_states[USER_BIND_KIND_TILE_TEST]; + struct bind_state explode_state = G.bind_states[USER_BIND_KIND_DEBUG_EXPLODE]; if (fire_state.num_presses || fire_state.is_held) { control.flags |= SIM_CONTROL_FLAG_FIRE; @@ -1609,6 +1611,9 @@ INTERNAL void user_update(void) if (tile_state.num_presses) { control.flags |= SIM_CONTROL_FLAG_TILE_TEST; } + if (explode_state.num_presses) { + control.flags |= SIM_CONTROL_FLAG_EXPLODE_TEST; + } if (pause_state.num_presses) { atomic_i32_eval_xor(&G.user_paused, 1); diff --git a/src/user.h b/src/user.h index 79f8f664..928aa003 100644 --- a/src/user.h +++ b/src/user.h @@ -36,6 +36,7 @@ enum user_bind_kind { USER_BIND_KIND_DEBUG_PAUSE, USER_BIND_KIND_DEBUG_STEP, USER_BIND_KIND_DEBUG_DRAG, + USER_BIND_KIND_DEBUG_EXPLODE, USER_BIND_KIND_FULLSCREEN, USER_BIND_KIND_ZOOM_IN, USER_BIND_KIND_ZOOM_OUT,