2 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
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:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
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.
23 * This is a port of the infamous "gears" demo to straight GLX (i.e. no GLUT)
24 * Port by Brian Paul 23 March 2001
26 * Command line options:
27 * -info print GL implementation information
28 * -stereo use stereo enabled GLX visual
38 #include <X11/keysym.h>
42 #ifndef GLX_MESA_swap_control
43 #define GLX_MESA_swap_control 1
44 typedef int (*PFNGLXGETSWAPINTERVALMESAPROC
)(void);
48 static int is_glx_extension_supported(Display
*dpy
, const char *query
);
50 static void query_vsync(Display
*dpy
);
56 /* XXX this probably isn't very portable */
61 /* return current time (in seconds) */
67 (void) gettimeofday(&tv
, NULL
);
70 (void) gettimeofday(&tv
, &tz
);
72 return (double) tv
.tv_sec
+ tv
.tv_usec
/ 1000000.0;
81 /* update this function for other platforms! */
82 static double t
= 0.0;
85 fprintf(stderr
, "Warning: current_time() not implemented!!\n");
96 #define M_PI 3.14159265
100 /** Event handler results: */
105 static GLfloat view_rotx
= 20.0, view_roty
= 30.0, view_rotz
= 0.0;
106 static GLint gear1
, gear2
, gear3
;
107 static GLfloat angle
= 0.0;
109 static GLboolean fullscreen
= GL_FALSE
; /* Create a single fullscreen window */
110 static GLboolean stereo
= GL_FALSE
; /* Enable stereo. */
111 static GLboolean animate
= GL_TRUE
; /* Animation */
112 static GLfloat eyesep
= 5.0; /* Eye separation. */
113 static GLfloat fix_point
= 40.0; /* Fixation point distance. */
114 static GLfloat left
, right
, asp
; /* Stereo frustum params. */
119 * Draw a gear wheel. You'll probably want to call this function when
120 * building a display list since we do a lot of trig here.
122 * Input: inner_radius - radius of hole at center
123 * outer_radius - radius at center of teeth
124 * width - width of gear
125 * teeth - number of teeth
126 * tooth_depth - depth of tooth
129 gear(GLfloat inner_radius
, GLfloat outer_radius
, GLfloat width
,
130 GLint teeth
, GLfloat tooth_depth
)
138 r1
= outer_radius
- tooth_depth
/ 2.0;
139 r2
= outer_radius
+ tooth_depth
/ 2.0;
141 da
= 2.0 * M_PI
/ teeth
/ 4.0;
143 glShadeModel(GL_FLAT
);
145 glNormal3f(0.0, 0.0, 1.0);
147 /* draw front face */
148 glBegin(GL_QUAD_STRIP
);
149 for (i
= 0; i
<= teeth
; i
++) {
150 angle
= i
* 2.0 * M_PI
/ teeth
;
151 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
152 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
154 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
155 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
161 /* draw front sides of teeth */
163 da
= 2.0 * M_PI
/ teeth
/ 4.0;
164 for (i
= 0; i
< teeth
; i
++) {
165 angle
= i
* 2.0 * M_PI
/ teeth
;
167 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
168 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
169 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
171 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
176 glNormal3f(0.0, 0.0, -1.0);
179 glBegin(GL_QUAD_STRIP
);
180 for (i
= 0; i
<= teeth
; i
++) {
181 angle
= i
* 2.0 * M_PI
/ teeth
;
182 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
183 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
185 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
187 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
192 /* draw back sides of teeth */
194 da
= 2.0 * M_PI
/ teeth
/ 4.0;
195 for (i
= 0; i
< teeth
; i
++) {
196 angle
= i
* 2.0 * M_PI
/ teeth
;
198 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
200 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
202 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
203 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
207 /* draw outward faces of teeth */
208 glBegin(GL_QUAD_STRIP
);
209 for (i
= 0; i
< teeth
; i
++) {
210 angle
= i
* 2.0 * M_PI
/ teeth
;
212 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
213 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
214 u
= r2
* cos(angle
+ da
) - r1
* cos(angle
);
215 v
= r2
* sin(angle
+ da
) - r1
* sin(angle
);
216 len
= sqrt(u
* u
+ v
* v
);
219 glNormal3f(v
, -u
, 0.0);
220 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
221 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
222 glNormal3f(cos(angle
), sin(angle
), 0.0);
223 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
225 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
227 u
= r1
* cos(angle
+ 3 * da
) - r2
* cos(angle
+ 2 * da
);
228 v
= r1
* sin(angle
+ 3 * da
) - r2
* sin(angle
+ 2 * da
);
229 glNormal3f(v
, -u
, 0.0);
230 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
232 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
234 glNormal3f(cos(angle
), sin(angle
), 0.0);
237 glVertex3f(r1
* cos(0), r1
* sin(0), width
* 0.5);
238 glVertex3f(r1
* cos(0), r1
* sin(0), -width
* 0.5);
242 glShadeModel(GL_SMOOTH
);
244 /* draw inside radius cylinder */
245 glBegin(GL_QUAD_STRIP
);
246 for (i
= 0; i
<= teeth
; i
++) {
247 angle
= i
* 2.0 * M_PI
/ teeth
;
248 glNormal3f(-cos(angle
), -sin(angle
), 0.0);
249 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
250 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
259 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
262 glRotatef(view_rotx
, 1.0, 0.0, 0.0);
263 glRotatef(view_roty
, 0.0, 1.0, 0.0);
264 glRotatef(view_rotz
, 0.0, 0.0, 1.0);
267 glTranslatef(-3.0, -2.0, 0.0);
268 glRotatef(angle
, 0.0, 0.0, 1.0);
273 glTranslatef(3.1, -2.0, 0.0);
274 glRotatef(-2.0 * angle
- 9.0, 0.0, 0.0, 1.0);
279 glTranslatef(-3.1, 4.2, 0.0);
280 glRotatef(-2.0 * angle
- 25.0, 0.0, 0.0, 1.0);
292 /* First left eye. */
293 glDrawBuffer(GL_BACK_LEFT
);
295 glMatrixMode(GL_PROJECTION
);
297 glFrustum(left
, right
, -asp
, asp
, 5.0, 60.0);
299 glMatrixMode(GL_MODELVIEW
);
302 glTranslated(+0.5 * eyesep
, 0.0, 0.0);
306 /* Then right eye. */
307 glDrawBuffer(GL_BACK_RIGHT
);
309 glMatrixMode(GL_PROJECTION
);
311 glFrustum(-right
, -left
, -asp
, asp
, 5.0, 60.0);
313 glMatrixMode(GL_MODELVIEW
);
316 glTranslated(-0.5 * eyesep
, 0.0, 0.0);
326 /** Draw single frame, do SwapBuffers, compute FPS */
328 draw_frame(Display
*dpy
, Window win
)
330 static int frames
= 0;
331 static double tRot0
= -1.0, tRate0
= -1.0;
332 double dt
, t
= current_time();
340 /* advance rotation for next frame */
341 angle
+= 70.0 * dt
; /* 70 degrees per second */
347 glXSwapBuffers(dpy
, win
);
353 if (t
- tRate0
>= 5.0) {
354 GLfloat seconds
= t
- tRate0
;
355 GLfloat fps
= frames
/ seconds
;
356 printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames
, seconds
,
364 /* new window size or exposure */
366 reshape(int width
, int height
)
368 glViewport(0, 0, (GLint
) width
, (GLint
) height
);
373 asp
= (GLfloat
) height
/ (GLfloat
) width
;
374 w
= fix_point
* (1.0 / 5.0);
376 left
= -5.0 * ((w
- 0.5 * eyesep
) / fix_point
);
377 right
= 5.0 * ((w
+ 0.5 * eyesep
) / fix_point
);
380 GLfloat h
= (GLfloat
) height
/ (GLfloat
) width
;
382 glMatrixMode(GL_PROJECTION
);
384 glFrustum(-1.0, 1.0, -h
, h
, 5.0, 60.0);
387 glMatrixMode(GL_MODELVIEW
);
389 glTranslatef(0.0, 0.0, -40.0);
397 static GLfloat pos
[4] = { 5.0, 5.0, 10.0, 0.0 };
398 static GLfloat red
[4] = { 0.8, 0.1, 0.0, 1.0 };
399 static GLfloat green
[4] = { 0.0, 0.8, 0.2, 1.0 };
400 static GLfloat blue
[4] = { 0.2, 0.2, 1.0, 1.0 };
402 glLightfv(GL_LIGHT0
, GL_POSITION
, pos
);
403 glEnable(GL_CULL_FACE
);
404 glEnable(GL_LIGHTING
);
406 glEnable(GL_DEPTH_TEST
);
409 gear1
= glGenLists(1);
410 glNewList(gear1
, GL_COMPILE
);
411 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, red
);
412 gear(1.0, 4.0, 1.0, 20, 0.7);
415 gear2
= glGenLists(1);
416 glNewList(gear2
, GL_COMPILE
);
417 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, green
);
418 gear(0.5, 2.0, 2.0, 10, 0.7);
421 gear3
= glGenLists(1);
422 glNewList(gear3
, GL_COMPILE
);
423 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, blue
);
424 gear(1.3, 2.0, 0.5, 10, 0.7);
427 glEnable(GL_NORMALIZE
);
432 * Remove window border/decorations.
435 no_border( Display
*dpy
, Window w
)
437 static const unsigned MWM_HINTS_DECORATIONS
= (1 << 1);
438 static const int PROP_MOTIF_WM_HINTS_ELEMENTS
= 5;
443 unsigned long functions
;
444 unsigned long decorations
;
446 unsigned long status
;
449 PropMotifWmHints motif_hints
;
451 unsigned long flags
= 0;
453 /* setup the property */
454 motif_hints
.flags
= MWM_HINTS_DECORATIONS
;
455 motif_hints
.decorations
= flags
;
457 /* get the atom for the property */
458 prop
= XInternAtom( dpy
, "_MOTIF_WM_HINTS", True
);
460 /* something went wrong! */
464 /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */
467 XChangeProperty( dpy
, w
, /* display, window */
468 prop
, proptype
, /* property, type */
469 32, /* format: 32-bit datums */
470 PropModeReplace
, /* mode */
471 (unsigned char *) &motif_hints
, /* data */
472 PROP_MOTIF_WM_HINTS_ELEMENTS
/* nelements */
478 * Create an RGB, double-buffered window.
479 * Return the window and context handles.
482 make_window( Display
*dpy
, const char *name
,
483 int x
, int y
, int width
, int height
,
484 Window
*winRet
, GLXContext
*ctxRet
)
486 int attribs
[] = { GLX_RGBA
,
493 int stereoAttribs
[] = { GLX_RGBA
,
502 XSetWindowAttributes attr
;
507 XVisualInfo
*visinfo
;
509 scrnum
= DefaultScreen( dpy
);
510 root
= RootWindow( dpy
, scrnum
);
514 width
= DisplayWidth( dpy
, scrnum
);
515 height
= DisplayHeight( dpy
, scrnum
);
519 visinfo
= glXChooseVisual( dpy
, scrnum
, stereoAttribs
);
521 visinfo
= glXChooseVisual( dpy
, scrnum
, attribs
);
524 printf("Error: couldn't get an RGB, "
525 "Double-buffered, Stereo visual\n");
527 printf("Error: couldn't get an RGB, Double-buffered visual\n");
531 /* window attributes */
532 attr
.background_pixel
= 0;
533 attr
.border_pixel
= 0;
534 attr
.colormap
= XCreateColormap( dpy
, root
, visinfo
->visual
, AllocNone
);
535 attr
.event_mask
= StructureNotifyMask
| ExposureMask
| KeyPressMask
;
536 /* XXX this is a bad way to get a borderless window! */
537 mask
= CWBackPixel
| CWBorderPixel
| CWColormap
| CWEventMask
;
539 win
= XCreateWindow( dpy
, root
, x
, y
, width
, height
,
540 0, visinfo
->depth
, InputOutput
,
541 visinfo
->visual
, mask
, &attr
);
546 /* set hints and properties */
548 XSizeHints sizehints
;
551 sizehints
.width
= width
;
552 sizehints
.height
= height
;
553 sizehints
.flags
= USSize
| USPosition
;
554 XSetNormalHints(dpy
, win
, &sizehints
);
555 XSetStandardProperties(dpy
, win
, name
, name
,
556 None
, (char **)NULL
, 0, &sizehints
);
559 ctx
= glXCreateContext( dpy
, visinfo
, NULL
, True
);
561 printf("Error: glXCreateContext failed\n");
573 * Determine whether or not a GLX extension is supported.
576 is_glx_extension_supported(Display
*dpy
, const char *query
)
578 const int scrnum
= DefaultScreen(dpy
);
579 const char *glx_extensions
= NULL
;
580 const size_t len
= strlen(query
);
583 if (glx_extensions
== NULL
) {
584 glx_extensions
= glXQueryExtensionsString(dpy
, scrnum
);
587 ptr
= strstr(glx_extensions
, query
);
588 return ((ptr
!= NULL
) && ((ptr
[len
] == ' ') || (ptr
[len
] == '\0')));
593 * Attempt to determine whether or not the display is synched to vblank.
596 query_vsync(Display
*dpy
)
601 if (is_glx_extension_supported(dpy
, "GLX_MESA_swap_control")) {
602 PFNGLXGETSWAPINTERVALMESAPROC pglXGetSwapIntervalMESA
=
603 (PFNGLXGETSWAPINTERVALMESAPROC
)
604 glXGetProcAddressARB((const GLubyte
*) "glXGetSwapIntervalMESA");
606 interval
= (*pglXGetSwapIntervalMESA
)();
607 } else if (is_glx_extension_supported(dpy
, "GLX_SGI_swap_control")) {
608 /* The default swap interval with this extension is 1. Assume that it
609 * is set to the default.
611 * Many Mesa-based drivers default to 0, but all of these drivers also
612 * export GLX_MESA_swap_control. In that case, this branch will never
613 * be taken, and the correct result should be reported.
620 printf("Running synchronized to the vertical refresh. The framerate should be\n");
622 printf("approximately the same as the monitor refresh rate.\n");
623 } else if (interval
> 1) {
624 printf("approximately 1/%d the monitor refresh rate.\n",
631 * Handle one X event.
632 * \return NOP, EXIT or DRAW
635 handle_event(Display
*dpy
, Window win
, XEvent
*event
)
640 switch (event
->type
) {
643 case ConfigureNotify
:
644 reshape(event
->xconfigure
.width
, event
->xconfigure
.height
);
650 code
= XLookupKeysym(&event
->xkey
, 0);
651 if (code
== XK_Left
) {
654 else if (code
== XK_Right
) {
657 else if (code
== XK_Up
) {
660 else if (code
== XK_Down
) {
664 r
= XLookupString(&event
->xkey
, buffer
, sizeof(buffer
),
666 if (buffer
[0] == 27) {
670 else if (buffer
[0] == 'a' || buffer
[0] == 'A') {
682 event_loop(Display
*dpy
, Window win
)
686 while (!animate
|| XPending(dpy
) > 0) {
688 XNextEvent(dpy
, &event
);
689 op
= handle_event(dpy
, win
, &event
);
696 draw_frame(dpy
, win
);
705 printf(" -display <displayname> set the display to run on\n");
706 printf(" -stereo run in stereo mode\n");
707 printf(" -fullscreen run in fullscreen mode\n");
708 printf(" -info display OpenGL renderer info\n");
709 printf(" -geometry WxH+X+Y window geometry\n");
714 main(int argc
, char *argv
[])
716 unsigned int winWidth
= 300, winHeight
= 300;
721 char *dpyName
= NULL
;
722 GLboolean printInfo
= GL_FALSE
;
725 for (i
= 1; i
< argc
; i
++) {
726 if (strcmp(argv
[i
], "-display") == 0) {
730 else if (strcmp(argv
[i
], "-info") == 0) {
733 else if (strcmp(argv
[i
], "-stereo") == 0) {
736 else if (strcmp(argv
[i
], "-fullscreen") == 0) {
737 fullscreen
= GL_TRUE
;
739 else if (i
< argc
-1 && strcmp(argv
[i
], "-geometry") == 0) {
740 XParseGeometry(argv
[i
+1], &x
, &y
, &winWidth
, &winHeight
);
749 dpy
= XOpenDisplay(dpyName
);
751 printf("Error: couldn't open display %s\n",
752 dpyName
? dpyName
: getenv("DISPLAY"));
756 make_window(dpy
, "glxgears", x
, y
, winWidth
, winHeight
, &win
, &ctx
);
757 XMapWindow(dpy
, win
);
758 glXMakeCurrent(dpy
, win
, ctx
);
762 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER
));
763 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION
));
764 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR
));
765 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS
));
770 /* Set initial projection/viewing transformation.
771 * We can't be sure we'll get a ConfigureNotify event when the window
774 reshape(winWidth
, winHeight
);
776 event_loop(dpy
, win
);
778 glDeleteLists(gear1
, 1);
779 glDeleteLists(gear2
, 1);
780 glDeleteLists(gear3
, 1);
781 glXDestroyContext(dpy
, ctx
);
782 XDestroyWindow(dpy
, win
);