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