Added README
[gl4] / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <stddef.h>
5 #include <assert.h>
6 #include <GL/freeglut.h>
7 #include <GL/glext.h>
8 #include <GL/glx.h>
9 #include "matrix.h"
10
11 #define SPIRV
12
13 enum {
14         UBLOCK_MATRIX,
15 };
16
17 enum {
18         VATTR_VERTEX,
19         VATTR_NORMAL,
20         VATTR_TEXCOORD
21 };
22
23 struct vertex {
24         float x, y, z;
25         float nx, ny, nz;
26         float tu, tv;
27 };
28
29 struct mesh {
30         struct vertex *varr;
31         unsigned int *iarr;
32         int vcount, icount;
33
34         unsigned int vbo, ibo, vao;
35 };
36
37 struct matrix_state {
38         float view_mat[16];
39         float proj_mat[16];
40         float mvmat[16];
41         float mvpmat[16];
42         float lpos[3];
43 } __attribute__((packed));
44
45 unsigned int tex;
46
47 int init(void);
48 void cleanup(void);
49 void display(void);
50 void reshape(int x, int y);
51 void keypress(unsigned char key, int x, int y);
52 void mouse(int bn, int st, int x, int y);
53 void motion(int x, int y);
54
55 int gen_torus(struct mesh *mesh, float rad, float rrad, int usub, int vsub);
56 void draw_mesh(struct mesh *mesh);
57 unsigned int gen_texture(int width, int height);
58
59 unsigned int load_shader(const char *fname, int type);
60 unsigned int load_program(const char *vfname, const char *pfname);
61 int link_program(unsigned int prog);
62
63 void GLAPIENTRY gldebug(GLenum src, GLenum type, GLuint id, GLenum severity,
64                 GLsizei len, const char *msg, const void *cls);
65
66 float cam_theta, cam_phi = 25, cam_dist = 4;
67 int prev_x, prev_y, bnstate[8];
68
69 struct mesh torus;
70
71 unsigned int sdr;
72
73 struct matrix_state matrix_state;
74
75 unsigned int ubo_matrix;
76
77 static PFNGLSPECIALIZESHADERPROC gl_specialize_shader;
78
79 int main(int argc, char **argv)
80 {
81         glutInit(&argc, argv);
82         glutInitWindowSize(800, 600);
83         glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
84         glutInitContextProfile(GLUT_CORE_PROFILE);
85         glutInitContextVersion(4, 4);
86         glutCreateWindow("GL4 test");
87
88         glutDisplayFunc(display);
89         glutReshapeFunc(reshape);
90         glutKeyboardFunc(keypress);
91         glutMouseFunc(mouse);
92         glutMotionFunc(motion);
93
94         if(init() == -1) {
95                 return 1;
96         }
97         atexit(cleanup);
98
99         glutMainLoop();
100         return 0;
101 }
102
103 int init(void)
104 {
105         glDebugMessageCallback(gldebug, 0);
106         glEnable(GL_DEPTH_TEST);
107         glEnable(GL_CULL_FACE);
108
109         gl_specialize_shader = (PFNGLSPECIALIZESHADERPROC)glXGetProcAddress((unsigned char*)"glSpecializeShaderARB");
110         if(!gl_specialize_shader) {
111                 fprintf(stderr, "failed to load glSpecializeShaderARB entry point\n");
112                 return -1;
113         }
114
115         if(!(tex = gen_texture(256, 256))) {
116                 return -1;
117         }
118
119         if(gen_torus(&torus, 1.0, 0.25, 32, 12) == -1) {
120                 return -1;
121         }
122
123 #ifndef SPIRV
124         if(!(sdr = load_program("vertex.glsl", "pixel.glsl"))) {
125                 return -1;
126         }
127 #else
128         if(!(sdr = load_program("spirv/vertex.spv", "spirv/pixel.spv"))) {
129                 return -1;
130         }
131 #endif
132
133         glUseProgram(sdr);
134
135         glGenBuffers(1, &ubo_matrix);
136         glBindBuffer(GL_UNIFORM_BUFFER, ubo_matrix);
137         glBufferData(GL_UNIFORM_BUFFER, sizeof matrix_state, &matrix_state, GL_STREAM_DRAW);
138
139         return 0;
140 }
141
142 void cleanup(void)
143 {
144         free(torus.varr);
145         free(torus.iarr);
146         if(torus.vbo) {
147                 glDeleteBuffers(1, &torus.vbo);
148                 glDeleteBuffers(1, &torus.ibo);
149         }
150         if(torus.vao) {
151                 glDeleteVertexArrays(1, &torus.vao);
152         }
153         glDeleteTextures(1, &tex);
154         glDeleteBuffers(1, &ubo_matrix);
155 }
156
157 void display(void)
158 {
159         matrix_state.lpos[0] = -10;
160         matrix_state.lpos[1] = 10;
161         matrix_state.lpos[2] = 10;
162
163         glClearColor(0.05, 0.05, 0.05, 1.0);
164         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
165
166         mat_identity(matrix_state.view_mat);
167         mat_translate(matrix_state.view_mat, 0, 0, -cam_dist);
168         mat_rotate(matrix_state.view_mat, cam_phi, 1, 0, 0);
169         mat_rotate(matrix_state.view_mat, cam_theta, 0, 1, 0);
170
171         mat_copy(matrix_state.mvmat, matrix_state.view_mat);
172
173         mat_copy(matrix_state.mvpmat, matrix_state.proj_mat);
174         mat_mul(matrix_state.mvpmat, matrix_state.mvmat);
175
176         mat_transform(matrix_state.view_mat, matrix_state.lpos);
177
178         glUseProgram(sdr);
179
180         glBindBuffer(GL_UNIFORM_BUFFER, ubo_matrix);
181         glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof matrix_state, &matrix_state);
182         glBindBufferBase(GL_UNIFORM_BUFFER, UBLOCK_MATRIX, ubo_matrix);
183
184         glBindTexture(GL_TEXTURE_2D, tex);
185         draw_mesh(&torus);
186
187         assert(glGetError() == GL_NO_ERROR);
188         glutSwapBuffers();
189 }
190
191 void reshape(int x, int y)
192 {
193         glViewport(0, 0, x, y);
194
195         mat_identity(matrix_state.proj_mat);
196         mat_perspective(matrix_state.proj_mat, 50.0, (float)x / (float)y, 0.5, 500.0);
197 }
198
199 void keypress(unsigned char key, int x, int y)
200 {
201         switch(key) {
202         case 27:
203                 exit(0);
204         }
205 }
206
207 void mouse(int bn, int st, int x, int y)
208 {
209         bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN ? 1 : 0;
210         prev_x = x;
211         prev_y = y;
212 }
213
214 void motion(int x, int y)
215 {
216         int dx = x - prev_x;
217         int dy = y - prev_y;
218         prev_x = x;
219         prev_y = y;
220
221         if(!dx && !dy) return;
222
223         if(bnstate[0]) {
224                 cam_theta += dx * 0.5;
225                 cam_phi += dy * 0.5;
226                 if(cam_phi < -90) cam_phi = -90;
227                 if(cam_phi > 90) cam_phi = 90;
228                 glutPostRedisplay();
229         }
230         if(bnstate[2]) {
231                 cam_dist += dy * 0.1;
232                 if(cam_dist < 0.0) cam_dist = 0.0;
233                 glutPostRedisplay();
234         }
235 }
236
237 static void torus_vertex(struct vertex *vout, float rad, float rrad, float u, float v)
238 {
239         float theta = u * M_PI * 2.0;
240         float phi = v * M_PI * 2.0;
241         float rx, ry, rz, cx, cy, cz;
242
243         cx = sin(theta) * rad;
244         cy = 0.0;
245         cz = -cos(theta) * rad;
246
247         rx = -cos(phi) * rrad + rad;
248         ry = sin(phi) * rrad;
249         rz = 0.0;
250
251         vout->x = rx * sin(theta) + rz * cos(theta);
252         vout->y = ry;
253         vout->z = -rx * cos(theta) + rz * sin(theta);
254
255         vout->nx = (vout->x - cx) / rrad;
256         vout->ny = (vout->y - cy) / rrad;
257         vout->nz = (vout->z - cz) / rrad;
258
259         vout->tu = u;
260         vout->tv = v;
261 }
262
263
264 int gen_torus(struct mesh *mesh, float rad, float rrad, int usub, int vsub)
265 {
266         int i, j, uverts, vverts, nverts, nquads, ntri;
267         float u, v;
268         float du = 1.0 / (float)usub;
269         float dv = 1.0 / (float)vsub;
270         struct vertex *vptr;
271         unsigned int *iptr;
272
273         if(usub < 3) usub = 3;
274         if(vsub < 3) vsub = 3;
275
276         uverts = usub + 1;
277         vverts = vsub + 1;
278
279         nverts = uverts * vverts;
280         nquads = usub * vsub;
281         ntri = nquads * 2;
282
283         mesh->vcount = nverts;
284         mesh->icount = ntri * 3;
285
286         if(!(mesh->varr = malloc(mesh->vcount * sizeof *mesh->varr))) {
287                 fprintf(stderr, "failed to allocate vertex array for %d vertices\n", mesh->vcount);
288                 return -1;
289         }
290         vptr = mesh->varr;
291         if(!(mesh->iarr = malloc(mesh->icount * sizeof *mesh->iarr))) {
292                 fprintf(stderr, "failed to allocate index array for %d indices\n", mesh->icount);
293                 free(mesh->varr);
294                 mesh->varr = 0;
295                 return -1;
296         }
297         iptr = mesh->iarr;
298
299         u = 0.0;
300         for(i=0; i<uverts; i++) {
301                 v = 0.0;
302                 for(j=0; j<vverts; j++) {
303                         torus_vertex(vptr++, rad, rrad, u, v);
304
305                         if(i < usub && j < vsub) {
306                                 int vnum = i * vverts + j;
307                                 *iptr++ = vnum;
308                                 *iptr++ = vnum + vverts + 1;
309                                 *iptr++ = vnum + 1;
310                                 *iptr++ = vnum;
311                                 *iptr++ = vnum + vverts;
312                                 *iptr++ = vnum + vverts + 1;
313                         }
314
315                         v += dv;
316                 }
317                 u += du;
318         }
319
320         glGenBuffers(1, &mesh->vbo);
321         glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
322         glBufferData(GL_ARRAY_BUFFER, mesh->vcount * sizeof *mesh->varr, mesh->varr, GL_STATIC_DRAW);
323         glBindBuffer(GL_ARRAY_BUFFER, 0);
324
325         glGenBuffers(1, &mesh->ibo);
326         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo);
327         glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->icount * sizeof *mesh->iarr, mesh->iarr, GL_STATIC_DRAW);
328         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
329
330         glGenVertexArrays(1, &mesh->vao);
331         glBindVertexArray(mesh->vao);
332
333         glEnableVertexAttribArray(VATTR_VERTEX);
334         glEnableVertexAttribArray(VATTR_NORMAL);
335         glEnableVertexAttribArray(VATTR_TEXCOORD);
336
337         glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
338         glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), 0);
339         glVertexAttribPointer(VATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)offsetof(struct vertex, nx));
340         glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)offsetof(struct vertex, tu));
341         glBindBuffer(GL_ARRAY_BUFFER, 0);
342
343         glBindVertexArray(0);
344         return 0;
345 }
346
347 void draw_mesh(struct mesh *mesh)
348 {
349         glBindVertexArray(mesh->vao);
350
351         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo);
352         glDrawElements(GL_TRIANGLES, mesh->icount, GL_UNSIGNED_INT, 0);
353         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
354
355         glBindVertexArray(0);
356 }
357
358 unsigned int gen_texture(int width, int height)
359 {
360         int i, j;
361         unsigned int tex;
362         unsigned char *pixels, *ptr;
363
364         if(!(pixels = malloc(width * height * 3))) {
365                 return 0;
366         }
367         ptr = pixels;
368
369         for(i=0; i<height; i++) {
370                 for(j=0; j<width; j++) {
371                         int x = i ^ j;
372                         *ptr++ = x;
373                         *ptr++ = (x << 1) & 0xff;
374                         *ptr++ = (x << 2) & 0xff;
375                 }
376         }
377
378         glGenTextures(1, &tex);
379         glBindTexture(GL_TEXTURE_2D, tex);
380         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
381         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
382         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
383         glGenerateMipmap(GL_TEXTURE_2D);
384
385         free(pixels);
386         return tex;
387 }
388
389 unsigned int load_shader(const char *fname, int type)
390 {
391         unsigned int sdr;
392         int fsz;
393         char *buf;
394         FILE *fp;
395         int status, loglen;
396
397         if(!(fp = fopen(fname, "rb"))) {
398                 fprintf(stderr, "failed to open shader: %s\n", fname);
399                 return 0;
400         }
401         fseek(fp, 0, SEEK_END);
402         fsz = ftell(fp);
403         rewind(fp);
404
405         if(!(buf = malloc(fsz + 1))) {
406                 fprintf(stderr, "failed to allocate %d bytes\n", fsz + 1);
407                 fclose(fp);
408                 return 0;
409         }
410         if(fread(buf, 1, fsz, fp) < fsz) {
411                 fprintf(stderr, "failed to read shader: %s\n", fname);
412                 free(buf);
413                 fclose(fp);
414                 return 0;
415         }
416         buf[fsz] = 0;
417         fclose(fp);
418
419         sdr = glCreateShader(type);
420
421 #ifndef SPIRV
422         glShaderSource(sdr, 1, (const char**)&buf, 0);
423         glCompileShader(sdr);
424 #else
425         glShaderBinary(1, &sdr, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, buf, fsz);
426         gl_specialize_shader(sdr, "main", 0, 0, 0);
427 #endif
428         free(buf);
429
430         glGetShaderiv(sdr, GL_COMPILE_STATUS, &status);
431         if(status) {
432                 printf("successfully compiled shader: %s\n", fname);
433         } else {
434                 printf("failed to compile shader: %s\n", fname);
435         }
436
437         glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &loglen);
438         if(loglen > 0 && (buf = malloc(loglen + 1))) {
439                 glGetShaderInfoLog(sdr, loglen, 0, buf);
440                 buf[loglen] = 0;
441                 printf("%s\n", buf);
442                 free(buf);
443         }
444
445         if(!status) {
446                 glDeleteShader(sdr);
447                 return 0;
448         }
449         return sdr;
450 }
451
452 unsigned int load_program(const char *vfname, const char *pfname)
453 {
454         unsigned int vs, ps, prog;
455
456         if(!(vs = load_shader(vfname, GL_VERTEX_SHADER))) {
457                 return 0;
458         }
459         if(!(ps = load_shader(pfname, GL_FRAGMENT_SHADER))) {
460                 glDeleteShader(vs);
461                 return 0;
462         }
463
464         prog = glCreateProgram();
465         glAttachShader(prog, vs);
466         glAttachShader(prog, ps);
467
468         if(link_program(prog) == -1) {
469                 glDeleteShader(vs);
470                 glDeleteShader(ps);
471                 glDeleteProgram(prog);
472                 return 0;
473         }
474
475         glDetachShader(prog, vs);
476         glDetachShader(prog, ps);
477
478         return prog;
479 }
480
481 int link_program(unsigned int prog)
482 {
483         int status, loglen;
484         char *buf;
485
486         glLinkProgram(prog);
487
488         glGetProgramiv(prog, GL_LINK_STATUS, &status);
489         if(status) {
490                 printf("successfully linked shader program\n");
491         } else {
492                 printf("failed to link shader program\n");
493         }
494
495         glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &loglen);
496         if(loglen > 0 && (buf = malloc(loglen + 1))) {
497                 glGetProgramInfoLog(prog, loglen, 0, buf);
498                 buf[loglen] = 0;
499                 printf("%s\n", buf);
500                 free(buf);
501         }
502
503         return status ? 0 : -1;
504 }
505
506 const char *gldebug_srcstr(unsigned int src)
507 {
508         switch(src) {
509         case GL_DEBUG_SOURCE_API:
510                 return "api";
511         case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
512                 return "wsys";
513         case GL_DEBUG_SOURCE_SHADER_COMPILER:
514                 return "sdrc";
515         case GL_DEBUG_SOURCE_THIRD_PARTY:
516                 return "3rdparty";
517         case GL_DEBUG_SOURCE_APPLICATION:
518                 return "app";
519         case GL_DEBUG_SOURCE_OTHER:
520                 return "other";
521         default:
522                 break;
523         }
524         return "unknown";
525 }
526
527 const char *gldebug_typestr(unsigned int type)
528 {
529         switch(type) {
530         case GL_DEBUG_TYPE_ERROR:
531                 return "error";
532         case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
533                 return "deprecated";
534         case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
535                 return "undefined behavior";
536         case GL_DEBUG_TYPE_PORTABILITY:
537                 return "portability";
538         case GL_DEBUG_TYPE_PERFORMANCE:
539                 return "performance";
540         case GL_DEBUG_TYPE_OTHER:
541                 return "other";
542         default:
543                 break;
544         }
545         return "unknown";
546 }
547
548 void GLAPIENTRY gldebug(GLenum src, GLenum type, GLuint id, GLenum severity,
549                 GLsizei len, const char *msg, const void *cls)
550 {
551         printf("[GLDEBUG] (%s) %s: %s\n", gldebug_srcstr(src), gldebug_typestr(type), msg);
552 }
553