simulated bullet testing
This commit is contained in:
parent
3dc22b9bd7
commit
e9ea1ec0f7
@ -93,6 +93,16 @@ void S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta)
|
||||
if (0)
|
||||
{
|
||||
}
|
||||
//- Reset
|
||||
if (delta->kind == S_DeltaKind_Reset)
|
||||
{
|
||||
// FIXME: Free list entities
|
||||
world->ents_count = 0;
|
||||
world->first_ent = 0;
|
||||
world->last_ent = 0;
|
||||
ZeroStructs(world->tiles, S_TilesCount);
|
||||
ZeroStructs(world->ent_bins, world->ent_bins_count);
|
||||
}
|
||||
//- Raw ent
|
||||
if (delta->kind == S_DeltaKind_RawEnt)
|
||||
{
|
||||
@ -921,14 +931,24 @@ S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1, Ve
|
||||
return result;
|
||||
}
|
||||
|
||||
S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir)
|
||||
S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir, b32 extend)
|
||||
{
|
||||
S_RaycastResult result = Zi;
|
||||
|
||||
// HACK: Create line shape as large as the world and perform a collision test
|
||||
Vec2 ray_shape_p0 = ray_start;
|
||||
Vec2 ray_shape_p1 = AddVec2(ray_start, MulVec2(NormVec2(ray_dir), S_WorldPitch * 1.414213562));
|
||||
Vec2 ray_shape_p1;
|
||||
if (extend)
|
||||
{
|
||||
ray_shape_p1 = AddVec2(ray_start, MulVec2(NormVec2(ray_dir), S_WorldPitch * 1.414213562));
|
||||
}
|
||||
else
|
||||
{
|
||||
ray_shape_p1 = AddVec2(ray_shape_p0, ray_dir);
|
||||
}
|
||||
|
||||
S_Shape ray_shape = S_ShapeFromDesc(.count = 2, .points = { ray_shape_p0, ray_shape_p1 });
|
||||
S_DebugDrawShape(ray_shape, Color_Cyan);
|
||||
|
||||
S_CollisionResult cls = S_CollisionResultFromShapes(ray_shape, shape, ray_dir);
|
||||
result.is_intersecting = cls.collision_points_count > 0;
|
||||
@ -982,6 +1002,34 @@ S_Ent *S_NextEnt(S_Ent *e)
|
||||
return result;
|
||||
}
|
||||
|
||||
S_Ent *S_PushTempEnt(Arena *arena, S_EntList *list)
|
||||
{
|
||||
S_EntListNode *n = PushStruct(arena, S_EntListNode);
|
||||
SllQueuePush(list->first, list->last, n);
|
||||
++list->count;
|
||||
S_Ent *ent = &n->ent;
|
||||
*ent = S_NilEnt;
|
||||
ent->valid = 1;
|
||||
ent->exists = 1;
|
||||
return ent;
|
||||
}
|
||||
|
||||
void S_SpawnEntsFromList(Arena *arena, S_World *world, S_EntList ents)
|
||||
{
|
||||
// FIXME: Don't reinsert duplicates
|
||||
// FIXME: Don't insert nil keys
|
||||
for (S_EntListNode *n = ents.first; n; n = n->next)
|
||||
{
|
||||
S_Ent *src = &n->ent;
|
||||
S_Ent *dst = PushStructNoZero(arena, S_Ent);
|
||||
*dst = *src;
|
||||
S_EntBin *bin = &world->ent_bins[dst->key.v % world->ent_bins_count];
|
||||
DllQueuePush(world->first_ent, world->last_ent, dst);
|
||||
DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin);
|
||||
++world->ents_count;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Debug draw
|
||||
|
||||
@ -1144,8 +1192,8 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
// FIXME: Only accept world deltas from users that can edit
|
||||
|
||||
i64 applied_user_tile_deltas_count = 0;
|
||||
S_Delta **applied_user_tile_deltas = PushStructsNoZero(frame_arena, S_Delta *, input->cmds_count);
|
||||
i64 applied_user_deltas_count = 0;
|
||||
S_Delta **applied_user_deltas = PushStructsNoZero(frame_arena, S_Delta *, input->cmds_count);
|
||||
for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
|
||||
{
|
||||
S_Cmd *cmd = &cmd_node->cmd;
|
||||
@ -1153,6 +1201,12 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
{
|
||||
S_Delta *delta = &cmd->delta;
|
||||
b32 allow = 0;
|
||||
b32 forward = 0;
|
||||
if (delta->kind == S_DeltaKind_Reset)
|
||||
{
|
||||
allow = 1;
|
||||
forward = 1;
|
||||
}
|
||||
if (delta->kind == S_DeltaKind_RawEnt)
|
||||
{
|
||||
allow = 1;
|
||||
@ -1160,8 +1214,12 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
if (delta->kind == S_DeltaKind_Tile)
|
||||
{
|
||||
allow = 1;
|
||||
applied_user_tile_deltas[applied_user_tile_deltas_count] = delta;
|
||||
applied_user_tile_deltas_count += 1;
|
||||
forward = 1;
|
||||
}
|
||||
if (forward)
|
||||
{
|
||||
applied_user_deltas[applied_user_deltas_count] = delta;
|
||||
applied_user_deltas_count += 1;
|
||||
}
|
||||
if (allow)
|
||||
{
|
||||
@ -1407,37 +1465,120 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//- Move bullets
|
||||
|
||||
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
|
||||
{
|
||||
if (bullet->is_bullet)
|
||||
{
|
||||
Vec2 start = bullet->bullet_start;
|
||||
Vec2 end = bullet->bullet_end;
|
||||
Vec2 vel = SubVec2(end, start);
|
||||
bullet->bullet_start = end;
|
||||
bullet->bullet_end = AddVec2(end, vel);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Spawn test bullet
|
||||
//- Spawn new bullets
|
||||
|
||||
// TODO: Remove this
|
||||
|
||||
// {
|
||||
// b32 bullet_spawned = 0;
|
||||
// for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
|
||||
// {
|
||||
// if (ent->is_bullet)
|
||||
// {
|
||||
// bullet_spawned = 1;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
{
|
||||
S_EntList bullets_to_spawn = Zi;
|
||||
for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer))
|
||||
{
|
||||
if (firer->fire_held)
|
||||
{
|
||||
Xform firer_xf = firer->xf;
|
||||
S_Shape firer_world_shape = S_MulXformShape(firer_xf, firer->local_shape);
|
||||
|
||||
// if (!bullet_spawned)
|
||||
// {
|
||||
S_Ent *bullet = S_PushTempEnt(frame_arena, &bullets_to_spawn);
|
||||
bullet->is_bullet = 1;
|
||||
bullet->key = S_RandKey();
|
||||
|
||||
// }
|
||||
// }
|
||||
f32 speed = 40 * sim_dt;
|
||||
|
||||
bullet->bullet_start = firer_world_shape.centroid;
|
||||
bullet->bullet_end = AddVec2(bullet->bullet_start, MulVec2(NormVec2(firer->look), speed));
|
||||
bullet->bullet_firer = firer->key;
|
||||
}
|
||||
}
|
||||
S_SpawnEntsFromList(world_arena, world, bullets_to_spawn);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//- Update bullets
|
||||
|
||||
|
||||
//- Update bullet hits
|
||||
|
||||
// TODO: Not like this
|
||||
|
||||
// TODO: Separate 'hits' from bullets, so that bullets can have multiple hits
|
||||
|
||||
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
|
||||
{
|
||||
bullet->has_hit = 0;
|
||||
Vec2 ray_start = bullet->bullet_start;
|
||||
Vec2 ray_end = bullet->bullet_end;
|
||||
Vec2 ray_dir = SubVec2(ray_end, ray_start);
|
||||
|
||||
// TODO: Real raycast query
|
||||
S_Ent *closest_victim = &S_NilEnt;
|
||||
S_RaycastResult victim_raycast = Zi;
|
||||
{
|
||||
f32 closest_len_sq = Inf;
|
||||
for (S_Ent *victim = S_FirstEnt(world); victim->valid; victim = S_NextEnt(victim))
|
||||
{
|
||||
if (victim->is_player && !S_MatchKey(victim->key, bullet->bullet_firer))
|
||||
{
|
||||
Xform victim_xf = victim->xf;
|
||||
S_Shape victim_world_shape = S_MulXformShape(victim_xf, victim->local_shape);
|
||||
|
||||
S_RaycastResult raycast = S_RaycastShape(victim_world_shape, ray_start, ray_dir, 0);
|
||||
if (raycast.is_intersecting)
|
||||
{
|
||||
f32 len_sq = Vec2LenSq(SubVec2(raycast.p, ray_start));
|
||||
if (len_sq < closest_len_sq)
|
||||
{
|
||||
closest_len_sq = len_sq;
|
||||
closest_victim = victim;
|
||||
victim_raycast = raycast;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_victim->valid)
|
||||
{
|
||||
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;
|
||||
S_DebugDrawPoint(bullet->bullet_end, Color_Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
S_DebugDrawPoint(bullet->bullet_end, Color_Purple);
|
||||
}
|
||||
|
||||
Rng2 bounds = Zi;
|
||||
bounds.p0 = VEC2(-S_WorldPitch / 2, -S_WorldPitch / 2);
|
||||
bounds.p1 = VEC2(S_WorldPitch / 2, S_WorldPitch / 2);
|
||||
if (
|
||||
bullet->bullet_start.x < bounds.p0.x || bullet->bullet_start.y < bounds.p0.y ||
|
||||
bullet->bullet_start.x > bounds.p1.x || bullet->bullet_start.y > bounds.p1.y
|
||||
)
|
||||
{
|
||||
bullet->exists = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// {
|
||||
// Struct(S_Bullet)
|
||||
@ -1607,8 +1748,8 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
snapshot->tick = world->tick;
|
||||
snapshot->time_ns = world->time_ns;
|
||||
|
||||
// Forward user tile deltas
|
||||
for (i64 applied_user_tile_delta_idx = 0; applied_user_tile_delta_idx < applied_user_tile_deltas_count; ++applied_user_tile_delta_idx)
|
||||
// Forward user edit deltas
|
||||
for (i64 applied_user_delta_idx = 0; applied_user_delta_idx < applied_user_deltas_count; ++applied_user_delta_idx)
|
||||
{
|
||||
S_Delta *delta = 0;
|
||||
{
|
||||
@ -1617,7 +1758,7 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn);
|
||||
delta = &dn->delta;
|
||||
}
|
||||
S_Delta *src = applied_user_tile_deltas[applied_user_tile_delta_idx];
|
||||
S_Delta *src = applied_user_deltas[applied_user_delta_idx];
|
||||
*delta = *src;
|
||||
}
|
||||
|
||||
|
||||
@ -74,10 +74,15 @@ Struct(S_Ent)
|
||||
|
||||
b32 has_weapon;
|
||||
|
||||
S_Key bullet_firer;
|
||||
b32 is_bullet;
|
||||
Vec2 bullet_start;
|
||||
Vec2 bullet_end;
|
||||
|
||||
b32 has_hit;
|
||||
Vec2 hit_entry;
|
||||
Vec2 hit_entry_normal;
|
||||
|
||||
//////////////////////////////
|
||||
//- Solver data
|
||||
|
||||
@ -85,6 +90,19 @@ Struct(S_Ent)
|
||||
f32 solved_dw;
|
||||
};
|
||||
|
||||
Struct(S_EntListNode)
|
||||
{
|
||||
S_EntListNode *next;
|
||||
S_Ent ent;
|
||||
};
|
||||
|
||||
Struct(S_EntList)
|
||||
{
|
||||
S_EntListNode *first;
|
||||
S_EntListNode *last;
|
||||
i64 count;
|
||||
};
|
||||
|
||||
Struct(S_EntBin)
|
||||
{
|
||||
S_Ent *first;
|
||||
@ -178,6 +196,7 @@ Struct(S_World)
|
||||
|
||||
Enum(S_DeltaKind)
|
||||
{
|
||||
S_DeltaKind_Reset,
|
||||
S_DeltaKind_RawEnt,
|
||||
S_DeltaKind_RawTiles,
|
||||
S_DeltaKind_Tile,
|
||||
@ -381,7 +400,7 @@ S_ClippedLine S_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal);
|
||||
Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal);
|
||||
|
||||
S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1, Vec2 sweep);
|
||||
S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir);
|
||||
S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir, b32 extend);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Lookup helpers
|
||||
@ -394,6 +413,12 @@ S_Ent *S_EntFromKey(S_World *world, S_Key key);
|
||||
S_Ent *S_FirstEnt(S_World *world);
|
||||
S_Ent *S_NextEnt(S_Ent *e);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ List helpers
|
||||
|
||||
S_Ent *S_PushTempEnt(Arena *arena, S_EntList *list);
|
||||
void S_SpawnEntsFromList(Arena *arena, S_World *world, S_EntList ents);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Debug draw
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Tile types
|
||||
|
||||
#define S_WorldPitch 96.0
|
||||
#define S_WorldPitch 80.0
|
||||
#define S_TilesPitch (S_WorldPitch * 2)
|
||||
#define S_TilesCount (S_TilesPitch * S_TilesPitch)
|
||||
|
||||
|
||||
@ -240,6 +240,16 @@ void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags)
|
||||
V_DrawPoly(points_arr, srgb, flags);
|
||||
}
|
||||
|
||||
void V_DrawPoint(Vec2 p, Vec4 srgb)
|
||||
{
|
||||
S_Shape ui_shape = S_ShapeFromDesc(
|
||||
.count = 1,
|
||||
.points = { p },
|
||||
.radius = 5
|
||||
);
|
||||
V_DrawShape(ui_shape, srgb, 24, V_DrawFlag_None);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Theme
|
||||
|
||||
@ -326,8 +336,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * S_WorldPitch, V_CellsPerMeter * S_WorldPitch);
|
||||
|
||||
// u32 max_particles = Kibi(128);
|
||||
u32 max_particles = Mebi(1);
|
||||
// u32 max_particles = Mebi(2);
|
||||
// u32 max_particles = Mebi(1);
|
||||
u32 max_particles = Mebi(2);
|
||||
// u32 max_particles = Mebi(16);
|
||||
|
||||
// Init gpu state
|
||||
@ -381,7 +391,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
gpu_perm, cl,
|
||||
// G_Format_R8_Uint,
|
||||
// G_Format_R11G11B10_Float,
|
||||
G_Format_R10G10B10A2_Unorm,
|
||||
// G_Format_R10G10B10A2_Unorm,
|
||||
G_Format_R16G16B16A16_Float,
|
||||
cells_dims,
|
||||
G_Layout_DirectQueue_ShaderReadWrite,
|
||||
.flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
|
||||
@ -394,7 +405,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
gpu_perm, cl,
|
||||
// G_Format_R8_Uint,
|
||||
// G_Format_R11G11B10_Float,
|
||||
G_Format_R10G10B10A2_Unorm,
|
||||
// G_Format_R10G10B10A2_Unorm,
|
||||
G_Format_R16G16B16A16_Float,
|
||||
cells_dims,
|
||||
G_Layout_DirectQueue_ShaderReadWrite,
|
||||
.flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
|
||||
@ -615,6 +627,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
b32 received_unseen_tick = 0;
|
||||
b32 tiles_dirty = 0;
|
||||
b32 should_clear_particles = 0;
|
||||
for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next)
|
||||
{
|
||||
S_Snapshot *snapshot = &n->snapshot;
|
||||
@ -626,6 +639,11 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
for (S_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
|
||||
{
|
||||
S_Delta *delta = &dn->delta;
|
||||
if (delta->kind == S_DeltaKind_Reset)
|
||||
{
|
||||
tiles_dirty = 1;
|
||||
should_clear_particles = 1;
|
||||
}
|
||||
if (delta->kind == S_DeltaKind_RawTiles || delta->kind == S_DeltaKind_Tile)
|
||||
{
|
||||
tiles_dirty = 1;
|
||||
@ -636,32 +654,6 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Prune ents
|
||||
|
||||
{
|
||||
i64 ents_to_prune_count = 0;
|
||||
S_Ent **ents_to_prune = PushStructsNoZero(frame->arena, S_Ent *, world->ents_count);
|
||||
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
|
||||
{
|
||||
if (ent->exists <= 0)
|
||||
{
|
||||
ents_to_prune[ents_to_prune_count] = ent;
|
||||
ents_to_prune_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx)
|
||||
{
|
||||
// FIXME: Add to free list
|
||||
S_Ent *ent = ents_to_prune[prune_idx];
|
||||
S_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count];
|
||||
DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
|
||||
DllQueueRemove(world->first_ent, world->last_ent, ent);
|
||||
world->ents_count -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Copy sim debug info
|
||||
|
||||
@ -2198,6 +2190,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
|
||||
{
|
||||
UI_BuildLabelF("World seed: 0x%F", FmtHex(world->seed));
|
||||
UI_BuildLabelF("Entities count: %F", FmtSint(world->ents_count));
|
||||
}
|
||||
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
|
||||
{
|
||||
@ -2390,7 +2383,6 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
//////////////////////////////
|
||||
//- Process vis commands
|
||||
|
||||
b32 should_clear_particles = 0;
|
||||
for (V_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
|
||||
{
|
||||
String cmd_name = cmd_node->cmd.name;
|
||||
@ -2478,23 +2470,38 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
} break;
|
||||
|
||||
case V_CmdKind_reset_world:
|
||||
case V_CmdKind_spawn:
|
||||
{
|
||||
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
|
||||
cmd->delta.kind = S_DeltaKind_RawEnt;
|
||||
S_Ent *ent = &cmd->delta.ent;
|
||||
*ent = S_NilEnt;
|
||||
ent->key = V.player_key;
|
||||
ent->xf = XformFromPos(frame->world_cursor);
|
||||
ent->move_speed = 0.075;
|
||||
ent->is_player = 1;
|
||||
ent->local_shape = S_ShapeFromDesc(
|
||||
.mass = 10,
|
||||
.count = 1,
|
||||
.radius = 0.3,
|
||||
);
|
||||
ent->has_weapon = 1;
|
||||
ent->exists = 1;
|
||||
// Reset world
|
||||
Vec2 player_pos = VEC2(5, 0);
|
||||
if (kind == V_CmdKind_reset_world)
|
||||
{
|
||||
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
|
||||
cmd->delta.kind = S_DeltaKind_Reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
player_pos = frame->world_cursor;
|
||||
}
|
||||
// Spawn player
|
||||
{
|
||||
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
|
||||
cmd->delta.kind = S_DeltaKind_RawEnt;
|
||||
S_Ent *ent = &cmd->delta.ent;
|
||||
*ent = S_NilEnt;
|
||||
ent->key = V.player_key;
|
||||
ent->xf = XformFromPos(player_pos);
|
||||
ent->move_speed = 0.075;
|
||||
ent->is_player = 1;
|
||||
ent->local_shape = S_ShapeFromDesc(
|
||||
.mass = 10,
|
||||
.count = 1,
|
||||
.radius = 0.3,
|
||||
);
|
||||
ent->has_weapon = 1;
|
||||
ent->exists = 1;
|
||||
}
|
||||
} break;
|
||||
|
||||
case V_CmdKind_spawn_dummy:
|
||||
@ -2600,69 +2607,160 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//- Spawn test bullet particles
|
||||
//- Push test bullet particles
|
||||
|
||||
// TODO: Not like this
|
||||
|
||||
|
||||
|
||||
|
||||
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
|
||||
{
|
||||
PERSIST Vec2 start = {5, 5};
|
||||
PERSIST Vec2 end = {3, -20};
|
||||
|
||||
|
||||
if (frame->held_buttons[Button_G])
|
||||
if (bullet->is_bullet)
|
||||
{
|
||||
end = frame->world_cursor;
|
||||
if (!last_frame->held_buttons[Button_G])
|
||||
// FIXME: Truncate bullet trail at hit
|
||||
Vec2 start = bullet->bullet_start;
|
||||
Vec2 end = bullet->bullet_end;
|
||||
|
||||
b32 skip = 0;
|
||||
if (bullet->has_hit)
|
||||
{
|
||||
start = end;
|
||||
Vec2 hit_pos = bullet->hit_entry;
|
||||
if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0)
|
||||
{
|
||||
skip = 1;
|
||||
}
|
||||
// V_DrawPoint(MulXformV2(frame->xf.world_to_ui, start), Color_Red);
|
||||
// V_DrawPoint(MulXformV2(frame->xf.world_to_ui, end), Color_Purple);
|
||||
end = hit_pos;
|
||||
}
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
f32 trail_len = Vec2Len(SubVec2(end, start));
|
||||
// f32 particles_count = MaxF32(trail_len * 10000, 1);
|
||||
// f32 particles_count = MaxF32(trail_len * 512, 1);
|
||||
// f32 particles_count = MaxF32(trail_len * 64, 1);
|
||||
f32 particles_count = MaxF32(trail_len * 32, 1);
|
||||
|
||||
|
||||
f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start)));
|
||||
// f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
|
||||
|
||||
V_Emitter *emitter = V_PushEmitter(particles_count);
|
||||
|
||||
// emitter->flags |= V_ParticleFlag_StainOnPrune;
|
||||
// emitter->flags |= V_ParticleFlag_StainTrail;
|
||||
|
||||
// emitter->lifetime = 1;
|
||||
// emitter->lifetime_spread = 2;
|
||||
|
||||
emitter->lifetime = 0.15;
|
||||
// emitter->lifetime = 0.04;
|
||||
emitter->lifetime_spread = emitter->lifetime * 2;
|
||||
|
||||
emitter->angle = angle;
|
||||
// emitter->angle_spread = Tau / 4;
|
||||
emitter->angle_spread = Tau / 4;
|
||||
|
||||
emitter->start = start;
|
||||
emitter->end = end;
|
||||
|
||||
// emitter->color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
|
||||
|
||||
// emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25));
|
||||
emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
|
||||
// emitter->color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2));
|
||||
|
||||
emitter->speed = 0;
|
||||
emitter->speed_spread = 1;
|
||||
|
||||
// emitter->speed = 1;
|
||||
// emitter->speed_spread = 1;
|
||||
|
||||
// emitter->velocity_falloff = 1;
|
||||
// emitter->velocity_falloff_spread = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Vec2 vel = SubVec2(end, start);
|
||||
|
||||
|
||||
f32 trail_len = Vec2Len(SubVec2(end, start));
|
||||
f32 particles_count = MaxF32(trail_len * 64, 1);
|
||||
|
||||
|
||||
// f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start)));
|
||||
f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
|
||||
|
||||
V_Emitter *emitter = V_PushEmitter(particles_count);
|
||||
|
||||
emitter->lifetime = 1;
|
||||
emitter->lifetime_spread = 1;
|
||||
|
||||
|
||||
emitter->angle = angle;
|
||||
emitter->angle_spread = Tau / 4;
|
||||
|
||||
emitter->start = start;
|
||||
emitter->end = end;
|
||||
|
||||
|
||||
emitter->color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
|
||||
|
||||
// emitter->speed = 0;
|
||||
// emitter->speed_spread = 1;
|
||||
|
||||
emitter->speed = 1;
|
||||
emitter->speed_spread = 1;
|
||||
|
||||
emitter->velocity_falloff = 0;
|
||||
emitter->velocity_falloff_spread = 0;
|
||||
|
||||
start = end;
|
||||
end = AddVec2(end, vel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// {
|
||||
// PERSIST Vec2 start = {5, 5};
|
||||
// PERSIST Vec2 end = {3, -20};
|
||||
|
||||
|
||||
// b32 skip = 1;
|
||||
// if (frame->held_buttons[Button_G])
|
||||
// {
|
||||
// end = frame->world_cursor;
|
||||
// if (!last_frame->held_buttons[Button_G])
|
||||
// {
|
||||
// start = end;
|
||||
// }
|
||||
// skip = 0;
|
||||
// }
|
||||
|
||||
// Vec2 vel = SubVec2(end, start);
|
||||
|
||||
|
||||
// f32 trail_len = Vec2Len(SubVec2(end, start));
|
||||
// // f32 particles_count = MaxF32(trail_len * 10000, 1);
|
||||
// // f32 particles_count = MaxF32(trail_len * 512, 1);
|
||||
// // f32 particles_count = MaxF32(trail_len * 64, 1);
|
||||
// f32 particles_count = MaxF32(trail_len * 32, 1);
|
||||
|
||||
|
||||
// f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start)));
|
||||
// // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
|
||||
|
||||
// V_Emitter *emitter = V_PushEmitter(particles_count);
|
||||
|
||||
// // emitter->flags |= V_ParticleFlag_StainOnPrune;
|
||||
// // emitter->flags |= V_ParticleFlag_StainTrail;
|
||||
|
||||
// // emitter->lifetime = 1;
|
||||
// // emitter->lifetime_spread = 2;
|
||||
|
||||
// emitter->lifetime = 0.25;
|
||||
// emitter->lifetime_spread = emitter->lifetime * 2;
|
||||
|
||||
// emitter->angle = angle;
|
||||
// // emitter->angle_spread = Tau / 4;
|
||||
// emitter->angle_spread = Tau / 4;
|
||||
|
||||
// emitter->start = start;
|
||||
// emitter->end = end;
|
||||
|
||||
// // emitter->color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
|
||||
|
||||
// // emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25));
|
||||
// emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
|
||||
// // emitter->color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2));
|
||||
|
||||
// emitter->speed = 0;
|
||||
// emitter->speed_spread = 1;
|
||||
|
||||
// // emitter->speed = 1;
|
||||
// // emitter->speed_spread = 1;
|
||||
|
||||
// // emitter->velocity_falloff = 1;
|
||||
// // emitter->velocity_falloff_spread = 0;
|
||||
|
||||
// start = end;
|
||||
// end = AddVec2(end, vel);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -2680,107 +2778,182 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//- Spawn test blood particles
|
||||
//- Push test blood particles
|
||||
|
||||
// TODO: Not like this
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer))
|
||||
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
|
||||
{
|
||||
if (firer->fire_held)
|
||||
if (bullet->is_bullet && bullet->has_hit)
|
||||
{
|
||||
Xform firer_xf = firer->xf;
|
||||
S_Shape firer_world_shape = S_MulXformShape(firer_xf, firer->local_shape);
|
||||
// Vec2 bullet_start = bullet->start;
|
||||
// Vec2 bullet_end = bullet->end;
|
||||
|
||||
Vec2 ray_start = firer_world_shape.centroid;
|
||||
Vec2 ray_dir = firer->look;
|
||||
Vec2 hit_entry = bullet->hit_entry;
|
||||
Vec2 hit_entry_normal = bullet->hit_entry_normal;
|
||||
Vec2 bullet_vel = SubVec2(bullet->bullet_end, bullet->bullet_start);
|
||||
|
||||
// TODO: Real raycast query
|
||||
S_Ent *closest_victim = &S_NilEnt;
|
||||
S_RaycastResult victim_raycast = Zi;
|
||||
V_DrawLine(bullet->bullet_start, bullet->bullet_end, Color_Cyan);
|
||||
|
||||
V_ParticleFlag flags = 0;
|
||||
flags |= V_ParticleFlag_PruneWhenStill;
|
||||
flags |= V_ParticleFlag_StainOnPrune;
|
||||
if (TweakBool("Emitter stain trail", 0))
|
||||
{
|
||||
f32 closest_len_sq = Inf;
|
||||
for (S_Ent *victim = S_FirstEnt(world); victim->valid; victim = S_NextEnt(victim))
|
||||
{
|
||||
if (victim != firer)
|
||||
{
|
||||
Xform victim_xf = victim->xf;
|
||||
S_Shape victim_world_shape = S_MulXformShape(victim_xf, victim->local_shape);
|
||||
|
||||
S_RaycastResult raycast = S_RaycastShape(victim_world_shape, ray_start, ray_dir);
|
||||
if (raycast.is_intersecting)
|
||||
{
|
||||
f32 len_sq = Vec2LenSq(SubVec2(raycast.p, ray_start));
|
||||
if (len_sq < closest_len_sq)
|
||||
{
|
||||
closest_len_sq = len_sq;
|
||||
closest_victim = victim;
|
||||
victim_raycast = raycast;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flags |= V_ParticleFlag_StainTrail;
|
||||
}
|
||||
// f32 count = TweakFloat("Emitter count", 50, 0, 10000);
|
||||
f32 count = TweakFloat("Emitter count", 50, 1, 1000);
|
||||
f32 speed = TweakFloat("Emitter speed", 20, 0, 100);
|
||||
f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100);
|
||||
f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau;
|
||||
|
||||
if (closest_victim->valid)
|
||||
{
|
||||
V_ParticleFlag flags = 0;
|
||||
flags |= V_ParticleFlag_StainOnPrune;
|
||||
if (TweakBool("Emitter stain trail", 1))
|
||||
{
|
||||
flags |= V_ParticleFlag_StainTrail;
|
||||
}
|
||||
// f32 count = TweakFloat("Emitter count", 50, 0, 10000);
|
||||
f32 count = TweakFloat("Emitter count", 50, 1, 1000);
|
||||
f32 speed = TweakFloat("Emitter speed", 20, 0, 100);
|
||||
f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100);
|
||||
f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau;
|
||||
V_Emitter *emitter = V_PushEmitter(count);
|
||||
emitter->flags = flags;
|
||||
|
||||
V_Emitter *emitter = V_PushEmitter(count);
|
||||
emitter->flags = flags;
|
||||
// Vec2 dir = hit_entry_normal;
|
||||
Vec2 dir = NormVec2(NegVec2(bullet_vel));
|
||||
|
||||
Vec2 dir = victim_raycast.normal;
|
||||
emitter->start = victim_raycast.p;
|
||||
emitter->end = emitter->start;
|
||||
emitter->start = hit_entry;
|
||||
emitter->end = emitter->start;
|
||||
|
||||
emitter->speed = speed;
|
||||
emitter->speed_spread = speed * 2;
|
||||
emitter->speed = speed;
|
||||
emitter->speed_spread = speed * 2;
|
||||
|
||||
emitter->velocity_falloff = falloff;
|
||||
emitter->velocity_falloff_spread = falloff * 2;
|
||||
emitter->velocity_falloff = falloff;
|
||||
emitter->velocity_falloff_spread = falloff * 1.5;
|
||||
|
||||
emitter->angle = AngleFromVec2(dir);
|
||||
// emitter->angle_spread = Tau / 4;
|
||||
emitter->angle_spread = angle_spread;
|
||||
// emitter->angle_spread = Tau / 32;
|
||||
emitter->angle = AngleFromVec2(dir);
|
||||
// emitter->angle_spread = Tau / 4;
|
||||
emitter->angle_spread = angle_spread;
|
||||
// emitter->angle_spread = Tau / 32;
|
||||
|
||||
emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1));
|
||||
emitter->color_spread = VEC3(0.1, 0, 0);
|
||||
emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1));
|
||||
emitter->color_spread = VEC4(0.1, 0, 0, 0);
|
||||
|
||||
// emitter->color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
|
||||
// emitter->color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
|
||||
|
||||
emitter->seed = RandU64FromState(&frame->rand);
|
||||
// emitter->angle_spread = 1;
|
||||
// emitter->angle_spread = 0.5;
|
||||
// emitter->angle_spread = Tau;
|
||||
emitter->seed = RandU64FromState(&frame->rand);
|
||||
// emitter->angle_spread = 1;
|
||||
// emitter->angle_spread = 0.5;
|
||||
// emitter->angle_spread = Tau;
|
||||
|
||||
// V_DrawPoint(victim_raycast.p, Color_Green);
|
||||
// V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White);
|
||||
}
|
||||
|
||||
|
||||
// for (S_QueryResult query = S_FirstRaycast(wrold, ray_start, ray_dir); query.
|
||||
// S_RaycastWorldResult hits = S_RaycastWorld(world, ray_start, ray_dir)
|
||||
// {
|
||||
// }
|
||||
// V_DrawPoint(victim_raycast.p, Color_Green);
|
||||
// V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Spawn test emitter
|
||||
|
||||
// Spawn test emitter
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// {
|
||||
// for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer))
|
||||
// {
|
||||
// if (firer->fire_held)
|
||||
// {
|
||||
// Xform firer_xf = firer->xf;
|
||||
// S_Shape firer_world_shape = S_MulXformShape(firer_xf, firer->local_shape);
|
||||
|
||||
// Vec2 ray_start = firer_world_shape.centroid;
|
||||
// Vec2 ray_dir = firer->look;
|
||||
|
||||
// // TODO: Real raycast query
|
||||
// S_Ent *closest_victim = &S_NilEnt;
|
||||
// S_RaycastResult victim_raycast = Zi;
|
||||
// {
|
||||
// f32 closest_len_sq = Inf;
|
||||
// for (S_Ent *victim = S_FirstEnt(world); victim->valid; victim = S_NextEnt(victim))
|
||||
// {
|
||||
// if (victim != firer)
|
||||
// {
|
||||
// Xform victim_xf = victim->xf;
|
||||
// S_Shape victim_world_shape = S_MulXformShape(victim_xf, victim->local_shape);
|
||||
|
||||
// S_RaycastResult raycast = S_RaycastShape(victim_world_shape, ray_start, ray_dir);
|
||||
// if (raycast.is_intersecting)
|
||||
// {
|
||||
// f32 len_sq = Vec2LenSq(SubVec2(raycast.p, ray_start));
|
||||
// if (len_sq < closest_len_sq)
|
||||
// {
|
||||
// closest_len_sq = len_sq;
|
||||
// closest_victim = victim;
|
||||
// victim_raycast = raycast;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (closest_victim->valid)
|
||||
// {
|
||||
// V_ParticleFlag flags = 0;
|
||||
// flags |= V_ParticleFlag_PruneWhenStill;
|
||||
// flags |= V_ParticleFlag_StainOnPrune;
|
||||
// if (TweakBool("Emitter stain trail", 1))
|
||||
// {
|
||||
// flags |= V_ParticleFlag_StainTrail;
|
||||
// }
|
||||
// // f32 count = TweakFloat("Emitter count", 50, 0, 10000);
|
||||
// f32 count = TweakFloat("Emitter count", 50, 1, 1000);
|
||||
// f32 speed = TweakFloat("Emitter speed", 20, 0, 100);
|
||||
// f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100);
|
||||
// f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau;
|
||||
|
||||
// V_Emitter *emitter = V_PushEmitter(count);
|
||||
// emitter->flags = flags;
|
||||
|
||||
// Vec2 dir = victim_raycast.normal;
|
||||
// emitter->start = victim_raycast.p;
|
||||
// emitter->end = emitter->start;
|
||||
|
||||
// emitter->speed = speed;
|
||||
// emitter->speed_spread = speed * 2;
|
||||
|
||||
// emitter->velocity_falloff = falloff;
|
||||
// emitter->velocity_falloff_spread = falloff * 1.5;
|
||||
|
||||
// emitter->angle = AngleFromVec2(dir);
|
||||
// // emitter->angle_spread = Tau / 4;
|
||||
// emitter->angle_spread = angle_spread;
|
||||
// // emitter->angle_spread = Tau / 32;
|
||||
|
||||
// emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1));
|
||||
// emitter->color_spread = VEC4(0.1, 0, 0, 0);
|
||||
|
||||
// // emitter->color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
|
||||
|
||||
// emitter->seed = RandU64FromState(&frame->rand);
|
||||
// // emitter->angle_spread = 1;
|
||||
// // emitter->angle_spread = 0.5;
|
||||
// // emitter->angle_spread = Tau;
|
||||
|
||||
// // V_DrawPoint(victim_raycast.p, Color_Green);
|
||||
// // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White);
|
||||
// }
|
||||
|
||||
|
||||
// // for (S_QueryResult query = S_FirstRaycast(wrold, ray_start, ray_dir); query.
|
||||
// // S_RaycastWorldResult hits = S_RaycastWorld(world, ray_start, ray_dir)
|
||||
// // {
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//////////////////////////////
|
||||
//- Push test emitter
|
||||
|
||||
if (frame->held_buttons[Button_F])
|
||||
{
|
||||
@ -2870,12 +3043,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
case S_DebugDrawKind_Point:
|
||||
{
|
||||
Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, desc->point.p);
|
||||
S_Shape ui_shape = S_ShapeFromDesc(
|
||||
.count = 1,
|
||||
.points = { ui_p },
|
||||
.radius = radius
|
||||
);
|
||||
V_DrawShape(ui_shape, color, detail, V_DrawFlag_None);
|
||||
V_DrawPoint(ui_p, color);
|
||||
} break;
|
||||
|
||||
case S_DebugDrawKind_Line:
|
||||
@ -2896,8 +3064,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
case S_DebugDrawKind_Shape:
|
||||
{
|
||||
S_Shape ui_shape = S_MulXformShape(frame->xf.world_to_ui, desc->shape);
|
||||
// V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
|
||||
V_DrawShape(ui_shape, color, detail, V_DrawFlag_None);
|
||||
V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
|
||||
// V_DrawShape(ui_shape, color, detail, V_DrawFlag_None);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
@ -3095,6 +3263,32 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Prune ents
|
||||
|
||||
{
|
||||
i64 ents_to_prune_count = 0;
|
||||
S_Ent **ents_to_prune = PushStructsNoZero(frame->arena, S_Ent *, world->ents_count);
|
||||
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
|
||||
{
|
||||
if (ent->exists <= 0)
|
||||
{
|
||||
ents_to_prune[ents_to_prune_count] = ent;
|
||||
ents_to_prune_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx)
|
||||
{
|
||||
// FIXME: Add to free list
|
||||
S_Ent *ent = ents_to_prune[prune_idx];
|
||||
S_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count];
|
||||
DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
|
||||
DllQueueRemove(world->first_ent, world->last_ent, ent);
|
||||
world->ents_count -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- End frame
|
||||
|
||||
|
||||
@ -12,9 +12,10 @@
|
||||
X(toggle_console, Toggle Developer Console, V_CmdDescFlag_None, V_HOTKEY( Button_GraveAccent ), ) \
|
||||
X(toggle_fullscreen, Toggle Fullscreen Mode, V_CmdDescFlag_None, V_HOTKEY( Button_Enter, .alt = 1 ) ) \
|
||||
X(toggle_window_topmost, Toggle Window Topmost, V_CmdDescFlag_None, V_HOTKEY( Button_F4 ), ) \
|
||||
X(spawn, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \
|
||||
X(spawn_dummy, Spawn Dummy, V_CmdDescFlag_None, V_HOTKEY( Button_R ), ) \
|
||||
X(spawn, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_Q ), ) \
|
||||
X(spawn_dummy, Spawn Dummy, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \
|
||||
X(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \
|
||||
X(reset_world, Reset world, V_CmdDescFlag_None, V_HOTKEY( Button_R ), ) \
|
||||
X(clear_particles, Clear particles, V_CmdDescFlag_None, V_HOTKEY( Button_C ), ) \
|
||||
/* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
|
||||
|
||||
@ -323,6 +324,7 @@ void V_DrawPoly(Vec2Array points, Vec4 srgb, V_DrawFlag flags);
|
||||
void V_DrawShape(S_Shape shape, Vec4 srgb, i32 detail, V_DrawFlag flags);
|
||||
void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb);
|
||||
void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags);
|
||||
void V_DrawPoint(Vec2 p, Vec4 srgb);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Theme
|
||||
|
||||
@ -153,30 +153,68 @@ ComputeShader2D(V_BackdropCS, 8, 8)
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: Remove this
|
||||
// Cells test
|
||||
{
|
||||
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
|
||||
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
|
||||
Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(world_pos, 1)));
|
||||
|
||||
Vec4 stain = stains.Load(cell_pos);
|
||||
Vec4 cell = cells.Load(cell_pos);
|
||||
if (cell.a != 0)
|
||||
{
|
||||
result = cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
|
||||
Vec4 stain = stains.Load(cell_pos);
|
||||
if (stain.a != 0)
|
||||
{
|
||||
result = stain;
|
||||
}
|
||||
}
|
||||
// cell.rgb *= cell.a;
|
||||
// stain.rgb *= stain.a;
|
||||
|
||||
|
||||
result.rgb = (stain.rgb * stain.a) + (result.rgb * (1.0 - stain.a));
|
||||
result.a = (stain.a * 1) + (result.a * (1.0 - stain.a));
|
||||
|
||||
result.rgb = (cell.rgb * cell.a) + (result.rgb * (1.0 - cell.a));
|
||||
result.a = (cell.a * 1) + (result.a * (1.0 - cell.a));
|
||||
|
||||
// Vec4 cell = cells.Load(cell_pos);
|
||||
// if (cell.a != 0)
|
||||
// {
|
||||
// result = cell;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Vec4 stain = stains.Load(cell_pos);
|
||||
// if (stain.a != 0)
|
||||
// {
|
||||
// result = stain;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // TODO: Remove this
|
||||
// // Cells test
|
||||
// {
|
||||
// RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
|
||||
// Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(world_pos, 1)));
|
||||
// Vec4 cell = cells.Load(cell_pos);
|
||||
// if (cell.a != 0)
|
||||
// {
|
||||
// result = cell;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
|
||||
// Vec4 stain = stains.Load(cell_pos);
|
||||
// if (stain.a != 0)
|
||||
// {
|
||||
// result = stain;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// TODO: Remove this
|
||||
// Stains test
|
||||
// {
|
||||
@ -258,7 +296,6 @@ ComputeShader(V_EmitParticlesCS, 64)
|
||||
if (emitter_idx < params.emitters_count)
|
||||
{
|
||||
V_Emitter emitter = emitters[emitter_idx];
|
||||
|
||||
for (u32 i = 0; i < emitter.count; ++i)
|
||||
{
|
||||
u32 particle_seq = emitter.first_particle_seq + i;
|
||||
@ -294,18 +331,23 @@ ComputeShader(V_SimParticlesCS, 64)
|
||||
u64 seed0 = MixU64(emitter.seed + particle.seq);
|
||||
u64 seed1 = MixU64(seed0);
|
||||
|
||||
|
||||
f32 rand_speed = (f32)((seed0 >> 0) & 0xFFFF) / (f32)0xFFFF;
|
||||
f32 rand_angle = (f32)((seed0 >> 16) & 0xFFFF) / (f32)0xFFFF;
|
||||
f32 rand_offset = (f32)((seed0 >> 32) & 0xFFFF) / (f32)0xFFFF;
|
||||
f32 rand_falloff = (f32)((seed0 >> 48) & 0xFFFF) / (f32)0xFFFF;
|
||||
|
||||
f32 rand_lifetime = (f32)((seed1 >> 0) & 0xFFFF) / (f32)0xFFFF;
|
||||
f32 rand_r = (f32)((seed1 >> 0) & 0xFF) / (f32)0xFF;
|
||||
f32 rand_g = (f32)((seed1 >> 8) & 0xFF) / (f32)0xFF;
|
||||
f32 rand_b = (f32)((seed1 >> 16) & 0xFF) / (f32)0xFF;
|
||||
f32 rand_a = (f32)((seed1 >> 24) & 0xFF) / (f32)0xFF;
|
||||
f32 rand_lifetime = (f32)((seed1 >> 32) & 0xFFFF) / (f32)0xFFFF;
|
||||
|
||||
f32 speed = emitter.speed + (rand_speed - 0.5) * emitter.speed_spread;
|
||||
f32 angle = emitter.angle + (rand_angle - 0.5) * emitter.angle_spread;
|
||||
f32 velocity_falloff = emitter.velocity_falloff + (rand_falloff - 0.5) * emitter.velocity_falloff_spread;
|
||||
f32 lifetime = emitter.lifetime + (rand_lifetime - 0.5) * emitter.lifetime_spread;
|
||||
Vec3 color = emitter.color_lin.rgb + (rand_lifetime - 0.5) * emitter.color_spread;
|
||||
Vec4 color = emitter.color_lin + (Vec4(rand_r, rand_g, rand_b, rand_a) - 0.5) * emitter.color_spread;
|
||||
Vec2 offset = (emitter.end - emitter.start) * rand_offset;
|
||||
|
||||
particle.flags = emitter.flags;
|
||||
@ -322,12 +364,24 @@ ComputeShader(V_SimParticlesCS, 64)
|
||||
particle.emitter_init_num = 0;
|
||||
}
|
||||
|
||||
Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(particle.pos, 1)));
|
||||
b32 is_in_bounds = cell_pos.x >= 0 && cell_pos.y >= 0 && cell_pos.x < countof(stains).x && cell_pos.y < countof(stains).y;
|
||||
|
||||
// Simulate
|
||||
f32 old_exists = particle.exists;
|
||||
{
|
||||
particle.pos += particle.velocity * params.dt;
|
||||
particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * params.dt);
|
||||
particle.lifetime -= params.dt;
|
||||
if (particle.exists < 0.0001 || particle.lifetime < 0.0001 || dot(particle.velocity, particle.velocity) < (0.0001 * 0.0001))
|
||||
particle.exists -= params.dt / particle.lifetime;
|
||||
if ((particle.flags & V_ParticleFlag_PruneWhenStill) && (dot(particle.velocity, particle.velocity) < (0.1 * 0.1)))
|
||||
{
|
||||
particle.exists = 0;
|
||||
}
|
||||
if (particle.exists < 0.000001)
|
||||
{
|
||||
particle.exists = 0;
|
||||
}
|
||||
if (!is_in_bounds)
|
||||
{
|
||||
particle.exists = 0;
|
||||
}
|
||||
@ -336,19 +390,19 @@ ComputeShader(V_SimParticlesCS, 64)
|
||||
// Commit
|
||||
{
|
||||
// FIXME: Atomic write
|
||||
Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(particle.pos, 1)));
|
||||
if (cell_pos.x >= 0 && cell_pos.y >= 0 && cell_pos.x < countof(stains).x && cell_pos.y < countof(stains).y)
|
||||
if (is_in_bounds)
|
||||
{
|
||||
b32 should_stain = 0;
|
||||
if (particle.flags & V_ParticleFlag_StainTrail || ((particle.flags & V_ParticleFlag_StainOnPrune) && particle.exists == 0))
|
||||
if ((particle.flags & V_ParticleFlag_StainTrail) || ((particle.flags & V_ParticleFlag_StainOnPrune) && particle.exists == 0))
|
||||
{
|
||||
should_stain = 1;
|
||||
}
|
||||
|
||||
cells[cell_pos] = Vec4(particle.color, 1);
|
||||
Vec4 color = particle.color;
|
||||
color.a *= old_exists;
|
||||
cells[cell_pos] = color;
|
||||
if (should_stain)
|
||||
{
|
||||
stains[cell_pos] = Vec4(particle.color, 1);
|
||||
stains[cell_pos] = color;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -87,8 +87,9 @@ Struct(V_GpuParams)
|
||||
Enum(V_ParticleFlag)
|
||||
{
|
||||
V_ParticleFlag_None = 0,
|
||||
V_ParticleFlag_StainOnPrune = (1 << 0),
|
||||
V_ParticleFlag_StainTrail = (1 << 1),
|
||||
V_ParticleFlag_PruneWhenStill = (1 << 0),
|
||||
V_ParticleFlag_StainOnPrune = (1 << 1),
|
||||
V_ParticleFlag_StainTrail = (1 << 2),
|
||||
};
|
||||
|
||||
Struct(V_Emitter)
|
||||
@ -115,7 +116,7 @@ Struct(V_Emitter)
|
||||
f32 velocity_falloff_spread;
|
||||
|
||||
Vec4 color_lin;
|
||||
Vec3 color_spread;
|
||||
Vec4 color_spread;
|
||||
};
|
||||
|
||||
// TODO: Pack this efficiently
|
||||
@ -133,7 +134,7 @@ Struct(V_Particle)
|
||||
f32 lifetime;
|
||||
|
||||
f32 velocity_falloff;
|
||||
Vec3 color;
|
||||
Vec4 color;
|
||||
};
|
||||
|
||||
#if IsLanguageC
|
||||
@ -149,7 +150,7 @@ Struct(V_Particle)
|
||||
|
||||
Enum(V_DQuadFlag)
|
||||
{
|
||||
V_DQuadFlag_None = 0,
|
||||
V_DQuadFlag_None = 0,
|
||||
};
|
||||
|
||||
Struct(V_DQuad)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user