/* * Demo of a reflective, texture-mapped surface with OpenGL. * Brian Paul August 14, 1995 This file is in the public domain. * * Hardware texture mapping is highly recommended! * * The basic steps are: * 1. Render the reflective object (a polygon) from the normal viewpoint, * setting the stencil planes = 1. * 2. Render the scene from a special viewpoint: the viewpoint which * is on the opposite side of the reflective plane. Only draw where * stencil = 1. This draws the objects in the reflective surface. * 3. Render the scene from the original viewpoint. This draws the * objects in the normal fashion. Use blending when drawing * the reflective, textured surface. * * This is a very crude demo. It could be much better. */ /* * Authors: * Brian Paul * Dirk Reiners (reiners@igd.fhg.de) made some modifications to this code. * Mark Kilgard (April 1997) * Brian Paul (April 2000 - added keyboard d/s options) * Brian Paul (August 2005 - added multi window feature) */ #include <assert.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include "GL/glut.h" #include "showbuffer.h" #include "readtex.h" #define DEG2RAD (3.14159/180.0) #define TABLE_TEXTURE "../images/tile.rgb" #define MAX_OBJECTS 2 #define INIT_WIDTH 400 #define INIT_HEIGHT 300 #ifdef _WIN32 #undef CreateWindowA #endif struct window { int id; /* returned by glutCreateWindow() */ int width, height; GLboolean anim; GLfloat xrot, yrot; GLfloat spin; GLenum showBuffer; GLenum drawBuffer; GLuint table_list; GLuint objects_list[MAX_OBJECTS]; double t0; struct window *next; }; static struct window *FirstWindow = NULL; static void CreateWindow(void); static struct window * CurrentWindow(void) { int id = glutGetWindow(); struct window *w; for (w = FirstWindow; w; w = w->next) { if (w->id == id) return w; } return NULL; } static GLboolean AnyAnimating(void) { struct window *w; for (w = FirstWindow; w; w = w->next) { if (w->anim) return 1; } return 0; } static void KillWindow(struct window *w) { struct window *win, *prev = NULL; for (win = FirstWindow; win; win = win->next) { if (win == w) { if (prev) { prev->next = win->next; } else { FirstWindow = win->next; } glutDestroyWindow(win->id); win->next = NULL; free(win); return; } prev = win; } } static void KillAllWindows(void) { while (FirstWindow) KillWindow(FirstWindow); } static GLuint MakeTable(void) { static GLfloat table_mat[] = { 1.0, 1.0, 1.0, 0.6 }; static GLfloat gray[] = { 0.4, 0.4, 0.4, 1.0 }; GLuint table_list; table_list = glGenLists(1); glNewList( table_list, GL_COMPILE ); /* load table's texture */ glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, table_mat ); /*glMaterialfv( GL_FRONT, GL_EMISSION, gray );*/ glMaterialfv( GL_FRONT, GL_DIFFUSE, table_mat ); glMaterialfv( GL_FRONT, GL_AMBIENT, gray ); /* draw textured square for the table */ glPushMatrix(); glScalef( 4.0, 4.0, 4.0 ); glBegin( GL_POLYGON ); glNormal3f( 0.0, 1.0, 0.0 ); glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, 0.0, 1.0 ); glTexCoord2f( 1.0, 0.0 ); glVertex3f( 1.0, 0.0, 1.0 ); glTexCoord2f( 1.0, 1.0 ); glVertex3f( 1.0, 0.0, -1.0 ); glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, 0.0, -1.0 ); glEnd(); glPopMatrix(); glDisable( GL_TEXTURE_2D ); glEndList(); return table_list; } static void MakeObjects(GLuint *objects_list) { GLUquadricObj *q; static GLfloat cyan[] = { 0.0, 1.0, 1.0, 1.0 }; static GLfloat green[] = { 0.2, 1.0, 0.2, 1.0 }; static GLfloat black[] = { 0.0, 0.0, 0.0, 0.0 }; q = gluNewQuadric(); gluQuadricDrawStyle( q, GLU_FILL ); gluQuadricNormals( q, GLU_SMOOTH ); objects_list[0] = glGenLists(1); glNewList( objects_list[0], GL_COMPILE ); glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cyan ); glMaterialfv( GL_FRONT, GL_EMISSION, black ); gluCylinder( q, 0.5, 0.5, 1.0, 15, 1 ); glEndList(); objects_list[1] = glGenLists(1); glNewList( objects_list[1], GL_COMPILE ); glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green ); glMaterialfv( GL_FRONT, GL_EMISSION, black ); gluCylinder( q, 1.5, 0.0, 2.5, 15, 1 ); glEndList(); gluDeleteQuadric(q); } static void InitWindow(struct window *w) { GLint imgWidth, imgHeight; GLenum imgFormat; GLubyte *image = NULL; w->table_list = MakeTable(); MakeObjects(w->objects_list); image = LoadRGBImage( TABLE_TEXTURE, &imgWidth, &imgHeight, &imgFormat ); if (!image) { printf("Couldn't read %s\n", TABLE_TEXTURE); exit(0); } gluBuild2DMipmaps(GL_TEXTURE_2D, 3, imgWidth, imgHeight, imgFormat, GL_UNSIGNED_BYTE, image); free(image); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glShadeModel( GL_FLAT ); glEnable( GL_LIGHT0 ); glEnable( GL_LIGHTING ); glClearColor( 0.5, 0.5, 0.9, 0.0 ); glEnable( GL_NORMALIZE ); } static void Reshape(int width, int height) { struct window *w = CurrentWindow(); GLfloat yAspect = 2.5; GLfloat xAspect = yAspect * (float) width / (float) height; w->width = width; w->height = height; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum( -xAspect, xAspect, -yAspect, yAspect, 10.0, 30.0 ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } static void DrawObjects(struct window *w, GLfloat eyex, GLfloat eyey, GLfloat eyez) { (void) eyex; (void) eyey; (void) eyez; #ifndef USE_ZBUFFER if (eyex<0.5) { #endif glPushMatrix(); glTranslatef( 1.0, 1.5, 0.0 ); glRotatef( w->spin, 1.0, 0.5, 0.0 ); glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 ); glCallList( w->objects_list[0] ); glPopMatrix(); glPushMatrix(); glTranslatef( -1.0, 0.85+3.0*fabs( cos(0.01*w->spin) ), 0.0 ); glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 ); glRotatef( w->spin, 1.0, 0.5, 0.0 ); glScalef( 0.5, 0.5, 0.5 ); glCallList( w->objects_list[1] ); glPopMatrix(); #ifndef USE_ZBUFFER } else { glPushMatrix(); glTranslatef( -1.0, 0.85+3.0*fabs( cos(0.01*w->spin) ), 0.0 ); glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 ); glRotatef( w->spin, 1.0, 0.5, 0.0 ); glScalef( 0.5, 0.5, 0.5 ); glCallList( w->objects_list[1] ); glPopMatrix(); glPushMatrix(); glTranslatef( 1.0, 1.5, 0.0 ); glRotatef( w->spin, 1.0, 0.5, 0.0 ); glRotatef( 0.5*w->spin, 0.0, 0.5, 1.0 ); glCallList( w->objects_list[0] ); glPopMatrix(); } #endif } static void DrawTable(struct window *w) { glCallList(w->table_list); } static void DrawWindow(void) { struct window *w = CurrentWindow(); static GLfloat light_pos[] = { 0.0, 20.0, 0.0, 1.0 }; GLfloat dist = 20.0; GLfloat eyex, eyey, eyez; if (w->drawBuffer == GL_NONE) { glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); } else { glDrawBuffer(w->drawBuffer); glReadBuffer(w->drawBuffer); } glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); if (w->drawBuffer == GL_NONE) { glDrawBuffer(GL_NONE); } eyex = dist * cos(w->yrot * DEG2RAD) * cos(w->xrot * DEG2RAD); eyez = dist * sin(w->yrot * DEG2RAD) * cos(w->xrot * DEG2RAD); eyey = dist * sin(w->xrot * DEG2RAD); /* view from top */ glPushMatrix(); gluLookAt( eyex, eyey, eyez, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ); glLightfv( GL_LIGHT0, GL_POSITION, light_pos ); /* draw table into stencil planes */ glDisable( GL_DEPTH_TEST ); glEnable( GL_STENCIL_TEST ); glStencilFunc( GL_ALWAYS, 1, 0xffffffff ); glStencilOp( GL_REPLACE, GL_REPLACE, GL_REPLACE ); glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); DrawTable(w); glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); glEnable( GL_DEPTH_TEST ); /* render view from below (reflected viewport) */ /* only draw where stencil==1 */ if (eyey>0.0) { glPushMatrix(); glStencilFunc( GL_EQUAL, 1, 0xffffffff ); /* draw if ==1 */ glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); glScalef( 1.0, -1.0, 1.0 ); /* Reposition light in reflected space. */ glLightfv(GL_LIGHT0, GL_POSITION, light_pos); DrawObjects(w, eyex, eyey, eyez); glPopMatrix(); /* Restore light's original unreflected position. */ glLightfv(GL_LIGHT0, GL_POSITION, light_pos); } glDisable( GL_STENCIL_TEST ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glEnable( GL_TEXTURE_2D ); DrawTable(w); glDisable( GL_TEXTURE_2D ); glDisable( GL_BLEND ); /* view from top */ glPushMatrix(); DrawObjects(w, eyex, eyey, eyez); glPopMatrix(); glPopMatrix(); if (w->showBuffer == GL_DEPTH) { ShowDepthBuffer(w->width, w->height, 1.0, 0.0); } else if (w->showBuffer == GL_STENCIL) { ShowStencilBuffer(w->width, w->height, 255.0, 0.0); } else if (w->showBuffer == GL_ALPHA) { ShowAlphaBuffer(w->width, w->height); } if (w->drawBuffer == GL_FRONT) glFinish(); else glutSwapBuffers(); /* calc/show frame rate */ { static GLint t0 = 0; static GLint frames = 0; GLint t = glutGet(GLUT_ELAPSED_TIME); frames++; if (t - t0 >= 5000) { GLfloat seconds = (t - t0) / 1000.0; GLfloat fps = frames / seconds; printf("%d frames in %g seconds = %g FPS\n", frames, seconds, fps); t0 = t; frames = 0; } } } static void Idle(void) { double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; struct window *w; for (w = FirstWindow; w; w = w->next) { if (w->anim) { double dt; if (w->t0 < 0.0) w->t0 = t; dt = t - w->t0; w->t0 = t; w->spin += 60.0 * dt; w->yrot += 90.0 * dt; assert(w->id); glutSetWindow(w->id); glutPostRedisplay(); } } } static void UpdateIdleFunc(void) { if (AnyAnimating()) glutIdleFunc(Idle); else glutIdleFunc(NULL); } static void Key(unsigned char key, int x, int y) { struct window *w = CurrentWindow(); (void) x; (void) y; switch (key) { case 'd': w->showBuffer = GL_DEPTH; glutPostRedisplay(); break; case 's': w->showBuffer = GL_STENCIL; glutPostRedisplay(); break; case 'a': w->showBuffer = GL_ALPHA; glutPostRedisplay(); break; case 'c': w->showBuffer = GL_NONE; glutPostRedisplay(); break; case 'f': if (w->drawBuffer == GL_FRONT) w->drawBuffer = GL_BACK; else w->drawBuffer = GL_FRONT; glutPostRedisplay(); break; case '0': w->drawBuffer = GL_NONE; glutPostRedisplay(); break; case ' ': w->anim = !w->anim; w->t0 = -1; UpdateIdleFunc(); glutPostRedisplay(); break; case 'n': CreateWindow(); UpdateIdleFunc(); break; case 'k': KillWindow(w); if (FirstWindow == NULL) exit(0); break; case 27: KillAllWindows(); exit(0); break; default: ; } } static void SpecialKey(int key, int x, int y) { struct window *w = CurrentWindow(); (void) x; (void) y; switch (key) { case GLUT_KEY_UP: w->xrot += 3.0; if (w->xrot > 85) w->xrot = 85; break; case GLUT_KEY_DOWN: w->xrot -= 3.0; if (w->xrot < 5) w->xrot = 5; break; case GLUT_KEY_LEFT: w->yrot += 3.0; break; case GLUT_KEY_RIGHT: w->yrot -= 3.0; break; } glutPostRedisplay(); } static void CreateWindow(void) { char title[1000]; struct window *w = (struct window *) calloc(1, sizeof(struct window)); glutInitWindowSize(INIT_WIDTH, INIT_HEIGHT); w->id = glutCreateWindow("foo"); sprintf(title, "reflect window %d", w->id); glutSetWindowTitle(title); assert(w->id); w->width = INIT_WIDTH; w->height = INIT_HEIGHT; w->anim = GL_TRUE; w->xrot = 30.0; w->yrot = 50.0; w->spin = 0.0; w->showBuffer = GL_NONE; w->drawBuffer = GL_BACK; InitWindow(w); glutReshapeFunc(Reshape); glutDisplayFunc(DrawWindow); glutKeyboardFunc(Key); glutSpecialFunc(SpecialKey); /* insert at head of list */ w->next = FirstWindow; FirstWindow = w; } static void Usage(void) { printf("Keys:\n"); printf(" a - show alpha buffer\n"); printf(" d - show depth buffer\n"); printf(" s - show stencil buffer\n"); printf(" c - show color buffer\n"); printf(" f - toggle rendering to front/back color buffer\n"); printf(" n - create new window\n"); printf(" k - kill window\n"); printf(" SPACE - toggle animation\n"); printf(" ARROWS - rotate scene\n"); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL | GLUT_ALPHA); CreateWindow(); glutIdleFunc(Idle); Usage(); glutMainLoop(); return 0; }