summaryrefslogtreecommitdiff
path: root/src/world.c
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-12-11 10:49:50 -0500
committerFreya Murphy <freya@freyacat.org>2025-12-11 10:51:40 -0500
commitfa8fa6784559ed0fc8d780e36880273f77e272c4 (patch)
tree7456a4e9148d47e409ba837bafdc6238b6c757db /src/world.c
parentadd ubos (diff)
downloadvoxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.tar.gz
voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.tar.bz2
voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.zip
i did a lot
Diffstat (limited to '')
-rw-r--r--src/world.c234
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);
+ }
+}