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