bullet raycast against spatial grid
This commit is contained in:
parent
19b7601c5f
commit
45ac2bf1af
182
src/pp/pp.c
182
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,9 +2764,9 @@ 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
|
||||
}
|
||||
|
||||
if (can_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);
|
||||
|
||||
22
src/pp/pp.h
22
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
|
||||
|
||||
@ -1963,22 +1963,17 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
if (frame->is_looking)
|
||||
{
|
||||
frame->screen_cursor = frame->screen_crosshair;
|
||||
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->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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user