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