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