3 * This program demonstrates how to do "off-screen" rendering using
4 * the GLX pixel buffer extension.
6 * Written by Brian Paul for the "OpenGL and Window System Integration"
7 * course presented at SIGGRAPH '97. Updated on 5 October 2002.
10 * pbuffers width height imgfile
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.
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.
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.
26 * O2 systems seem to support pbuffers well.
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
43 /* Some ugly global vars */
44 static GLXFBConfigSGIX gFBconfig
= 0;
45 static Display
*gDpy
= NULL
;
46 static int gScreen
= 0;
47 static GLXPbufferSGIX gPBuffer
= 0;
48 static int gWidth
, gHeight
;
53 * Create the pbuffer and return a GLXPbufferSGIX handle.
55 * We loop over a list of fbconfigs trying to create
56 * a pixel buffer. We return the first pixel buffer which we successfully
60 MakePbuffer( Display
*dpy
, int screen
, int width
, int height
)
62 #define NUM_FB_CONFIGS 4
63 const char fbString
[NUM_FB_CONFIGS
][100] = {
64 "Single Buffered, depth buffer",
65 "Double Buffered, depth buffer",
66 "Single Buffered, no depth buffer",
67 "Double Buffered, no depth buffer"
69 int fbAttribs
[NUM_FB_CONFIGS
][100] = {
71 /* Single buffered, with depth buffer */
72 GLX_RENDER_TYPE_SGIX
, GLX_RGBA_BIT_SGIX
,
73 GLX_DRAWABLE_TYPE_SGIX
, GLX_PBUFFER_BIT_SGIX
,
83 /* Double buffered, with depth buffer */
84 GLX_RENDER_TYPE_SGIX
, GLX_RGBA_BIT_SGIX
,
85 GLX_DRAWABLE_TYPE_SGIX
, GLX_PBUFFER_BIT_SGIX
,
95 /* Single bufferd, without depth buffer */
96 GLX_RENDER_TYPE_SGIX
, GLX_RGBA_BIT_SGIX
,
97 GLX_DRAWABLE_TYPE_SGIX
, GLX_PBUFFER_BIT_SGIX
,
107 /* Double bufferd, without depth buffer */
108 GLX_RENDER_TYPE_SGIX
, GLX_RGBA_BIT_SGIX
,
109 GLX_DRAWABLE_TYPE_SGIX
, GLX_PBUFFER_BIT_SGIX
,
120 GLX_LARGEST_PBUFFER_SGIX
, True
,
121 GLX_PRESERVED_CONTENTS_SGIX
, False
,
124 GLXFBConfigSGIX
*fbConfigs
;
125 GLXPbufferSGIX pBuffer
= None
;
130 for (attempt
=0; attempt
<NUM_FB_CONFIGS
; attempt
++) {
132 /* Get list of possible frame buffer configurations */
133 fbConfigs
= glXChooseFBConfigSGIX(dpy
, screen
, fbAttribs
[attempt
], &nConfigs
);
134 if (nConfigs
==0 || !fbConfigs
) {
135 printf("Error: glxChooseFBConfigSGIX failed\n");
141 for (i
=0;i
<nConfigs
;i
++) {
142 printf("Config %d\n", i
);
143 PrintFBConfigInfo(dpy
, fbConfigs
[i
], 0);
147 /* Create the pbuffer using first fbConfig in the list that works. */
148 for (i
=0;i
<nConfigs
;i
++) {
149 pBuffer
= CreatePbuffer(dpy
, fbConfigs
[i
], width
, height
, pbAttribs
);
151 gFBconfig
= fbConfigs
[i
];
164 printf("Using: %s\n", fbString
[attempt
]);
170 #undef NUM_FB_CONFIGS
176 * Do all the X / GLX setup stuff.
179 Setup(int width
, int height
)
181 #if defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
182 XVisualInfo
*visInfo
;
185 /* Open the X display */
186 gDpy
= XOpenDisplay(NULL
);
188 printf("Error: couldn't open default X display.\n");
192 /* Get default screen */
193 gScreen
= DefaultScreen(gDpy
);
195 /* Test that pbuffers are available */
196 if (!QueryPbuffers(gDpy
, gScreen
)) {
197 printf("Error: pbuffers not available on this screen\n");
203 gPBuffer
= MakePbuffer( gDpy
, gScreen
, width
, height
);
204 if (gPBuffer
==None
) {
205 printf("Error: couldn't create pbuffer\n");
210 /* Get corresponding XVisualInfo */
211 visInfo
= glXGetVisualFromFBConfigSGIX(gDpy
, gFBconfig
);
213 printf("Error: can't get XVisualInfo from FBconfig\n");
218 /* Create GLX context */
219 glCtx
= glXCreateContext(gDpy
, visInfo
, NULL
, True
);
222 glCtx
= glXCreateContext(gDpy
, visInfo
, NULL
, False
);
224 printf("Error: Couldn't create GLXContext\n");
230 printf("Warning: using indirect GLXContext\n");
234 /* Bind context to pbuffer */
235 if (!glXMakeCurrent(gDpy
, gPBuffer
, glCtx
)) {
236 printf("Error: glXMakeCurrent failed\n");
242 return 1; /* Success!! */
244 printf("Error: GLX_SGIX_fbconfig and/or GLX_SGIX_pbuffer extensions not"
245 " available at compile-time.\n");
252 /* One-time GL setup */
256 static GLfloat pos
[4] = {0.0, 0.0, 10.0, 0.0};
257 glEnable(GL_LIGHTING
);
259 glLightfv(GL_LIGHT0
, GL_POSITION
, pos
);
260 glEnable(GL_NORMALIZE
);
261 glEnable(GL_DEPTH_TEST
);
262 glEnable(GL_CULL_FACE
);
264 glViewport(0, 0, gWidth
, gHeight
);
265 glMatrixMode( GL_PROJECTION
);
267 glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 );
268 glMatrixMode( GL_MODELVIEW
);
270 glTranslatef( 0.0, 0.0, -15.0 );
274 /* Return random float in [0,1] */
279 return (float) (i
% 1000) / 1000.0;
291 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, c
);
295 /* This function borrowed from Mark Kilgard's GLUT */
297 drawBox(GLfloat x0
, GLfloat x1
, GLfloat y0
, GLfloat y1
,
298 GLfloat z0
, GLfloat z1
, GLenum type
)
300 static GLfloat n
[6][3] =
309 static GLint faces
[6][4] =
318 GLfloat v
[8][3], tmp
;
336 v
[0][0] = v
[1][0] = v
[2][0] = v
[3][0] = x0
;
337 v
[4][0] = v
[5][0] = v
[6][0] = v
[7][0] = x1
;
338 v
[0][1] = v
[1][1] = v
[4][1] = v
[5][1] = y0
;
339 v
[2][1] = v
[3][1] = v
[6][1] = v
[7][1] = y1
;
340 v
[0][2] = v
[3][2] = v
[4][2] = v
[7][2] = z0
;
341 v
[1][2] = v
[2][2] = v
[5][2] = v
[6][2] = z1
;
343 for (i
= 0; i
< 6; i
++) {
345 glNormal3fv(&n
[i
][0]);
346 glVertex3fv(&v
[faces
[i
][0]][0]);
347 glVertex3fv(&v
[faces
[i
][1]][0]);
348 glVertex3fv(&v
[faces
[i
][2]][0]);
349 glVertex3fv(&v
[faces
[i
][3]][0]);
364 glClearColor(0.2, 0.2, 0.9, 0.0);
365 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
367 for (i
=0;i
<NumBoxes
;i
++) {
368 float tx
= -2.0 + 4.0 * Random();
369 float ty
= -2.0 + 4.0 * Random();
370 float tz
= 4.0 - 16.0 * Random();
371 float sx
= 0.1 + Random() * 0.4;
372 float sy
= 0.1 + Random() * 0.4;
373 float sz
= 0.1 + Random() * 0.4;
377 float ra
= Random() * 360.0;
379 glTranslatef(tx
, ty
, tz
);
380 glRotatef(ra
, rx
, ry
, rz
);
381 glScalef(sx
, sy
, sz
);
383 drawBox(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0, GL_POLYGON
);
393 WriteFile(const char *filename
)
399 image
= malloc(gWidth
* gHeight
* 3 * sizeof(GLubyte
));
401 printf("Error: couldn't allocate image buffer\n");
405 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
406 glReadPixels(0, 0, gWidth
, gHeight
, GL_RGB
, GL_UNSIGNED_BYTE
, image
);
408 f
= fopen(filename
, "w");
410 printf("Couldn't open image file: %s\n", filename
);
414 fprintf(f
,"# ppm-file created by %s\n", "trdemo2");
415 fprintf(f
,"%i %i\n", gWidth
, gHeight
);
418 f
= fopen(filename
, "ab"); /* now append binary data */
420 printf("Couldn't append to image file: %s\n", filename
);
424 for (i
=0;i
<gHeight
;i
++) {
426 /* Remember, OpenGL images are bottom to top. Have to reverse. */
427 rowPtr
= image
+ (gHeight
-1-i
) * gWidth
*3;
428 fwrite(rowPtr
, 1, gWidth
*3, f
);
434 printf("Wrote %d by %d image file: %s\n", gWidth
, gHeight
, filename
);
440 * Print message describing command line parameters.
443 Usage(const char *appName
)
446 printf(" %s width height imgfile\n", appName
);
447 printf("Where imgfile is a ppm file\n");
453 main(int argc
, char *argv
[])
459 int width
= atoi(argv
[1]);
460 int height
= atoi(argv
[2]);
461 char *fileName
= argv
[3];
463 printf("Error: width parameter must be at least 1.\n");
467 printf("Error: height parameter must be at least 1.\n");
470 if (!Setup(width
, height
)) {
475 glXDestroyGLXPbufferSGIX( gDpy
, gPBuffer
);