/* $Revision: 1.4 $ */ /* compile: cc -o glxdino glxdino.c -lGLU -lGL -lX11 */ #include #include #include #include /* for cos() and sin() */ #include /* this includes the necessary X and gl.h headers */ #include typedef enum { RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE, LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE, DINOSAUR } displayLists; Display *dpy; Window win; GLfloat angle = 0.5; /* in radians */ GLboolean doubleBuffer = GL_TRUE, iconic = GL_FALSE, keepAspect = GL_FALSE; int W = 300, H = 300; XSizeHints sizeHints = {0}; GLdouble bodyWidth = 2.0; int dblBuf[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, None}; GLfloat lightGreen[] = {0.1, 1.0, 0.3}, darkGreen[] = {0.0, 0.4, 0.1}, anotherGreen[] = {0.0, 0.8, 0.1}, bloodRed[] = {1.0, 0.0, 0.1}; GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5}, {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16}, {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2}, {1, 2}}; GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9}, {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10}, {13, 9}, {11, 11}, {9, 11}}; GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0}, {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7}}; GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15}, {9.6, 15.25}, {9, 15.25}}; void fatalError(char *message) { fprintf(stderr, "glxdino: %s\n", message); exit(1); } void extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize, GLdouble thickness, GLuint side, GLuint edge, GLuint whole, GLfloat * sideColor, GLfloat * edgeColor) { static GLUtriangulatorObj *tobj = NULL; GLdouble vertex[3]; int i; if (tobj == NULL) { tobj = gluNewTess(); /* create and initialize a GLU polygon tesselation object */ gluTessCallback(tobj, GLU_BEGIN, glBegin); gluTessCallback(tobj, GLU_VERTEX, glVertex2fv); /* semi-tricky */ gluTessCallback(tobj, GLU_END, glEnd); } glNewList(side, GL_COMPILE); gluBeginPolygon(tobj); for (i = 0; i < dataSize / (2 * sizeof(GLfloat)); i++) { vertex[0] = data[i][0]; vertex[1] = data[i][1]; vertex[2] = 0; gluTessVertex(tobj, vertex, &data[i]); } gluEndPolygon(tobj); glEndList(); glNewList(edge, GL_COMPILE); glBegin(GL_QUAD_STRIP); for (i = 0; i < dataSize / (2 * sizeof(GLfloat)); i++) { vertex[0] = data[i][0]; vertex[1] = data[i][1]; vertex[2] = 0; glVertex3dv(vertex); vertex[2] = thickness; glVertex3dv(vertex); } vertex[0] = data[0][0]; vertex[1] = data[0][1]; vertex[2] = 0; glVertex3dv(vertex); vertex[2] = thickness; glVertex3dv(vertex); glEnd(); glEndList(); glNewList(whole, GL_COMPILE); glColor3fv(edgeColor); glFrontFace(GL_CW); glCallList(edge); glColor3fv(sideColor); glCallList(side); glPushMatrix(); glTranslatef(0.0, 0.0, thickness); glFrontFace(GL_CCW); glCallList(side); glPopMatrix(); glEndList(); } void makeDinosaur(void) { GLfloat bodyWidth = 3.0; extrudeSolidFromPolygon(body, sizeof(body), bodyWidth, BODY_SIDE, BODY_EDGE, BODY_WHOLE, lightGreen, darkGreen); extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4, ARM_SIDE, ARM_EDGE, ARM_WHOLE, darkGreen, anotherGreen); extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2, LEG_SIDE, LEG_EDGE, LEG_WHOLE, darkGreen, anotherGreen); extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2, EYE_SIDE, EYE_EDGE, EYE_WHOLE, bloodRed, bloodRed); glNewList(DINOSAUR, GL_COMPILE); glCallList(BODY_WHOLE); glPushMatrix(); glTranslatef(0.0, 0.0, -0.1); glCallList(EYE_WHOLE); glTranslatef(0.0, 0.0, bodyWidth + 0.1); glCallList(ARM_WHOLE); glCallList(LEG_WHOLE); glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4); glCallList(ARM_WHOLE); glTranslatef(0.0, 0.0, -bodyWidth / 4); glCallList(LEG_WHOLE); glPopMatrix(); glEndList(); } void redraw(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glCallList(DINOSAUR); if (doubleBuffer) glXSwapBuffers(dpy, win); /* buffer swap does implicit glFlush */ else glFlush(); /* explicit flush for single buffered case */ } void main(int argc, char **argv) { XVisualInfo *vi; Colormap cmap; XSetWindowAttributes swa; XWMHints *wmHints; Atom wmDeleteWindow; GLXContext cx; XEvent event; GLboolean needRedraw = GL_FALSE, recalcModelView = GL_TRUE; char *display = NULL, *geometry = NULL; int dummy, flags, x, y, width, height, lastX, i; /*** (1) process normal X command line arguments ***/ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-geometry")) { i++; if (i >= argc) fatalError("follow -geometry option with geometry parameter"); geometry = argv[i]; } else if (!strcmp(argv[i], "-display")) { i++; if (i >= argc) fatalError("follow -display option with display parameter"); display = argv[i]; } else if (!strcmp(argv[i], "-iconic")) iconic = GL_TRUE; else if (!strcmp(argv[i], "-keepaspect")) keepAspect = GL_TRUE; else fatalError("bad option"); } /*** (2) open a connection to the X server ***/ dpy = XOpenDisplay(display); if (dpy == NULL) fatalError("could not open display"); /*** (3) make sure OpenGL's GLX extension supported ***/ if (!glXQueryExtension(dpy, &dummy, &dummy)) fatalError("X server has no OpenGL GLX extension"); /*** (4) find an appropriate visual ***/ /* find an OpenGL-capable RGB visual with depth buffer */ vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf); if (vi == NULL) { vi = glXChooseVisual(dpy, DefaultScreen(dpy), &dblBuf[1]); if (vi == NULL) fatalError("no RGB visual with depth buffer"); doubleBuffer = GL_FALSE; } if (vi->class != TrueColor) fatalError("TrueColor visual required for this program"); /*** (5) create an OpenGL rendering context ***/ /* create an OpenGL rendering context */ cx = glXCreateContext(dpy, vi, /* no sharing of display lists */ None, /* direct rendering if possible */ GL_TRUE); if (cx == NULL) fatalError("could not create rendering context"); /*** (6) create an X window with the selected visual and right properties ***/ flags = XParseGeometry(geometry, &x, &y, (unsigned int *) &width, (unsigned int *) &height); if (WidthValue & flags) { sizeHints.flags |= USSize; sizeHints.width = width; W = width; } if (HeightValue & flags) { sizeHints.flags |= USSize; sizeHints.height = height; H = height; } if (XValue & flags) { if (XNegative & flags) x = DisplayWidth(dpy, DefaultScreen(dpy)) + x - sizeHints.width; sizeHints.flags |= USPosition; sizeHints.x = x; } if (YValue & flags) { if (YNegative & flags) y = DisplayHeight(dpy, DefaultScreen(dpy)) + y - sizeHints.height; sizeHints.flags |= USPosition; sizeHints.y = y; } if (keepAspect) { sizeHints.flags |= PAspect; sizeHints.min_aspect.x = sizeHints.max_aspect.x = W; sizeHints.min_aspect.y = sizeHints.max_aspect.y = H; } /* create an X colormap since probably not using default visual */ cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone); swa.colormap = cmap; swa.border_pixel = 0; swa.event_mask = ExposureMask | ButtonPressMask | Button1MotionMask | StructureNotifyMask; win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), sizeHints.x, sizeHints.y, W, H, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa); XSetStandardProperties(dpy, win, "OpenGLosaurus", "glxdino", None, argv, argc, &sizeHints); wmHints = XAllocWMHints(); wmHints->initial_state = iconic ? IconicState : NormalState; wmHints->flags = StateHint; XSetWMHints(dpy, win, wmHints); wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(dpy, win, &wmDeleteWindow, 1); /*** (7) bind the rendering context to the window ***/ glXMakeCurrent(dpy, win, cx); /*** (8) make the desired display lists ***/ makeDinosaur(); /*** (9) request the X window to be displayed on the screen ***/ XMapWindow(dpy, win); /*** (10) configure the OpenGL context for rendering ***/ glEnable(GL_CULL_FACE); /* ~50% better perfomance than non-face culled on Starter Indigo */ glEnable(GL_DEPTH_TEST); /* enable depth buffering */ glClearColor(0.0, 0.0, 0.0, 0.0); /* frame buffer clears should be to black */ glMatrixMode(GL_PROJECTION); /* set up projection transform */ glLoadIdentity(); gluPerspective(40.0, 1, 1.0, 40.0); glMatrixMode(GL_MODELVIEW); /* now change to modelview */ /*** (11) dispatch X events ***/ while (1) { do { XNextEvent(dpy, &event); switch (event.type) { case ButtonPress: lastX = event.xbutton.x; break; case MotionNotify: recalcModelView = GL_TRUE; angle += (lastX - event.xmotion.x) / (GLfloat) 50; /* fifty is empirical scale factor */ lastX = event.xmotion.x; break; case ConfigureNotify: glViewport(0, 0, event.xconfigure.width, event.xconfigure.height); /* fall through... */ case Expose: needRedraw = GL_TRUE; break; case ClientMessage: if (event.xclient.data.l[0] == wmDeleteWindow) exit(0); break; } } while (XPending(dpy));/* loop to compress events */ if (recalcModelView) { /* reset modelview matrix to the identity matrix */ glLoadIdentity(); gluLookAt(30 * sin(angle), 0, 30 * cos(angle), 0, 0, 0, 0, 1, 0); glTranslatef(-8, -8, -bodyWidth / 2); recalcModelView = GL_FALSE; needRedraw = GL_TRUE; } if (needRedraw) { redraw(); needRedraw = GL_FALSE; } } }