/* hiddenline.c - by Tom McReynolds, SGI */ /* Line Rendering: Hidden line techniques */ #include #include #include /* ** Create a single component texture map */ GLfloat *make_texture(int maxs, int maxt) { int s, t; static GLfloat *texture; texture = (GLfloat *)malloc(maxs * maxt * sizeof(GLfloat)); for(t = 0; t < maxt; t++) { for(s = 0; s < maxs; s++) { texture[s + maxs * t] = ((s >> 4) & 0x1) ^ ((t >> 4) & 0x1); } } return texture; } enum {FILL, WIRE, BACKFACE, FRONTLINES, DEPTH, STENCIL, FAT_STENCIL}; int rendermode = FILL; void menu(int selection) { rendermode = selection; glutPostRedisplay(); } /* geometry display list names */ enum {SPHERE = 1, CONE, FLOOR, WALLS}; void drawscene(void) { glEnable(GL_TEXTURE_2D); glCallList(FLOOR); glDisable(GL_TEXTURE_2D); glCallList(WALLS); glPushMatrix(); glTranslatef(-40.f, -50.f, -400.f); glCallList(SPHERE); glPopMatrix(); glPushMatrix(); glTranslatef(20.f, -100.f, -420.f); glCallList(CONE); glPopMatrix(); } /* do hidden line removal on an object in the scene */ /* use display lists to represent objects */ void hiddenlineobj(GLint dlist) { GLboolean tex2d; glGetBooleanv(GL_TEXTURE_2D, &tex2d); if(tex2d) glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glColor3f(.7f, .7f, .7f); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 0, 0); /* clear stencil for this object */ glCallList(dlist); /* draw filled object in depth buffer */ glEnable(GL_LIGHTING); if(tex2d) glEnable(GL_TEXTURE_2D); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); /* turn off color */ glDisable(GL_DEPTH_TEST); /* turn off depth */ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glCallList(dlist); /* draw lines into stencil buffer */ glStencilFunc(GL_EQUAL, 1 , 1); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glCallList(dlist); /* use lines in stencil to stencil out solid pgons */ /* clean up state */ glDisable(GL_STENCIL_TEST); glDepthFunc(GL_LESS); } void drawscenestencil(void) { glEnable(GL_TEXTURE_2D); hiddenlineobj(FLOOR); glDisable(GL_TEXTURE_2D); hiddenlineobj(WALLS); glPushMatrix(); glTranslatef(-40.f, -50.f, -400.f); hiddenlineobj(SPHERE); glPopMatrix(); glPushMatrix(); glTranslatef(20.f, -100.f, -420.f); hiddenlineobj(CONE); glPopMatrix(); } /* Called when window needs to be redrawn */ void redraw(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); switch(rendermode) { case FILL: drawscene(); break; case WIRE: /* basic wireframe mode */ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); drawscene(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case BACKFACE: /* use backface culling to clean things up */ glEnable(GL_CULL_FACE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); drawscene(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glDisable(GL_CULL_FACE); break; case FRONTLINES: /* use polygon mode line on front, fill on back */ glPolygonMode(GL_FRONT, GL_LINE); drawscene(); glPolygonMode(GL_FRONT, GL_FILL); break; case DEPTH: /* use depth buffer to remove hidden lines */ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); drawscene(); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDepthFunc(GL_LEQUAL); drawscene(); glDepthFunc(GL_LESS); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case STENCIL: /* use stencil to remove hidden lines */ glEnable(GL_CULL_FACE); drawscenestencil(); glDisable(GL_CULL_FACE); break; case FAT_STENCIL: /* use stencil with fat lines to fix edges */ glLineWidth(2.f); glEnable(GL_CULL_FACE); drawscenestencil(); glDisable(GL_CULL_FACE); glLineWidth(1.f); break; } if(glGetError()) /* to catch programming errors; should never happen */ printf("Oops! I screwed up my OpenGL calls somewhere\n"); glFlush(); /* high end machines may need this */ } /* ARGSUSED1 */ void key(unsigned char key, int x, int y) { if(key == '\033') exit(0); } const int TEXDIM = 256; /* Parse arguments, and set up interface between OpenGL and window system */ main(int argc, char *argv[]) { /* material properties for objects in scene */ static GLfloat wall_mat[] = {1.f, 1.f, 1.f, 1.f}; GLfloat *tex; static GLfloat lightpos[] = {50.f, 50.f, -320.f, 1.f}; static GLfloat sphere_mat[] = {1.f, .5f, 0.f, 1.f}; static GLfloat cone_mat[] = {0.f, .5f, 1.f, 1.f}; GLUquadricObj *sphere, *cone, *base; glutInit(&argc, argv); glutInitWindowSize(512, 512); glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL); (void)glutCreateWindow("hidden line removal survey"); glutDisplayFunc(redraw); glutKeyboardFunc(key); glutCreateMenu(menu); glutAddMenuEntry("Solid Fill", FILL); glutAddMenuEntry("Wireframe", WIRE); glutAddMenuEntry("Backface Culling", BACKFACE); glutAddMenuEntry("Frontface Lines", FRONTLINES); glutAddMenuEntry("Depth Test", DEPTH); glutAddMenuEntry("Stencil", STENCIL); glutAddMenuEntry("Stencil: Fat Lines", FAT_STENCIL); glutAttachMenu(GLUT_RIGHT_BUTTON); /* draw a perspective scene */ glMatrixMode(GL_PROJECTION); glFrustum(-100., 100., -100., 100., 320., 640.); glMatrixMode(GL_MODELVIEW); /* turn on features */ glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glClearColor(.7f, .7f, .7f, .7f); glCullFace(GL_BACK); /* place light 0 in the right place */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* remove back faces to speed things up */ glCullFace(GL_BACK); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glNewList(SPHERE, GL_COMPILE); /* make display lists for sphere and cone; for efficiency */ sphere = gluNewQuadric(); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, sphere_mat); gluSphere(sphere, 50.f, 20, 20); gluDeleteQuadric(sphere); glEndList(); glNewList(CONE, GL_COMPILE); cone = gluNewQuadric(); base = gluNewQuadric(); glPushMatrix(); glRotatef(-90.f, 1.f, 0.f, 0.f); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cone_mat); gluQuadricOrientation(base, GLU_INSIDE); gluDisk(base, 0., 40., 20, 1); gluCylinder(cone, 40., 0., 120., 20, 20); glPopMatrix(); gluDeleteQuadric(cone); gluDeleteQuadric(base); glEndList(); glNewList(FLOOR, GL_COMPILE); /* ** Note: wall verticies are ordered so they are all front facing ** this lets me do back face culling to speed things up. */ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, wall_mat); /* floor */ /* ** Since we want to turn texturing on for floor only, we have to ** make floor a separate glBegin()/glEnd() sequence. You can't ** turn texturing on and off between begin and end calls */ glBegin(GL_QUADS); glNormal3f(0.f, 1.f, 0.f); glTexCoord2i(0, 0); glVertex3f(-100.f, -100.f, -320.f); glTexCoord2i(1, 0); glVertex3f( 100.f, -100.f, -320.f); glTexCoord2i(1, 1); glVertex3f( 100.f, -100.f, -520.f); glTexCoord2i(0, 1); glVertex3f(-100.f, -100.f, -520.f); glEnd(); glEndList(); /* walls */ glNewList(WALLS, 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); /* 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(); /* load pattern for current 2d texture */ tex = make_texture(TEXDIM, TEXDIM); glTexImage2D(GL_TEXTURE_2D, 0, 1, TEXDIM, TEXDIM, 0, GL_RED, GL_FLOAT, tex); free(tex); glutMainLoop(); return 0; /* ANSI C requires main to return int. */ }