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 | |
| parent | add ubos (diff) | |
| download | voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.tar.gz voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.tar.bz2 voxel-fa8fa6784559ed0fc8d780e36880273f77e272c4.zip | |
i did a lot
Diffstat (limited to 'src')
| -rw-r--r-- | src/camera.c | 69 | ||||
| -rw-r--r-- | src/chunk.c | 237 | ||||
| -rw-r--r-- | src/cube.h | 60 | ||||
| -rw-r--r-- | src/gl.h | 73 | ||||
| -rw-r--r-- | src/list.c | 48 | ||||
| -rw-r--r-- | src/list.h | 37 | ||||
| -rw-r--r-- | src/main.c | 125 | ||||
| -rw-r--r-- | src/mesh.c | 44 | ||||
| -rw-r--r-- | src/mesh.h | 31 | ||||
| -rw-r--r-- | src/render.c | 79 | ||||
| -rw-r--r-- | src/shader.c | 246 | ||||
| -rw-r--r-- | src/shader.h | 25 | ||||
| -rw-r--r-- | src/types.h | 15 | ||||
| -rw-r--r-- | src/utils.c | 65 | ||||
| -rw-r--r-- | src/utils.h | 10 | ||||
| -rw-r--r-- | src/voxel.h | 124 | ||||
| -rw-r--r-- | src/window.c | 90 | ||||
| -rw-r--r-- | src/window.h | 29 | ||||
| -rw-r--r-- | src/world.c | 234 |
19 files changed, 1238 insertions, 403 deletions
diff --git a/src/camera.c b/src/camera.c index ec486d7..a841faa 100644 --- a/src/camera.c +++ b/src/camera.c @@ -1,18 +1,19 @@ -#include "cglm/vec3.h" +#include <GLFW/glfw3.h> #include <math.h> #include "voxel.h" #include "window.h" -Camera camera_init(void) +void camera_init(Camera *camera) { - Camera camera = { 0 }; - camera.fov = 70; - camera.near = 0.1; - camera.far = 1000; - camera.look_speed = 100; - camera.move_speed = 4.317; - return camera; + camera->fov = 70; + camera->near = 0.1; + camera->far = 1000; + camera->look_speed = 100; + camera->move_speed = 4.317 * 10; + glm_vec3_zero(camera->position); + glm_vec3_zero(camera->rotation); + camera->frustum = camera_frustum(camera); } void camera_proj(Camera *camera, mat4 proj) @@ -143,4 +144,54 @@ void camera_update(Camera *camera) { camera_update_rotation(camera); camera_update_movement(camera); + camera->frustum = camera_frustum(camera); +} + +Frustum camera_frustum(Camera *camera) +{ + Frustum frustum; + mat4 proj_view; + + camera_proj_view(camera, proj_view); + glm_mat4_transpose(proj_view); + + // left + glm_vec4_add(proj_view[3], proj_view[0], frustum.left); + + // right + glm_vec4_sub(proj_view[3], proj_view[0], frustum.right); + + // bottom + glm_vec4_add(proj_view[3], proj_view[1], frustum.bottom); + + // top + glm_vec4_sub(proj_view[3], proj_view[1], frustum.top); + + // near + glm_vec4_add(proj_view[3], proj_view[2], frustum.near); + + // far + glm_vec4_sub(proj_view[3], proj_view[2], frustum.far); + + return frustum; +} + +bool aabb_in_plane(AABB *aabb, Plane plane) +{ + vec3 p; + float f; + + p[0] = plane[0] >= 0 ? aabb->max[0] : aabb->min[0]; + p[1] = plane[1] >= 0 ? aabb->max[1] : aabb->min[1]; + p[2] = plane[2] >= 0 ? aabb->max[2] : aabb->min[2]; + f = glm_vec3_dot(p, plane) + plane[3]; + + return f >= 0.0f; +} + +bool aabb_in_frustum(AABB *aabb, Frustum *frustum) +{ + return aabb_in_plane(aabb, frustum->near) && aabb_in_plane(aabb, frustum->far) && + aabb_in_plane(aabb, frustum->left) && aabb_in_plane(aabb, frustum->right) && + aabb_in_plane(aabb, frustum->top) && aabb_in_plane(aabb, frustum->bottom); } diff --git a/src/chunk.c b/src/chunk.c index 50fe5c3..f142788 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -1,98 +1,183 @@ -#include <stdio.h> +#include <cglm/vec3.h> #include <stdlib.h> -#include <string.h> +#include "utils.h" #include "voxel.h" #include "list.h" -#include "cube.h" -#include "mesh.h" +#include "gl.h" +#include "noise.h" -Chunk *chunk_init(int x, int y, int z) +void chunk_init(Chunk *chunk, i32 x, i32 y, i32 z) { - Chunk *chunk = malloc(sizeof(Chunk)); - chunk->x = x; - chunk->y = y; - chunk->z = z; - memset(chunk->blocks, AIR, sizeof(chunk->blocks)); - return chunk; + chunk->blocks = xzalloc(sizeof(Block) * CHUNK_BLOCKS); + chunk->pos[0] = x; + chunk->pos[1] = y; + chunk->pos[2] = z; + chunk->state = CHUNK_NEW; + chunk->has_mesh = false; } void chunk_free(Chunk *chunk) { - free(chunk); + if (chunk->state >= CHUNK_MESHED) + uniform_free(&chunk->mesh); + free(chunk->blocks); } -void chunk_generate(Chunk *chunk) +void chunk_generate(Chunk *chunk, seed_t seed) { - int size = sizeof(chunk->blocks); - for (int i = 0; i < size; i++) { - int x = i % CHUNK_SIZE; - int z = (i / CHUNK_SIZE) % CHUNK_SIZE; - int y = i / (CHUNK_SIZE * CHUNK_SIZE); + fnl_state noise; - char block = AIR; - int temp = x + z - y; - if (temp > 16) - block = DIRT; + if (chunk->state != CHUNK_NEW) + return; - chunk->blocks[i] = block; + chunk->state = CHUNK_GENERATING; + + noise = fnlCreateState(); + noise.seed = seed; + noise.noise_type = FNL_NOISE_PERLIN; + + for (i32 i = 0; i < CHUNK_BLOCKS; i++) { + i32 lx = i % CHUNK_SIZE; + i32 lz = (i / CHUNK_SIZE) % CHUNK_SIZE; + i32 ly = i / (CHUNK_SIZE * CHUNK_SIZE); + + i32 gx = lx + chunk->pos[0] * CHUNK_SIZE; + i32 gy = ly + chunk->pos[1] * CHUNK_SIZE; + i32 gz = lz + chunk->pos[2] * CHUNK_SIZE; + + double data = fnlGetNoise3D(&noise, gx, gy, gz); + data -= gy * 0.01; + if (data < 0.1) + continue; + + chunk->blocks[i] = A; } + + chunk->state = CHUNK_GENERATED; } typedef struct { - Chunk *chunk; - List pos; List data; - int vertex_count; + RcChunk *rc_chunks[7]; } MeshState; -static void add_vertex(MeshState *state, const vec3 pos, const vec3 xyz, unsigned int data) +typedef union { + struct { + u64 x : 5; + u64 y : 5; + u64 z : 5; + u64 width : 5; + u64 height : 5; + u64 face : 3; + u64 block : 4; + u64 : 32; + }; + u64 raw; +} Quad; + +static void mesh_state_init(MeshState *state, World *world, Chunk *chunk) +{ + i32 cx, cy, cz; + + cx = chunk->pos[0]; + cy = chunk->pos[1]; + cz = chunk->pos[2]; + + state->rc_chunks[0] = world_get_chunk(world, cx, cy, cz); + state->rc_chunks[1] = world_get_chunk(world, cx - 1, cy, cz); + state->rc_chunks[2] = world_get_chunk(world, cx + 1, cy, cz); + state->rc_chunks[3] = world_get_chunk(world, cx, cy - 1, cz); + state->rc_chunks[4] = world_get_chunk(world, cx, cy + 1, cz); + state->rc_chunks[5] = world_get_chunk(world, cx, cy, cz - 1); + state->rc_chunks[6] = world_get_chunk(world, cx, cy, cz + 1); + + list_init_u64(&state->data); +} + +static void mesh_state_free(MeshState *state) +{ + for (i32 i = 0; i < 7; i++) { + RcChunk *rc_chunk = state->rc_chunks[i]; + if (rc_chunk != NULL) + rcchunk_drop(rc_chunk); + } + + list_free(&state->data); +} + +static Block mesh_state_get_block(MeshState *state, i32 lx, i32 ly, i32 lz) { - list_pushf(&state->pos, pos[0] + xyz[0]); - list_pushf(&state->pos, pos[1] + xyz[1]); - list_pushf(&state->pos, pos[2] + xyz[2]); - list_pushu(&state->data, data); - state->vertex_count++; + RcChunk *rc_chunk; + i32 index = 0; + + if (lx < 0) { + index = 1; + lx += CHUNK_SIZE; + } else if (lx >= CHUNK_SIZE) { + index = 2; + lx -= CHUNK_SIZE; + } else if (ly < 0) { + index = 3; + ly += CHUNK_SIZE; + } else if (ly >= CHUNK_SIZE) { + index = 4; + ly -= CHUNK_SIZE; + } else if (lz < 0) { + index = 5; + lz += CHUNK_SIZE; + } else if (lz >= CHUNK_SIZE) { + index = 6; + lz -= CHUNK_SIZE; + } + + rc_chunk = state->rc_chunks[index]; + if (rc_chunk == NULL) + return AIR; + + return chunk_at(&rc_chunk->chunk, lx, ly, lz); } -static void add_quad(MeshState *state, const vec3 xyz, Face face, Block block) +static void add_quad(MeshState *state, const ivec3 xyz, Face face, Block block) { - unsigned int data = (face << 2) | block; - const vec3 *verts = CUBE[face]; + Quad quad = { 0 }; + quad.x = xyz[0]; + quad.y = xyz[1]; + quad.z = xyz[2]; + quad.width = 1; + quad.height = 1; + quad.face = face; + quad.block = block; - add_vertex(state, verts[0], xyz, data); - add_vertex(state, verts[1], xyz, data); - add_vertex(state, verts[2], xyz, data); - add_vertex(state, verts[3], xyz, data); - add_vertex(state, verts[4], xyz, data); - add_vertex(state, verts[5], xyz, data); + list_push_u64(&state->data, quad.raw); } -Mesh chunk_mesh(Chunk *chunk) +void chunk_mesh(Chunk *chunk, World *world) { MeshState state; - state.chunk = chunk; - state.vertex_count = 0; - list_initf(&state.pos); - list_initu(&state.data); - int size = sizeof(chunk->blocks); - for (int i = 0; i < size; i++) { - int x = i % CHUNK_SIZE; - int z = (i / CHUNK_SIZE) % CHUNK_SIZE; - int y = i / (CHUNK_SIZE * CHUNK_SIZE); - vec3 xyz = { x, y, z }; + if (chunk->state != CHUNK_GENERATED) + return; + + chunk->state = CHUNK_MESHING; + mesh_state_init(&state, world, chunk); + + for (i32 i = 0; i < CHUNK_BLOCKS; i++) { + i32 lx = i % CHUNK_SIZE; + i32 lz = (i / CHUNK_SIZE) % CHUNK_SIZE; + i32 ly = i / (CHUNK_SIZE * CHUNK_SIZE); + ivec3 xyz = { lx, ly, lz }; Block block = chunk->blocks[i]; if (block == AIR) continue; - Block px = chunk_at(chunk, x + 1, y, z); - Block nx = chunk_at(chunk, x - 1, y, z); - Block py = chunk_at(chunk, x, y + 1, z); - Block ny = chunk_at(chunk, x, y - 1, z); - Block pz = chunk_at(chunk, x, y, z + 1); - Block nz = chunk_at(chunk, x, y, z - 1); + Block px = mesh_state_get_block(&state, lx + 1, ly, lz); + Block nx = mesh_state_get_block(&state, lx - 1, ly, lz); + Block py = mesh_state_get_block(&state, lx, ly + 1, lz); + Block ny = mesh_state_get_block(&state, lx, ly - 1, lz); + Block pz = mesh_state_get_block(&state, lx, ly, lz + 1); + Block nz = mesh_state_get_block(&state, lx, ly, lz - 1); if (px == AIR) add_quad(&state, xyz, POS_X, block); @@ -108,19 +193,20 @@ Mesh chunk_mesh(Chunk *chunk) add_quad(&state, xyz, NEG_Z, block); } - Mesh mesh; - mesh_init(&mesh, state.vertex_count); - mesh_storef(&mesh, state.pos.fdata, state.pos.len, 3); - mesh_storeu(&mesh, state.data.udata, state.data.len, 1); - mesh_finish(); + if (chunk->has_mesh) + uniform_free(&chunk->mesh); - list_free(&state.pos); - list_free(&state.data); + Uniform uniform; + uniform_init(&uniform, state.data.len * sizeof(Quad)); + uniform_store(&uniform, 0, state.data.len * sizeof(Quad), state.data.data); + mesh_state_free(&state); - return mesh; + chunk->mesh = uniform; + chunk->has_mesh = true; + chunk->state = CHUNK_MESHED; } -Block chunk_at(Chunk *chunk, int x, int y, int z) +Block chunk_at(Chunk *chunk, i32 x, i32 y, i32 z) { if (x < 0 || x >= CHUNK_SIZE) return AIR; @@ -129,10 +215,25 @@ Block chunk_at(Chunk *chunk, int x, int y, int z) if (z < 0 || z >= CHUNK_SIZE) return AIR; - int i = 0; + i32 i = 0; i += x; i += z * CHUNK_SIZE; i += y * CHUNK_SIZE * CHUNK_SIZE; return chunk->blocks[i]; } + +AABB chunk_aabb(Chunk *chunk) +{ + AABB aabb = { 0 }; + + // min + aabb.min[0] = chunk->pos[0] * CHUNK_SIZE; + aabb.min[1] = chunk->pos[1] * CHUNK_SIZE; + aabb.min[2] = chunk->pos[2] * CHUNK_SIZE; + + // max + glm_vec3_adds(aabb.min, CHUNK_SIZE, aabb.max); + + return aabb; +} diff --git a/src/cube.h b/src/cube.h deleted file mode 100644 index fd071d0..0000000 --- a/src/cube.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include <cglm/cglm.h> - -static const vec3 CUBE[6][6] = { - { - // PX - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 0, 0 }, - { 1, 0, 0 }, - { 1, 1, 0 }, - { 1, 1, 1 }, - }, - { - // NX - { 0, 1, 0 }, - { 0, 0, 0 }, - { 0, 0, 1 }, - { 0, 0, 1 }, - { 0, 1, 1 }, - { 0, 1, 0 }, - }, - { - // PY - { 1, 1, 0 }, - { 0, 1, 0 }, - { 0, 1, 1 }, - { 0, 1, 1 }, - { 1, 1, 1 }, - { 1, 1, 0 }, - }, - { - // NY - { 0, 0, 1 }, - { 0, 0, 0 }, - { 1, 0, 0 }, - { 1, 0, 0 }, - { 1, 0, 1 }, - { 0, 0, 1 }, - }, - { - // PZ - { 0, 1, 1 }, - { 0, 0, 1 }, - { 1, 0, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 }, - { 0, 1, 1 }, - }, - { - // NZ - { 1, 1, 0 }, - { 1, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 1, 0 }, - { 1, 1, 0 }, - }, -}; diff --git a/src/gl.h b/src/gl.h new file mode 100644 index 0000000..58809c9 --- /dev/null +++ b/src/gl.h @@ -0,0 +1,73 @@ +#pragma once + +#include <GL/gl.h> +#include <cglm/cglm.h> + +#include "list.h" + +#define GL_OK 0 +#define GL_ERROR 1 +#define GL_RESULT int + +#define MAX_VBOS 4 + +typedef struct { + u32 vao; + u32 vbos[MAX_VBOS]; + i32 vbos_count; + i32 vertex_count; +} Mesh; + +void mesh_init(Mesh *mesh, u32 vertex_count); +void mesh_store_float(Mesh *mesh, float *data, u32 count, u32 dimensions); +void mesh_store_i32(Mesh *mesh, i32 *data, u32 count, u32 dimensions); +void mesh_store_u8(Mesh *mesh, u8 *data, u32 count, u32 dimensions); +void mesh_store_u32(Mesh *mesh, u32 *data, u32 count, u32 dimensions); +void mesh_finish(void); +void mesh_bind(Mesh *mesh); +void mesh_unbind(Mesh *mesh); +void mesh_draw(Mesh *mesh); +void mesh_draw_instanced(Mesh *mesh, u32 count); +void mesh_free(Mesh *mesh); + +typedef struct { + u32 id; + u32 len; +} Uniform; + +void uniform_init(Uniform *uniform, u32 len); +void uniform_store(Uniform *uniform, u32 offset, u32 len, void *data); +void uniform_bind(Uniform *uniform, u32 binding); +void uniform_unbind(u32 binding); +void uniform_free(Uniform *uniform); + +typedef struct { + u32 program_id; + u32 vertex_id; + u32 fragment_id; + /* vertex attributes */ + List attribute_names; + List attribute_locations; + /* uniforms */ + List uniform_names; + List uniform_locations; + /* uniform blocks */ + List uniform_block_names; + List uniform_block_indicies; +} Shader; + +GL_RESULT shader_init(Shader *shader, const char *vertexFile, const char *fragmentFile); +void shader_bind(Shader *shader); +void shader_unbind(void); +void shader_free(Shader *shader); + +i32 shader_attribute_location(Shader *shader, const char *name); +i32 shader_uniform_location(Shader *shader, const char *name); +i32 shader_uniform_block_index(Shader *shader, const char *name); + +void shader_load_float(Shader *shader, const char *name, float value); +void shader_load_int(Shader *shader, const char *name, int value); +void shader_load_vec3(Shader *shader, const char *name, vec3 value); +void shader_load_ivec3(Shader *shader, const char *name, ivec3 value); +void shader_load_mat4(Shader *shader, const char *name, mat4 value); +void shader_load_ubo(Shader *shader, const char *name, u32 binding); @@ -3,7 +3,7 @@ #include "list.h" -static void list_init(List *list, int elmSize) +static void list_init(List *list, i32 elmSize) { list->data = NULL; list->capacity = 0; @@ -11,27 +11,37 @@ static void list_init(List *list, int elmSize) list->elmSize = elmSize; } -void list_initf(List *list) +void list_init_float(List *list) { list_init(list, sizeof(float)); } -void list_initi(List *list) +void list_init_i32(List *list) { - list_init(list, sizeof(int)); + list_init(list, sizeof(i32)); } -void list_initu(List *list) +void list_init_u8(List *list) { - list_init(list, sizeof(unsigned int)); + list_init(list, sizeof(u8)); } -void list_initb(List *list) +void list_init_u32(List *list) { - list_init(list, sizeof(unsigned char)); + list_init(list, sizeof(u32)); } -static void list_push(List *list, const void *elm) +void list_init_u64(List *list) +{ + list_init(list, sizeof(u64)); +} + +void list_init_string(List *list) +{ + list_init(list, sizeof(char *)); +} + +static void list_push(List *list, const void *restrict elm) { if (list->len == list->capacity) { list->capacity *= 2; @@ -45,24 +55,34 @@ static void list_push(List *list, const void *elm) list->len++; } -void list_pushf(List *list, float f) +void list_push_float(List *list, float f) { list_push(list, &f); } -void list_pushi(List *list, int i) +void list_push_i32(List *list, i32 i) { list_push(list, &i); } -void list_pushu(List *list, unsigned int u) +void list_push_u8(List *list, u8 b) +{ + list_push(list, &b); +} + +void list_push_u32(List *list, u32 u) { list_push(list, &u); } -void list_pushb(List *list, unsigned char b) +void list_push_u64(List *list, u64 u) { - list_push(list, &b); + list_push(list, &u); +} + +void list_push_string(List *list, char *s) +{ + list_push(list, &s); } void list_free(List *list) @@ -1,26 +1,33 @@ #pragma once +#include "types.h" + typedef struct { union { - float *fdata; - int *idata; - unsigned int *udata; - unsigned char *bdata; + float *floats; + int *ints; + unsigned int *uints; + unsigned char *bytes; + char **strings; void *data; }; - int len; - int capacity; - int elmSize; + u32 len; + u32 capacity; + u32 elmSize; } List; -void list_initf(List *list); -void list_initi(List *list); -void list_initu(List *list); -void list_initb(List *list); +void list_init_float(List *list); +void list_init_i32(List *list); +void list_init_u8(List *list); +void list_init_u32(List *list); +void list_init_u64(List *list); +void list_init_string(List *list); -void list_pushf(List *list, float f); -void list_pushi(List *list, int i); -void list_pushu(List *list, unsigned int u); -void list_pushb(List *list, unsigned char u); +void list_push_float(List *list, float f); +void list_push_i32(List *list, i32 i); +void list_push_u8(List *list, u8 u); +void list_push_u32(List *list, u32 u); +void list_push_u64(List *list, u64 u); +void list_push_string(List *list, char *s); void list_free(List *list); @@ -1,51 +1,118 @@ #include <GLFW/glfw3.h> +#include <threads.h> #include "voxel.h" #include "window.h" -#include "shader.h" -#include "mesh.h" -int main(void) +World world; +Camera camera; + +void sleep(double seconds) +{ + struct timespec req, rem; + req.tv_sec = seconds; + req.tv_nsec = 0; + + if (seconds < 0) + return; + + while (thrd_sleep(&req, &rem) == thrd_error) + req = rem; +} + +/// handles rendering +VOXEL_RESULT render_main(void *) +{ + Renderer renderer; + + if (window_init_gl() != VOXEL_OK) + return VOXEL_ERROR; + + if (renderer_init(&renderer) != VOXEL_OK) + return VOXEL_ERROR; + + while (!window_closed()) { + if (window.resize) { + glViewport(0, 0, window.width, window.height); + window.resize = false; + } + + renderer_start(&renderer, &camera); + world_render(&world, &renderer); + renderer_stop(&renderer); + + window_swap(); + } + + renderer_free(&renderer); + return VOXEL_OK; +} + +#define TPS 20 + +/// handles game logic +VOXEL_RESULT logic_main(void *) { - Shader *shader; - Chunk *chunk; - Mesh mesh; - Camera camera; - mat4 proj_view; + double last_tick, next_tick; + last_tick = 0; + next_tick = 0; - if (window_init()) - return 1; + while (!window_closed()) { + world_update(&world, &camera); - shader = shader_init("assets/vertex.glsl", "assets/fragment.glsl"); - if (!shader) - return 1; + next_tick = last_tick + (1.0 / TPS); + sleep(next_tick - last_tick); + last_tick = next_tick; + } - chunk = chunk_init(0, 0, 0); - chunk_generate(chunk); - mesh = chunk_mesh(chunk); + return VOXEL_OK; +} - camera = camera_init(); +/// handles user input +VOXEL_RESULT input_main(void *) +{ + double last_tick, next_tick; + last_tick = 0; + next_tick = 0; while (!window_closed()) { window_update(); - if (key_down(GLFW_KEY_ESCAPE)) + if (key_down(GLFW_KEY_ESCAPE)) { + window.close = true; break; + } camera_update(&camera); - camera_proj_view(&camera, proj_view); - - shader_bind(shader); - shader_load_mat4(0, proj_view); - mesh_bind(&mesh); - mesh_draw(&mesh); - mesh_unbind(&mesh); - shader_unbind(); - // main game loop - window_swap(); + next_tick = last_tick + (1.0 / TPS); + sleep(next_tick - last_tick); + last_tick = next_tick; } + return VOXEL_OK; +} + +VOXEL_RESULT main(void) +{ + thrd_t render_thread, logic_thread, input_thread; + + if (window_init() != VOXEL_OK) + return VOXEL_ERROR; + + camera_init(&camera); + world_init(&world, 0); + + thrd_create(&logic_thread, logic_main, NULL); + thrd_create(&input_thread, input_main, NULL); + thrd_create(&render_thread, render_main, NULL); + + thrd_join(logic_thread, NULL); + thrd_join(input_thread, NULL); + thrd_join(render_thread, NULL); + + world_free(&world); window_close(); - return 0; + + return VOXEL_OK; } @@ -1,8 +1,8 @@ #include <GL/glew.h> -#include "mesh.h" +#include "gl.h" -void mesh_init(Mesh *mesh, int vertex_count) +void mesh_init(Mesh *mesh, u32 vertex_count) { glGenVertexArrays(1, &mesh->vao); mesh->vbos_count = 0; @@ -10,7 +10,7 @@ void mesh_init(Mesh *mesh, int vertex_count) glBindVertexArray(mesh->vao); } -static void mesh_store(Mesh *mesh, void *data, int len, int dimensions, GLenum type) +static void mesh_store(Mesh *mesh, void *data, u32 len, u32 dimensions, GLenum type) { GLuint vbo; GLint index = mesh->vbos_count; @@ -25,24 +25,24 @@ static void mesh_store(Mesh *mesh, void *data, int len, int dimensions, GLenum t mesh->vbos[mesh->vbos_count++] = vbo; } -void mesh_storef(Mesh *mesh, float *data, int count, int dimensions) +void mesh_store_float(Mesh *mesh, float *data, u32 count, u32 dimensions) { mesh_store(mesh, data, count * sizeof(float), dimensions, GL_FLOAT); } -void mesh_storei(Mesh *mesh, int *data, int count, int dimensions) +void mesh_store_i32(Mesh *mesh, i32 *data, u32 count, u32 dimensions) { - mesh_store(mesh, data, count * sizeof(int), dimensions, GL_INT); + mesh_store(mesh, data, count * sizeof(i32), dimensions, GL_INT); } -void mesh_storeu(Mesh *mesh, unsigned int *data, int count, int dimensions) +void mesh_store_u8(Mesh *mesh, u8 *data, u32 count, u32 dimensions) { - mesh_store(mesh, data, count * sizeof(unsigned int), dimensions, GL_UNSIGNED_INT); + mesh_store(mesh, data, count * sizeof(u8), dimensions, GL_UNSIGNED_BYTE); } -void mesh_storeb(Mesh *mesh, unsigned char *data, int count, int dimensions) +void mesh_store_u32(Mesh *mesh, u32 *data, u32 count, u32 dimensions) { - mesh_store(mesh, data, count * sizeof(unsigned char), dimensions, GL_UNSIGNED_BYTE); + mesh_store(mesh, data, count * sizeof(u32), dimensions, GL_UNSIGNED_INT); } void mesh_finish(void) @@ -69,7 +69,7 @@ void mesh_draw(Mesh *mesh) glDrawArrays(GL_TRIANGLES, 0, mesh->vertex_count); } -void mesh_draw_instanced(Mesh *mesh, int count) +void mesh_draw_instanced(Mesh *mesh, u32 count) { glDrawArraysInstanced(GL_TRIANGLES, 0, mesh->vertex_count, count); } @@ -80,14 +80,32 @@ void mesh_free(Mesh *mesh) glDeleteVertexArrays(1, &mesh->vao); } -void uniform_init(Uniform *uniform, void *data, int len) +void uniform_init(Uniform *uniform, u32 len) { + uniform->len = len; glGenBuffers(1, &uniform->id); glBindBuffer(GL_UNIFORM_BUFFER, uniform->id); - glBufferData(GL_UNIFORM_BUFFER, len, data, GL_STATIC_DRAW); + glBufferData(GL_UNIFORM_BUFFER, len, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); } +void uniform_store(Uniform *uniform, u32 offset, u32 len, void *data) +{ + glBindBuffer(GL_UNIFORM_BUFFER, uniform->id); + glBufferSubData(GL_UNIFORM_BUFFER, offset, len, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void uniform_bind(Uniform *uniform, u32 binding) +{ + glBindBufferBase(GL_UNIFORM_BUFFER, binding, uniform->id); +} + +void uniform_unbind(u32 binding) +{ + glBindBufferBase(GL_UNIFORM_BUFFER, binding, 0); +} + void uniform_free(Uniform *uniform) { glDeleteBuffers(1, &uniform->id); diff --git a/src/mesh.h b/src/mesh.h deleted file mode 100644 index 451803e..0000000 --- a/src/mesh.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include <GL/gl.h> - -#define MAX_VBOS 4 - -typedef struct { - GLuint vao; - GLuint vbos[MAX_VBOS]; - int vbos_count; - int vertex_count; -} Mesh; - -void mesh_init(Mesh *mesh, int vertex_count); -void mesh_storef(Mesh *mesh, float *data, int count, int dimensions); -void mesh_storei(Mesh *mesh, int *data, int count, int dimensions); -void mesh_storeu(Mesh *mesh, unsigned int *data, int count, int dimensions); -void mesh_storeb(Mesh *mesh, unsigned char *data, int count, int dimensions); -void mesh_finish(void); -void mesh_bind(Mesh *mesh); -void mesh_unbind(Mesh *mesh); -void mesh_draw(Mesh *mesh); -void mesh_draw_instanced(Mesh *mesh, int count); -void mesh_free(Mesh *mesh); - -typedef struct { - GLuint id; -} Uniform; - -void uniform_init(Uniform *uniform, void *data, int len); -void uniform_free(Uniform *uniform); diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..b70832b --- /dev/null +++ b/src/render.c @@ -0,0 +1,79 @@ +#include <cglm/cglm.h> + +#include "voxel.h" +#include "gl.h" + +#define MATS_UBO_BINDING 1 +#define FACE_UBO_BINDING 2 + +VOXEL_RESULT renderer_init(Renderer *renderer) +{ + // load quad mesh + float quad_verts[] = { + 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, + }; + mesh_init(&renderer->quad, 6); + mesh_store_float(&renderer->quad, quad_verts, 6 * 3, 3); + mesh_finish(); + + // load shader + if (shader_init(&renderer->shader, "assets/vertex.glsl", "assets/fragment.glsl") != + GL_OK) + return VOXEL_ERROR; + + // load matricies uniform + uniform_init(&renderer->matrices, sizeof(mat4) * 2); + uniform_bind(&renderer->matrices, 1); + shader_load_ubo(&renderer->shader, "Matrices", MATS_UBO_BINDING); + shader_load_ubo(&renderer->shader, "Face", FACE_UBO_BINDING); + + return VOXEL_OK; +} + +void renderer_start(Renderer *renderer, Camera *camera) +{ + mat4 proj, view; + + camera_proj(camera, proj); + camera_view(camera, view); + uniform_store(&renderer->matrices, 0, sizeof(mat4), proj); + uniform_store(&renderer->matrices, sizeof(mat4), sizeof(mat4), view); + + shader_bind(&renderer->shader); + mesh_bind(&renderer->quad); + + renderer->frustum = camera->frustum; +} + +void renderer_draw(Renderer *renderer, Chunk *chunk) +{ + Uniform mesh; + AABB aabb; + + if (chunk->has_mesh == false) + return; + + aabb = chunk_aabb(chunk); + if (!aabb_in_frustum(&aabb, &renderer->frustum)) + return; + + mesh = chunk->mesh; + uniform_bind(&mesh, FACE_UBO_BINDING); + shader_load_ivec3(&renderer->shader, "chunk_position", chunk->pos); + mesh_draw_instanced(&renderer->quad, mesh.len / sizeof(u64)); + uniform_unbind(FACE_UBO_BINDING); +} + +void renderer_stop(Renderer *renderer) +{ + mesh_unbind(&renderer->quad); + shader_unbind(); +} + +void renderer_free(Renderer *renderer) +{ + uniform_unbind(MATS_UBO_BINDING); + uniform_free(&renderer->matrices); + shader_free(&renderer->shader); + mesh_free(&renderer->quad); +} diff --git a/src/shader.c b/src/shader.c index 26bb92b..abb62a2 100644 --- a/src/shader.c +++ b/src/shader.c @@ -1,60 +1,28 @@ #include <GL/glew.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> #include "voxel.h" -#include "shader.h" +#include "utils.h" +#include "list.h" +#include "gl.h" -static GLint current_program; - -static char *read_file(const char *filename) +static void print_shader_log(const char *filename, u32 id) { - FILE *file; - long length, read; - char *buffer; - - file = fopen(filename, "r"); - if (file == NULL) { - ERROR("could not read file: %s", filename); - return NULL; - } - - fseek(file, 0, SEEK_END); - length = ftell(file); - fseek(file, 0, SEEK_SET); - - buffer = malloc(length + 1); - read = fread(buffer, 1, length, file); - buffer[length] = 0; - - if (read < length) { - ERROR("could not read file: %s", filename); - free(buffer); - return NULL; - } - - fclose(file); - return buffer; -} - -static void print_shader_log(const char *filename, GLuint id) -{ - GLint log_len; + i32 log_len; char *log; glGetShaderiv(id, GL_INFO_LOG_LENGTH, &log_len); - log = malloc(log_len + 1); + log = xalloc(log_len + 1); glGetShaderInfoLog(id, log_len, &log_len, log); log[log_len] = 0; ERROR("failed to compile shader: '%s'\n%s", filename, log); free(log); } -static int compile_shader(GLuint *out, const char *filename, const char *code, GLenum type) +static GL_RESULT compile_shader(u32 *out, const char *filename, const char *code, u32 type) { - GLuint id; - GLint status; - int code_len; + u32 id; + i32 status, code_len; id = glCreateShader(type); code_len = strlen(code); @@ -64,41 +32,112 @@ static int compile_shader(GLuint *out, const char *filename, const char *code, G if (status == GL_FALSE) { print_shader_log(filename, id); glDeleteShader(id); - return 1; + return GL_ERROR; } *out = id; - return 0; + return GL_OK; } -static void parse_bind_attributes(Shader *shader, char *code) +static void locate_attributes(Shader *shader) { - char *line, *last_line; - char *token, *last_token; - int attribute = 0; + i32 count, max_len; + List names, locations; + + list_init_string(&names); + list_init_i32(&locations); + + glGetProgramiv(shader->program_id, GL_ACTIVE_ATTRIBUTES, &count); + glGetProgramiv(shader->program_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); + DEBUG("shader %d has %d attributes", shader->program_id, count); - line = strtok_r(code, "\n", &last_line); - for (; line != NULL; line = strtok_r(NULL, "\n", &last_line)) { - token = strtok_r(line, " \t", &last_token); - if (strcmp(token, "in") != 0) - continue; + for (i32 i = 0; i < count; i++) { + i32 size, location; + u32 type; + char *name; - token = strtok_r(NULL, " \t", &last_token); - token = strtok_r(NULL, " \t", &last_token); - *strchr(token, ';') = 0; + name = xalloc(max_len + 1); + glGetActiveAttrib(shader->program_id, i, max_len, NULL, &size, &type, name); - glBindAttribLocation(shader->program_id, attribute, token); - attribute++; + location = glGetAttribLocation(shader->program_id, name); + DEBUG("shader %d located attribute %s at location %d", shader->program_id, name, + location); + + list_push_string(&names, name); + list_push_i32(&locations, location); } + + shader->attribute_names = names; + shader->attribute_locations = locations; } -Shader *shader_init(const char *vertex_file, const char *fragment_file) +static void locate_uniforms(Shader *shader) { - Shader *shader; - char *vertex, *fragment; + i32 count, max_len; + List names, locations; + + list_init_string(&names); + list_init_i32(&locations); + + glGetProgramiv(shader->program_id, GL_ACTIVE_UNIFORMS, &count); + glGetProgramiv(shader->program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_len); + DEBUG("shader %d has %d uniforms", shader->program_id, count); + + for (i32 i = 0; i < count; i++) { + i32 size, location; + u32 type; + char *name; + + name = xalloc(max_len + 1); + glGetActiveUniform(shader->program_id, i, max_len, NULL, &size, &type, name); + + location = glGetUniformLocation(shader->program_id, name); + DEBUG("shader %d located uniform %s at location %d", shader->program_id, name, + location); + + list_push_string(&names, name); + list_push_i32(&locations, location); + } + + shader->uniform_names = names; + shader->uniform_locations = locations; +} + +static void locate_uniform_blocks(Shader *shader) +{ + i32 count; + List names, indicies; + + list_init_string(&names); + list_init_i32(&indicies); + + glGetProgramiv(shader->program_id, GL_ACTIVE_UNIFORM_BLOCKS, &count); + DEBUG("shader %d has %d uniform blocks", shader->program_id, count); + + for (i32 i = 0; i < count; i++) { + i32 index, name_len; + char *name; - shader = malloc(sizeof(Shader)); - memset(shader, 0, sizeof(Shader)); + glGetActiveUniformBlockiv(shader->program_id, i, GL_UNIFORM_BLOCK_NAME_LENGTH, + &name_len); + name = xalloc(name_len + 1); + glGetActiveUniformBlockName(shader->program_id, i, name_len, NULL, name); + + index = glGetUniformBlockIndex(shader->program_id, name); + DEBUG("shader %d located uniform block %s at index %d", shader->program_id, name, + index); + + list_push_string(&names, name); + list_push_i32(&indicies, index); + } + + shader->uniform_block_names = names; + shader->uniform_block_indicies = indicies; +} + +GL_RESULT shader_init(Shader *shader, const char *vertex_file, const char *fragment_file) +{ + char *vertex, *fragment; // read shader code from file vertex = read_file(vertex_file); @@ -109,23 +148,25 @@ Shader *shader_init(const char *vertex_file, const char *fragment_file) // compile shaders if (compile_shader(&shader->vertex_id, vertex_file, vertex, GL_VERTEX_SHADER)) goto failure; - if (compile_shader(&shader->fragment_id, fragment_file, fragment, - GL_FRAGMENT_SHADER)) + if (compile_shader(&shader->fragment_id, fragment_file, fragment, GL_FRAGMENT_SHADER)) goto failure; shader->program_id = glCreateProgram(); glAttachShader(shader->program_id, shader->vertex_id); glAttachShader(shader->program_id, shader->fragment_id); - parse_bind_attributes(shader, vertex); glLinkProgram(shader->program_id); glValidateProgram(shader->program_id); + locate_attributes(shader); + locate_uniforms(shader); + locate_uniform_blocks(shader); + if (vertex) free(vertex); if (fragment) free(fragment); - return shader; + return GL_OK; failure: if (vertex) @@ -133,12 +174,11 @@ failure: if (fragment) free(fragment); free(shader); - return NULL; + return GL_ERROR; } void shader_bind(Shader *shader) { - current_program = shader->program_id; glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -149,7 +189,6 @@ void shader_bind(Shader *shader) void shader_unbind(void) { - current_program = 0; glDisable(GL_CULL_FACE); glUseProgram(0); } @@ -161,36 +200,85 @@ void shader_free(Shader *shader) glDeleteShader(shader->vertex_id); glDeleteShader(shader->fragment_id); glDeleteProgram(shader->program_id); - free(shader); + for (u32 i = 0; i < shader->attribute_names.len; i++) + free(shader->attribute_names.strings[i]); + list_free(&shader->attribute_names); + list_free(&shader->attribute_locations); + for (u32 i = 0; i < shader->uniform_names.len; i++) + free(shader->uniform_names.strings[i]); + list_free(&shader->uniform_names); + list_free(&shader->uniform_locations); + for (u32 i = 0; i < shader->uniform_block_names.len; i++) + free(shader->uniform_block_names.strings[i]); + list_free(&shader->uniform_block_names); + list_free(&shader->uniform_block_indicies); } -GLint shader_uniform_location(Shader *shader, const char *name) +int shader_attribute_location(Shader *shader, const char *name) { - return glGetUniformLocation(shader->program_id, name); + for (u32 i = 0; i < shader->attribute_names.len; i++) { + const char *attribute = shader->attribute_names.strings[i]; + if (strcmp(attribute, name) == 0) + return shader->attribute_locations.ints[i]; + } + WARN("unknown attribute '%s'", name); + return -1; } -void shader_load_float(GLint location, float value) +int shader_uniform_location(Shader *shader, const char *name) { + for (u32 i = 0; i < shader->uniform_names.len; i++) { + const char *uniform = shader->uniform_names.strings[i]; + if (strcmp(uniform, name) == 0) + return shader->uniform_locations.ints[i]; + } + WARN("unknown uniform '%s'", name); + return -1; +} + +int shader_uniform_block_index(Shader *shader, const char *name) +{ + for (u32 i = 0; i < shader->uniform_block_names.len; i++) { + const char *uniform_block = shader->uniform_block_names.strings[i]; + if (strcmp(uniform_block, name) == 0) + return shader->uniform_block_indicies.ints[i]; + } + WARN("unknown uniform block '%s'", name); + return -1; +} + +void shader_load_float(Shader *shader, const char *name, float value) +{ + int location = shader_uniform_location(shader, name); glUniform1f(location, value); } -void shader_load_int(GLint location, int value) +void shader_load_int(Shader *shader, const char *name, int value) { + int location = shader_uniform_location(shader, name); glUniform1i(location, value); } -void shader_load_vec3(GLint location, vec3 value) +void shader_load_vec3(Shader *shader, const char *name, vec3 value) { + int location = shader_uniform_location(shader, name); glUniform3f(location, value[0], value[1], value[2]); } -void shader_load_mat4(GLint location, mat4 value) +void shader_load_ivec3(Shader *shader, const char *name, ivec3 value) +{ + int location = shader_uniform_location(shader, name); + glUniform3i(location, value[0], value[1], value[2]); +} + +void shader_load_mat4(Shader *shader, const char *name, mat4 value) { + int location = shader_uniform_location(shader, name); glUniformMatrix4fv(location, 1, GL_FALSE, (float *)value); } -void shader_load_ubo(GLint location, GLuint index, Uniform *uniform) +void shader_load_ubo(Shader *shader, const char *name, u32 binding) { - glUniformBlockBinding(current_program, index, location); - glBindBufferBase(GL_UNIFORM_BUFFER, location, uniform->id); + int index = shader_uniform_block_index(shader, name); + glUniformBlockBinding(shader->program_id, index, binding); } diff --git a/src/shader.h b/src/shader.h deleted file mode 100644 index e6f794e..0000000 --- a/src/shader.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include <GL/gl.h> -#include <cglm/cglm.h> - -#include "mesh.h" - -typedef struct { - GLuint program_id; - GLuint vertex_id; - GLuint fragment_id; - int attributes; -} Shader; - -Shader *shader_init(const char *vertexFile, const char *fragmentFile); -void shader_bind(Shader *shader); -void shader_unbind(void); -void shader_free(Shader *shader); - -GLint shader_uniform_location(Shader *shader, const char *name); -void shader_load_float(GLint location, float value); -void shader_load_int(GLint location, int value); -void shader_load_vec3(GLint location, vec3 value); -void shader_load_mat4(GLint location, mat4 value); -void shader_load_ubo(GLint location, GLuint index, Uniform *uniform); diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..739828c --- /dev/null +++ b/src/types.h @@ -0,0 +1,15 @@ +#pragma once + +#include <sys/types.h> + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef size_t usize; + +typedef signed char i8; +typedef signed short i16; +typedef signed int i32; +typedef signed long long i64; +typedef ssize_t isize; diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..fdc6aea --- /dev/null +++ b/src/utils.c @@ -0,0 +1,65 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "types.h" +#include "voxel.h" +#include "utils.h" + +char *read_file(const char *filename) +{ + FILE *file; + long length, read; + char *buffer; + + file = fopen(filename, "r"); + if (file == NULL) { + ERROR("could not read file: %s", filename); + return NULL; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + fseek(file, 0, SEEK_SET); + + buffer = malloc(length + 1); + read = fread(buffer, 1, length, file); + buffer[length] = 0; + + if (read < length) { + ERROR("could not read file: %s", filename); + free(buffer); + return NULL; + } + + fclose(file); + return buffer; +} + +_Noreturn void die(void) +{ + exit(1); +} + +void *xalloc(usize size) +{ + void *ptr = malloc(size); + if (ptr == NULL && size != 0) + die(); + return ptr; +} + +void *xrealloc(void *ptr, usize size) +{ + ptr = realloc(ptr, size); + if (ptr == NULL && size != 0) + die(); + return ptr; +} + +void *xzalloc(usize size) +{ + void *ptr = xalloc(size); + memset(ptr, 0, size); + return ptr; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..d8feef4 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,10 @@ +#pragma once + +#include "types.h" + +char *read_file(const char *filename); + +_Noreturn void die(void); +void *xalloc(usize size); +void *xrealloc(void *ptr, usize size); +void *xzalloc(usize size); diff --git a/src/voxel.h b/src/voxel.h index 9a3b237..12480b2 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -1,12 +1,13 @@ #pragma once #include <cglm/cglm.h> -#include <stdio.h> +#include <threads.h> -#include "mesh.h" +#include "types.h" +#include "gl.h" -#define LOG(level, ...) \ - do { \ +#define LOG(level, ...) \ + do { \ printf("%s", level); \ printf(__VA_ARGS__); \ printf("\n"); \ @@ -15,6 +16,13 @@ #define INFO(...) LOG("INFO ", __VA_ARGS__) #define WARN(...) LOG("WARN ", __VA_ARGS__) #define ERROR(...) LOG("ERROR ", __VA_ARGS__) +#define DEBUG(...) LOG("DEBUG ", __VA_ARGS__) + +#define VOXEL_OK 0 +#define VOXEL_ERROR 1 +#define VOXEL_TRUE 0 +#define VOXEL_FALSE 1 +#define VOXEL_RESULT int typedef enum : char { POS_X = 0, @@ -25,24 +33,61 @@ typedef enum : char { NEG_Z = 5, } Face; -typedef enum : char { AIR = 0, DIRT } Block; +typedef enum : char { AIR = 0, A, B, C, D } Block; + +#define CHUNK_SIZE 16 +#define CHUNK_BLOCKS CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE + +#define LOCAL_POS(n) (((n) % CHUNK_SIZE + CHUNK_SIZE) % CHUNK_SIZE) +#define LOCAL_VEC(gx, gy, gz) { LOCAL_POS(gx), LOCAL_POS(gy), LOCAL_POS(gz) } +#define LOCAL_VEC_V(vec) LOCAL_VEC((int)(vec)[0], (int)(vec)[1], (int)(vec)[2]) + +#define CHUNK_POS(n) (((n) - LOCAL_POS(n)) / CHUNK_SIZE) +#define CHUNK_VEC(gx, gy, gz) { CHUNK_POS(gx), CHUNK_POS(gy), CHUNK_POS(gz) } +#define CHUNK_VEC_V(vec) CHUNK_VEC((int)(vec)[0], (int)(vec)[1], (int)(vec)[2]) -#define CHUNK_SIZE 32 +typedef enum chunk_state { + CHUNK_NEW = 0, + CHUNK_GENERATING, + CHUNK_GENERATED, + CHUNK_MESHING, + CHUNK_MESHED, +} ChunkState; -typedef struct { - int x; - int y; - int z; - Block blocks[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; +typedef struct chunk { + ivec3 pos; + Block *blocks; + _Atomic ChunkState state; + /* cached mesh */ + _Atomic bool has_mesh; + Uniform mesh; } Chunk; -Chunk *chunk_init(int x, int y, int z); +void chunk_init(Chunk *chunk, i32 cx, i32 cy, i32 cz); void chunk_free(Chunk *chunk); -void chunk_generate(Chunk *chunk); -Mesh chunk_mesh(Chunk *chunk); -Block chunk_at(Chunk *chunk, int x, int y, int z); +void chunk_generate(Chunk *chunk, i32 seed); +Block chunk_at(Chunk *chunk, i32 lx, i32 ly, i32 lz); -typedef struct { +typedef vec4 Plane; +typedef struct frustum { + Plane left; + Plane right; + Plane bottom; + Plane top; + Plane near; + Plane far; +} Frustum; + +typedef struct aabb { + vec3 min; + vec3 max; +} AABB; + +bool aabb_in_plane(AABB *aabb, Plane plane); +bool aabb_in_frustum(AABB *aabb, Frustum *frustum); +AABB chunk_aabb(Chunk *chunk); + +typedef struct camera { vec3 position; vec3 rotation; int fov; @@ -50,10 +95,55 @@ typedef struct { float far; float look_speed; float move_speed; + Frustum frustum; } Camera; -Camera camera_init(void); +void camera_init(Camera *camera); void camera_proj(Camera *camera, mat4 proj); void camera_view(Camera *camera, mat4 view); void camera_proj_view(Camera *camera, mat4 proj_view); void camera_update(Camera *camera); +Frustum camera_frustum(Camera *camera); + +typedef struct renderer { + Mesh quad; + Shader shader; + Uniform matrices; + Frustum frustum; +} Renderer; + +VOXEL_RESULT renderer_init(Renderer *renderer); +void renderer_start(Renderer *renderer, Camera *camera); +void renderer_draw(Renderer *renderer, Chunk *chunk); +void renderer_stop(Renderer *renderer); +void renderer_free(Renderer *renderer); + +#define RENDER_DISTANCE 10 + +typedef struct rc_chunk { + Chunk chunk; + _Atomic u32 rc; +} RcChunk; + +void rcchunk_drop(RcChunk *chunk); + +typedef i32 seed_t; + +typedef struct world { + seed_t seed; + bool loaded; + ivec3 center; + RcChunk **chunks; + mtx_t lock; +} World; + +void world_init(World *world, seed_t seed); +void world_lock(World *world); +void world_unlock(World *world); +void world_free(World *world); +void world_update(World *world, Camera *camera); +void world_render(World *world, Renderer *renderer); +RcChunk *world_get_chunk(World *world, i32 cx, i32 cy, i32 cz); +bool world_is_generated(World *world, i32 cx, i32 cy, i32 cz); +Block world_get_block(World *world, i32 gx, i32 gy, i32 gz); +void chunk_mesh(Chunk *chunk, World *world); diff --git a/src/window.c b/src/window.c index c7e83c6..559dcc2 100644 --- a/src/window.c +++ b/src/window.c @@ -1,14 +1,15 @@ #include <GL/glew.h> #include <GLFW/glfw3.h> #include <string.h> +#include <threads.h> #include "voxel.h" #include "window.h" Window window; -double delta_time; +_Atomic double delta_time; -static void key_callback(GLFWwindow *, int key, int, int action, int) +static void key_callback(GLFWwindow *, i32 key, i32, i32 action, i32) { switch (action) { case GLFW_PRESS: @@ -29,7 +30,7 @@ static void mouse_move_callback(GLFWwindow *, double xpos, double ypos) window.mouse_y = ypos; } -static void mouse_button_callback(GLFWwindow *, int button, int action, int) +static void mouse_button_callback(GLFWwindow *, i32 button, i32 action, i32) { switch (action) { case GLFW_PRESS: @@ -42,59 +43,69 @@ static void mouse_button_callback(GLFWwindow *, int button, int action, int) } } -static void framebuffer_callback(GLFWwindow *, int width, int height) +static void framebuffer_callback(GLFWwindow *, i32 width, i32 height) { window.width = width; window.height = height; - glViewport(0, 0, width, height); + window.resize = true; } -int window_init(void) +VOXEL_RESULT window_init(void) { if (glfwInit() == GLFW_FALSE) { ERROR("GLFW failed to initalize"); - return 1; + return VOXEL_ERROR; } memset(&window, 0, sizeof(Window)); delta_time = 0; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - window.monitor = glfwGetPrimaryMonitor(); - window.window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, - window.monitor, NULL); + window.window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL); if (window.window == NULL) { ERROR("failed to create window"); - return 1; + return VOXEL_ERROR; } + glfwSetKeyCallback(window.window, key_callback); + glfwSetCursorPosCallback(window.window, mouse_move_callback); + glfwSetMouseButtonCallback(window.window, mouse_button_callback); + glfwSetFramebufferSizeCallback(window.window, framebuffer_callback); + + mtx_init(&window.lock, mtx_plain); + + return VOXEL_OK; +} + +VOXEL_RESULT window_init_gl(void) +{ glfwMakeContextCurrent(window.window); + +#ifndef USE_VSYNC glfwSwapInterval(0); +#endif glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { ERROR("GLEW failed to initalize"); - return 1; + return VOXEL_ERROR; } - glfwSetKeyCallback(window.window, key_callback); - glfwSetCursorPosCallback(window.window, mouse_move_callback); - glfwSetMouseButtonCallback(window.window, mouse_button_callback); - glfwSetFramebufferSizeCallback(window.window, framebuffer_callback); - - return 0; + return VOXEL_OK; } bool window_closed(void) { - return glfwWindowShouldClose(window.window) == GLFW_TRUE; + return window.close || glfwWindowShouldClose(window.window) == GLFW_TRUE; } void window_update(void) { + mtx_lock(&window.lock); + // reset "pressed" memset(&window.key_pressed, 0, sizeof(window.key_pressed)); memset(&window.btn_pressed, 0, sizeof(window.btn_pressed)); @@ -110,6 +121,8 @@ void window_update(void) ERROR("OpenGL error: %d", error); glfwPollEvents(); + + mtx_unlock(&window.lock); } void window_swap(void) @@ -119,29 +132,44 @@ void window_swap(void) void window_close(void) { + mtx_lock(&window.lock); glfwDestroyWindow(window.window); glfwTerminate(); + mtx_destroy(&window.lock); } -int window_grab_cursor(void); -int window_release_cursor(void); - -bool key_down(int key) +bool key_down(i32 key) { - return window.key_down[key]; + bool down; + mtx_lock(&window.lock); + down = window.key_down[key]; + mtx_unlock(&window.lock); + return down; } -bool key_pressed(int key) +bool key_pressed(i32 key) { - return window.key_pressed[key]; + bool pressed; + mtx_lock(&window.lock); + pressed = window.key_pressed[key]; + mtx_unlock(&window.lock); + return pressed; } -bool btn_down(int btn) +bool btn_down(i32 btn) { - return window.btn_down[btn]; + bool down; + mtx_lock(&window.lock); + down = window.btn_down[btn]; + mtx_unlock(&window.lock); + return down; } -bool btn_pressed(int btn) +bool btn_pressed(i32 btn) { - return window.btn_pressed[btn]; + bool pressed; + mtx_lock(&window.lock); + pressed = window.btn_pressed[btn]; + mtx_unlock(&window.lock); + return pressed; } diff --git a/src/window.h b/src/window.h index 3690781..5fc3197 100644 --- a/src/window.h +++ b/src/window.h @@ -1,6 +1,9 @@ #pragma once #include <GLFW/glfw3.h> +#include <threads.h> + +#include "voxel.h" #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 @@ -8,8 +11,8 @@ typedef struct { // screen size - int width; - int height; + u32 width; + u32 height; // input bool key_down[GLFW_KEY_LAST]; bool key_pressed[GLFW_KEY_LAST]; @@ -24,23 +27,25 @@ typedef struct { double last_time; // glfw GLFWwindow *window; - GLFWmonitor *monitor; + // sync + mtx_t lock; + // flags + _Atomic bool close; + _Atomic bool resize; } Window; extern Window window; -extern double delta_time; +extern _Atomic double delta_time; -int window_init(void); +VOXEL_RESULT window_init(void); +VOXEL_RESULT window_init_gl(void); bool window_closed(void); void window_update(void); void window_swap(void); void window_close(void); -int window_grab_cursor(void); -int window_release_cursor(void); - -bool key_down(int key); -bool key_pressed(int key); +bool key_down(i32 key); +bool key_pressed(i32 key); -bool btn_down(int button); -bool btn_pressed(int button); +bool btn_down(i32 button); +bool btn_pressed(i32 button); 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); + } +} |