summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-12-04 13:16:21 -0500
committerFreya Murphy <freya@freyacat.org>2025-12-04 13:18:33 -0500
commitba2f810f0752bdecf8253b34bd245c7939f23534 (patch)
tree23a5fa60afb7f9f5d0ff1b51d11bb8dd24f46e04 /src
downloadvoxel-ba2f810f0752bdecf8253b34bd245c7939f23534.tar.gz
voxel-ba2f810f0752bdecf8253b34bd245c7939f23534.tar.bz2
voxel-ba2f810f0752bdecf8253b34bd245c7939f23534.zip
initial chunk rendering
Diffstat (limited to 'src')
-rw-r--r--src/camera.c146
-rw-r--r--src/chunk.c138
-rw-r--r--src/cube.h60
-rw-r--r--src/list.c71
-rw-r--r--src/list.h26
-rw-r--r--src/main.c50
-rw-r--r--src/mesh.c78
-rw-r--r--src/mesh.h23
-rw-r--r--src/shader.c186
-rw-r--r--src/shader.h22
-rw-r--r--src/voxel.h59
-rw-r--r--src/window.c147
-rw-r--r--src/window.h46
13 files changed, 1052 insertions, 0 deletions
diff --git a/src/camera.c b/src/camera.c
new file mode 100644
index 0000000..ec486d7
--- /dev/null
+++ b/src/camera.c
@@ -0,0 +1,146 @@
+#include "cglm/vec3.h"
+#include <math.h>
+
+#include "voxel.h"
+#include "window.h"
+
+Camera camera_init(void)
+{
+ Camera camera = { 0 };
+ camera.fov = 70;
+ camera.near = 0.1;
+ camera.far = 1000;
+ camera.look_speed = 100;
+ camera.move_speed = 4.317;
+ return camera;
+}
+
+void camera_proj(Camera *camera, mat4 proj)
+{
+ float aspect, tan_half_foxy;
+
+ aspect = (float)window.width / (float)window.height;
+ tan_half_foxy = tanf(camera->fov * (M_PI / 180)) / 2.0;
+
+ glm_mat4_zero(proj);
+ proj[0][0] = 1.0 / (aspect * tan_half_foxy);
+ proj[1][1] = 1.0 / tan_half_foxy;
+ proj[2][2] = camera->far / (camera->far - camera->near);
+ proj[2][3] = 1;
+ proj[3][2] = -(camera->far * camera->near) / (camera->far - camera->near);
+}
+
+void camera_view(Camera *camera, mat4 view)
+{
+ float c3, s3, c2, s2, c1, s1;
+ vec3 u, v, w;
+
+ c3 = cosf(camera->rotation[2] * (M_PI / 180));
+ s3 = sinf(camera->rotation[2] * (M_PI / 180));
+ c2 = cosf(camera->rotation[0] * (M_PI / 180));
+ s2 = sin(camera->rotation[0] * (M_PI / 180));
+ c1 = cosf(camera->rotation[1] * (M_PI / 180));
+ s1 = sinf(camera->rotation[1] * (M_PI / 180));
+
+ u[0] = c1 * c3 + s1 * s2 * s3;
+ u[1] = c2 * s3;
+ u[2] = c1 * s2 * s3 - c3 * s1;
+
+ v[0] = c3 * s1 * s2 - c1 * s3;
+ v[1] = c2 * c3;
+ v[2] = c1 * c3 * s2 + s1 * s3;
+
+ w[0] = c2 * s1;
+ w[1] = -s2;
+ w[2] = c1 * c2;
+
+ view[0][0] = u[0];
+ view[0][1] = v[0];
+ view[0][2] = w[0];
+ view[0][3] = 0;
+ view[1][0] = u[1];
+ view[1][1] = v[1];
+ view[1][2] = w[1];
+ view[1][3] = 0;
+ view[2][0] = u[2];
+ view[2][1] = v[2];
+ view[2][2] = w[2];
+ view[2][3] = 0;
+ view[3][0] = -glm_dot(u, camera->position);
+ view[3][1] = -glm_dot(v, camera->position);
+ view[3][2] = -glm_dot(w, camera->position);
+ view[3][3] = 1;
+}
+
+void camera_proj_view(Camera *camera, mat4 proj_view)
+{
+ mat4 proj, view;
+ camera_proj(camera, proj);
+ camera_view(camera, view);
+ glm_mat4_mul(proj, view, proj_view);
+}
+
+static void camera_update_rotation(Camera *camera)
+{
+ vec3 rotate;
+
+ glm_vec3_zero(rotate);
+ if (key_down(GLFW_KEY_RIGHT))
+ rotate[1] += 1;
+ if (key_down(GLFW_KEY_LEFT))
+ rotate[1] -= 1;
+ if (key_down(GLFW_KEY_UP))
+ rotate[0] -= 1;
+ if (key_down(GLFW_KEY_DOWN))
+ rotate[0] += 1;
+ glm_normalize(rotate);
+
+ camera->rotation[0] += camera->look_speed * delta_time * rotate[0];
+ camera->rotation[1] += camera->look_speed * delta_time * rotate[1];
+
+ camera->rotation[0] = glm_clamp(camera->rotation[0], -90, 90);
+ //camera->rotation[1] = camera->rotation[0] % 360.0;
+}
+
+static void camera_update_movement(Camera *camera)
+{
+ vec3 forward, left, up, move;
+ float yaw;
+
+ yaw = camera->rotation[1] * (M_PI / 180);
+
+ glm_vec3_zero(forward);
+ forward[0] = sinf(yaw);
+ forward[2] = cosf(yaw);
+
+ glm_vec3_zero(left);
+ left[0] = -forward[2];
+ left[2] = forward[0];
+
+ glm_vec3_zero(up);
+ up[1] = 1;
+
+ glm_vec3_zero(move);
+ if (key_down(GLFW_KEY_W))
+ glm_vec3_add(move, forward, move);
+ if (key_down(GLFW_KEY_S))
+ glm_vec3_sub(move, forward, move);
+ if (key_down(GLFW_KEY_A))
+ glm_vec3_add(move, left, move);
+ if (key_down(GLFW_KEY_D))
+ glm_vec3_sub(move, left, move);
+ if (key_down(GLFW_KEY_E))
+ glm_vec3_add(move, up, move);
+ if (key_down(GLFW_KEY_Q))
+ glm_vec3_sub(move, up, move);
+ glm_normalize(move);
+ glm_vec3_scale(move, camera->move_speed * delta_time, move);
+
+ glm_vec3_add(camera->position, move, camera->position);
+}
+
+void camera_update(Camera *camera)
+{
+ camera_update_rotation(camera);
+ camera_update_movement(camera);
+}
diff --git a/src/chunk.c b/src/chunk.c
new file mode 100644
index 0000000..50fe5c3
--- /dev/null
+++ b/src/chunk.c
@@ -0,0 +1,138 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "voxel.h"
+#include "list.h"
+#include "cube.h"
+#include "mesh.h"
+
+Chunk *chunk_init(int x, int y, int z)
+{
+ Chunk *chunk = malloc(sizeof(Chunk));
+ chunk->x = x;
+ chunk->y = y;
+ chunk->z = z;
+ memset(chunk->blocks, AIR, sizeof(chunk->blocks));
+ return chunk;
+}
+
+void chunk_free(Chunk *chunk)
+{
+ free(chunk);
+}
+
+void chunk_generate(Chunk *chunk)
+{
+ 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);
+
+ char block = AIR;
+ int temp = x + z - y;
+ if (temp > 16)
+ block = DIRT;
+
+ chunk->blocks[i] = block;
+ }
+}
+
+typedef struct {
+ Chunk *chunk;
+ List pos;
+ List data;
+ int vertex_count;
+} MeshState;
+
+static void add_vertex(MeshState *state, const vec3 pos, const vec3 xyz, unsigned int data)
+{
+ 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++;
+}
+
+static void add_quad(MeshState *state, const vec3 xyz, Face face, Block block)
+{
+ unsigned int data = (face << 2) | block;
+ const vec3 *verts = CUBE[face];
+
+ 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);
+}
+
+Mesh chunk_mesh(Chunk *chunk)
+{
+ 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 };
+
+ 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);
+
+ if (px == AIR)
+ add_quad(&state, xyz, POS_X, block);
+ if (nx == AIR)
+ add_quad(&state, xyz, NEG_X, block);
+ if (py == AIR)
+ add_quad(&state, xyz, POS_Y, block);
+ if (ny == AIR)
+ add_quad(&state, xyz, NEG_Y, block);
+ if (pz == AIR)
+ add_quad(&state, xyz, POS_Z, block);
+ if (nz == AIR)
+ 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();
+
+ list_free(&state.pos);
+ list_free(&state.data);
+
+ return mesh;
+}
+
+Block chunk_at(Chunk *chunk, int x, int y, int z)
+{
+ if (x < 0 || x >= CHUNK_SIZE)
+ return AIR;
+ if (y < 0 || y >= CHUNK_SIZE)
+ return AIR;
+ if (z < 0 || z >= CHUNK_SIZE)
+ return AIR;
+
+ int i = 0;
+ i += x;
+ i += z * CHUNK_SIZE;
+ i += y * CHUNK_SIZE * CHUNK_SIZE;
+
+ return chunk->blocks[i];
+}
diff --git a/src/cube.h b/src/cube.h
new file mode 100644
index 0000000..fd071d0
--- /dev/null
+++ b/src/cube.h
@@ -0,0 +1,60 @@
+#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/list.c b/src/list.c
new file mode 100644
index 0000000..0f00cdd
--- /dev/null
+++ b/src/list.c
@@ -0,0 +1,71 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "list.h"
+
+static void list_init(List *list, int elmSize)
+{
+ list->data = NULL;
+ list->capacity = 0;
+ list->len = 0;
+ list->elmSize = elmSize;
+}
+
+void list_initf(List *list)
+{
+ list_init(list, sizeof(float));
+}
+
+void list_initi(List *list)
+{
+ list_init(list, sizeof(int));
+}
+
+void list_initu(List *list)
+{
+ list_init(list, sizeof(unsigned int));
+}
+
+void list_initb(List *list)
+{
+ list_init(list, sizeof(unsigned char));
+}
+
+static void list_push(List *list, const void *elm)
+{
+ if (list->len == list->capacity) {
+ list->capacity *= 2;
+ if (!list->capacity)
+ list->capacity = 8;
+ list->data = realloc(list->data, list->elmSize * list->capacity);
+ }
+
+ void *ptr = ((char *)list->data) + list->elmSize * list->len;
+ memcpy(ptr, elm, list->elmSize);
+ list->len++;
+}
+
+void list_pushf(List *list, float f)
+{
+ list_push(list, &f);
+}
+
+void list_pushi(List *list, int i)
+{
+ list_push(list, &i);
+}
+
+void list_pushu(List *list, unsigned int u)
+{
+ list_push(list, &u);
+}
+
+void list_pushb(List *list, unsigned char b)
+{
+ list_push(list, &b);
+}
+
+void list_free(List *list)
+{
+ free(list->data);
+}
diff --git a/src/list.h b/src/list.h
new file mode 100644
index 0000000..6d2211b
--- /dev/null
+++ b/src/list.h
@@ -0,0 +1,26 @@
+#pragma once
+
+typedef struct {
+ union {
+ float *fdata;
+ int *idata;
+ unsigned int *udata;
+ unsigned char *bdata;
+ void *data;
+ };
+ int len;
+ int capacity;
+ int elmSize;
+} List;
+
+void list_initf(List *list);
+void list_initi(List *list);
+void list_initu(List *list);
+void list_initb(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_free(List *list);
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..2ed1a49
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,50 @@
+#include <GLFW/glfw3.h>
+
+#include "voxel.h"
+#include "window.h"
+#include "shader.h"
+
+int main(void)
+{
+ Shader *shader;
+ Chunk *chunk;
+ Mesh mesh;
+ Camera camera;
+ mat4 proj_view;
+
+ if (window_init())
+ return 1;
+
+ shader = shader_init("assets/vertex.glsl", "assets/fragment.glsl");
+ if (!shader)
+ return 1;
+
+ chunk = chunk_init(0, 0, 0);
+ chunk_generate(chunk);
+ mesh = chunk_mesh(chunk);
+
+ camera = camera_init();
+
+ while (!window_closed()) {
+ window_update();
+
+ if (key_down(GLFW_KEY_ESCAPE))
+ break;
+
+ camera_update(&camera);
+ camera_proj_view(&camera, proj_view);
+
+ shader_bind(shader);
+ shader_loadm4f(0, proj_view);
+ mesh_bind(&mesh);
+ mesh_draw(&mesh);
+ mesh_unbind(&mesh);
+ shader_unbind();
+
+ // main game loop
+ window_swap();
+ }
+
+ window_close();
+ return 0;
+}
diff --git a/src/mesh.c b/src/mesh.c
new file mode 100644
index 0000000..6b2951b
--- /dev/null
+++ b/src/mesh.c
@@ -0,0 +1,78 @@
+#include <GL/glew.h>
+
+#include "mesh.h"
+
+void mesh_init(Mesh *mesh, int vertex_count)
+{
+ glGenVertexArrays(1, &mesh->vao);
+ mesh->vbos_count = 0;
+ mesh->vertex_count = vertex_count;
+ glBindVertexArray(mesh->vao);
+}
+
+static void mesh_store(Mesh *mesh, void *data, int data_len, int dimensions, GLenum type)
+{
+ GLuint vbo;
+ GLint index = mesh->vbos_count;
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, data_len, data, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(index);
+ if (type == GL_FLOAT)
+ glVertexAttribPointer(index, dimensions, type, GL_FALSE, 0, 0);
+ else
+ glVertexAttribIPointer(index, dimensions, type, 0, 0);
+ mesh->vbos[mesh->vbos_count++] = vbo;
+}
+
+void mesh_storef(Mesh *mesh, float *data, int data_len, int dimensions)
+{
+ mesh_store(mesh, data, data_len * sizeof(float), dimensions, GL_FLOAT);
+}
+
+void mesh_storei(Mesh *mesh, int *data, int data_len, int dimensions)
+{
+ mesh_store(mesh, data, data_len * sizeof(int), dimensions, GL_INT);
+}
+
+void mesh_storeu(Mesh *mesh, unsigned int *data, int data_len, int dimensions)
+{
+ mesh_store(mesh, data, data_len * sizeof(unsigned int), dimensions,
+ GL_UNSIGNED_INT);
+}
+
+void mesh_storeb(Mesh *mesh, unsigned char *data, int data_len, int dimensions)
+{
+ mesh_store(mesh, data, data_len * sizeof(unsigned char), dimensions,
+ GL_UNSIGNED_BYTE);
+}
+
+void mesh_finish(void)
+{
+ glBindVertexArray(0);
+}
+
+void mesh_bind(Mesh *mesh)
+{
+ glBindVertexArray(mesh->vao);
+ for (int i = 0; i < mesh->vbos_count; i++)
+ glEnableVertexAttribArray(i);
+}
+
+void mesh_unbind(Mesh *mesh)
+{
+ for (int i = 0; i < mesh->vbos_count; i++)
+ glDisableVertexAttribArray(i);
+ glBindVertexArray(0);
+}
+
+void mesh_draw(Mesh *mesh)
+{
+ glDrawArrays(GL_TRIANGLES, 0, mesh->vertex_count);
+}
+
+void mesh_free(Mesh *mesh)
+{
+ glDeleteBuffers(mesh->vbos_count, mesh->vbos);
+ glDeleteVertexArrays(1, &mesh->vao);
+}
diff --git a/src/mesh.h b/src/mesh.h
new file mode 100644
index 0000000..e15b222
--- /dev/null
+++ b/src/mesh.h
@@ -0,0 +1,23 @@
+#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 data_len, int dimensions);
+void mesh_storei(Mesh *mesh, int *data, int data_len, int dimensions);
+void mesh_storeu(Mesh *mesh, unsigned int *data, int data_len, int dimensions);
+void mesh_storeb(Mesh *mesh, unsigned char *data, int data_len, int dimensions);
+void mesh_finish(void);
+void mesh_bind(Mesh *mesh);
+void mesh_unbind(Mesh *mesh);
+void mesh_draw(Mesh *mesh);
+void mesh_free(Mesh *mesh);
diff --git a/src/shader.c b/src/shader.c
new file mode 100644
index 0000000..eefcfc2
--- /dev/null
+++ b/src/shader.c
@@ -0,0 +1,186 @@
+#include <GL/glew.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "voxel.h"
+#include "shader.h"
+
+static 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;
+}
+
+static void print_shader_log(const char *filename, GLuint id)
+{
+ GLint log_len;
+ char *log;
+ glGetShaderiv(id, GL_INFO_LOG_LENGTH, &log_len);
+ log = malloc(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)
+{
+ GLuint id;
+ GLint status;
+ int code_len;
+
+ id = glCreateShader(type);
+ code_len = strlen(code);
+ glShaderSource(id, 1, &code, &code_len);
+ glCompileShader(id);
+ glGetShaderiv(id, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE) {
+ print_shader_log(filename, id);
+ glDeleteShader(id);
+ return 1;
+ }
+
+ *out = id;
+ return 0;
+}
+
+static void parse_bind_attributes(Shader *shader, char *code)
+{
+ char *line, *last_line;
+ char *token, *last_token;
+ int attribute = 0;
+
+ 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;
+
+ token = strtok_r(NULL, " \t", &last_token);
+ token = strtok_r(NULL, " \t", &last_token);
+ *strchr(token, ';') = 0;
+
+ glBindAttribLocation(shader->program_id, attribute, token);
+ attribute++;
+ }
+}
+
+Shader *shader_init(const char *vertex_file, const char *fragment_file)
+{
+ Shader *shader;
+ char *vertex, *fragment;
+
+ shader = malloc(sizeof(Shader));
+ memset(shader, 0, sizeof(Shader));
+
+ // read shader code from file
+ vertex = read_file(vertex_file);
+ fragment = read_file(fragment_file);
+ if (!vertex || !fragment)
+ goto failure;
+
+ // 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))
+ 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);
+
+ if (vertex)
+ free(vertex);
+ if (fragment)
+ free(fragment);
+
+ return shader;
+
+failure:
+ if (vertex)
+ free(vertex);
+ if (fragment)
+ free(fragment);
+ free(shader);
+ return NULL;
+}
+
+void shader_bind(Shader *shader)
+{
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ glFrontFace(GL_CW);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glUseProgram(shader->program_id);
+}
+
+void shader_unbind(void)
+{
+ glDisable(GL_CULL_FACE);
+ glUseProgram(0);
+}
+
+void shader_free(Shader *shader)
+{
+ glDetachShader(shader->program_id, shader->vertex_id);
+ glDetachShader(shader->program_id, shader->fragment_id);
+ glDeleteShader(shader->vertex_id);
+ glDeleteShader(shader->fragment_id);
+ glDeleteProgram(shader->program_id);
+ free(shader);
+}
+
+GLint shader_uniform_location(Shader *shader, const char *name)
+{
+ return glGetUniformLocation(shader->program_id, name);
+}
+
+void shader_loadf(GLint location, float value)
+{
+ glUniform1f(location, value);
+}
+
+void shader_loadi(GLint location, int value)
+{
+ glUniform1i(location, value);
+}
+
+void shader_loadv3f(GLint location, vec3 value)
+{
+ glUniform3f(location, value[0], value[1], value[2]);
+}
+
+void shader_loadm4f(GLint location, mat4 value)
+{
+ glUniformMatrix4fv(location, 1, GL_FALSE, (float *)value);
+}
diff --git a/src/shader.h b/src/shader.h
new file mode 100644
index 0000000..931dd33
--- /dev/null
+++ b/src/shader.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <GL/gl.h>
+#include <cglm/cglm.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_loadf(GLint location, float value);
+void shader_loadi(GLint location, int value);
+void shader_loadv3f(GLint location, vec3 value);
+void shader_loadm4f(GLint location, mat4 value);
diff --git a/src/voxel.h b/src/voxel.h
new file mode 100644
index 0000000..9a3b237
--- /dev/null
+++ b/src/voxel.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <cglm/cglm.h>
+#include <stdio.h>
+
+#include "mesh.h"
+
+#define LOG(level, ...) \
+ do { \
+ printf("%s", level); \
+ printf(__VA_ARGS__); \
+ printf("\n"); \
+ } while (0)
+
+#define INFO(...) LOG("INFO ", __VA_ARGS__)
+#define WARN(...) LOG("WARN ", __VA_ARGS__)
+#define ERROR(...) LOG("ERROR ", __VA_ARGS__)
+
+typedef enum : char {
+ POS_X = 0,
+ NEG_X = 1,
+ POS_Y = 2,
+ NEG_Y = 3,
+ POS_Z = 4,
+ NEG_Z = 5,
+} Face;
+
+typedef enum : char { AIR = 0, DIRT } Block;
+
+#define CHUNK_SIZE 32
+
+typedef struct {
+ int x;
+ int y;
+ int z;
+ Block blocks[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
+} Chunk;
+
+Chunk *chunk_init(int x, int y, int z);
+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);
+
+typedef struct {
+ vec3 position;
+ vec3 rotation;
+ int fov;
+ float near;
+ float far;
+ float look_speed;
+ float move_speed;
+} Camera;
+
+Camera camera_init(void);
+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);
diff --git a/src/window.c b/src/window.c
new file mode 100644
index 0000000..c7e83c6
--- /dev/null
+++ b/src/window.c
@@ -0,0 +1,147 @@
+#include <GL/glew.h>
+#include <GLFW/glfw3.h>
+#include <string.h>
+
+#include "voxel.h"
+#include "window.h"
+
+Window window;
+double delta_time;
+
+static void key_callback(GLFWwindow *, int key, int, int action, int)
+{
+ switch (action) {
+ case GLFW_PRESS:
+ window.key_down[key] = true;
+ window.key_pressed[key] = true;
+ break;
+ case GLFW_RELEASE:
+ window.key_down[key] = false;
+ break;
+ }
+}
+
+static void mouse_move_callback(GLFWwindow *, double xpos, double ypos)
+{
+ window.mouse_x_last = window.mouse_x;
+ window.moues_y_last = window.mouse_y;
+ window.mouse_x = xpos;
+ window.mouse_y = ypos;
+}
+
+static void mouse_button_callback(GLFWwindow *, int button, int action, int)
+{
+ switch (action) {
+ case GLFW_PRESS:
+ window.btn_down[button] = true;
+ window.btn_pressed[button] = true;
+ break;
+ case GLFW_RELEASE:
+ window.btn_down[button] = false;
+ break;
+ }
+}
+
+static void framebuffer_callback(GLFWwindow *, int width, int height)
+{
+ window.width = width;
+ window.height = height;
+ glViewport(0, 0, width, height);
+}
+
+int window_init(void)
+{
+ if (glfwInit() == GLFW_FALSE) {
+ ERROR("GLFW failed to initalize");
+ return 1;
+ }
+
+ memset(&window, 0, sizeof(Window));
+ delta_time = 0;
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ window.monitor = glfwGetPrimaryMonitor();
+ window.window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE,
+ window.monitor, NULL);
+ if (window.window == NULL) {
+ ERROR("failed to create window");
+ return 1;
+ }
+
+ glfwMakeContextCurrent(window.window);
+ glfwSwapInterval(0);
+
+ glewExperimental = GL_TRUE;
+ if (glewInit() != GLEW_OK) {
+ ERROR("GLEW failed to initalize");
+ return 1;
+ }
+
+ glfwSetKeyCallback(window.window, key_callback);
+ glfwSetCursorPosCallback(window.window, mouse_move_callback);
+ glfwSetMouseButtonCallback(window.window, mouse_button_callback);
+ glfwSetFramebufferSizeCallback(window.window, framebuffer_callback);
+
+ return 0;
+}
+
+bool window_closed(void)
+{
+ return glfwWindowShouldClose(window.window) == GLFW_TRUE;
+}
+
+void window_update(void)
+{
+ // reset "pressed"
+ memset(&window.key_pressed, 0, sizeof(window.key_pressed));
+ memset(&window.btn_pressed, 0, sizeof(window.btn_pressed));
+
+ // update delta time
+ double time = glfwGetTime();
+ delta_time = time - window.last_time;
+ window.last_time = time;
+
+ // check for errors
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR)
+ ERROR("OpenGL error: %d", error);
+
+ glfwPollEvents();
+}
+
+void window_swap(void)
+{
+ glfwSwapBuffers(window.window);
+}
+
+void window_close(void)
+{
+ glfwDestroyWindow(window.window);
+ glfwTerminate();
+}
+
+int window_grab_cursor(void);
+int window_release_cursor(void);
+
+bool key_down(int key)
+{
+ return window.key_down[key];
+}
+
+bool key_pressed(int key)
+{
+ return window.key_pressed[key];
+}
+
+bool btn_down(int btn)
+{
+ return window.btn_down[btn];
+}
+
+bool btn_pressed(int btn)
+{
+ return window.btn_pressed[btn];
+}
diff --git a/src/window.h b/src/window.h
new file mode 100644
index 0000000..3690781
--- /dev/null
+++ b/src/window.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <GLFW/glfw3.h>
+
+#define WINDOW_WIDTH 640
+#define WINDOW_HEIGHT 480
+#define WINDOW_TITLE "Voxel Engine"
+
+typedef struct {
+ // screen size
+ int width;
+ int height;
+ // input
+ bool key_down[GLFW_KEY_LAST];
+ bool key_pressed[GLFW_KEY_LAST];
+ bool btn_down[GLFW_MOUSE_BUTTON_LAST];
+ bool btn_pressed[GLFW_MOUSE_BUTTON_LAST];
+ // cursor
+ double mouse_x;
+ double mouse_y;
+ double mouse_x_last;
+ double moues_y_last;
+ // time
+ double last_time;
+ // glfw
+ GLFWwindow *window;
+ GLFWmonitor *monitor;
+} Window;
+
+extern Window window;
+extern double delta_time;
+
+int window_init(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 btn_down(int button);
+bool btn_pressed(int button);