/* compile: cc -o glxdino glxdino.c -lGLU -lGL -lXmu -lX11 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> /* for cos(), sin(), and sqrt() */ #include <GL/glx.h> /* this includes X and gl.h headers */ #include <GL/glu.h> /* gluPerspective(), gluLookAt(), GLU polygon * tesselator */ #include <X11/Xatom.h> /* for XA_RGB_DEFAULT_MAP atom */ #include <X11/Xmu/StdCmap.h> /* for XmuLookupStandardColormap() */ #include <X11/keysym.h> /* for XK_Escape keysym */ 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 = -150; /* in degrees */ GLboolean doubleBuffer = GL_TRUE, iconic = GL_FALSE, keepAspect = GL_FALSE; int W = 300, H = 300; XSizeHints sizeHints = {0}; GLdouble bodyWidth = 2.0; int configuration[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, None}; 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} }; GLfloat lightZeroPosition[] = {10.0, 4.0, 10.0, 1.0}; GLfloat lightZeroColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */ GLfloat lightOnePosition[] = {-1.0, -2.0, 1.0, 0.0}; GLfloat lightOneColor[] = {0.6, 0.3, 0.2, 1.0}; /* red-tinted */ GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0}; GC gc; XGCValues gcvals; void fatalError(char *message) { fprintf(stderr, "glxdino: %s\n", message); exit(1); } Colormap getColormap(XVisualInfo * vi) { Status status; XStandardColormap *standardCmaps; Colormap cmap; int i, numCmaps; /* be lazy; using DirectColor too involved for this example */ if (vi->class != TrueColor) fatalError("no support for non-TrueColor visual"); /* if no standard colormap but TrueColor, just make an unshared one */ status = XmuLookupStandardColormap(dpy, vi->screen, vi->visualid, vi->depth, XA_RGB_DEFAULT_MAP, /* replace */ False, /* retain */ True); if (status == 1) { status = XGetRGBColormaps(dpy, RootWindow(dpy, vi->screen), &standardCmaps, &numCmaps, XA_RGB_DEFAULT_MAP); if (status == 1) for (i = 0; i < numCmaps; i++) if (standardCmaps[i].visualid == vi->visualid) { cmap = standardCmaps[i].colormap; XFree(standardCmaps); return cmap; } } cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone); return cmap; } void extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize, GLdouble thickness, GLuint side, GLuint edge, GLuint whole) { static GLUtriangulatorObj *tobj = NULL; GLdouble vertex[3], dx, dy, len; int i; int count = dataSize / (2 * sizeof(GLfloat)); 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); glShadeModel(GL_SMOOTH); /* smooth minimizes seeing tessellation */ gluBeginPolygon(tobj); for (i = 0; i < count; 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); glShadeModel(GL_FLAT); /* flat shade keeps angular hands from being * "smoothed" */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= count; i++) { /* mod function handles closing the edge */ glVertex3f(data[i % count][0], data[i % count][1], 0.0); glVertex3f(data[i % count][0], data[i % count][1], thickness); /* Calculate a unit normal by dividing by Euclidean distance. We * could be lazy and use glEnable(GL_NORMALIZE) so we could pass in * arbitrary normals for a very slight performance hit. */ dx = data[(i + 1) % count][1] - data[i % count][1]; dy = data[i % count][0] - data[(i + 1) % count][0]; len = sqrt(dx * dx + dy * dy); glNormal3f(dx / len, dy / len, 0.0); } glEnd(); glEndList(); glNewList(whole, GL_COMPILE); glFrontFace(GL_CW); glCallList(edge); glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */ glCallList(side); glPushMatrix(); glTranslatef(0.0, 0.0, thickness); glFrontFace(GL_CCW); glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */ glCallList(side); glPopMatrix(); glEndList(); } void makeDinosaur(void) { GLfloat bodyWidth = 3.0; extrudeSolidFromPolygon(body, sizeof(body), bodyWidth, BODY_SIDE, BODY_EDGE, BODY_WHOLE); extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4, ARM_SIDE, ARM_EDGE, ARM_WHOLE); extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2, LEG_SIDE, LEG_EDGE, LEG_WHOLE); extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2, EYE_SIDE, EYE_EDGE, EYE_WHOLE); glNewList(DINOSAUR, GL_COMPILE); glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor); glCallList(BODY_WHOLE); glPushMatrix(); glTranslatef(0.0, 0.0, bodyWidth); 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); glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1); glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor); glCallList(EYE_WHOLE); glPopMatrix(); glEndList(); } void redraw(void) { static int x = 0; 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 */ #if 1 XDrawLine(dpy, win, gc, 10+x, 10, 40+x, 40); x+=8; XSync(dpy, 0); #endif } void main(int argc, char **argv) { XVisualInfo *vi; Colormap cmap; XSetWindowAttributes swa; XWMHints *wmHints; Atom wmDeleteWindow; GLXContext cx; XEvent event; KeySym ks; GLboolean needRedraw = GL_FALSE, recalcModelView = GL_TRUE; char *display = NULL, *geometry = NULL; int 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")) { if (++i >= argc) fatalError("follow -geometry option with geometry parameter"); geometry = argv[i]; } else if (!strcmp(argv[i], "-display")) { 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 if (!strcmp(argv[i], "-single")) doubleBuffer = GL_FALSE; 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, NULL, NULL)) fatalError("X server has no OpenGL GLX extension"); /*** (4) find an appropriate visual and a colormap for it ***/ /* find an OpenGL-capable RGB visual with depth buffer */ if (!doubleBuffer) goto SingleBufferOverride; vi = glXChooseVisual(dpy, DefaultScreen(dpy), configuration); if (vi == NULL) { SingleBufferOverride: vi = glXChooseVisual(dpy, DefaultScreen(dpy), &configuration[1]); if (vi == NULL) fatalError("no appropriate RGB visual with depth buffer"); doubleBuffer = GL_FALSE; } cmap = getColormap(vi); /*** (5) create an OpenGL rendering context ***/ /* create an OpenGL rendering context */ cx = glXCreateContext(dpy, vi, /* no sharing of display lists */ NULL, /* direct rendering if possible */ GL_TRUE); if (cx == NULL) fatalError("could not create rendering context"); /*** (6) create an X window with 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; } swa.colormap = cmap; swa.border_pixel = 0; swa.event_mask = ExposureMask | StructureNotifyMask | ButtonPressMask | Button1MotionMask | KeyPressMask; win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), sizeHints.x, sizeHints.y, W, H, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); gcvals.line_width = 5; gcvals.foreground = 45; gc = XCreateGC(dpy, win, GCForeground|GCLineWidth, &gcvals); 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) configure the OpenGL context for rendering ***/ glEnable(GL_CULL_FACE); /* ~50% better perfomance than no back-face * culling on Entry Indigo */ glEnable(GL_DEPTH_TEST); /* enable depth buffering */ glEnable(GL_LIGHTING); /* enable lighting */ glMatrixMode(GL_PROJECTION);/* set up projection transform */ gluPerspective( /* field of view in degree */ 40.0, /* aspect ratio */ 1.0, /* Z near */ 1.0, /* Z far */ 40.0); glMatrixMode(GL_MODELVIEW); /* now change to modelview */ gluLookAt(0.0, 0.0, 30.0, /* eye is at (0,0,30) */ 0.0, 0.0, 0.0, /* center is at (0,0,0) */ 0.0, 1.0, 0.); /* up is in postivie Y direction */ glPushMatrix(); /* dummy push so we can pop on model recalc */ glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition); glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor); glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1); glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05); glLightfv(GL_LIGHT1, GL_POSITION, lightOnePosition); glLightfv(GL_LIGHT1, GL_DIFFUSE, lightOneColor); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); /* enable both lights */ /*** (10) request the X window to be displayed on the screen ***/ XMapWindow(dpy, win); /*** (11) dispatch X events ***/ while (1) { do { XNextEvent(dpy, &event); switch (event.type) { case ConfigureNotify: glViewport(0, 0, event.xconfigure.width, event.xconfigure.height); /* fall through... */ case Expose: needRedraw = GL_TRUE; break; case MotionNotify: recalcModelView = GL_TRUE; angle -= (lastX - event.xmotion.x); case ButtonPress: lastX = event.xbutton.x; break; case KeyPress: ks = XLookupKeysym((XKeyEvent *) & event, 0); if (ks == XK_Escape) exit(0); break; case ClientMessage: if (event.xclient.data.l[0] == wmDeleteWindow) exit(0); break; } } while (XPending(dpy));/* loop to compress events */ if (recalcModelView) { glPopMatrix(); /* pop old rotated matrix (or dummy matrix if * first time) */ glPushMatrix(); glRotatef(angle, 0.0, 1.0, 0.0); glTranslatef(-8, -8, -bodyWidth / 2); recalcModelView = GL_FALSE; needRedraw = GL_TRUE; } if (needRedraw) { redraw(); needRedraw = GL_FALSE; } } }