protocol uint/magnolia / source / world / chunk.c
  1#include <stdlib.h>
  2#include <string.h>
  3
  4#include "util.h"
  5#include "world/chunk.h"
  6
  7#define MAX_MESH_VERTICES 65535U
  8
  9static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* index_count,
 10                     i32 face, i32 x, i32 y, i32 z);
 11
 12/* 6 = number of faces on a cube */
 13static const i32 face_offsets[6][3] = { { 0, 0, -1 }, { 0, 0, 1 },  { -1, 0, 0 },
 14	                                { 1, 0, 0 },  { 0, -1, 0 }, { 0, 1, 0 } };
 15
 16static const MGVertex face_vertices[24] = {
 17	/* north: -z */
 18	{ 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, RGBA(255, 0, 0, 255) },
 19	{ 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 0, 255) },
 20	{ 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, RGBA(255, 0, 0, 255) },
 21	{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, RGBA(255, 0, 0, 255) },
 22
 23	/* south: +z */
 24	{ 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, RGBA(0, 255, 0, 255) },
 25	{ 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 0, 255) },
 26	{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, RGBA(0, 255, 0, 255) },
 27	{ 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, RGBA(0, 255, 0, 255) },
 28
 29	/* west: -x */
 30	{ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, RGBA(0, 0, 255, 255) },
 31	{ 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 0, 255, 255) },
 32	{ 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, RGBA(0, 0, 255, 255) },
 33	{ 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, RGBA(0, 0, 255, 255) },
 34
 35	/* east: +x */
 36	{ 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, RGBA(255, 255, 0, 255) },
 37	{ 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 255, 0, 255) },
 38	{ 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, RGBA(255, 255, 0, 255) },
 39	{ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, RGBA(255, 255, 0, 255) },
 40
 41	/* bottom: -y */
 42	{ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, RGBA(255, 0, 255, 255) },
 43	{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 255, 255) },
 44	{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, RGBA(255, 0, 255, 255) },
 45	{ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, RGBA(255, 0, 255, 255) },
 46
 47	/* top: +y */
 48	{ 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, RGBA(0, 255, 255, 255) },
 49	{ 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 255, 255) },
 50	{ 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, RGBA(0, 255, 255, 255) },
 51	{ 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, RGBA(0, 255, 255, 255) }
 52};
 53
 54static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* index_count,
 55                     i32 face, i32 x, i32 y, i32 z)
 56{
 57	MGVertex vtx;
 58	u32      base;
 59	i32      i;
 60
 61	base = *vertex_count;
 62
 63	for (i = 0; i < 4; i++) {
 64		vtx = face_vertices[face * 4 + i];
 65
 66		vtx.x += x;
 67		vtx.y += y;
 68		vtx.z += z;
 69
 70		vertices[(*vertex_count)++] = vtx;
 71	}
 72
 73	indices[(*index_count)++] = (u16)(base + 0);
 74	indices[(*index_count)++] = (u16)(base + 1);
 75	indices[(*index_count)++] = (u16)(base + 2);
 76	indices[(*index_count)++] = (u16)(base + 0);
 77	indices[(*index_count)++] = (u16)(base + 2);
 78	indices[(*index_count)++] = (u16)(base + 3);
 79}
 80
 81void chunk_create(Chunk* chunk)
 82{
 83	i32 x;
 84	i32 y;
 85	i32 z;
 86
 87	if (!chunk)
 88		return;
 89
 90	memset(chunk, 0, sizeof(*chunk));
 91	for (z = 0; z < CHUNK_DEPTH; z++) {
 92		for (x = 0; x < CHUNK_WIDTH; x++) {
 93			for (y = 0; y < CHUNK_HEIGHT - 5; y++)
 94				chunk_set(chunk, x, y, z, BLOCK_STONE);
 95
 96			chunk_set(chunk, x, CHUNK_HEIGHT - 5, z, BLOCK_DIRT);
 97		}
 98	}
 99
100	chunk_set(chunk, 6, 28, 8, BLOCK_BRICK);
101	chunk_set(chunk, 8, 28, 8, BLOCK_BRICK);
102	chunk_set(chunk, 7, 29, 8, BLOCK_BRICK);
103	chunk_set(chunk, 7, 30, 8, BLOCK_BRICK);
104	chunk_set(chunk, 7, 31, 8, BLOCK_STONE);
105}
106
107BlockType chunk_get(const Chunk* chunk, i32 x, i32 y, i32 z)
108{
109	if (!chunk)
110		return BLOCK_AIR;
111
112	if (x < 0 || x >= CHUNK_WIDTH)
113		return BLOCK_AIR;
114
115	if (y < 0 || y >= CHUNK_HEIGHT)
116		return BLOCK_AIR;
117
118	if (z < 0 || z >= CHUNK_DEPTH)
119		return BLOCK_AIR;
120
121	return chunk->blocks[x][y][z];
122}
123
124void chunk_set(Chunk* chunk, i32 x, i32 y, i32 z, BlockType block)
125{
126	if (!chunk)
127		return;
128
129	if (x < 0 || x >= CHUNK_WIDTH)
130		return;
131
132	if (y < 0 || y >= CHUNK_HEIGHT)
133		return;
134
135	if (z < 0 || z >= CHUNK_DEPTH)
136		return;
137
138	chunk->blocks[x][y][z] = block;
139}
140
141i8 chunk_mesh_create(Chunk* chunk)
142{
143	MGVertex*  vertices[BLOCK_LAST];
144	u16*       indices[BLOCK_LAST];
145	u32        face_counts[BLOCK_LAST];
146	u32        vertex_counts[BLOCK_LAST];
147	u32        index_counts[BLOCK_LAST];
148	MGMeshDesc desc;
149	BlockType  block;
150	i32        face;
151	i32        x;
152	i32        y;
153	i32        z;
154
155	if (!chunk)
156		return 0;
157
158	memset(vertices, 0, sizeof(vertices));
159	memset(indices, 0, sizeof(indices));
160	memset(face_counts, 0, sizeof(face_counts));
161	memset(vertex_counts, 0, sizeof(vertex_counts));
162	memset(index_counts, 0, sizeof(index_counts));
163
164	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++)
165		chunk->meshes[block] = MGMESH_INVALID;
166
167	/* count visible faces belonging to each block type  */
168	for (z = 0; z < CHUNK_DEPTH; z++) {
169		for (y = 0; y < CHUNK_HEIGHT; y++) {
170			for (x = 0; x < CHUNK_WIDTH; x++) {
171				block = chunk_get(chunk, x, y, z);
172
173				if (block == BLOCK_AIR)
174					continue;
175
176				for (face = 0; face < 6; face++) {
177					if (chunk_get(chunk, x + face_offsets[face][0],
178					              y + face_offsets[face][1],
179					              z + face_offsets[face][2]) == BLOCK_AIR)
180						face_counts[block]++;
181				}
182			}
183		}
184	}
185
186	/* allocate  for each block type */
187	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
188		if (face_counts[block] == 0)
189			continue;
190
191		if (face_counts[block] * 4U > MAX_MESH_VERTICES) {
192			mg_log(LOG_ERR, "Chunk mesh exceeds u16 vertex limit");
193			goto fail;
194		}
195
196		vertices[block] = malloc(face_counts[block] * 4U * sizeof(*vertices[block]));
197		indices[block] = malloc(face_counts[block] * 6U * sizeof(*indices[block]));
198		if (!vertices[block] || !indices[block]) {
199			mg_log(LOG_ERR, "Failed to allocate chunk mesh");
200			goto fail;
201		}
202	}
203
204	/* omit only faces whose neighbour is air */
205	for (z = 0; z < CHUNK_DEPTH; z++) {
206		for (y = 0; y < CHUNK_HEIGHT; y++) {
207			for (x = 0; x < CHUNK_WIDTH; x++) {
208				block = chunk_get(chunk, x, y, z);
209
210				if (block == BLOCK_AIR)
211					continue;
212
213				for (face = 0; face < 6; face++) {
214					if (chunk_get(chunk, x + face_offsets[face][0],
215					              y + face_offsets[face][1],
216					              z + face_offsets[face][2]) != BLOCK_AIR)
217						continue;
218
219					add_face(vertices[block], indices[block],
220					         &vertex_counts[block], &index_counts[block], face,
221					         x, y, z);
222				}
223			}
224		}
225	}
226
227	/* upload one mesh for each texture */
228	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
229		if (index_counts[block] == 0)
230			continue;
231
232		desc.vtc = vertices[block];
233		desc.vtcc = vertex_counts[block];
234		desc.idc = indices[block];
235		desc.idcc = index_counts[block];
236
237		chunk->meshes[block] = renderer_mesh_create(&desc);
238
239		if (chunk->meshes[block] == MGMESH_INVALID) {
240			mg_log(LOG_ERR, "Failed to upload chunk mesh");
241			goto fail;
242		}
243	}
244
245	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
246		free(indices[block]);
247		free(vertices[block]);
248	}
249
250	return 1;
251
252fail:
253	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
254		free(indices[block]);
255		free(vertices[block]);
256	}
257
258	chunk_mesh_destroy(chunk);
259	return 0;
260}
261
262void chunk_mesh_destroy(Chunk* chunk)
263{
264	BlockType block;
265
266	if (!chunk)
267		return;
268
269	for (block = BLOCK_BRICK; block < BLOCK_LAST; block++) {
270		renderer_mesh_destroy(chunk->meshes[block]);
271		chunk->meshes[block] = MGMESH_INVALID;
272	}
273}