some meshgen and map rendering updates
[voxelands-alt.git] / src / graphics / render3d.c
blobcc2e8560b26932640afe6434348e1595745476f0
1 /************************************************************************
2 * render3d.c
3 * voxelands - 3d voxel world sandbox game
4 * Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>
18 ************************************************************************/
20 #include "common.h"
21 #include "graphics.h"
22 #include "list.h"
23 #include "array.h"
25 #include <math.h>
27 static struct {
28 array_t *objects;
29 object_t *sorted;
30 shader_t *shader;
31 float frustum[6][4];
32 matrix_t view;
33 } render3d_data = {
34 NULL,
35 NULL,
36 NULL
39 static int render3d_sort(void *e1, void *e2)
41 camera_t *c;
42 v3_t cp;
43 v3_t ep;
44 object_t *o1;
45 object_t *o2;
46 GLfloat d1;
47 GLfloat d2;
48 c = camera_get();
49 cp.x = c->x;
50 cp.y = c->y;
51 cp.z = c->z;
53 o1 = (object_t*)e1;
54 o2 = (object_t*)e2;
56 ep.x = o1->pos.x;
57 ep.y = o1->pos.y;
58 ep.z = o1->pos.z;
59 d1 = math_distance(&cp,&ep);
60 if (d1 < 0)
61 d1 *= -1;
63 ep.x = o2->pos.x;
64 ep.y = o2->pos.y;
65 ep.z = o2->pos.z;
66 d2 = math_distance(&cp,&ep);
67 if (d2 < 0)
68 d2 *= -1;
70 /* return 1 if e1 is closer to the camera than e2 */
71 if (d1 < d2)
72 return 1;
73 return 0;
76 static void render3d_genvao(mesh_t *m)
78 if (!m->v->length || !m->i->length)
79 return;
81 glGenVertexArrays(1,&m->vao.list);
82 glBindVertexArray(m->vao.list);
84 glGenBuffers(1, &m->vao.indices);
85 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->vao.indices);
86 glBufferData(GL_ELEMENT_ARRAY_BUFFER, m->i->length*4, m->i->data, GL_STATIC_DRAW);
88 glGenBuffers(1, &m->vao.vertices);
89 glBindBuffer(GL_ARRAY_BUFFER, m->vao.vertices);
90 glBufferData(GL_ARRAY_BUFFER, m->v->length*4, m->v->data, GL_STATIC_DRAW);
91 glEnableVertexAttribArray(0);
92 glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
94 if (m->n && m->n->length) {
95 glGenBuffers(1, &m->vao.normals);
96 glBindBuffer(GL_ARRAY_BUFFER, m->vao.normals);
97 glBufferData(GL_ARRAY_BUFFER, m->n->length*4, m->n->data, GL_STATIC_DRAW);
98 glEnableVertexAttribArray(1);
99 glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,0);
102 if (m->t && m->t->length) {
103 glGenBuffers(1, &m->vao.texcoords);
104 glBindBuffer(GL_ARRAY_BUFFER, m->vao.texcoords);
105 glBufferData(GL_ARRAY_BUFFER, m->t->length*4, m->t->data, GL_STATIC_DRAW);
106 glEnableVertexAttribArray(2);
107 glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,0,0);
110 m->vao.state = 1;
113 /* find a 3d object */
114 object_t *render3d_object_find(int id)
116 if (render3d_data.objects) {
117 int i;
118 object_t **o = render3d_data.objects->data;
119 for (i=0; i<render3d_data.objects->length; i++) {
120 if (o[i] && o[i]->id == id)
121 return o[i];
125 return NULL;
128 /* destroy a 3d object */
129 void render3d_object_free(object_t *o)
131 mesh_t *m;
132 int i = array_find_ptr(render3d_data.objects,o);
133 if (i < 0)
134 return;
136 o->ignore = 2;
138 while (o->ignore != 3) {
139 delay(1);
142 ((unsigned char**)(render3d_data.objects->data))[i] = NULL;
144 if (!o->m) {
145 while ((m = array_pop_ptr(o->meshes))) {
146 mesh_free(m);
148 array_free(o->meshes,1);
150 free(o);
153 /* create a new 3d object */
154 object_t *render3d_object_create()
156 static int object_ids = 1;
157 object_t *o = malloc(sizeof(object_t));
159 o->meshes = array_create(ARRAY_TYPE_PTR);
160 o->id = object_ids++;
161 o->anim.skeleton = 0;
162 o->anim.frame = 0;
163 o->anim.value = 0.0;
164 o->ignore = 1;
165 o->drop = 0;
166 o->m = NULL;
167 o->pos.x = 0;
168 o->pos.y = 0;
169 o->pos.z = 0;
170 o->rot.x = 0;
171 o->rot.y = 0;
172 o->rot.z = 0;
173 o->scale.x = 1.0;
174 o->scale.y = 1.0;
175 o->scale.z = 1.0;
177 o->bounds.max.x = 0.0;
178 o->bounds.max.y = 0.0;
179 o->bounds.max.z = 0.0;
180 o->bounds.min.x = 0.0;
181 o->bounds.min.y = 0.0;
182 o->bounds.min.z = 0.0;
184 if (!render3d_data.objects)
185 render3d_data.objects = array_create(ARRAY_TYPE_PTR);
187 array_push_ptr(render3d_data.objects,o);
189 return o;
192 /* render a model */
193 object_t *render3d_model(model_t *mod, v3_t *pos)
195 object_t *o = render3d_object_create();
196 o->pos.x = pos->x;
197 o->pos.y = pos->y;
198 o->pos.z = pos->z;
200 o->m = mod;
202 array_free(o->meshes,1);
204 o->meshes = mod->meshes;
206 o->ignore = 0;
208 return o;
211 object_t *render3d_cube(float scale, v3_t *pos, material_t *mat)
213 v3_t v[24] = {
214 {0.5,0.5,-0.5},
215 {0.5,-0.5,-0.5},
216 {-0.5,-0.5,-0.5},
217 {-0.5,0.5,-0.5},
218 {-0.5,0.5,0.5},
219 {-0.5,-0.5,0.5},
220 {0.5,-0.5,0.5},
221 {0.5,0.5,0.5},
222 {0.5,0.5,0.5},
223 {0.5,-0.5,0.5},
224 {0.5,-0.5,-0.5},
225 {0.5,0.5,-0.5},
226 {-0.5,0.5,-0.5},
227 {-0.5,-0.5,-0.5},
228 {-0.5,-0.5,0.5},
229 {-0.5,0.5,0.5},
230 {0.5,0.5,0.5},
231 {0.5,0.5,-0.5},
232 {-0.5,0.5,-0.5},
233 {-0.5,0.5,0.5},
234 {-0.5,-0.5,0.5},
235 {-0.5,-0.5,-0.5},
236 {0.5,-0.5,-0.5},
237 {0.5,-0.5,0.5}
239 v2_t t[24] = {
240 {0,0},
241 {0,1},
242 {1,1},
243 {1,0},
244 {0,0},
245 {0,1},
246 {1,1},
247 {1,0},
248 {0,0},
249 {0,1},
250 {1,1},
251 {1,0},
252 {0,0},
253 {0,1},
254 {1,1},
255 {1,0},
256 {0,0},
257 {0,1},
258 {1,1},
259 {1,0},
260 {0,0},
261 {0,1},
262 {1,1},
263 {1,0}
265 int indices[36] = {
266 0,1,3,
267 3,1,2,
268 4,5,7,
269 7,5,6,
270 8,9,11,
271 11,9,10,
272 12,13,15,
273 15,13,14,
274 16,17,19,
275 19,17,18,
276 20,21,23,
277 23,21,22
279 int i;
280 mesh_t *m;
281 object_t *o = render3d_object_create();
282 o->pos.x = pos->x;
283 o->pos.y = pos->y;
284 o->pos.z = pos->z;
285 m = mesh_create_material(mat);
286 for (i=0; i<24; i++) {
287 array_push_v2t(m->t,&t[i]);
288 v[i].x *= scale;
289 v[i].y *= scale;
290 v[i].z *= scale;
291 array_push_v3t(m->v,&v[i]);
293 for (i=0; i<36; i++) {
294 array_push_int(m->i,indices[i]);
296 array_push_ptr(o->meshes,m);
297 object_calc_normals(o);
298 o->ignore = 0;
299 return o;
302 int render3d_point_in_frustum(v3_t *p)
304 int i;
305 for (i=0; i<6; i++) {
306 if (((render3d_data.frustum[i][0]*p->x)+(render3d_data.frustum[i][1]*p->y)+(render3d_data.frustum[i][2]*p->z)+render3d_data.frustum[i][3]) <= 0)
307 return 0;
309 return 1;
312 int render3d_bounds_in_frustum(v3_t *p, aabox_t *b)
314 v3_t v[8];
315 int i;
316 int k;
318 if (render3d_point_in_frustum(p))
319 return 1;
321 v[0].x = b->min.x;
322 v[0].y = b->min.y;
323 v[0].z = b->min.z;
325 v[1].x = b->max.x;
326 v[1].y = b->min.y;
327 v[1].z = b->min.z;
329 v[2].x = b->min.x;
330 v[2].y = b->max.y;
331 v[2].z = b->min.z;
333 v[3].x = b->max.x;
334 v[3].y = b->max.y;
335 v[3].z = b->min.z;
337 v[4].x = b->min.x;
338 v[4].y = b->min.y;
339 v[4].z = b->max.z;
341 v[5].x = b->max.x;
342 v[5].y = b->min.y;
343 v[5].z = b->max.z;
345 v[6].x = b->min.x;
346 v[6].y = b->max.y;
347 v[6].z = b->max.z;
349 v[7].x = b->max.x;
350 v[7].y = b->max.y;
351 v[7].z = b->max.z;
353 for (i=0; i<6; i++) {
354 for (k=0; k<8; k++) {
355 if (((
356 render3d_data.frustum[i][0]*v[k].x)
357 +(render3d_data.frustum[i][1]*v[k].y)
358 +(render3d_data.frustum[i][2]*v[k].z)
359 +render3d_data.frustum[i][3]) > 0
361 break;
363 if (k == 8)
364 return 0;
367 return 1;
370 /* render 3d graphics to the frame */
371 void render3d(camera_t *cam, v4_t *plane)
373 static v4_t p = {1000000.0,0.0,-1.0,0.0};
374 int i;
375 object_t *o;
376 mesh_t *m;
377 matrix_t mat;
378 matrix_t *projection;
379 shader_t *active_shader;
380 float t;
381 GLuint active_vao = 0;
382 if (!render3d_data.objects || !render3d_data.objects->length)
383 return;
385 if (!render3d_data.shader) {
386 render3d_data.shader = shader_create("model");
387 shader_attribute(render3d_data.shader,0,"position");
388 shader_attribute(render3d_data.shader,1,"normals");
389 shader_attribute(render3d_data.shader,2,"texcoords");
390 /* possibly a colours attribute as well */
393 shader_enable(render3d_data.shader);
394 active_shader = render3d_data.shader;
396 camera_view_matrix(&render3d_data.view,cam);
397 projection = render_get_projection_matrix();
399 shader_uniform_matrix(active_shader,"projectionMatrix",projection);
400 shader_uniform_matrix(active_shader,"viewMatrix",&render3d_data.view);
401 if (plane) {
402 glEnable(GL_CLIP_DISTANCE0);
403 }else{
404 glDisable(GL_CLIP_DISTANCE0);
405 plane = &p;
407 shader_uniform_v4(active_shader,"plane",plane);
409 /* calculate the frustum */
410 mat = *projection;
411 matrix_multiply(&mat,&render3d_data.view);
413 /* Extract the numbers for the RIGHT plane */
414 render3d_data.frustum[0][0] = mat.data[ 3] - mat.data[ 0];
415 render3d_data.frustum[0][1] = mat.data[ 7] - mat.data[ 4];
416 render3d_data.frustum[0][2] = mat.data[11] - mat.data[ 8];
417 render3d_data.frustum[0][3] = mat.data[15] - mat.data[12];
419 /* Normalize the result */
420 t = sqrt( render3d_data.frustum[0][0] * render3d_data.frustum[0][0] + render3d_data.frustum[0][1] * render3d_data.frustum[0][1] + render3d_data.frustum[0][2] * render3d_data.frustum[0][2] );
421 render3d_data.frustum[0][0] /= t;
422 render3d_data.frustum[0][1] /= t;
423 render3d_data.frustum[0][2] /= t;
424 render3d_data.frustum[0][3] /= t;
426 /* Extract the numbers for the LEFT plane */
427 render3d_data.frustum[1][0] = mat.data[ 3] + mat.data[ 0];
428 render3d_data.frustum[1][1] = mat.data[ 7] + mat.data[ 4];
429 render3d_data.frustum[1][2] = mat.data[11] + mat.data[ 8];
430 render3d_data.frustum[1][3] = mat.data[15] + mat.data[12];
432 /* Normalize the result */
433 t = sqrt( render3d_data.frustum[1][0] * render3d_data.frustum[1][0] + render3d_data.frustum[1][1] * render3d_data.frustum[1][1] + render3d_data.frustum[1][2] * render3d_data.frustum[1][2] );
434 render3d_data.frustum[1][0] /= t;
435 render3d_data.frustum[1][1] /= t;
436 render3d_data.frustum[1][2] /= t;
437 render3d_data.frustum[1][3] /= t;
439 /* Extract the BOTTOM plane */
440 render3d_data.frustum[2][0] = mat.data[ 3] + mat.data[ 1];
441 render3d_data.frustum[2][1] = mat.data[ 7] + mat.data[ 5];
442 render3d_data.frustum[2][2] = mat.data[11] + mat.data[ 9];
443 render3d_data.frustum[2][3] = mat.data[15] + mat.data[13];
445 /* Normalize the result */
446 t = sqrt( render3d_data.frustum[2][0] * render3d_data.frustum[2][0] + render3d_data.frustum[2][1] * render3d_data.frustum[2][1] + render3d_data.frustum[2][2] * render3d_data.frustum[2][2] );
447 render3d_data.frustum[2][0] /= t;
448 render3d_data.frustum[2][1] /= t;
449 render3d_data.frustum[2][2] /= t;
450 render3d_data.frustum[2][3] /= t;
452 /* Extract the TOP plane */
453 render3d_data.frustum[3][0] = mat.data[ 3] - mat.data[ 1];
454 render3d_data.frustum[3][1] = mat.data[ 7] - mat.data[ 5];
455 render3d_data.frustum[3][2] = mat.data[11] - mat.data[ 9];
456 render3d_data.frustum[3][3] = mat.data[15] - mat.data[13];
458 /* Normalize the result */
459 t = sqrt( render3d_data.frustum[3][0] * render3d_data.frustum[3][0] + render3d_data.frustum[3][1] * render3d_data.frustum[3][1] + render3d_data.frustum[3][2] * render3d_data.frustum[3][2] );
460 render3d_data.frustum[3][0] /= t;
461 render3d_data.frustum[3][1] /= t;
462 render3d_data.frustum[3][2] /= t;
463 render3d_data.frustum[3][3] /= t;
465 /* Extract the FAR plane */
466 render3d_data.frustum[4][0] = mat.data[ 3] - mat.data[ 2];
467 render3d_data.frustum[4][1] = mat.data[ 7] - mat.data[ 6];
468 render3d_data.frustum[4][2] = mat.data[11] - mat.data[10];
469 render3d_data.frustum[4][3] = mat.data[15] - mat.data[14];
471 /* Normalize the result */
472 t = sqrt( render3d_data.frustum[4][0] * render3d_data.frustum[4][0] + render3d_data.frustum[4][1] * render3d_data.frustum[4][1] + render3d_data.frustum[4][2] * render3d_data.frustum[4][2] );
473 render3d_data.frustum[4][0] /= t;
474 render3d_data.frustum[4][1] /= t;
475 render3d_data.frustum[4][2] /= t;
476 render3d_data.frustum[4][3] /= t;
478 /* Extract the NEAR plane */
479 render3d_data.frustum[5][0] = mat.data[ 3] + mat.data[ 2];
480 render3d_data.frustum[5][1] = mat.data[ 7] + mat.data[ 6];
481 render3d_data.frustum[5][2] = mat.data[11] + mat.data[10];
482 render3d_data.frustum[5][3] = mat.data[15] + mat.data[14];
484 /* Normalize the result */
485 t = sqrt( render3d_data.frustum[5][0] * render3d_data.frustum[5][0] + render3d_data.frustum[5][1] * render3d_data.frustum[5][1] + render3d_data.frustum[5][2] * render3d_data.frustum[5][2] );
486 render3d_data.frustum[5][0] /= t;
487 render3d_data.frustum[5][1] /= t;
488 render3d_data.frustum[5][2] /= t;
489 render3d_data.frustum[5][3] /= t;
491 render3d_data.sorted = NULL;
493 for (i=0; i<render3d_data.objects->length; i++) {
494 o = ((object_t**)(render3d_data.objects->data))[i];
495 /* check if we're meant to touch it */
496 if (!o)
497 continue;
498 if (o->drop)
499 o->ignore = 2;
500 if (o->ignore) {
501 o->drop = 0;
502 if (o->ignore == 2)
503 o->ignore++;
504 continue;
506 /* call step on each object, this may modify the data (such as with animated models) */
507 if (o->m && o->m->step) {
508 o->m->step(o);
509 object_calc_bounds(o);
510 }else if (o->bounds.min.x == o->bounds.max.x && o->bounds.min.y == o->bounds.max.y && o->bounds.min.z == o->bounds.max.z) {
511 object_calc_bounds(o);
513 /* ignore anything that can't be seen */
514 if (!render3d_bounds_in_frustum(&o->pos,&o->bounds))
515 continue;
517 /* sort objects by distance */
518 render3d_data.sorted = list_insert_cmp(&render3d_data.sorted,o,render3d_sort);
521 o = render3d_data.sorted;
522 /* now render objects, furthest first */
523 while (o) {
524 matrix_init(&mat);
525 matrix_scale_v(&mat,&o->scale);
526 matrix_rotate_deg_z(&mat,o->rot.z);
527 matrix_rotate_deg_y(&mat,o->rot.y);
528 matrix_rotate_deg_x(&mat,o->rot.x);
529 matrix_translate_v(&mat,&o->pos);
530 shader_uniform_matrix(active_shader,"transformationMatrix",&mat);
531 light_bind_near(render3d_data.shader,&o->pos);
532 for (i=0; i<o->meshes->length; i++) {
533 m = ((mesh_t**)o->meshes->data)[i];
534 /* create buffers if necessary and fill with data */
535 if (!m->vao.state)
536 render3d_genvao(m);
538 if (!m->vao.list)
539 continue;
541 if (m->vao.list != active_vao) {
542 glBindVertexArray(m->vao.list);
543 glEnableVertexAttribArray(0);
545 /* use vertex, normal, and texcoord or colour arrays */
546 if (m->vao.normals) {
547 glEnableVertexAttribArray(1);
548 }else{
549 glDisableVertexAttribArray(1);
551 if (m->vao.texcoords) {
552 glEnableVertexAttribArray(2);
553 }else{
554 glDisableVertexAttribArray(2);
557 active_vao = m->vao.list;
560 mat_use(m->mat,active_shader);
562 if (m->option) {
563 switch(m->mode) {
564 case GL_POINTS:
565 glPointSize(m->option);
566 break;
567 case GL_LINES:
568 glLineWidth(m->option);
569 break;
570 default:;
572 glDrawElements(m->mode,m->i->length,GL_UNSIGNED_INT,NULL);
573 switch(m->mode) {
574 case GL_POINTS:
575 glPointSize(1);
576 break;
577 case GL_LINES:
578 glLineWidth(1);
579 break;
580 default:;
582 }else{
583 glDrawElements(m->mode,m->i->length,GL_UNSIGNED_INT,NULL);
586 o = o->next;
589 glDisableVertexAttribArray(0);
590 glDisableVertexAttribArray(1);
591 glDisableVertexAttribArray(2);
592 glBindVertexArray(0);
594 shader_disable(active_shader);