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, const BlockFace* face,
10 i32 x, i32 y, i32 z);
11static BlockType chunk_section_get_neighbour(const ChunkSection* section, const ChunkSection* neighbours[6], i32 x,
12 i32 y, i32 z);
13
14/* 6 = number of faces on a cube */
15static const i32 face_offsets[6][3] = {
16 { 0, 0, -1 }, { 0, 0, 1 }, { -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 }, { 0, 1, 0 }
17};
18
19/* add face vertices and indices */
20static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* index_count, const BlockFace* face,
21 i32 x, i32 y, i32 z)
22{
23 MGVertex vtx;
24 u32 base;
25 i32 i;
26
27 base = *vertex_count;
28
29 for (i = 0; i < 4; i++) {
30 vtx = face->vertices[i];
31
32 vtx.x += x;
33 vtx.y += y;
34 vtx.z += z;
35
36 vertices[(*vertex_count)++] = vtx;
37 }
38
39 indices[(*index_count)++] = (u16)(base + 0);
40 indices[(*index_count)++] = (u16)(base + 1);
41 indices[(*index_count)++] = (u16)(base + 2);
42 indices[(*index_count)++] = (u16)(base + 0);
43 indices[(*index_count)++] = (u16)(base + 2);
44 indices[(*index_count)++] = (u16)(base + 3);
45}
46
47/* get block type from section or neighbouring sections
48 returns the BlockType or BLOCK_AIR if not found */
49static BlockType chunk_section_get_neighbour(const ChunkSection* section, const ChunkSection* neighbours[6], i32 x,
50 i32 y, i32 z)
51{
52 if (neighbours) {
53 if (z < 0 && neighbours[0])
54 return chunk_section_get(neighbours[0], x, y, CHUNK_SECTION_DEPTH - 1);
55 if (z >= CHUNK_SECTION_DEPTH && neighbours[1])
56 return chunk_section_get(neighbours[1], x, y, 0);
57 if (x < 0 && neighbours[2])
58 return chunk_section_get(neighbours[2], CHUNK_SECTION_WIDTH - 1, y, z);
59 if (x >= CHUNK_SECTION_WIDTH && neighbours[3])
60 return chunk_section_get(neighbours[3], 0, y, z);
61 if (y < 0 && neighbours[4])
62 return chunk_section_get(neighbours[4], x, CHUNK_SECTION_HEIGHT - 1, z);
63 if (y >= CHUNK_SECTION_HEIGHT && neighbours[5])
64 return chunk_section_get(neighbours[5], x, 0, z);
65 }
66
67 return chunk_section_get(section, x, y, z);
68}
69
70void chunk_clear(Chunk* chunk)
71{
72 i32 section;
73
74 if (!chunk)
75 return;
76
77 for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
78 if (!chunk->sections[section])
79 continue;
80
81 chunk_section_mesh_destroy(chunk->sections[section]);
82 free(chunk->sections[section]);
83 chunk->sections[section] = NULL;
84 }
85
86 chunk->section_mask = 0;
87}
88
89void chunk_remove(Chunk* chunk)
90{
91 if (!chunk)
92 return;
93
94 chunk_clear(chunk);
95 memset(chunk, 0, sizeof(*chunk));
96}
97
98void chunk_section_create(ChunkSection* section)
99{
100 i32 x;
101 i32 y;
102 i32 z;
103
104 if (!section)
105 return;
106
107 memset(section, 0, sizeof(*section));
108 for (z = 0; z < CHUNK_SECTION_DEPTH; z++) {
109 for (x = 0; x < CHUNK_SECTION_WIDTH; x++) {
110 for (y = 0; y < CHUNK_SECTION_HEIGHT - 5; y++)
111 chunk_section_set(section, x, y, z, BLOCK_STONE);
112
113 chunk_section_set(section, x, CHUNK_SECTION_HEIGHT - 5, z, BLOCK_DIRT);
114 }
115 }
116
117 /* dih */
118 chunk_section_set(section, 6, 12, 8, BLOCK_BRICK);
119 chunk_section_set(section, 8, 12, 8, BLOCK_BRICK);
120 chunk_section_set(section, 7, 13, 8, BLOCK_BRICK);
121 chunk_section_set(section, 7, 14, 8, BLOCK_BRICK);
122 chunk_section_set(section, 7, 15, 8, BLOCK_STONE);
123}
124
125i8 chunk_section_decode(ChunkSection* section, const u8* data, size_t size)
126{
127 i32 index;
128 i32 x;
129 i32 y;
130 i32 z;
131 u16 state;
132
133 if (!section || !data)
134 return 0;
135
136 if (size < 8192)
137 return 0;
138
139 chunk_section_mesh_destroy(section);
140 memset(section, 0, sizeof(*section));
141
142 for (index = 0; index < 4096; index++) {
143 /* combine first two bytes to get block state (l operation
144 is little endian) */
145 state = (u16)data[index * 2] | ((u16)data[index * 2 + 1] << 8);
146
147 /* convert index->3D */
148 x = index & 15;
149 z = (index >> 4) & 15;
150 y = (index >> 8) & 15;
151
152 chunk_section_set(section, x, y, z, state);
153 }
154
155 return 1;
156}
157
158BlockType chunk_section_get(const ChunkSection* section, i32 x, i32 y, i32 z)
159{
160 if (!section)
161 return BLOCK_AIR;
162
163 if (x < 0 || x >= CHUNK_SECTION_WIDTH)
164 return BLOCK_AIR;
165
166 if (y < 0 || y >= CHUNK_SECTION_HEIGHT)
167 return BLOCK_AIR;
168
169 if (z < 0 || z >= CHUNK_SECTION_DEPTH)
170 return BLOCK_AIR;
171
172 return section->blocks[x][y][z];
173}
174
175void chunk_section_set(ChunkSection* section, i32 x, i32 y, i32 z, BlockType block)
176{
177 if (!section)
178 return;
179
180 if (x < 0 || x >= CHUNK_SECTION_WIDTH)
181 return;
182
183 if (y < 0 || y >= CHUNK_SECTION_HEIGHT)
184 return;
185
186 if (z < 0 || z >= CHUNK_SECTION_DEPTH)
187 return;
188
189 section->blocks[x][y][z] = block;
190}
191
192i8 chunk_section_mesh_create(ChunkSection* section, const ChunkSection* neighbours[6])
193{
194 MGVertex* vertices[BLOCK_TEXTURE_COUNT];
195 u16* indices[BLOCK_TEXTURE_COUNT];
196 u32 face_counts[BLOCK_TEXTURE_COUNT];
197 u32 vertex_counts[BLOCK_TEXTURE_COUNT];
198 u32 index_counts[BLOCK_TEXTURE_COUNT];
199 MGMeshDesc desc;
200 BlockType block;
201 BlockType neighbour;
202 const BlockFace* faces;
203 i32 face_count;
204 i32 texture;
205 i32 face;
206 i32 x;
207 i32 y;
208 i32 z;
209
210 if (!section)
211 return 0;
212
213 chunk_section_mesh_destroy(section);
214
215 memset(vertices, 0, sizeof(vertices));
216 memset(indices, 0, sizeof(indices));
217 memset(face_counts, 0, sizeof(face_counts));
218 memset(vertex_counts, 0, sizeof(vertex_counts));
219 memset(index_counts, 0, sizeof(index_counts));
220
221 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++)
222 section->meshes[texture] = MGMESH_INVALID;
223
224 /* count visible faces belonging to each block type */
225 for (z = 0; z < CHUNK_SECTION_DEPTH; z++) {
226 for (y = 0; y < CHUNK_SECTION_HEIGHT; y++) {
227 for (x = 0; x < CHUNK_SECTION_WIDTH; x++) {
228 block = chunk_section_get(section, x, y, z);
229
230 if (block == BLOCK_AIR)
231 continue;
232
233 faces = block_model_faces(block, &face_count);
234 for (face = 0; face < face_count; face++) {
235 if (faces[face].texture < 0)
236 continue;
237 if (faces[face].cullface >= 0) {
238 texture = faces[face].cullface;
239 neighbour = chunk_section_get_neighbour(
240 section, neighbours, x + face_offsets[texture][0],
241 y + face_offsets[texture][1], z + face_offsets[texture][2]);
242 if (neighbour != BLOCK_AIR && block_model_occludes(neighbour))
243 continue;
244 }
245 face_counts[faces[face].texture]++;
246 }
247 }
248 }
249 }
250
251 /* allocate for each block type */
252 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
253 if (face_counts[texture] == 0)
254 continue;
255
256 if (face_counts[texture] * 4U > MAX_MESH_VERTICES) {
257 mg_log(LOG_ERR, "Chunk mesh exceeds u16 vertex limit");
258 goto fail;
259 }
260
261 vertices[texture] = malloc(face_counts[texture] * 4U * sizeof(*vertices[texture]));
262 indices[texture] = malloc(face_counts[texture] * 6U * sizeof(*indices[texture]));
263 if (!vertices[texture] || !indices[texture]) {
264 mg_log(LOG_ERR, "Failed to allocate chunk mesh");
265 goto fail;
266 }
267 }
268
269 /* omit culled model faces hidden by a full cube */
270 for (z = 0; z < CHUNK_SECTION_DEPTH; z++) {
271 for (y = 0; y < CHUNK_SECTION_HEIGHT; y++) {
272 for (x = 0; x < CHUNK_SECTION_WIDTH; x++) {
273 block = chunk_section_get(section, x, y, z);
274
275 if (block == BLOCK_AIR)
276 continue;
277
278 faces = block_model_faces(block, &face_count);
279 for (face = 0; face < face_count; face++) {
280 texture = faces[face].texture;
281 if (texture < 0)
282 continue;
283 if (faces[face].cullface >= 0) {
284 texture = faces[face].cullface;
285 neighbour = chunk_section_get_neighbour(
286 section, neighbours, x + face_offsets[texture][0],
287 y + face_offsets[texture][1], z + face_offsets[texture][2]);
288 if (neighbour != BLOCK_AIR && block_model_occludes(neighbour))
289 continue;
290 }
291
292 texture = faces[face].texture;
293 add_face(vertices[texture], indices[texture], &vertex_counts[texture],
294 &index_counts[texture], &faces[face], x, y, z);
295 }
296 }
297 }
298 }
299
300 /* upload one mesh for each texture */
301 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
302 if (index_counts[texture] == 0)
303 continue;
304
305 desc.vtc = vertices[texture];
306 desc.vtcc = vertex_counts[texture];
307 desc.idc = indices[texture];
308 desc.idcc = index_counts[texture];
309
310 section->meshes[texture] = renderer_mesh_create(&desc);
311
312 if (section->meshes[texture] == MGMESH_INVALID) {
313 mg_log(LOG_ERR, "Failed to upload chunk mesh");
314 goto fail;
315 }
316 }
317
318 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
319 free(indices[texture]);
320 free(vertices[texture]);
321 }
322
323 return 1;
324
325fail:
326 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
327 free(indices[texture]);
328 free(vertices[texture]);
329 }
330
331 chunk_section_mesh_destroy(section);
332 return 0;
333}
334
335void chunk_section_mesh_destroy(ChunkSection* section)
336{
337 i32 texture;
338
339 if (!section)
340 return;
341
342 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
343 renderer_mesh_destroy(section->meshes[texture]);
344 section->meshes[texture] = MGMESH_INVALID;
345 }
346}