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 static int is_glx_extension_supported(Display
*dpy
, const char *query
);
44 static void query_vsync(Display
*dpy
);
50 /* XXX this probably isn't very portable */
55 /* return current time (in seconds) */
61 (void) gettimeofday(&tv
, NULL
);
64 (void) gettimeofday(&tv
, &tz
);
66 return (double) tv
.tv_sec
+ tv
.tv_usec
/ 1000000.0;
75 /* update this function for other platforms! */
76 static double t
= 0.0;
79 fprintf(stderr
, "Warning: current_time() not implemented!!\n");
90 #define M_PI 3.14159265
94 /** Event handler results: */
99 static GLfloat view_rotx
= 20.0, view_roty
= 30.0, view_rotz
= 0.0;
100 static GLint gear1
, gear2
, gear3
;
101 static GLfloat angle
= 0.0;
103 static GLboolean fullscreen
= GL_FALSE
; /* Create a single fullscreen window */
104 static GLboolean stereo
= GL_FALSE
; /* Enable stereo. */
105 static GLboolean animate
= GL_TRUE
; /* Animation */
106 static GLfloat eyesep
= 5.0; /* Eye separation. */
107 static GLfloat fix_point
= 40.0; /* Fixation point distance. */
108 static GLfloat left
, right
, asp
; /* Stereo frustum params. */
113 * Draw a gear wheel. You'll probably want to call this function when
114 * building a display list since we do a lot of trig here.
116 * Input: inner_radius - radius of hole at center
117 * outer_radius - radius at center of teeth
118 * width - width of gear
119 * teeth - number of teeth
120 * tooth_depth - depth of tooth
123 gear(GLfloat inner_radius
, GLfloat outer_radius
, GLfloat width
,
124 GLint teeth
, GLfloat tooth_depth
)
132 r1
= outer_radius
- tooth_depth
/ 2.0;
133 r2
= outer_radius
+ tooth_depth
/ 2.0;
135 da
= 2.0 * M_PI
/ teeth
/ 4.0;
137 glShadeModel(GL_FLAT
);
139 glNormal3f(0.0, 0.0, 1.0);
141 /* draw front face */
142 glBegin(GL_QUAD_STRIP
);
143 for (i
= 0; i
<= teeth
; i
++) {
144 angle
= i
* 2.0 * M_PI
/ teeth
;
145 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
146 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
148 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
149 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
155 /* draw front sides of teeth */
157 da
= 2.0 * M_PI
/ teeth
/ 4.0;
158 for (i
= 0; i
< teeth
; i
++) {
159 angle
= i
* 2.0 * M_PI
/ teeth
;
161 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
162 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
163 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
165 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
170 glNormal3f(0.0, 0.0, -1.0);
173 glBegin(GL_QUAD_STRIP
);
174 for (i
= 0; i
<= teeth
; i
++) {
175 angle
= i
* 2.0 * M_PI
/ teeth
;
176 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
177 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
179 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
181 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
186 /* draw back sides of teeth */
188 da
= 2.0 * M_PI
/ teeth
/ 4.0;
189 for (i
= 0; i
< teeth
; i
++) {
190 angle
= i
* 2.0 * M_PI
/ teeth
;
192 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
194 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
196 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
197 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
201 /* draw outward faces of teeth */
202 glBegin(GL_QUAD_STRIP
);
203 for (i
= 0; i
< teeth
; i
++) {
204 angle
= i
* 2.0 * M_PI
/ teeth
;
206 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
207 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
208 u
= r2
* cos(angle
+ da
) - r1
* cos(angle
);
209 v
= r2
* sin(angle
+ da
) - r1
* sin(angle
);
210 len
= sqrt(u
* u
+ v
* v
);
213 glNormal3f(v
, -u
, 0.0);
214 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
215 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
216 glNormal3f(cos(angle
), sin(angle
), 0.0);
217 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
219 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
221 u
= r1
* cos(angle
+ 3 * da
) - r2
* cos(angle
+ 2 * da
);
222 v
= r1
* sin(angle
+ 3 * da
) - r2
* sin(angle
+ 2 * da
);
223 glNormal3f(v
, -u
, 0.0);
224 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
226 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
228 glNormal3f(cos(angle
), sin(angle
), 0.0);
231 glVertex3f(r1
* cos(0), r1
* sin(0), width
* 0.5);
232 glVertex3f(r1
* cos(0), r1
* sin(0), -width
* 0.5);
236 glShadeModel(GL_SMOOTH
);
238 /* draw inside radius cylinder */
239 glBegin(GL_QUAD_STRIP
);
240 for (i
= 0; i
<= teeth
; i
++) {
241 angle
= i
* 2.0 * M_PI
/ teeth
;
242 glNormal3f(-cos(angle
), -sin(angle
), 0.0);
243 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
244 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
253 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
256 glRotatef(view_rotx
, 1.0, 0.0, 0.0);
257 glRotatef(view_roty
, 0.0, 1.0, 0.0);
258 glRotatef(view_rotz
, 0.0, 0.0, 1.0);
261 glTranslatef(-3.0, -2.0, 0.0);
262 glRotatef(angle
, 0.0, 0.0, 1.0);
267 glTranslatef(3.1, -2.0, 0.0);
268 glRotatef(-2.0 * angle
- 9.0, 0.0, 0.0, 1.0);
273 glTranslatef(-3.1, 4.2, 0.0);
274 glRotatef(-2.0 * angle
- 25.0, 0.0, 0.0, 1.0);
286 /* First left eye. */
287 glDrawBuffer(GL_BACK_LEFT
);
289 glMatrixMode(GL_PROJECTION
);
291 glFrustum(left
, right
, -asp
, asp
, 5.0, 60.0);
293 glMatrixMode(GL_MODELVIEW
);
296 glTranslated(+0.5 * eyesep
, 0.0, 0.0);
300 /* Then right eye. */
301 glDrawBuffer(GL_BACK_RIGHT
);
303 glMatrixMode(GL_PROJECTION
);
305 glFrustum(-right
, -left
, -asp
, asp
, 5.0, 60.0);
307 glMatrixMode(GL_MODELVIEW
);
310 glTranslated(-0.5 * eyesep
, 0.0, 0.0);
320 /** Draw single frame, do SwapBuffers, compute FPS */
322 draw_frame(Display
*dpy
, Window win
)
324 static int frames
= 0;
325 static double tRot0
= -1.0, tRate0
= -1.0;
326 double dt
, t
= current_time();
334 /* advance rotation for next frame */
335 angle
+= 70.0 * dt
; /* 70 degrees per second */
341 glXSwapBuffers(dpy
, win
);
347 if (t
- tRate0
>= 5.0) {
348 GLfloat seconds
= t
- tRate0
;
349 GLfloat fps
= frames
/ seconds
;
350 printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames
, seconds
,
358 /* new window size or exposure */
360 reshape(int width
, int height
)
362 glViewport(0, 0, (GLint
) width
, (GLint
) height
);
367 asp
= (GLfloat
) height
/ (GLfloat
) width
;
368 w
= fix_point
* (1.0 / 5.0);
370 left
= -5.0 * ((w
- 0.5 * eyesep
) / fix_point
);
371 right
= 5.0 * ((w
+ 0.5 * eyesep
) / fix_point
);
374 GLfloat h
= (GLfloat
) height
/ (GLfloat
) width
;
376 glMatrixMode(GL_PROJECTION
);
378 glFrustum(-1.0, 1.0, -h
, h
, 5.0, 60.0);
381 glMatrixMode(GL_MODELVIEW
);
383 glTranslatef(0.0, 0.0, -40.0);
391 static GLfloat pos
[4] = { 5.0, 5.0, 10.0, 0.0 };
392 static GLfloat red
[4] = { 0.8, 0.1, 0.0, 1.0 };
393 static GLfloat green
[4] = { 0.0, 0.8, 0.2, 1.0 };
394 static GLfloat blue
[4] = { 0.2, 0.2, 1.0, 1.0 };
396 glLightfv(GL_LIGHT0
, GL_POSITION
, pos
);
397 glEnable(GL_CULL_FACE
);
398 glEnable(GL_LIGHTING
);
400 glEnable(GL_DEPTH_TEST
);
403 gear1
= glGenLists(1);
404 glNewList(gear1
, GL_COMPILE
);
405 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, red
);
406 gear(1.0, 4.0, 1.0, 20, 0.7);
409 gear2
= glGenLists(1);
410 glNewList(gear2
, GL_COMPILE
);
411 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, green
);
412 gear(0.5, 2.0, 2.0, 10, 0.7);
415 gear3
= glGenLists(1);
416 glNewList(gear3
, GL_COMPILE
);
417 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, blue
);
418 gear(1.3, 2.0, 0.5, 10, 0.7);
421 glEnable(GL_NORMALIZE
);
426 * Remove window border/decorations.
429 no_border( Display
*dpy
, Window w
)
431 static const unsigned MWM_HINTS_DECORATIONS
= (1 << 1);
432 static const int PROP_MOTIF_WM_HINTS_ELEMENTS
= 5;
437 unsigned long functions
;
438 unsigned long decorations
;
440 unsigned long status
;
443 PropMotifWmHints motif_hints
;
445 unsigned long flags
= 0;
447 /* setup the property */
448 motif_hints
.flags
= MWM_HINTS_DECORATIONS
;
449 motif_hints
.decorations
= flags
;
451 /* get the atom for the property */
452 prop
= XInternAtom( dpy
, "_MOTIF_WM_HINTS", True
);
454 /* something went wrong! */
458 /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */
461 XChangeProperty( dpy
, w
, /* display, window */
462 prop
, proptype
, /* property, type */
463 32, /* format: 32-bit datums */
464 PropModeReplace
, /* mode */
465 (unsigned char *) &motif_hints
, /* data */
466 PROP_MOTIF_WM_HINTS_ELEMENTS
/* nelements */
472 * Create an RGB, double-buffered window.
473 * Return the window and context handles.
476 make_window( Display
*dpy
, const char *name
,
477 int x
, int y
, int width
, int height
,
478 Window
*winRet
, GLXContext
*ctxRet
)
480 int attribs
[] = { GLX_RGBA
,
487 int stereoAttribs
[] = { GLX_RGBA
,
496 XSetWindowAttributes attr
;
501 XVisualInfo
*visinfo
;
503 scrnum
= DefaultScreen( dpy
);
504 root
= RootWindow( dpy
, scrnum
);
508 width
= DisplayWidth( dpy
, scrnum
);
509 height
= DisplayHeight( dpy
, scrnum
);
513 visinfo
= glXChooseVisual( dpy
, scrnum
, stereoAttribs
);
515 visinfo
= glXChooseVisual( dpy
, scrnum
, attribs
);
518 printf("Error: couldn't get an RGB, "
519 "Double-buffered, Stereo visual\n");
521 printf("Error: couldn't get an RGB, Double-buffered visual\n");
525 /* window attributes */
526 attr
.background_pixel
= 0;
527 attr
.border_pixel
= 0;
528 attr
.colormap
= XCreateColormap( dpy
, root
, visinfo
->visual
, AllocNone
);
529 attr
.event_mask
= StructureNotifyMask
| ExposureMask
| KeyPressMask
;
530 /* XXX this is a bad way to get a borderless window! */
531 mask
= CWBackPixel
| CWBorderPixel
| CWColormap
| CWEventMask
;
533 win
= XCreateWindow( dpy
, root
, x
, y
, width
, height
,
534 0, visinfo
->depth
, InputOutput
,
535 visinfo
->visual
, mask
, &attr
);
540 /* set hints and properties */
542 XSizeHints sizehints
;
545 sizehints
.width
= width
;
546 sizehints
.height
= height
;
547 sizehints
.flags
= USSize
| USPosition
;
548 XSetNormalHints(dpy
, win
, &sizehints
);
549 XSetStandardProperties(dpy
, win
, name
, name
,
550 None
, (char **)NULL
, 0, &sizehints
);
553 ctx
= glXCreateContext( dpy
, visinfo
, NULL
, True
);
555 printf("Error: glXCreateContext failed\n");
567 * Determine whether or not a GLX extension is supported.
570 is_glx_extension_supported(Display
*dpy
, const char *query
)
572 const int scrnum
= DefaultScreen(dpy
);
573 const char *glx_extensions
= NULL
;
574 const size_t len
= strlen(query
);
577 if (glx_extensions
== NULL
) {
578 glx_extensions
= glXQueryExtensionsString(dpy
, scrnum
);
581 ptr
= strstr(glx_extensions
, query
);
582 return ((ptr
!= NULL
) && ((ptr
[len
] == ' ') || (ptr
[len
] == '\0')));
587 * Attempt to determine whether or not the display is synched to vblank.
590 query_vsync(Display
*dpy
)
595 #ifdef GLX_MESA_swap_control
597 && is_glx_extension_supported(dpy
, "GLX_MESA_swap_control")) {
598 PFNGLXGETSWAPINTERVALMESAPROC pglXGetSwapIntervalMESA
=
599 (PFNGLXGETSWAPINTERVALMESAPROC
)
600 glXGetProcAddressARB((const GLubyte
*) "glXGetSwapIntervalMESA");
602 interval
= (*pglXGetSwapIntervalMESA
)();
607 #ifdef GLX_SGI_video_sync
609 && is_glx_extension_supported(dpy
, "GLX_SGI_video_sync")) {
610 PFNGLXGETVIDEOSYNCSGIPROC pglXGetVideoSyncSGI
=
611 (PFNGLXGETVIDEOSYNCSGIPROC
)
612 glXGetProcAddressARB((const GLubyte
*) "glXGetVideoSyncSGI");
615 if ((*pglXGetVideoSyncSGI
)(& count
) == 0) {
616 interval
= (int) count
;
623 printf("Running synchronized to the vertical refresh. The framerate should be\n");
625 printf("approximately the same as the monitor refresh rate.\n");
626 } else if (interval
> 1) {
627 printf("approximately 1/%d the monitor refresh rate.\n",
634 * Handle one X event.
635 * \return NOP, EXIT or DRAW
638 handle_event(Display
*dpy
, Window win
, XEvent
*event
)
643 switch (event
->type
) {
646 case ConfigureNotify
:
647 reshape(event
->xconfigure
.width
, event
->xconfigure
.height
);
653 code
= XLookupKeysym(&event
->xkey
, 0);
654 if (code
== XK_Left
) {
657 else if (code
== XK_Right
) {
660 else if (code
== XK_Up
) {
663 else if (code
== XK_Down
) {
667 r
= XLookupString(&event
->xkey
, buffer
, sizeof(buffer
),
669 if (buffer
[0] == 27) {
673 else if (buffer
[0] == 'a' || buffer
[0] == 'A') {
685 event_loop(Display
*dpy
, Window win
)
689 while (!animate
|| XPending(dpy
) > 0) {
691 XNextEvent(dpy
, &event
);
692 op
= handle_event(dpy
, win
, &event
);
699 draw_frame(dpy
, win
);
708 printf(" -display <displayname> set the display to run on\n");
709 printf(" -stereo run in stereo mode\n");
710 printf(" -fullscreen run in fullscreen mode\n");
711 printf(" -info display OpenGL renderer info\n");
712 printf(" -geometry WxH+X+Y window geometry\n");
717 main(int argc
, char *argv
[])
719 unsigned int winWidth
= 300, winHeight
= 300;
724 char *dpyName
= NULL
;
725 GLboolean printInfo
= GL_FALSE
;
728 for (i
= 1; i
< argc
; i
++) {
729 if (strcmp(argv
[i
], "-display") == 0) {
733 else if (strcmp(argv
[i
], "-info") == 0) {
736 else if (strcmp(argv
[i
], "-stereo") == 0) {
739 else if (strcmp(argv
[i
], "-fullscreen") == 0) {
740 fullscreen
= GL_TRUE
;
742 else if (i
< argc
-1 && strcmp(argv
[i
], "-geometry") == 0) {
743 XParseGeometry(argv
[i
+1], &x
, &y
, &winWidth
, &winHeight
);
752 dpy
= XOpenDisplay(dpyName
);
754 printf("Error: couldn't open display %s\n",
755 dpyName
? dpyName
: getenv("DISPLAY"));
759 make_window(dpy
, "glxgears", x
, y
, winWidth
, winHeight
, &win
, &ctx
);
760 XMapWindow(dpy
, win
);
761 glXMakeCurrent(dpy
, win
, ctx
);
765 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER
));
766 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION
));
767 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR
));
768 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS
));
773 /* Set initial projection/viewing transformation.
774 * We can't be sure we'll get a ConfigureNotify event when the window
777 reshape(winWidth
, winHeight
);
779 event_loop(dpy
, win
);
781 glDeleteLists(gear1
, 1);
782 glDeleteLists(gear2
, 1);
783 glDeleteLists(gear3
, 1);
784 glXDestroyContext(dpy
, ctx
);
785 XDestroyWindow(dpy
, win
);