update user & sim worlds via structured deltas

This commit is contained in:
jacob 2026-01-05 01:45:10 -06:00
parent 54f77c87be
commit 6a305b4803
13 changed files with 522 additions and 502 deletions

View File

@ -6,7 +6,7 @@ pushd build
for %%a in (%*) do set "%%a=1"
set program_build_cmd=meta.exe %*
set meta_build_warnings=-W4 -WX -wd4244 -wd4201 -wd4324 -wd4100 -wd4101 -wd4189
set meta_build_warnings=-W4 -WX -we4668 -wd4244 -wd4201 -wd4324 -wd4100 -wd4101 -wd4189 -wd4200 -wd4702 -wd4305
set meta_build_cmd=cl.exe ../src/meta/meta.c -Od -Z7 -nologo -diagnostics:column %meta_build_warnings% -link -DEBUG:FULL -INCREMENTAL:NO
set meta_rebuild_code=1317212284

View File

@ -484,7 +484,6 @@
typedef double f64;
typedef i8 b8;
typedef u32 b32;
Struct(U128) { u64 hi; u64 lo; };
#elif IsLanguageG
typedef int i32;
typedef uint u32;
@ -516,6 +515,12 @@
#define IsNan(x) isnan(x)
#endif
//- u128
#if IsLanguageC
Struct(u128) { u64 lo; u64 hi; };
#define U128(_hi, _lo) ((u128) { .hi = (_hi), .lo = (_lo) })
#endif
////////////////////////////////////////////////////////////
//~ Atomics

View File

@ -15,10 +15,10 @@
#define G_D12_TearingIsAllowed 1
#define G_D12_FrameLatency 1
#define G_D12_SwapchainBufferCount 3
#define G_D12_SwapchainFlags (0 \
| ((G_D12_TearingIsAllowed != 0) * DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) \
| ((G_D12_FrameLatency != 0) * DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) \
#define G_D12_SwapchainBufferCount 2
#define G_D12_SwapchainFlags ( \
((G_D12_TearingIsAllowed != 0) * DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) | \
((G_D12_FrameLatency != 0) * DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) \
)
#define G_D12_MaxCbvSrvUavDescriptors (1024 * 128)

View File

@ -1,9 +1,7 @@
S_Ctx S = Zi;
Readonly S_ReadonlyCtx S_ro = {
.nil_ent = {
.xf = CompXformIdentity
}
Readonly S_Ent S_NilEnt = {
.xf = CompXformIdentity,
};
////////////////////////////////////////////////////////////
@ -39,17 +37,17 @@ void S_Shutdown(void)
b32 S_IsKeyNil(S_Key key)
{
return key.v.hi == 0 && key.v.lo == 0;
return key.v == 0;
}
b32 S_IsEntNil(S_Ent *ent)
{
return ent == 0 || ent == &S_ro.nil_ent;
return ent == 0 || ent == &S_NilEnt;
}
b32 S_MatchKey(S_Key a, S_Key b)
{
return a.v.hi == b.v.hi && a.v.lo == b.v.lo;
return a.v == b.v;
}
////////////////////////////////////////////////////////////
@ -83,37 +81,86 @@ String S_NameFromTileKind(S_TileKind kind)
return result;
}
Rng2I32 S_UpdateTilesInPlaceFromPlacement(u8 *tiles, S_TilePlacement placement)
{
Rng2I32 dirty_rect = Zi;
switch (placement.placement_kind)
{
case S_TilePlacementKind_Raw:
{
CopyBytes(tiles, placement.raw_tiles, S_WorldSize * S_WorldSize * 4);
dirty_rect = RNG2I32(
VEC2I32(0, 0),
VEC2I32(S_WorldSize * 2, S_WorldSize * 2)
);
} break;
////////////////////////////////////////////////////////////
//~ Delta helpers
case S_TilePlacementKind_Range:
{
S_TileKind tile = placement.tile_kind;
Rng2I32 range = placement.range;
for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y)
{
for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x)
{
Vec2I32 tile_pos = VEC2I32(tile_x, tile_y);
i32 tile_idx = S_TileIdxFromTilePos(tile_pos);
tiles[tile_idx] = (u8)tile;
}
}
dirty_rect = range;
} break;
Rng2I32 S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta)
{
// FIXME: Bounds check tile deltas
Rng2I32 dirty_tile_rect = Zi;
if (0)
{
}
return dirty_rect;
//- Raw ent
if (delta->kind == S_DeltaKind_RawEnt)
{
S_Key key = delta->ent.key;
S_Ent *ent = S_EntFromKey(world, key);
if (!ent->valid)
{
// FIXME: Use ent free list
ent = PushStructNoZero(arena, S_Ent);
*ent = S_NilEnt;
ent->key = key;
ent->exists = 1;
S_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count];
DllQueuePushNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
DllQueuePush(world->first_ent, world->last_ent, ent);
++world->ents_count;
}
S_Ent *old_next = ent->next;
S_Ent *old_prev = ent->prev;
S_Ent *old_next_in_bin = ent->next_in_bin;
S_Ent *old_prev_in_bin = ent->prev_in_bin;
{
S_Ent *src = &delta->ent;
*ent = *src;
}
ent->next = old_next;
ent->prev = old_prev;
ent->next_in_bin = old_next_in_bin;
ent->prev_in_bin = old_prev_in_bin;
ent->valid = 1;
}
//- Raw tiles
else if (delta->kind == S_DeltaKind_RawTiles)
{
Rng2I32 range = delta->tile_range;
for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y)
{
i32 src_tile_y = tile_y - range.p0.y;
for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x)
{
i32 src_tile_x = tile_x - range.p0.x;
Vec2I32 src_tile_pos = VEC2I32(src_tile_x, src_tile_y);
i32 src_tile_idx = S_TileIdxFromTilePos(src_tile_pos);
u8 src_tile = delta->raw_tiles[src_tile_idx];
Vec2I32 tile_pos = VEC2I32(tile_x, tile_y);
i32 tile_idx = S_TileIdxFromTilePos(tile_pos);
world->tiles[tile_idx] = src_tile;
}
}
dirty_tile_rect = range;
}
//- Tile range
else if (delta->kind == S_DeltaKind_Tile)
{
S_TileKind tile = delta->tile_kind;
Rng2I32 range = delta->tile_range;
for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y)
{
for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x)
{
Vec2I32 tile_pos = VEC2I32(tile_x, tile_y);
i32 tile_idx = S_TileIdxFromTilePos(tile_pos);
world->tiles[tile_idx] = (u8)tile;
}
}
dirty_tile_rect = range;
}
return dirty_tile_rect;
}
////////////////////////////////////////////////////////////
@ -836,44 +883,18 @@ S_CollisionData S_CollisionDataFromShapes(S_Shape shape0, S_Shape shape1)
////////////////////////////////////////////////////////////
//~ Lookup helpers
S_Lookup S_LookupFromWorld(Arena *arena, S_World *world)
S_Ent *S_EntFromKey(S_World *world, S_Key key)
{
S_Lookup lookup = Zi;
lookup.bins_count = 4096;
lookup.bins = PushStructs(arena, S_LookupEntNode *, lookup.bins_count);
for (i64 ent_idx = 0; ent_idx < world->ents_count; ++ent_idx)
S_Ent *result = &S_NilEnt;
if (!S_IsKeyNil(key) && world->ent_bins_count > 0)
{
S_Ent *ent = &world->ents[ent_idx];
if (ent->active)
S_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count];
for (S_Ent *e = bin->first; e; e = e->next_in_bin)
{
S_Key key = ent->key;
S_LookupEntNode *n = PushStruct(arena, S_LookupEntNode);
n->ent = ent;
S_LookupEntNode **bin = &lookup.bins[ent->key.v.lo % lookup.bins_count];
SllStackPush(*bin, n);
}
}
return lookup;
}
S_Ent *S_EntFromKey(S_Lookup *lookup, S_Key key)
{
S_Ent *result = &S_ro.nil_ent;
if (!S_IsKeyNil(key))
{
i64 bins_count = lookup->bins_count;
if (bins_count > 0)
{
S_LookupEntNode *n = lookup->bins[key.v.lo % bins_count];
for (; n; n = n->next)
if (e->key.v == key.v)
{
if (S_MatchKey(n->ent->key, key))
{
result = n->ent;
break;
}
result = e;
break;
}
}
}
@ -883,67 +904,22 @@ S_Ent *S_EntFromKey(S_Lookup *lookup, S_Key key)
////////////////////////////////////////////////////////////
//~ Iteration helpers
S_Ent *S_FirstEnt(S_Iter *iter, S_World *world)
S_Ent *S_FirstEnt(S_World *world)
{
ZeroStruct(iter);
iter->world = world;
return S_NextEnt(iter);
}
S_Ent *S_NextEnt(S_Iter *iter)
{
S_World *world = iter->world;
S_Ent *result = &S_ro.nil_ent;
i64 ent_idx = iter->cur_idx;
for (; ent_idx < world->ents_count; ++ent_idx)
S_Ent *result = &S_NilEnt;
if (world->first_ent)
{
S_Ent *ent = &world->ents[ent_idx];
if (ent->active)
{
result = ent;
break;
}
result = world->first_ent;
}
iter->cur_idx = ent_idx + 1;
return result;
}
////////////////////////////////////////////////////////////
//~ Snapshot helpers
S_World *S_WorldFromSnapshot(Arena *arena, S_Snapshot *snapshot)
S_Ent *S_NextEnt(S_Ent *e)
{
S_World *world = PushStruct(arena, S_World);
// Copy ents
world->ents = PushStructsNoZero(arena, S_Ent, snapshot->ents_count);
CopyStructs(world->ents, snapshot->ents, snapshot->ents_count);
world->ents_count = snapshot->ents_count;
world->tick = snapshot->tick;
world->time_ns = snapshot->time_ns;
return world;
}
////////////////////////////////////////////////////////////
//~ Sorting
MergesortCompareFuncDef(S_SortEntsByKeyCmp, arg_a, arg_b, _)
{
S_Ent *a = *(S_Ent **)arg_a;
S_Ent *b = *(S_Ent **)arg_b;
S_Key a_key = a->key;
S_Key b_key = b->key;
i32 result = 0;
if (result == 0)
S_Ent *result = &S_NilEnt;
if (e && e->next)
{
result = ((a_key.v.lo < b_key.v.lo) - (a_key.v.lo > b_key.v.lo));
}
if (result == 0)
{
result = ((a_key.v.hi < b_key.v.hi) - (a_key.v.hi > b_key.v.hi));
result = e->next;
}
return result;
}
@ -1006,13 +982,8 @@ void S_TickForever(WaveLaneCtx *lane)
S.debug_draw_descs_arena = AcquireArena(Gibi(64));
//- World data
Arena *ents_arena = AcquireArena(Gibi(64));
S_World *world = PushStruct(perm, S_World);
world->ents = ArenaFirst(ents_arena, S_Ent);
i64 first_free_ent_num = 0;
u8 *tiles = PushBytes(perm, S_WorldSize * S_WorldSize * 4, alignof(S_TileKind));
Arena *world_arena = AcquireArena(Gibi(64));
S_World *world = 0;
// TODO: Real per-client deltas
b32 has_sent_initial_tick = 0;
@ -1027,8 +998,55 @@ void S_TickForever(WaveLaneCtx *lane)
S.debug_draw_enabled = TweakBool("Simulation debug draw", 1);
ResetArena(frame_arena);
S_Iter iter = Zi;
S_Lookup lookup = Zi;
//////////////////////////////
//- Swap
{
b32 swapin = IsSwappedIn();
b32 swapout = shutdown && IsSwappingOut();
//- Swap in
if (!world)
{
String packed = Zi;
if (swapin)
{
packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp"));
}
S_TranscodeResult tr = S_TranscodeWorld(frame_arena, 0, packed, 0);
ResetArena(world_arena);
world = PushStruct(world_arena, S_World);
world->tick = tr.unpacked->tick;
world->time_ns = tr.unpacked->time_ns;
world->ent_bins_count = Kibi(16);
world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count);
// Copy tiles
world->tiles = PushStructsNoZero(world_arena, u8, S_TilesCount);
CopyStructs(world->tiles, tr.unpacked->tiles, S_TilesCount);
// Copy ents
world->ents_count = tr.unpacked->ents_count;
for (S_Ent *src = tr.unpacked->first_ent; src; src = src->next)
{
S_Ent *dst = PushStructNoZero(world_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);
}
}
//- Swap out
if (swapout)
{
S_TranscodeResult tr = S_TranscodeWorld(frame_arena, world, Zstr, 1);
WriteSwappedState(Lit("pp_sim.swp"), tr.packed);
}
}
//////////////////////////////
//- Begin sim frame
@ -1038,46 +1056,6 @@ void S_TickForever(WaveLaneCtx *lane)
f64 sim_dt = SecondsFromNs(sim_dt_ns);
world->tick += 1;
//////////////////////////////
//- Swap
{
b32 swapin = world->tick == 1 && IsSwappedIn();
b32 swapout = !swapin && shutdown && IsSwappingOut();
//- Swap out
if (swapout)
{
S_TranscodeResult tr = S_TranscodeLevel(frame_arena, 1, Zstr, world, tiles);
if (tr.ok)
{
WriteSwappedState(Lit("pp_sim.swp"), tr.packed);
}
}
//- Swap in
if (swapin)
{
String packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp"));
S_TranscodeResult tr = S_TranscodeLevel(frame_arena, 0, packed, world, tiles);
if (tr.ok)
{
// Unpack tiles
CopyBytes(tiles, tr.unpacked.tiles, S_WorldSize * S_WorldSize * 4);
// Unpack world
world->tick = tr.unpacked.world.tick;
world->time_ns = tr.unpacked.world.time_ns;
world->ents_count = tr.unpacked.world.ents_count;
ResetArena(ents_arena);
PushStructsNoZero(ents_arena, S_Ent, world->ents_count);
CopyStructs(world->ents, tr.unpacked.world.ents, world->ents_count);
}
}
}
lookup = S_LookupFromWorld(frame_arena, world);
//////////////////////////////
//- Pop sim commands
@ -1094,60 +1072,43 @@ void S_TickForever(WaveLaneCtx *lane)
UnlockTicketMutex(&S.input_back_tm);
//////////////////////////////
//- Store double-buffered entity data
//- Update double-buffered entity data
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
ent->last_xf = ent->xf;
}
//////////////////////////////
//- Process world edit commands
//- Process user world delta commands
u64 tile_placements_count = 0;
// 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);
for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
{
S_Cmd *cmd = &cmd_node->cmd;
// Spawn entity
if (cmd->kind == S_CmdKind_Spawn)
if (cmd->kind == S_CmdKind_Delta)
{
S_Ent *src = &cmd->ent;
S_Key key = src->key;
if (!S_IsKeyNil(key))
S_Delta *delta = &cmd->delta;
b32 allow = 0;
if (delta->kind == S_DeltaKind_RawEnt)
{
S_Ent *dst = S_EntFromKey(&lookup, key);
if (S_IsEntNil(dst))
{
if (first_free_ent_num > 0)
{
dst = &world->ents[first_free_ent_num - 1];
first_free_ent_num = dst->next_free_ent_num;
}
else
{
dst = PushStructNoZero(ents_arena, S_Ent);
}
*dst = S_ro.nil_ent;
dst->key = key;
++world->ents_count;
}
*dst = *src;
dst->local_shape.points_count = MaxI32(dst->local_shape.points_count, 1);
dst->active = 1;
allow = 1;
}
if (delta->kind == S_DeltaKind_Tile)
{
allow = 1;
applied_user_tile_deltas[applied_user_tile_deltas_count] = delta;
applied_user_tile_deltas_count += 1;
}
if (allow)
{
S_UpdateWorldFromDelta(world_arena, world, delta);
}
}
// Place tiles
if (cmd->kind == S_CmdKind_Tile)
{
tile_placements_count += 1;
S_TilePlacement placement = cmd->tile_placement;
S_UpdateTilesInPlaceFromPlacement(tiles, placement);
}
}
lookup = S_LookupFromWorld(frame_arena, world);
//////////////////////////////
//- Update ent controls
@ -1157,8 +1118,8 @@ void S_TickForever(WaveLaneCtx *lane)
S_Cmd cmd = cmd_node->cmd;
if (cmd.kind == S_CmdKind_Control)
{
S_Ent *target = S_EntFromKey(&lookup, cmd.target);
if (target->active)
S_Ent *target = S_EntFromKey(world, cmd.target);
if (target->valid)
{
target->move = ClampVec2Len(cmd.move, 1);
target->look = cmd.look;
@ -1169,7 +1130,7 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Integrate control forces
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
Xform xf = ent->xf;
@ -1215,7 +1176,7 @@ void S_TickForever(WaveLaneCtx *lane)
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
Xform last_xf = ent->last_xf;
S_Shape last_world_shape = S_MulXformShape(last_xf, ent->local_shape);
@ -1260,8 +1221,8 @@ void S_TickForever(WaveLaneCtx *lane)
{
S_Constraint *constraint = &constraints[constraint_idx];
S_Ent *ent0 = S_EntFromKey(&lookup, constraint->ent0);
S_Ent *ent1 = S_EntFromKey(&lookup, constraint->ent1);
S_Ent *ent0 = S_EntFromKey(world, constraint->ent0);
S_Ent *ent1 = S_EntFromKey(world, constraint->ent1);
Vec2 old_dv0 = ent0->solved_dv;
Vec2 old_dv1 = ent1->solved_dv;
@ -1330,12 +1291,12 @@ void S_TickForever(WaveLaneCtx *lane)
}
// Update solved velocity
if (ent0->active)
if (ent0->valid)
{
ent0->solved_dv = AddVec2(ent0->solved_dv, dv0);
ent0->solved_dw += dw0;
}
if (ent1->active)
if (ent1->valid)
{
ent1->solved_dv = AddVec2(ent1->solved_dv, dv1);
ent1->solved_dw += dw1;
@ -1345,7 +1306,7 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Integrate velocities
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
Xform xf = ent->xf;
xf.og = AddVec2(xf.og, ent->solved_dv);
@ -1358,7 +1319,7 @@ void S_TickForever(WaveLaneCtx *lane)
if (S.debug_draw_enabled)
{
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
Xform xf = ent->xf;
S_Shape world_shape = S_MulXformShape(xf, ent->local_shape);
@ -1398,51 +1359,54 @@ void S_TickForever(WaveLaneCtx *lane)
S_Snapshot *snapshot = &snapshot_node->snapshot;
SllQueuePush(output->first_snapshot_node, output->last_snapshot_node, snapshot_node);
++output->snapshots_count;
snapshot->ents_count = world->ents_count;
snapshot->tick = world->tick;
snapshot->ents = PushStructsNoZero(output->arena, S_Ent, snapshot->ents_count);
for (i64 ent_idx = 0; ent_idx < snapshot->ents_count; ++ent_idx)
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)
{
S_Ent *src = &world->ents[ent_idx];
S_Ent *dst = &snapshot->ents[ent_idx];
*dst = *src;
S_Delta *delta = 0;
{
S_DeltaNode *dn = PushStruct(output->arena, S_DeltaNode);
snapshot->deltas_count += 1;
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];
*delta = *src;
}
// Push tile map
// Push full tile delta
if (!has_sent_initial_tick)
{
u64 dst_tile_placement_idx = 0;
snapshot->tile_placements_count = tile_placements_count;
snapshot->tile_placements = PushStructs(output->arena, S_TilePlacement, tile_placements_count + 1);
// Send raw tile map
if (!has_sent_initial_tick)
S_Delta *delta = 0;
{
snapshot->tile_placements_count += 1;
{
S_TilePlacement *dst = &snapshot->tile_placements[dst_tile_placement_idx];
dst->placement_kind = S_TilePlacementKind_Raw;
dst->raw_tiles = PushStructsNoZero(output->arena, u8, S_WorldSize * S_WorldSize * 4);
CopyBytes(dst->raw_tiles, tiles, S_WorldSize * S_WorldSize * 4);
}
dst_tile_placement_idx += 1;
has_sent_initial_tick = 1;
S_DeltaNode *dn = PushStruct(output->arena, S_DeltaNode);
snapshot->deltas_count += 1;
SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn);
delta = &dn->delta;
}
delta->kind = S_DeltaKind_RawTiles;
delta->raw_tiles = PushStructsNoZero(output->arena, u8, S_TilesCount);
delta->tile_range = RNG2I32(VEC2I32(0, 0), VEC2I32(S_TilesPitch, S_TilesPitch));
CopyBytes(delta->raw_tiles, world->tiles, S_TilesCount);
has_sent_initial_tick = 1;
}
// Forward range tile placements
// Push full entity deltas
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
S_Delta *delta = 0;
{
u64 src_tile_placement_idx = 0;
for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node && src_tile_placement_idx < tile_placements_count; cmd_node = cmd_node->next)
{
S_Cmd *cmd = &cmd_node->cmd;
if (cmd->kind == S_CmdKind_Tile)
{
S_TilePlacement *dst = &snapshot->tile_placements[src_tile_placement_idx];
*dst = cmd->tile_placement;
dst_tile_placement_idx += 1;
src_tile_placement_idx += 1;
}
}
S_DeltaNode *dn = PushStruct(output->arena, S_DeltaNode);
snapshot->deltas_count += 1;
SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn);
delta = &dn->delta;
}
delta->kind = S_DeltaKind_RawEnt;
delta->ent = *ent;
}
// Push debug draw information

View File

@ -5,26 +5,7 @@
Struct(S_Key)
{
U128 v;
};
////////////////////////////////////////////////////////////
//~ Tile types
Enum(S_TilePlacementKind)
{
S_TilePlacementKind_Raw,
S_TilePlacementKind_Range,
};
Struct(S_TilePlacement)
{
S_TilePlacementKind placement_kind;
u8 *raw_tiles;
S_TileKind tile_kind;
Rng2I32 range;
u64 v;
};
////////////////////////////////////////////////////////////
@ -56,15 +37,27 @@ Struct(S_Shape)
Struct(S_Ent)
{
//////////////////////////////
//- Internal world state
S_Ent *next;
S_Ent *prev;
S_Ent *next_in_bin;
S_Ent *prev_in_bin;
b32 valid;
//////////////////////////////
//- Persistent data
b32 active;
S_Key key;
//////////////////////////////
//- Build data
f32 exists;
Xform last_xf;
Xform xf;
@ -81,33 +74,12 @@ Struct(S_Ent)
Vec2 solved_dv;
f32 solved_dw;
//////////////////////////////
//- Internal sim data
i64 next_free_ent_num;
};
////////////////////////////////////////////////////////////
//~ Ent container types
Struct(S_EntArray)
Struct(S_EntBin)
{
u64 count;
S_Ent *ents;
};
Struct(S_EntListNode)
{
S_EntListNode *next;
S_Ent ent;
};
Struct(S_EntList)
{
S_EntListNode *first;
S_EntListNode *last;
u64 count;
S_Ent *first;
S_Ent *last;
};
////////////////////////////////////////////////////////////
@ -168,21 +140,6 @@ Struct(S_Constraint)
S_Shape shape1;
};
////////////////////////////////////////////////////////////
//~ Lookup types
Struct(S_LookupEntNode)
{
S_LookupEntNode *next;
S_Ent *ent;
};
Struct(S_Lookup)
{
S_LookupEntNode **bins;
i64 bins_count;
};
////////////////////////////////////////////////////////////
//~ World types
@ -191,8 +148,38 @@ Struct(S_World)
i64 tick;
i64 time_ns;
S_Ent *ents;
i64 ents_count;
S_Ent *first_ent;
S_Ent *last_ent;
i64 ent_bins_count;
S_EntBin *ent_bins;
u8 *tiles;
};
Enum(S_DeltaKind)
{
S_DeltaKind_RawEnt,
S_DeltaKind_RawTiles,
S_DeltaKind_Tile,
};
Struct(S_Delta)
{
S_DeltaKind kind;
S_Ent ent;
u8 *raw_tiles;
S_TileKind tile_kind;
Rng2I32 tile_range;
};
Struct(S_DeltaNode)
{
S_DeltaNode *next;
S_Delta delta;
};
Struct(S_Snapshot)
@ -200,11 +187,9 @@ Struct(S_Snapshot)
i64 tick;
i64 time_ns;
S_Ent *ents;
i64 ents_count;
S_TilePlacement *tile_placements;
u64 tile_placements_count;
i64 deltas_count;
S_DeltaNode *first_delta_node;
S_DeltaNode *last_delta_node;
};
Struct(S_SnapshotNode)
@ -213,23 +198,13 @@ Struct(S_SnapshotNode)
S_Snapshot snapshot;
};
////////////////////////////////////////////////////////////
//~ Iterator types
Struct(S_Iter)
{
S_World *world;
i64 cur_idx;
};
////////////////////////////////////////////////////////////
//~ Command types
Enum(S_CmdKind)
{
S_CmdKind_Nop,
S_CmdKind_Tile,
S_CmdKind_Spawn,
S_CmdKind_Delta,
S_CmdKind_Control,
};
@ -237,11 +212,8 @@ Struct(S_Cmd)
{
S_CmdKind kind;
// Tiles
S_TilePlacement tile_placement;
// Spawn
S_Ent ent;
// Delta
S_Delta delta;
// Control
S_Key target;
@ -331,13 +303,8 @@ Struct(S_Ctx)
S_OutputState output_states[2];
};
Struct(S_ReadonlyCtx)
{
S_Ent nil_ent;
};
extern S_Ctx S;
extern Readonly S_ReadonlyCtx S_ro;
extern Readonly S_Ent S_NilEnt;
////////////////////////////////////////////////////////////
//~ Bootstrap
@ -361,7 +328,11 @@ S_Key S_RandKey(void);
//~ Tile helpers
String S_NameFromTileKind(S_TileKind kind);
Rng2I32 S_UpdateTilesInPlaceFromPlacement(u8 *tiles, S_TilePlacement placement);
////////////////////////////////////////////////////////////
//~ Delta helpers
Rng2I32 S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta);
////////////////////////////////////////////////////////////
//~ Shape helpers
@ -385,24 +356,13 @@ S_CollisionData S_CollisionDataFromShapes(S_Shape shape0, S_Shape shape1);
////////////////////////////////////////////////////////////
//~ Lookup helpers
S_Lookup S_LookupFromWorld(Arena *arena, S_World *world);
S_Ent *S_EntFromKey(S_Lookup *lookup, S_Key key);
S_Ent *S_EntFromKey(S_World *world, S_Key key);
////////////////////////////////////////////////////////////
//~ Iteration helpers
S_Ent *S_FirstEnt(S_Iter *iter, S_World *world);
S_Ent *S_NextEnt(S_Iter *iter);
////////////////////////////////////////////////////////////
//~ Snapshot helpers
S_World *S_WorldFromSnapshot(Arena *arena, S_Snapshot *snapshot);
////////////////////////////////////////////////////////////
//~ Sorting
MergesortCompareFuncDef(S_SortEntsByKeyCmp, arg_a, arg_b, _);
S_Ent *S_FirstEnt(S_World *world);
S_Ent *S_NextEnt(S_Ent *e);
////////////////////////////////////////////////////////////
//~ Debug draw

View File

@ -4,13 +4,13 @@
Vec2I32 S_TilePosFromWorldPos(Vec2 p)
{
Vec2I32 result;
result.x = ClampI32((p.x + S_WorldSize / 2) * 2, 0, (S_WorldSize * 2) - 1);
result.y = ClampI32((p.y + S_WorldSize / 2) * 2, 0, (S_WorldSize * 2) - 1);
result.x = ClampI32((p.x + S_WorldPitch * ((f32)S_WorldPitch / (f32)S_TilesPitch)) * 2, 0, S_TilesPitch - 1);
result.y = ClampI32((p.y + S_WorldPitch * ((f32)S_WorldPitch / (f32)S_TilesPitch)) * 2, 0, S_TilesPitch - 1);
return result;
}
i32 S_TileIdxFromTilePos(Vec2I32 p)
{
i32 result = ClampI32(p.x + (p.y * S_WorldSize * 2), 0, S_WorldSize * S_WorldSize * 4);
i32 result = ClampI32(p.x + (p.y * S_TilesPitch), 0, S_TilesCount);
return result;
}

View File

@ -1,7 +1,9 @@
////////////////////////////////////////////////////////////
//~ Tile types
#define S_WorldSize 96
#define S_WorldPitch 96
#define S_TilesPitch (S_WorldPitch * 2)
#define S_TilesCount (S_TilesPitch * S_TilesPitch)
#define S_TilesXMacro(X) \
X(Empty) \

View File

@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//~ Transcode
S_TranscodeResult S_TranscodeLevel(Arena *arena, b32 pack, String packed, S_World *world, u8 *tiles)
S_TranscodeResult S_TranscodeWorld(Arena *arena, S_World *src_world, String src_packed, b32 pack)
{
S_TranscodeResult result = Zi;
result.ok = 1;
@ -24,27 +24,25 @@ S_TranscodeResult S_TranscodeLevel(Arena *arena, b32 pack, String packed, S_Worl
}
else
{
bb = BB_BuffFromString(packed);
bb = BB_BuffFromString(src_packed);
br = BB_ReaderFromBuff(&bb);
result.ok = BB_ReadUBits(&br, 32) == level_magic;
result.version = (S_Tv)BB_ReadUBits(&br, 32);
result.unpacked = PushStruct(arena, S_World);
}
//////////////////////////////
//- Transcode world metadata
if (result.ok)
if (pack)
{
if (pack)
{
BB_WriteIBits(&bw, world->tick, 64);
BB_WriteIBits(&bw, world->time_ns, 64);
}
else
{
result.unpacked.world.tick = BB_ReadIBits(&br, 64);
result.unpacked.world.time_ns = BB_ReadIBits(&br, 64);
}
BB_WriteIBits(&bw, src_world->tick, 64);
BB_WriteIBits(&bw, src_world->time_ns, 64);
}
else
{
result.unpacked->tick = BB_ReadIBits(&br, 64);
result.unpacked->time_ns = BB_ReadIBits(&br, 64);
}
//////////////////////////////
@ -52,28 +50,27 @@ S_TranscodeResult S_TranscodeLevel(Arena *arena, b32 pack, String packed, S_Worl
// TODO: Compress tile data
if (result.ok)
if (pack)
{
u64 tiles_count = S_WorldSize * S_WorldSize * 4;
if (pack)
String tiles_str = Zi;
tiles_str.len = S_TilesCount;
tiles_str.text = src_world->tiles;
BB_WriteUBits(&bw, tiles_str.len, 64);
BB_WriteBytes(&bw, tiles_str);
}
else
{
u64 raw_tiles_count = BB_ReadUBits(&br, 64);
u8 *raw_tiles = BB_ReadBytesRaw(&br, raw_tiles_count);
if (raw_tiles && raw_tiles_count == S_TilesCount)
{
String tiles_str = Zi;
tiles_str.len = tiles_count;
tiles_str.text = tiles;
BB_WriteBytes(&bw, tiles_str);
result.unpacked->tiles = PushStructsNoZero(arena, u8, raw_tiles_count);
CopyBytes(result.unpacked->tiles, raw_tiles, raw_tiles_count);
}
else
{
u8 *raw_tiles = BB_ReadBytesRaw(&br, tiles_count);
if (raw_tiles)
{
result.unpacked.tiles = PushStructsNoZero(arena, u8, S_WorldSize * S_WorldSize * 4);
CopyStructs(result.unpacked.tiles, raw_tiles, tiles_count);
}
else
{
result.ok = 0;
}
result.unpacked->tiles = PushStructs(arena, u8, S_TilesCount);
result.ok = 0;
}
}
@ -82,47 +79,46 @@ S_TranscodeResult S_TranscodeLevel(Arena *arena, b32 pack, String packed, S_Worl
// TODO: Compress entity data
// Write entity size & alignment
u32 ent_size = sizeof(S_Ent);
u32 ent_align = alignof(S_Ent);
if (result.ok)
{
if (pack)
{
u32 ent_size = sizeof(S_Ent);
u32 ent_align = alignof(S_Ent);
i64 ents_count = src_world->ents_count;
BB_WriteUBits(&bw, ent_size, 32);
BB_WriteUBits(&bw, ent_align, 32);
}
else
{
ent_size = BB_ReadUBits(&br, 32);
ent_align = BB_ReadUBits(&br, 32);
}
}
if (result.ok)
{
if (pack)
{
String ents_str = Zi;
ents_str.len = sizeof(S_Ent) * world->ents_count;
ents_str.text = (u8 *)world->ents;
BB_WriteUBits(&bw, world->ents_count, 64);
BB_WriteAlignBytes(&bw, alignof(S_Ent));
BB_WriteBytes(&bw, ents_str);
}
else
{
u64 ents_count = BB_ReadUBits(&br, 64);
BB_ReadAlignBytes(&br, alignof(S_Ent));
S_Ent *ents_raw = (S_Ent *)BB_ReadBytesRaw(&br, ents_count * sizeof(S_Ent));
if (ents_raw)
BB_WriteUBits(&bw, ents_count, 64);
for (S_Ent *ent = S_FirstEnt(src_world); ent->valid; ent = S_NextEnt(ent))
{
if (ent_size == sizeof(S_Ent) && ent_align == alignof(S_Ent))
BB_WriteBytes(&bw, StringFromStruct(ent));
}
}
else
{
i64 ent_size = BB_ReadUBits(&br, 32);
i64 ent_align = BB_ReadUBits(&br, 32);
i64 ents_count = BB_ReadUBits(&br, 64);
if (ent_size == sizeof(S_Ent) && ent_align == alignof(S_Ent))
{
for (i64 i = 0; i < ents_count; ++i)
{
result.unpacked.world.ents_count = ents_count;
result.unpacked.world.ents = PushStructsNoZero(arena, S_Ent, ents_count);
CopyStructs(result.unpacked.world.ents, ents_raw, ents_count);
S_Ent *raw_ent = (S_Ent *)BB_ReadBytesRaw(&br, ents_count * sizeof(S_Ent));
if (raw_ent)
{
S_Ent *ent = PushStructNoZero(arena, S_Ent);
{
CopyBytes(ent, raw_ent, ent_size);
ent->valid = 1;
}
DllQueuePush(result.unpacked->first_ent, result.unpacked->last_ent, ent);
result.unpacked->ents_count += 1;
}
else
{
result.ok = 0;
break;
}
}
}
else

View File

@ -14,15 +14,12 @@ Struct(S_TranscodeResult)
{
b32 ok;
S_Tv version;
String packed;
struct
{
S_World world;
u8 *tiles;
} unpacked;
S_World *unpacked;
};
////////////////////////////////////////////////////////////
//~ Transcode
S_TranscodeResult S_TranscodeLevel(Arena *arena, b32 pack, String packed, S_World *world, u8 *tiles);
S_TranscodeResult S_TranscodeWorld(Arena *arena, S_World *world, String packed, b32 pack);

View File

@ -289,7 +289,7 @@ void V_TickForever(WaveLaneCtx *lane)
Arena *perm = PermArena();
G_ArenaHandle gpu_perm = G_PermArena();
const i32 world_size = S_WorldSize;
const i32 world_pitch = S_WorldPitch;
const f32 zoom_rate = 1.50;
const f32 min_zoom = 0.03;
const f32 max_zoom = 15.0;
@ -298,11 +298,17 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Init vis state
V.world_arena = AcquireArena(Gibi(64));
V.world = PushStruct(V.world_arena, S_World);
Arena *sim_debug_arena = AcquireArena(Gibi(64));
u64 sim_debug_draw_descs_count = 0;
S_DebugDrawDesc *sim_debug_draw_descs = 0;
Arena *world_arena = AcquireArena(Gibi(64));
S_World *world = PushStruct(world_arena, S_World);
world->ent_bins_count = Kibi(16);
world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count);
world->tiles = PushStructs(world_arena, u8, S_TilesCount);
Vec2I32 tiles_dims = VEC2I32(S_TilesPitch, S_TilesPitch);
Vec2I32 tiles_dims = VEC2I32(world_size * 2, world_size * 2);
u8 *tiles = PushBytes(perm, tiles_dims.x * tiles_dims.y, alignof(S_TileKind));
G_ResourceHandle gpu_tiles = Zi;
G_Texture2DRef gpu_tiles_ref = Zi;
{
@ -319,7 +325,7 @@ void V_TickForever(WaveLaneCtx *lane)
G_CopyCpuToTexture(
cl,
gpu_tiles, VEC3I32(0, 0, 0),
tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1))
);
}
@ -373,7 +379,6 @@ void V_TickForever(WaveLaneCtx *lane)
while (!shutdown)
{
shutdown = Atomic32Fetch(&V.shutdown);
S_Iter iter = Zi;
//////////////////////////////
//- Begin frame
@ -450,8 +455,7 @@ void V_TickForever(WaveLaneCtx *lane)
//- Transcode swap state
if (swapout)
{
BB_WriteUBits(&bw, V.player_key.v.hi, 64);
BB_WriteUBits(&bw, V.player_key.v.lo, 64);
BB_WriteUBits(&bw, V.player_key.v, 64);
BB_WriteBit(&bw, last_frame->is_editing);
BB_WriteF32(&bw, last_frame->edit_camera_pos.x);
BB_WriteF32(&bw, last_frame->edit_camera_pos.y);
@ -460,8 +464,7 @@ void V_TickForever(WaveLaneCtx *lane)
}
else
{
V.player_key.v.hi = BB_ReadUBits(&br, 64);
V.player_key.v.lo = BB_ReadUBits(&br, 64);
V.player_key.v = BB_ReadUBits(&br, 64);
frame->is_editing = BB_ReadBit(&br);
frame->edit_camera_pos.x = BB_ReadF32(&br);
frame->edit_camera_pos.y = BB_ReadF32(&br);
@ -532,44 +535,95 @@ void V_TickForever(WaveLaneCtx *lane)
UnlockTicketMutex(&S.output_back_tm);
//////////////////////////////
//- Update tiles from sim
//- Apply sim snapshots
// FIXME: Only apply latest snapshot
b32 received_unseen_tick = 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;
if (snapshot->tick > world->tick)
{
S_Snapshot *snapshot = &n->snapshot;
if (snapshot->tick > V.world->tick)
world->tick = snapshot->tick;
world->time_ns = snapshot->time_ns;
for (S_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
{
for (u64 placement_idx = 0; placement_idx < snapshot->tile_placements_count; ++placement_idx)
S_Delta *delta = &dn->delta;
Rng2I32 dirty_tile_rect = S_UpdateWorldFromDelta(world_arena, world, delta);
Vec2I32 dirty_tile_dims = DimsFromRng2I32(dirty_tile_rect);
if (dirty_tile_dims.x != 0 || dirty_tile_dims.y != 0)
{
S_TilePlacement placement = snapshot->tile_placements[placement_idx];
Rng2I32 dirty_rect = S_UpdateTilesInPlaceFromPlacement(tiles, placement);
G_CopyCpuToTexture(
frame->cl,
gpu_tiles, VEC3I32(dirty_rect.p0.x, dirty_rect.p0.y, 0),
tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
RNG3I32(VEC3I32(dirty_rect.p0.x, dirty_rect.p0.y, 0), VEC3I32(dirty_rect.p1.x, dirty_rect.p1.y, 1))
gpu_tiles, VEC3I32(dirty_tile_rect.p0.x, dirty_tile_rect.p0.y, 0),
world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
RNG3I32(VEC3I32(dirty_tile_rect.p0.x, dirty_tile_rect.p0.y, 0), VEC3I32(dirty_tile_rect.p1.x, dirty_tile_rect.p1.y, 1))
);
}
}
received_unseen_tick = 1;
}
}
//////////////////////////////
//- Update world from sim
//- Prune entities
if (sim_output->last_snapshot_node && sim_output->last_snapshot_node->snapshot.tick > V.world->tick)
// TODO
//////////////////////////////
//- Copy sim debug info
if (received_unseen_tick)
{
ResetArena(V.world_arena);
V.world = S_WorldFromSnapshot(V.world_arena, &sim_output->last_snapshot_node->snapshot);
V.lookup = S_LookupFromWorld(V.world_arena, V.world);
ResetArena(sim_debug_arena);
// Copy sim debug info
V.debug_draw_descs_count = sim_output->debug_draw_descs_count;
V.debug_draw_descs = PushStructsNoZero(V.world_arena, S_DebugDrawDesc, V.debug_draw_descs_count);
CopyStructs(V.debug_draw_descs, sim_output->debug_draw_descs, V.debug_draw_descs_count);
sim_debug_draw_descs_count = sim_output->debug_draw_descs_count;
sim_debug_draw_descs = PushStructsNoZero(sim_debug_arena, S_DebugDrawDesc, sim_debug_draw_descs_count);
CopyStructs(sim_debug_draw_descs, sim_output->debug_draw_descs, sim_debug_draw_descs_count);
}
// //////////////////////////////
// //- Update tiles from sim
// {
// for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next)
// {
// S_Snapshot *snapshot = &n->snapshot;
// if (snapshot->tick > world->tick)
// {
// for (u64 placement_idx = 0; placement_idx < snapshot->tile_placements_count; ++placement_idx)
// {
// S_TilePlacement placement = snapshot->tile_placements[placement_idx];
// Rng2I32 dirty_rect = S_UpdateTilesInPlaceFromPlacement(tiles, placement);
// G_CopyCpuToTexture(
// frame->cl,
// gpu_tiles, VEC3I32(dirty_rect.p0.x, dirty_rect.p0.y, 0),
// tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
// RNG3I32(VEC3I32(dirty_rect.p0.x, dirty_rect.p0.y, 0), VEC3I32(dirty_rect.p1.x, dirty_rect.p1.y, 1))
// );
// }
// }
// }
// }
// //////////////////////////////
// //- Update world from sim
// if (sim_output->last_snapshot_node && sim_output->last_snapshot_node->snapshot.tick > world->tick)
// {
// ResetArena(world_arena);
// world = S_WorldFromSnapshot(world_arena, &sim_output->last_snapshot_node->snapshot);
// V.lookup = S_LookupFromWorld(world_arena, world);
// // Copy sim debug info
// sim_debug_draw_descs_count = sim_output->debug_draw_descs_count;
// sim_debug_draw_descs = PushStructsNoZero(world_arena, S_DebugDrawDesc, sim_debug_draw_descs_count);
// CopyStructs(sim_debug_draw_descs, sim_output->debug_draw_descs, sim_debug_draw_descs_count);
// }
//////////////////////////////
//- Process controller events into vis cmds
@ -688,8 +742,8 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 diff = SubVec2(last_target_cursor, target_cursor);
frame->edit_camera_pos = AddVec2(frame->edit_camera_pos, diff);
}
frame->edit_camera_pos.x = ClampF32(frame->edit_camera_pos.x, -world_size / 2, world_size / 2);
frame->edit_camera_pos.y = ClampF32(frame->edit_camera_pos.y, -world_size / 2, world_size / 2);
frame->edit_camera_pos.x = ClampF32(frame->edit_camera_pos.x, -world_pitch / 2, world_pitch / 2);
frame->edit_camera_pos.y = ClampF32(frame->edit_camera_pos.y, -world_pitch / 2, world_pitch / 2);
target_camera_pos = frame->edit_camera_pos;
target_camera_zoom = frame->edit_camera_zoom;
}
@ -698,13 +752,13 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 look_ratio = Zi;
look_ratio.y = 0.25;
look_ratio.x = look_ratio.y / (16.0 / 9.0);
S_Ent *player = S_EntFromKey(&V.lookup, V.player_key);
S_Ent *player = S_EntFromKey(world, V.player_key);
target_camera_pos = MulXformV2(player->xf, player->local_shape.centroid);
target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(player->look, look_ratio));
target_camera_zoom = 1;
}
target_camera_pos.x = ClampF32(target_camera_pos.x, -world_size / 2, world_size / 2);
target_camera_pos.y = ClampF32(target_camera_pos.y, -world_size / 2, world_size / 2);
target_camera_pos.x = ClampF32(target_camera_pos.x, -world_pitch / 2, world_pitch / 2);
target_camera_pos.y = ClampF32(target_camera_pos.y, -world_pitch / 2, world_pitch / 2);
target_camera_zoom = ClampF32(target_camera_zoom, min_zoom, max_zoom);
// Create world <-> ui xforms
@ -816,10 +870,28 @@ void V_TickForever(WaveLaneCtx *lane)
tile_range.p1 = S_TilePosFromWorldPos(last_frame->world_selection.p1);
tile_range.p1 = AddVec2I32(tile_range.p1, VEC2I32(1, 1));
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Tile);
cmd->tile_placement.placement_kind = S_TilePlacementKind_Range;
cmd->tile_placement.tile_kind = last_frame->equipped_tile;
cmd->tile_placement.range = tile_range;
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
cmd->delta.kind = S_DeltaKind_Tile;
cmd->delta.tile_kind = last_frame->equipped_tile;
cmd->delta.tile_range = tile_range;
}
}
//////////////////////////////
//- Query entities
S_Ent *player = S_EntFromKey(world, V.player_key);
S_Ent *hovered_ent = &S_NilEnt;
{
S_Shape cursor_shape = S_ShapeFromDesc(.count = 1, .points = { frame->world_cursor });
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
S_Shape ent_shape = S_MulXformShape(ent->xf, ent->local_shape);
b32 is_hovered = S_CollisionDataFromShapes(ent_shape, cursor_shape).collision_points_count > 0;
if (is_hovered)
{
hovered_ent = ent;
}
}
}
@ -2292,19 +2364,47 @@ void V_TickForever(WaveLaneCtx *lane)
case V_CmdKind_spawn:
{
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Spawn);
S_Ent *ent = &cmd->ent;
*ent = *S_EntFromKey(&V.lookup, V.player_key);
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
cmd->delta.kind = S_DeltaKind_RawEnt;
S_Ent *ent = &cmd->delta.ent;
ent->key = V.player_key;
ent->xf = XformFromPos(frame->world_cursor);
ent->move_speed = 0.05;
ent->local_shape = S_ShapeFromDesc(
.mass = 10,
.count = 1,
.radius = 0.3,
);
ent->xf = XformFromPos(frame->world_cursor);
ent->has_weapon = 1;
} break;
case V_CmdKind_spawn_dummy:
{
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
cmd->delta.kind = S_DeltaKind_RawEnt;
S_Ent *ent = &cmd->delta.ent;
ent->key = S_RandKey();
ent->xf = XformFromPos(frame->world_cursor);
ent->move_speed = 0.05;
ent->local_shape = S_ShapeFromDesc(
.mass = 10,
.count = 1,
.radius = 0.3,
);
ent->has_weapon = 1;
} break;
case V_CmdKind_delete:
{
if (hovered_ent->valid)
{
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta);
cmd->delta.kind = S_DeltaKind_RawEnt;
S_Ent *ent = &cmd->delta.ent;
ent->key = hovered_ent->key;
ent->exists = 0;
}
} break;
}
}
@ -2321,7 +2421,6 @@ void V_TickForever(WaveLaneCtx *lane)
}
Vec2 look = Zi;
{
S_Ent *player = S_EntFromKey(&V.lookup, V.player_key);
Vec2 center = MulXformV2(player->xf, player->local_shape.centroid);
look = SubVec2(frame->world_cursor, center);
}
@ -2372,7 +2471,7 @@ void V_TickForever(WaveLaneCtx *lane)
//- Draw entities
// Build shape buffers
for (S_Ent *ent = S_FirstEnt(&iter, V.world); ent->active; ent = S_NextEnt(&iter))
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{
Xform ent_to_world_xf = ent->xf;
Xform ent_to_draw_xf = MulXform(frame->world_to_draw_xf, ent_to_world_xf);
@ -2424,9 +2523,9 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Draw sim debug shapes
for (u64 desc_idx = 0; desc_idx < V.debug_draw_descs_count; ++desc_idx)
for (u64 desc_idx = 0; desc_idx < sim_debug_draw_descs_count; ++desc_idx)
{
S_DebugDrawDesc *desc = &V.debug_draw_descs[desc_idx];
S_DebugDrawDesc *desc = &sim_debug_draw_descs[desc_idx];
Vec4 color = Vec4FromU32(desc->srgb32);
i32 detail = 24;
f32 radius = 5;
@ -2513,7 +2612,6 @@ void V_TickForever(WaveLaneCtx *lane)
params.camera_pos = frame->camera_pos;
params.camera_zoom = frame->camera_zoom;
params.world_size = world_size;
params.tiles = gpu_tiles_ref;
params.shape_verts = dverts_ro;
}

View File

@ -12,8 +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 to Cursor, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \
// --------------------------------------------------------------------------------------------------------------------
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(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \
/* --------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
////////////////////////////////////////////////////////////
//~ Theme types
@ -279,12 +281,7 @@ Struct(V_Frame)
Struct(V_Ctx)
{
Arena *world_arena;
S_World *world;
S_Lookup lookup;
S_Key player_key;
u64 debug_draw_descs_count;
S_DebugDrawDesc *debug_draw_descs;
i64 panels_count;
i64 windows_count;

View File

@ -33,7 +33,6 @@ Struct(V_DParams)
Vec2 camera_pos;
f32 camera_zoom;
f32 world_size;
G_Texture2DRef tiles;
G_StructuredBufferRef quads;
G_StructuredBufferRef shape_verts;

View File

@ -22,7 +22,7 @@ ComputeShader2D(V_BackdropCS, 8, 8)
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos);
f32 half_thickness = 1;
f32 half_bounds_size = params.world_size * 0.5;
f32 half_bounds_size = S_WorldPitch * 0.5;
Vec2 bounds_screen_p0 = mul(params.world_to_draw_xf, Vec3(-half_bounds_size, -half_bounds_size, 1));
Vec2 bounds_screen_p1 = mul(params.world_to_draw_xf, Vec3(half_bounds_size, half_bounds_size, 1));
bool is_in_bounds = screen_pos.x > (bounds_screen_p0.x - half_thickness) &&
@ -253,14 +253,16 @@ PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input)
// }
// else
{
if (world_pos.x > -(S_WorldSize / 2) &&
world_pos.y > -(S_WorldSize / 2) &&
world_pos.x < (S_WorldSize / 2) &&
world_pos.y < (S_WorldSize / 2) &&
tile_pos.x >= tile_selection.p0.x &&
tile_pos.x <= tile_selection.p1.x &&
tile_pos.y >= tile_selection.p0.y &&
tile_pos.y <= tile_selection.p1.y)
if (
world_pos.x > -(S_WorldPitch / 2) &&
world_pos.y > -(S_WorldPitch / 2) &&
world_pos.x < (S_WorldPitch / 2) &&
world_pos.y < (S_WorldPitch / 2) &&
tile_pos.x >= tile_selection.p0.x &&
tile_pos.x <= tile_selection.p1.x &&
tile_pos.y >= tile_selection.p0.y &&
tile_pos.y <= tile_selection.p1.y
)
{
result = inner_color;
}