1
2/*
3 * Specular reflection demo.  The specular highlight is modulated by
4 * a sphere-mapped texture.  The result is a high-gloss surface.
5 * NOTE: you really need hardware acceleration for this.
6 * Also note, this technique can't be implemented with multi-texture
7 * and separate specular color interpolation because there's no way
8 * to indicate that the second texture unit (the reflection map)
9 * should modulate the specular color and not the base color.
10 * A future multi-texture extension could fix that.
11 *
12 * Command line options:
13 *    -info      print GL implementation information
14 *
15 *
16 * Brian Paul  October 22, 1999  This program is in the public domain.
17 */
18
19
20#include <assert.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <math.h>
24#include <string.h>
25#include <GL/glew.h>
26#include "glut_wrap.h"
27
28#include "readtex.h"
29#include "trackball.h"
30
31
32#define SPECULAR_TEXTURE_FILE DEMOS_DATA_DIR "reflect.rgb"
33#define BASE_TEXTURE_FILE DEMOS_DATA_DIR "tile.rgb"
34
35/* Menu items */
36#define DO_SPEC_TEXTURE 1
37#define OBJECT 2
38#define ANIMATE 3
39#define QUIT 100
40
41/* for convolution */
42#define FILTER_SIZE 7
43
44static GLint Win;
45static GLint WinWidth = 500, WinHeight = 500;
46static GLuint CylinderObj = 0;
47static GLuint TeapotObj = 0;
48static GLuint Object = 0;
49static GLboolean Animate = GL_TRUE;
50
51static float CurQuat[4] = { 0, 0, 0, 1 };
52
53static GLfloat Black[4] = { 0, 0, 0, 0 };
54static GLfloat White[4] = { 1, 1, 1, 1 };
55static GLfloat Diffuse[4] = { .3, .3, 1.0, 1.0 };  /* blue */
56static GLfloat Shininess = 6;
57
58static GLuint BaseTexture, SpecularTexture;
59static GLboolean DoSpecTexture = GL_TRUE;
60
61static GLboolean ButtonDown = GL_FALSE;
62static GLint ButtonX, ButtonY;
63
64
65/* performance info */
66static GLint T0 = 0;
67static GLint Frames = 0;
68
69
70static void Idle( void )
71{
72   static const float yAxis[3] = {0, 1, 0};
73   static double t0 = -1.;
74   float quat[4];
75   double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
76   if (t0 < 0.0)
77      t0 = t;
78   dt = t - t0;
79   t0 = t;
80
81   axis_to_quat(yAxis, 2.0 * dt, quat);
82   add_quats(quat, CurQuat, CurQuat);
83
84   glutPostRedisplay();
85}
86
87
88static void Display( void )
89{
90   GLfloat rot[4][4];
91
92   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
93
94   glPushMatrix();
95   build_rotmatrix(rot, CurQuat);
96   glMultMatrixf(&rot[0][0]);
97
98   /* First pass: diffuse lighting with base texture */
99   glMaterialfv(GL_FRONT, GL_DIFFUSE, Diffuse);
100   glMaterialfv(GL_FRONT, GL_SPECULAR, Black);
101   glEnable(GL_TEXTURE_2D);
102   glBindTexture(GL_TEXTURE_2D, BaseTexture);
103   glCallList(Object);
104
105   /* Second pass: specular lighting with reflection texture */
106   glEnable(GL_POLYGON_OFFSET_FILL);
107   glBlendFunc(GL_ONE, GL_ONE);  /* add */
108   glEnable(GL_BLEND);
109   glMaterialfv(GL_FRONT, GL_DIFFUSE, Black);
110   glMaterialfv(GL_FRONT, GL_SPECULAR, White);
111   if (DoSpecTexture) {
112      glBindTexture(GL_TEXTURE_2D, SpecularTexture);
113      glEnable(GL_TEXTURE_GEN_S);
114      glEnable(GL_TEXTURE_GEN_T);
115   }
116   else {
117      glDisable(GL_TEXTURE_2D);
118   }
119   glCallList(Object);
120   glDisable(GL_TEXTURE_GEN_S);
121   glDisable(GL_TEXTURE_GEN_T);
122   glDisable(GL_BLEND);
123   glDisable(GL_POLYGON_OFFSET_FILL);
124
125   glPopMatrix();
126
127   glutSwapBuffers();
128
129   if (Animate) {
130      GLint t = glutGet(GLUT_ELAPSED_TIME);
131      Frames++;
132      if (t - T0 >= 5000) {
133         GLfloat seconds = (t - T0) / 1000.0;
134         GLfloat fps = Frames / seconds;
135         printf("%d frames in %g seconds = %g FPS\n", Frames, seconds, fps);
136         fflush(stdout);
137         T0 = t;
138         Frames = 0;
139      }
140   }
141}
142
143
144static void Reshape( int width, int height )
145{
146   GLfloat h = 30.0;
147   GLfloat w = h * width / height;
148   WinWidth = width;
149   WinHeight = height;
150   glViewport( 0, 0, width, height );
151   glMatrixMode( GL_PROJECTION );
152   glLoadIdentity();
153   glFrustum( -w, w, -h, h, 150.0, 500.0 );
154   glMatrixMode( GL_MODELVIEW );
155   glLoadIdentity();
156   glTranslatef( 0.0, 0.0, -380.0 );
157}
158
159
160static void ToggleAnimate(void)
161{
162   Animate = !Animate;
163   if (Animate) {
164      glutIdleFunc( Idle );
165      T0 = glutGet(GLUT_ELAPSED_TIME);
166      Frames = 0;
167   }
168   else {
169      glutIdleFunc( NULL );
170   }
171}
172
173
174static void ModeMenu(int entry)
175{
176   if (entry==ANIMATE) {
177      ToggleAnimate();
178   }
179   else if (entry==DO_SPEC_TEXTURE) {
180      DoSpecTexture = !DoSpecTexture;
181   }
182   else if (entry==OBJECT) {
183      if (Object == TeapotObj)
184         Object = CylinderObj;
185      else
186         Object = TeapotObj;
187   }
188   else if (entry==QUIT) {
189      exit(0);
190   }
191   glutPostRedisplay();
192}
193
194
195static void Key( unsigned char key, int x, int y )
196{
197   (void) x;
198   (void) y;
199   switch (key) {
200      case 's':
201         Shininess--;
202         if (Shininess < 0.0)
203            Shininess = 0.0;
204         glMaterialf(GL_FRONT, GL_SHININESS, Shininess);
205         printf("Shininess = %g\n", Shininess);
206         break;
207      case 'S':
208         Shininess++;
209         if (Shininess > 128.0)
210            Shininess = 128.0;
211         glMaterialf(GL_FRONT, GL_SHININESS, Shininess);
212         printf("Shininess = %g\n", Shininess);
213         break;
214      case 'a':
215      case ' ':
216         ToggleAnimate();
217         break;
218      case 'n':
219         Idle();
220         break;
221      case 27:
222         glutDestroyWindow(Win);
223         exit(0);
224         break;
225   }
226   glutPostRedisplay();
227}
228
229
230static void
231MouseMotion(int x, int y)
232{
233   if (ButtonDown) {
234      float x0 = (2.0 * ButtonX - WinWidth) / WinWidth;
235      float y0 = (WinHeight - 2.0 * ButtonY) / WinHeight;
236      float x1 = (2.0 * x - WinWidth) / WinWidth;
237      float y1 = (WinHeight - 2.0 * y) / WinHeight;
238      float q[4];
239
240      trackball(q, x0, y0, x1, y1);
241      ButtonX = x;
242      ButtonY = y;
243      add_quats(q, CurQuat, CurQuat);
244
245      glutPostRedisplay();
246   }
247}
248
249
250static void
251MouseButton(int button, int state, int x, int y)
252{
253  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
254     ButtonDown = GL_TRUE;
255     ButtonX = x;
256     ButtonY = y;
257  }
258  else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
259     ButtonDown = GL_FALSE;
260  }
261}
262
263
264static void Init( int argc, char *argv[] )
265{
266   GLboolean convolve = GL_FALSE;
267   GLboolean fullscreen = GL_FALSE;
268   int i;
269
270   for (i = 1; i < argc; i++) {
271      if (strcmp(argv[i], "-info")==0) {
272         printf("GL_RENDERER   = %s\n", (char *) glGetString(GL_RENDERER));
273         printf("GL_VERSION    = %s\n", (char *) glGetString(GL_VERSION));
274         printf("GL_VENDOR     = %s\n", (char *) glGetString(GL_VENDOR));
275         printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
276      }
277      else if (strcmp(argv[i], "-c")==0) {
278         convolve = GL_TRUE;
279      }
280      else if (strcmp(argv[i], "-f")==0) {
281         fullscreen = GL_TRUE;
282      }
283   }
284
285   if (convolve && !glutExtensionSupported("GL_ARB_imaging")) {
286      fprintf(stderr,
287              "GL_ARB_imaging is not supported, disabling convolution.\n");
288      exit(1);
289   }
290
291
292   if (fullscreen)
293      glutFullScreen();
294
295   /* Cylinder object */
296   {
297      static GLfloat height = 100.0;
298      static GLfloat radius = 40.0;
299      static GLint slices = 24;  /* pie slices around Z axis */
300      static GLint stacks = 10;  /* subdivisions along length of cylinder */
301      static GLint rings = 4;    /* rings in the end disks */
302      GLUquadricObj *q = gluNewQuadric();
303      assert(q);
304      gluQuadricTexture(q, GL_TRUE);
305
306      CylinderObj = glGenLists(1);
307      glNewList(CylinderObj, GL_COMPILE);
308
309      glPushMatrix();
310      glTranslatef(0.0, 0.0, -0.5 * height);
311
312      glMatrixMode(GL_TEXTURE);
313      glLoadIdentity();
314      /*glScalef(8.0, 4.0, 2.0);*/
315      glMatrixMode(GL_MODELVIEW);
316
317      /* cylinder */
318      gluQuadricNormals(q, GL_SMOOTH);
319      gluQuadricTexture(q, GL_TRUE);
320      gluCylinder(q, radius, radius, height, slices, stacks);
321
322      /* end cap */
323      glMatrixMode(GL_TEXTURE);
324      glLoadIdentity();
325      glScalef(3.0, 3.0, 1.0);
326      glMatrixMode(GL_MODELVIEW);
327
328      glTranslatef(0.0, 0.0, height);
329      gluDisk(q, 0.0, radius, slices, rings);
330
331      /* other end cap */
332      glTranslatef(0.0, 0.0, -height);
333      gluQuadricOrientation(q, GLU_INSIDE);
334      gluDisk(q, 0.0, radius, slices, rings);
335
336      glPopMatrix();
337
338      glMatrixMode(GL_TEXTURE);
339      glLoadIdentity();
340      glMatrixMode(GL_MODELVIEW);
341
342      glEndList();
343      gluDeleteQuadric(q);
344   }
345
346   /* Teapot */
347   {
348      TeapotObj = glGenLists(1);
349      glNewList(TeapotObj, GL_COMPILE);
350
351      glFrontFace(GL_CW);
352      glutSolidTeapot(40.0);
353      glFrontFace(GL_CCW);
354
355      glEndList();
356   }
357
358   /* show cylinder by default */
359   Object = CylinderObj;
360
361
362   /* lighting */
363   glEnable(GL_LIGHTING);
364   {
365      GLfloat pos[4] = { 3, 3, 3, 1 };
366      glLightfv(GL_LIGHT0, GL_AMBIENT, Black);
367      glLightfv(GL_LIGHT0, GL_DIFFUSE, White);
368      glLightfv(GL_LIGHT0, GL_SPECULAR, White);
369      glLightfv(GL_LIGHT0, GL_POSITION, pos);
370      glEnable(GL_LIGHT0);
371      glMaterialfv(GL_FRONT, GL_AMBIENT, Black);
372      glMaterialf(GL_FRONT, GL_SHININESS, Shininess);
373      glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
374   }
375
376   /* Base texture */
377   glGenTextures(1, &BaseTexture);
378   glBindTexture(GL_TEXTURE_2D, BaseTexture);
379   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
380   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
381   if (!LoadRGBMipmaps(BASE_TEXTURE_FILE, GL_RGB)) {
382      printf("Error: couldn't load texture image file %s\n", BASE_TEXTURE_FILE);
383      exit(1);
384   }
385
386   /* Specular texture */
387   glGenTextures(1, &SpecularTexture);
388   glBindTexture(GL_TEXTURE_2D, SpecularTexture);
389   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
390   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
391   glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
392   glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
393   if (convolve) {
394      /* use convolution to blur the texture to simulate a dull finish
395       * on the object.
396       */
397      GLubyte *img;
398      GLenum format;
399      GLint w, h;
400      GLfloat filter[FILTER_SIZE][FILTER_SIZE][4];
401
402      for (h = 0; h < FILTER_SIZE; h++) {
403         for (w = 0; w < FILTER_SIZE; w++) {
404            const GLfloat k = 1.0 / (FILTER_SIZE * FILTER_SIZE);
405            filter[h][w][0] = k;
406            filter[h][w][1] = k;
407            filter[h][w][2] = k;
408            filter[h][w][3] = k;
409         }
410      }
411
412      glEnable(GL_CONVOLUTION_2D);
413      glConvolutionParameteri(GL_CONVOLUTION_2D,
414                              GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER);
415      glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_RGBA,
416                            FILTER_SIZE, FILTER_SIZE,
417                            GL_RGBA, GL_FLOAT, filter);
418
419      img = LoadRGBImage(SPECULAR_TEXTURE_FILE, &w, &h, &format);
420      if (!img) {
421         printf("Error: couldn't load texture image file %s\n",
422                SPECULAR_TEXTURE_FILE);
423         exit(1);
424      }
425
426      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
427                   format, GL_UNSIGNED_BYTE, img);
428      free(img);
429   }
430   else {
431      /* regular path */
432      if (!LoadRGBMipmaps(SPECULAR_TEXTURE_FILE, GL_RGB)) {
433         printf("Error: couldn't load texture image file %s\n",
434                SPECULAR_TEXTURE_FILE);
435         exit(1);
436      }
437   }
438
439   /* misc */
440   glEnable(GL_CULL_FACE);
441   glEnable(GL_TEXTURE_2D);
442   glEnable(GL_DEPTH_TEST);
443   glEnable(GL_NORMALIZE);
444
445   glPolygonOffset( -1, -1 );
446}
447
448
449int main( int argc, char *argv[] )
450{
451   glutInitWindowSize(WinWidth, WinHeight);
452   glutInit( &argc, argv );
453   glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
454   Win = glutCreateWindow(argv[0] );
455   glewInit();
456   glutReshapeFunc( Reshape );
457   glutKeyboardFunc( Key );
458   glutDisplayFunc( Display );
459   glutMotionFunc(MouseMotion);
460   glutMouseFunc(MouseButton);
461   if (Animate)
462      glutIdleFunc( Idle );
463
464   glutCreateMenu(ModeMenu);
465   glutAddMenuEntry("Toggle Highlight", DO_SPEC_TEXTURE);
466   glutAddMenuEntry("Toggle Object", OBJECT);
467   glutAddMenuEntry("Toggle Animate", ANIMATE);
468   glutAddMenuEntry("Quit", QUIT);
469   glutAttachMenu(GLUT_RIGHT_BUTTON);
470
471   Init(argc, argv);
472
473   glutMainLoop();
474   return 0;
475}
476