1/**
2 * Test OpenGL 2.0 vertex/fragment shaders.
3 * Brian Paul
4 * 1 November 2006
5 *
6 * Based on ARB version by:
7 * Michal Krol
8 * 20 February 2006
9 *
10 * Based on the original demo by:
11 * Brian Paul
12 * 17 April 2003
13 */
14
15#include <assert.h>
16#include <string.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <math.h>
20#include <GL/glew.h>
21#include "glut_wrap.h"
22
23
24#define TEXTURE 0
25
26static GLint CoordAttrib = 0;
27
28static char *FragProgFile = NULL;
29static char *VertProgFile = NULL;
30
31static GLfloat diffuse[4] = { 0.5f, 0.5f, 1.0f, 1.0f };
32static GLfloat specular[4] = { 0.8f, 0.8f, 0.8f, 1.0f };
33static GLfloat lightPos[4] = { 0.0f, 10.0f, 20.0f, 0.0f };
34static GLfloat delta = 1.0f;
35
36static GLuint fragShader;
37static GLuint vertShader;
38static GLuint program;
39
40static GLint uDiffuse;
41static GLint uSpecular;
42static GLint uTexture;
43
44static GLuint SphereList, RectList, CurList;
45static GLint win = 0;
46static GLboolean anim = GL_TRUE;
47static GLboolean wire = GL_FALSE;
48static GLboolean pixelLight = GL_TRUE;
49
50static GLint t0 = 0;
51static GLint frames = 0;
52
53static GLfloat xRot = 90.0f, yRot = 0.0f;
54
55
56static void
57normalize(GLfloat *dst, const GLfloat *src)
58{
59   GLfloat len = sqrt(src[0] * src[0] + src[1] * src[1] + src[2] * src[2]);
60   dst[0] = src[0] / len;
61   dst[1] = src[1] / len;
62   dst[2] = src[2] / len;
63   dst[3] = src[3];
64}
65
66
67static void
68Redisplay(void)
69{
70   GLfloat vec[4];
71
72   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
73
74   /* update light position */
75   normalize(vec, lightPos);
76   glLightfv(GL_LIGHT0, GL_POSITION, vec);
77
78   if (pixelLight) {
79      glUseProgram(program);
80      glDisable(GL_LIGHTING);
81   }
82   else {
83      glUseProgram(0);
84      glEnable(GL_LIGHTING);
85   }
86
87   glPushMatrix();
88   glRotatef(xRot, 1.0f, 0.0f, 0.0f);
89   glRotatef(yRot, 0.0f, 1.0f, 0.0f);
90   /*
91   glutSolidSphere(2.0, 10, 5);
92   */
93   glCallList(CurList);
94   glPopMatrix();
95
96   glutSwapBuffers();
97   frames++;
98
99   if (anim) {
100      GLint t = glutGet(GLUT_ELAPSED_TIME);
101      if (t - t0 >= 5000) {
102         GLfloat seconds =(GLfloat)(t - t0) / 1000.0f;
103         GLfloat fps = frames / seconds;
104         printf("%d frames in %6.3f seconds = %6.3f FPS\n",
105                frames, seconds, fps);
106         fflush(stdout);
107         t0 = t;
108         frames = 0;
109      }
110   }
111}
112
113
114static void
115Idle(void)
116{
117   lightPos[0] += delta;
118   if (lightPos[0] > 25.0f || lightPos[0] < -25.0f)
119      delta = -delta;
120   glutPostRedisplay();
121}
122
123
124static void
125Reshape(int width, int height)
126{
127   glViewport(0, 0, width, height);
128   glMatrixMode(GL_PROJECTION);
129   glLoadIdentity();
130   glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
131   glMatrixMode(GL_MODELVIEW);
132   glLoadIdentity();
133   glTranslatef(0.0f, 0.0f, -15.0f);
134}
135
136
137static void
138CleanUp(void)
139{
140   glDeleteShader(fragShader);
141   glDeleteShader(vertShader);
142   glDeleteProgram(program);
143   glutDestroyWindow(win);
144}
145
146
147static void
148Key(unsigned char key, int x, int y)
149{
150  (void) x;
151  (void) y;
152
153   switch(key) {
154   case ' ':
155   case 'a':
156      anim = !anim;
157      if (anim)
158         glutIdleFunc(Idle);
159      else
160         glutIdleFunc(NULL);
161      break;
162   case 'x':
163      lightPos[0] -= 1.0f;
164      break;
165   case 'X':
166      lightPos[0] += 1.0f;
167      break;
168   case 'w':
169      wire = !wire;
170      if (wire)
171         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
172      else
173         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
174      break;
175   case 'o':
176      if (CurList == SphereList)
177         CurList = RectList;
178      else
179         CurList = SphereList;
180      break;
181   case 'p':
182      pixelLight = !pixelLight;
183      if (pixelLight)
184         printf("Per-pixel lighting\n");
185      else
186         printf("Conventional lighting\n");
187      break;
188   case 27:
189      CleanUp();
190      exit(0);
191      break;
192   }
193   glutPostRedisplay();
194}
195
196
197static void
198SpecialKey(int key, int x, int y)
199{
200   const GLfloat step = 3.0f;
201
202  (void) x;
203  (void) y;
204
205   switch(key) {
206   case GLUT_KEY_UP:
207      xRot -= step;
208      break;
209   case GLUT_KEY_DOWN:
210      xRot += step;
211      break;
212   case GLUT_KEY_LEFT:
213      yRot -= step;
214      break;
215   case GLUT_KEY_RIGHT:
216      yRot += step;
217      break;
218   }
219   glutPostRedisplay();
220}
221
222
223static void
224TestFunctions(void)
225{
226   printf("Error 0x%x at line %d\n", glGetError(), __LINE__);
227
228   assert(glIsProgram(program));
229   assert(glIsShader(fragShader));
230   assert(glIsShader(vertShader));
231
232   /* attached shaders */
233   {
234      GLuint shaders[20];
235      GLsizei count;
236      int i;
237      glGetAttachedShaders(program, 20, &count, shaders);
238      for (i = 0; i < count; i++) {
239         printf("Attached: %u\n", shaders[i]);
240         assert(shaders[i] == fragShader ||
241                shaders[i] == vertShader);
242      }
243   }
244
245   {
246      GLchar log[1000];
247      GLsizei len;
248      glGetShaderInfoLog(vertShader, 1000, &len, log);
249      printf("Vert Shader Info Log: %s\n", log);
250      glGetShaderInfoLog(fragShader, 1000, &len, log);
251      printf("Frag Shader Info Log: %s\n", log);
252      glGetProgramInfoLog(program, 1000, &len, log);
253      printf("Program Info Log: %s\n", log);
254   }
255
256   /* active uniforms */
257   {
258      GLint n, max, i;
259      glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &n);
260      glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max);
261      printf("Num uniforms: %d  Max name length: %d\n", n, max);
262      for (i = 0; i < n; i++) {
263         GLint size, len;
264         GLenum type;
265         char name[100];
266         glGetActiveUniform(program, i, 100, &len, &size, &type, name);
267         printf("  %d: %s nameLen=%d size=%d type=0x%x\n",
268                i, name, len, size, type);
269      }
270   }
271}
272
273
274#if TEXTURE
275static void
276MakeTexture(void)
277{
278#define SZ0 64
279#define SZ1 32
280   GLubyte image0[SZ0][SZ0][SZ0][4];
281   GLubyte image1[SZ1][SZ1][SZ1][4];
282   GLuint i, j, k;
283
284   /* level 0: two-tone gray checkboard */
285   for (i = 0; i < SZ0; i++) {
286      for (j = 0; j < SZ0; j++) {
287         for (k = 0; k < SZ0; k++) {
288            if ((i/8 + j/8 + k/8) & 1) {
289               image0[i][j][k][0] =
290               image0[i][j][k][1] =
291               image0[i][j][k][2] = 200;
292            }
293            else {
294               image0[i][j][k][0] =
295               image0[i][j][k][1] =
296               image0[i][j][k][2] = 100;
297            }
298            image0[i][j][k][3] = 255;
299         }
300      }
301   }
302
303   /* level 1: two-tone green checkboard */
304   for (i = 0; i < SZ1; i++) {
305      for (j = 0; j < SZ1; j++) {
306         for (k = 0; k < SZ1; k++) {
307            if ((i/8 + j/8 + k/8) & 1) {
308               image1[i][j][k][0] = 0;
309               image1[i][j][k][1] = 250;
310               image1[i][j][k][2] = 0;
311            }
312            else {
313               image1[i][j][k][0] = 0;
314               image1[i][j][k][1] = 200;
315               image1[i][j][k][2] = 0;
316            }
317            image1[i][j][k][3] = 255;
318         }
319      }
320   }
321
322   glActiveTexture(GL_TEXTURE2); /* unit 2 */
323   glBindTexture(GL_TEXTURE_2D, 42);
324   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SZ0, SZ0, 0,
325                GL_RGBA, GL_UNSIGNED_BYTE, image0);
326   glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, SZ1, SZ1, 0,
327                GL_RGBA, GL_UNSIGNED_BYTE, image1);
328   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
329   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
330   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
331
332   glActiveTexture(GL_TEXTURE4); /* unit 4 */
333   glBindTexture(GL_TEXTURE_3D, 43);
334   glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, SZ0, SZ0, SZ0, 0,
335                GL_RGBA, GL_UNSIGNED_BYTE, image0);
336   glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA, SZ1, SZ1, SZ1, 0,
337                GL_RGBA, GL_UNSIGNED_BYTE, image1);
338   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 1);
339   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
340   glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
341}
342#endif
343
344
345static void
346MakeSphere(void)
347{
348   GLUquadricObj *obj = gluNewQuadric();
349   SphereList = glGenLists(1);
350   gluQuadricTexture(obj, GL_TRUE);
351   glNewList(SphereList, GL_COMPILE);
352   gluSphere(obj, 2.0f, 10, 5);
353   glEndList();
354   gluDeleteQuadric(obj);
355}
356
357static void
358VertAttrib(GLint index, float x, float y)
359{
360#if 1
361   glVertexAttrib2f(index, x, y);
362#else
363   glTexCoord2f(x, y);
364#endif
365}
366
367static void
368MakeRect(void)
369{
370   RectList = glGenLists(1);
371   glNewList(RectList, GL_COMPILE);
372   glNormal3f(0, 0, 1);
373   glBegin(GL_POLYGON);
374   VertAttrib(CoordAttrib, 0, 0);   glVertex2f(-2, -2);
375   VertAttrib(CoordAttrib, 1, 0);   glVertex2f( 2, -2);
376   VertAttrib(CoordAttrib, 1, 1);   glVertex2f( 2,  2);
377   VertAttrib(CoordAttrib, 0, 1);   glVertex2f(-2,  2);
378   glEnd();    /* XXX omit this and crash! */
379   glEndList();
380}
381
382
383
384static void
385LoadAndCompileShader(GLuint shader, const char *text)
386{
387   GLint stat;
388
389   glShaderSource(shader, 1, (const GLchar **) &text, NULL);
390
391   glCompileShader(shader);
392
393   glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
394   if (!stat) {
395      GLchar log[1000];
396      GLsizei len;
397      glGetShaderInfoLog(shader, 1000, &len, log);
398      fprintf(stderr, "fslight: problem compiling shader:\n%s\n", log);
399      exit(1);
400   }
401}
402
403
404/**
405 * Read a shader from a file.
406 */
407static void
408ReadShader(GLuint shader, const char *filename)
409{
410   const int max = 100*1000;
411   int n;
412   char *buffer = (char*) malloc(max);
413   FILE *f = fopen(filename, "r");
414   if (!f) {
415      fprintf(stderr, "fslight: Unable to open shader file %s\n", filename);
416      exit(1);
417   }
418
419   n = fread(buffer, 1, max, f);
420   printf("fslight: read %d bytes from shader file %s\n", n, filename);
421   if (n > 0) {
422      buffer[n] = 0;
423      LoadAndCompileShader(shader, buffer);
424   }
425
426   fclose(f);
427   free(buffer);
428}
429
430
431static void
432CheckLink(GLuint prog)
433{
434   GLint stat;
435   glGetProgramiv(prog, GL_LINK_STATUS, &stat);
436   if (!stat) {
437      GLchar log[1000];
438      GLsizei len;
439      glGetProgramInfoLog(prog, 1000, &len, log);
440      fprintf(stderr, "Linker error:\n%s\n", log);
441   }
442}
443
444
445static void
446Init(void)
447{
448   static const char *fragShaderText =
449      "uniform vec4 diffuse;\n"
450      "uniform vec4 specular;\n"
451      "varying vec3 normal;\n"
452      "void main() {\n"
453      "   // Compute dot product of light direction and normal vector\n"
454      "   float dotProd = max(dot(gl_LightSource[0].position.xyz, \n"
455      "                           normalize(normal)), 0.0);\n"
456      "   // Compute diffuse and specular contributions\n"
457      "   gl_FragColor = diffuse * dotProd + specular * pow(dotProd, 20.0);\n"
458      "}\n";
459   static const char *vertShaderText =
460      "varying vec3 normal;\n"
461      "void main() {\n"
462      "   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
463      "   normal = gl_NormalMatrix * gl_Normal;\n"
464      "}\n";
465
466   if (!GLEW_VERSION_2_0) {
467      printf("This program requires OpenGL 2.x or higher\n");
468      exit(1);
469   }
470
471   fragShader = glCreateShader(GL_FRAGMENT_SHADER);
472   if (FragProgFile)
473      ReadShader(fragShader, FragProgFile);
474   else
475      LoadAndCompileShader(fragShader, fragShaderText);
476
477
478   vertShader = glCreateShader(GL_VERTEX_SHADER);
479   if (VertProgFile)
480      ReadShader(vertShader, VertProgFile);
481   else
482      LoadAndCompileShader(vertShader, vertShaderText);
483
484   program = glCreateProgram();
485   glAttachShader(program, fragShader);
486   glAttachShader(program, vertShader);
487   glLinkProgram(program);
488   CheckLink(program);
489   glUseProgram(program);
490
491   uDiffuse = glGetUniformLocation(program, "diffuse");
492   uSpecular = glGetUniformLocation(program, "specular");
493   uTexture = glGetUniformLocation(program, "texture");
494   printf("DiffusePos %d  SpecularPos %d  TexturePos %d\n",
495          uDiffuse, uSpecular, uTexture);
496
497   glUniform4fv(uDiffuse, 1, diffuse);
498   glUniform4fv(uSpecular, 1, specular);
499   /*   assert(glGetError() == 0);*/
500#if TEXTURE
501   glUniform1i(uTexture, 2);  /* use texture unit 2 */
502#endif
503   /*assert(glGetError() == 0);*/
504
505   if (CoordAttrib) {
506      int i;
507      glBindAttribLocation(program, CoordAttrib, "coord");
508      i = glGetAttribLocation(program, "coord");
509      assert(i >= 0);
510      if (i != CoordAttrib) {
511         printf("Hmmm, NVIDIA bug?\n");
512         CoordAttrib = i;
513      }
514      else {
515         printf("Mesa bind attrib: coord = %d\n", i);
516      }
517   }
518
519   /*assert(glGetError() == 0);*/
520
521   glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
522   glEnable(GL_DEPTH_TEST);
523   glEnable(GL_LIGHT0);
524   glEnable(GL_LIGHTING);
525   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
526   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
527   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0f);
528
529   MakeSphere();
530   MakeRect();
531
532   CurList = SphereList;
533
534#if TEXTURE
535   MakeTexture();
536#endif
537
538   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
539   printf("Press p to toggle between per-pixel and per-vertex lighting\n");
540
541   /* test glGetShaderSource() */
542   if (0) {
543      GLsizei len = strlen(fragShaderText) + 1;
544      GLsizei lenOut;
545      GLchar *src =(GLchar *) malloc(len * sizeof(GLchar));
546      glGetShaderSource(fragShader, 0, NULL, src);
547      glGetShaderSource(fragShader, len, &lenOut, src);
548      assert(len == lenOut + 1);
549      assert(strcmp(src, fragShaderText) == 0);
550      free(src);
551   }
552
553   assert(glIsProgram(program));
554   assert(glIsShader(fragShader));
555   assert(glIsShader(vertShader));
556
557   glColor3f(1, 0, 0);
558
559   /* for testing state vars */
560   {
561      static GLfloat fc[4] = { 1, 1, 0, 0 };
562      static GLfloat amb[4] = { 1, 0, 1, 0 };
563      glFogfv(GL_FOG_COLOR, fc);
564      glLightfv(GL_LIGHT1, GL_AMBIENT, amb);
565   }
566
567#if 0
568   TestFunctions();
569#else
570   (void) TestFunctions;
571#endif
572}
573
574
575static void
576ParseOptions(int argc, char *argv[])
577{
578   int i;
579   for (i = 1; i < argc; i++) {
580      if (strcmp(argv[i], "-fs") == 0) {
581         FragProgFile = argv[++i];
582      }
583      else if (strcmp(argv[i], "-vs") == 0) {
584         VertProgFile = argv[++i];
585      }
586      else {
587         fprintf(stderr, "unknown option %s\n", argv[i]);
588         break;
589      }
590   }
591}
592
593
594int
595main(int argc, char *argv[])
596{
597   glutInitWindowSize(200, 200);
598   glutInit(&argc, argv);
599   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
600   win = glutCreateWindow(argv[0]);
601   glewInit();
602   glutReshapeFunc(Reshape);
603   glutKeyboardFunc(Key);
604   glutSpecialFunc(SpecialKey);
605   glutDisplayFunc(Redisplay);
606   if (anim)
607      glutIdleFunc(Idle);
608   ParseOptions(argc, argv);
609   Init();
610   glutMainLoop();
611   return 0;
612}
613
614
615