]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/model.c
f2a224402ea6c54ac97d7524e67353080991fb8a
[dragonblocks_alpha.git] / src / client / model.c
1 #include <dragonstd/tree.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <pthread.h>
5 #include "client/camera.h"
6 #include "client/client_config.h"
7 #include "client/frustum.h"
8 #include "client/gl_debug.h"
9 #include "client/model.h"
10
11 typedef struct {
12         GLuint texture;
13         Array vertices;
14 } ModelBatchTexture;
15
16 static List scene;
17 static List scene_new;
18 static pthread_mutex_t lock_scene_new;
19 static GLint units;
20
21 static int cmp_batch_texture(const ModelBatchTexture *ta, const ModelBatchTexture *tb)
22 {
23         return
24                 ta->vertices.siz > tb->vertices.siz ? -1 :
25                 ta->vertices.siz < tb->vertices.siz ? +1 :
26                 0;
27 }
28
29 static int cmp_node(const ModelNode *node, const char *str)
30 {
31         if (str == node->name)
32                 return 0;
33
34         if (!node->name)
35                 return -1;
36
37         if (!str)
38                 return +1;
39
40         return strcmp(node->name, str);
41 }
42
43 static void transform_node(ModelNode *node)
44 {
45         if (node->parent)
46                 mat4x4_mul(node->abs, node->parent->abs, node->rel);
47         else
48                 mat4x4_dup(node->abs, node->rel);
49
50         list_itr(&node->children, &transform_node, NULL, NULL);
51 }
52
53 static void render_node(ModelNode *node)
54 {
55         if (!node->visible)
56                 return;
57
58         if (node->clockwise) {
59                 glFrontFace(GL_CW); GL_DEBUG
60         }
61
62         for (size_t i = 0; i < node->meshes.siz; i++) {
63                 ModelMesh *mesh = &((ModelMesh *) node->meshes.ptr)[i];
64
65                 if (!mesh->mesh)
66                         continue;
67
68                 glUseProgram(mesh->shader->prog); GL_DEBUG
69                 glUniformMatrix4fv(mesh->shader->loc_transform, 1, GL_FALSE, node->abs[0]); GL_DEBUG
70
71                 // bind textures
72                 for (GLuint i = 0; i < mesh->num_textures; i++) {
73                         glBindTextureUnit(i, mesh->textures[i]); GL_DEBUG
74                 }
75
76                 mesh_render(mesh->mesh);
77         }
78
79         list_itr(&node->children, &render_node, NULL, NULL);
80
81         if (node->clockwise) {
82                 glFrontFace(GL_CCW); GL_DEBUG
83         }
84 }
85
86 static void free_node_meshes(ModelNode *node)
87 {
88         for (size_t i = 0; i < node->meshes.siz; i++) {
89                 ModelMesh *mesh = &((ModelMesh *) node->meshes.ptr)[i];
90
91                 mesh_destroy(mesh->mesh);
92                 free(mesh->mesh);
93         }
94
95         list_clr(&node->children, &free_node_meshes, NULL, NULL);
96 }
97
98 static void delete_node(ModelNode *node)
99 {
100         for (size_t i = 0; i < node->meshes.siz; i++) {
101                 ModelMesh *mesh = &((ModelMesh *) node->meshes.ptr)[i];
102
103                 if (mesh->textures)
104                         free(mesh->textures);
105         }
106         list_clr(&node->children, &delete_node, NULL, NULL);
107         array_clr(&node->meshes);
108
109         free(node);
110 }
111
112 static void init_node(ModelNode *node, ModelNode *parent)
113 {
114         if ((node->parent = parent))
115                 list_apd(&parent->children, node);
116
117         list_ini(&node->children);
118 }
119
120 static void clone_mesh(ModelMesh *mesh)
121 {
122         GLuint *old_textures = mesh->textures;
123         memcpy(mesh->textures = malloc(mesh->num_textures * sizeof *mesh->textures),
124                 old_textures, mesh->num_textures * sizeof *mesh->textures);
125 }
126
127 static ModelNode *clone_node(ModelNode *original, ModelNode *parent)
128 {
129         ModelNode *node = malloc(sizeof *node);
130         *node = *original;
131         init_node(node, parent);
132
133         array_cln(&node->meshes, &original->meshes);
134         for (size_t i = 0; i < node->meshes.siz; i++)
135                 clone_mesh(&((ModelMesh *) node->meshes.ptr)[i]);
136
137         list_itr(&original->children, &clone_node, node, NULL);
138         return node;
139 }
140
141 static int cmp_model(const Model *model, const f32 *distance)
142 {
143         return -f32_cmp(&model->distance, distance);
144 }
145
146 static void render_model(Model *model)
147 {
148         if (model->callbacks.before_render)
149                 model->callbacks.before_render(model);
150
151         render_node(model->root);
152
153         if (model->callbacks.after_render)
154                 model->callbacks.after_render(model);
155 }
156
157 // step model help im stuck
158 static void model_step(Model *model, Tree *transparent, f64 dtime)
159 {
160         if (model->callbacks.step)
161                 model->callbacks.step(model, dtime);
162
163         if (client_config.view_distance < (model->distance = sqrt(
164                         pow(model->root->pos.x - camera.eye[0], 2) +
165                         pow(model->root->pos.y - camera.eye[1], 2) +
166                         pow(model->root->pos.z - camera.eye[2], 2))))
167                 return;
168
169         if (!model->root->visible)
170                 return;
171
172         if (model->flags.frustum_culling && frustum_cull((aabb3f32) {
173                         v3f32_add(model->box.min, model->root->pos),
174                         v3f32_add(model->box.max, model->root->pos)}))
175                 return;
176
177         // fixme: if there are multiple objects with the exact same distance, only one is rendered
178         if (model->flags.transparent)
179                 tree_add(transparent, &model->distance, model, &cmp_model, NULL);
180         else
181                 render_model(model);
182 }
183
184 // init
185 void model_init()
186 {
187         list_ini(&scene);
188         list_ini(&scene_new);
189
190         pthread_mutex_init(&lock_scene_new, NULL);
191         glGetIntegerv(GL_MAX_TEXTURE_UNITS, &units); GL_DEBUG
192 }
193
194 // ded
195 void model_deinit()
196 {
197         list_clr(&scene, &model_delete, NULL, NULL);
198         list_clr(&scene_new, &model_delete, NULL, NULL);
199
200         pthread_mutex_destroy(&lock_scene_new);
201 }
202
203 // Model functions
204
205 Model *model_create()
206 {
207         Model *model = malloc(sizeof *model);
208         model->root = model_node_create(NULL);
209         model->extra = NULL;
210         model->replace = NULL;
211
212         model->callbacks.step = NULL;
213         model->callbacks.before_render = NULL;
214         model->callbacks.after_render = NULL;
215         model->callbacks.delete = NULL;
216
217         model->flags.delete =
218                 model->flags.frustum_culling =
219                 model->flags.transparent = 0;
220
221         return model;
222 }
223
224 Model *model_load(const char *path, const char *textures_path, Mesh *cube, ModelShader *shader)
225 {
226         Model *model = model_create();
227
228         Array stack;
229         array_ini(&stack, sizeof(ModelNode *), 5);
230         array_apd(&stack, &model->root);
231
232         FILE *file = fopen(path, "r");
233         if (!file) {
234                 fprintf(stderr, "[warning] failed to open model %s\n", path);
235                 return NULL;
236         }
237
238         char *line = NULL;
239         size_t siz = 0;
240         ssize_t length;
241         int count = 0;
242
243         while ((length = getline(&line, &siz, file)) > 0) {
244                 count++;
245
246                 if (*line == '#')
247                         continue;
248
249                 char *cursor = line - 1;
250
251                 size_t tabs = 0;
252                 while (*++cursor == '\t')
253                         tabs++;
254
255                 if (tabs >= stack.siz) {
256                         fprintf(stderr, "[warning] invalid indent in model %s in line %d\n", path, count);
257                         continue;
258                 }
259
260                 ModelNode *node = model_node_create(((ModelNode **) stack.ptr)[tabs]);
261
262                 int n;
263                 char key[length + 1];
264                 while (sscanf(cursor, "%s %n", key, &n) == 1) {
265                         cursor += n;
266
267                         if (strcmp(key, "name") == 0) {
268                                 char name[length + 1];
269
270                                 if (sscanf(cursor, "%s %n", name, &n) == 1) {
271                                         cursor += n;
272
273                                         if (node->name)
274                                                 free(node->name);
275                                         node->name = strdup(name);
276                                 } else {
277                                         fprintf(stderr, "[warning] invalid value for name in model %s in line %d\n", path, count);
278                                 }
279                         } else if (strcmp(key, "pos") == 0) {
280                                 if (sscanf(cursor, "%f %f %f %n", &node->pos.x, &node->pos.y, &node->pos.z, &n) == 3)
281                                         cursor += n;
282                                 else
283                                         fprintf(stderr, "[warning] invalid value for pos in model %s in line %d\n", path, count);
284                         } else if (strcmp(key, "rot") == 0) {
285                                 if (sscanf(cursor, "%f %f %f %n", &node->rot.x, &node->rot.y, &node->rot.z, &n) == 3)
286                                         cursor += n;
287                                 else
288                                         fprintf(stderr, "[warning] invalid value for rot in model %s in line %d\n", path, count);
289
290                                 node->rot = v3f32_scale(node->rot, M_PI / 180.0);
291                         } else if (strcmp(key, "scale") == 0) {
292                                 if (sscanf(cursor, "%f %f %f %n", &node->scale.x, &node->scale.y, &node->scale.z, &n) == 3)
293                                         cursor += n;
294                                 else
295                                         fprintf(stderr, "[warning] invalid value for scale in model %s in line %d\n", path, count);
296                         } else if (strcmp(key, "clockwise") == 0) {
297                                 node->clockwise = 1;
298                         } else if (strcmp(key, "cube") == 0) {
299                                 char texture[length + 1];
300
301                                 if (sscanf(cursor, "%s %n", texture, &n) == 1) {
302                                         cursor += n;
303
304                                         char filepath[strlen(textures_path) + 1 + strlen(texture) + 1];
305                                         sprintf(filepath, "%s/%s", textures_path, texture);
306                                         Texture *texture = texture_load_cubemap(filepath, false);
307
308                                         model_node_add_mesh(node, &(ModelMesh) {
309                                                 .mesh = cube,
310                                                 .textures = &texture->txo,
311                                                 .num_textures = 1,
312                                                 .shader = shader,
313                                         });
314                                 } else {
315                                         fprintf(stderr, "[warning] invalid value for cube in model %s in line %d\n", path, count);
316                                 }
317                         } else {
318                                 fprintf(stderr, "[warning] invalid key '%s' in model %s in line %d\n", key, path, count);
319                         }
320                 }
321
322                 model_node_transform(node);
323
324                 stack.siz = tabs + 1;
325                 array_apd(&stack, &node);
326         }
327
328         if (line)
329                 free(line);
330
331         fclose(file);
332         array_clr(&stack);
333
334         transform_node(model->root);
335         return model;
336 }
337
338 Model *model_clone(Model *original)
339 {
340         Model *model = malloc(sizeof *model);
341         *model = *original;
342         model->root = clone_node(original->root, NULL);
343         return model;
344 }
345
346 void model_delete(Model *model)
347 {
348         if (model->callbacks.delete)
349                 model->callbacks.delete(model);
350
351         delete_node(model->root);
352         free(model);
353 }
354
355 void model_free_meshes(Model *model)
356 {
357         free_node_meshes(model->root);
358 }
359
360 void model_get_bones(Model *model, ModelBoneMapping *mappings, size_t num_mappings)
361 {
362         char *name, *cursor, *saveptr, *tok;
363
364         for (size_t i = 0; i < num_mappings; i++) {
365                 name = cursor = strdup(mappings[i].name);
366
367                 ModelNode *node = model->root;
368
369                 while (node && (tok = strtok_r(cursor, ".", &saveptr))) {
370                         node = list_get(&node->children, tok, &cmp_node, NULL);
371                         cursor = NULL;
372                 }
373
374                 if (node)
375                         *mappings[i].node = node;
376                 else
377                         fprintf(stderr, "[warning] no such bone: %s\n", mappings[i].name);
378
379                 free(name);
380         }
381 }
382
383 // ModelNode functions
384
385 ModelNode *model_node_create(ModelNode *parent)
386 {
387         ModelNode *node = malloc(sizeof *node);
388         node->name = NULL;
389         node->visible = 1;
390         node->clockwise = 0;
391         node->pos = (v3f32) {0.0f, 0.0f, 0.0f};
392         node->rot = (v3f32) {0.0f, 0.0f, 0.0f};
393         node->scale = (v3f32) {1.0f, 1.0f, 1.0f};
394         array_ini(&node->meshes, sizeof(ModelMesh), 0);
395         init_node(node, parent);
396         return node;
397 }
398
399 void model_node_transform(ModelNode *node)
400 {
401         mat4x4_identity(node->rel);
402
403         mat4x4_translate(node->rel,
404                 node->pos.x,
405                 node->pos.y,
406                 node->pos.z);
407
408         mat4x4_scale_aniso(node->rel, node->rel,
409                 node->scale.x,
410                 node->scale.y,
411                 node->scale.z);
412
413         mat4x4_rotate_X(node->rel, node->rel, node->rot.x);
414         mat4x4_rotate_Y(node->rel, node->rel, node->rot.y);
415         mat4x4_rotate_Z(node->rel, node->rel, node->rot.z);
416
417         transform_node(node);
418 }
419
420 void model_node_add_mesh(ModelNode *node, const ModelMesh *mesh)
421 {
422         array_apd(&node->meshes, mesh);
423         clone_mesh(&((ModelMesh *) node->meshes.ptr)[node->meshes.siz - 1]);
424 }
425
426 void model_node_add_batch(ModelNode *node, ModelBatch *batch)
427 {
428         if (!batch->textures.siz) {
429                 free(batch);
430                 return;
431         }
432
433         array_srt(&batch->textures, &cmp_batch_texture);
434         ModelBatchTexture *textures = batch->textures.ptr;
435
436         size_t num_meshes = ceil((double) batch->textures.siz / (double) units);
437         array_grw(&node->meshes, num_meshes);
438         ModelMesh *meshes = &((ModelMesh *) node->meshes.ptr)[node->meshes.siz - num_meshes];
439
440         for (size_t m = 0; m < num_meshes; m++) {
441                 ModelMesh *mesh = &meshes[m];
442
443                 mesh->mesh = malloc(sizeof *mesh->mesh);
444                 mesh->mesh->layout = batch->layout;
445                 mesh->mesh->vao = mesh->mesh->vbo = 0;
446                 mesh->mesh->free_data = true;
447
448                 mesh->textures = malloc(sizeof *mesh->textures * (mesh->num_textures =
449                         ceil((double) (batch->textures.siz - m) / (double) num_meshes)));
450
451                 mesh->shader = batch->shader;
452
453                 mesh->mesh->count = 0;
454                 for (size_t t = 0; t < mesh->num_textures; t++) {
455                         ModelBatchTexture *texture = &textures[m + t * num_meshes];
456                         mesh->mesh->count += texture->vertices.siz;
457                         mesh->textures[t] = texture->texture;
458
459                         for (size_t v = 0; v < texture->vertices.siz; v++)
460                                 *((f32 *) (texture->vertices.ptr + v * texture->vertices.mbs
461                                         + batch->off_tex_idx)) = t;
462                 }
463
464                 ModelBatchTexture *first = &textures[m];
465                 first->vertices.cap = mesh->mesh->count;
466                 first->vertices.ext = 0;
467                 array_rlc(&first->vertices);
468
469                 mesh->mesh->data = first->vertices.ptr;
470
471                 for (size_t t = 1; t < mesh->num_textures; t++) {
472                         ModelBatchTexture *texture = &textures[m + t * num_meshes];
473                         memcpy(first->vertices.ptr + first->vertices.siz * first->vertices.mbs,
474                                 texture->vertices.ptr, texture->vertices.siz * texture->vertices.mbs);
475                         first->vertices.siz += texture->vertices.siz;
476
477                         array_clr(&texture->vertices);
478                 }
479         }
480
481         array_clr(&batch->textures);
482         free(batch);
483 }
484
485 // ModelBatch functions
486
487 ModelBatch *model_batch_create(ModelShader *shader, VertexLayout *layout, size_t off_tex_idx)
488 {
489         ModelBatch *batch = malloc(sizeof *batch);
490         batch->shader = shader;
491         batch->layout = layout;
492         batch->off_tex_idx = off_tex_idx;
493         array_ini(&batch->textures, sizeof(ModelBatchTexture), 5);
494         return batch;
495 }
496
497 void model_batch_free(ModelBatch *batch)
498 {
499         for (size_t i = 0; i < batch->textures.siz; i++)
500                 array_clr(&((ModelBatchTexture *) batch->textures.ptr)[i].vertices);
501
502         array_clr(&batch->textures);
503         free(batch);
504 }
505
506 void model_batch_add_vertex(ModelBatch *batch, GLuint texture, const void *vertex)
507 {
508         ModelBatchTexture *batch_texture = NULL;
509
510         for (size_t i = 0; i <= batch->textures.siz; i++) {
511                 if (i == batch->textures.siz) {
512                         ModelBatchTexture new;
513                         new.texture = texture;
514                         array_ini(&new.vertices, batch->layout->size, 10000);
515                         array_apd(&batch->textures, &new);
516                 }
517
518                 if ((batch_texture = &((ModelBatchTexture *) batch->textures.ptr)[i])->texture == texture)
519                         break;
520         }
521
522         array_apd(&batch_texture->vertices, vertex);
523 }
524
525 // scene functions
526
527 void model_scene_add(Model *model)
528 {
529         pthread_mutex_lock(&lock_scene_new);
530         list_apd(&scene_new, model);
531         pthread_mutex_unlock(&lock_scene_new);
532 }
533
534 void model_scene_render(f64 dtime)
535 {
536         pthread_mutex_lock(&lock_scene_new);
537         if (scene_new.fst) {
538                 *scene.end = scene_new.fst;
539                 scene.end = scene_new.end;
540
541                 list_ini(&scene_new);
542         }
543         pthread_mutex_unlock(&lock_scene_new);
544
545         Tree transparent;
546         tree_ini(&transparent);
547
548         for (ListNode **node = &scene.fst; *node != NULL;) {
549                 Model *model = (*node)->dat;
550
551                 if (model->flags.delete) {
552                         if (model->replace)
553                                 (*node)->dat = model->replace;
554                         else
555                                 list_nrm(&scene, node);
556
557                         model_delete(model);
558                 } else {
559                         node = &(*node)->nxt;
560                         model_step(model, &transparent, dtime);
561                 }
562         }
563
564         tree_clr(&transparent, &render_model, NULL, NULL, TRAVERSION_INORDER);
565 }