/* Copyright (c) Mark J. Kilgard, 1996. */ /* This program is freely distributable without licensing fees and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ /* 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 */ 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; GLfloat angle = -50; /* in degrees */ GLboolean doubleBuffer = GL_TRUE, iconic = GL_FALSE, keepAspect = GL_FALSE; GLdouble bodyWidth = 2.0; 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}; void fatalError(char *message) { fprintf(stderr, "glxdino: %s\n", message); exit(1); } /* it seems that some compilers have a new type for the third argument */ /* 03 Aug 2004 [rickr] */ #ifdef LINUX2 #define CAST_GLU_FUNCPTR (_GLUfuncptr) #else #define CAST_GLU_FUNCPTR #endif 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, CAST_GLU_FUNCPTR glBegin); gluTessCallback(tobj, GLU_VERTEX, CAST_GLU_FUNCPTR glVertex2fv); /* semi-tricky */ gluTessCallback(tobj, GLU_END, CAST_GLU_FUNCPTR 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 contextInit(void) { glClearColor(0.0, 0.0, 0.0, 1.0); 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 positive Y direction */ glRotatef(angle, 0.0, 1.0, 0.0); glTranslatef(-8, -8, -bodyWidth / 2); 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 */ } void redraw(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glCallList(DINOSAUR); } int generateEPS(char *filename, int inColor, unsigned int width, unsigned int height); int main(int argc, char **argv) { static int configuration[] = { GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None}; Display *dpy; XVisualInfo *vi; GLXContext cx; Pixmap pmap; GLXPixmap glxpmap; int imageWidth = 400, imageHeight = 400; dpy = XOpenDisplay(NULL); if (dpy == NULL) fatalError("could not open display"); if (!glXQueryExtension(dpy, NULL, NULL)) fatalError("X server has no OpenGL GLX extension"); /* find an OpenGL-capable RGB visual with depth buffer */ vi = glXChooseVisual(dpy, DefaultScreen(dpy), &configuration[1]); if (vi == NULL) { vi = glXChooseVisual(dpy, DefaultScreen(dpy), &configuration[0]); if (vi == NULL) { fatalError("no appropriate RGB visual with depth buffer"); } } /* create an OpenGL rendering context */ cx = glXCreateContext(dpy, vi, NULL, /* no sharing of display lists */ False); /* direct rendering if possible */ if (cx == NULL) fatalError("could not create rendering context"); pmap = XCreatePixmap(dpy, RootWindow(dpy, vi->screen), imageWidth, imageHeight, vi->depth); glxpmap = glXCreateGLXPixmap(dpy, vi, pmap); glXMakeCurrent(dpy, glxpmap, cx); contextInit(); makeDinosaur(); glViewport(0, 0, imageWidth, imageHeight); redraw(); generateEPS("dino.rgb.eps", /* color */ 1, imageWidth, imageHeight); generateEPS("dino.bw.eps", /* black&white */ 0, imageWidth, imageHeight); exit(0) ; } GLvoid *grabPixels(int inColor, unsigned int width, unsigned int height); int generateEPS(char *filename, int inColor, unsigned int width, unsigned int height) { FILE *fp; GLvoid *pixels; unsigned char *curpix; int components, pos, i; pixels = grabPixels(inColor, width, height); if (pixels == NULL) return 1; if (inColor) components = 3; /* Red, green, blue. */ else components = 1; /* Luminance. */ fp = fopen(filename, "w"); if (fp == NULL) { return 2; } fprintf(fp, "%%!PS-Adobe-2.0 EPSF-1.2\n"); fprintf(fp, "%%%%Creator: OpenGL pixmap render output\n"); fprintf(fp, "%%%%BoundingBox: 0 0 %d %d\n", width, height); fprintf(fp, "%%%%EndComments\n"); fprintf(fp, "gsave\n"); fprintf(fp, "/bwproc {\n"); fprintf(fp, " rgbproc\n"); fprintf(fp, " dup length 3 idiv string 0 3 0\n"); fprintf(fp, " 5 -1 roll {\n"); fprintf(fp, " add 2 1 roll 1 sub dup 0 eq\n"); fprintf(fp, " { pop 3 idiv 3 -1 roll dup 4 -1 roll dup\n"); fprintf(fp, " 3 1 roll 5 -1 roll put 1 add 3 0 }\n"); fprintf(fp, " { 2 1 roll } ifelse\n"); fprintf(fp, " } forall\n"); fprintf(fp, " pop pop pop\n"); fprintf(fp, "} def\n"); fprintf(fp, "systemdict /colorimage known not {\n"); fprintf(fp, " /colorimage {\n"); fprintf(fp, " pop\n"); fprintf(fp, " pop\n"); fprintf(fp, " /rgbproc exch def\n"); fprintf(fp, " { bwproc } image\n"); fprintf(fp, " } def\n"); fprintf(fp, "} if\n"); fprintf(fp, "/picstr %d string def\n", width * components); fprintf(fp, "%d %d scale\n", width, height); fprintf(fp, "%d %d %d\n", width, height, 8); fprintf(fp, "[%d 0 0 %d 0 0]\n", width, height); fprintf(fp, "{currentfile picstr readhexstring pop}\n"); fprintf(fp, "false %d\n", components); fprintf(fp, "colorimage\n"); curpix = (unsigned char *) pixels; pos = 0; for (i = width * height * components; i > 0; i--) { fprintf(fp, "%02hx", *curpix++); if (++pos >= 32) { fprintf(fp, "\n"); pos = 0; } } if (pos) fprintf(fp, "\n"); fprintf(fp, "grestore\n"); free(pixels); fclose(fp); return 0; } GLvoid * grabPixels(int inColor, unsigned int width, unsigned int height) { GLvoid *buffer; GLint swapbytes, lsbfirst, rowlength; GLint skiprows, skippixels, alignment; GLenum format; unsigned int size; if (inColor) { format = GL_RGB; size = width * height * 3; } else { format = GL_LUMINANCE; size = width * height * 1; } buffer = (GLvoid *) malloc(size); if (buffer == NULL) return NULL; /* Save current modes. */ glGetIntegerv(GL_PACK_SWAP_BYTES, &swapbytes); glGetIntegerv(GL_PACK_LSB_FIRST, &lsbfirst); glGetIntegerv(GL_PACK_ROW_LENGTH, &rowlength); glGetIntegerv(GL_PACK_SKIP_ROWS, &skiprows); glGetIntegerv(GL_PACK_SKIP_PIXELS, &skippixels); glGetIntegerv(GL_PACK_ALIGNMENT, &alignment); /* Little endian machines (DEC Alpha for example) could benefit from setting GL_PACK_LSB_FIRST to GL_TRUE instead of GL_FALSE, but this would require changing the generated bitmaps too. */ glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); /* Actually read the pixels. */ glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, (GLvoid *) buffer); /* Restore saved modes. */ glPixelStorei(GL_PACK_SWAP_BYTES, swapbytes); glPixelStorei(GL_PACK_LSB_FIRST, lsbfirst); glPixelStorei(GL_PACK_ROW_LENGTH, rowlength); glPixelStorei(GL_PACK_SKIP_ROWS, skiprows); glPixelStorei(GL_PACK_SKIP_PIXELS, skippixels); glPixelStorei(GL_PACK_ALIGNMENT, alignment); return buffer; }