e9cbc7a6a9ec68fb17ad0f7ebfae12b0841b5224
[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 "c3program.h"
40
41 #include "c3driver_context.h"
42
43 #include "c3gl.h"
44
45 #define GLCHECK(_w) {_w; dumpError(#_w);}
46
47 static int dumpError(const char * what)
48 {
49         GLenum e;
50         int count = 0;
51         while ((e = glGetError()) != GL_NO_ERROR) {
52                 printf("%s: %s\n", what, gluErrorString(e));
53                 count++;
54         }
55         return count;
56 }
57
58 static void
59 _c3_load_program(
60                 c3program_p p)
61 {
62         if (!p || p->pid || p->log)
63                 return;
64
65         if (p->verbose)
66                 printf("%s loading %s\n", __func__, p->name->str);
67         for (int si = 0; si < p->shaders.count && !p->log; si++) {
68                 c3shader_p s = &p->shaders.e[si];
69
70                 if (p->verbose)
71                         printf("%s compiling shader %s\n", __func__, s->name->str);
72
73                 s->sid = (c3apiobject_t)glCreateShader(s->type);
74                 const GLchar * pgm = s->shader->str;
75                 glShaderSource((GLuint)s->sid, 1, &pgm, NULL);
76
77                 glCompileShader((GLuint)s->sid);
78
79                 GLint status;
80                 glGetShaderiv((GLuint)s->sid, GL_COMPILE_STATUS, &status);
81
82                 if (status != GL_FALSE)
83                         continue;
84
85                 GLint infoLogLength;
86                 glGetShaderiv((GLuint)s->sid, GL_INFO_LOG_LENGTH, &infoLogLength);
87
88                 p->log = str_alloc(infoLogLength);
89                 glGetShaderInfoLog((GLuint)s->sid, infoLogLength, NULL, p->log->str);
90
91                 fprintf(stderr, "%s compile %s: %s\n", __func__, s->name->str, p->log->str);
92                 break;
93         }
94         if (p->log)
95                 return;
96     p->pid = (c3apiobject_t)glCreateProgram();
97
98         for (int si = 0; si < p->shaders.count && !p->log; si++) {
99                 c3shader_p s = &p->shaders.e[si];
100
101         glAttachShader((GLuint)p->pid, (GLuint)s->sid);
102         }
103     glLinkProgram((GLuint)p->pid);
104
105     GLint status;
106     glGetProgramiv((GLuint)p->pid, GL_LINK_STATUS, &status);
107
108         for (int si = 0; si < p->shaders.count && !p->log; si++) {
109                 c3shader_p s = &p->shaders.e[si];
110
111                 glDetachShader((GLuint)p->pid, (GLuint)s->sid);
112                 glDeleteShader((GLuint)s->sid);
113         s->sid = 0;
114         }
115
116     if (status == GL_FALSE) {
117         GLint infoLogLength;
118         glGetProgramiv((GLuint)p->pid, GL_INFO_LOG_LENGTH, &infoLogLength);
119
120                 p->log = str_alloc(infoLogLength);
121
122         glGetProgramInfoLog((GLuint)p->pid, infoLogLength, NULL, p->log->str);
123                 fprintf(stderr, "%s link %s: %s\n", __func__, p->name->str, p->log->str);
124
125                 goto error;
126     }
127     for (int pi = 0; pi < p->params.count; pi++) {
128         c3program_param_p pa = &p->params.e[pi];
129         pa->pid = (c3apiobject_t)glGetUniformLocation((GLuint)p->pid, pa->name->str);
130         if (p->verbose)
131                 printf("%s %s load parameter '%s'\n", __func__, p->name->str, pa->name->str);
132         if (pa->pid == (c3apiobject_t)-1) {
133                 fprintf(stderr, "%s %s: parameter '%s' not found\n",
134                                 __func__, p->name->str, pa->name->str);
135         }
136     }
137
138     c3program_purge(p);
139     return;
140 error:
141         c3program_purge(p);
142         if (p->pid)
143                 glDeleteProgram((GLuint)p->pid);
144         p->pid = 0;
145 }
146
147 static void
148 _c3_load_pixels(
149                 c3pixels_p pix)
150 {
151         GLuint mode = pix->normalize ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_ARB;
152         if (!pix->texture) {
153                 if (pix->trace)
154                         printf("%s Creating texture %s %dx%d\n",
155                                 __func__, pix->name ? pix->name->str : "", pix->w, pix->h);
156                 pix->dirty = 1;
157                 GLuint texID = 0;
158                 GLCHECK(glEnable(mode));
159
160                 glGenTextures(1, &texID);
161 //              glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
162 //                              GL_MODULATE); //set texture environment parameters
163 //              dumpError("glTexEnvf");
164
165                 glPixelStorei(GL_UNPACK_ROW_LENGTH, pix->row / pix->psize);
166                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
167                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_MIN_FILTER,
168                                 pix->normalize ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
169                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER));
170                 GLCHECK(glTexParameteri(mode, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER));
171                 if (pix->normalize)
172                         GLCHECK(glTexParameteri(mode, GL_GENERATE_MIPMAP, GL_TRUE));
173         #if 1
174                 GLfloat fLargest;
175                 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
176                 //printf("fLargest = %f\n", fLargest);
177                 GLCHECK(glTexParameterf(mode, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest));
178         #endif
179                 if (pix->normalize)
180                         GLCHECK(glGenerateMipmap(mode));
181
182                 pix->texture = (c3apiobject_t)texID;
183                 pix->dirty = 1;
184         }
185         if (pix->dirty) {
186                 pix->dirty = 0;
187                 GLCHECK(glBindTexture(mode, (GLuint)pix->texture));
188                 glTexImage2D(mode, 0,
189                                 pix->format == C3PIXEL_A ? GL_ALPHA16 : GL_RGBA8,
190                                 pix->w, pix->h, 0,
191                                 pix->format == C3PIXEL_A ? GL_ALPHA : GL_BGRA,
192                                 GL_UNSIGNED_BYTE,
193                                 pix->base);
194                 dumpError("glTexImage2D");
195                 if (pix->normalize)
196                         GLCHECK(glGenerateMipmap(mode));
197         }
198 }
199
200 static void _c3_create_buffer(
201                 GLuint name,
202         GLuint bufferType,
203         void * data,
204         size_t dataSize,
205         GLuint  * out)
206 {
207         GLuint bid;
208         glGenBuffers(1, &bid);
209
210         GLCHECK(glBindBuffer(GL_ARRAY_BUFFER, bid));
211         GLCHECK(glBufferData(GL_ARRAY_BUFFER,
212                         dataSize,
213                 data,
214                 bufferType));
215         GLCHECK(glEnableClientState(name));
216 }
217
218 static void
219 _c3_load_vbo(
220                 c3geometry_p g)
221 {
222         if (!g->bid) {
223                 GLuint  vao;
224                 glGenVertexArrays(1, &vao);
225                 g->bid = (c3apiobject_t)vao;
226         }
227         glBindVertexArray((GLuint)g->bid);
228
229         /*
230          * Use 'realloc' on the array as it frees the data, but leaves 'count'
231          * unchanged. We neec that for the vertices and the indexes, the others
232          * can have a straight free()
233          */
234         if (!g->vertice.buffer.bid && g->vertice.count) {
235                 GLuint bid;
236
237                 _c3_create_buffer(GL_VERTEX_ARRAY,
238                                 g->vertice.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
239                                 g->vertice.e, g->vertice.count * sizeof(g->vertice.e[0]),
240                                 &bid);
241                 glVertexPointer(3, GL_FLOAT, 0, (void*)0);
242                 g->vertice.buffer.bid = (c3apiobject_t)bid;
243                 if (!g->vertice.buffer.mutable)
244                         c3vertex_array_realloc(&g->vertice, 0);
245                 g->vertice.buffer.dirty = 0;
246         }
247         if (!g->textures.buffer.bid && g->textures.count) {
248                 GLuint bid;
249
250                 _c3_create_buffer(GL_TEXTURE_COORD_ARRAY,
251                                 g->textures.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
252                                 g->textures.e, g->textures.count * sizeof(g->textures.e[0]),
253                                 &bid);
254                 glTexCoordPointer(2, GL_FLOAT, 0, (void*)0);
255                 g->textures.buffer.bid = (c3apiobject_t)bid;
256                 if (!g->textures.buffer.mutable)
257                         c3tex_array_free(&g->textures);
258                 g->textures.buffer.dirty = 0;
259         }
260         if (!g->normals.buffer.bid && g->normals.count) {
261                 GLuint bid;
262
263                 _c3_create_buffer(GL_NORMAL_ARRAY,
264                                 g->normals.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
265                                 g->normals.e, g->normals.count * sizeof(g->normals.e[0]),
266                                 &bid);
267                 glNormalPointer(GL_FLOAT, 0, (void*) 0);
268                 g->normals.buffer.bid = (c3apiobject_t)bid;
269                 if (!g->normals.buffer.mutable)
270                         c3vertex_array_free(&g->normals);
271                 g->normals.buffer.dirty = 0;
272         }
273         if (!g->colorf.buffer.bid && g->colorf.count) {
274                 GLuint bid;
275
276                 _c3_create_buffer(GL_COLOR_ARRAY,
277                                 g->colorf.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW,
278                                 g->colorf.e, g->colorf.count * sizeof(g->colorf.e[0]),
279                                 &bid);
280                 glColorPointer(4, GL_FLOAT, 0, (void*) 0);
281                 g->colorf.buffer.bid = (c3apiobject_t)bid;
282                 if (!g->colorf.buffer.mutable)
283                         c3colorf_array_free(&g->colorf);
284                 g->colorf.buffer.dirty = 0;
285         }
286         if (!g->indices.buffer.bid && g->indices.count) {
287                 GLuint bid;
288                 glGenBuffers(1, &bid);
289
290         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bid);
291         glBufferData(GL_ELEMENT_ARRAY_BUFFER,
292                         g->indices.count * sizeof(g->indices.e[0]),
293                         g->indices.e,
294                         g->indices.buffer.mutable ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
295                 g->indices.buffer.bid = (c3apiobject_t)bid;
296                 if (!g->indices.buffer.mutable)
297                         c3indices_array_realloc(&g->indices, 0);
298                 g->indices.buffer.dirty = 0;
299         }
300 }
301
302 static void
303 _c3_geometry_project(
304                 c3context_p c,
305                 const struct c3driver_context_t * d,
306                 c3geometry_p g,
307                 c3mat4p m)
308 {
309         if (g->mat.texture)
310                 _c3_load_pixels(g->mat.texture);
311         if (g->mat.program)
312                 _c3_load_program(g->mat.program);
313
314         switch(g->type.type) {
315                 case C3_SPHERE_TYPE:
316                 case C3_TRIANGLE_TYPE:
317                 case C3_LINES_TYPE:
318                         g->type.subtype = (c3apiobject_t)GL_TRIANGLES;
319                         break;
320                 case C3_TEXTURE_TYPE: {
321                         if (g->mat.texture)
322                                 g->type.subtype = (c3apiobject_t)GL_TRIANGLE_FAN;
323                 }       break;
324                 default:
325                     break;
326         }
327
328         _c3_load_vbo(g);
329 //      _c3_update_vbo(g);
330
331         glBindVertexArray(0);
332 }
333
334 /*
335  * Thid id the meta function that draws a c3geometry. It looks for normals,
336  * indices, textures and so on and call the glDrawArrays
337  */
338 static void
339 _c3_geometry_draw(
340                 c3context_p c,
341                 const struct c3driver_context_t *d,
342                 c3geometry_p g )
343 {
344         glColor4fv(g->mat.color.n);
345         dumpError("glColor");
346 //      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->mat.color.n);
347
348         c3mat4 eye = c3mat4_mul(
349                         &g->object->world,
350                         &c3context_view_get(g->object->context)->cam.mtx);
351         glLoadMatrixf(eye.n);
352
353         GLCHECK(glBindVertexArray((GLuint)g->bid));
354
355         glDisable(GL_TEXTURE_2D);
356         if (g->mat.texture) {
357                 GLuint mode = g->mat.texture->normalize ?
358                                 GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_ARB;
359                 glEnable(mode);
360                 if (g->mat.texture->trace)
361                         printf("%s uses texture %s (%d tex)\n",
362                                         __func__, g->mat.texture->name->str, g->textures.count);
363         //      printf("tex mode %d texture %d\n", g->mat.mode, g->mat.texture);
364                 dumpError("glEnable texture");
365                 glBindTexture(mode, (GLuint)g->mat.texture->texture);
366                 dumpError("glBindTexture");
367         }
368         if (g->mat.program)
369                 GLCHECK(glUseProgram((GLuint)g->mat.program->pid));
370
371         if (g->indices.buffer.bid) {
372                 GLCHECK(glDrawElements((GLuint)g->type.subtype,
373                                 g->indices.count, GL_UNSIGNED_SHORT,
374                                 (void*)NULL /*g->indices.e*/));
375         } else {
376                 glDrawArrays((GLuint)g->type.subtype, 0, g->vertice.count);
377         }
378         glBindVertexArray(0);
379
380         if (g->mat.texture)
381                 glDisable(g->mat.texture->normalize ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_ARB);
382         if (g->mat.program)
383                 glUseProgram(0);
384 }
385
386 const c3driver_context_t c3context_driver = {
387                 .geometry_project = _c3_geometry_project,
388                 .geometry_draw = _c3_geometry_draw,
389 };
390
391 const struct c3driver_context_t *
392 c3gl_getdriver()
393 {
394         return &c3context_driver;
395 }