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