diff --git a/src/pp/pp.c b/src/pp/pp.c index ab8c3898..93dbe434 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -1233,10 +1233,10 @@ P_Space P_SpaceFromEnts(Arena *arena, P_Frame *frame) { i64 cell_idx = y * P_WorldPitch + x; P_SpaceCell *cell = &space.cells[cell_idx]; - P_SpaceEntry *entry = PushStruct(arena, P_SpaceEntry); - SllStackPush(cell->first, entry); - entry->shape = shape; - entry->shape_id = ent->key.v; + P_SpaceEntryNode *entry_node = PushStruct(arena, P_SpaceEntryNode); + SllStackPush(cell->first, entry_node); + entry_node->entry.shape = shape; + entry_node->entry.shape_id = ent->key.v; } } } @@ -1386,22 +1386,25 @@ P_Space P_SpaceFromWalls(Arena *arena, P_Frame *frame) aabb.p1.x = MaxF32(aabb.p1.x, aabb.p0.x + 1); aabb.p1.y = MaxF32(aabb.p1.y, aabb.p0.y + 1); aabb = IntersectRng2(aabb, space_aabb); - u64 id = P_WallShapeIDBasis ^ MixU64s( - ((u64)wall->start.x << 0) | ((u64)wall->start.y << 32), - ((u64)wall->end.x << 0) | ((u64)wall->end.y << 32) - ); + u64 id = P_WallShapeIDBasis ^ wall->dir; + id = MixU64s(id, (((u64)wall->start.x) | ((u64)wall->start.y << 32))); + id = MixU64s(id, (((u64)wall->end.x) | ((u64)wall->end.y << 32))); + + // FIXME: Remove this + // TrueRand(StringFromStruct(&id)); + for (i32 y = aabb.p0.y; y < aabb.p1.y; ++y) { for (i32 x = aabb.p0.x; x < aabb.p1.x; ++x) { i64 cell_idx = y * P_WorldPitch + x; P_SpaceCell *cell = &space.cells[cell_idx]; - P_SpaceEntry *entry = PushStruct(arena, P_SpaceEntry); - SllStackPush(cell->first, entry); - entry->shape = shape; - entry->shape_id = id; - entry->dir.x = (wall->dir == WallDir_Right) + ((wall->dir == WallDir_Left) * -1); - entry->dir.y = (wall->dir == WallDir_Down) + ((wall->dir == WallDir_Up) * -1); + P_SpaceEntryNode *entry_node = PushStruct(arena, P_SpaceEntryNode); + SllStackPush(cell->first, entry_node); + entry_node->entry.shape = shape; + entry_node->entry.shape_id = id; + entry_node->entry.dir.x = (wall->dir == WallDir_Right) + ((wall->dir == WallDir_Left) * -1); + entry_node->entry.dir.y = (wall->dir == WallDir_Down) + ((wall->dir == WallDir_Up) * -1); } } } @@ -1422,6 +1425,103 @@ P_SpaceCell P_SpaceCellFromPos(P_Space *space, Vec2 pos) return result; } +void P_UniqueSpaceEntriesFromRay(Arena *arena, P_SpaceEntryList *result, i32 spaces_count, P_Space **spaces, Vec2 ray_p0, Vec2 ray_p1) +{ + TempArena scratch = BeginScratch(arena); + { + Struct(BinEntry) { BinEntry *next; u64 shape_id; }; + u64 bins_count = 256; + BinEntry **bins = PushStructs(scratch.arena, BinEntry *, bins_count); + + // TODO: Clip to avoid unnecessary iterations outside of world bounds + ray_p0 = AddVec2(ray_p0, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0)); + ray_p1 = AddVec2(ray_p1, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0)); + + Vec2I32 grid_start = Vec2I32FromVec(FloorVec2(ray_p0)); + Vec2I32 grid_end = Vec2I32FromVec(FloorVec2(ray_p1)); + + Vec2 delta = SubVec2(ray_p1, ray_p0); + Vec2 inv_delta = RecipVec2(delta); + Vec2 step_dir = VEC2((delta.x > 0) - (delta.x < 0), (delta.y > 0) - (delta.y < 0)); + Vec2 t_delta = MulVec2Vec2(step_dir, inv_delta); + Vec2 t_max = SubVec2(Vec2FromVec(grid_start), ray_p0); + t_max.x += step_dir.x > 0; + t_max.y += step_dir.y > 0; + t_max = MulVec2Vec2(t_max, inv_delta); + + Vec2I32 grid_pos = grid_start; + b32 done = 0; + while (!done) + { + if (grid_pos.x >= 0 && grid_pos.y >= 0 && grid_pos.x < P_WorldPitch && grid_pos.y < P_WorldPitch) + { + if (P_tl.debug_draw_enabled) + { + Vec2 world_pos = Vec2FromVec(grid_pos); + world_pos = SubVec2(world_pos, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0)); + P_DebugDrawRect(RNG2(world_pos, AddVec2(world_pos, VEC2(1, 1))), Color_Cyan); + } + for (i32 space_idx = 0; space_idx < spaces_count; ++space_idx) + { + P_Space *space = spaces[space_idx]; + if (grid_pos.x < space->dims.x && grid_pos.y < space->dims.y) + { + i64 cell_idx = grid_pos.y * P_WorldPitch + grid_pos.x; + P_SpaceCell cell = space->cells[cell_idx]; + for ( + P_SpaceEntryNode *src_space_entry_node = cell.first; + src_space_entry_node; + src_space_entry_node = src_space_entry_node->next + ) + { + P_SpaceEntry *src_space_entry = &src_space_entry_node->entry; + BinEntry **bin = &bins[src_space_entry->shape_id % bins_count]; + BinEntry *bin_entry = *bin; + for (; bin_entry; bin_entry = bin_entry->next) + { + if (bin_entry->shape_id == src_space_entry->shape_id) + { + break; + } + } + if (!bin_entry) + { + // Entry is unique + { + bin_entry = PushStruct(scratch.arena, BinEntry); + bin_entry->shape_id = src_space_entry->shape_id; + SllStackPush(*bin, bin_entry); + } + { + P_SpaceEntryNode *dst = PushStruct(arena, P_SpaceEntryNode); + dst->entry = *src_space_entry; + SllQueuePush(result->first, result->last, dst); + ++result->count; + } + } + } + } + } + } + if (grid_pos.x == grid_end.x && grid_pos.y == grid_end.y) + { + done = 1; + } + else if (t_max.x < t_max.y) + { + grid_pos.x += step_dir.x; + t_max.x += t_delta.x; + } + else + { + grid_pos.y += step_dir.y; + t_max.y += t_delta.y; + } + } + } + EndScratch(scratch); +} + //////////////////////////////////////////////////////////// //~ List helpers @@ -2093,8 +2193,9 @@ void P_StepFrame(P_Frame *frame) for (i64 cell_idx = 0; cell_idx < countof(cells); ++cell_idx) { P_SpaceCell cell = cells[cell_idx]; - for (P_SpaceEntry *space_entry = cell.first; space_entry; space_entry = space_entry->next) + for (P_SpaceEntryNode *space_entry_node = cell.first; space_entry_node; space_entry_node = space_entry_node->next) { + P_SpaceEntry *space_entry = &space_entry_node->entry; Rng2 aabb1 = P_BoundingBoxFromShape(space_entry->shape); P_Ent *ent1 = P_EntFromKey(frame, (P_EntKey) { .v = space_entry->shape_id }); if (!P_MatchEntKey(ent0->key, ent1->key) && IsIntersectingRng2(aabb0, aabb1)) @@ -2570,7 +2671,7 @@ void P_StepFrame(P_Frame *frame) ////////////////////////////// //- Build post-solve space from ents - // P_Space post_solve_ents_space = P_SpaceFromEnts(scratch.arena, frame); + P_Space post_solve_ents_space = P_SpaceFromEnts(scratch.arena, frame); ////////////////////////////// //- Move bullets @@ -2596,8 +2697,8 @@ void P_StepFrame(P_Frame *frame) P_EntList bullets_to_spawn = Zi; for (P_Ent *firer = P_FirstEnt(frame); !P_IsEntNil(firer); firer = P_NextEnt(firer)) { - if (firer->is_guy && firer->control.fire_held) - // if (firer->fire_presses) + if (firer->has_weapon && firer->control.fire_held) + // if (firer->has_weapon && firer->control.fire_presses) { // i64 fire_delta_ns = frame->time_ns - firer->last_fire_ns; @@ -2610,6 +2711,7 @@ void P_StepFrame(P_Frame *frame) f32 spread = Tau * 0.05; // f32 spread = Tau * 0.01; f32 tweak_speed = TweakFloat("Bullet speed", 100, 1, 100); + // f32 tweak_speed = TweakFloat("Bullet speed", 1, 1, 100); b32 can_fire = (firer->last_fire_ns + NsFromSeconds(1.0 / fire_rate)) <= frame->time_ns; @@ -2662,10 +2764,10 @@ void P_StepFrame(P_Frame *frame) SPR_Ray fire_ray = wep.rays[SPR_RayKind_Ap]; fire_pos = MulAffineVec2(wep_pix_to_world_af, fire_ray.pos); fire_dir = NormRot(MulAffineBasisVec2(wep_pix_to_world_af, fire_ray.dir)); - - // FIXME: Prevent obstructed weapons from firing through walls } + // FIXME: Prevent obstructed weapons from firing through walls + if (can_fire) { i64 tick_bullets_count = bullets_per_fire; @@ -2708,8 +2810,6 @@ void P_StepFrame(P_Frame *frame) ////////////////////////////// //- Update bullet hits - // TODO: Not like this - // TODO: Separate 'hits' from bullets, so that bullets can have multiple hits for (P_Ent *bullet = P_FirstEnt(frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) @@ -2723,22 +2823,37 @@ void P_StepFrame(P_Frame *frame) P_DebugDrawLine(bullet->bullet_start, bullet->bullet_end, Color_Red); - // TODO: Real raycast query - P_Ent *victim = &P_NilEnt; + P_Space *cast_spaces[] = { + &world->walls_space, + &post_solve_ents_space, + }; + + P_SpaceEntryList cast_entries = Zi; + P_UniqueSpaceEntriesFromRay( + scratch.arena, + &cast_entries, + countof(cast_spaces), + cast_spaces, + ray_start, + ray_end + ); + + P_EntKey victim_key = Zi; P_RaycastResult victim_raycast = Zi; { f32 closest_len_sq = Inf; - for (P_Ent *potential_victim = P_FirstEnt(frame); !P_IsEntNil(potential_victim); potential_victim = P_NextEnt(potential_victim)) + for (P_SpaceEntryNode *entry_node = cast_entries.first; entry_node; entry_node = entry_node->next) { - if (potential_victim->is_guy && !P_MatchEntKey(potential_victim->key, bullet->bullet_firer)) + P_SpaceEntry *entry = &entry_node->entry; + P_EntKey potential_victim_key = (P_EntKey) { .v = entry->shape_id }; + if (!P_MatchEntKey(potential_victim_key, bullet->bullet_firer)) { - P_Shape victim_world_shape = P_WorldShapeFromEnt(potential_victim); - - P_RaycastResult entrance_raycast = P_RaycastShape(victim_world_shape, ray_start, ray_dir); + P_Shape potential_victim_shape = entry->shape; + P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, ray_start, ray_dir); Vec2 entrance = entrance_raycast.p; if (entrance_raycast.is_intersecting) { - P_RaycastResult exit_raycast = P_RaycastShape(victim_world_shape, ray_start, NegVec2(ray_dir)); + P_RaycastResult exit_raycast = P_RaycastShape(potential_victim_shape, ray_start, NegVec2(ray_dir)); Vec2 exit = exit_raycast.p; f32 da = DotVec2(ray_dir, SubVec2(entrance, ray_start)); f32 db = DotVec2(ray_dir, SubVec2(exit, ray_start)); @@ -2748,7 +2863,7 @@ void P_StepFrame(P_Frame *frame) if (len_sq < closest_len_sq) { closest_len_sq = len_sq; - victim = potential_victim; + victim_key = potential_victim_key; victim_raycast = entrance_raycast; } } @@ -2756,20 +2871,25 @@ void P_StepFrame(P_Frame *frame) } } } + P_Ent *victim = P_EntFromKey(frame, victim_key); // TODO: Truncate bullet trail - if (!P_IsEntNil(victim)) + if (!P_IsEntKeyNil(victim_key)) { bullet->has_hit = 1; bullet->hit_entry = victim_raycast.p; bullet->hit_entry_normal = victim_raycast.normal; // bullet->bullet_end = bullet->hit_entry; bullet->exists = 0; + } + if (!P_IsEntNil(victim)) + { // TODO: Remove this victim->health -= 0.25; } + // Prune out of bounds bullet Rng2 bounds = Zi; bounds.p0 = VEC2(-P_WorldPitch / 2, -P_WorldPitch / 2); bounds.p1 = VEC2(P_WorldPitch / 2, P_WorldPitch / 2); diff --git a/src/pp/pp.h b/src/pp/pp.h index b3149396..b9da36e8 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -125,6 +125,8 @@ Struct(P_Ent) i64 last_fire_ns; b32 has_weapon; + //- Bullet + P_EntKey bullet_firer; b32 is_bullet; Vec2 bullet_start; @@ -254,15 +256,27 @@ Struct(P_ConstraintBin) Struct(P_SpaceEntry) { - P_SpaceEntry *next; - P_Shape shape; u64 shape_id; + P_Shape shape; Vec2 dir; }; +Struct(P_SpaceEntryNode) +{ + P_SpaceEntryNode *next; + P_SpaceEntry entry; +}; + +Struct(P_SpaceEntryList) +{ + i64 count; + P_SpaceEntryNode *first; + P_SpaceEntryNode *last; +}; + Struct(P_SpaceCell) { - P_SpaceEntry *first; + P_SpaceEntryNode *first; }; Struct(P_Space) @@ -610,7 +624,9 @@ P_Constraint *P_NextConstraint(P_Constraint *c); P_Space P_SpaceFromEnts(Arena *arena, P_Frame *frame); P_Space P_SpaceFromWalls(Arena *arena, P_Frame *frame); + P_SpaceCell P_SpaceCellFromPos(P_Space *space, Vec2 pos); +void P_UniqueSpaceEntriesFromRay(Arena *arena, P_SpaceEntryList *result, i32 spaces_count, P_Space **spaces, Vec2 ray_p0, Vec2 ray_p1); //////////////////////////////////////////////////////////// //~ List helpers diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 67a0633d..6944ca6f 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -1963,21 +1963,16 @@ void V_TickForever(WaveLaneCtx *lane) if (frame->is_looking) { frame->screen_cursor = frame->screen_crosshair; - WND_PushCmd(window_frame, .kind = WND_CmdKind_SetLockedCursor, .v = 1); - WND_SetCursor(window_frame, WND_CursorKind_Hidden); + if (window_frame.has_focus) + { + WND_PushCmd(window_frame, .kind = WND_CmdKind_SetLockedCursor, .v = 1); + WND_SetCursor(window_frame, WND_CursorKind_Hidden); + WND_PushCmd(window_frame, .kind = WND_CmdKind_SetCursorPos, .pos = frame->screen_cursor); + } } else { - if (prev_frame->is_looking) - { - // Snap cursor to crosshair - frame->screen_cursor = prev_frame->screen_cursor; - WND_PushCmd(window_frame, .kind = WND_CmdKind_SetCursorPos, .pos = prev_frame->screen_cursor); - } - else - { - frame->screen_cursor = ui_frame->cursor_pos; - } + frame->screen_cursor = ui_frame->cursor_pos; } frame->shade_cursor = MulAffineVec2(frame->af.screen_to_shade, frame->screen_cursor); frame->world_cursor = MulAffineVec2(frame->af.screen_to_world, frame->screen_cursor); @@ -2560,7 +2555,6 @@ void V_TickForever(WaveLaneCtx *lane) // emitter.flags |= V_ParticleFlag_StainTrail; emitter.count = 128; - // emitter.count = 100; emitter.speed = 10; emitter.color_lin = LinearFromSrgb(Color_Yellow);