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 * Version of glxgears that creates/destroys the rendering context for each 24 * frame. Also periodically destroy/recreate the window. 25 * Good for finding memory leaks, etc. 26 * 27 * Command line options: 28 * -info print GL implementation information 29 * 30 */ 31 32 33#include <assert.h> 34#include <math.h> 35#include <stdlib.h> 36#include <stdio.h> 37#include <string.h> 38#include <X11/Xlib.h> 39#include <X11/keysym.h> 40#include <GL/gl.h> 41#include <GL/glx.h> 42 43 44#define BENCHMARK 45 46#ifdef BENCHMARK 47 48/* XXX this probably isn't very portable */ 49 50#include <sys/time.h> 51#include <unistd.h> 52 53/* return current time (in seconds) */ 54static double 55current_time(void) 56{ 57 struct timeval tv; 58#ifdef __VMS 59 (void) gettimeofday(&tv, NULL ); 60#else 61 struct timezone tz; 62 (void) gettimeofday(&tv, &tz); 63#endif 64 return (double) tv.tv_sec + tv.tv_usec / 1000000.0; 65} 66 67#else /*BENCHMARK*/ 68 69/* dummy */ 70static double 71current_time(void) 72{ 73 /* update this function for other platforms! */ 74 static double t = 0.0; 75 static int warn = 1; 76 if (warn) { 77 fprintf(stderr, "Warning: current_time() not implemented!!\n"); 78 warn = 0; 79 } 80 return t += 1.0; 81} 82 83#endif /*BENCHMARK*/ 84 85 86 87#ifndef M_PI 88#define M_PI 3.14159265 89#endif 90 91 92static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; 93static GLint gear1, gear2, gear3; 94static GLfloat angle = 0.0; 95 96static XVisualInfo *visinfo = NULL; 97static int WinWidth = 300, WinHeight = 300; 98 99 100/* 101 * 102 * Draw a gear wheel. You'll probably want to call this function when 103 * building a display list since we do a lot of trig here. 104 * 105 * Input: inner_radius - radius of hole at center 106 * outer_radius - radius at center of teeth 107 * width - width of gear 108 * teeth - number of teeth 109 * tooth_depth - depth of tooth 110 */ 111static void 112gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, 113 GLint teeth, GLfloat tooth_depth) 114{ 115 GLint i; 116 GLfloat r0, r1, r2; 117 GLfloat angle, da; 118 GLfloat u, v, len; 119 120 r0 = inner_radius; 121 r1 = outer_radius - tooth_depth / 2.0; 122 r2 = outer_radius + tooth_depth / 2.0; 123 124 da = 2.0 * M_PI / teeth / 4.0; 125 126 glShadeModel(GL_FLAT); 127 128 glNormal3f(0.0, 0.0, 1.0); 129 130 /* draw front face */ 131 glBegin(GL_QUAD_STRIP); 132 for (i = 0; i <= teeth; i++) { 133 angle = i * 2.0 * M_PI / teeth; 134 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 135 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 136 if (i < teeth) { 137 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 138 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 139 width * 0.5); 140 } 141 } 142 glEnd(); 143 144 /* draw front sides of teeth */ 145 glBegin(GL_QUADS); 146 da = 2.0 * M_PI / teeth / 4.0; 147 for (i = 0; i < teeth; i++) { 148 angle = i * 2.0 * M_PI / teeth; 149 150 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 151 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); 152 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 153 width * 0.5); 154 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 155 width * 0.5); 156 } 157 glEnd(); 158 159 glNormal3f(0.0, 0.0, -1.0); 160 161 /* draw back face */ 162 glBegin(GL_QUAD_STRIP); 163 for (i = 0; i <= teeth; i++) { 164 angle = i * 2.0 * M_PI / teeth; 165 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 166 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 167 if (i < teeth) { 168 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 169 -width * 0.5); 170 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 171 } 172 } 173 glEnd(); 174 175 /* draw back sides of teeth */ 176 glBegin(GL_QUADS); 177 da = 2.0 * M_PI / teeth / 4.0; 178 for (i = 0; i < teeth; i++) { 179 angle = i * 2.0 * M_PI / teeth; 180 181 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 182 -width * 0.5); 183 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 184 -width * 0.5); 185 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); 186 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 187 } 188 glEnd(); 189 190 /* draw outward faces of teeth */ 191 glBegin(GL_QUAD_STRIP); 192 for (i = 0; i < teeth; i++) { 193 angle = i * 2.0 * M_PI / teeth; 194 195 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 196 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 197 u = r2 * cos(angle + da) - r1 * cos(angle); 198 v = r2 * sin(angle + da) - r1 * sin(angle); 199 len = sqrt(u * u + v * v); 200 u /= len; 201 v /= len; 202 glNormal3f(v, -u, 0.0); 203 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); 204 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); 205 glNormal3f(cos(angle), sin(angle), 0.0); 206 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 207 width * 0.5); 208 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 209 -width * 0.5); 210 u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); 211 v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); 212 glNormal3f(v, -u, 0.0); 213 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 214 width * 0.5); 215 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 216 -width * 0.5); 217 glNormal3f(cos(angle), sin(angle), 0.0); 218 } 219 220 glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5); 221 glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5); 222 223 glEnd(); 224 225 glShadeModel(GL_SMOOTH); 226 227 /* draw inside radius cylinder */ 228 glBegin(GL_QUAD_STRIP); 229 for (i = 0; i <= teeth; i++) { 230 angle = i * 2.0 * M_PI / teeth; 231 glNormal3f(-cos(angle), -sin(angle), 0.0); 232 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 233 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 234 } 235 glEnd(); 236} 237 238 239static void 240do_draw(void) 241{ 242 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 243 244 glPushMatrix(); 245 glRotatef(view_rotx, 1.0, 0.0, 0.0); 246 glRotatef(view_roty, 0.0, 1.0, 0.0); 247 glRotatef(view_rotz, 0.0, 0.0, 1.0); 248 249 glPushMatrix(); 250 glTranslatef(-3.0, -2.0, 0.0); 251 glRotatef(angle, 0.0, 0.0, 1.0); 252 glCallList(gear1); 253 glPopMatrix(); 254 255 glPushMatrix(); 256 glTranslatef(3.1, -2.0, 0.0); 257 glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0); 258 glCallList(gear2); 259 glPopMatrix(); 260 261 glPushMatrix(); 262 glTranslatef(-3.1, 4.2, 0.0); 263 glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0); 264 glCallList(gear3); 265 glPopMatrix(); 266 267 glPopMatrix(); 268} 269 270 271/* new window size or exposure */ 272static void 273reshape(int width, int height) 274{ 275 glViewport(0, 0, (GLint) width, (GLint) height); 276 277 { 278 GLfloat h = (GLfloat) height / (GLfloat) width; 279 280 glMatrixMode(GL_PROJECTION); 281 glLoadIdentity(); 282 glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0); 283 } 284 285 glMatrixMode(GL_MODELVIEW); 286 glLoadIdentity(); 287 glTranslatef(0.0, 0.0, -40.0); 288} 289 290 291static void 292init(void) 293{ 294 static GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 }; 295 static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 }; 296 static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 }; 297 static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 }; 298 299 glLightfv(GL_LIGHT0, GL_POSITION, pos); 300 glEnable(GL_CULL_FACE); 301 glEnable(GL_LIGHTING); 302 glEnable(GL_LIGHT0); 303 glEnable(GL_DEPTH_TEST); 304 305 /* make the gears */ 306 gear1 = glGenLists(1); 307 glNewList(gear1, GL_COMPILE); 308 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); 309 gear(1.0, 4.0, 1.0, 20, 0.7); 310 glEndList(); 311 312 gear2 = glGenLists(1); 313 glNewList(gear2, GL_COMPILE); 314 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); 315 gear(0.5, 2.0, 2.0, 10, 0.7); 316 glEndList(); 317 318 gear3 = glGenLists(1); 319 glNewList(gear3, GL_COMPILE); 320 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); 321 gear(1.3, 2.0, 0.5, 10, 0.7); 322 glEndList(); 323 324 glEnable(GL_NORMALIZE); 325} 326 327 328static void 329draw( Display *dpy, Window win ) 330{ 331 GLXContext ctx; 332 333 ctx = glXCreateContext( dpy, visinfo, NULL, True ); 334 if (!ctx) { 335 printf("Error: glXCreateContext failed\n"); 336 exit(1); 337 } 338 339 glXMakeCurrent(dpy, win, ctx); 340 341 init(); 342 343 reshape(WinWidth, WinHeight); 344 345 do_draw(); 346 347 glDeleteLists(gear1, 1); 348 glDeleteLists(gear2, 1); 349 glDeleteLists(gear3, 1); 350 351 glXSwapBuffers(dpy, win); 352 glXDestroyContext(dpy, ctx); 353} 354 355 356/* 357 * Create an RGB, double-buffered window. 358 * Return the window and context handles. 359 */ 360static void 361make_window( Display *dpy, const char *name, 362 int x, int y, int width, int height, 363 Window *winRet) 364{ 365 int attribs[] = { GLX_RGBA, 366 GLX_RED_SIZE, 1, 367 GLX_GREEN_SIZE, 1, 368 GLX_BLUE_SIZE, 1, 369 GLX_DOUBLEBUFFER, 370 GLX_DEPTH_SIZE, 1, 371 None }; 372 int scrnum; 373 XSetWindowAttributes attr; 374 unsigned long mask; 375 Window root; 376 Window win; 377 378 scrnum = DefaultScreen( dpy ); 379 root = RootWindow( dpy, scrnum ); 380 381 if (visinfo) 382 XFree(visinfo); 383 384 visinfo = glXChooseVisual( dpy, scrnum, attribs ); 385 if (!visinfo) { 386 printf("Error: couldn't get an RGB, Double-buffered visual\n"); 387 exit(1); 388 } 389 390 /* window attributes */ 391 attr.background_pixel = 0; 392 attr.border_pixel = 0; 393 attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); 394 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; 395 attr.override_redirect = 0; 396 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect; 397 398 win = XCreateWindow( dpy, root, x, y, width, height, 399 0, visinfo->depth, InputOutput, 400 visinfo->visual, mask, &attr ); 401 402 /* set hints and properties */ 403 { 404 XSizeHints sizehints; 405 sizehints.x = x; 406 sizehints.y = y; 407 sizehints.width = width; 408 sizehints.height = height; 409 sizehints.flags = USSize | USPosition; 410 XSetNormalHints(dpy, win, &sizehints); 411 XSetStandardProperties(dpy, win, name, name, 412 None, (char **)NULL, 0, &sizehints); 413 } 414 415 *winRet = win; 416} 417 418 419static void 420event_loop(Display *dpy) 421{ 422 Window win; 423 make_window(dpy, "glxgears", 0, 0, WinWidth, WinHeight, &win); 424 XMapWindow(dpy, win); 425 426 while (1) { 427 while (XPending(dpy) > 0) { 428 XEvent event; 429 XNextEvent(dpy, &event); 430 switch (event.type) { 431 case Expose: 432 /* we'll redraw below */ 433 break; 434 case ConfigureNotify: 435 WinWidth = event.xconfigure.width; 436 WinHeight = event.xconfigure.height; 437 break; 438 case KeyPress: 439 { 440 char buffer[10]; 441 int code; 442 code = XLookupKeysym(&event.xkey, 0); 443 if (code == XK_Left) { 444 view_roty += 5.0; 445 } 446 else if (code == XK_Right) { 447 view_roty -= 5.0; 448 } 449 else if (code == XK_Up) { 450 view_rotx += 5.0; 451 } 452 else if (code == XK_Down) { 453 view_rotx -= 5.0; 454 } 455 else { 456 XLookupString(&event.xkey, buffer, sizeof(buffer), 457 NULL, NULL); 458 if (buffer[0] == 27) { 459 /* escape */ 460 return; 461 } 462 } 463 } 464 } 465 } 466 467 { 468 static int frames = 0; 469 static double tRot0 = -1.0, tRate0 = -1.0; 470 double dt, t = current_time(); 471 if (tRot0 < 0.0) 472 tRot0 = t; 473 dt = t - tRot0; 474 tRot0 = t; 475 476 /* advance rotation for next frame */ 477 angle += 70.0 * dt; /* 70 degrees per second */ 478 if (angle > 3600.0) 479 angle -= 3600.0; 480 481 draw( dpy, win ); 482 483 frames++; 484 485 if (tRate0 < 0.0) 486 tRate0 = t; 487 488 if (t - tRate0 >= 1.0) { 489 GLfloat seconds = t - tRate0; 490 GLfloat fps = frames / seconds; 491 printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames, seconds, 492 fps); 493 fflush(stdout); 494 tRate0 = t; 495 496 /* Destroy window and create new one */ 497 XDestroyWindow(dpy, win); 498 make_window(dpy, "glxgears", 499 (int)(fps * 100) % 100, (int)(fps * 100) % 100, /* x,y */ 500 WinWidth, WinHeight, &win); 501 XMapWindow(dpy, win); 502 503 frames = 0; 504 } 505 } 506 } 507} 508 509 510int 511main(int argc, char *argv[]) 512{ 513 Display *dpy; 514 char *dpyName = NULL; 515 GLboolean printInfo = GL_FALSE; 516 int i; 517 518 for (i = 1; i < argc; i++) { 519 if (strcmp(argv[i], "-display") == 0) { 520 dpyName = argv[i+1]; 521 i++; 522 } 523 else if (strcmp(argv[i], "-info") == 0) { 524 printInfo = GL_TRUE; 525 } 526 else 527 printf("Warrning: unknown parameter: %s\n", argv[i]); 528 } 529 530 dpy = XOpenDisplay(dpyName); 531 if (!dpy) { 532 fprintf(stderr, "Error: couldn't open display %s\n", 533 XDisplayName(dpyName)); 534 return -1; 535 } 536 537 if (printInfo) { 538 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 539 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); 540 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR)); 541 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); 542 } 543 544 event_loop(dpy); 545 546 XCloseDisplay(dpy); 547 548 return 0; 549} 550