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