1 /************************************************************************
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 ************************************************************************/
39 static int render3d_sort(void *e1
, void *e2
)
59 d1
= math_distance(&cp
,&ep
);
66 d2
= math_distance(&cp
,&ep
);
70 /* return 1 if e1 is closer to the camera than e2 */
76 static void render3d_genvao(mesh_t
*m
)
78 if (!m
->v
->length
|| !m
->i
->length
)
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);
113 /* find a 3d object */
114 object_t
*render3d_object_find(int id
)
116 if (render3d_data
.objects
) {
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
)
128 /* destroy a 3d object */
129 void render3d_object_free(object_t
*o
)
132 int i
= array_find_ptr(render3d_data
.objects
,o
);
138 while (o
->ignore
!= 3) {
142 ((unsigned char**)(render3d_data
.objects
->data
))[i
] = NULL
;
145 while ((m
= array_pop_ptr(o
->meshes
))) {
148 array_free(o
->meshes
,1);
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;
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
);
193 object_t
*render3d_model(model_t
*mod
, v3_t
*pos
)
195 object_t
*o
= render3d_object_create();
202 array_free(o
->meshes
,1);
204 o
->meshes
= mod
->meshes
;
211 object_t
*render3d_cube(float scale
, v3_t
*pos
, material_t
*mat
)
281 object_t
*o
= render3d_object_create();
285 m
= mesh_create_material(mat
);
286 for (i
=0; i
<24; i
++) {
287 array_push_v2t(m
->t
,&t
[i
]);
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
);
302 int render3d_point_in_frustum(v3_t
*p
)
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)
312 int render3d_bounds_in_frustum(v3_t
*p
, aabox_t
*b
)
318 if (render3d_point_in_frustum(p
))
353 for (i
=0; i
<6; i
++) {
354 for (k
=0; k
<8; k
++) {
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
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};
378 matrix_t
*projection
;
379 shader_t
*active_shader
;
381 GLuint active_vao
= 0;
382 if (!render3d_data
.objects
|| !render3d_data
.objects
->length
)
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
);
402 glEnable(GL_CLIP_DISTANCE0
);
404 glDisable(GL_CLIP_DISTANCE0
);
407 shader_uniform_v4(active_shader
,"plane",plane
);
409 /* calculate the frustum */
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 */
506 /* call step on each object, this may modify the data (such as with animated models) */
507 if (o
->m
&& o
->m
->step
) {
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
))
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 */
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 */
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);
549 glDisableVertexAttribArray(1);
551 if (m
->vao
.texcoords
) {
552 glEnableVertexAttribArray(2);
554 glDisableVertexAttribArray(2);
557 active_vao
= m
->vao
.list
;
560 mat_use(m
->mat
,active_shader
);
565 glPointSize(m
->option
);
568 glLineWidth(m
->option
);
572 glDrawElements(m
->mode
,m
->i
->length
,GL_UNSIGNED_INT
,NULL
);
583 glDrawElements(m
->mode
,m
->i
->length
,GL_UNSIGNED_INT
,NULL
);
589 glDisableVertexAttribArray(0);
590 glDisableVertexAttribArray(1);
591 glDisableVertexAttribArray(2);
592 glBindVertexArray(0);
594 shader_disable(active_shader
);