diff options
| author | Freya Murphy <freya@freyacat.org> | 2025-12-11 22:10:52 -0500 |
|---|---|---|
| committer | Freya Murphy <freya@freyacat.org> | 2025-12-11 22:10:52 -0500 |
| commit | e511e9c678eeb76906e75dbc92165e538d13546b (patch) | |
| tree | 30b6dfde164603fa0a677baebe770f44536dfeff | |
| parent | i did a lot (diff) | |
| download | voxel-main.tar.gz voxel-main.tar.bz2 voxel-main.zip | |
| -rwxr-xr-x | assets/fragment.glsl | 2 | ||||
| -rwxr-xr-x | assets/vertex.glsl | 106 | ||||
| -rw-r--r-- | src/chunk.c | 161 | ||||
| -rw-r--r-- | src/gl.h | 2 | ||||
| -rw-r--r-- | src/main.c | 28 | ||||
| -rw-r--r-- | src/math.c | 77 | ||||
| -rw-r--r-- | src/mesh.c | 27 | ||||
| -rw-r--r-- | src/render.c | 6 | ||||
| -rw-r--r-- | src/shader.c | 1 | ||||
| -rw-r--r-- | src/voxel.h | 8 | ||||
| -rw-r--r-- | src/window.c | 5 | ||||
| -rw-r--r-- | src/window.h | 1 | ||||
| -rw-r--r-- | src/world.c | 6 |
13 files changed, 360 insertions, 70 deletions
diff --git a/assets/fragment.glsl b/assets/fragment.glsl index 5ce136e..bb1eab2 100755 --- a/assets/fragment.glsl +++ b/assets/fragment.glsl @@ -1,6 +1,7 @@ #version 330 core
flat in uvec2 pass_data;
+in float pass_ao;
out vec4 color;
@@ -18,5 +19,6 @@ void main(void) uint block = (pass_data.x >> 28) & 15u;
uint face = (pass_data.x >> 25) & 7u;
float tint = TINT[face];
+ tint *= 1.0 - pow(pass_ao / 3.0, 1.2) * 0.65;
color = vec4(vec3(tint), 1);
}
diff --git a/assets/vertex.glsl b/assets/vertex.glsl index f4c3fde..78a479c 100755 --- a/assets/vertex.glsl +++ b/assets/vertex.glsl @@ -1,6 +1,6 @@ #version 330 core
-layout (location = 0) in vec3 quad;
+layout (location = 0) in vec3 in_quad;
layout (std140) uniform Matrices {
mat4 proj;
@@ -19,6 +19,13 @@ layout (std140) uniform Face { };
flat out uvec2 pass_data;
+out float pass_ao;
+
+const mat3 ROT90 = mat3(
+ 0.0, 0.0, 1.0,
+ 0.0, 1.0, 0.0,
+ -1.0, 0.0, 0.0
+);
uint get_half(int index)
{
@@ -34,28 +41,98 @@ uvec2 get_data(int index) return uvec2(lower, upper);
}
+bool get_ao_rotate(uint data)
+{
+ vec4 ao;
+
+ ao.x = (data >> 0) & 3u;
+ ao.y = (data >> 2) & 3u;
+ ao.z = (data >> 4) & 3u;
+ ao.w = (data >> 6) & 3u;
+
+ float a = abs(ao.x - ao.w);
+ float b = abs(ao.y - ao.z);
+
+ return a > b;
+}
+
+float get_ao(uint data, uint face)
+{
+ vec4 ao;
+
+ ao.x = (data >> 0) & 3u;
+ ao.y = (data >> 2) & 3u;
+ ao.z = (data >> 4) & 3u;
+ ao.w = (data >> 6) & 3u;
+
+ switch (face) {
+ case 0u: // PX
+ case 2u: // PY
+ case 4u: // PZ
+ ao = ao.xywz;
+ if (get_ao_rotate(data))
+ ao = ao.wxyz;
+ break;
+ case 1u: // NX
+ case 3u: // NY
+ case 5u: // NZ
+ if (get_ao_rotate(data))
+ ao = ao.ywxz;
+ ao = ao.wzxy;
+ break;
+ }
+
+ return ao[gl_VertexID];
+}
+
+vec3 rotate(vec3 v, uint face)
+{
+ switch(face) {
+ case 0u: // PX
+ case 1u: // NX
+ v = vec3(v.x, v.z, -v.y + 1);
+ break;
+ case 2u: // PY
+ case 3u: // NY
+ v = vec3(-v.z + 1, v.y, v.x);
+ break;
+ case 4u: // PZ
+ case 5u: // NZ
+ v = vec3(v.y, -v.x + 1, v.z);
+ break;
+ }
+
+ return v;
+}
+
vec3 get_quad(uint face, uint width, uint height)
{
- vec3 squad = quad;
- squad.x *= width;
- squad.y *= height;
+ vec3 quad = in_quad;
+ quad.x *= width;
+ quad.y *= height;
switch(face) {
case 0u: // PX
- return squad.yzx + vec3(1, 0, 0);
+ quad = quad.yzx + vec3(1, 0, 0);
+ break;
case 1u: // NX
- return squad.yxz;
+ quad = quad.yxz;
+ break;
case 2u: // PY
- return squad.xyz + vec3(0, 1, 0);
+ quad = quad.xyz + vec3(0, 1, 0);
+ break;
case 3u: // NY
- return squad.zyx;
+ quad = quad.zyx;
+ break;
case 4u: // PZ
- return squad.zxy + vec3(0, 0, 1);
+ quad = quad.zxy + vec3(0, 0, 1);
+ break;
case 5u: // NZ
- return squad.xzy;
+ quad = quad.xzy;
+ break;
}
- // should not happen
- return vec3(0);
+
+ return quad;
}
void main(void)
@@ -71,6 +148,8 @@ void main(void) // get quad verts
vec3 quad = get_quad(face, width, height);
+ if (get_ao_rotate(data.y))
+ quad = rotate(quad, face);
// get position
vec3 position = vec3(x, y, z);
@@ -79,5 +158,8 @@ void main(void) // draw
gl_Position = proj * view * vec4(position, 1.0);
+
+ // fragment input
pass_data = data;
+ pass_ao = get_ao(data.y, face);
}
diff --git a/src/chunk.c b/src/chunk.c index f142788..8610a25 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -59,7 +59,7 @@ void chunk_generate(Chunk *chunk, seed_t seed) typedef struct { List data; - RcChunk *rc_chunks[7]; + RcChunk *rc_chunks[27]; } MeshState; typedef union { @@ -71,7 +71,11 @@ typedef union { u64 height : 5; u64 face : 3; u64 block : 4; - u64 : 32; + u64 ao0 : 2; + u64 ao1 : 2; + u64 ao2 : 2; + u64 ao3 : 2; + u64 : 24; }; u64 raw; } Quad; @@ -84,20 +88,20 @@ static void mesh_state_init(MeshState *state, World *world, Chunk *chunk) 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); + for (int i = 0; i < 27; i++) { + int x_off = ((i / 1) % 3) - 1; + int z_off = ((i / 3) % 3) - 1; + int y_off = ((i / 9) % 3) - 1; + + state->rc_chunks[i] = world_get_chunk(world, cx + x_off, cy + y_off, cz + z_off); + } list_init_u64(&state->data); } static void mesh_state_free(MeshState *state) { - for (i32 i = 0; i < 7; i++) { + for (i32 i = 0; i < 27; i++) { RcChunk *rc_chunk = state->rc_chunks[i]; if (rc_chunk != NULL) rcchunk_drop(rc_chunk); @@ -106,28 +110,37 @@ static void mesh_state_free(MeshState *state) list_free(&state->data); } -static Block mesh_state_get_block(MeshState *state, i32 lx, i32 ly, i32 lz) +static Block mesh_state_get_block(MeshState *state, const ivec3 xyz) { RcChunk *rc_chunk; - i32 index = 0; + i32 index, lx, ly, lz; + + index = 13; + lx = xyz[0]; + ly = xyz[1]; + lz = xyz[2]; if (lx < 0) { - index = 1; + index -= 1; lx += CHUNK_SIZE; } else if (lx >= CHUNK_SIZE) { - index = 2; + index += 1; lx -= CHUNK_SIZE; - } else if (ly < 0) { - index = 3; + } + + if (ly < 0) { + index -= 9; ly += CHUNK_SIZE; } else if (ly >= CHUNK_SIZE) { - index = 4; + index += 9; ly -= CHUNK_SIZE; - } else if (lz < 0) { - index = 5; + } + + if (lz < 0) { + index -= 3; lz += CHUNK_SIZE; } else if (lz >= CHUNK_SIZE) { - index = 6; + index += 3; lz -= CHUNK_SIZE; } @@ -138,7 +151,8 @@ static Block mesh_state_get_block(MeshState *state, i32 lx, i32 ly, i32 lz) return chunk_at(&rc_chunk->chunk, lx, ly, lz); } -static void add_quad(MeshState *state, const ivec3 xyz, Face face, Block block) +static void add_quad(MeshState *state, const ivec3 xyz, Face face, Block block, + u8 occlusion[4]) { Quad quad = { 0 }; quad.x = xyz[0]; @@ -148,10 +162,90 @@ static void add_quad(MeshState *state, const ivec3 xyz, Face face, Block block) quad.height = 1; quad.face = face; quad.block = block; + quad.ao0 = occlusion[0]; + quad.ao1 = occlusion[1]; + quad.ao2 = occlusion[2]; + quad.ao3 = occlusion[3]; list_push_u64(&state->data, quad.raw); } +static void mesh_get_ao(MeshState *state, ivec3 xyz, u32 face, u8 out[4]) +{ + Face perpendicular[2]; + ivec3 other, u, v, vecs[8]; + u8 occluded[8]; + + face_to_vec(face, other); + glm_ivec3_add(xyz, other, other); + + face_perpendicular(face, perpendicular); + face_to_vec(perpendicular[0], u); + face_to_vec(perpendicular[1], v); + + // 0: u + glm_ivec3_add(other, u, vecs[0]); + // 1: -u + glm_ivec3_sub(other, u, vecs[1]); + // 2: v + glm_ivec3_add(other, v, vecs[2]); + // 3: -v + glm_ivec3_sub(other, v, vecs[3]); + // 4: u + v + glm_ivec3_add(other, u, vecs[4]); + glm_ivec3_add(vecs[4], v, vecs[4]); + // 5: u - v + glm_ivec3_add(other, u, vecs[5]); + glm_ivec3_sub(vecs[5], v, vecs[5]); + // 6: -u + v + glm_ivec3_sub(other, u, vecs[6]); + glm_ivec3_add(vecs[6], v, vecs[6]); + // 7: -u - v + glm_ivec3_sub(other, u, vecs[7]); + glm_ivec3_sub(vecs[7], v, vecs[7]); + + occluded[0] = (mesh_state_get_block(state, vecs[0]) != AIR) * 2; + occluded[1] = (mesh_state_get_block(state, vecs[1]) != AIR) * 2; + occluded[2] = (mesh_state_get_block(state, vecs[2]) != AIR) * 2; + occluded[3] = (mesh_state_get_block(state, vecs[3]) != AIR) * 2; + occluded[4] = mesh_state_get_block(state, vecs[4]) != AIR; + occluded[5] = mesh_state_get_block(state, vecs[5]) != AIR; + occluded[6] = mesh_state_get_block(state, vecs[6]) != AIR; + occluded[7] = mesh_state_get_block(state, vecs[7]) != AIR; + + // u + v + out[0] = occluded[0] + occluded[2] + occluded[4]; + // u - v + out[1] = occluded[0] + occluded[3] + occluded[5]; + // -u + v + out[2] = occluded[1] + occluded[2] + occluded[6]; + // -u - v + out[3] = occluded[1] + occluded[3] + occluded[7]; + + for (u32 i = 0; i < 4; i++) + out[i] = (out[i] * 2 + 1) / 3; +} + +static void mesh_face(MeshState *state, ivec3 xyz, Block block, u32 face) +{ + u8 occlusion[4]; + ivec3 other; + Block temp; + + face_to_vec(face, other); + glm_ivec3_add(xyz, other, other); + + // cull check + temp = mesh_state_get_block(state, other); + if (temp != AIR) + return; + + // ambient occlusion + mesh_get_ao(state, xyz, face, occlusion); + + add_quad(state, xyz, face, block, occlusion); +} + void chunk_mesh(Chunk *chunk, World *world) { MeshState state; @@ -172,25 +266,12 @@ void chunk_mesh(Chunk *chunk, World *world) if (block == AIR) continue; - 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); - 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_face(&state, xyz, block, POS_X); + mesh_face(&state, xyz, block, NEG_X); + mesh_face(&state, xyz, block, POS_Y); + mesh_face(&state, xyz, block, NEG_Y); + mesh_face(&state, xyz, block, POS_Z); + mesh_face(&state, xyz, block, NEG_Z); } if (chunk->has_mesh) @@ -13,6 +13,7 @@ typedef struct { u32 vao; + u32 ebo; u32 vbos[MAX_VBOS]; i32 vbos_count; i32 vertex_count; @@ -23,6 +24,7 @@ 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_store_indicies(Mesh *mesh, u16 *data, u32 count); void mesh_finish(void); void mesh_bind(Mesh *mesh); void mesh_unbind(Mesh *mesh); @@ -7,6 +7,8 @@ World world; Camera camera; +_Atomic bool loaded; + void sleep(double seconds) { struct timespec req, rem; @@ -26,10 +28,12 @@ VOXEL_RESULT render_main(void *) Renderer renderer; if (window_init_gl() != VOXEL_OK) - return VOXEL_ERROR; + goto error; if (renderer_init(&renderer) != VOXEL_OK) - return VOXEL_ERROR; + goto error; + + loaded = true; while (!window_closed()) { if (window.resize) { @@ -46,6 +50,11 @@ VOXEL_RESULT render_main(void *) renderer_free(&renderer); return VOXEL_OK; + +error: + window_close(); + loaded = true; + return VOXEL_ERROR; } #define TPS 20 @@ -57,6 +66,9 @@ VOXEL_RESULT logic_main(void *) last_tick = 0; next_tick = 0; + while (!loaded) + ; + while (!window_closed()) { world_update(&world, &camera); @@ -75,13 +87,14 @@ VOXEL_RESULT input_main(void *) last_tick = 0; next_tick = 0; + while (!loaded) + ; + while (!window_closed()) { window_update(); - if (key_down(GLFW_KEY_ESCAPE)) { - window.close = true; - break; - } + if (key_down(GLFW_KEY_ESCAPE)) + window_close(); camera_update(&camera); @@ -96,6 +109,7 @@ VOXEL_RESULT input_main(void *) VOXEL_RESULT main(void) { thrd_t render_thread, logic_thread, input_thread; + loaded = false; if (window_init() != VOXEL_OK) return VOXEL_ERROR; @@ -112,7 +126,7 @@ VOXEL_RESULT main(void) thrd_join(render_thread, NULL); world_free(&world); - window_close(); + window_free(); return VOXEL_OK; } diff --git a/src/math.c b/src/math.c new file mode 100644 index 0000000..ecba107 --- /dev/null +++ b/src/math.c @@ -0,0 +1,77 @@ +#include "voxel.h" + +void face_to_vec(Face face, ivec3 out) +{ + glm_ivec3_zero(out); + + switch (face) { + case POS_X: + out[0] = 1; + break; + case NEG_X: + out[0] = -1; + break; + case POS_Y: + out[1] = 1; + break; + case NEG_Y: + out[1] = -1; + break; + case POS_Z: + out[2] = 1; + break; + case NEG_Z: + out[2] = -1; + break; + } +} + +Face face_opposite(Face face) +{ + switch (face) { + case POS_X: + return NEG_X; + case NEG_X: + return POS_X; + case POS_Y: + return NEG_Y; + case NEG_Y: + return POS_Y; + case POS_Z: + return NEG_Z; + case NEG_Z: + return POS_Z; + } + + return 0; +} + +void face_perpendicular(Face face, Face out[2]) +{ + switch (face) { + case POS_X: + out[0] = NEG_Z; + out[1] = NEG_Y; + break; + case NEG_X: + out[0] = POS_Y; + out[1] = POS_Z; + break; + case POS_Y: + out[0] = NEG_X; + out[1] = NEG_Z; + break; + case NEG_Y: + out[0] = POS_Z; + out[1] = POS_X; + break; + case POS_Z: + out[0] = NEG_Y; + out[1] = NEG_X; + break; + case NEG_Z: + out[0] = POS_X; + out[1] = POS_Y; + break; + } +} @@ -7,6 +7,7 @@ void mesh_init(Mesh *mesh, u32 vertex_count) glGenVertexArrays(1, &mesh->vao); mesh->vbos_count = 0; mesh->vertex_count = vertex_count; + mesh->ebo = 0; glBindVertexArray(mesh->vao); } @@ -16,7 +17,7 @@ static void mesh_store(Mesh *mesh, void *data, u32 len, u32 dimensions, GLenum t GLint index = mesh->vbos_count; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, len, data, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, len * dimensions, data, GL_STATIC_DRAW); glEnableVertexAttribArray(index); if (type == GL_FLOAT) glVertexAttribPointer(index, dimensions, type, GL_FALSE, 0, 0); @@ -45,6 +46,17 @@ void mesh_store_u32(Mesh *mesh, u32 *data, u32 count, u32 dimensions) mesh_store(mesh, data, count * sizeof(u32), dimensions, GL_UNSIGNED_INT); } +void mesh_store_indicies(Mesh *mesh, u16 *data, u32 count) +{ + GLuint ebo; + u32 len; + len = count * sizeof(u16); + glGenBuffers(1, &ebo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, len, data, GL_STATIC_DRAW); + mesh->ebo = ebo; +} + void mesh_finish(void) { glBindVertexArray(0); @@ -66,18 +78,27 @@ void mesh_unbind(Mesh *mesh) void mesh_draw(Mesh *mesh) { - glDrawArrays(GL_TRIANGLES, 0, mesh->vertex_count); + if (mesh->ebo) + glDrawElements(GL_TRIANGLES, mesh->vertex_count, GL_UNSIGNED_SHORT, NULL); + else + glDrawArrays(GL_TRIANGLES, 0, mesh->vertex_count); } void mesh_draw_instanced(Mesh *mesh, u32 count) { - glDrawArraysInstanced(GL_TRIANGLES, 0, mesh->vertex_count, count); + if (mesh->ebo) + glDrawElementsInstanced(GL_TRIANGLES, mesh->vertex_count, GL_UNSIGNED_SHORT, NULL, + count); + else + glDrawArraysInstanced(GL_TRIANGLES, 0, mesh->vertex_count, count); } void mesh_free(Mesh *mesh) { glDeleteBuffers(mesh->vbos_count, mesh->vbos); glDeleteVertexArrays(1, &mesh->vao); + if (mesh->ebo) + glDeleteBuffers(1, &mesh->ebo); } void uniform_init(Uniform *uniform, u32 len) diff --git a/src/render.c b/src/render.c index b70832b..77c0bd8 100644 --- a/src/render.c +++ b/src/render.c @@ -10,10 +10,12 @@ 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, + 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, }; + u16 quad_indicies[] = { 0, 1, 2, 0, 2, 3 }; mesh_init(&renderer->quad, 6); - mesh_store_float(&renderer->quad, quad_verts, 6 * 3, 3); + mesh_store_float(&renderer->quad, quad_verts, 4, 3); + mesh_store_indicies(&renderer->quad, quad_indicies, 6); mesh_finish(); // load shader diff --git a/src/shader.c b/src/shader.c index abb62a2..a0da573 100644 --- a/src/shader.c +++ b/src/shader.c @@ -173,7 +173,6 @@ failure: free(vertex); if (fragment) free(fragment); - free(shader); return GL_ERROR; } diff --git a/src/voxel.h b/src/voxel.h index 12480b2..bba2c44 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -24,7 +24,7 @@ #define VOXEL_FALSE 1 #define VOXEL_RESULT int -typedef enum : char { +typedef enum : u8 { POS_X = 0, NEG_X = 1, POS_Y = 2, @@ -33,7 +33,11 @@ typedef enum : char { NEG_Z = 5, } Face; -typedef enum : char { AIR = 0, A, B, C, D } Block; +void face_to_vec(Face face, ivec3 out); +Face face_opposite(Face face); +void face_perpendicular(Face face, Face out[2]); + +typedef enum : u8 { AIR = 0, A, B, C, D } Block; #define CHUNK_SIZE 16 #define CHUNK_BLOCKS CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE diff --git a/src/window.c b/src/window.c index 559dcc2..d43ba12 100644 --- a/src/window.c +++ b/src/window.c @@ -132,6 +132,11 @@ void window_swap(void) void window_close(void) { + window.close = true; +} + +void window_free(void) +{ mtx_lock(&window.lock); glfwDestroyWindow(window.window); glfwTerminate(); diff --git a/src/window.h b/src/window.h index 5fc3197..c9de8ac 100644 --- a/src/window.h +++ b/src/window.h @@ -43,6 +43,7 @@ bool window_closed(void); void window_update(void); void window_swap(void); void window_close(void); +void window_free(void); bool key_down(i32 key); bool key_pressed(i32 key); diff --git a/src/world.c b/src/world.c index a53055d..0be9a85 100644 --- a/src/world.c +++ b/src/world.c @@ -110,10 +110,10 @@ void world_free(World *world) int size = side * side * side; world_lock(world); for (int i = 0; i < size; i++) { - RcChunk **entry = &world->chunks[i]; - if (entry == NULL) + RcChunk *rc_chunk = world->chunks[i]; + if (rc_chunk == NULL) continue; - rcchunk_drop(*entry); + rcchunk_drop(rc_chunk); } free(world->chunks); mtx_destroy(&world->lock); |