(original) (raw)

/* edge: draw a spinning tetrahedron with edges; a simple Opengl program. testing edge highlighting To make this work, you offset the polygons to be behind the lines, instead of offsetting the lines to be in front of the polygons, since lines aren't affected by glPolygonOffsetEXT in Opengl 1.0 The following works pretty well, but not perfect: occasional edge gaps or polygon intersection on angled polygons edge -pfill -edge -offset 1 1e-6 this looks the same edge -pfill -pline -offset 1 1e-6 the following shows that glPolygonOffsetEXT works for coincident polygons too edge -pfill -pcoin -offset 1 1e-6 These numbers (1 and 1e-6) might not work for all geometric models or all implementations of OpenGL; I've mostly tested just with this tetrahedron. compile with cc -O2 -mips2 -o edge edge.c -lGLU -lGL -lX11 -lm see "man glintro" and "man glpolygonoffsetext" for info on polygon offset Paul Heckbert 22 July 1996, 19 Oct 1996 ph@cs.cmu.edu */ #include #include #include #include /* Opengl graphics library subroutine prototypes */ #include /* includes Xlib.h and other X stuff too */ #include /* X keyboard definitions */ static Display *display; static Window window; /* window number */ /* these options can be used in combination */ static int pfill = 0; /* draw polygons with glPolygonMode(...GL_FILL)? */ static int pcoin = 0; /* draw polygons filled and coincident? */ static int pline = 0; /* draw polygons with glPolygonMode(...GL_LINE)? */ static int edge = 0; /* draw edges using glBegin(GL_LINES)? */ static float scale = 0., offset = 0.; /* for polygon offset */ static float gscale = 1.; /* global scale of object */ #define ROTANGLE .25 #define NV 4 #define NF 4 /* tetrahedron */ static float vert[4][3] = { /* coordinates of the four vertices */ -1., -1., -1., /* x, y, and z of vert[0] */ 1., 1., -1., /* vert[1], etc */ 1., -1., 1., -1., 1., 1., }; static short face[4][3] = { /* indices of vertices making up each face */ 0, 1, 2, 1, 3, 2, 0, 3, 1, 0, 2, 3, }; static float color[4][3] = { /* colors with which to draw faces */ .3, 0., 0., /* red */ 0., .3, 0., /* green */ .2, .2, .5, /* blue */ .2, .1, 0., /* brown */ }; void draw_faces(int colored) { int i, f; for (f=0; f<nf; f++)="" {="" if="" (colored)="" glcolor3fv(color[f]);="" glbegin(gl_polygon);="" for="" (i="0;" i<3;="" i++)="" glvertex3fv(vert[face[f][i]]);="" glend();="" }="" void="" draw_edge(int="" i,="" int="" j)="">=j) return; /* draw each edge just once */ glVertex3fv(vert[i]); glVertex3fv(vert[j]); } void draw_edges() { int i, f; glColor3f(1., 1., 1.); /* make edges white */ glBegin(GL_LINES); for (f=0; f<nf; f++)="" {="" draw_edge(face[f][0],="" face[f][1]);="" draw_edge(face[f][1],="" face[f][2]);="" draw_edge(face[f][2],="" face[f][0]);="" }="" glend();="" void="" error_check(int="" loc)="" glenum="" e;="" while="" ((e="glGetError())" !="GL_NO_ERROR)" printf("error:="" %s="" at="" location="" %d\n",="" gluerrorstring(e),="" loc);="" offset_init()="" char="" *s;="" s="(char" *)glgetstring(gl_extensions);="" assert(s);="" *="" printf("[%s]\n",="" s);="" if="" (!strstr(s,="" "gl_ext_polygon_offset"))="" fprintf(stderr,="" "warning:="" polygon="" offset="" not="" supported\n");="" glenable(gl_polygon_offset_ext);="" assert(glisenabled(gl_polygon_offset_ext));="" #ifdef="" debug="" offset_check(char="" *str)="" float="" s,="" o;="" printf("%s\n",="" str);="" printf("enabled="%d\n"," glisenabled(gl_polygon_offset_ext));="" glgetfloatv(gl_polygon_offset_factor_ext,="" &s);="" glgetfloatv(gl_polygon_offset_bias_ext,="" &o);="" printf("s="%g" o="%g\n"," o);="" #endif="" draw_tet()="" draw="" edges="" glpolygonoffsetext(0.,="" 0.);="" (edge)="" draw_edges();="" (pline)="" glcolor3f(1.,="" 1.,="" 1.);="" make="" white="" glpolygonmode(gl_front_and_back,="" gl_line);="" draw_faces(0);="" polygons="" to="" be="" behind="" the="" glpolygonoffsetext(scale,="" offset);="" (pfill)="" gl_fill);="" draw_faces(1);="" (pcoin)="" glcolor3f(0.,="" scale=""> 1e-2 or offset > 1e-6 then this second * face is drawn in front of the first. */ } } } void redisplay() { /* clear screen and redraw object */ glClearColor(0., 0., 0., 0.); /* set clear color to black */ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); /* clear color buffer to color specified above, * and clear depth buffer (z buffer) also, for hidden surface removal */ draw_tet(); /* draw tetrahedron */ glXSwapBuffers(display, window); /* double buffering: swap displayed picture memory buffer with * work buffer */ error_check(1); } /* ====== begin generic code - you shouldn't have to modify this much ====== */ /* Attributes for a normal, 24-bit, z-buffered and double-buffered window */ int gl_double_attrs[] = {GLX_RGBA, GLX_DEPTH_SIZE, 12, GLX_DOUBLEBUFFER, None}; static int is_map_notify(Display *display, XEvent *event, char *arg) { return event->type==MapNotify && event->xmap.window==(Window)arg; } /* gl_create_window: create an X window for opengl output * with corner position (x,y) and specified width and height. */ void gl_create_window(int x, int y, int width, int height) { XSetWindowAttributes winattr; XEvent event; XVisualInfo *visinfo; Colormap colormap; /* colormap for window */ GLXContext context; display = XOpenDisplay(0); /* get a double-buffered "Visual" */ visinfo = glXChooseVisual(display, DefaultScreen(display), gl_double_attrs); if (!visinfo) { fprintf(stderr, "gl_create_window: Can't create window with the requested attributes\n"); exit(1); } colormap = XCreateColormap(display, RootWindow(display, visinfo->screen), visinfo->visual, AllocNone); /* * note: if you try to create a window with a 24 bit visual when the * default visual is 8 bits (or more precisely, when the parent window * has a different visual or depth) and you don't explicitly set the * colormap and border, you will die with a BadMatch error. * You can thank the brain-damaged designers of X Windows for this. */ winattr.colormap = colormap; winattr.border_pixel = 0; winattr.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask; /* solicit key press events as well as basic ones */ /* create the subwindow */ window = XCreateWindow(display, /*parent*/ RootWindow(display, DefaultScreen(display)), /*origin*/ x, y, /*width,height*/ width, height, /*borderwidth*/ 0, /*depth*/ visinfo->depth, /*class*/ InputOutput, visinfo->visual, /*window attribute mask*/ CWBorderPixel | CWColormap | CWEventMask, &winattr); if (x>=0) { XSizeHints size; size.x = x; size.y = y; size.flags = USPosition; /* window manager obeys USPosition requests, but not PPosition */ XSetNormalHints(display, window, &size); } /* tell window manager to draw the window */ XMapWindow(display, window); /* Wait for MapNotify event when the window is first drawn */ XIfEvent(display, &event, is_map_notify, (char *)window); /* Create a graphics context if it hasn't been done before */ /* Bind the X window to an OpenGL context */ context = glXCreateContext(display, visinfo, /*context share list*/ 0, /*direct rendering?*/ GL_TRUE); assert(context); assert(glXMakeCurrent(display, window, context)); glEnable(GL_DEPTH_TEST); /* turn on z-buffering */ } /* ====== end generic code ================================================= */ static int running = 1; void handle_event() { XEvent event; XNextEvent(display, &event); switch (event.type) { case Expose: /* redraw window */ /* get rid of all other Expose events on the queue */ while (XCheckTypedEvent(display, Expose, &event)); redisplay(); break; case KeyPress: { char buf[10]; KeySym keysym; XLookupString(&event.xkey, buf, sizeof buf - 1, &keysym, 0); switch (keysym) { case 's': running = !running; break; case 'f': glRotatef(ROTANGLE, 0., 0., 1.); redisplay(); break; case 'b': glRotatef(-ROTANGLE, 0., 0., 1.); redisplay(); break; case 'q': case XK_Escape: exit(0); } } break; } } static char usage[] = "Usage: edge [-pfill|-pline|-pcoin|-edge|-offset SC OFF]\n"; main(int argc, char **argv) { int i; for (i=1; i</nf;></nf;>