1/* 2 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included 12 * in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 */ 21 22/** 23 * \file glxgears_fbconfig.c 24 * Yet-another-version of gears. Originally ported to GLX by Brian Paul on 25 * 23 March 2001. Modified to use fbconfigs by Ian Romanick on 10 Feb 2004. 26 * 27 * Command line options: 28 * -info print GL implementation information 29 * 30 * \author Brian Paul 31 * \author Ian Romanick <idr@us.ibm.com> 32 */ 33 34 35#define GLX_GLXEXT_PROTOTYPES 36 37#include <math.h> 38#include <stdlib.h> 39#include <stdio.h> 40#include <string.h> 41#include <X11/Xlib.h> 42#include <X11/keysym.h> 43#include <GL/gl.h> 44#include <GL/glx.h> 45#include <GL/glxext.h> 46#include <assert.h> 47#include "pbutil.h" 48 49static PFNGLXCHOOSEFBCONFIGPROC choose_fbconfig = NULL; 50static PFNGLXGETVISUALFROMFBCONFIGPROC get_visual_from_fbconfig = NULL; 51static PFNGLXCREATENEWCONTEXTPROC create_new_context = NULL; 52static PFNGLXCREATEWINDOWPROC create_window = NULL; 53static PFNGLXDESTROYWINDOWPROC destroy_window = NULL; 54 55#define BENCHMARK 56 57#ifdef BENCHMARK 58 59/* XXX this probably isn't very portable */ 60 61#include <sys/time.h> 62#include <unistd.h> 63 64/* return current time (in seconds) */ 65static int 66current_time(void) 67{ 68 struct timeval tv; 69#ifdef __VMS 70 (void) gettimeofday(&tv, NULL ); 71#else 72 struct timezone tz; 73 (void) gettimeofday(&tv, &tz); 74#endif 75 return (int) tv.tv_sec; 76} 77 78#else /*BENCHMARK*/ 79 80/* dummy */ 81static int 82current_time(void) 83{ 84 return 0; 85} 86 87#endif /*BENCHMARK*/ 88 89 90 91#ifndef M_PI 92#define M_PI 3.14159265 93#endif 94 95 96static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; 97static GLint gear1, gear2, gear3; 98static GLfloat angle = 0.0; 99 100 101/* 102 * 103 * Draw a gear wheel. You'll probably want to call this function when 104 * building a display list since we do a lot of trig here. 105 * 106 * Input: inner_radius - radius of hole at center 107 * outer_radius - radius at center of teeth 108 * width - width of gear 109 * teeth - number of teeth 110 * tooth_depth - depth of tooth 111 */ 112static void 113gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, 114 GLint teeth, GLfloat tooth_depth) 115{ 116 GLint i; 117 GLfloat r0, r1, r2; 118 GLfloat angle, da; 119 GLfloat u, v, len; 120 121 r0 = inner_radius; 122 r1 = outer_radius - tooth_depth / 2.0; 123 r2 = outer_radius + tooth_depth / 2.0; 124 125 da = 2.0 * M_PI / teeth / 4.0; 126 127 glShadeModel(GL_FLAT); 128 129 glNormal3f(0.0, 0.0, 1.0); 130 131 /* draw front face */ 132 glBegin(GL_QUAD_STRIP); 133 for (i = 0; i <= teeth; i++) { 134 angle = i * 2.0 * M_PI / teeth; 135 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 136 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 137 if (i < teeth) { 138 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 139 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 140 width * 0.5); 141 } 142 } 143 glEnd(); 144 145 /* draw front sides of teeth */ 146 glBegin(GL_QUADS); 147 da = 2.0 * M_PI / teeth / 4.0; 148 for (i = 0; i < teeth; i++) { 149 angle = i * 2.0 * M_PI / teeth; 150 151 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 152 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); 153 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 154 width * 0.5); 155 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 156 width * 0.5); 157 } 158 glEnd(); 159 160 glNormal3f(0.0, 0.0, -1.0); 161 162 /* draw back face */ 163 glBegin(GL_QUAD_STRIP); 164 for (i = 0; i <= teeth; i++) { 165 angle = i * 2.0 * M_PI / teeth; 166 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 167 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 168 if (i < teeth) { 169 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 170 -width * 0.5); 171 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 172 } 173 } 174 glEnd(); 175 176 /* draw back sides of teeth */ 177 glBegin(GL_QUADS); 178 da = 2.0 * M_PI / teeth / 4.0; 179 for (i = 0; i < teeth; i++) { 180 angle = i * 2.0 * M_PI / teeth; 181 182 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 183 -width * 0.5); 184 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 185 -width * 0.5); 186 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); 187 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 188 } 189 glEnd(); 190 191 /* draw outward faces of teeth */ 192 glBegin(GL_QUAD_STRIP); 193 for (i = 0; i < teeth; i++) { 194 angle = i * 2.0 * M_PI / teeth; 195 196 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 197 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 198 u = r2 * cos(angle + da) - r1 * cos(angle); 199 v = r2 * sin(angle + da) - r1 * sin(angle); 200 len = sqrt(u * u + v * v); 201 u /= len; 202 v /= len; 203 glNormal3f(v, -u, 0.0); 204 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); 205 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); 206 glNormal3f(cos(angle), sin(angle), 0.0); 207 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 208 width * 0.5); 209 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 210 -width * 0.5); 211 u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); 212 v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); 213 glNormal3f(v, -u, 0.0); 214 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 215 width * 0.5); 216 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 217 -width * 0.5); 218 glNormal3f(cos(angle), sin(angle), 0.0); 219 } 220 221 glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5); 222 glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5); 223 224 glEnd(); 225 226 glShadeModel(GL_SMOOTH); 227 228 /* draw inside radius cylinder */ 229 glBegin(GL_QUAD_STRIP); 230 for (i = 0; i <= teeth; i++) { 231 angle = i * 2.0 * M_PI / teeth; 232 glNormal3f(-cos(angle), -sin(angle), 0.0); 233 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 234 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 235 } 236 glEnd(); 237} 238 239 240static void 241draw(void) 242{ 243 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 244 245 glPushMatrix(); 246 glRotatef(view_rotx, 1.0, 0.0, 0.0); 247 glRotatef(view_roty, 0.0, 1.0, 0.0); 248 glRotatef(view_rotz, 0.0, 0.0, 1.0); 249 250 glPushMatrix(); 251 glTranslatef(-3.0, -2.0, 0.0); 252 glRotatef(angle, 0.0, 0.0, 1.0); 253 glCallList(gear1); 254 glPopMatrix(); 255 256 glPushMatrix(); 257 glTranslatef(3.1, -2.0, 0.0); 258 glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0); 259 glCallList(gear2); 260 glPopMatrix(); 261 262 glPushMatrix(); 263 glTranslatef(-3.1, 4.2, 0.0); 264 glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0); 265 glCallList(gear3); 266 glPopMatrix(); 267 268 glPopMatrix(); 269} 270 271 272/* new window size or exposure */ 273static void 274reshape(int width, int height) 275{ 276 GLfloat h = (GLfloat) height / (GLfloat) width; 277 278 glViewport(0, 0, (GLint) width, (GLint) height); 279 glMatrixMode(GL_PROJECTION); 280 glLoadIdentity(); 281 glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0); 282 glMatrixMode(GL_MODELVIEW); 283 glLoadIdentity(); 284 glTranslatef(0.0, 0.0, -40.0); 285} 286 287 288static void 289init(void) 290{ 291 static GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 }; 292 static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 }; 293 static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 }; 294 static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 }; 295 296 glLightfv(GL_LIGHT0, GL_POSITION, pos); 297 glEnable(GL_CULL_FACE); 298 glEnable(GL_LIGHTING); 299 glEnable(GL_LIGHT0); 300 glEnable(GL_DEPTH_TEST); 301 302 /* make the gears */ 303 gear1 = glGenLists(1); 304 glNewList(gear1, GL_COMPILE); 305 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); 306 gear(1.0, 4.0, 1.0, 20, 0.7); 307 glEndList(); 308 309 gear2 = glGenLists(1); 310 glNewList(gear2, GL_COMPILE); 311 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); 312 gear(0.5, 2.0, 2.0, 10, 0.7); 313 glEndList(); 314 315 gear3 = glGenLists(1); 316 glNewList(gear3, GL_COMPILE); 317 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); 318 gear(1.3, 2.0, 0.5, 10, 0.7); 319 glEndList(); 320 321 glEnable(GL_NORMALIZE); 322} 323 324 325static GLXWindow 326dummy_create_window(Display *dpy, GLXFBConfig config, Window win, 327 const int *attrib_list) 328{ 329 (void) dpy; 330 (void) config; 331 (void) attrib_list; 332 333 return (GLXWindow) win; 334} 335 336 337static void 338dummy_destroy_window(Display *dpy, GLXWindow win) 339{ 340 (void) dpy; 341 (void) win; 342} 343 344 345/** 346 * Initialize fbconfig related function pointers. 347 */ 348static void 349init_fbconfig_functions(Display *dpy, int scrnum) 350{ 351 const char * glx_extensions; 352 const char * match; 353 static const char ext_name[] = "GLX_SGIX_fbconfig"; 354 const size_t len = strlen( ext_name ); 355 int major; 356 int minor; 357 GLboolean ext_version_supported; 358 GLboolean glx_1_3_supported; 359 360 361 /* Determine if GLX 1.3 or greater is supported. 362 */ 363 glXQueryVersion(dpy, & major, & minor); 364 glx_1_3_supported = (major == 1) && (minor >= 3); 365 366 /* Determine if GLX_SGIX_fbconfig is supported. 367 */ 368 glx_extensions = glXQueryExtensionsString(dpy, scrnum); 369 match = strstr( glx_extensions, ext_name ); 370 371 ext_version_supported = (match != NULL) 372 && ((match[len] == '\0') || (match[len] == ' ')); 373 374 printf( "GLX 1.3 is %ssupported.\n", 375 (glx_1_3_supported) ? "" : "not " ); 376 printf( "%s is %ssupported.\n", 377 ext_name, (ext_version_supported) ? "" : "not " ); 378 379 if ( glx_1_3_supported ) { 380 choose_fbconfig = (PFNGLXCHOOSEFBCONFIGPROC) 381 glXGetProcAddressARB((GLubyte *) "glXChooseFBConfig"); 382 get_visual_from_fbconfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) 383 glXGetProcAddressARB((GLubyte *) "glXGetVisualFromFBConfig"); 384 create_new_context = (PFNGLXCREATENEWCONTEXTPROC) 385 glXGetProcAddressARB((GLubyte *) "glXCreateNewContext"); 386 create_window = (PFNGLXCREATEWINDOWPROC) 387 glXGetProcAddressARB((GLubyte *) "glXCreateWindow"); 388 destroy_window = (PFNGLXDESTROYWINDOWPROC) 389 glXGetProcAddressARB((GLubyte *) "glXDestroyWindow"); 390 } 391 else if ( ext_version_supported ) { 392 choose_fbconfig = (PFNGLXCHOOSEFBCONFIGPROC) 393 glXGetProcAddressARB((GLubyte *) "glXChooseFBConfigSGIX"); 394 get_visual_from_fbconfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) 395 glXGetProcAddressARB((GLubyte *) "glXGetVisualFromFBConfigSGIX"); 396 create_new_context = (PFNGLXCREATENEWCONTEXTPROC) 397 glXGetProcAddressARB((GLubyte *) "glXCreateContextWithConfigSGIX"); 398 create_window = dummy_create_window; 399 destroy_window = dummy_destroy_window; 400 } 401 else { 402 printf( "This demo requires either GLX 1.3 or %s be supported.\n", 403 ext_name ); 404 exit(1); 405 } 406 407 if ( choose_fbconfig == NULL ) { 408 printf( "glXChooseFBConfig not found!\n" ); 409 exit(1); 410 } 411 412 if ( get_visual_from_fbconfig == NULL ) { 413 printf( "glXGetVisualFromFBConfig not found!\n" ); 414 exit(1); 415 } 416 417 if ( create_new_context == NULL ) { 418 printf( "glXCreateNewContext not found!\n" ); 419 exit(1); 420 } 421} 422 423 424/* 425 * Create an RGB, double-buffered window. 426 * Return the window and context handles. 427 */ 428static void 429make_window( Display *dpy, const char *name, 430 int x, int y, int width, int height, 431 Window *winRet, GLXWindow *glxWinRet, GLXContext *ctxRet) 432{ 433 int attrib[] = { GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 434 GLX_RENDER_TYPE, GLX_RGBA_BIT, 435 GLX_RED_SIZE, 1, 436 GLX_GREEN_SIZE, 1, 437 GLX_BLUE_SIZE, 1, 438 GLX_DOUBLEBUFFER, GL_TRUE, 439 GLX_DEPTH_SIZE, 1, 440 None }; 441 GLXFBConfig * fbconfig; 442 int num_configs; 443 int scrnum; 444 int i; 445 XSetWindowAttributes attr; 446 unsigned long mask; 447 Window root; 448 Window win; 449 GLXWindow glxWin; 450 GLXContext ctx; 451 XVisualInfo *visinfo; 452 453 scrnum = DefaultScreen( dpy ); 454 root = RootWindow( dpy, scrnum ); 455 456 init_fbconfig_functions(dpy, scrnum); 457 fbconfig = (*choose_fbconfig)(dpy, scrnum, attrib, & num_configs); 458 if (fbconfig == NULL) { 459 printf("Error: couldn't get an RGB, Double-buffered visual\n"); 460 exit(1); 461 } 462 463 printf("\nThe following fbconfigs meet the requirements. The first one " 464 "will be used.\n\n"); 465 for ( i = 0 ; i < num_configs ; i++ ) { 466 PrintFBConfigInfo(dpy, scrnum, fbconfig[i], GL_TRUE); 467 } 468 469 /* window attributes */ 470 visinfo = (*get_visual_from_fbconfig)(dpy, fbconfig[0]); 471 assert(visinfo != NULL); 472 attr.background_pixel = 0; 473 attr.border_pixel = 0; 474 attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); 475 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; 476 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 477 478 win = XCreateWindow( dpy, root, 0, 0, width, height, 479 0, visinfo->depth, InputOutput, 480 visinfo->visual, mask, &attr ); 481 482 /* set hints and properties */ 483 { 484 XSizeHints sizehints; 485 sizehints.x = x; 486 sizehints.y = y; 487 sizehints.width = width; 488 sizehints.height = height; 489 sizehints.flags = USSize | USPosition; 490 XSetNormalHints(dpy, win, &sizehints); 491 XSetStandardProperties(dpy, win, name, name, 492 None, (char **)NULL, 0, &sizehints); 493 } 494 495 glxWin = (*create_window)(dpy, fbconfig[0], win, NULL); 496 497 ctx = (*create_new_context)(dpy, fbconfig[0], GLX_RGBA_TYPE, NULL, GL_TRUE); 498 if (!ctx) { 499 printf("Error: glXCreateNewContext failed\n"); 500 exit(1); 501 } 502 503 XFree(fbconfig); 504 505 *glxWinRet = glxWin; 506 *winRet = win; 507 *ctxRet = ctx; 508} 509 510 511static void 512event_loop(Display *dpy, GLXWindow win) 513{ 514 while (1) { 515 while (XPending(dpy) > 0) { 516 XEvent event; 517 XNextEvent(dpy, &event); 518 switch (event.type) { 519 case Expose: 520 /* we'll redraw below */ 521 break; 522 case ConfigureNotify: 523 reshape(event.xconfigure.width, event.xconfigure.height); 524 break; 525 case KeyPress: 526 { 527 char buffer[10]; 528 int code; 529 code = XLookupKeysym(&event.xkey, 0); 530 if (code == XK_Left) { 531 view_roty += 5.0; 532 } 533 else if (code == XK_Right) { 534 view_roty -= 5.0; 535 } 536 else if (code == XK_Up) { 537 view_rotx += 5.0; 538 } 539 else if (code == XK_Down) { 540 view_rotx -= 5.0; 541 } 542 else { 543 XLookupString(&event.xkey, buffer, sizeof(buffer), 544 NULL, NULL); 545 if (buffer[0] == 27) { 546 /* escape */ 547 return; 548 } 549 } 550 } 551 } 552 } 553 554 /* next frame */ 555 angle += 2.0; 556 557 draw(); 558 glXSwapBuffers(dpy, win); 559 560 /* calc framerate */ 561 { 562 static int t0 = -1; 563 static int frames = 0; 564 int t = current_time(); 565 566 if (t0 < 0) 567 t0 = t; 568 569 frames++; 570 571 if (t - t0 >= 5.0) { 572 GLfloat seconds = t - t0; 573 GLfloat fps = frames / seconds; 574 printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames, seconds, 575 fps); 576 fflush(stdout); 577 t0 = t; 578 frames = 0; 579 } 580 } 581 } 582} 583 584 585int 586main(int argc, char *argv[]) 587{ 588 Display *dpy; 589 Window win; 590 GLXWindow glxWin; 591 GLXContext ctx; 592 const char *dpyName = NULL; 593 GLboolean printInfo = GL_FALSE; 594 int i; 595 596 for (i = 1; i < argc; i++) { 597 if (strcmp(argv[i], "-display") == 0) { 598 dpyName = argv[i+1]; 599 i++; 600 } 601 else if (strcmp(argv[i], "-info") == 0) { 602 printInfo = GL_TRUE; 603 } 604 } 605 606 dpy = XOpenDisplay(dpyName); 607 if (!dpy) { 608 printf("Error: couldn't open display %s\n", XDisplayName(dpyName)); 609 return -1; 610 } 611 612 make_window(dpy, "glxgears", 0, 0, 300, 300, &win, &glxWin, &ctx); 613 XMapWindow(dpy, win); 614 glXMakeCurrent(dpy, glxWin, ctx); 615 616 if (printInfo) { 617 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 618 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); 619 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR)); 620 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); 621 } 622 623 init(); 624 625 event_loop(dpy, glxWin); 626 627 glXDestroyContext(dpy, ctx); 628 destroy_window(dpy, glxWin); 629 XDestroyWindow(dpy, win); 630 XCloseDisplay(dpy); 631 632 return 0; 633} 634