132001f49Smrg 232001f49Smrg/* 332001f49Smrg * This program demonstrates how to do "off-screen" rendering using 432001f49Smrg * the GLX pixel buffer extension. 532001f49Smrg * 632001f49Smrg * Written by Brian Paul for the "OpenGL and Window System Integration" 732001f49Smrg * course presented at SIGGRAPH '97. Updated on 5 October 2002. 832001f49Smrg * 932001f49Smrg * Usage: 1032001f49Smrg * pbuffers width height imgfile 1132001f49Smrg * Where: 1232001f49Smrg * width is the width, in pixels, of the image to generate. 1332001f49Smrg * height is the height, in pixels, of the image to generate. 1432001f49Smrg * imgfile is the name of the PPM image file to write. 1532001f49Smrg * 1632001f49Smrg * 1732001f49Smrg * This demo draws 3-D boxes with random orientation. A pbuffer with 1832001f49Smrg * a depth (Z) buffer is prefered but if such a pbuffer can't be created 1932001f49Smrg * we use a non-depth-buffered config. 2032001f49Smrg * 2132001f49Smrg * On machines such as the SGI Indigo you may have to reconfigure your 2232001f49Smrg * display/X server to enable pbuffers. Look in the /usr/gfx/ucode/MGRAS/vof/ 2332001f49Smrg * directory for display configurationswith the _pbuf suffix. Use 2432001f49Smrg * setmon -x <vof> to configure your X server and display for pbuffers. 2532001f49Smrg * 2632001f49Smrg * O2 systems seem to support pbuffers well. 2732001f49Smrg * 2832001f49Smrg * IR systems (at least 1RM systems) don't have single-buffered, RGBA, 2932001f49Smrg * Z-buffered pbuffer configs. BUT, they DO have DOUBLE-buffered, RGBA, 3032001f49Smrg * Z-buffered pbuffers. Note how we try four different fbconfig attribute 3132001f49Smrg * lists below! 3232001f49Smrg */ 3332001f49Smrg 3432001f49Smrg 3532001f49Smrg#include <assert.h> 3632001f49Smrg#include <string.h> 3732001f49Smrg#include <stdio.h> 3832001f49Smrg#include <stdlib.h> 3932001f49Smrg#include <X11/Xlib.h> 4032001f49Smrg#include "pbutil.h" 4132001f49Smrg 4232001f49Smrg 4332001f49Smrg/* Some ugly global vars */ 4432001f49Smrgstatic Display *gDpy = NULL; 4532001f49Smrgstatic int gScreen = 0; 4632001f49Smrgstatic FBCONFIG gFBconfig = 0; 4732001f49Smrgstatic PBUFFER gPBuffer = 0; 4832001f49Smrgstatic int gWidth, gHeight; 4932001f49Smrgstatic GLXContext glCtx; 5032001f49Smrg 5132001f49Smrg 5232001f49Smrg 5332001f49Smrg/* 5432001f49Smrg * Create the pbuffer and return a GLXPbuffer handle. 5532001f49Smrg * 5632001f49Smrg * We loop over a list of fbconfigs trying to create 5732001f49Smrg * a pixel buffer. We return the first pixel buffer which we successfully 5832001f49Smrg * create. 5932001f49Smrg */ 6032001f49Smrgstatic PBUFFER 6132001f49SmrgMakePbuffer( Display *dpy, int screen, int width, int height ) 6232001f49Smrg{ 6332001f49Smrg#define NUM_FB_CONFIGS 4 6432001f49Smrg const char fbString[NUM_FB_CONFIGS][100] = { 6532001f49Smrg "Single Buffered, depth buffer", 6632001f49Smrg "Double Buffered, depth buffer", 6732001f49Smrg "Single Buffered, no depth buffer", 6832001f49Smrg "Double Buffered, no depth buffer" 6932001f49Smrg }; 7032001f49Smrg int fbAttribs[NUM_FB_CONFIGS][100] = { 7132001f49Smrg { 7232001f49Smrg /* Single buffered, with depth buffer */ 7332001f49Smrg GLX_RENDER_TYPE, GLX_RGBA_BIT, 7432001f49Smrg GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, 7532001f49Smrg GLX_RED_SIZE, 1, 7632001f49Smrg GLX_GREEN_SIZE, 1, 7732001f49Smrg GLX_BLUE_SIZE, 1, 7832001f49Smrg GLX_DEPTH_SIZE, 1, 7932001f49Smrg GLX_DOUBLEBUFFER, 0, 8032001f49Smrg GLX_STENCIL_SIZE, 0, 8132001f49Smrg None 8232001f49Smrg }, 8332001f49Smrg { 8432001f49Smrg /* Double buffered, with depth buffer */ 8532001f49Smrg GLX_RENDER_TYPE, GLX_RGBA_BIT, 8632001f49Smrg GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, 8732001f49Smrg GLX_RED_SIZE, 1, 8832001f49Smrg GLX_GREEN_SIZE, 1, 8932001f49Smrg GLX_BLUE_SIZE, 1, 9032001f49Smrg GLX_DEPTH_SIZE, 1, 9132001f49Smrg GLX_DOUBLEBUFFER, 1, 9232001f49Smrg GLX_STENCIL_SIZE, 0, 9332001f49Smrg None 9432001f49Smrg }, 9532001f49Smrg { 9632001f49Smrg /* Single buffered, without depth buffer */ 9732001f49Smrg GLX_RENDER_TYPE, GLX_RGBA_BIT, 9832001f49Smrg GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, 9932001f49Smrg GLX_RED_SIZE, 1, 10032001f49Smrg GLX_GREEN_SIZE, 1, 10132001f49Smrg GLX_BLUE_SIZE, 1, 10232001f49Smrg GLX_DEPTH_SIZE, 0, 10332001f49Smrg GLX_DOUBLEBUFFER, 0, 10432001f49Smrg GLX_STENCIL_SIZE, 0, 10532001f49Smrg None 10632001f49Smrg }, 10732001f49Smrg { 10832001f49Smrg /* Double buffered, without depth buffer */ 10932001f49Smrg GLX_RENDER_TYPE, GLX_RGBA_BIT, 11032001f49Smrg GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, 11132001f49Smrg GLX_RED_SIZE, 1, 11232001f49Smrg GLX_GREEN_SIZE, 1, 11332001f49Smrg GLX_BLUE_SIZE, 1, 11432001f49Smrg GLX_DEPTH_SIZE, 0, 11532001f49Smrg GLX_DOUBLEBUFFER, 1, 11632001f49Smrg GLX_STENCIL_SIZE, 0, 11732001f49Smrg None 11832001f49Smrg } 11932001f49Smrg }; 12032001f49Smrg Bool largest = True; 12132001f49Smrg Bool preserve = False; 12232001f49Smrg FBCONFIG *fbConfigs; 12332001f49Smrg PBUFFER pBuffer = None; 12432001f49Smrg int nConfigs; 12532001f49Smrg int i; 12632001f49Smrg int attempt; 12732001f49Smrg 12832001f49Smrg for (attempt=0; attempt<NUM_FB_CONFIGS; attempt++) { 12932001f49Smrg 13032001f49Smrg /* Get list of possible frame buffer configurations */ 13132001f49Smrg fbConfigs = ChooseFBConfig(dpy, screen, fbAttribs[attempt], &nConfigs); 13232001f49Smrg if (nConfigs==0 || !fbConfigs) { 13332001f49Smrg printf("Note: glXChooseFBConfig(%s) failed\n", fbString[attempt]); 13432001f49Smrg XFree(fbConfigs); 13532001f49Smrg continue; 13632001f49Smrg } 13732001f49Smrg 13832001f49Smrg#if 0 /*DEBUG*/ 13932001f49Smrg for (i=0;i<nConfigs;i++) { 14032001f49Smrg printf("Config %d\n", i); 14132001f49Smrg PrintFBConfigInfo(dpy, screen, fbConfigs[i], 0); 14232001f49Smrg } 14332001f49Smrg#endif 14432001f49Smrg 14532001f49Smrg /* Create the pbuffer using first fbConfig in the list that works. */ 14632001f49Smrg for (i=0;i<nConfigs;i++) { 14732001f49Smrg pBuffer = CreatePbuffer(dpy, screen, fbConfigs[i], width, height, largest, preserve); 14832001f49Smrg if (pBuffer) { 14932001f49Smrg gFBconfig = fbConfigs[i]; 15032001f49Smrg gWidth = width; 15132001f49Smrg gHeight = height; 15232001f49Smrg break; 15332001f49Smrg } 15432001f49Smrg } 15532001f49Smrg 15632001f49Smrg if (pBuffer!=None) { 15732001f49Smrg break; 15832001f49Smrg } 15932001f49Smrg } 16032001f49Smrg 16132001f49Smrg if (pBuffer) { 16232001f49Smrg printf("Using: %s\n", fbString[attempt]); 16332001f49Smrg } 16432001f49Smrg 16532001f49Smrg XFree(fbConfigs); 16632001f49Smrg 16732001f49Smrg return pBuffer; 16832001f49Smrg#undef NUM_FB_CONFIGS 16932001f49Smrg} 17032001f49Smrg 17132001f49Smrg 17232001f49Smrg 17332001f49Smrg/* 17432001f49Smrg * Do all the X / GLX setup stuff. 17532001f49Smrg */ 17632001f49Smrgstatic int 17732001f49SmrgSetup(int width, int height) 17832001f49Smrg{ 17932001f49Smrg int pbSupport; 18032001f49Smrg XVisualInfo *visInfo; 18132001f49Smrg 18232001f49Smrg /* Open the X display */ 18332001f49Smrg gDpy = XOpenDisplay(NULL); 18432001f49Smrg if (!gDpy) { 18532001f49Smrg printf("Error: couldn't open default X display.\n"); 18632001f49Smrg return 0; 18732001f49Smrg } 18832001f49Smrg 18932001f49Smrg /* Get default screen */ 19032001f49Smrg gScreen = DefaultScreen(gDpy); 19132001f49Smrg 19232001f49Smrg /* Test that pbuffers are available */ 19332001f49Smrg pbSupport = QueryPbuffers(gDpy, gScreen); 19432001f49Smrg if (pbSupport == 1) { 19532001f49Smrg printf("Using GLX 1.3 Pbuffers\n"); 19632001f49Smrg } 19732001f49Smrg else if (pbSupport == 2) { 19832001f49Smrg printf("Using SGIX Pbuffers\n"); 19932001f49Smrg } 20032001f49Smrg else { 20132001f49Smrg printf("Error: pbuffers not available on this screen\n"); 20232001f49Smrg XCloseDisplay(gDpy); 20332001f49Smrg return 0; 20432001f49Smrg } 20532001f49Smrg 20632001f49Smrg /* Create Pbuffer */ 20732001f49Smrg gPBuffer = MakePbuffer( gDpy, gScreen, width, height ); 20832001f49Smrg if (gPBuffer==None) { 20932001f49Smrg printf("Error: couldn't create pbuffer\n"); 21032001f49Smrg XCloseDisplay(gDpy); 21132001f49Smrg return 0; 21232001f49Smrg } 21332001f49Smrg 21432001f49Smrg /* Test drawable queries */ 21532001f49Smrg { 21632001f49Smrg unsigned int v; 21732001f49Smrg glXQueryDrawable( gDpy, gPBuffer, GLX_WIDTH, &v); 21832001f49Smrg printf("GLX_WIDTH = %u\n", v); 21932001f49Smrg glXQueryDrawable( gDpy, gPBuffer, GLX_HEIGHT, &v); 22032001f49Smrg printf("GLX_HEIGHT = %u\n", v); 22132001f49Smrg glXQueryDrawable( gDpy, gPBuffer, GLX_PRESERVED_CONTENTS, &v); 22232001f49Smrg printf("GLX_PRESERVED_CONTENTS = %u\n", v); 22332001f49Smrg glXQueryDrawable( gDpy, gPBuffer, GLX_LARGEST_PBUFFER, &v); 22432001f49Smrg printf("GLX_LARGEST_PBUFFER = %u\n", v); 22532001f49Smrg glXQueryDrawable( gDpy, gPBuffer, GLX_FBCONFIG_ID, &v); 22632001f49Smrg printf("GLX_FBCONFIG_ID = %u\n", v); 22732001f49Smrg } 22832001f49Smrg 22932001f49Smrg /* Get corresponding XVisualInfo */ 23032001f49Smrg visInfo = GetVisualFromFBConfig(gDpy, gScreen, gFBconfig); 23132001f49Smrg if (!visInfo) { 23232001f49Smrg printf("Error: can't get XVisualInfo from FBconfig\n"); 23332001f49Smrg XCloseDisplay(gDpy); 23432001f49Smrg return 0; 23532001f49Smrg } 23632001f49Smrg 23732001f49Smrg /* Create GLX context */ 23832001f49Smrg glCtx = glXCreateContext(gDpy, visInfo, NULL, True); 23932001f49Smrg if (!glCtx) { 24032001f49Smrg /* try indirect */ 24132001f49Smrg glCtx = glXCreateContext(gDpy, visInfo, NULL, False); 24232001f49Smrg if (!glCtx) { 24332001f49Smrg printf("Error: Couldn't create GLXContext\n"); 24432001f49Smrg XFree(visInfo); 24532001f49Smrg XCloseDisplay(gDpy); 24632001f49Smrg return 0; 24732001f49Smrg } 24832001f49Smrg else { 24932001f49Smrg printf("Warning: using indirect GLXContext\n"); 25032001f49Smrg } 25132001f49Smrg } 25232001f49Smrg 25332001f49Smrg /* Bind context to pbuffer */ 25432001f49Smrg if (!glXMakeCurrent(gDpy, gPBuffer, glCtx)) { 25532001f49Smrg printf("Error: glXMakeCurrent failed\n"); 25632001f49Smrg XFree(visInfo); 25732001f49Smrg XCloseDisplay(gDpy); 25832001f49Smrg return 0; 25932001f49Smrg } 26032001f49Smrg 26132001f49Smrg return 1; /* Success!! */ 26232001f49Smrg} 26332001f49Smrg 26432001f49Smrg 26532001f49Smrg 26632001f49Smrg/* One-time GL setup */ 26732001f49Smrgstatic void 26832001f49SmrgInitGL(void) 26932001f49Smrg{ 27032001f49Smrg static GLfloat pos[4] = {0.0, 0.0, 10.0, 0.0}; 27132001f49Smrg glEnable(GL_LIGHTING); 27232001f49Smrg glEnable(GL_LIGHT0); 27332001f49Smrg glLightfv(GL_LIGHT0, GL_POSITION, pos); 27432001f49Smrg glEnable(GL_NORMALIZE); 27532001f49Smrg glEnable(GL_DEPTH_TEST); 27632001f49Smrg glEnable(GL_CULL_FACE); 27732001f49Smrg 27832001f49Smrg glViewport(0, 0, gWidth, gHeight); 27932001f49Smrg glMatrixMode( GL_PROJECTION ); 28032001f49Smrg glLoadIdentity(); 28132001f49Smrg glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 ); 28232001f49Smrg glMatrixMode( GL_MODELVIEW ); 28332001f49Smrg glLoadIdentity(); 28432001f49Smrg glTranslatef( 0.0, 0.0, -15.0 ); 28532001f49Smrg} 28632001f49Smrg 28732001f49Smrg 28832001f49Smrg/* Return random float in [0,1] */ 28932001f49Smrgstatic float 29032001f49SmrgRandom(void) 29132001f49Smrg{ 29232001f49Smrg int i = rand(); 29332001f49Smrg return (float) (i % 1000) / 1000.0; 29432001f49Smrg} 29532001f49Smrg 29632001f49Smrg 29732001f49Smrgstatic void 29832001f49SmrgRandomColor(void) 29932001f49Smrg{ 30032001f49Smrg GLfloat c[4]; 30132001f49Smrg c[0] = Random(); 30232001f49Smrg c[1] = Random(); 30332001f49Smrg c[2] = Random(); 30432001f49Smrg c[3] = 1.0; 30532001f49Smrg glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c); 30632001f49Smrg} 30732001f49Smrg 30832001f49Smrg 30932001f49Smrg/* This function borrowed from Mark Kilgard's GLUT */ 31032001f49Smrgstatic void 31132001f49SmrgdrawBox(GLfloat x0, GLfloat x1, GLfloat y0, GLfloat y1, 31232001f49Smrg GLfloat z0, GLfloat z1, GLenum type) 31332001f49Smrg{ 31432001f49Smrg static GLfloat n[6][3] = 31532001f49Smrg { 31632001f49Smrg {-1.0, 0.0, 0.0}, 31732001f49Smrg {0.0, 1.0, 0.0}, 31832001f49Smrg {1.0, 0.0, 0.0}, 31932001f49Smrg {0.0, -1.0, 0.0}, 32032001f49Smrg {0.0, 0.0, 1.0}, 32132001f49Smrg {0.0, 0.0, -1.0} 32232001f49Smrg }; 32332001f49Smrg static GLint faces[6][4] = 32432001f49Smrg { 32532001f49Smrg {0, 1, 2, 3}, 32632001f49Smrg {3, 2, 6, 7}, 32732001f49Smrg {7, 6, 5, 4}, 32832001f49Smrg {4, 5, 1, 0}, 32932001f49Smrg {5, 6, 2, 1}, 33032001f49Smrg {7, 4, 0, 3} 33132001f49Smrg }; 33232001f49Smrg GLfloat v[8][3], tmp; 33332001f49Smrg GLint i; 33432001f49Smrg 33532001f49Smrg if (x0 > x1) { 33632001f49Smrg tmp = x0; 33732001f49Smrg x0 = x1; 33832001f49Smrg x1 = tmp; 33932001f49Smrg } 34032001f49Smrg if (y0 > y1) { 34132001f49Smrg tmp = y0; 34232001f49Smrg y0 = y1; 34332001f49Smrg y1 = tmp; 34432001f49Smrg } 34532001f49Smrg if (z0 > z1) { 34632001f49Smrg tmp = z0; 34732001f49Smrg z0 = z1; 34832001f49Smrg z1 = tmp; 34932001f49Smrg } 35032001f49Smrg v[0][0] = v[1][0] = v[2][0] = v[3][0] = x0; 35132001f49Smrg v[4][0] = v[5][0] = v[6][0] = v[7][0] = x1; 35232001f49Smrg v[0][1] = v[1][1] = v[4][1] = v[5][1] = y0; 35332001f49Smrg v[2][1] = v[3][1] = v[6][1] = v[7][1] = y1; 35432001f49Smrg v[0][2] = v[3][2] = v[4][2] = v[7][2] = z0; 35532001f49Smrg v[1][2] = v[2][2] = v[5][2] = v[6][2] = z1; 35632001f49Smrg 35732001f49Smrg for (i = 0; i < 6; i++) { 35832001f49Smrg glBegin(type); 35932001f49Smrg glNormal3fv(&n[i][0]); 36032001f49Smrg glVertex3fv(&v[faces[i][0]][0]); 36132001f49Smrg glVertex3fv(&v[faces[i][1]][0]); 36232001f49Smrg glVertex3fv(&v[faces[i][2]][0]); 36332001f49Smrg glVertex3fv(&v[faces[i][3]][0]); 36432001f49Smrg glEnd(); 36532001f49Smrg } 36632001f49Smrg} 36732001f49Smrg 36832001f49Smrg 36932001f49Smrg 37032001f49Smrg/* Render a scene */ 37132001f49Smrgstatic void 37232001f49SmrgRender(void) 37332001f49Smrg{ 37432001f49Smrg int NumBoxes = 100; 37532001f49Smrg int i; 37632001f49Smrg 37732001f49Smrg glClearColor(0.2, 0.2, 0.9, 0.0); 37832001f49Smrg glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 37932001f49Smrg 38032001f49Smrg for (i=0;i<NumBoxes;i++) { 38132001f49Smrg float tx = -2.0 + 4.0 * Random(); 38232001f49Smrg float ty = -2.0 + 4.0 * Random(); 38332001f49Smrg float tz = 4.0 - 16.0 * Random(); 38432001f49Smrg float sx = 0.1 + Random() * 0.4; 38532001f49Smrg float sy = 0.1 + Random() * 0.4; 38632001f49Smrg float sz = 0.1 + Random() * 0.4; 38732001f49Smrg float rx = Random(); 38832001f49Smrg float ry = Random(); 38932001f49Smrg float rz = Random(); 39032001f49Smrg float ra = Random() * 360.0; 39132001f49Smrg glPushMatrix(); 39232001f49Smrg glTranslatef(tx, ty, tz); 39332001f49Smrg glRotatef(ra, rx, ry, rz); 39432001f49Smrg glScalef(sx, sy, sz); 39532001f49Smrg RandomColor(); 39632001f49Smrg drawBox(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0, GL_POLYGON); 39732001f49Smrg glPopMatrix(); 39832001f49Smrg } 39932001f49Smrg 40032001f49Smrg glFinish(); 40132001f49Smrg} 40232001f49Smrg 40332001f49Smrg 40432001f49Smrg 40532001f49Smrgstatic void 40632001f49SmrgWriteFile(const char *filename) 40732001f49Smrg{ 40832001f49Smrg FILE *f; 40932001f49Smrg GLubyte *image; 41032001f49Smrg int i; 41132001f49Smrg 41232001f49Smrg image = malloc(gWidth * gHeight * 3 * sizeof(GLubyte)); 41332001f49Smrg if (!image) { 41432001f49Smrg printf("Error: couldn't allocate image buffer\n"); 41532001f49Smrg return; 41632001f49Smrg } 41732001f49Smrg 41832001f49Smrg glPixelStorei(GL_PACK_ALIGNMENT, 1); 41932001f49Smrg glReadPixels(0, 0, gWidth, gHeight, GL_RGB, GL_UNSIGNED_BYTE, image); 42032001f49Smrg 42132001f49Smrg f = fopen(filename, "w"); 42232001f49Smrg if (!f) { 42332001f49Smrg printf("Couldn't open image file: %s\n", filename); 42432001f49Smrg return; 42532001f49Smrg } 42632001f49Smrg fprintf(f,"P6\n"); 42732001f49Smrg fprintf(f,"# ppm-file created by %s\n", "trdemo2"); 42832001f49Smrg fprintf(f,"%i %i\n", gWidth, gHeight); 42932001f49Smrg fprintf(f,"255\n"); 43032001f49Smrg fclose(f); 43132001f49Smrg f = fopen(filename, "ab"); /* now append binary data */ 43232001f49Smrg if (!f) { 43332001f49Smrg printf("Couldn't append to image file: %s\n", filename); 43432001f49Smrg return; 43532001f49Smrg } 43632001f49Smrg 43732001f49Smrg for (i=0;i<gHeight;i++) { 43832001f49Smrg GLubyte *rowPtr; 43932001f49Smrg /* Remember, OpenGL images are bottom to top. Have to reverse. */ 44032001f49Smrg rowPtr = image + (gHeight-1-i) * gWidth*3; 44132001f49Smrg fwrite(rowPtr, 1, gWidth*3, f); 44232001f49Smrg } 44332001f49Smrg 44432001f49Smrg fclose(f); 44532001f49Smrg free(image); 44632001f49Smrg 44732001f49Smrg printf("Wrote %d by %d image file: %s\n", gWidth, gHeight, filename); 44832001f49Smrg} 44932001f49Smrg 45032001f49Smrg 45132001f49Smrg 45232001f49Smrg/* 45332001f49Smrg * Print message describing command line parameters. 45432001f49Smrg */ 45532001f49Smrgstatic void 45632001f49SmrgUsage(const char *appName) 45732001f49Smrg{ 45832001f49Smrg printf("Usage:\n"); 45932001f49Smrg printf(" %s width height imgfile\n", appName); 46032001f49Smrg printf("Where imgfile is a ppm file\n"); 46132001f49Smrg} 46232001f49Smrg 46332001f49Smrg 46432001f49Smrg 46532001f49Smrgint 46632001f49Smrgmain(int argc, char *argv[]) 46732001f49Smrg{ 46832001f49Smrg if (argc!=4) { 46932001f49Smrg Usage(argv[0]); 47032001f49Smrg } 47132001f49Smrg else { 47232001f49Smrg int width = atoi(argv[1]); 47332001f49Smrg int height = atoi(argv[2]); 47432001f49Smrg char *fileName = argv[3]; 47532001f49Smrg if (width<=0) { 47632001f49Smrg printf("Error: width parameter must be at least 1.\n"); 47732001f49Smrg return 1; 47832001f49Smrg } 47932001f49Smrg if (height<=0) { 48032001f49Smrg printf("Error: height parameter must be at least 1.\n"); 48132001f49Smrg return 1; 48232001f49Smrg } 48332001f49Smrg if (!Setup(width, height)) { 48432001f49Smrg return 1; 48532001f49Smrg } 48632001f49Smrg InitGL(); 48732001f49Smrg Render(); 48832001f49Smrg WriteFile(fileName); 48932001f49Smrg DestroyPbuffer(gDpy, gScreen, gPBuffer); 49032001f49Smrg } 49132001f49Smrg return 0; 49232001f49Smrg} 49332001f49Smrg 494