f6923a2e6e2e79db838c1597bf359c986d3da2cc
[simavr] / examples / shared / libc3 / srcgl / c3gl.c
1 /*
2         c3gl.c
3
4         Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #if __APPLE__
23 #define GL_GLEXT_PROTOTYPES
24 #include <GLUT/glut.h>
25 #include <OpenGL/gl.h>
26 #include <OpenGL/glext.h>
27 #else
28 #define GL_GLEXT_PROTOTYPES
29 #include <GL/gl.h>
30 #include <GL/glut.h>
31 #include <GL/glext.h>
32 #endif
33 #include <stdio.h>
34
35
36 #include "c3.h"
37 #include "c3lines.h"
38 #include "c3sphere.h"
39 #include "c3light.h"
40 #include "c3program.h"
41
42 #include "c3driver_context.h"
43
44 #include "c3gl.h"
45
46 #define GLCHECK(_w) {_w; dumpError(#_w);}
47
48 static int dumpError(const char * what)
49 {
50         GLenum e;
51         int count = 0;
52         while ((e = glGetError()) != GL_NO_ERROR) {
53                 printf("%s: %s\n", what, gluErrorString(e));
54                 count++;
55         }
56         return count;
57 }
58
59 static void
60 _c3_load_program(
61                 c3program_p p)
62 {
63         if (!p || p->pid || p->log)
64                 return;
65
66         if (p->verbose)
67                 printf("%s loading %s\n", __func__, p->name->str);
68         for (int si = 0; si < p->shaders.count && !p->log; si++) {
69                 c3shader_p s = &p->shaders.e[si];
70
71                 if (p->verbose)
72                         printf("%s compiling shader %s\n", __func__, s->name->str);
73
74                 s->sid = (c3apiobject_t)glCreateShader(s->type);
75                 const GLchar * pgm = s->shader->str;
76                 glShaderSource((GLuint)s->sid, 1, &pgm, NULL);
77
78                 glCompileShader((GLuint)s->sid);
79
80                 GLint status;
81                 glGetShaderiv((GLuint)s->sid, GL_COMPILE_STATUS, &status);
82
83                 if (status != GL_FALSE)
84                         continue;
85
86                 GLint infoLogLength;
87                 glGetShaderiv((GLuint)s->sid, GL_INFO_LOG_LENGTH, &infoLogLength);
88
89                 p->log = str_alloc(infoLogLength);
90                 glGetShaderInfoLog((GLuint)s->sid, infoLogLength, NULL, p->log->str);
91
92                 fprintf(stderr, "%s compile %s: %s\n", __func__, s->name->str, p->log->str);
93                 break;
94         }
95         if (p->log)
96                 return;
97     p->pid = (c3apiobject_t)glCreateProgram();
98
99         for (int si = 0; si < p->shaders.count && !p->log; si++) {
100                 c3shader_p s = &p->shaders.e[si];
101
102         glAttachShader((GLuint)p->pid, (GLuint)s->sid);
103         }
104     glLinkProgram((GLuint)p->pid);
105
106     GLint status;
107     glGetProgramiv((GLuint)p->pid, GL_LINK_STATUS, &status);
108
109         for (int si = 0; si < p->shaders.count && !p->log; si++) {
110                 c3shader_p s = &p->shaders.e[si];
111
112                 glDetachShader((GLuint)p->pid, (GLuint)s->sid);
113                 glDeleteShader((GLuint)s->sid);
114         s->sid = 0;
115         }
116
117     if (status == GL_FALSE) {
118         GLint infoLogLength;
119         glGetProgramiv((GLuint)p->pid, GL_INFO_LOG_LENGTH, &infoLogLength);
120
121                 p->log = str_alloc(infoLogLength);
122
123         glGetProgramInfoLog((GLuint)p->pid, infoLogLength, NULL, p->log->str);
124                 fprintf(stderr, "%s link %s: %s\n", __func__, p->name->str, p->log->str);
125
126                 goto error;
127     }
128     for (int pi = 0; pi < p->params.count; pi++) {
129         c3program_param_p pa = &p->params.e[pi];
130         pa->pid = (c3apiobject_t)glGetUniformLocation((GLuint)p->pid, pa->name->str);
131         if (p->verbose)
132                 printf("%s %s load parameter '%s'\n", __func__, p->name->str, pa->name->str);
133         if (pa->pid == (c3apiobject_t)-1) {
134                 fprintf(stderr, "%s %s: parameter '%s' not found\n",
135                                 __func__, p->name->str, pa->name->str);
136         }
137     }
138
139     c3program_purge(p);
140     return;
141 error:
142         c3program_purge(p);
143         if (p->pid)
144                 glDeleteProgram((GLuint)p->pid);
145         p->pid = 0;
146 }
147
148 static void
149 _c3_load_pixels(
150                 c3pixels_p pix)
151 {
152         GLuint mode = pix->normalize ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_ARB;
153         if (!pix->texture) {
154                 if (pix->trace)
155                         printf("%s Creating texture %s %dx%d\n",
156                                 __func__, pix->name ? pix->name->str : "", pix->w, pix->h);
157                 pix->dirty = 1;
158                 GLuint texID = 0;
159                 GLCHECK(glEnable(mode));
160
161                 glGenTextures(1, &texID);
162 //              glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
163 //                              GL_MODULATE); //set texture environment parameters
164 //              dumpError("glTexEnvf");
165
166                 glPixelStorei(GL_UNPACK_ROW_LENGTH, pix->row / pix->psize);
167                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
168                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_MIN_FILTER,
169                                 pix->normalize ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
170                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER));
171                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER));
172                 if (pix->normalize)
173                         GLCHECK(glTexParameteri(mode, GL_GENERATE_MIPMAP, GL_TRUE));
174         #if 1
175                 GLfloat fLargest;
176                 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
177                 //printf("fLargest = %f\n", fLargest);
178                 GLCHECK(glTexParameterf(mode, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest));
179         #endif
180                 if (pix->normalize)
181                         GLCHECK(glGenerateMipmap(mode));
182
183                 pix->texture = (c3apiobject_t)texID;
184                 pix->dirty = 1;
185         }
186         if (pix->dirty) {
187                 pix->dirty = 0;
188                 GLCHECK(glBindTexture(mode, (GLuint)pix->texture));
189                 glTexImage2D(mode, 0,
190                                 pix->format == C3PIXEL_A ? GL_ALPHA16 : GL_RGBA8,
191                                 pix->w, pix->h, 0,
192                                 pix->format == C3PIXEL_A ? GL_ALPHA : GL_BGRA,
193                                 GL_UNSIGNED_BYTE,
194                                 pix->base);
195                 dumpError("glTexImage2D");
196                 if (pix->normalize)
197                         GLCHECK(glGenerateMipmap(mode));
198         }
199 }
200
201 static void _c3_create_buffer(
202                 GLuint name,
203         GLuint bufferType,
204         void * data,
205         size_t dataSize,
206         GLuint  * out)
207 {
208         GLuint bid;
209         glGenBuffers(1, &bid);
210
211         GLCHECK(glBindBuffer(GL_ARRAY_BUFFER, bid));
212         GLCHECK(glBufferData(GL_ARRAY_BUFFER,
213                         dataSize,
214                 data,
215                 bufferType));
216         GLCHECK(glEnableClientState(name));
217 }
218
219 static void
220 _c3_load_vbo(
221                 c3geometry_p g)
222 {
223         if (!g->vertice.count)
224                 return ;
225         if (!g->bid) {
226                 GLuint  vao;
227                 glGenVertexArrays(1, &vao);
228                 g->bid = (c3apiobject_t)vao;
229         }
230         glBindVertexArray((GLuint)g->bid);
231
232         /*
233          * Use 'realloc' on the array as it frees the data, but leaves 'count'
234          * unchanged. We neec that for the vertices and the indexes, the others
235          * can have a straight free()
236          */
237         if (!g->vertice.buffer.bid && g->vertice.count) {
238                 GLuint bid;
239
240                 _c3_create_buffer(GL_VERTEX_ARRAY,
241                                 g->vertice.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
242                                 g->vertice.e, g->vertice.count * sizeof(g->vertice.e[0]),
243                                 &bid);
244                 glVertexPointer(3, GL_FLOAT, 0, (void*)0);
245                 g->vertice.buffer.bid = (c3apiobject_t)bid;
246                 if (!g->vertice.buffer.mutable)
247                         c3vertex_array_realloc(&g->vertice, 0);
248                 g->vertice.buffer.dirty = 0;
249         }
250         if (!g->textures.buffer.bid && g->textures.count) {
251                 GLuint bid;
252
253                 _c3_create_buffer(GL_TEXTURE_COORD_ARRAY,
254                                 g->textures.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
255                                 g->textures.e, g->textures.count * sizeof(g->textures.e[0]),
256                                 &bid);
257                 glTexCoordPointer(2, GL_FLOAT, 0, (void*)0);
258                 g->textures.buffer.bid = (c3apiobject_t)bid;
259                 if (!g->textures.buffer.mutable)
260                         c3tex_array_free(&g->textures);
261                 g->textures.buffer.dirty = 0;
262         }
263         if (!g->normals.buffer.bid && g->normals.count) {
264                 GLuint bid;
265
266                 _c3_create_buffer(GL_NORMAL_ARRAY,
267                                 g->normals.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
268                                 g->normals.e, g->normals.count * sizeof(g->normals.e[0]),
269                                 &bid);
270                 glNormalPointer(GL_FLOAT, 0, (void*) 0);
271                 g->normals.buffer.bid = (c3apiobject_t)bid;
272                 if (!g->normals.buffer.mutable)
273                         c3vertex_array_free(&g->normals);
274                 g->normals.buffer.dirty = 0;
275         }
276         if (!g->colorf.buffer.bid && g->colorf.count) {
277                 GLuint bid;
278
279                 _c3_create_buffer(GL_COLOR_ARRAY,
280                                 g->colorf.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
281                                 g->colorf.e, g->colorf.count * sizeof(g->colorf.e[0]),
282                                 &bid);
283                 glColorPointer(4, GL_FLOAT, 0, (void*) 0);
284                 g->colorf.buffer.bid = (c3apiobject_t)bid;
285                 if (!g->colorf.buffer.mutable)
286                         c3colorf_array_free(&g->colorf);
287                 g->colorf.buffer.dirty = 0;
288         }
289         if (!g->indices.buffer.bid && g->indices.count) {
290                 GLuint bid;
291                 glGenBuffers(1, &bid);
292
293         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bid);
294         glBufferData(GL_ELEMENT_ARRAY_BUFFER,
295                         g->indices.count * sizeof(g->indices.e[0]),
296                         g->indices.e,
297                         g->indices.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
298                 g->indices.buffer.bid = (c3apiobject_t)bid;
299                 if (!g->indices.buffer.mutable)
300                         c3indices_array_realloc(&g->indices, 0);
301                 g->indices.buffer.dirty = 0;
302         }
303 }
304
305 static void
306 _c3_geometry_project(
307                 c3context_p c,
308                 const struct c3driver_context_t * d,
309                 c3geometry_p g,
310                 c3mat4p m)
311 {
312         if (g->mat.texture)
313                 _c3_load_pixels(g->mat.texture);
314         if (g->mat.program)
315                 _c3_load_program(g->mat.program);
316
317         switch(g->type.type) {
318                 case C3_SPHERE_TYPE:
319                 case C3_TRIANGLE_TYPE:
320                 case C3_LINES_TYPE:
321                         g->type.subtype = (c3apiobject_t)GL_TRIANGLES;
322                         break;
323                 case C3_TEXTURE_TYPE: {
324                         if (g->mat.texture)
325                                 g->type.subtype = (c3apiobject_t)GL_TRIANGLE_FAN;
326                 }       break;
327                 case C3_LIGHT_TYPE: {
328                         c3light_p l = (c3light_p)g;
329                         GLuint lid = GL_LIGHT0 + (int)l->light_id;
330                         if (l->color.specular.w > 0)
331                                 glLightfv(lid, GL_SPECULAR, l->color.specular.n);
332                         if (l->color.ambiant.w > 0)
333                                 glLightfv(lid, GL_AMBIENT, l->color.ambiant.n);
334                 }       break;
335                 default:
336                     break;
337         }
338
339         _c3_load_vbo(g);
340 //      _c3_update_vbo(g);
341
342         glBindVertexArray(0);
343 }
344
345 /*
346  * Thid id the meta function that draws a c3geometry. It looks for normals,
347  * indices, textures and so on and call the glDrawArrays
348  */
349 static void
350 _c3_geometry_draw(
351                 c3context_p c,
352                 const struct c3driver_context_t *d,
353                 c3geometry_p g )
354 {
355         c3mat4 eye = c3mat4_mul(
356                         &g->object->world,
357                         &c3context_view_get(g->object->context)->cam.mtx);
358         glLoadMatrixf(eye.n);
359
360         switch(g->type.type) {
361                 case C3_LIGHT_TYPE: {
362                         c3light_p l = (c3light_p)g;
363                         GLuint lid = GL_LIGHT0 + (int)l->light_id;
364                         glLightfv(lid, GL_POSITION, l->position.n);
365                         if (c3context_view_get(c)->type != C3_CONTEXT_VIEW_LIGHT)
366                                 glEnable(lid);
367                         else
368                                 glDisable(lid);
369                 }       break;
370         }
371         if (!g->bid)
372                 return;
373
374         glColor4fv(g->mat.color.n);
375         dumpError("glColor");
376
377         GLCHECK(glBindVertexArray((GLuint)g->bid));
378
379         glDisable(GL_TEXTURE_2D);
380         if (g->mat.texture) {
381                 GLuint mode = g->mat.texture->normalize ?
382                                 GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_ARB;
383                 glEnable(mode);
384                 if (g->mat.texture->trace)
385                         printf("%s uses texture %s (%d tex)\n",
386                                         __func__, g->mat.texture->name->str, g->textures.count);
387         //      printf("tex mode %d texture %d\n", g->mat.mode, g->mat.texture);
388                 dumpError("glEnable texture");
389                 glBindTexture(mode, (GLuint)g->mat.texture->texture);
390                 dumpError("glBindTexture");
391         }
392         if (g->mat.program)
393                 GLCHECK(glUseProgram((GLuint)g->mat.program->pid));
394
395         if (g->indices.buffer.bid) {
396                 GLCHECK(glDrawElements((GLuint)g->type.subtype,
397                                 g->indices.count, GL_UNSIGNED_SHORT,
398                                 (void*)NULL /*g->indices.e*/));
399         } else {
400                 glDrawArrays((GLuint)g->type.subtype, 0, g->vertice.count);
401         }
402         glBindVertexArray(0);
403
404         if (g->mat.texture)
405                 glDisable(g->mat.texture->normalize ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_ARB);
406         if (g->mat.program)
407                 glUseProgram(0);
408 }
409
410 const c3driver_context_t c3context_driver = {
411                 .geometry_project = _c3_geometry_project,
412                 .geometry_draw = _c3_geometry_draw,
413 };
414
415 const struct c3driver_context_t *
416 c3gl_getdriver()
417 {
418         return &c3context_driver;
419 }