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