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