1#include <math.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5
6#include "json.h"
7#include "util.h"
8#include "world/block.h"
9
10#define NAME_SIZE 128
11#define PACK_SIZE 256
12#define PATH_SIZE 512
13#define VARIABLE_COUNT 16
14#define MODEL_FACE_COUNT 64
15#define BLOCK_STATE_COUNT 4096
16
17typedef struct {
18 char name[NAME_SIZE];
19 char value[NAME_SIZE];
20} TextureVariable;
21
22typedef struct {
23 MGVertex vertices[4];
24 char texture[NAME_SIZE];
25 i32 cullface;
26} ModelFace;
27
28typedef struct {
29 TextureVariable variables[VARIABLE_COUNT];
30 i32 variable_count;
31 ModelFace faces[MODEL_FACE_COUNT];
32 i32 face_count;
33} BlockModel;
34
35typedef struct {
36 BlockFace* faces;
37 i32 face_count;
38 i8 loaded;
39 i8 occludes;
40} LoadedBlock;
41
42static const char* face_names[BLOCK_FACE_COUNT] = { "north", "south", "west", "east", "down", "up" };
43
44/*its a cube... what more do you want*/
45/* clang-format off */
46static const MGVertex face_vertices[BLOCK_FACE_COUNT][4] = {
47 { { 0, 0, 0, 0, 1, 0xffffffff }, { 0, 1, 0, 0, 0, 0xffffffff },
48 { 1, 1, 0, 1, 0, 0xffffffff }, { 1, 0, 0, 1, 1, 0xffffffff } },
49 { { 1, 0, 1, 0, 1, 0xffffffff }, { 1, 1, 1, 0, 0, 0xffffffff },
50 { 0, 1, 1, 1, 0, 0xffffffff }, { 0, 0, 1, 1, 1, 0xffffffff } },
51 { { 0, 0, 1, 0, 1, 0xffffffff }, { 0, 1, 1, 0, 0, 0xffffffff },
52 { 0, 1, 0, 1, 0, 0xffffffff }, { 0, 0, 0, 1, 1, 0xffffffff } },
53 { { 1, 0, 0, 0, 1, 0xffffffff }, { 1, 1, 0, 0, 0, 0xffffffff },
54 { 1, 1, 1, 1, 0, 0xffffffff }, { 1, 0, 1, 1, 1, 0xffffffff } },
55 { { 0, 0, 1, 0, 1, 0xffffffff }, { 0, 0, 0, 0, 0, 0xffffffff },
56 { 1, 0, 0, 1, 0, 0xffffffff }, { 1, 0, 1, 1, 1, 0xffffffff } },
57 { { 0, 1, 0, 0, 1, 0xffffffff }, { 0, 1, 1, 0, 0, 0xffffffff },
58 { 1, 1, 1, 1, 0, 0xffffffff }, { 1, 1, 0, 1, 1, 0xffffffff } }
59};
60/* clang-format on */
61
62static MGTexture textures[BLOCK_TEXTURE_COUNT];
63static char texture_names[BLOCK_TEXTURE_COUNT][NAME_SIZE];
64static LoadedBlock blocks[BLOCK_STATE_COUNT];
65static char pack_path[PACK_SIZE];
66static i32 texture_count;
67static cJSON* block_registry;
68
69static void block_load(BlockType block);
70static i8 face_covers_cube(const ModelFace* face);
71static float json_number(cJSON* array, i32 index);
72static cJSON* json_load(const char* path);
73static void model_cube(BlockModel* model, const char* texture);
74static void model_face(BlockModel* model, cJSON* from, cJSON* to, cJSON* faces, i32 face);
75static i8 model_load(const char* name, BlockModel* model, i32 depth);
76static const char* model_resolve(const BlockModel* model, const char* name);
77static void model_rotate(BlockModel* model, i32 first, cJSON* rotation);
78static void model_texture(BlockModel* model, const char* name, const char* value);
79static i32 texture_load(const char* name);
80
81/* load block model and textures */
82static void block_load(BlockType block)
83{
84 LoadedBlock* loaded;
85 BlockModel model;
86 cJSON* entry;
87 cJSON* item;
88 const char* name;
89 const char* fallback;
90 i32 face;
91 i32 mask;
92 char path[PATH_SIZE];
93
94 loaded = &blocks[block];
95 loaded->loaded = 1;
96 entry = cJSON_GetArrayItem(block_registry, block >> 4);
97 item = cJSON_GetObjectItemCaseSensitive(entry, "blockState");
98 name = cJSON_IsString(item) ? item->valuestring : NULL;
99 item = cJSON_GetObjectItemCaseSensitive(entry, "texture");
100 fallback = cJSON_IsString(item) ? item->valuestring : NULL;
101 memset(&model, 0, sizeof(model));
102 if (name) {
103 snprintf(path, sizeof(path), "%s/blockstates/%s.json", pack_path, name);
104 item = json_load(path);
105 if (item) {
106 entry = cJSON_GetObjectItemCaseSensitive(item, "variants");
107 if (cJSON_GetArraySize(entry) > (block & 15))
108 entry = cJSON_GetArrayItem(entry, block & 15);
109 else
110 entry = entry ? entry->child : NULL;
111 if (cJSON_IsArray(entry))
112 entry = cJSON_GetArrayItem(entry, 0);
113 entry = cJSON_GetObjectItemCaseSensitive(entry, "model");
114 if (cJSON_IsString(entry))
115 model_load(entry->valuestring, &model, 0);
116 cJSON_Delete(item);
117 }
118 }
119 if (!model.face_count && fallback)
120 model_cube(&model, fallback);
121 if (!model.face_count)
122 return;
123 loaded->faces = malloc(model.face_count * sizeof(*loaded->faces));
124 if (!loaded->faces)
125 return;
126 loaded->face_count = model.face_count;
127 mask = 0;
128 for (face = 0; face < model.face_count; face++) {
129 name = model_resolve(&model, model.faces[face].texture);
130 if (!name)
131 name = fallback;
132 loaded->faces[face].texture = name ? texture_load(name) : -1;
133 loaded->faces[face].cullface = model.faces[face].cullface;
134 memcpy(loaded->faces[face].vertices, model.faces[face].vertices, sizeof(model.faces[face].vertices));
135 if (model.faces[face].cullface >= 0 && face_covers_cube(&model.faces[face]))
136 mask |= 1 << model.faces[face].cullface;
137 }
138 loaded->occludes = mask == (1 << BLOCK_FACE_COUNT) - 1;
139}
140
141/* check whether face covers cube side
142 returns 1 if face covers side, 0 otherwise */
143static i8 face_covers_cube(const ModelFace* face)
144{
145 /* only faces spanning their entire boundary plane can occlude neighbours */
146 static const i32 face_axes[BLOCK_FACE_COUNT] = { 2, 2, 0, 0, 1, 1 };
147 float low[3] = { 1, 1, 1 };
148 float high[3] = { 0, 0, 0 };
149 float value[3];
150 i32 vertex;
151 i32 axis;
152 i32 plane;
153
154 for (vertex = 0; vertex < 4; vertex++) {
155 value[0] = face->vertices[vertex].x;
156 value[1] = face->vertices[vertex].y;
157 value[2] = face->vertices[vertex].z;
158 for (axis = 0; axis < 3; axis++) {
159 if (value[axis] < low[axis])
160 low[axis] = value[axis];
161 if (value[axis] > high[axis])
162 high[axis] = value[axis];
163 }
164 }
165 plane = face_axes[face->cullface];
166 for (axis = 0; axis < 3; axis++) {
167 if (axis != plane && (low[axis] != 0 || high[axis] != 1))
168 return 0;
169 }
170 return face->cullface & 1 ? high[plane] == 1 : low[plane] == 0;
171}
172
173/* load JSON file
174 returns parsed JSON, or NULL on failure */
175static cJSON* json_load(const char* path)
176{
177 FILE* file;
178 char* data;
179 long size;
180 cJSON* json;
181
182 file = fopen(path, "rb");
183 if (!file)
184 return NULL;
185 fseek(file, 0, SEEK_END);
186 size = ftell(file);
187 rewind(file);
188 data = malloc((size_t)size + 1);
189 if (!data || fread(data, 1, (size_t)size, file) != (size_t)size) {
190 free(data);
191 fclose(file);
192 return NULL;
193 }
194 data[size] = '\0';
195 fclose(file);
196 json = cJSON_Parse(data);
197 free(data);
198 return json;
199}
200
201/* read normalized JSON number
202 returns number, or 0 on failure */
203static float json_number(cJSON* array, i32 index)
204{
205 cJSON* item = cJSON_GetArrayItem(array, index);
206 return cJSON_IsNumber(item) ? (float)item->valuedouble / 16.0f : 0.0f;
207}
208
209/* create cube model from texture */
210static void model_cube(BlockModel* model, const char* texture)
211{
212 i32 face;
213
214 for (face = 0; face < BLOCK_FACE_COUNT; face++) {
215 model->faces[face].cullface = face;
216 memcpy(model->faces[face].vertices, face_vertices[face], sizeof(face_vertices[face]));
217 snprintf(model->faces[face].texture, NAME_SIZE, "%s", texture);
218 }
219 model->face_count = BLOCK_FACE_COUNT;
220}
221
222/* convert JSON face element into vertices */
223static void model_face(BlockModel* model, cJSON* from, cJSON* to, cJSON* faces, i32 face)
224{
225 ModelFace* out;
226 cJSON* item;
227 cJSON* uv;
228 float low[3];
229 float high[3];
230 i32 vertex;
231 i32 axis;
232
233 item = cJSON_GetObjectItemCaseSensitive(faces, face_names[face]);
234 if (!cJSON_IsObject(item) || model->face_count == MODEL_FACE_COUNT)
235 return;
236 out = &model->faces[model->face_count++];
237 memset(out, 0, sizeof(*out));
238 out->cullface = -1;
239 for (axis = 0; axis < 3; axis++) {
240 low[axis] = json_number(from, axis);
241 high[axis] = json_number(to, axis);
242 }
243 for (vertex = 0; vertex < 4; vertex++) {
244 out->vertices[vertex] = face_vertices[face][vertex];
245 out->vertices[vertex].x = out->vertices[vertex].x ? high[0] : low[0];
246 out->vertices[vertex].y = out->vertices[vertex].y ? high[1] : low[1];
247 out->vertices[vertex].z = out->vertices[vertex].z ? high[2] : low[2];
248 }
249 uv = cJSON_GetObjectItemCaseSensitive(item, "uv");
250 if (cJSON_IsArray(uv) && cJSON_GetArraySize(uv) == 4) {
251 out->vertices[0].u = out->vertices[1].u = json_number(uv, 0);
252 out->vertices[2].u = out->vertices[3].u = json_number(uv, 2);
253 out->vertices[0].v = out->vertices[3].v = json_number(uv, 3);
254 out->vertices[1].v = out->vertices[2].v = json_number(uv, 1);
255 }
256 uv = cJSON_GetObjectItemCaseSensitive(item, "texture");
257 if (cJSON_IsString(uv))
258 snprintf(out->texture, NAME_SIZE, "%s", uv->valuestring);
259 uv = cJSON_GetObjectItemCaseSensitive(item, "tintindex");
260 if (cJSON_IsNumber(uv)) {
261 /* TODO for now we just tint everything the same green */
262 for (vertex = 0; vertex < 4; vertex++)
263 out->vertices[vertex].col = 0xff59bd91;
264 }
265 uv = cJSON_GetObjectItemCaseSensitive(item, "cullface");
266 if (cJSON_IsString(uv)) {
267 for (axis = 0; axis < BLOCK_FACE_COUNT; axis++) {
268 if (strcmp(uv->valuestring, face_names[axis]) == 0)
269 out->cullface = axis;
270 }
271 }
272}
273
274/* load inherited model data and apply overrides
275 returns 1 on success, 0 on failure */
276static i8 model_load(const char* name, BlockModel* model, i32 depth)
277{
278 char path[PATH_SIZE];
279 cJSON* json;
280 cJSON* item;
281 cJSON* element;
282 cJSON* from;
283 cJSON* to;
284 cJSON* faces;
285 i32 face;
286 i32 first;
287
288 if (depth == 16)
289 return 0;
290 if (strchr(name, ':'))
291 name = strchr(name, ':') + 1;
292 snprintf(path, sizeof(path), "%s/models/%s%s.json", pack_path, strchr(name, '/') ? "" : "block/", name);
293 json = json_load(path);
294 if (!json)
295 return 0;
296 /* load the parent first... */
297 item = cJSON_GetObjectItemCaseSensitive(json, "parent");
298 if (cJSON_IsString(item) && !model_load(item->valuestring, model, depth + 1)) {
299 cJSON_Delete(json);
300 return 0;
301 }
302 item = cJSON_GetObjectItemCaseSensitive(json, "textures");
303 cJSON_ArrayForEach(faces, item)
304 {
305 if (cJSON_IsString(faces))
306 model_texture(model, faces->string, faces->valuestring);
307 }
308 item = cJSON_GetObjectItemCaseSensitive(json, "elements");
309 if (cJSON_IsArray(item)) {
310 /* ... then let the child override*/
311 model->face_count = 0;
312 /* get the from and to values and then turn them into verticies*/
313 /*TODO we dont rotate stairs*/
314 cJSON_ArrayForEach(element, item)
315 {
316 first = model->face_count;
317 from = cJSON_GetObjectItemCaseSensitive(element, "from");
318 to = cJSON_GetObjectItemCaseSensitive(element, "to");
319 faces = cJSON_GetObjectItemCaseSensitive(element, "faces");
320 for (face = 0; face < BLOCK_FACE_COUNT; face++)
321 model_face(model, from, to, faces, face);
322 model_rotate(model, first, cJSON_GetObjectItemCaseSensitive(element, "rotation"));
323 }
324 }
325 cJSON_Delete(json);
326 return 1;
327}
328
329/* resolve model texture variable
330 returns resolved texture name, or NULL on failure */
331static const char* model_resolve(const BlockModel* model, const char* name)
332{
333 i32 depth;
334 i32 i;
335
336 for (depth = 0; name && name[0] == '#' && depth < VARIABLE_COUNT; depth++) {
337 for (i = 0; i < model->variable_count; i++) {
338 if (strcmp(model->variables[i].name, name + 1) == 0)
339 break;
340 }
341 name = i == model->variable_count ? NULL : model->variables[i].value;
342 }
343 return name;
344}
345
346/* rotate model faces from JSON rotation */
347static void model_rotate(BlockModel* model, i32 first, cJSON* rotation)
348{
349 cJSON* origin;
350 cJSON* item;
351 const char* axis;
352 float center[3];
353 float value[3];
354 float angle;
355 float sine;
356 float cosine;
357 float a;
358 float b;
359 i32 face;
360 i32 vertex;
361
362 if (!cJSON_IsObject(rotation))
363 return;
364 origin = cJSON_GetObjectItemCaseSensitive(rotation, "origin");
365 item = cJSON_GetObjectItemCaseSensitive(rotation, "axis");
366 axis = cJSON_IsString(item) ? item->valuestring : NULL;
367 item = cJSON_GetObjectItemCaseSensitive(rotation, "angle");
368 if (!axis || !cJSON_IsNumber(item))
369 return;
370 center[0] = json_number(origin, 0);
371 center[1] = json_number(origin, 1);
372 center[2] = json_number(origin, 2);
373 angle = (float)(item->valuedouble * 3.14159265358979323846 / 180.0);
374 sine = sinf(angle);
375 cosine = cosf(angle);
376 for (face = first; face < model->face_count; face++) {
377 for (vertex = 0; vertex < 4; vertex++) {
378 value[0] = model->faces[face].vertices[vertex].x - center[0];
379 value[1] = model->faces[face].vertices[vertex].y - center[1];
380 value[2] = model->faces[face].vertices[vertex].z - center[2];
381 if (axis[0] == 'x') {
382 a = value[1];
383 b = value[2];
384 value[1] = a * cosine - b * sine;
385 value[2] = a * sine + b * cosine;
386 }
387 else if (axis[0] == 'y') {
388 a = value[2];
389 b = value[0];
390 value[2] = a * cosine - b * sine;
391 value[0] = a * sine + b * cosine;
392 }
393 else {
394 a = value[0];
395 b = value[1];
396 value[0] = a * cosine - b * sine;
397 value[1] = a * sine + b * cosine;
398 }
399 model->faces[face].vertices[vertex].x = value[0] + center[0];
400 model->faces[face].vertices[vertex].y = value[1] + center[1];
401 model->faces[face].vertices[vertex].z = value[2] + center[2];
402 }
403 }
404}
405
406/* add or override model texture */
407static void model_texture(BlockModel* model, const char* name, const char* value)
408{
409 i32 i;
410
411 for (i = 0; i < model->variable_count; i++) {
412 if (strcmp(model->variables[i].name, name) == 0)
413 break;
414 }
415 if (i == VARIABLE_COUNT)
416 return;
417 if (i == model->variable_count)
418 model->variable_count++;
419 snprintf(model->variables[i].name, NAME_SIZE, "%s", name);
420 snprintf(model->variables[i].value, NAME_SIZE, "%s", value);
421}
422
423/* load texture by name
424 returns texture index, or -1 on failure */
425static i32 texture_load(const char* name)
426{
427 char path[PATH_SIZE];
428 const char* colon;
429 i32 index;
430
431 colon = strchr(name, ':');
432 if (colon)
433 name = colon + 1;
434 for (index = 0; index < texture_count; index++) {
435 if (strcmp(texture_names[index], name) == 0)
436 return index;
437 }
438 if (texture_count == BLOCK_TEXTURE_COUNT)
439 return -1;
440 snprintf(path, sizeof(path), "%s/%s.png", pack_path, name);
441 textures[texture_count] = renderer_texture_load(path);
442 if (textures[texture_count] == MGTEXTURE_INVALID)
443 return -1;
444 snprintf(texture_names[texture_count], NAME_SIZE, "%s", name);
445 return texture_count++;
446}
447
448i8 block_textures_create(const char* path)
449{
450 block_textures_destroy();
451 snprintf(pack_path, sizeof(pack_path), "%s", path);
452 block_registry = json_load("assets/resourcepacks/blocks.json");
453 return cJSON_IsArray(block_registry);
454}
455
456void block_textures_destroy(void)
457{
458 i32 index;
459
460 for (index = 0; index < BLOCK_STATE_COUNT; index++)
461 free(blocks[index].faces);
462 for (index = 0; index < texture_count; index++)
463 renderer_texture_destroy(textures[index]);
464 cJSON_Delete(block_registry);
465 memset(blocks, 0, sizeof(blocks));
466 memset(textures, 0, sizeof(textures));
467 memset(texture_names, 0, sizeof(texture_names));
468 block_registry = NULL;
469 pack_path[0] = '\0';
470 texture_count = 0;
471}
472
473const BlockFace* block_model_faces(BlockType block, i32* count)
474{
475 if (!blocks[block].loaded)
476 block_load(block);
477 *count = blocks[block].face_count;
478 return blocks[block].faces;
479}
480
481i8 block_model_occludes(BlockType block)
482{
483 i32 count;
484
485 block_model_faces(block, &count);
486 return blocks[block].occludes;
487}
488
489MGTexture block_texture_get(i32 index)
490{
491 return index >= 0 && index < texture_count ? textures[index] : MGTEXTURE_INVALID;
492}