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