cross-chunk tile wall merging

This commit is contained in:
jacob 2025-05-21 21:17:04 -05:00
parent 4bc6ca6679
commit a47009f16b
6 changed files with 239 additions and 169 deletions

View File

@ -39,7 +39,7 @@
#define SPACE_CELL_BINS_SQRT (64) #define SPACE_CELL_BINS_SQRT (64)
#define SPACE_CELL_SIZE (1) #define SPACE_CELL_SIZE (1)
#define SIM_TILES_PER_UNIT_SQRT (2) #define SIM_TILES_PER_UNIT_SQRT (4)
#define SIM_TILES_PER_CHUNK_SQRT (16) #define SIM_TILES_PER_CHUNK_SQRT (16)
#define SIM_TICKS_PER_SECOND 50 #define SIM_TICKS_PER_SECOND 50

View File

@ -13,6 +13,15 @@
* Contact * Contact
* ========================== */ * ========================== */
INTERNAL b32 can_contact(struct sim_ent *e0, struct sim_ent *e1)
{
b32 res = false;
res = e0 != e1 &&
!sim_ent_id_eq(e0->top, e1->top) &&
!(sim_ent_has_prop(e0, SEPROP_WALL) && sim_ent_has_prop(e1, SEPROP_WALL));
return res;
}
void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration) void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration)
{ {
__prof; __prof;
@ -39,10 +48,10 @@ void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt,
struct space_entry *space_entry; struct space_entry *space_entry;
while ((space_entry = space_iter_next(&iter))) { while ((space_entry = space_iter_next(&iter))) {
struct sim_ent *check1 = sim_ent_from_id(ss, space_entry->ent); struct sim_ent *check1 = sim_ent_from_id(ss, space_entry->ent);
if (check1 == check0) continue;
if (!sim_ent_is_valid_and_active(check1)) continue; if (!sim_ent_is_valid_and_active(check1)) continue;
if (!(sim_ent_has_prop(check1, SEPROP_SOLID) || sim_ent_has_prop(check1, SEPROP_SENSOR))) continue; if (!(sim_ent_has_prop(check1, SEPROP_SOLID) || sim_ent_has_prop(check1, SEPROP_SENSOR))) continue;
if (check1->local_collider.count <= 0) continue; if (check1->local_collider.count <= 0) continue;
if (!can_contact(check0, check1)) continue;
/* Deterministic order based on entity id */ /* Deterministic order based on entity id */
struct sim_ent *e0; struct sim_ent *e0;
@ -1171,10 +1180,10 @@ f32 phys_determine_earliest_toi(struct phys_step_ctx *ctx, f32 step_dt, f32 tole
struct space_entry *entry; struct space_entry *entry;
while ((entry = space_iter_next(&iter))) { while ((entry = space_iter_next(&iter))) {
struct sim_ent *e1 = sim_ent_from_id(ss, entry->ent); struct sim_ent *e1 = sim_ent_from_id(ss, entry->ent);
if (e1 == e0) continue;
if (!sim_ent_should_simulate(e1)) continue; if (!sim_ent_should_simulate(e1)) continue;
if (!(sim_ent_has_prop(e1, SEPROP_SOLID) || sim_ent_has_prop(e1, SEPROP_SENSOR))) continue; if (!(sim_ent_has_prop(e1, SEPROP_SOLID) || sim_ent_has_prop(e1, SEPROP_SENSOR))) continue;
if (e1->local_collider.count <= 0) continue; if (e1->local_collider.count <= 0) continue;
if (!can_contact(e0, e1)) continue;
struct collider_shape e1_collider = e1->local_collider; struct collider_shape e1_collider = e1->local_collider;
struct xform e1_xf_t0 = sim_ent_get_xform(e1); struct xform e1_xf_t0 = sim_ent_get_xform(e1);

View File

@ -377,21 +377,21 @@ INTERNAL void test_spawn_tile(struct sim_snapshot *world, struct v2 world_pos)
INTERNAL SORT_COMPARE_FUNC_DEF(tile_chunk_sort, arg_a, arg_b, udata) INTERNAL SORT_COMPARE_FUNC_DEF(tile_chunk_sort, arg_a, arg_b, udata)
{ {
(UNUSED)udata; (UNUSED)udata;
struct sim_ent *a = (struct sim_ent *)arg_a; struct sim_ent *a = *(struct sim_ent **)arg_a;
struct sim_ent *b = (struct sim_ent *)arg_b; struct sim_ent *b = *(struct sim_ent **)arg_b;
struct v2i32 index_a = a->tile_chunk_index; struct v2i32 index_a = a->tile_chunk_index;
struct v2i32 index_b = b->tile_chunk_index; struct v2i32 index_b = b->tile_chunk_index;
i32 res = 0; i32 res = 0;
res = 2 * ((index_a.y > index_b.y) - (index_a.y < index_b.y)) + ((index_a.x > index_b.x) - (index_a.x < index_b.x)); res = 2 * ((index_a.y < index_b.y) - (index_a.y > index_b.y)) + ((index_a.x < index_b.x) - (index_a.x > index_b.x));
return res; return res;
} }
INTERNAL void test_generate_walls(struct sim_ent *parent) INTERNAL void test_generate_walls(struct sim_snapshot *world)
{ {
#if 1 __prof;
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
struct sim_snapshot *world = parent->ss; struct sim_ent *root = sim_ent_from_id(world, SIM_ENT_ROOT_ID);
/* Release existing walls */ /* Release existing walls */
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
@ -403,17 +403,17 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
} }
/* Sort tile chunks */ /* Sort tile chunks */
struct sim_ent **tile_chunks = arena_dry_push(scratch.arena, struct sim_ent *); struct sim_ent **sorted_tile_chunks = arena_dry_push(scratch.arena, struct sim_ent *);
u64 tile_chunks_count = 0; u64 sorted_tile_chunks_count = 0;
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
struct sim_ent *ent = &world->ents[ent_index]; struct sim_ent *ent = &world->ents[ent_index];
if (!ent->valid) continue; if (!ent->valid) continue;
if (sim_ent_has_prop(ent, SEPROP_TILE_CHUNK)) { if (sim_ent_has_prop(ent, SEPROP_TILE_CHUNK)) {
*arena_push_no_zero(scratch.arena, struct sim_ent *) = ent; *arena_push_no_zero(scratch.arena, struct sim_ent *) = ent;
++tile_chunks_count; ++sorted_tile_chunks_count;
} }
} }
merge_sort(tile_chunks, tile_chunks_count, sizeof(*tile_chunks), tile_chunk_sort, NULL); merge_sort(sorted_tile_chunks, sorted_tile_chunks_count, sizeof(*sorted_tile_chunks), tile_chunk_sort, NULL);
struct wall_node { struct wall_node {
struct v2i32 start; struct v2i32 start;
@ -421,124 +421,156 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
struct wall_node *next; struct wall_node *next;
}; };
/* Dicts containing walls that end on edge of tile chunk, keyed by tile end index.
* Used to expand walls accross tile chunks. */
struct dict horizontal_ends_dict = dict_init(scratch.arena, 1024);
struct dict vertical_ends_dict = dict_init(scratch.arena, 1024);
struct wall_node *first_wall = NULL; struct wall_node *first_wall = NULL;
/* Generate horizontal wall nodes */ /* Generate horizontal wall nodes */
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { for (u64 sorted_index = 0; sorted_index < sorted_tile_chunks_count; ++sorted_index) {
struct sim_ent *chunk = &world->ents[ent_index]; struct sim_ent *chunk = sorted_tile_chunks[sorted_index];
if (!chunk->valid) continue; struct v2i32 chunk_index = chunk->tile_chunk_index;
if (sim_ent_has_prop(chunk, SEPROP_TILE_CHUNK)) { /* Generate horizontal wall nodes */
struct v2i32 chunk_index = chunk->tile_chunk_index; {
/* Generate horizontal wall nodes */ struct sim_ent *top_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y - 1));
{ struct sim_ent *bottom_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y + 1));
struct sim_ent *top_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y - 1)); /* If there's no chunk below this one, then do an extra iteration (since walls are created at the top of each tile) */
struct sim_ent *bottom_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x, chunk_index.y + 1)); i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + !bottom_chunk->valid;
/* If there's no chunk below this one, then do an extra iteration (since walls are created at the top of each tile) */ i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + 1;
i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + !bottom_chunk->valid; for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) {
i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + 1; i32 wall_start = -1;
for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) { i32 wall_end = -1;
i32 wall_start = -1; for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) {
i32 wall_end = -1; enum sim_tile_kind tile = SIM_TILE_KIND_NONE;
for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) { if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) {
enum sim_tile_kind tile = SIM_TILE_KIND_NONE; tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y));
if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) { }
tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y)); b32 should_have_top_wall = false;
} if (tile_x < SIM_TILES_PER_CHUNK_SQRT) {
b32 should_have_top_wall = false; enum sim_tile_kind top_tile = SIM_TILE_KIND_NONE;
if (tile_x < SIM_TILES_PER_CHUNK_SQRT) { if (tile_y == 0) {
enum sim_tile_kind top_tile = SIM_TILE_KIND_NONE; if (top_chunk->valid) {
if (tile_y == 0) { struct v2i32 top_tile_local_index = V2I32(tile_x, SIM_TILES_PER_CHUNK_SQRT - 1);
if (top_chunk->valid) { top_tile = sim_get_chunk_tile(top_chunk, top_tile_local_index);
struct v2i32 top_tile_local_index = V2I32(tile_x, SIM_TILES_PER_CHUNK_SQRT - 1);
top_tile = sim_get_chunk_tile(top_chunk, top_tile_local_index);
}
} else {
top_tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y - 1));
} }
if (tile == SIM_TILE_KIND_WALL) { } else {
/* Process wall tile */ top_tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y - 1));
should_have_top_wall = top_tile != SIM_TILE_KIND_WALL; }
} else { if (tile == SIM_TILE_KIND_WALL) {
/* Process non-wall tile */ /* Process wall tile */
should_have_top_wall = top_tile == SIM_TILE_KIND_WALL; should_have_top_wall = top_tile != SIM_TILE_KIND_WALL;
} else {
/* Process non-wall tile */
should_have_top_wall = top_tile == SIM_TILE_KIND_WALL;
}
}
if (should_have_top_wall) {
if (wall_start < 0) {
/* Start wall */
wall_start = tile_x;
}
/* Extend wall */
wall_end = tile_x + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_start, tile_y));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_end, tile_y));
struct wall_node *node = NULL;
if (wall_start == 0) {
u64 start_hash = rand_u64_from_seed(*(u64 *)&start);
struct dict_entry *entry = dict_get_entry(&horizontal_ends_dict, start_hash);
if (entry) {
node = (struct wall_node *)entry->value;
dict_remove_entry(&horizontal_ends_dict, entry);
} }
} }
if (should_have_top_wall) { if (!node) {
if (wall_start < 0) { node = arena_push(scratch.arena, struct wall_node);
/* Start wall */
wall_start = tile_x;
}
/* Extend wall */
wall_end = tile_x + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_start, tile_y));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(wall_end, tile_y));
struct wall_node *node = arena_push(scratch.arena, struct wall_node);
node->start = start; node->start = start;
node->end = end;
node->next = first_wall; node->next = first_wall;
first_wall = node; first_wall = node;
wall_start = -1;
wall_end = -1;
} }
node->end = end;
if (wall_end == SIM_TILES_PER_CHUNK_SQRT) {
u64 end_hash = rand_u64_from_seed(*(u64 *)&end);
dict_set(scratch.arena, &horizontal_ends_dict, end_hash, node);
}
wall_start = -1;
wall_end = -1;
} }
} }
} }
/* Generate vertical wall nodes */ }
{ /* Generate vertical wall nodes */
struct sim_ent *left_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x - 1, chunk_index.y)); {
struct sim_ent *right_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x + 1, chunk_index.y)); struct sim_ent *left_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x - 1, chunk_index.y));
/* If there's no chunk to the right of this one, then do an extra iteration (since walls are created on the left of each tile) */ struct sim_ent *right_chunk = sim_tile_chunk_from_chunk_index(world, V2I32(chunk_index.x + 1, chunk_index.y));
i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + 1; /* If there's no chunk to the right of this one, then do an extra iteration (since walls are created on the left of each tile) */
i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + !right_chunk->valid; i32 y_iterations = SIM_TILES_PER_CHUNK_SQRT + 1;
for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) { i32 x_iterations = SIM_TILES_PER_CHUNK_SQRT + !right_chunk->valid;
i32 wall_start = -1; for (i32 tile_x = 0; tile_x < x_iterations; ++tile_x) {
i32 wall_end = -1; i32 wall_start = -1;
for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) { i32 wall_end = -1;
enum sim_tile_kind tile = SIM_TILE_KIND_NONE; for (i32 tile_y = 0; tile_y < y_iterations; ++tile_y) {
if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) { enum sim_tile_kind tile = SIM_TILE_KIND_NONE;
tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y)); if (tile_x < SIM_TILES_PER_CHUNK_SQRT && tile_y < SIM_TILES_PER_CHUNK_SQRT) {
} tile = sim_get_chunk_tile(chunk, V2I32(tile_x, tile_y));
}
b32 should_have_left_wall = false; b32 should_have_left_wall = false;
if (tile_y < SIM_TILES_PER_CHUNK_SQRT) { if (tile_y < SIM_TILES_PER_CHUNK_SQRT) {
enum sim_tile_kind left_tile = SIM_TILE_KIND_NONE; enum sim_tile_kind left_tile = SIM_TILE_KIND_NONE;
if (tile_x == 0) { if (tile_x == 0) {
if (left_chunk->valid) { if (left_chunk->valid) {
struct v2i32 left_tile_local_index = V2I32(SIM_TILES_PER_CHUNK_SQRT - 1, tile_y); struct v2i32 left_tile_local_index = V2I32(SIM_TILES_PER_CHUNK_SQRT - 1, tile_y);
left_tile = sim_get_chunk_tile(left_chunk, left_tile_local_index); left_tile = sim_get_chunk_tile(left_chunk, left_tile_local_index);
}
} else {
left_tile = sim_get_chunk_tile(chunk, V2I32(tile_x - 1, tile_y));
} }
if (tile == SIM_TILE_KIND_WALL) { } else {
/* Process wall tile */ left_tile = sim_get_chunk_tile(chunk, V2I32(tile_x - 1, tile_y));
should_have_left_wall = left_tile != SIM_TILE_KIND_WALL; }
} else { if (tile == SIM_TILE_KIND_WALL) {
/* Process non-wall tile */ /* Process wall tile */
should_have_left_wall = left_tile == SIM_TILE_KIND_WALL; should_have_left_wall = left_tile != SIM_TILE_KIND_WALL;
} else {
/* Process non-wall tile */
should_have_left_wall = left_tile == SIM_TILE_KIND_WALL;
}
}
if (should_have_left_wall) {
if (wall_start < 0) {
/* Start wall */
wall_start = tile_y;
}
/* Extend wall */
wall_end = tile_y + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_start));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_end));
struct wall_node *node = NULL;
if (wall_start == 0) {
u64 start_hash = rand_u64_from_seed(*(u64 *)&start);
struct dict_entry *entry = dict_get_entry(&vertical_ends_dict, start_hash);
if (entry) {
node = (struct wall_node *)entry->value;
dict_remove_entry(&vertical_ends_dict, entry);
} }
} }
if (should_have_left_wall) { if (!node) {
if (wall_start < 0) { node = arena_push(scratch.arena, struct wall_node);
/* Start wall */
wall_start = tile_y;
}
/* Extend wall */
wall_end = tile_y + 1;
} else if (wall_end >= 0) {
/* Stop wall */
struct v2i32 start = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_start));
struct v2i32 end = sim_world_tile_index_from_local_tile_index(chunk_index, V2I32(tile_x, wall_end));
struct wall_node *node = arena_push(scratch.arena, struct wall_node);
node->start = start; node->start = start;
node->end = end;
node->next = first_wall; node->next = first_wall;
first_wall = node; first_wall = node;
wall_start = -1;
wall_end = -1;
} }
node->end = end;
if (wall_end == SIM_TILES_PER_CHUNK_SQRT) {
u64 end_hash = rand_u64_from_seed(*(u64 *)&end);
dict_set(scratch.arena, &vertical_ends_dict, end_hash, node);
}
wall_start = -1;
wall_end = -1;
} }
} }
} }
@ -547,17 +579,9 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
/* Create wall entities */ /* Create wall entities */
for (struct wall_node *node = first_wall; node; node = node->next) { for (struct wall_node *node = first_wall; node; node = node->next) {
struct sim_ent *wall_ent = sim_ent_alloc_sync_src(parent); struct sim_ent *wall_ent = sim_ent_alloc_sync_src(root);
sim_ent_enable_prop(wall_ent, SEPROP_WALL); sim_ent_enable_prop(wall_ent, SEPROP_WALL);
#if 0
struct v2i32 node_start = node->start;
struct v2i32 node_end = node->end;
struct v2 start = V2(node_start.x / SIM_TILES_PER_UNIT_SQRT, node_start.y / SIM_TILES_PER_UNIT_SQRT);
struct v2 end = V2(node_end.x / SIM_TILES_PER_UNIT_SQRT, node_end.y / SIM_TILES_PER_UNIT_SQRT);
#endif
struct v2 start = sim_pos_from_world_tile_index(node->start); struct v2 start = sim_pos_from_world_tile_index(node->start);
struct v2 end = sim_pos_from_world_tile_index(node->end); struct v2 end = sim_pos_from_world_tile_index(node->end);
@ -567,12 +591,11 @@ INTERNAL void test_generate_walls(struct sim_ent *parent)
sim_ent_enable_prop(wall_ent, SEPROP_SOLID); sim_ent_enable_prop(wall_ent, SEPROP_SOLID);
wall_ent->local_collider.count = 2; wall_ent->local_collider.count = 2;
wall_ent->local_collider.points[1] = v2_sub(end, start); wall_ent->local_collider.points[1] = v2_sub(end, start);
sim_ent_activate(wall_ent, world->tick);
} }
scratch_end(scratch); scratch_end(scratch);
#else
(UNUSED)parent;
#endif
} }
@ -774,7 +797,7 @@ void sim_step(struct sim_step_ctx *ctx)
player->player_client_handle = client->handle; player->player_client_handle = client->handle;
sim_ent_enable_prop(player, SEPROP_PLAYER); sim_ent_enable_prop(player, SEPROP_PLAYER);
player->predictor = player->id; player->predictor = player->id;
sim_ent_enable_prop(player, SEPROP_ACTIVE); sim_ent_activate(player, world->tick);
client->player_id = player->id; client->player_id = player->id;
if (client == user_input_client) { if (client == user_input_client) {
user_input_client->player_id = player->id; user_input_client->player_id = player->id;
@ -949,7 +972,7 @@ void sim_step(struct sim_step_ctx *ctx)
} }
} }
if (flags & SIM_CONTROL_FLAG_WALLS_TEST) { if (flags & SIM_CONTROL_FLAG_WALLS_TEST) {
test_generate_walls(root); test_generate_walls(world);
} }
if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) { if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) {
logf_info("Explosion test"); logf_info("Explosion test");
@ -958,37 +981,9 @@ void sim_step(struct sim_step_ctx *ctx)
} }
if (flags & SIM_CONTROL_FLAG_TILE_TEST) { if (flags & SIM_CONTROL_FLAG_TILE_TEST) {
#if 0
if (is_master) {
struct v2 cursor_pos = player->player_cursor_pos;
/* FIXME: Negative indices */
struct v2i32 tile_pos = V2I32(cursor_pos.x * SIM_TILES_PER_UNIT_SQRT, cursor_pos.y * SIM_TILES_PER_UNIT_SQRT);
struct v2i32 chunk_pos = V2I32(tile_pos.x * SIM_TILES_PER_CHUNK_SQRT, tile_pos.y * SIM_TILES_PER_CHUNK_SQRT);
struct v2i32 tile_pos_in_chunk = V2I32(tile_pos.x % SIM_TILES_PER_CHUNK_SQRT, tile_pos.y % SIM_TILES_PER_CHUNK_SQRT);
struct sim_ent_id chunk_id = sim_ent_chunk_id_from_chunk_pos(player->id, chunk_pos);
struct sim_ent *chunk_ent = sim_ent_from_id(world, chunk_id);
if (!chunk_ent->valid) {
chunk_ent = sim_ent_alloc_sync_src_with_id(root, chunk_id);
sim_ent_enable_prop(chunk_ent, SEPROP_TILE_CHUNK);
}
struct string old_data = sim_ent_get_tile_chunk_data(chunk_ent);
struct string new_data = ZI;
new_data.len = SIM_TILES_PER_CHUNK_SQRT * SIM_TILES_PER_CHUNK_SQRT;
new_data.text = arena_push_array_no_zero(scratch.arena, u8, new_data.len);
if (old_data.len == new_data.len) {
MEMCPY(new_data.text, old_data.text, new_data.len);
} else {
MEMZERO(new_data.text, new_data.len);
}
u64 tile_index = tile_pos_in_chunk.x + (tile_pos_in_chunk.y * SIM_TILES_PER_CHUNK_SQRT);
new_data.text[tile_index] = SIM_TILE_KIND_TEST;
sim_ent_set_tile_chunk_data(chunk_ent, new_data);
}
#else
test_spawn_tile(world, player->player_cursor_pos); test_spawn_tile(world, player->player_cursor_pos);
#endif } else if (old_control.flags & SIM_CONTROL_FLAG_TILE_TEST) {
test_generate_walls(world);
} }
} }
} break; } break;

View File

@ -162,7 +162,7 @@ struct tar_archive tar_parse(struct arena *arena, struct string data, struct str
return archive; return archive;
} }
struct tar_entry *tar_get(const struct tar_archive *archive, struct string name) struct tar_entry *tar_get(struct tar_archive *archive, struct string name)
{ {
u64 hash = hash_fnv64(HASH_FNV64_BASIS, name); u64 hash = hash_fnv64(HASH_FNV64_BASIS, name);
return dict_get(&archive->lookup, hash); return dict_get(&archive->lookup, hash);

View File

@ -18,6 +18,6 @@ struct tar_archive {
}; };
struct tar_archive tar_parse(struct arena *arena, struct string data, struct string prefix); struct tar_archive tar_parse(struct arena *arena, struct string data, struct string prefix);
struct tar_entry *tar_get(const struct tar_archive *archive, struct string name); struct tar_entry *tar_get(struct tar_archive *archive, struct string name);
#endif #endif

View File

@ -35,9 +35,9 @@ INLINE u64 hash_fnv64(u64 seed, struct string s)
* ========================== */ * ========================== */
/* Compare functions should /* Compare functions should
* return an int < 0 if a < b * return a positive value if a should go before b
* return an int = 0 if a == b * return a negative value if a should go after b
* return an int > 0 if a > b * return 0 otherwise
*/ */
#define SORT_COMPARE_FUNC_DEF(name, arg_a, arg_b, arg_udata) i32 name(void *arg_a, void *arg_b, void *arg_udata) #define SORT_COMPARE_FUNC_DEF(name, arg_a, arg_b, arg_udata) i32 name(void *arg_a, void *arg_b, void *arg_udata)
typedef SORT_COMPARE_FUNC_DEF(sort_compare_func, a, b, udata); typedef SORT_COMPARE_FUNC_DEF(sort_compare_func, a, b, udata);
@ -108,18 +108,23 @@ INLINE void merge_sort(void *items, u64 item_count, u64 item_size, sort_compare_
struct dict_entry { struct dict_entry {
u64 hash; u64 hash;
u64 value; u64 value;
struct dict_entry *prev_in_bin;
struct dict_entry *next_in_bin; struct dict_entry *next_in_bin;
struct dict_entry *prev;
struct dict_entry *next; struct dict_entry *next;
}; };
struct dict_bin { struct dict_bin {
struct dict_entry *first; struct dict_entry *first;
struct dict_entry *last;
}; };
struct dict { struct dict {
u64 bins_count; u64 bins_count;
struct dict_bin *bins; struct dict_bin *bins;
struct dict_entry *first_free;
struct dict_entry *first; struct dict_entry *first;
struct dict_entry *last;
}; };
INLINE struct dict dict_init(struct arena *arena, u64 bins_count) INLINE struct dict dict_init(struct arena *arena, u64 bins_count)
@ -131,10 +136,9 @@ INLINE struct dict dict_init(struct arena *arena, u64 bins_count)
return dict; return dict;
} }
INLINE void dict_set(struct arena *arena, struct dict *dict, u64 hash, u64 value) INLINE struct dict_entry *dict_ensure_entry(struct arena *arena, struct dict *dict, u64 hash)
{ {
__prof; __prof;
struct dict_bin *bin = &dict->bins[hash % dict->bins_count]; struct dict_bin *bin = &dict->bins[hash % dict->bins_count];
struct dict_entry *entry = bin->first; struct dict_entry *entry = bin->first;
@ -148,19 +152,41 @@ INLINE void dict_set(struct arena *arena, struct dict *dict, u64 hash, u64 value
/* No match found, create new entry */ /* No match found, create new entry */
if (!entry) { if (!entry) {
entry = arena_push(arena, struct dict_entry); if (dict->first_free) {
entry->value = value; entry = dict->first_free;
dict->first_free = entry->next;
} else {
entry = arena_push_no_zero(arena, struct dict_entry);
}
MEMZERO_STRUCT(entry);
entry->hash = hash; entry->hash = hash;
entry->next_in_bin = bin->first; if (bin->last) {
entry->next = dict->first; bin->last->next_in_bin = entry;
dict->first = entry; entry->prev_in_bin = bin->last;
bin->first = entry; } else {
bin->first = entry;
}
bin->last = entry;
if (dict->last) {
dict->last->next = entry;
entry->prev = dict->last;
} else {
dict->first = entry;
}
dict->last = entry;
} }
return entry;
}
INLINE void dict_set(struct arena *arena, struct dict *dict, u64 hash, u64 value)
{
__prof;
struct dict_entry *entry = dict_ensure_entry(arena, dict, hash);
entry->value = value; entry->value = value;
} }
INLINE struct dict_entry *dict_get_entry(const struct dict *dict, u64 hash) INLINE struct dict_entry *dict_get_entry(struct dict *dict, u64 hash)
{ {
__prof; __prof;
struct dict_entry *result = NULL; struct dict_entry *result = NULL;
@ -175,13 +201,53 @@ INLINE struct dict_entry *dict_get_entry(const struct dict *dict, u64 hash)
return result; return result;
} }
INLINE u64 dict_get(const struct dict *dict, u64 hash) INLINE u64 dict_get(struct dict *dict, u64 hash)
{ {
__prof; __prof;
struct dict_entry *entry = dict_get_entry(dict, hash); struct dict_entry *entry = dict_get_entry(dict, hash);
return entry ? entry->value : 0; return entry ? entry->value : 0;
} }
INLINE void dict_remove_entry(struct dict *dict, struct dict_entry *entry)
{
/* Remove from bin */
{
struct dict_bin *bin = &dict->bins[entry->hash % dict->bins_count];
struct dict_entry *prev_in_bin = entry->prev_in_bin;
struct dict_entry *next_in_bin = entry->next_in_bin;
if (prev_in_bin) {
prev_in_bin->next_in_bin = next_in_bin;
} else {
bin->first = next_in_bin;
}
if (next_in_bin) {
next_in_bin->prev_in_bin = prev_in_bin;
} else {
bin->last = prev_in_bin;
}
}
/* Remove from list */
{
struct dict_entry *prev = entry->prev;
struct dict_entry *next = entry->next;
if (prev) {
prev->next = next;
} else {
dict->first = next;
}
if (next) {
next->prev = prev;
} else {
dict->last = prev;
}
}
/* Insert into free list */
{
entry->next = dict->first_free;
dict->first_free = entry;
}
}
/* ========================== * /* ========================== *
* Sync flag * Sync flag
* ========================== */ * ========================== */