/* Copyright (c) Mark J. Kilgard, 1997. */ /* This program is freely distributable without licensing fees and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ /* This code demonstrates use of the OpenGL Real-time Shadowing (RTS) routines. The program renders two objects with two light sources in a scene with several other walls and curved surfaces. Objects cast shadows on the walls and curved surfaces as well as each other. The shadowing objects spin. See the rts.c and rtshadow.h source code for more details. */ #include #include #include #include #ifdef GLU_VERSION_1_2 #include "rtshadow.h" /* Some files do not define M_PI... */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif enum { X, Y, Z }; enum { DL_NONE, DL_TORUS, DL_CUBE, DL_DOUBLE_TORUS, DL_SPHERE }; enum { M_TORUS, M_CUBE, M_DOUBLE_TORUS, M_NORMAL_VIEW, M_LIGHT1_VIEW, M_LIGHT2_VIEW, M_START_MOTION, M_ROTATING, M_TWO_BIT_STENCIL, M_ALL_STENCIL, M_RENDER_SILHOUETTE }; #define OBJECT_1 0x8000 #define OBJECT_2 0x4000 int lightView = M_NORMAL_VIEW; int rotate1 = 1, rotate2 = 1; RTSscene *scene; RTSlight *light; RTSlight *light2; RTSobject *object, *object2; GLfloat eyePos[3] = {0.0, 0.0, 10.0}; GLfloat lightPos[4] = {-3.9, 5.0, 1.0, 1.0}; GLfloat lightPos2[4] = {4.0, 5.0, 0.0, 1.0}; GLfloat objectPos[3] = {-1.0, 1.0, 0.0}; GLfloat objectPos2[3] = {2.0, -2.0, 0.0}; GLfloat pink[4] = {0.75, 0.5, 0.5, 1.0}; GLfloat greeny[4] = {0.5, 0.75, 0.5, 1.0}; int shape1 = M_TORUS, shape2 = M_CUBE; int renderSilhouette1, renderSilhouette2; GLfloat angle1 = 75.0; GLfloat angle2 = 75.0; GLfloat viewAngle = 0.0; int moving, begin; void renderBasicObject(int shape) { switch (shape) { case M_TORUS: glCallList(DL_TORUS); break; case M_CUBE: glCallList(DL_CUBE); break; case M_DOUBLE_TORUS: glCallList(DL_DOUBLE_TORUS); glutSolidTorus(0.2, 0.8, 10, 10); break; } } /* ARGSUSED */ void renderObject(void *data) { glPushMatrix(); glTranslatef(objectPos[X], objectPos[Y], objectPos[Z]); glRotatef(angle1, 1.0, 1.2, 0.0); renderBasicObject(shape1); glPopMatrix(); } /* ARGSUSED */ void renderObject2(void *data) { glPushMatrix(); glTranslatef(objectPos2[X], objectPos2[Y], objectPos2[Z]); glRotatef(-angle2, 1.3, 0.0, 1.0); renderBasicObject(shape2); glPopMatrix(); } /* ARGSUSED1 */ void renderScene(GLenum castingLight, void *sceneData, RTSscene * scene) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(70.0, 1.0, 0.01, 30.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); switch (lightView) { case M_NORMAL_VIEW: gluLookAt(eyePos[X], eyePos[Y], eyePos[Z], objectPos[X], objectPos[Y], objectPos[Z], 0.0, 1.0, 0.0); break; case M_LIGHT1_VIEW: gluLookAt(lightPos[X], lightPos[Y], lightPos[Z], objectPos[X], objectPos[Y], objectPos[Z], 0.0, 1.0, 0.0); break; case M_LIGHT2_VIEW: gluLookAt(lightPos2[X], lightPos2[Y], lightPos2[Z], objectPos[X], objectPos[Y], objectPos[Z], 0.0, 1.0, 0.0); break; } glLightfv(GL_LIGHT0, GL_POSITION, lightPos); glLightfv(GL_LIGHT1, GL_POSITION, lightPos2); glEnable(GL_NORMALIZE); glPushMatrix(); glTranslatef(0.0, 8.0, -5.0); glScalef(3.0, 3.0, 3.0); glCallList(DL_SPHERE); glPopMatrix(); glDisable(GL_NORMALIZE); glPushMatrix(); glTranslatef(-5.0, 0.0, 0.0); glCallList(DL_SPHERE); glPopMatrix(); glBegin(GL_QUADS); glNormal3f(0.0, 0.0, 1.0); glVertex3f(-7.5, -7.5, -7.0); glVertex3f(7.5, -7.5, -7.0); glVertex3f(7.5, 7.5, -7.0); glVertex3f(-7.5, 7.5, -7.0); glNormal3f(-1.0, 0.0, 0.0); glVertex3f(5.0, -5.0, -5.0); glVertex3f(5.0, -5.0, 5.0); glVertex3f(5.0, 5.0, 5.0); glVertex3f(5.0, 5.0, -5.0); glNormal3f(0.0, 1.0, 0.0); glVertex3f(-5.0, -5.0, -5.0); glVertex3f(-5.0, -5.0, 5.0); glVertex3f(5.0, -5.0, 5.0); glVertex3f(5.0, -5.0, -5.0); glEnd(); if (castingLight == GL_NONE) { /* Rendering that is not affected by lighting should be drawn only once. The time to render it is when no light is casting. */ glDisable(GL_LIGHTING); glColor3fv(pink); glPushMatrix(); glTranslatef(lightPos[X], lightPos[Y], lightPos[Z]); glutSolidSphere(0.3, 8, 8); glPopMatrix(); glColor3fv(greeny); glPushMatrix(); glTranslatef(lightPos2[X], lightPos2[Y], lightPos2[Z]); glutSolidSphere(0.3, 8, 8); glPopMatrix(); glEnable(GL_LIGHTING); } renderObject(NULL); renderObject2(NULL); } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); rtsRenderScene(scene, RTS_USE_SHADOWS); if (renderSilhouette1) { glColor3f(0.0, 1.0, 0.0); rtsRenderSilhouette(scene, light, object); glColor3f(0.0, 0.0, 1.0); rtsRenderSilhouette(scene, light2, object); } if (renderSilhouette2) { glColor3f(1.0, 0.0, 0.0); rtsRenderSilhouette(scene, light, object2); glColor3f(1.0, 1.0, 0.0); rtsRenderSilhouette(scene, light2, object2); } glutSwapBuffers(); } void idle(void) { if (rotate1) { angle1 += 10; rtsUpdateObjectShape(object); } if (rotate2) { angle2 += 10; rtsUpdateObjectShape(object2); } glutPostRedisplay(); } /* ARGSUSED1 */ void special(int c, int x, int y) { switch (c) { case GLUT_KEY_UP: lightPos[Y] += 0.5; rtsUpdateLightPos(light, lightPos); break; case GLUT_KEY_DOWN: lightPos[Y] -= 0.5; rtsUpdateLightPos(light, lightPos); break; case GLUT_KEY_RIGHT: lightPos[X] += 0.5; rtsUpdateLightPos(light, lightPos); break; case GLUT_KEY_LEFT: lightPos[X] -= 0.5; rtsUpdateLightPos(light, lightPos); break; case GLUT_KEY_PAGE_UP: lightPos[Z] += 0.5; rtsUpdateLightPos(light, lightPos); break; case GLUT_KEY_PAGE_DOWN: lightPos[Z] -= 0.5; rtsUpdateLightPos(light, lightPos); break; case GLUT_KEY_HOME: angle1 += 15; angle2 += 15; rtsUpdateObjectShape(object); rtsUpdateObjectShape(object2); break; case GLUT_KEY_END: angle1 -= 15; angle2 -= 15; rtsUpdateObjectShape(object); rtsUpdateObjectShape(object2); break; case GLUT_KEY_F1: lightView = !lightView; break; } glutPostRedisplay(); } /* ARGSUSED1 */ void keyboard(unsigned char c, int x, int y) { switch (c) { case 27: exit(0); /* NOTREACHED */ break; case ' ': if (rotate1 || rotate2) { glutIdleFunc(NULL); rotate1 = 0; rotate2 = 0; } else { glutIdleFunc(idle); rotate1 = 1; rotate2 = 1; } break; } } void updateIdleCallback(void) { if (rotate1 || rotate2) { glutIdleFunc(idle); } else { glutIdleFunc(NULL); } } void visible(int vis) { if (vis == GLUT_VISIBLE) updateIdleCallback(); else glutIdleFunc(NULL); } void menuHandler(int value) { switch (value) { case OBJECT_1 | M_TORUS: case OBJECT_1 | M_CUBE: case OBJECT_1 | M_DOUBLE_TORUS: shape1 = value & ~OBJECT_1; rtsUpdateObjectShape(object); glutPostRedisplay(); break; case OBJECT_2 | M_TORUS: case OBJECT_2 | M_CUBE: case OBJECT_2 | M_DOUBLE_TORUS: shape2 = value & ~OBJECT_2; rtsUpdateObjectShape(object2); glutPostRedisplay(); break; case M_NORMAL_VIEW: case M_LIGHT1_VIEW: case M_LIGHT2_VIEW: lightView = value; glutPostRedisplay(); break; case M_START_MOTION: rotate1 = 1; rotate2 = 1; glutIdleFunc(idle); break; case OBJECT_1 | M_ROTATING: rotate1 = !rotate1; updateIdleCallback(); break; case OBJECT_2 | M_ROTATING: rotate2 = !rotate2; updateIdleCallback(); break; case M_ALL_STENCIL: rtsUpdateUsableStencilBits(scene, ~0); glutPostRedisplay(); break; case M_TWO_BIT_STENCIL: rtsUpdateUsableStencilBits(scene, 0x3); glutPostRedisplay(); break; case OBJECT_1 | M_RENDER_SILHOUETTE: renderSilhouette1 = !renderSilhouette1; glutPostRedisplay(); break; case OBJECT_2 | M_RENDER_SILHOUETTE: renderSilhouette2 = !renderSilhouette2; glutPostRedisplay(); break; } } void initMenu(void) { glutCreateMenu(menuHandler); glutAddMenuEntry("1 Torus", OBJECT_1 | M_TORUS); glutAddMenuEntry("1 Cube", OBJECT_1 | M_CUBE); glutAddMenuEntry("1 Double torus", OBJECT_1 | M_DOUBLE_TORUS); glutAddMenuEntry("2 Torus", OBJECT_2 | M_TORUS); glutAddMenuEntry("2 Cube", OBJECT_2 | M_CUBE); glutAddMenuEntry("2 Double torus", OBJECT_2 | M_DOUBLE_TORUS); glutAddMenuEntry("Normal view", M_NORMAL_VIEW); glutAddMenuEntry("View from light 1", M_LIGHT1_VIEW); glutAddMenuEntry("View from light 2", M_LIGHT2_VIEW); glutAddMenuEntry("Start motion", M_START_MOTION); glutAddMenuEntry("1 Toggle rotating", OBJECT_1 | M_ROTATING); glutAddMenuEntry("2 Toggle rotating", OBJECT_2 | M_ROTATING); glutAddMenuEntry("Use all stencil", M_ALL_STENCIL); glutAddMenuEntry("Use only 2 bits stencil", M_TWO_BIT_STENCIL); glutAddMenuEntry("1 Toggle silhouette", OBJECT_1 | M_RENDER_SILHOUETTE); glutAddMenuEntry("2 Toggle silhouette", OBJECT_2 | M_RENDER_SILHOUETTE); glutAttachMenu(GLUT_RIGHT_BUTTON); } /* ARGSUSED2 */ void mouse(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { moving = 1; begin = x; } if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { moving = 0; } } /* ARGSUSED1 */ void motion(int x, int y) { if (moving) { viewAngle = viewAngle + (x - begin); eyePos[X] = sin(viewAngle * M_PI / 180.0) * 10.0; eyePos[Z] = cos(viewAngle * M_PI / 180.0) * 10.0; begin = x; glutPostRedisplay(); } } int main(int argc, char **argv) { glutInitDisplayString("stencil>=2 rgb double depth samples"); glutInit(&argc, argv); glutCreateWindow("Hello to Real Time Shadows"); glutDisplayFunc(display); glutSpecialFunc(special); glutKeyboardFunc(keyboard); glutVisibilityFunc(visible); glutMouseFunc(mouse); glutMotionFunc(motion); /* 0xffffffff means "use as much stencil as is available". */ scene = rtsCreateScene(eyePos, 0xffffffff, renderScene, NULL); glLightfv(GL_LIGHT0, GL_POSITION, lightPos); glLightfv(GL_LIGHT0, GL_DIFFUSE, pink); light = rtsCreateLight(GL_LIGHT0, lightPos, 1000.0); glLightfv(GL_LIGHT1, GL_POSITION, lightPos2); glLightfv(GL_LIGHT1, GL_DIFFUSE, greeny); light2 = rtsCreateLight(GL_LIGHT1, lightPos2, 1000.0); object = rtsCreateObject(objectPos, 1.0, renderObject, NULL, 100); object2 = rtsCreateObject(objectPos2, 1.0, renderObject2, NULL, 100); rtsAddLightToScene(scene, light); rtsAddObjectToLight(light, object); rtsAddObjectToLight(light, object2); rtsAddLightToScene(scene, light2); rtsAddObjectToLight(light2, object); rtsAddObjectToLight(light2, object2); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); initMenu(); glNewList(DL_TORUS, GL_COMPILE); glutSolidTorus(0.2, 0.8, 10, 10); glEndList(); glNewList(DL_CUBE, GL_COMPILE); glutSolidCube(1.0); glEndList(); glNewList(DL_DOUBLE_TORUS, GL_COMPILE); glCallList(DL_TORUS); glRotatef(90.0, 0.0, 1.0, 0.0); glCallList(DL_TORUS); glRotatef(-90.0, 0.0, 1.0, 0.0); glEndList(); glNewList(DL_SPHERE, GL_COMPILE); glutSolidSphere(1.5, 20, 20); glEndList(); glutMainLoop(); return 0; /* ANSI C requires main to return int. */ } #else int main(int argc, char** argv) { fprintf(stderr, "This program requires the new tesselator API in GLU 1.2.\n"); fprintf(stderr, "Your GLU library does not support this new interface, sorry.\n"); return 0; } #endif /* GLU_VERSION_1_2 */