1/*
2 * Demo of a reflective, texture-mapped surface with OpenGL.
3 * Brian Paul   August 14, 1995   This file is in the public domain.
4 *
5 * Hardware texture mapping is highly recommended!
6 *
7 * The basic steps are:
8 *    1. Render the reflective object (a polygon) from the normal viewpoint,
9 *       setting the stencil planes = 1.
10 *    2. Render the scene from a special viewpoint:  the viewpoint which
11 *       is on the opposite side of the reflective plane.  Only draw where
12 *       stencil = 1.  This draws the objects in the reflective surface.
13 *    3. Render the scene from the original viewpoint.  This draws the
14 *       objects in the normal fashion.  Use blending when drawing
15 *       the reflective, textured surface.
16 *
17 * This is a very crude demo.  It could be much better.
18 */
19
20/*
21 * Authors:
22 *   Brian Paul
23 *   Dirk Reiners (reiners@igd.fhg.de) made some modifications to this code.
24 *   Mark Kilgard (April 1997)
25 *   Brian Paul (April 2000 - added keyboard d/s options)
26 *   Brian Paul (August 2005 - added multi window feature)
27 */
28
29
30#include <assert.h>
31#include <math.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include "glut_wrap.h"
35#include "showbuffer.h"
36#include "readtex.h"
37
38
39#define DEG2RAD (3.14159/180.0)
40#define TABLE_TEXTURE DEMOS_DATA_DIR "tile.rgb"
41#define MAX_OBJECTS 2
42#define INIT_WIDTH 400
43#define INIT_HEIGHT 300
44
45#ifdef _WIN32
46#undef CreateWindowA
47#endif
48
49struct window {
50   int id;               /* returned by glutCreateWindow() */
51   int width, height;
52   GLboolean anim;
53   GLfloat xrot, yrot;
54   GLfloat spin;
55   GLenum showBuffer;
56   GLenum drawBuffer;
57   GLuint table_list;
58   GLuint objects_list[MAX_OBJECTS];
59   double t0;
60   struct window *next;
61};
62
63
64static struct window *FirstWindow = NULL;
65
66
67static void
68CreateWindow(void);
69
70
71static struct window *
72CurrentWindow(void)
73{
74   int id = glutGetWindow();
75   struct window *w;
76   for (w = FirstWindow; w; w = w->next) {
77      if (w->id == id)
78         return w;
79   }
80   return NULL;
81}
82
83
84static GLboolean
85AnyAnimating(void)
86{
87   struct window *w;
88   for (w = FirstWindow; w; w = w->next) {
89      if (w->anim)
90         return 1;
91   }
92   return 0;
93}
94
95
96static void
97KillWindow(struct window *w)
98{
99   struct window *win, *prev = NULL;
100   for (win = FirstWindow; win; win = win->next) {
101      if (win == w) {
102         if (prev) {
103            prev->next = win->next;
104         }
105         else {
106            FirstWindow = win->next;
107         }
108         glutDestroyWindow(win->id);
109         win->next = NULL;
110         free(win);
111         return;
112      }
113      prev = win;
114   }
115}
116
117
118static void
119KillAllWindows(void)
120{
121   while (FirstWindow)
122      KillWindow(FirstWindow);
123}
124
125
126static GLuint
127MakeTable(void)
128{
129   static GLfloat table_mat[] = { 1.0, 1.0, 1.0, 0.6 };
130   static GLfloat gray[] = { 0.4, 0.4, 0.4, 1.0 };
131   GLuint table_list;
132
133   table_list = glGenLists(1);
134   glNewList( table_list, GL_COMPILE );
135
136   /* load table's texture */
137   glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, table_mat );
138   /*glMaterialfv( GL_FRONT, GL_EMISSION, gray );*/
139   glMaterialfv( GL_FRONT, GL_DIFFUSE, table_mat );
140   glMaterialfv( GL_FRONT, GL_AMBIENT, gray );
141
142   /* draw textured square for the table */
143   glPushMatrix();
144   glScalef( 4.0, 4.0, 4.0 );
145   glBegin( GL_POLYGON );
146   glNormal3f( 0.0, 1.0, 0.0 );
147   glTexCoord2f( 0.0, 0.0 );   glVertex3f( -1.0, 0.0,  1.0 );
148   glTexCoord2f( 1.0, 0.0 );   glVertex3f(  1.0, 0.0,  1.0 );
149   glTexCoord2f( 1.0, 1.0 );   glVertex3f(  1.0, 0.0, -1.0 );
150   glTexCoord2f( 0.0, 1.0 );   glVertex3f( -1.0, 0.0, -1.0 );
151   glEnd();
152   glPopMatrix();
153
154   glDisable( GL_TEXTURE_2D );
155
156   glEndList();
157   return table_list;
158}
159
160
161static void
162MakeObjects(GLuint *objects_list)
163{
164   GLUquadricObj *q;
165
166   static GLfloat cyan[] = { 0.0, 1.0, 1.0, 1.0 };
167   static GLfloat green[] = { 0.2, 1.0, 0.2, 1.0 };
168   static GLfloat black[] = { 0.0, 0.0, 0.0, 0.0 };
169
170   q = gluNewQuadric();
171   gluQuadricDrawStyle( q, GLU_FILL );
172   gluQuadricNormals( q, GLU_SMOOTH );
173
174   objects_list[0] = glGenLists(1);
175   glNewList( objects_list[0], GL_COMPILE );
176   glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cyan );
177   glMaterialfv( GL_FRONT, GL_EMISSION, black );
178   gluCylinder( q, 0.5, 0.5,  1.0, 15, 1 );
179   glEndList();
180
181   objects_list[1] = glGenLists(1);
182   glNewList( objects_list[1], GL_COMPILE );
183   glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green );
184   glMaterialfv( GL_FRONT, GL_EMISSION, black );
185   gluCylinder( q, 1.5, 0.0,  2.5, 15, 1 );
186   glEndList();
187
188   gluDeleteQuadric(q);
189}
190
191
192static void
193InitWindow(struct window *w)
194{
195   GLint imgWidth, imgHeight;
196   GLenum imgFormat;
197   GLubyte *image = NULL;
198
199   w->table_list = MakeTable();
200   MakeObjects(w->objects_list);
201
202   image = LoadRGBImage( TABLE_TEXTURE, &imgWidth, &imgHeight, &imgFormat );
203   if (!image) {
204      printf("Couldn't read %s\n", TABLE_TEXTURE);
205      exit(0);
206   }
207
208   gluBuild2DMipmaps(GL_TEXTURE_2D, 3, imgWidth, imgHeight,
209                     imgFormat, GL_UNSIGNED_BYTE, image);
210   free(image);
211
212   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
213   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
214   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
215   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
216
217   glShadeModel( GL_FLAT );
218
219   glEnable( GL_LIGHT0 );
220   glEnable( GL_LIGHTING );
221
222   glClearColor( 0.5, 0.5, 0.9, 0.0 );
223
224   glEnable( GL_NORMALIZE );
225}
226
227
228static void
229Reshape(int width, int height)
230{
231   struct window *w = CurrentWindow();
232   GLfloat yAspect = 2.5;
233   GLfloat xAspect = yAspect * (float) width / (float) height;
234   w->width = width;
235   w->height = height;
236   glViewport(0, 0, width, height);
237   glMatrixMode(GL_PROJECTION);
238   glLoadIdentity();
239   glFrustum( -xAspect, xAspect, -yAspect, yAspect, 10.0, 30.0 );
240   glMatrixMode(GL_MODELVIEW);
241   glLoadIdentity();
242}
243
244
245static void
246DrawObjects(struct window *w, GLfloat eyex, GLfloat eyey, GLfloat eyez)
247{
248   (void) eyex;
249   (void) eyey;
250   (void) eyez;
251#ifndef USE_ZBUFFER
252   if (eyex<0.5) {
253#endif
254      glPushMatrix();
255      glTranslatef( 1.0, 1.5, 0.0 );
256      glRotatef( w->spin, 1.0, 0.5, 0.0 );
257      glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 );
258      glCallList( w->objects_list[0] );
259      glPopMatrix();
260
261      glPushMatrix();
262      glTranslatef( -1.0, 0.85+3.0*fabs( cos(0.01*w->spin) ), 0.0 );
263      glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 );
264      glRotatef( w->spin, 1.0, 0.5, 0.0 );
265      glScalef( 0.5, 0.5, 0.5 );
266      glCallList( w->objects_list[1] );
267      glPopMatrix();
268#ifndef USE_ZBUFFER
269   }
270   else {
271      glPushMatrix();
272      glTranslatef( -1.0, 0.85+3.0*fabs( cos(0.01*w->spin) ), 0.0 );
273      glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 );
274      glRotatef( w->spin, 1.0, 0.5, 0.0 );
275      glScalef( 0.5, 0.5, 0.5 );
276      glCallList( w->objects_list[1] );
277      glPopMatrix();
278
279      glPushMatrix();
280      glTranslatef( 1.0, 1.5, 0.0 );
281      glRotatef( w->spin, 1.0, 0.5, 0.0 );
282      glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 );
283      glCallList( w->objects_list[0] );
284      glPopMatrix();
285   }
286#endif
287}
288
289
290static void
291DrawTable(struct window *w)
292{
293   glCallList(w->table_list);
294}
295
296
297static void
298DrawWindow(void)
299{
300   struct window *w = CurrentWindow();
301   static GLfloat light_pos[] = { 0.0, 20.0, 0.0, 1.0 };
302   GLfloat dist = 20.0;
303   GLfloat eyex, eyey, eyez;
304
305   if (w->drawBuffer == GL_NONE) {
306      glDrawBuffer(GL_BACK);
307      glReadBuffer(GL_BACK);
308   }
309   else {
310      glDrawBuffer(w->drawBuffer);
311      glReadBuffer(w->drawBuffer);
312   }
313
314   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
315
316   if (w->drawBuffer == GL_NONE) {
317      glDrawBuffer(GL_NONE);
318   }
319
320   eyex = dist  *  cos(w->yrot * DEG2RAD)  *  cos(w->xrot * DEG2RAD);
321   eyez = dist  *  sin(w->yrot * DEG2RAD)  *  cos(w->xrot * DEG2RAD);
322   eyey = dist  *  sin(w->xrot * DEG2RAD);
323
324   /* view from top */
325   glPushMatrix();
326   gluLookAt( eyex, eyey, eyez, 0.0, 0.0, 0.0,  0.0, 1.0, 0.0 );
327
328   glLightfv( GL_LIGHT0, GL_POSITION, light_pos );
329
330   /* draw table into stencil planes */
331   glDisable( GL_DEPTH_TEST );
332   glEnable( GL_STENCIL_TEST );
333   glStencilFunc( GL_ALWAYS, 1, 0xffffffff );
334   glStencilOp( GL_REPLACE, GL_REPLACE, GL_REPLACE );
335   glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
336   DrawTable(w);
337   glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
338
339   glEnable( GL_DEPTH_TEST );
340
341   /* render view from below (reflected viewport) */
342   /* only draw where stencil==1 */
343   if (eyey>0.0) {
344      glPushMatrix();
345
346      glStencilFunc( GL_EQUAL, 1, 0xffffffff );  /* draw if ==1 */
347      glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
348      glScalef( 1.0, -1.0, 1.0 );
349
350      /* Reposition light in reflected space. */
351      glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
352
353      DrawObjects(w, eyex, eyey, eyez);
354      glPopMatrix();
355
356      /* Restore light's original unreflected position. */
357      glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
358   }
359
360   glDisable( GL_STENCIL_TEST );
361
362   glEnable( GL_BLEND );
363   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
364
365   glEnable( GL_TEXTURE_2D );
366   DrawTable(w);
367   glDisable( GL_TEXTURE_2D );
368   glDisable( GL_BLEND );
369
370   /* view from top */
371   glPushMatrix();
372
373   DrawObjects(w, eyex, eyey, eyez);
374
375   glPopMatrix();
376
377   glPopMatrix();
378
379   if (w->showBuffer == GL_DEPTH) {
380      ShowDepthBuffer(w->width, w->height, 1.0, 0.0);
381   }
382   else if (w->showBuffer == GL_STENCIL) {
383      ShowStencilBuffer(w->width, w->height, 255.0, 0.0);
384   }
385   else if (w->showBuffer == GL_ALPHA) {
386      ShowAlphaBuffer(w->width, w->height);
387   }
388
389   if (w->drawBuffer == GL_FRONT)
390      glFinish();
391   else
392      glutSwapBuffers();
393
394   /* calc/show frame rate */
395   {
396      static GLint t0 = 0;
397      static GLint frames = 0;
398      GLint t = glutGet(GLUT_ELAPSED_TIME);
399      frames++;
400      if (t - t0 >= 5000) {
401         GLfloat seconds = (t - t0) / 1000.0;
402         GLfloat fps = frames / seconds;
403         printf("%d frames in %g seconds = %g FPS\n", frames, seconds, fps);
404         fflush(stdout);
405         t0 = t;
406         frames = 0;
407      }
408   }
409}
410
411
412static void
413Idle(void)
414{
415   double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
416   struct window *w;
417   for (w = FirstWindow; w; w = w->next) {
418      if (w->anim) {
419         double dt;
420         if (w->t0 < 0.0)
421            w->t0 = t;
422         dt = t - w->t0;
423         w->t0 = t;
424         w->spin += 60.0 * dt;
425         w->yrot += 90.0 * dt;
426         assert(w->id);
427         glutSetWindow(w->id);
428         glutPostRedisplay();
429      }
430   }
431}
432
433
434static void
435UpdateIdleFunc(void)
436{
437   if (AnyAnimating())
438      glutIdleFunc(Idle);
439   else
440      glutIdleFunc(NULL);
441}
442
443static void
444Key(unsigned char key, int x, int y)
445{
446   struct window *w = CurrentWindow();
447   (void) x;
448   (void) y;
449
450   switch (key) {
451   case 'd':
452      w->showBuffer = GL_DEPTH;
453      glutPostRedisplay();
454      break;
455   case 's':
456      w->showBuffer = GL_STENCIL;
457      glutPostRedisplay();
458      break;
459   case 'a':
460      w->showBuffer = GL_ALPHA;
461      glutPostRedisplay();
462      break;
463   case 'c':
464      w->showBuffer = GL_NONE;
465      glutPostRedisplay();
466      break;
467   case 'f':
468      if (w->drawBuffer == GL_FRONT)
469         w->drawBuffer = GL_BACK;
470      else
471         w->drawBuffer = GL_FRONT;
472      glutPostRedisplay();
473      break;
474   case '0':
475      w->drawBuffer = GL_NONE;
476      glutPostRedisplay();
477      break;
478   case ' ':
479      w->anim = !w->anim;
480      w->t0 = -1;
481      UpdateIdleFunc();
482      glutPostRedisplay();
483      break;
484   case 'n':
485      CreateWindow();
486      UpdateIdleFunc();
487      break;
488   case 'k':
489      KillWindow(w);
490      if (FirstWindow == NULL)
491         exit(0);
492      break;
493   case 27:
494      KillAllWindows();
495      exit(0);
496      break;
497   default:
498      ;
499   }
500}
501
502
503static void
504SpecialKey(int key, int x, int y)
505{
506   struct window *w = CurrentWindow();
507   (void) x;
508   (void) y;
509   switch (key) {
510      case GLUT_KEY_UP:
511         w->xrot += 3.0;
512         if (w->xrot > 85)
513            w->xrot = 85;
514         break;
515      case GLUT_KEY_DOWN:
516         w->xrot -= 3.0;
517         if (w->xrot < 5)
518            w->xrot = 5;
519         break;
520      case GLUT_KEY_LEFT:
521         w->yrot += 3.0;
522         break;
523      case GLUT_KEY_RIGHT:
524         w->yrot -= 3.0;
525         break;
526   }
527   glutPostRedisplay();
528}
529
530
531static void
532CreateWindow(void)
533{
534   char title[1000];
535   struct window *w = (struct window *) calloc(1, sizeof(struct window));
536
537   glutInitWindowSize(INIT_WIDTH, INIT_HEIGHT);
538   w->id = glutCreateWindow("foo");
539   sprintf(title, "reflect window %d", w->id);
540   glutSetWindowTitle(title);
541   assert(w->id);
542   w->width = INIT_WIDTH;
543   w->height = INIT_HEIGHT;
544   w->anim = GL_TRUE;
545   w->xrot = 30.0;
546   w->yrot = 50.0;
547   w->spin = 0.0;
548   w->showBuffer = GL_NONE;
549   w->drawBuffer = GL_BACK;
550
551   InitWindow(w);
552
553   glutReshapeFunc(Reshape);
554   glutDisplayFunc(DrawWindow);
555   glutKeyboardFunc(Key);
556   glutSpecialFunc(SpecialKey);
557
558   /* insert at head of list */
559   w->next = FirstWindow;
560   FirstWindow = w;
561}
562
563
564static void
565Usage(void)
566{
567   printf("Keys:\n");
568   printf("  a      - show alpha buffer\n");
569   printf("  d      - show depth buffer\n");
570   printf("  s      - show stencil buffer\n");
571   printf("  c      - show color buffer\n");
572   printf("  f      - toggle rendering to front/back color buffer\n");
573   printf("  n      - create new window\n");
574   printf("  k      - kill window\n");
575   printf("  SPACE  - toggle animation\n");
576   printf("  ARROWS - rotate scene\n");
577}
578
579
580int
581main(int argc, char *argv[])
582{
583   glutInit(&argc, argv);
584   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH |
585                       GLUT_STENCIL | GLUT_ALPHA);
586   CreateWindow();
587   glutIdleFunc(Idle);
588   Usage();
589   glutMainLoop();
590   return 0;
591}
592