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}