simulated bullet testing

This commit is contained in:
jacob 2026-01-07 08:12:14 -06:00
parent 3dc22b9bd7
commit e9ea1ec0f7
7 changed files with 656 additions and 239 deletions

View File

@ -93,6 +93,16 @@ void S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta)
if (0) 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 //- Raw ent
if (delta->kind == S_DeltaKind_RawEnt) if (delta->kind == S_DeltaKind_RawEnt)
{ {
@ -921,14 +931,24 @@ S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1, Ve
return result; 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; S_RaycastResult result = Zi;
// HACK: Create line shape as large as the world and perform a collision test // HACK: Create line shape as large as the world and perform a collision test
Vec2 ray_shape_p0 = ray_start; 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_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); S_CollisionResult cls = S_CollisionResultFromShapes(ray_shape, shape, ray_dir);
result.is_intersecting = cls.collision_points_count > 0; result.is_intersecting = cls.collision_points_count > 0;
@ -982,6 +1002,34 @@ S_Ent *S_NextEnt(S_Ent *e)
return result; 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 //~ Debug draw
@ -1144,8 +1192,8 @@ void S_TickForever(WaveLaneCtx *lane)
// FIXME: Only accept world deltas from users that can edit // FIXME: Only accept world deltas from users that can edit
i64 applied_user_tile_deltas_count = 0; i64 applied_user_deltas_count = 0;
S_Delta **applied_user_tile_deltas = PushStructsNoZero(frame_arena, S_Delta *, input->cmds_count); 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) for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
{ {
S_Cmd *cmd = &cmd_node->cmd; S_Cmd *cmd = &cmd_node->cmd;
@ -1153,6 +1201,12 @@ void S_TickForever(WaveLaneCtx *lane)
{ {
S_Delta *delta = &cmd->delta; S_Delta *delta = &cmd->delta;
b32 allow = 0; b32 allow = 0;
b32 forward = 0;
if (delta->kind == S_DeltaKind_Reset)
{
allow = 1;
forward = 1;
}
if (delta->kind == S_DeltaKind_RawEnt) if (delta->kind == S_DeltaKind_RawEnt)
{ {
allow = 1; allow = 1;
@ -1160,8 +1214,12 @@ void S_TickForever(WaveLaneCtx *lane)
if (delta->kind == S_DeltaKind_Tile) if (delta->kind == S_DeltaKind_Tile)
{ {
allow = 1; allow = 1;
applied_user_tile_deltas[applied_user_tile_deltas_count] = delta; forward = 1;
applied_user_tile_deltas_count += 1; }
if (forward)
{
applied_user_deltas[applied_user_deltas_count] = delta;
applied_user_deltas_count += 1;
} }
if (allow) 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 // TODO: Remove this
// { {
// b32 bullet_spawned = 0; S_EntList bullets_to_spawn = Zi;
// for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer))
// { {
// if (ent->is_bullet) if (firer->fire_held)
// { {
// bullet_spawned = 1; Xform firer_xf = firer->xf;
// break; 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: 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) // Struct(S_Bullet)
@ -1607,8 +1748,8 @@ void S_TickForever(WaveLaneCtx *lane)
snapshot->tick = world->tick; snapshot->tick = world->tick;
snapshot->time_ns = world->time_ns; snapshot->time_ns = world->time_ns;
// Forward user tile deltas // Forward user edit deltas
for (i64 applied_user_tile_delta_idx = 0; applied_user_tile_delta_idx < applied_user_tile_deltas_count; ++applied_user_tile_delta_idx) for (i64 applied_user_delta_idx = 0; applied_user_delta_idx < applied_user_deltas_count; ++applied_user_delta_idx)
{ {
S_Delta *delta = 0; S_Delta *delta = 0;
{ {
@ -1617,7 +1758,7 @@ void S_TickForever(WaveLaneCtx *lane)
SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn); SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn);
delta = &dn->delta; 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; *delta = *src;
} }

View File

@ -74,10 +74,15 @@ Struct(S_Ent)
b32 has_weapon; b32 has_weapon;
S_Key bullet_firer;
b32 is_bullet; b32 is_bullet;
Vec2 bullet_start; Vec2 bullet_start;
Vec2 bullet_end; Vec2 bullet_end;
b32 has_hit;
Vec2 hit_entry;
Vec2 hit_entry_normal;
////////////////////////////// //////////////////////////////
//- Solver data //- Solver data
@ -85,6 +90,19 @@ Struct(S_Ent)
f32 solved_dw; 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) Struct(S_EntBin)
{ {
S_Ent *first; S_Ent *first;
@ -178,6 +196,7 @@ Struct(S_World)
Enum(S_DeltaKind) Enum(S_DeltaKind)
{ {
S_DeltaKind_Reset,
S_DeltaKind_RawEnt, S_DeltaKind_RawEnt,
S_DeltaKind_RawTiles, S_DeltaKind_RawTiles,
S_DeltaKind_Tile, 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); Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal);
S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1, Vec2 sweep); 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 //~ 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_FirstEnt(S_World *world);
S_Ent *S_NextEnt(S_Ent *e); 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 //~ Debug draw

View File

@ -1,7 +1,7 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Tile types //~ Tile types
#define S_WorldPitch 96.0 #define S_WorldPitch 80.0
#define S_TilesPitch (S_WorldPitch * 2) #define S_TilesPitch (S_WorldPitch * 2)
#define S_TilesCount (S_TilesPitch * S_TilesPitch) #define S_TilesCount (S_TilesPitch * S_TilesPitch)

View File

@ -240,6 +240,16 @@ void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags)
V_DrawPoly(points_arr, srgb, 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 //~ Theme
@ -326,8 +336,8 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * S_WorldPitch, V_CellsPerMeter * S_WorldPitch); Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * S_WorldPitch, V_CellsPerMeter * S_WorldPitch);
// u32 max_particles = Kibi(128); // u32 max_particles = Kibi(128);
u32 max_particles = Mebi(1); // u32 max_particles = Mebi(1);
// u32 max_particles = Mebi(2); u32 max_particles = Mebi(2);
// u32 max_particles = Mebi(16); // u32 max_particles = Mebi(16);
// Init gpu state // Init gpu state
@ -381,7 +391,8 @@ void V_TickForever(WaveLaneCtx *lane)
gpu_perm, cl, gpu_perm, cl,
// G_Format_R8_Uint, // G_Format_R8_Uint,
// G_Format_R11G11B10_Float, // G_Format_R11G11B10_Float,
G_Format_R10G10B10A2_Unorm, // G_Format_R10G10B10A2_Unorm,
G_Format_R16G16B16A16_Float,
cells_dims, cells_dims,
G_Layout_DirectQueue_ShaderReadWrite, G_Layout_DirectQueue_ShaderReadWrite,
.flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
@ -394,7 +405,8 @@ void V_TickForever(WaveLaneCtx *lane)
gpu_perm, cl, gpu_perm, cl,
// G_Format_R8_Uint, // G_Format_R8_Uint,
// G_Format_R11G11B10_Float, // G_Format_R11G11B10_Float,
G_Format_R10G10B10A2_Unorm, // G_Format_R10G10B10A2_Unorm,
G_Format_R16G16B16A16_Float,
cells_dims, cells_dims,
G_Layout_DirectQueue_ShaderReadWrite, G_Layout_DirectQueue_ShaderReadWrite,
.flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
@ -615,6 +627,7 @@ void V_TickForever(WaveLaneCtx *lane)
b32 received_unseen_tick = 0; b32 received_unseen_tick = 0;
b32 tiles_dirty = 0; b32 tiles_dirty = 0;
b32 should_clear_particles = 0;
for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next) for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next)
{ {
S_Snapshot *snapshot = &n->snapshot; 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) for (S_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
{ {
S_Delta *delta = &dn->delta; 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) if (delta->kind == S_DeltaKind_RawTiles || delta->kind == S_DeltaKind_Tile)
{ {
tiles_dirty = 1; 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 //- Copy sim debug info
@ -2198,6 +2190,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
{ {
UI_BuildLabelF("World seed: 0x%F", FmtHex(world->seed)); 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); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
{ {
@ -2390,7 +2383,6 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Process vis commands //- Process vis commands
b32 should_clear_particles = 0;
for (V_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) for (V_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
{ {
String cmd_name = cmd_node->cmd.name; String cmd_name = cmd_node->cmd.name;
@ -2478,14 +2470,28 @@ void V_TickForever(WaveLaneCtx *lane)
} }
} break; } break;
case V_CmdKind_reset_world:
case V_CmdKind_spawn: case V_CmdKind_spawn:
{
// 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); S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
cmd->delta.kind = S_DeltaKind_RawEnt; cmd->delta.kind = S_DeltaKind_RawEnt;
S_Ent *ent = &cmd->delta.ent; S_Ent *ent = &cmd->delta.ent;
*ent = S_NilEnt; *ent = S_NilEnt;
ent->key = V.player_key; ent->key = V.player_key;
ent->xf = XformFromPos(frame->world_cursor); ent->xf = XformFromPos(player_pos);
ent->move_speed = 0.075; ent->move_speed = 0.075;
ent->is_player = 1; ent->is_player = 1;
ent->local_shape = S_ShapeFromDesc( ent->local_shape = S_ShapeFromDesc(
@ -2495,6 +2501,7 @@ void V_TickForever(WaveLaneCtx *lane)
); );
ent->has_weapon = 1; ent->has_weapon = 1;
ent->exists = 1; ent->exists = 1;
}
} break; } break;
case V_CmdKind_spawn_dummy: case V_CmdKind_spawn_dummy:
@ -2600,64 +2607,155 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Spawn test bullet particles //- Push test bullet particles
// TODO: Not like this // TODO: Not like this
{
PERSIST Vec2 start = {5, 5};
PERSIST Vec2 end = {3, -20};
if (frame->held_buttons[Button_G])
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
{ {
end = frame->world_cursor; if (bullet->is_bullet)
if (!last_frame->held_buttons[Button_G])
{ {
start = end; // FIXME: Truncate bullet trail at hit
Vec2 start = bullet->bullet_start;
Vec2 end = bullet->bullet_end;
b32 skip = 0;
if (bullet->has_hit)
{
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;
} }
Vec2 vel = SubVec2(end, start); if (!skip)
{
f32 trail_len = Vec2Len(SubVec2(end, start)); f32 trail_len = Vec2Len(SubVec2(end, start));
f32 particles_count = MaxF32(trail_len * 64, 1); // 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(PerpVec2(SubVec2(end, start)));
f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
V_Emitter *emitter = V_PushEmitter(particles_count); V_Emitter *emitter = V_PushEmitter(particles_count);
emitter->lifetime = 1; // emitter->flags |= V_ParticleFlag_StainOnPrune;
emitter->lifetime_spread = 1; // 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 = angle;
// emitter->angle_spread = Tau / 4;
emitter->angle_spread = Tau / 4; emitter->angle_spread = Tau / 4;
emitter->start = start; emitter->start = start;
emitter->end = end; emitter->end = end;
// emitter->color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
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;
}
}
}
// {
// 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 = 0;
// emitter->speed_spread = 1; // emitter->speed_spread = 1;
emitter->speed = 1; // // emitter->speed = 1;
emitter->speed_spread = 1; // // emitter->speed_spread = 1;
emitter->velocity_falloff = 0; // // emitter->velocity_falloff = 1;
emitter->velocity_falloff_spread = 0; // // emitter->velocity_falloff_spread = 0;
start = end; // start = end;
end = AddVec2(end, vel); // end = AddVec2(end, vel);
} // }
@ -2680,53 +2778,33 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Spawn test blood particles //- Push test blood particles
// TODO: Not like this // TODO: Not like this
{
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)
{ {
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
{
if (bullet->is_bullet && bullet->has_hit)
{
// Vec2 bullet_start = bullet->start;
// Vec2 bullet_end = bullet->end;
Vec2 hit_entry = bullet->hit_entry;
Vec2 hit_entry_normal = bullet->hit_entry_normal;
Vec2 bullet_vel = SubVec2(bullet->bullet_end, bullet->bullet_start);
V_DrawLine(bullet->bullet_start, bullet->bullet_end, Color_Cyan);
V_ParticleFlag flags = 0; V_ParticleFlag flags = 0;
flags |= V_ParticleFlag_PruneWhenStill;
flags |= V_ParticleFlag_StainOnPrune; flags |= V_ParticleFlag_StainOnPrune;
if (TweakBool("Emitter stain trail", 1)) if (TweakBool("Emitter stain trail", 0))
{ {
flags |= V_ParticleFlag_StainTrail; flags |= V_ParticleFlag_StainTrail;
} }
@ -2739,15 +2817,17 @@ void V_TickForever(WaveLaneCtx *lane)
V_Emitter *emitter = V_PushEmitter(count); V_Emitter *emitter = V_PushEmitter(count);
emitter->flags = flags; emitter->flags = flags;
Vec2 dir = victim_raycast.normal; // Vec2 dir = hit_entry_normal;
emitter->start = victim_raycast.p; Vec2 dir = NormVec2(NegVec2(bullet_vel));
emitter->start = hit_entry;
emitter->end = emitter->start; emitter->end = emitter->start;
emitter->speed = speed; emitter->speed = speed;
emitter->speed_spread = speed * 2; emitter->speed_spread = speed * 2;
emitter->velocity_falloff = falloff; emitter->velocity_falloff = falloff;
emitter->velocity_falloff_spread = falloff * 2; emitter->velocity_falloff_spread = falloff * 1.5;
emitter->angle = AngleFromVec2(dir); emitter->angle = AngleFromVec2(dir);
// emitter->angle_spread = Tau / 4; // emitter->angle_spread = Tau / 4;
@ -2755,7 +2835,7 @@ void V_TickForever(WaveLaneCtx *lane)
// emitter->angle_spread = Tau / 32; // emitter->angle_spread = Tau / 32;
emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1));
emitter->color_spread = VEC3(0.1, 0, 0); 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));
@ -2767,20 +2847,113 @@ void V_TickForever(WaveLaneCtx *lane)
// V_DrawPoint(victim_raycast.p, Color_Green); // V_DrawPoint(victim_raycast.p, Color_Green);
// V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White); // 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)
// { // {
// 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)
// // {
// // }
// }
// }
// } // }
}
}
}
////////////////////////////// //////////////////////////////
//- Spawn test emitter //- Push test emitter
// Spawn test emitter
if (frame->held_buttons[Button_F]) if (frame->held_buttons[Button_F])
{ {
@ -2870,12 +3043,7 @@ void V_TickForever(WaveLaneCtx *lane)
case S_DebugDrawKind_Point: case S_DebugDrawKind_Point:
{ {
Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, desc->point.p); Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, desc->point.p);
S_Shape ui_shape = S_ShapeFromDesc( V_DrawPoint(ui_p, color);
.count = 1,
.points = { ui_p },
.radius = radius
);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_None);
} break; } break;
case S_DebugDrawKind_Line: case S_DebugDrawKind_Line:
@ -2896,8 +3064,8 @@ void V_TickForever(WaveLaneCtx *lane)
case S_DebugDrawKind_Shape: case S_DebugDrawKind_Shape:
{ {
S_Shape ui_shape = S_MulXformShape(frame->xf.world_to_ui, desc->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_Line);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_None); // V_DrawShape(ui_shape, color, detail, V_DrawFlag_None);
} break; } 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 //- End frame

View File

@ -12,9 +12,10 @@
X(toggle_console, Toggle Developer Console, V_CmdDescFlag_None, V_HOTKEY( Button_GraveAccent ), ) \ 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_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(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, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_Q ), ) \
X(spawn_dummy, Spawn Dummy, V_CmdDescFlag_None, V_HOTKEY( Button_R ), ) \ 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(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 ), ) \ 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_DrawShape(S_Shape shape, Vec4 srgb, i32 detail, V_DrawFlag flags);
void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb); void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb);
void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags); void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags);
void V_DrawPoint(Vec2 p, Vec4 srgb);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Theme //~ Theme

View File

@ -153,30 +153,68 @@ ComputeShader2D(V_BackdropCS, 8, 8)
// TODO: Remove this // TODO: Remove this
// Cells test // Cells test
{ {
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells); RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(world_pos, 1))); 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); Vec4 stain = stains.Load(cell_pos);
if (stain.a != 0) Vec4 cell = cells.Load(cell_pos);
{ // cell.rgb *= cell.a;
result = stain; // 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 // TODO: Remove this
// Stains test // Stains test
// { // {
@ -258,7 +296,6 @@ ComputeShader(V_EmitParticlesCS, 64)
if (emitter_idx < params.emitters_count) if (emitter_idx < params.emitters_count)
{ {
V_Emitter emitter = emitters[emitter_idx]; V_Emitter emitter = emitters[emitter_idx];
for (u32 i = 0; i < emitter.count; ++i) for (u32 i = 0; i < emitter.count; ++i)
{ {
u32 particle_seq = emitter.first_particle_seq + 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 seed0 = MixU64(emitter.seed + particle.seq);
u64 seed1 = MixU64(seed0); u64 seed1 = MixU64(seed0);
f32 rand_speed = (f32)((seed0 >> 0) & 0xFFFF) / (f32)0xFFFF; f32 rand_speed = (f32)((seed0 >> 0) & 0xFFFF) / (f32)0xFFFF;
f32 rand_angle = (f32)((seed0 >> 16) & 0xFFFF) / (f32)0xFFFF; f32 rand_angle = (f32)((seed0 >> 16) & 0xFFFF) / (f32)0xFFFF;
f32 rand_offset = (f32)((seed0 >> 32) & 0xFFFF) / (f32)0xFFFF; f32 rand_offset = (f32)((seed0 >> 32) & 0xFFFF) / (f32)0xFFFF;
f32 rand_falloff = (f32)((seed0 >> 48) & 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 speed = emitter.speed + (rand_speed - 0.5) * emitter.speed_spread;
f32 angle = emitter.angle + (rand_angle - 0.5) * emitter.angle_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 velocity_falloff = emitter.velocity_falloff + (rand_falloff - 0.5) * emitter.velocity_falloff_spread;
f32 lifetime = emitter.lifetime + (rand_lifetime - 0.5) * emitter.lifetime_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; Vec2 offset = (emitter.end - emitter.start) * rand_offset;
particle.flags = emitter.flags; particle.flags = emitter.flags;
@ -322,12 +364,24 @@ ComputeShader(V_SimParticlesCS, 64)
particle.emitter_init_num = 0; 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 // Simulate
f32 old_exists = particle.exists;
{ {
particle.pos += particle.velocity * params.dt; particle.pos += particle.velocity * params.dt;
particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * params.dt); particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * params.dt);
particle.lifetime -= params.dt; particle.exists -= params.dt / particle.lifetime;
if (particle.exists < 0.0001 || particle.lifetime < 0.0001 || dot(particle.velocity, particle.velocity) < (0.0001 * 0.0001)) 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; particle.exists = 0;
} }
@ -336,19 +390,19 @@ ComputeShader(V_SimParticlesCS, 64)
// Commit // Commit
{ {
// FIXME: Atomic write // FIXME: Atomic write
Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(particle.pos, 1))); if (is_in_bounds)
if (cell_pos.x >= 0 && cell_pos.y >= 0 && cell_pos.x < countof(stains).x && cell_pos.y < countof(stains).y)
{ {
b32 should_stain = 0; 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; should_stain = 1;
} }
Vec4 color = particle.color;
cells[cell_pos] = Vec4(particle.color, 1); color.a *= old_exists;
cells[cell_pos] = color;
if (should_stain) if (should_stain)
{ {
stains[cell_pos] = Vec4(particle.color, 1); stains[cell_pos] = color;
} }
} }
else else

View File

@ -87,8 +87,9 @@ Struct(V_GpuParams)
Enum(V_ParticleFlag) Enum(V_ParticleFlag)
{ {
V_ParticleFlag_None = 0, V_ParticleFlag_None = 0,
V_ParticleFlag_StainOnPrune = (1 << 0), V_ParticleFlag_PruneWhenStill = (1 << 0),
V_ParticleFlag_StainTrail = (1 << 1), V_ParticleFlag_StainOnPrune = (1 << 1),
V_ParticleFlag_StainTrail = (1 << 2),
}; };
Struct(V_Emitter) Struct(V_Emitter)
@ -115,7 +116,7 @@ Struct(V_Emitter)
f32 velocity_falloff_spread; f32 velocity_falloff_spread;
Vec4 color_lin; Vec4 color_lin;
Vec3 color_spread; Vec4 color_spread;
}; };
// TODO: Pack this efficiently // TODO: Pack this efficiently
@ -133,7 +134,7 @@ Struct(V_Particle)
f32 lifetime; f32 lifetime;
f32 velocity_falloff; f32 velocity_falloff;
Vec3 color; Vec4 color;
}; };
#if IsLanguageC #if IsLanguageC