map rendering - it actually renders, partially
[voxelands-alt.git] / src / graphics / render_map.c
blob1777f9f77e2e111e039a982344459e3eae983407
1 /************************************************************************
2 * render_map.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 "map.h"
23 #include "list.h"
24 #include "array.h"
25 #include "thread.h"
27 #include <math.h>
29 static struct {
30 array_t *objects;
31 mapobj_t *sorted;
32 shader_t *shader;
33 mutex_t *mutex;
34 float frustum[6][4];
35 matrix_t view;
36 } render_map_data = {
37 NULL,
38 NULL,
39 NULL,
40 NULL,
43 static int render_map_sort(void *e1, void *e2)
45 camera_t *c;
46 mapobj_t *o1;
47 mapobj_t *o2;
48 v3_t cp;
49 v3_t ep;
50 GLfloat d1;
51 GLfloat d2;
52 c = camera_get();
53 cp.x = c->x;
54 cp.y = c->y;
55 cp.z = c->z;
57 o1 = e1;
58 o2 = e2;
60 ep.x = o1->pos.x;
61 ep.y = o1->pos.y;
62 ep.z = o1->pos.z;
63 d1 = math_distance(&cp,&ep);
64 if (d1 < 0)
65 d1 *= -1;
67 /* return -1 if this object is out of visual range */
68 if (d1 > wm_data.distance)
69 return -1;
72 o1->lod = ((d1+8)/16);
73 if (o1->lod > 0) {
74 o1->lod--;
75 if (o1->lod > 6)
76 o1->lod = 6;
77 }else if (o1->lod < 0) {
78 o1->lod = 0;
81 o1->lod = 0;
83 ep.x = o2->pos.x;
84 ep.y = o2->pos.y;
85 ep.z = o2->pos.z;
86 d2 = math_distance(&cp,&ep);
87 if (d2 < 0)
88 d2 *= -1;
90 /* return 1 if e1 is closer to the camera than e2 */
91 if (d1 < d2)
92 return 1;
93 return 0;
96 static void render_map_genvao(mesh_t *m)
98 m->vao.list = 0;
99 if (!m->v->length || !m->i->length)
100 return;
102 glGenVertexArrays(1,&m->vao.list);
103 glBindVertexArray(m->vao.list);
105 glGenBuffers(1, &m->vao.indices);
106 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->vao.indices);
107 glBufferData(GL_ELEMENT_ARRAY_BUFFER, m->i->length*4, m->i->data, GL_STATIC_DRAW);
109 glGenBuffers(1, &m->vao.vertices);
110 glBindBuffer(GL_ARRAY_BUFFER, m->vao.vertices);
111 glBufferData(GL_ARRAY_BUFFER, m->v->length*4, m->v->data, GL_STATIC_DRAW);
112 glEnableVertexAttribArray(0);
113 glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
115 glGenBuffers(1, &m->vao.normals);
116 glBindBuffer(GL_ARRAY_BUFFER, m->vao.normals);
117 glBufferData(GL_ARRAY_BUFFER, m->n->length*4, m->n->data, GL_STATIC_DRAW);
118 glEnableVertexAttribArray(1);
119 glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,0);
121 glGenBuffers(1, &m->vao.texcoords);
122 glBindBuffer(GL_ARRAY_BUFFER, m->vao.texcoords);
123 glBufferData(GL_ARRAY_BUFFER, m->t->length*4, m->t->data, GL_STATIC_DRAW);
124 glEnableVertexAttribArray(2);
125 glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,0,0);
127 m->vao.state = 1;
130 static void render_map_calc_frustum(matrix_t *projection)
132 matrix_t mat;
133 float t;
135 mat = *projection;
136 matrix_multiply(&mat,&render_map_data.view);
138 /* Extract the numbers for the RIGHT plane */
139 render_map_data.frustum[0][0] = mat.data[ 3] - mat.data[ 0];
140 render_map_data.frustum[0][1] = mat.data[ 7] - mat.data[ 4];
141 render_map_data.frustum[0][2] = mat.data[11] - mat.data[ 8];
142 render_map_data.frustum[0][3] = mat.data[15] - mat.data[12];
144 /* Normalize the result */
145 t = sqrt( render_map_data.frustum[0][0] * render_map_data.frustum[0][0] + render_map_data.frustum[0][1] * render_map_data.frustum[0][1] + render_map_data.frustum[0][2] * render_map_data.frustum[0][2] );
146 render_map_data.frustum[0][0] /= t;
147 render_map_data.frustum[0][1] /= t;
148 render_map_data.frustum[0][2] /= t;
149 render_map_data.frustum[0][3] /= t;
151 /* Extract the numbers for the LEFT plane */
152 render_map_data.frustum[1][0] = mat.data[ 3] + mat.data[ 0];
153 render_map_data.frustum[1][1] = mat.data[ 7] + mat.data[ 4];
154 render_map_data.frustum[1][2] = mat.data[11] + mat.data[ 8];
155 render_map_data.frustum[1][3] = mat.data[15] + mat.data[12];
157 /* Normalize the result */
158 t = sqrt( render_map_data.frustum[1][0] * render_map_data.frustum[1][0] + render_map_data.frustum[1][1] * render_map_data.frustum[1][1] + render_map_data.frustum[1][2] * render_map_data.frustum[1][2] );
159 render_map_data.frustum[1][0] /= t;
160 render_map_data.frustum[1][1] /= t;
161 render_map_data.frustum[1][2] /= t;
162 render_map_data.frustum[1][3] /= t;
164 /* Extract the BOTTOM plane */
165 render_map_data.frustum[2][0] = mat.data[ 3] + mat.data[ 1];
166 render_map_data.frustum[2][1] = mat.data[ 7] + mat.data[ 5];
167 render_map_data.frustum[2][2] = mat.data[11] + mat.data[ 9];
168 render_map_data.frustum[2][3] = mat.data[15] + mat.data[13];
170 /* Normalize the result */
171 t = sqrt( render_map_data.frustum[2][0] * render_map_data.frustum[2][0] + render_map_data.frustum[2][1] * render_map_data.frustum[2][1] + render_map_data.frustum[2][2] * render_map_data.frustum[2][2] );
172 render_map_data.frustum[2][0] /= t;
173 render_map_data.frustum[2][1] /= t;
174 render_map_data.frustum[2][2] /= t;
175 render_map_data.frustum[2][3] /= t;
177 /* Extract the TOP plane */
178 render_map_data.frustum[3][0] = mat.data[ 3] - mat.data[ 1];
179 render_map_data.frustum[3][1] = mat.data[ 7] - mat.data[ 5];
180 render_map_data.frustum[3][2] = mat.data[11] - mat.data[ 9];
181 render_map_data.frustum[3][3] = mat.data[15] - mat.data[13];
183 /* Normalize the result */
184 t = sqrt( render_map_data.frustum[3][0] * render_map_data.frustum[3][0] + render_map_data.frustum[3][1] * render_map_data.frustum[3][1] + render_map_data.frustum[3][2] * render_map_data.frustum[3][2] );
185 render_map_data.frustum[3][0] /= t;
186 render_map_data.frustum[3][1] /= t;
187 render_map_data.frustum[3][2] /= t;
188 render_map_data.frustum[3][3] /= t;
190 /* Extract the FAR plane */
191 render_map_data.frustum[4][0] = mat.data[ 3] - mat.data[ 2];
192 render_map_data.frustum[4][1] = mat.data[ 7] - mat.data[ 6];
193 render_map_data.frustum[4][2] = mat.data[11] - mat.data[10];
194 render_map_data.frustum[4][3] = mat.data[15] - mat.data[14];
196 /* Normalize the result */
197 t = sqrt( render_map_data.frustum[4][0] * render_map_data.frustum[4][0] + render_map_data.frustum[4][1] * render_map_data.frustum[4][1] + render_map_data.frustum[4][2] * render_map_data.frustum[4][2] );
198 render_map_data.frustum[4][0] /= t;
199 render_map_data.frustum[4][1] /= t;
200 render_map_data.frustum[4][2] /= t;
201 render_map_data.frustum[4][3] /= t;
203 /* Extract the NEAR plane */
204 render_map_data.frustum[5][0] = mat.data[ 3] + mat.data[ 2];
205 render_map_data.frustum[5][1] = mat.data[ 7] + mat.data[ 6];
206 render_map_data.frustum[5][2] = mat.data[11] + mat.data[10];
207 render_map_data.frustum[5][3] = mat.data[15] + mat.data[14];
209 /* Normalize the result */
210 t = sqrt( render_map_data.frustum[5][0] * render_map_data.frustum[5][0] + render_map_data.frustum[5][1] * render_map_data.frustum[5][1] + render_map_data.frustum[5][2] * render_map_data.frustum[5][2] );
211 render_map_data.frustum[5][0] /= t;
212 render_map_data.frustum[5][1] /= t;
213 render_map_data.frustum[5][2] /= t;
214 render_map_data.frustum[5][3] /= t;
217 int render_map_point_in_frustum(v3_t *p)
219 int i;
220 for (i=0; i<6; i++) {
221 if (((render_map_data.frustum[i][0]*p->x)+(render_map_data.frustum[i][1]*p->y)+(render_map_data.frustum[i][2]*p->z)+render_map_data.frustum[i][3]) <= 0)
222 return 0;
224 return 1;
227 int render_map_bounds_in_frustum(v3_t *p, aabox_t *b)
229 v3_t v[8];
230 int i;
231 int k;
233 if (render_map_point_in_frustum(p))
234 return 1;
236 v[0].x = b->min.x;
237 v[0].y = b->min.y;
238 v[0].z = b->min.z;
240 v[1].x = b->max.x;
241 v[1].y = b->min.y;
242 v[1].z = b->min.z;
244 v[2].x = b->min.x;
245 v[2].y = b->max.y;
246 v[2].z = b->min.z;
248 v[3].x = b->max.x;
249 v[3].y = b->max.y;
250 v[3].z = b->min.z;
252 v[4].x = b->min.x;
253 v[4].y = b->min.y;
254 v[4].z = b->max.z;
256 v[5].x = b->max.x;
257 v[5].y = b->min.y;
258 v[5].z = b->max.z;
260 v[6].x = b->min.x;
261 v[6].y = b->max.y;
262 v[6].z = b->max.z;
264 v[7].x = b->max.x;
265 v[7].y = b->max.y;
266 v[7].z = b->max.z;
268 for (i=0; i<6; i++) {
269 for (k=0; k<8; k++) {
270 if (((
271 render_map_data.frustum[i][0]*v[k].x)
272 +(render_map_data.frustum[i][1]*v[k].y)
273 +(render_map_data.frustum[i][2]*v[k].z)
274 +render_map_data.frustum[i][3]) > 0
276 break;
278 if (k == 8)
279 return 0;
282 return 1;
285 /* render a mesh */
286 mapobj_t *render_map_chunk(mapobj_t *o)
288 if (!render_map_data.objects) {
289 render_map_data.mutex = mutex_create();
290 render_map_data.objects = array_create(ARRAY_TYPE_PTR);
293 mutex_lock(render_map_data.mutex);
294 array_push_ptr(render_map_data.objects,o);
295 mutex_unlock(render_map_data.mutex);
297 return o;
300 /* render 3d graphics to the frame */
301 void render_map(camera_t *cam, v4_t *plane)
303 static v4_t p = {1000000.0,0.0,-1.0,0.0};
304 int i;
305 mapobj_t *o;
306 maplodobj_t *lo;
307 mesh_t *m;
308 matrix_t mat;
309 matrix_t *projection;
310 if (!render_map_data.objects || !render_map_data.objects->length)
311 return;
313 if (!render_map_data.shader) {
314 render_map_data.shader = shader_create("map");
315 shader_attribute(render_map_data.shader,0,"position");
316 shader_attribute(render_map_data.shader,1,"normals");
317 shader_attribute(render_map_data.shader,2,"texcoords");
318 /* possibly a colours attribute as well */
321 shader_enable(render_map_data.shader);
323 camera_view_matrix(&render_map_data.view,cam);
325 projection = render_get_projection_matrix();
327 shader_uniform_matrix(render_map_data.shader,"projectionMatrix",projection);
328 shader_uniform_matrix(render_map_data.shader,"viewMatrix",&render_map_data.view);
330 /* calculate the frustum */
331 render_map_calc_frustum(projection);
333 render_map_data.sorted = NULL;
335 mutex_lock(render_map_data.mutex);
336 for (i=0; i<render_map_data.objects->length; i++) {
337 o = ((mapobj_t**)(render_map_data.objects->data))[i];
338 /* check if we're meant to touch it */
339 if (!o || !o->objs[0].meshes)
340 continue;
341 /*if (!render_map_bounds_in_frustum(&o->pos,&o->bounds))
342 continue;
343 /* sort objects by distance, ignoring anything that can't be seen */
344 render_map_data.sorted = list_insert_cmp(&render_map_data.sorted,o,render_map_sort);
346 mutex_unlock(render_map_data.mutex);
348 o = render_map_data.sorted;
350 /* now render chunks, furthest first */
351 while (o) {
352 matrix_init(&mat);
353 matrix_translate_v(&mat,&o->pos);
354 shader_uniform_matrix(render_map_data.shader,"transformationMatrix",&mat);
356 light_bind_near(render_map_data.shader,&o->pos);
359 lo = &o->objs[o->lod];
361 if (lo && lo->meshes) {
362 for (i=0; i<lo->meshes->length; i++) {
363 m = ((mesh_t**)lo->meshes->data)[i];
364 /* create buffers if necessary and fill with data */
365 if (!m->vao.state)
366 render_map_genvao(m);
368 if (!m->vao.list)
369 continue;
371 glBindVertexArray(m->vao.list);
372 glEnableVertexAttribArray(0);
374 mat_use(m->mat,render_map_data.shader);
376 glEnableVertexAttribArray(1);
377 glEnableVertexAttribArray(2);
378 glDrawElements(m->mode,m->i->length,GL_UNSIGNED_INT,NULL);
382 if (o->lod < 2) {
383 /* TODO: render block objects */
386 o = o->next;
389 glDisableVertexAttribArray(0);
390 glDisableVertexAttribArray(1);
391 glDisableVertexAttribArray(2);
392 glBindVertexArray(0);
394 shader_disable(render_map_data.shader);