#include #include #include "utils.h" #include "voxel.h" #include "list.h" #include "gl.h" #include "noise.h" void chunk_init(Chunk *chunk, i32 x, i32 y, i32 z) { 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) { if (chunk->state >= CHUNK_MESHED) uniform_free(&chunk->mesh); free(chunk->blocks); } void chunk_generate(Chunk *chunk, seed_t seed) { fnl_state noise; if (chunk->state != CHUNK_NEW) return; 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 { List data; RcChunk *rc_chunks[27]; } MeshState; 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 ao0 : 2; u64 ao1 : 2; u64 ao2 : 2; u64 ao3 : 2; u64 : 24; }; 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]; 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 < 27; 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, const ivec3 xyz) { RcChunk *rc_chunk; i32 index, lx, ly, lz; index = 13; lx = xyz[0]; ly = xyz[1]; lz = xyz[2]; if (lx < 0) { index -= 1; lx += CHUNK_SIZE; } else if (lx >= CHUNK_SIZE) { index += 1; lx -= CHUNK_SIZE; } if (ly < 0) { index -= 9; ly += CHUNK_SIZE; } else if (ly >= CHUNK_SIZE) { index += 9; ly -= CHUNK_SIZE; } if (lz < 0) { index -= 3; lz += CHUNK_SIZE; } else if (lz >= CHUNK_SIZE) { index += 3; 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 ivec3 xyz, Face face, Block block, u8 occlusion[4]) { 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; 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; 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; 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) uniform_free(&chunk->mesh); 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); chunk->mesh = uniform; chunk->has_mesh = true; chunk->state = CHUNK_MESHED; } Block chunk_at(Chunk *chunk, i32 x, i32 y, i32 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; 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; }