/* modified by: akbar A. bugs: there is a tea pot inside of a teapot. fix it fixed it. thanks goes out to those guys at sgi. you guys are awsome. supports being able to move your objects which are being shadowed. hit 't' and 'T' and that will do it for the x axis note: the changes are quick and crappy. i have a fast machine also i tried to make this as understandable as possible */ #include #include #include #include #include #include "stuff.h" void draw_torus(int numc,int numt, int scale); void make_shadow_matrix(GLfloat shadow_matrix[4][4], GLfloat groundplane[4], GLfloat lightpos[4]); void findplane(float plane[4], float v0[3], float v1[3], float v2[3]); void reshape(int wid, int ht); void motion(int x, int y); void mouse(int button, int state, int x, int y); void draw_shadowed(GLint shadowed, GLint shadowers, GLfloat (*xform)[4]); void redraw_shadow_black(void); void key(unsigned char key, int x, int y); void menu(int choice); int main(int argc, char *argv[]); void idle(void); void init(); void draw_teapot_wireframe(); int dblbuf = GL_TRUE; GLfloat floorplane[4], lwallplane[4]; GLfloat slope_floor_plane[4]; GLuint torus; GLuint glut_sphere; // default is on off int wire_mode = 1; // 1 is false 0 is true ;) enum {WIRE_MODE_ON, WIRE_MODE_OFF}; enum {NONE, SHADOW_BLACK, SHADOW}; enum {BOUNCE_ONN, BOUNCE_OFF}; enum {MOVING_SPHERE}; enum {MELT_UP, MELT_DOWN}; int melt_mode = 0; int winwid, winht; float color_abuse = 0; float tea_x = 0; float tea_y = 0; float tea_z = 0; GLUquadricObj *sphere, *cone, *base; GLfloat v0[3], v1[3], v2[3]; GLfloat leftwallshadow[4][4]; GLfloat floorshadow[4][4]; GLfloat lightpos[] = {50.f, 50.f, -320.f, 1.f}; int active; void draw_teapot_wireframe() { glColor3f(1.0 + color_abuse, 0.0, 0.0); glPushMatrix(); glTranslatef(15.f + tea_x, -10.f, -345.f); //glutWireTeapot(15); glPopMatrix(); } void init() { /* draw a perspective scene */ glMatrixMode(GL_PROJECTION); glFrustum(-100., 100., -100., 100., 320., 640.); glMatrixMode(GL_MODELVIEW); // power up =) glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); /* make shadow matricies */ /* 3 points on floor */ v0[X] = -100.f; v0[Y] = -100.f; v0[Z] = -320.f; v1[X] = 100.f; v1[Y] = -100.f; v1[Z] = -320.f; v2[X] = 100.f; v2[Y] = -100.f; v2[Z] = -520.f; findplane(floorplane, v0, v1, v2); make_shadow_matrix(floorshadow, floorplane, lightpos); // create matrix to project with /* 3 points on left wall */ v0[X] = -100.f; v0[Y] = -100.f; v0[Z] = -320.f; v1[X] = -100.f; v1[Y] = -100.f; v1[Z] = -520.f; v2[X] = -100.f; v2[Y] = 100.f; v2[Z] = -520.f; findplane(lwallplane, v0, v1, v2); make_shadow_matrix(leftwallshadow, lwallplane, lightpos); // create matrix to project with glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* remove back faces to speed things up */ glCullFace(GL_BACK); glNewList(WALLS, GL_COMPILE); glColor3f(1.f, 1.f, 1.f); glBegin(GL_QUADS); /* right wall */ glNormal3f(-1.f, 0.f, 0.f); glVertex3f( 100.f, -100.f, -320.f); glVertex3f( 100.f, 100.f, -320.f); glVertex3f( 100.f, 100.f, -520.f); glVertex3f( 100.f, -100.f, -520.f); /* ceiling */ glNormal3f(0.f, -1.f, 0.f); glVertex3f(-100.f, 100.f, -320.f); glVertex3f(-100.f, 100.f, -520.f); glVertex3f( 100.f, 100.f, -520.f); glVertex3f( 100.f, 100.f, -320.f); /* back wall */ glNormal3f(0.f, 0.f, 1.f); glVertex3f(-100.f, -100.f, -520.f); glVertex3f( 100.f, -100.f, -520.f); glVertex3f( 100.f, 100.f, -520.f); glVertex3f(-100.f, 100.f, -520.f); glEnd(); glEndList(); glNewList(SPHERE, GL_COMPILE); sphere = gluNewQuadric(); glPushMatrix(); glTranslatef(60.f, -50.f, -360.f); gluSphere(sphere, 20.f, 20, 20); glPopMatrix(); glEndList(); glNewList(TEAPOT, GL_COMPILE); glutWireTeapot(10); glEndList(); /* glNewList(TEAPOT, GL_COMPILE); glColor3f(1.0 + color_abuse, 0.0, 0.0); glPushMatrix(); glTranslatef(15.f, -10.f, -345.f); glutWireTeapot(15); glPopMatrix(); glEndList(); */ glNewList(LIGHT, GL_COMPILE); sphere = gluNewQuadric(); glColor3f(0.50, 0.50, 0.50); gluSphere(sphere, 5.f, 20, 20); gluDeleteQuadric(sphere); glEndList(); glNewList(CONE, GL_COMPILE); cone = gluNewQuadric(); base = gluNewQuadric(); glPushMatrix(); glTranslatef(-40.f, -40.f, -400.f); glRotatef(-90.f, 1.f, 0.f, 0.f); gluDisk(base, 0., 20., 20, 1); gluCylinder(cone, 20., 0., 60., 20, 20); gluDeleteQuadric(cone); gluDeleteQuadric(base); glPopMatrix(); glEndList(); /*********************************/ /* cram them all into one!!!! */ glNewList(SHADOWERS, GL_COMPILE); glCallList(CONE); glCallList(SPHERE); glEndList(); /*********************************/ glNewList(FLOOR, GL_COMPILE); glBegin(GL_QUADS); glNormal3f(0.f, 1.f, 0.f); glVertex3f(-100.f, -100.f, -320.f); glVertex3f( 100.f, -100.f, -320.f); glVertex3f( 100.f, -100.f, -520.f); glVertex3f(-100.f, -100.f, -520.f); glEnd(); glEndList(); glNewList(LEFTWALL, GL_COMPILE); glBegin(GL_QUADS); /* left wall */ glNormal3f(1.f, 0.f, 0.f); glVertex3f(-100.f, -100.f, -320.f); glVertex3f(-100.f, -100.f, -520.f); glVertex3f(-100.f, 100.f, -520.f); glVertex3f(-100.f, 100.f, -320.f); glEnd(); glEndList(); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); } void idle(void) { color_abuse += 0.0025; if(color_abuse >= 1) { color_abuse = color_abuse -1; } glutDisplayFunc(redraw_shadow_black); } /* create the _SHADOW matrix. the new matrix is in the first argument in the function */ void make_shadow_matrix(GLfloat shadow_matrix[4][4], GLfloat groundplane[4], GLfloat lightpos[4]) { GLfloat dot; /* find dot product between light position vector and ground plane normal */ dot = groundplane[X] * lightpos[X] + groundplane[Y] * lightpos[Y] + groundplane[Z] * lightpos[Z] + groundplane[W] * lightpos[W]; shadow_matrix[0][0] = dot - lightpos[X] * groundplane[X]; shadow_matrix[1][0] = 0.f - lightpos[X] * groundplane[Y]; shadow_matrix[2][0] = 0.f - lightpos[X] * groundplane[Z]; shadow_matrix[3][0] = 0.f - lightpos[X] * groundplane[W]; shadow_matrix[X][1] = 0.f - lightpos[Y] * groundplane[X]; shadow_matrix[1][1] = dot - lightpos[Y] * groundplane[Y]; shadow_matrix[2][1] = 0.f - lightpos[Y] * groundplane[Z]; shadow_matrix[3][1] = 0.f - lightpos[Y] * groundplane[W]; shadow_matrix[X][2] = 0.f - lightpos[Z] * groundplane[X]; shadow_matrix[1][2] = 0.f - lightpos[Z] * groundplane[Y]; shadow_matrix[2][2] = dot - lightpos[Z] * groundplane[Z]; shadow_matrix[3][2] = 0.f - lightpos[Z] * groundplane[W]; shadow_matrix[X][3] = 0.f - lightpos[W] * groundplane[X]; shadow_matrix[1][3] = 0.f - lightpos[W] * groundplane[Y]; shadow_matrix[2][3] = 0.f - lightpos[W] * groundplane[Z]; shadow_matrix[3][3] = dot - lightpos[W] * groundplane[W]; } /* find the plane equation given 3 points */ void findplane(float plane[4], float v0[3], float v1[3], float v2[3]) { float vec0[3], vec1[3]; /* need 2 vectors to find cross product */ vec0[X] = v1[X] - v0[X]; vec0[Y] = v1[Y] - v0[Y]; vec0[Z] = v1[Z] - v0[Z]; vec1[X] = v2[X] - v0[X]; vec1[Y] = v2[Y] - v0[Y]; vec1[Z] = v2[Z] - v0[Z]; /* find cross product to get A, B, and C of plane equation */ plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y]; plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]); plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X]; plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]); } void reshape(int wid, int ht) { winwid = wid; winht = ht; glViewport(0, 0, wid, ht); } void motion(int x, int y) { switch(active) { case MOVE_LIGHT_XY: { lightpos[X] = 100.f + (x - winwid) * 200.f/winwid; lightpos[Y] = -100.f + (winht - y) * 200.f/winht; make_shadow_matrix(leftwallshadow, lwallplane, lightpos); make_shadow_matrix(floorshadow, floorplane, lightpos); /* place light 0 in the right place */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); glutPostRedisplay(); break; } case MOVE_LIGHT_Z: lightpos[Z] = -320.f - (winht - y) * 200.f/winht; make_shadow_matrix(leftwallshadow, lwallplane, lightpos); make_shadow_matrix(floorshadow, floorplane, lightpos); /* place light 0 in the right place */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); glutPostRedisplay(); break; } } void mouse(int button, int state, int x, int y) { /* hack for 2 button mouse */ if (button == GLUT_LEFT_BUTTON && glutGetModifiers() & GLUT_ACTIVE_SHIFT) button = GLUT_MIDDLE_BUTTON; if(state == GLUT_DOWN) switch(button) { case GLUT_LEFT_BUTTON: /* move the light */ active = MOVE_LIGHT_XY; motion(x, y); break; case GLUT_MIDDLE_BUTTON: active = MOVE_LIGHT_Z; motion(x, y); break; } } void draw_teapot() { // this is drawing the shadow glPushMatrix(); glTranslatef(15.f +tea_x, -10.f +tea_y, -345.f); glCallList(TEAPOT); glPopMatrix(); } /* method: draw the ground plane first, then draw the projected polygons with the z-buffer off, then render the rest of the geometry as usual. by doing this we make sure that there is none of that nasty blending of the two objects. sometimes two planes are the _SAME_, and the driver doesn't know which one to draw so it gets all nasty. */ void draw_shadowed(GLint shadowed, GLint shadowers, GLfloat (*xform)[4]) { glEnable(GL_STENCIL_TEST); //draw shadowed polygon lit and into stencil buffer glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glCallList(shadowed); glDisable(GL_DEPTH_TEST); // to avoid z-fighting glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); glPushMatrix(); glMultMatrixf((GLfloat *) xform );// project to get correct place to start drawing are anything (usually these are shadows) draw_teapot(); glCallList(shadowers); // call all that extra drawing code and redraw it with this new locations glPopMatrix(); /* draw shadowed polygon again, unlit where stencil is cleared */ glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDisable(GL_LIGHTING); /* draw shadow with only ambient lighting */ glStencilFunc(GL_EQUAL, 0, 1); /*only draw where stencil cleared by shadow*/ glColor3f(0.f, 0.f, 0.f); glCallList(shadowed); glEnable(GL_LIGHTING); glDepthFunc(GL_LESS); glDisable(GL_STENCIL_TEST); } void redraw_shadow_black(void) { //glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);//|GL_STENCIL_BUFFER_BIT); // i am not having any problems on my geforce driver withouth clearing the stencil buffer // aren;t si supposed to clear it?? glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); /* Floor with Shadow */ glColor3f(0.0f, 1.f, 1.f); //draw shadow in floor draw_shadowed(FLOOR, SHADOWERS, floorshadow); /* draw shadow on the floor */ glColor3f(1.f, 1.f, 1.f); // draw shadow on the left wall draw_shadowed(LEFTWALL, SHADOWERS,leftwallshadow);/* draw shadowed floor*/ /* Unshadowed Surfaces */ glCallList(WALLS); glPushMatrix(); glTranslatef(lightpos[X], lightpos[Y], lightpos[Z]); glDisable(GL_LIGHTING); glColor3f(.7f, .1f, .7f); glCallList(LIGHT); glEnable(GL_LIGHTING); glPopMatrix(); draw_teapot_wireframe(); draw_teapot(); glColor3f(1.0, 0.75, 0.0); glCallList(CONE); glColor3f(0.45f - color_abuse, .25f - color_abuse, 0.10f + color_abuse); glPushMatrix(); glCallList(SPHERE); glPopMatrix(); glLoadIdentity(); if(dblbuf) glutSwapBuffers(); else glFlush(); } /*ARGSUSED1*/ void key(unsigned char key, int x, int y) { switch(key) { case 'n': case 'N': glutPostRedisplay(); break; case 'b': case 'B': glutDisplayFunc(redraw_shadow_black); glutPostRedisplay(); break; case 't': tea_x += 15.0f; glutDisplayFunc(redraw_shadow_black); glutPostRedisplay(); break; case 'T': tea_x -= 15.0f; glutDisplayFunc(redraw_shadow_black); glutPostRedisplay(); break; case '\033': exit(0); } } void menu(int choice) { switch(choice) { case NONE: key('n', 0, 0); break; case SHADOW_BLACK: key('b', 0, 0); break; case SHADOW: key('s', 0, 0); break; //WIRE_MODE_ON, WIRE_MODE_OFF } } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitWindowSize(512, 512); if(argc > 1) { char *args = argv[1]; int done = GL_FALSE; while(!done) { switch(*args) { case 's': /* single buffer */ printf("Single Buffered\n"); dblbuf = GL_FALSE; break; case '-': /* do nothing */ break; case 0: done = GL_TRUE; break; } args++; } } if(dblbuf) glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL|GLUT_DOUBLE); else glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL); (void)glutCreateWindow("moving shadows, hit 't' and 'T'"); init(); glutKeyboardFunc(key); glutMouseFunc(mouse); glutMotionFunc(motion); glutReshapeFunc(reshape); glutIdleFunc(idle); glutCreateMenu(menu); //glutAddMenuEntry("No Shadows", NONE); //glutAddMenuEntry("Black Shadows", SHADOW_BLACK); //glutAttachMenu(GLUT_RIGHT_BUTTON); glutMainLoop(); return 0; }