…aaand add PREFIX to the freebsd makefile too
[voxelands-alt.git] / src / graphics / render3d.c
blobcd5c7b045ad3804a5dcc06254a260a8283a17aca
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 matrix_t projection;
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 GLfloat d1;
45 GLfloat d2;
46 c = camera_get();
47 cp.x = c->x;
48 cp.y = c->y;
49 cp.z = c->z;
51 ep.x = ((object_t*)e1)->pos.x;
52 ep.y = ((object_t*)e1)->pos.y;
53 ep.z = ((object_t*)e1)->pos.z;
54 d1 = math_distance(&cp,&ep);
55 if (d1 < 0)
56 d1 *= -1;
58 /* return -1 if this object is out of visual range */
59 if (d1 > wm_data.distance)
60 return -1;
62 /* TODO: return -1 if this object is completely behind the camera */
64 ep.x = ((object_t*)e2)->pos.x;
65 ep.y = ((object_t*)e2)->pos.y;
66 ep.z = ((object_t*)e2)->pos.z;
67 d2 = math_distance(&cp,&ep);
68 if (d2 < 0)
69 d2 *= -1;
71 /* return 1 if e1 is closer to the camera than e2 */
72 if (d1 < d2)
73 return 1;
74 return 0;
77 static void render3d_step(object_t *o)
80 if (o->m->step(o)) {
81 int i;
82 mesh_t *m;
83 for (i=0; i<o->meshes->length; i++) {
84 m = ((mesh_t**)o->meshes->data)[i];
85 if (m->vbo.state == 1)
86 m->vbo.state = 2;
92 static void render3d_genvao(mesh_t *m)
94 if (!m->v->length || !m->i->length)
95 return;
97 glGenVertexArrays(1,&m->vao.list);
98 glBindVertexArray(m->vao.list);
100 glGenBuffers(1, &m->vao.indices);
101 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->vao.indices);
102 glBufferData(GL_ELEMENT_ARRAY_BUFFER, m->i->length*4, m->i->data, GL_STATIC_DRAW);
104 glGenBuffers(1, &m->vao.vertices);
105 glBindBuffer(GL_ARRAY_BUFFER, m->vao.vertices);
106 glBufferData(GL_ARRAY_BUFFER, m->v->length*4, m->v->data, GL_STATIC_DRAW);
107 glEnableVertexAttribArray(0);
108 glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
110 if (m->n && m->n->length) {
111 glGenBuffers(1, &m->vao.normals);
112 glBindBuffer(GL_ARRAY_BUFFER, m->vao.normals);
113 glBufferData(GL_ARRAY_BUFFER, m->n->length*4, m->n->data, GL_STATIC_DRAW);
114 glEnableVertexAttribArray(1);
115 glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,0);
118 if (m->t && m->t->length) {
119 glGenBuffers(1, &m->vao.texcoords);
120 glBindBuffer(GL_ARRAY_BUFFER, m->vao.texcoords);
121 glBufferData(GL_ARRAY_BUFFER, m->t->length*4, m->t->data, GL_STATIC_DRAW);
122 glEnableVertexAttribArray(2);
123 glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,0,0);
126 m->vao.state = 1;
129 static void render3d_regenvao(mesh_t *m)
133 /* find a 3d object */
134 object_t *render3d_object_find(int id)
136 if (render3d_data.objects) {
137 int i;
138 object_t **o = render3d_data.objects->data;
139 for (i=0; i<render3d_data.objects->length; i++) {
140 if (o[i] && o[i]->id == id)
141 return o[i];
145 return NULL;
148 /* destroy a 3d object */
149 void render3d_object_free(object_t *o)
151 mesh_t *m;
152 int i = array_find_ptr(render3d_data.objects,o);
153 if (i < 0)
154 return;
156 o->ignore = 2;
158 while (o->ignore != 3) {
159 delay(1);
162 ((unsigned char**)(render3d_data.objects->data))[i] = NULL;
164 while ((m = array_pop_ptr(o->meshes))) {
165 mesh_free(m);
167 array_free(o->meshes,1);
168 free(o);
171 /* create a new 3d object */
172 object_t *render3d_object_create()
174 static int object_ids = 1;
175 object_t *o = malloc(sizeof(object_t));
177 o->meshes = array_create(ARRAY_TYPE_PTR);
178 o->id = object_ids++;
179 o->ignore = 1;
180 o->drop = 0;
181 o->m = NULL;
182 o->pos.x = 0;
183 o->pos.y = 0;
184 o->pos.z = 0;
185 o->rot.x = 0;
186 o->rot.y = 0;
187 o->rot.z = 0;
188 o->scale.x = 1.0;
189 o->scale.y = 1.0;
190 o->scale.z = 1.0;
192 if (!render3d_data.objects)
193 render3d_data.objects = array_create(ARRAY_TYPE_PTR);
195 array_push_ptr(render3d_data.objects,o);
197 return o;
200 /* render a model */
201 object_t *render3d_model(model_t *mod, v3_t *pos)
203 int i;
204 mesh_t *m;
205 object_t *o = render3d_object_create();
206 o->pos.x = pos->x;
207 o->pos.y = pos->y;
208 o->pos.z = pos->z;
210 o->m = mod;
212 for (i=0; i<mod->meshes->length; i++) {
213 m = ((mesh_t**)(mod->meshes->data))[i];
214 m = mesh_copy(m);
215 array_push_ptr(o->meshes,m);
218 o->ignore = 0;
220 return o;
223 /* render a mesh */
224 object_t *render3d_mesh(mesh_t *m, v3_t *pos)
226 object_t *o = render3d_object_create();
227 o->pos.x = pos->x;
228 o->pos.y = pos->y;
229 o->pos.z = pos->z;
231 o->ignore = 0;
233 array_push_ptr(o->meshes,m);
235 return o;
238 object_t *render3d_cube(float scale, v3_t *pos, material_t *mat)
240 v3_t v[24] = {
241 {0.5,0.5,-0.5},
242 {0.5,-0.5,-0.5},
243 {-0.5,-0.5,-0.5},
244 {-0.5,0.5,-0.5},
246 {-0.5,0.5,0.5},
247 {-0.5,-0.5,0.5},
248 {0.5,-0.5,0.5},
249 {0.5,0.5,0.5},
251 {0.5,0.5,0.5},
252 {0.5,-0.5,0.5},
253 {0.5,-0.5,-0.5},
254 {0.5,0.5,-0.5},
256 {-0.5,0.5,-0.5},
257 {-0.5,-0.5,-0.5},
258 {-0.5,-0.5,0.5},
259 {-0.5,0.5,0.5},
261 {0.5,0.5,0.5},
262 {0.5,0.5,-0.5},
263 {-0.5,0.5,-0.5},
264 {-0.5,0.5,0.5},
266 {-0.5,-0.5,0.5},
267 {-0.5,-0.5,-0.5},
268 {0.5,-0.5,-0.5},
269 {0.5,-0.5,0.5}
271 v2_t t[24] = {
272 {0,0},
273 {0,1},
274 {1,1},
275 {1,0},
276 {0,0},
277 {0,1},
278 {1,1},
279 {1,0},
280 {0,0},
281 {0,1},
282 {1,1},
283 {1,0},
284 {0,0},
285 {0,1},
286 {1,1},
287 {1,0},
288 {0,0},
289 {0,1},
290 {1,1},
291 {1,0},
292 {0,0},
293 {0,1},
294 {1,1},
295 {1,0}
297 int indices[36] = {
298 0,1,3,
299 3,1,2,
300 4,5,7,
301 7,5,6,
302 8,9,11,
303 11,9,10,
304 12,13,15,
305 15,13,14,
306 16,17,19,
307 19,17,18,
308 20,21,23,
309 23,21,22
311 int i;
312 mesh_t *m;
313 object_t *o = render3d_object_create();
314 o->pos.x = pos->x;
315 o->pos.y = pos->y;
316 o->pos.z = pos->z;
318 m = mesh_create_material(mat);
320 for (i=0; i<24; i++) {
321 array_push_v2t(m->t,&t[i]);
322 v[i].x *= scale;
323 v[i].y *= scale;
324 v[i].z *= scale;
325 array_push_v3t(m->v,&v[i]);
327 for (i=0; i<36; i++) {
328 array_push_int(m->i,indices[i]);
331 array_push_ptr(o->meshes,m);
333 object_calc_normals(o);
335 o->ignore = 0;
337 return o;
340 void render3d_set_projection_matrix()
342 float fov;
343 float near_plane;
344 float far_plane;
345 float ratio;
346 float x;
347 float y;
348 float z;
350 fov = 70.0;
351 near_plane = 0.1;
352 far_plane = 1000.0;
354 fov = math_degrees_to_radians(fov/2.0);
356 ratio = (float)wm_data.size.width/(float)wm_data.size.height;
357 y = (1.0/tan(fov))*ratio;
358 x = y/ratio;
359 z = far_plane-near_plane;
361 matrix_init(&render3d_data.projection);
363 render3d_data.projection.data[0] = x;
364 render3d_data.projection.data[5] = y;
365 render3d_data.projection.data[10] = -((far_plane+near_plane)/z);
366 render3d_data.projection.data[11] = -1;
367 render3d_data.projection.data[14] = -((2.0*near_plane*far_plane)/z);
368 render3d_data.projection.data[15] = 0;
371 /* render 3d graphics to the frame */
372 void render3d()
374 int i;
375 object_t *o;
376 mesh_t *m;
377 matrix_t mat;
378 shader_t *active_shader;
379 if (!render3d_data.objects || !render3d_data.objects->length)
380 return;
382 glEnable(GL_DEPTH_TEST);
383 if (!render3d_data.shader) {
384 render3d_data.shader = shader_create("model");
385 shader_attribute(render3d_data.shader,0,"position");
386 shader_attribute(render3d_data.shader,1,"normals");
387 shader_attribute(render3d_data.shader,2,"texcoords");
388 /* possibly a colours attribute as well */
391 shader_enable(render3d_data.shader);
392 active_shader = render3d_data.shader;
394 camera_view_matrix(&render3d_data.view);
396 shader_uniform_matrix(active_shader,"projectionMatrix",&render3d_data.projection);
397 shader_uniform_matrix(active_shader,"viewMatrix",&render3d_data.view);
399 render3d_data.sorted = NULL;
401 for (i=0; i<render3d_data.objects->length; i++) {
402 o = ((object_t**)(render3d_data.objects->data))[i];
403 /* check if we're meant to touch it */
404 if (!o)
405 continue;
406 if (o->drop)
407 o->ignore = 2;
408 if (o->ignore) {
409 o->drop = 0;
410 if (o->ignore == 2)
411 o->ignore++;
412 continue;
414 /* call step on each object, this may modify the data (such as with animated models) */
415 if (o->m && o->m->step)
416 render3d_step(o);
417 /* sort objects by distance, ignoring anything that can't be seen */
418 render3d_data.sorted = list_insert_cmp(&render3d_data.sorted,o,render3d_sort);
421 o = render3d_data.sorted;
422 /* now render objects, furthest first */
423 while (o) {
424 matrix_init(&mat);
425 matrix_scale_v(&mat,&o->scale);
426 matrix_rotate_deg_z(&mat,o->rot.z);
427 matrix_rotate_deg_y(&mat,o->rot.y);
428 matrix_rotate_deg_x(&mat,o->rot.x);
429 matrix_translate_v(&mat,&o->pos);
430 shader_uniform_matrix(active_shader,"transformationMatrix",&mat);
431 for (i=0; i<o->meshes->length; i++) {
432 m = ((mesh_t**)o->meshes->data)[i];
433 /* create buffers if necessary and fill with data */
434 if (!m->vao.state) {
435 render3d_genvao(m);
436 }else if (m->vao.state == 2) {
437 render3d_regenvao(m);
440 if (!m->vao.list)
441 continue;
443 glBindVertexArray(m->vao.list);
444 glEnableVertexAttribArray(0);
446 mat_use(m->mat,active_shader);
448 /* use vertex, normal, and texcoord or colour arrays */
449 if (m->vao.normals) {
450 glEnableVertexAttribArray(1);
451 }else{
452 glDisableVertexAttribArray(1);
454 if (m->vao.texcoords) {
455 glEnableVertexAttribArray(2);
456 }else{
457 glDisableVertexAttribArray(2);
459 if (m->option) {
460 switch(m->mode) {
461 case GL_POINTS:
462 glPointSize(m->option);
463 break;
464 case GL_LINES:
465 glLineWidth(m->option);
466 break;
467 default:;
469 glDrawElements(m->mode,m->i->length,GL_UNSIGNED_INT,NULL);
470 switch(m->mode) {
471 case GL_POINTS:
472 glPointSize(1);
473 break;
474 case GL_LINES:
475 glLineWidth(1);
476 break;
477 default:;
479 }else{
480 glDrawElements(m->mode,m->i->length,GL_UNSIGNED_INT,NULL);
483 o = o->next;
485 glBindVertexArray(0);
487 shader_disable(active_shader);