diff options
| author | Freya Murphy <freya@freyacat.org> | 2025-12-11 10:49:50 -0500 |
|---|---|---|
| committer | Freya Murphy <freya@freyacat.org> | 2025-12-11 10:51:40 -0500 |
| commit | fa8fa6784559ed0fc8d780e36880273f77e272c4 (patch) | |
| tree | 7456a4e9148d47e409ba837bafdc6238b6c757db /src/world.c | |
| parent | add ubos (diff) | |
| download | voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.tar.gz voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.tar.bz2 voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.zip | |
i did a lot
Diffstat (limited to '')
| -rw-r--r-- | src/world.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/world.c b/src/world.c new file mode 100644 index 0000000..a53055d --- /dev/null +++ b/src/world.c @@ -0,0 +1,234 @@ +#include <cglm/cglm.h> +#include <threads.h> + +#include "utils.h" +#include "voxel.h" + +static RcChunk **world_get_chunk_entry(World *world, int cx, int cy, int cz) +{ + int side = RENDER_DISTANCE * 2 + 1; + + ivec3 min, max; + glm_ivec3_subs(world->center, RENDER_DISTANCE, min); + glm_ivec3_adds(world->center, RENDER_DISTANCE, max); + + if (cx < min[0] || cy < min[1] || cz < min[2]) + return NULL; + + if (cx > max[0] || cy > max[1] || cz > max[2]) + return NULL; + + int index = 0; + index += ((cx % side + side) % side); + index += ((cy % side + side) % side) * side; + index += ((cz % side + side) % side) * side * side; + + return &world->chunks[index]; +} + +static void world_mark_chunk_for_update(World *world, int cx, int cy, int cz) +{ + RcChunk **entry, *inner; + + entry = world_get_chunk_entry(world, cx, cy, cz); + if (entry == NULL) + return; + + inner = *entry; + if (inner == NULL) + return; + + if (inner->chunk.state >= CHUNK_MESHED) + inner->chunk.state = CHUNK_GENERATED; +} + +static void world_reload_chunk(World *world, int cx, int cy, int cz) +{ + ivec3 pos = { cx, cy, cz }; + RcChunk **entry, *inner; + + entry = world_get_chunk_entry(world, cx, cy, cz); + if (entry == NULL) + return; + + inner = *entry; + if (inner != NULL) { + if (glm_ivec3_eqv(inner->chunk.pos, pos)) { + return; + } else { + *entry = NULL; + world_lock(world); + rcchunk_drop(inner); + world_unlock(world); + } + } + + inner = xalloc(sizeof(RcChunk)); + inner->rc = 1; + chunk_init(&inner->chunk, cx, cy, cz); + chunk_generate(&inner->chunk, world->seed); + *entry = inner; + + // mark neighboring chunks as mesh update needed + world_mark_chunk_for_update(world, cx - 1, cy, cz); + world_mark_chunk_for_update(world, cx + 1, cy, cz); + world_mark_chunk_for_update(world, cx, cy - 1, cz); + world_mark_chunk_for_update(world, cx, cy + 1, cz); + world_mark_chunk_for_update(world, cx, cy, cz - 1); + world_mark_chunk_for_update(world, cx, cy, cz + 1); +} + +static void world_reload_chunks(World *world) +{ + ivec3 min, max; + glm_ivec3_subs(world->center, RENDER_DISTANCE, min); + glm_ivec3_adds(world->center, RENDER_DISTANCE, max); + + // clang-format off + for (int cx = min[0]; cx <= max[0]; cx++) + for (int cy = min[1]; cy <= max[1]; cy++) + for (int cz = min[2]; cz <= max[2]; cz++) { + world_reload_chunk(world, cx, cy, cz); + } + // clang-format on +} + +void world_init(World *world, int seed) +{ + int side = RENDER_DISTANCE * 2 + 1; + int size = side * side * side; + world->seed = seed; + world->loaded = false; + world->chunks = xzalloc(size * sizeof(RcChunk *)); + glm_ivec3_zero(world->center); + mtx_init(&world->lock, mtx_plain); +} + +void world_free(World *world) +{ + int side = RENDER_DISTANCE * 2 + 1; + int size = side * side * side; + world_lock(world); + for (int i = 0; i < size; i++) { + RcChunk **entry = &world->chunks[i]; + if (entry == NULL) + continue; + rcchunk_drop(*entry); + } + free(world->chunks); + mtx_destroy(&world->lock); +} + +void world_lock(World *world) +{ + mtx_lock(&world->lock); +} + +void world_unlock(World *world) +{ + mtx_unlock(&world->lock); +} + +void world_update(World *world, Camera *camera) +{ + ivec3 cp = CHUNK_VEC_V(camera->position); + if (!world->loaded || !glm_ivec3_eqv(world->center, cp)) { + // camera has moved, load new chunks + glm_ivec3_copy(cp, world->center); + world_reload_chunks(world); + } +} + +void world_render(World *world, Renderer *renderer) +{ + RcChunk *rc_chunk; + ivec3 min, max; + int world_size, mesh_quota; + + glm_ivec3_subs(world->center, RENDER_DISTANCE, min); + glm_ivec3_adds(world->center, RENDER_DISTANCE, max); + + // how many chunk we want to mesh at most + world_size = RENDER_DISTANCE * 2 + 1; + mesh_quota = world_size * world_size / 10; + + // clang-format off + for (int cx = min[0]; cx <= max[0]; cx++) + for (int cy = min[1]; cy <= max[1]; cy++) + for (int cz = min[2]; cz <= max[2]; cz++) { + rc_chunk = world_get_chunk(world, cx, cy, cz); + if (rc_chunk == NULL) + continue; + + if (rc_chunk->chunk.state < CHUNK_MESHING && mesh_quota > 0) { + chunk_mesh(&rc_chunk->chunk, world); + mesh_quota--; + } + + if (rc_chunk->chunk.has_mesh) + renderer_draw(renderer, &rc_chunk->chunk); + + rcchunk_drop(rc_chunk); + } + // clang-format on +} + +RcChunk *world_get_chunk(World *world, int cx, int cy, int cz) +{ + RcChunk **entry, *inner; + world_lock(world); + entry = world_get_chunk_entry(world, cx, cy, cz); + if (entry == NULL) + goto not_found; + inner = *entry; + if (inner == NULL) + goto not_found; + inner->rc += 1; + world_unlock(world); + return inner; + +not_found: + world_unlock(world); + return NULL; +} + +bool world_is_generated(World *world, int cx, int cy, int cz) +{ + RcChunk *rc_chunk; + bool generated; + + rc_chunk = world_get_chunk(world, cx, cy, cz); + if (rc_chunk == NULL) + return false; + + generated = rc_chunk->chunk.state >= CHUNK_GENERATED; + rcchunk_drop(rc_chunk); + + return generated; +} + +Block world_get_block(World *world, int gx, int gy, int gz) +{ + RcChunk *rc_chunk; + Block block; + ivec3 lp = LOCAL_VEC(gx, gy, gz); + ivec3 cp = CHUNK_VEC(gx, gy, gz); + + rc_chunk = world_get_chunk(world, cp[0], cp[1], cp[2]); + if (rc_chunk == NULL) + return AIR; + + block = chunk_at(&rc_chunk->chunk, lp[0], lp[1], lp[2]); + rcchunk_drop(rc_chunk); + + return block; +} + +void rcchunk_drop(RcChunk *rc_chunk) +{ + rc_chunk->rc -= 1; + if (rc_chunk->rc == 0) { + chunk_free(&rc_chunk->chunk); + free(rc_chunk); + } +} |