1
2/*
3 * This program demonstrates how to do "off-screen" rendering using
4 * the GLX pixel buffer extension.
5 *
6 * Written by Brian Paul for the "OpenGL and Window System Integration"
7 * course presented at SIGGRAPH '97.  Updated on 5 October 2002.
8 *
9 * Usage:
10 *   pbuffers width height imgfile
11 * Where:
12 *   width is the width, in pixels, of the image to generate.
13 *   height is the height, in pixels, of the image to generate.
14 *   imgfile is the name of the PPM image file to write.
15 *
16 *
17 * This demo draws 3-D boxes with random orientation.  A pbuffer with
18 * a depth (Z) buffer is prefered but if such a pbuffer can't be created
19 * we use a non-depth-buffered config.
20 *
21 * On machines such as the SGI Indigo you may have to reconfigure your
22 * display/X server to enable pbuffers.  Look in the /usr/gfx/ucode/MGRAS/vof/
23 * directory for display configurationswith the _pbuf suffix.  Use
24 * setmon -x <vof> to configure your X server and display for pbuffers.
25 *
26 * O2 systems seem to support pbuffers well.
27 *
28 * IR systems (at least 1RM systems) don't have single-buffered, RGBA,
29 * Z-buffered pbuffer configs.  BUT, they DO have DOUBLE-buffered, RGBA,
30 * Z-buffered pbuffers.  Note how we try four different fbconfig attribute
31 * lists below!
32 */
33
34
35#include <assert.h>
36#include <string.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <X11/Xlib.h>
40#include "pbutil.h"
41
42
43/* Some ugly global vars */
44static Display *gDpy = NULL;
45static int gScreen = 0;
46static FBCONFIG gFBconfig = 0;
47static PBUFFER gPBuffer = 0;
48static int gWidth, gHeight;
49static GLXContext glCtx;
50
51
52
53/*
54 * Create the pbuffer and return a GLXPbuffer handle.
55 *
56 * We loop over a list of fbconfigs trying to create
57 * a pixel buffer.  We return the first pixel buffer which we successfully
58 * create.
59 */
60static PBUFFER
61MakePbuffer( Display *dpy, int screen, int width, int height )
62{
63#define NUM_FB_CONFIGS 4
64   const char fbString[NUM_FB_CONFIGS][100] = {
65      "Single Buffered, depth buffer",
66      "Double Buffered, depth buffer",
67      "Single Buffered, no depth buffer",
68      "Double Buffered, no depth buffer"
69   };
70   int fbAttribs[NUM_FB_CONFIGS][100] = {
71      {
72         /* Single buffered, with depth buffer */
73         GLX_RENDER_TYPE, GLX_RGBA_BIT,
74         GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
75         GLX_RED_SIZE, 1,
76         GLX_GREEN_SIZE, 1,
77         GLX_BLUE_SIZE, 1,
78         GLX_DEPTH_SIZE, 1,
79         GLX_DOUBLEBUFFER, 0,
80         GLX_STENCIL_SIZE, 0,
81         None
82      },
83      {
84         /* Double buffered, with depth buffer */
85         GLX_RENDER_TYPE, GLX_RGBA_BIT,
86         GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
87         GLX_RED_SIZE, 1,
88         GLX_GREEN_SIZE, 1,
89         GLX_BLUE_SIZE, 1,
90         GLX_DEPTH_SIZE, 1,
91         GLX_DOUBLEBUFFER, 1,
92         GLX_STENCIL_SIZE, 0,
93         None
94      },
95      {
96         /* Single buffered, without depth buffer */
97         GLX_RENDER_TYPE, GLX_RGBA_BIT,
98         GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
99         GLX_RED_SIZE, 1,
100         GLX_GREEN_SIZE, 1,
101         GLX_BLUE_SIZE, 1,
102         GLX_DEPTH_SIZE, 0,
103         GLX_DOUBLEBUFFER, 0,
104         GLX_STENCIL_SIZE, 0,
105         None
106      },
107      {
108         /* Double buffered, without depth buffer */
109         GLX_RENDER_TYPE, GLX_RGBA_BIT,
110         GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
111         GLX_RED_SIZE, 1,
112         GLX_GREEN_SIZE, 1,
113         GLX_BLUE_SIZE, 1,
114         GLX_DEPTH_SIZE, 0,
115         GLX_DOUBLEBUFFER, 1,
116         GLX_STENCIL_SIZE, 0,
117         None
118      }
119   };
120   Bool largest = True;
121   Bool preserve = False;
122   FBCONFIG *fbConfigs;
123   PBUFFER pBuffer = None;
124   int nConfigs;
125   int i;
126   int attempt;
127
128   for (attempt=0; attempt<NUM_FB_CONFIGS; attempt++) {
129
130      /* Get list of possible frame buffer configurations */
131      fbConfigs = ChooseFBConfig(dpy, screen, fbAttribs[attempt], &nConfigs);
132      if (nConfigs==0 || !fbConfigs) {
133         printf("Note: glXChooseFBConfig(%s) failed\n", fbString[attempt]);
134         XFree(fbConfigs);
135         continue;
136      }
137
138#if 0 /*DEBUG*/
139      for (i=0;i<nConfigs;i++) {
140         printf("Config %d\n", i);
141         PrintFBConfigInfo(dpy, screen, fbConfigs[i], 0);
142      }
143#endif
144
145      /* Create the pbuffer using first fbConfig in the list that works. */
146      for (i=0;i<nConfigs;i++) {
147         pBuffer = CreatePbuffer(dpy, screen, fbConfigs[i], width, height, largest, preserve);
148         if (pBuffer) {
149            gFBconfig = fbConfigs[i];
150            gWidth = width;
151            gHeight = height;
152            break;
153         }
154      }
155
156      if (pBuffer!=None) {
157         break;
158      }
159   }
160
161   if (pBuffer) {
162      printf("Using: %s\n", fbString[attempt]);
163   }
164
165   XFree(fbConfigs);
166
167   return pBuffer;
168#undef NUM_FB_CONFIGS
169}
170
171
172
173/*
174 * Do all the X / GLX setup stuff.
175 */
176static int
177Setup(int width, int height)
178{
179   int pbSupport;
180   XVisualInfo *visInfo;
181
182   /* Open the X display */
183   gDpy = XOpenDisplay(NULL);
184   if (!gDpy) {
185      printf("Error: couldn't open default X display.\n");
186      return 0;
187   }
188
189   /* Get default screen */
190   gScreen = DefaultScreen(gDpy);
191
192   /* Test that pbuffers are available */
193   pbSupport = QueryPbuffers(gDpy, gScreen);
194   if (pbSupport == 1) {
195      printf("Using GLX 1.3 Pbuffers\n");
196   }
197   else if (pbSupport == 2) {
198      printf("Using SGIX Pbuffers\n");
199   }
200   else {
201      printf("Error: pbuffers not available on this screen\n");
202      XCloseDisplay(gDpy);
203      return 0;
204   }
205
206   /* Create Pbuffer */
207   gPBuffer = MakePbuffer( gDpy, gScreen, width, height );
208   if (gPBuffer==None) {
209      printf("Error: couldn't create pbuffer\n");
210      XCloseDisplay(gDpy);
211      return 0;
212   }
213
214   /* Test drawable queries */
215   {
216      unsigned int v;
217      glXQueryDrawable( gDpy, gPBuffer, GLX_WIDTH, &v);
218      printf("GLX_WIDTH = %u\n", v);
219      glXQueryDrawable( gDpy, gPBuffer, GLX_HEIGHT, &v);
220      printf("GLX_HEIGHT = %u\n", v);
221      glXQueryDrawable( gDpy, gPBuffer, GLX_PRESERVED_CONTENTS, &v);
222      printf("GLX_PRESERVED_CONTENTS = %u\n", v);
223      glXQueryDrawable( gDpy, gPBuffer, GLX_LARGEST_PBUFFER, &v);
224      printf("GLX_LARGEST_PBUFFER = %u\n", v);
225      glXQueryDrawable( gDpy, gPBuffer, GLX_FBCONFIG_ID, &v);
226      printf("GLX_FBCONFIG_ID = %u\n", v);
227   }
228
229   /* Get corresponding XVisualInfo */
230   visInfo = GetVisualFromFBConfig(gDpy, gScreen, gFBconfig);
231   if (!visInfo) {
232      printf("Error: can't get XVisualInfo from FBconfig\n");
233      XCloseDisplay(gDpy);
234      return 0;
235   }
236
237   /* Create GLX context */
238   glCtx = glXCreateContext(gDpy, visInfo, NULL, True);
239   if (!glCtx) {
240      /* try indirect */
241      glCtx = glXCreateContext(gDpy, visInfo, NULL, False);
242      if (!glCtx) {
243         printf("Error: Couldn't create GLXContext\n");
244         XFree(visInfo);
245         XCloseDisplay(gDpy);
246         return 0;
247      }
248      else {
249         printf("Warning: using indirect GLXContext\n");
250      }
251   }
252
253   /* Bind context to pbuffer */
254   if (!glXMakeCurrent(gDpy, gPBuffer, glCtx)) {
255      printf("Error: glXMakeCurrent failed\n");
256      XFree(visInfo);
257      XCloseDisplay(gDpy);
258      return 0;
259   }
260
261   return 1;  /* Success!! */
262}
263
264
265
266/* One-time GL setup */
267static void
268InitGL(void)
269{
270   static GLfloat pos[4] = {0.0, 0.0, 10.0, 0.0};
271   glEnable(GL_LIGHTING);
272   glEnable(GL_LIGHT0);
273   glLightfv(GL_LIGHT0, GL_POSITION, pos);
274   glEnable(GL_NORMALIZE);
275   glEnable(GL_DEPTH_TEST);
276   glEnable(GL_CULL_FACE);
277
278   glViewport(0, 0, gWidth, gHeight);
279   glMatrixMode( GL_PROJECTION );
280   glLoadIdentity();
281   glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 );
282   glMatrixMode( GL_MODELVIEW );
283   glLoadIdentity();
284   glTranslatef( 0.0, 0.0, -15.0 );
285}
286
287
288/* Return random float in [0,1] */
289static float
290Random(void)
291{
292   int i = rand();
293   return (float) (i % 1000) / 1000.0;
294}
295
296
297static void
298RandomColor(void)
299{
300   GLfloat c[4];
301   c[0] = Random();
302   c[1] = Random();
303   c[2] = Random();
304   c[3] = 1.0;
305   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c);
306}
307
308
309/* This function borrowed from Mark Kilgard's GLUT */
310static void
311drawBox(GLfloat x0, GLfloat x1, GLfloat y0, GLfloat y1,
312  GLfloat z0, GLfloat z1, GLenum type)
313{
314  static GLfloat n[6][3] =
315  {
316    {-1.0, 0.0, 0.0},
317    {0.0, 1.0, 0.0},
318    {1.0, 0.0, 0.0},
319    {0.0, -1.0, 0.0},
320    {0.0, 0.0, 1.0},
321    {0.0, 0.0, -1.0}
322  };
323  static GLint faces[6][4] =
324  {
325    {0, 1, 2, 3},
326    {3, 2, 6, 7},
327    {7, 6, 5, 4},
328    {4, 5, 1, 0},
329    {5, 6, 2, 1},
330    {7, 4, 0, 3}
331  };
332  GLfloat v[8][3], tmp;
333  GLint i;
334
335  if (x0 > x1) {
336    tmp = x0;
337    x0 = x1;
338    x1 = tmp;
339  }
340  if (y0 > y1) {
341    tmp = y0;
342    y0 = y1;
343    y1 = tmp;
344  }
345  if (z0 > z1) {
346    tmp = z0;
347    z0 = z1;
348    z1 = tmp;
349  }
350  v[0][0] = v[1][0] = v[2][0] = v[3][0] = x0;
351  v[4][0] = v[5][0] = v[6][0] = v[7][0] = x1;
352  v[0][1] = v[1][1] = v[4][1] = v[5][1] = y0;
353  v[2][1] = v[3][1] = v[6][1] = v[7][1] = y1;
354  v[0][2] = v[3][2] = v[4][2] = v[7][2] = z0;
355  v[1][2] = v[2][2] = v[5][2] = v[6][2] = z1;
356
357  for (i = 0; i < 6; i++) {
358    glBegin(type);
359    glNormal3fv(&n[i][0]);
360    glVertex3fv(&v[faces[i][0]][0]);
361    glVertex3fv(&v[faces[i][1]][0]);
362    glVertex3fv(&v[faces[i][2]][0]);
363    glVertex3fv(&v[faces[i][3]][0]);
364    glEnd();
365  }
366}
367
368
369
370/* Render a scene */
371static void
372Render(void)
373{
374   int NumBoxes = 100;
375   int i;
376
377   glClearColor(0.2, 0.2, 0.9, 0.0);
378   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
379
380   for (i=0;i<NumBoxes;i++) {
381      float tx = -2.0 + 4.0 * Random();
382      float ty = -2.0 + 4.0 * Random();
383      float tz =  4.0 - 16.0 * Random();
384      float sx = 0.1 + Random() * 0.4;
385      float sy = 0.1 + Random() * 0.4;
386      float sz = 0.1 + Random() * 0.4;
387      float rx = Random();
388      float ry = Random();
389      float rz = Random();
390      float ra = Random() * 360.0;
391      glPushMatrix();
392      glTranslatef(tx, ty, tz);
393      glRotatef(ra, rx, ry, rz);
394      glScalef(sx, sy, sz);
395      RandomColor();
396      drawBox(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0, GL_POLYGON);
397      glPopMatrix();
398   }
399
400   glFinish();
401}
402
403
404
405static void
406WriteFile(const char *filename)
407{
408   FILE *f;
409   GLubyte *image;
410   int i;
411
412   image = malloc(gWidth * gHeight * 3 * sizeof(GLubyte));
413   if (!image) {
414      printf("Error: couldn't allocate image buffer\n");
415      return;
416   }
417
418   glPixelStorei(GL_PACK_ALIGNMENT, 1);
419   glReadPixels(0, 0, gWidth, gHeight, GL_RGB, GL_UNSIGNED_BYTE, image);
420
421   f = fopen(filename, "w");
422   if (!f) {
423      printf("Couldn't open image file: %s\n", filename);
424      return;
425   }
426   fprintf(f,"P6\n");
427   fprintf(f,"# ppm-file created by %s\n", "trdemo2");
428   fprintf(f,"%i %i\n", gWidth, gHeight);
429   fprintf(f,"255\n");
430   fclose(f);
431   f = fopen(filename, "ab");  /* now append binary data */
432   if (!f) {
433      printf("Couldn't append to image file: %s\n", filename);
434      return;
435   }
436
437   for (i=0;i<gHeight;i++) {
438      GLubyte *rowPtr;
439      /* Remember, OpenGL images are bottom to top.  Have to reverse. */
440      rowPtr = image + (gHeight-1-i) * gWidth*3;
441      fwrite(rowPtr, 1, gWidth*3, f);
442   }
443
444   fclose(f);
445   free(image);
446
447   printf("Wrote %d by %d image file: %s\n", gWidth, gHeight, filename);
448}
449
450
451
452/*
453 * Print message describing command line parameters.
454 */
455static void
456Usage(const char *appName)
457{
458   printf("Usage:\n");
459   printf("  %s width height imgfile\n", appName);
460   printf("Where imgfile is a ppm file\n");
461}
462
463
464
465int
466main(int argc, char *argv[])
467{
468   if (argc!=4) {
469      Usage(argv[0]);
470   }
471   else {
472      int width = atoi(argv[1]);
473      int height = atoi(argv[2]);
474      char *fileName = argv[3];
475      if (width<=0) {
476         printf("Error: width parameter must be at least 1.\n");
477         return 1;
478      }
479      if (height<=0) {
480         printf("Error: height parameter must be at least 1.\n");
481         return 1;
482      }
483      if (!Setup(width, height)) {
484         return 1;
485      }
486      InitGL();
487      Render();
488      WriteFile(fileName);
489      DestroyPbuffer(gDpy, gScreen, gPBuffer);
490   }
491   return 0;
492}
493
494