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 * See usage() below for command line options.
35 #include <X11/keysym.h>
39 #ifndef GLX_MESA_swap_control
40 #define GLX_MESA_swap_control 1
41 typedef int (*PFNGLXGETSWAPINTERVALMESAPROC
)(void);
49 /* XXX this probably isn't very portable */
54 /* return current time (in seconds) */
60 (void) gettimeofday(&tv
, NULL
);
63 (void) gettimeofday(&tv
, &tz
);
65 return (double) tv
.tv_sec
+ tv
.tv_usec
/ 1000000.0;
74 /* update this function for other platforms! */
75 static double t
= 0.0;
78 fprintf(stderr
, "Warning: current_time() not implemented!!\n");
89 #define M_PI 3.14159265
93 /** Event handler results: */
98 static GLfloat view_rotx
= 20.0, view_roty
= 30.0, view_rotz
= 0.0;
99 static GLint gear1
, gear2
, gear3
;
100 static GLfloat angle
= 0.0;
102 static GLboolean fullscreen
= GL_FALSE
; /* Create a single fullscreen window */
103 static GLboolean stereo
= GL_FALSE
; /* Enable stereo. */
104 static GLboolean animate
= GL_TRUE
; /* Animation */
105 static GLfloat eyesep
= 5.0; /* Eye separation. */
106 static GLfloat fix_point
= 40.0; /* Fixation point distance. */
107 static GLfloat left
, right
, asp
; /* Stereo frustum params. */
112 * Draw a gear wheel. You'll probably want to call this function when
113 * building a display list since we do a lot of trig here.
115 * Input: inner_radius - radius of hole at center
116 * outer_radius - radius at center of teeth
117 * width - width of gear
118 * teeth - number of teeth
119 * tooth_depth - depth of tooth
122 gear(GLfloat inner_radius
, GLfloat outer_radius
, GLfloat width
,
123 GLint teeth
, GLfloat tooth_depth
)
131 r1
= outer_radius
- tooth_depth
/ 2.0;
132 r2
= outer_radius
+ tooth_depth
/ 2.0;
134 da
= 2.0 * M_PI
/ teeth
/ 4.0;
136 glShadeModel(GL_FLAT
);
138 glNormal3f(0.0, 0.0, 1.0);
140 /* draw front face */
141 glBegin(GL_QUAD_STRIP
);
142 for (i
= 0; i
<= teeth
; i
++) {
143 angle
= i
* 2.0 * M_PI
/ teeth
;
144 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
145 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
147 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
148 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
154 /* draw front sides of teeth */
156 da
= 2.0 * M_PI
/ teeth
/ 4.0;
157 for (i
= 0; i
< teeth
; i
++) {
158 angle
= i
* 2.0 * M_PI
/ teeth
;
160 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
161 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
162 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
164 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
169 glNormal3f(0.0, 0.0, -1.0);
172 glBegin(GL_QUAD_STRIP
);
173 for (i
= 0; i
<= teeth
; i
++) {
174 angle
= i
* 2.0 * M_PI
/ teeth
;
175 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
176 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
178 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
180 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
185 /* draw back sides of teeth */
187 da
= 2.0 * M_PI
/ teeth
/ 4.0;
188 for (i
= 0; i
< teeth
; i
++) {
189 angle
= i
* 2.0 * M_PI
/ teeth
;
191 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
193 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
195 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
196 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
200 /* draw outward faces of teeth */
201 glBegin(GL_QUAD_STRIP
);
202 for (i
= 0; i
< teeth
; i
++) {
203 angle
= i
* 2.0 * M_PI
/ teeth
;
205 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
206 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
207 u
= r2
* cos(angle
+ da
) - r1
* cos(angle
);
208 v
= r2
* sin(angle
+ da
) - r1
* sin(angle
);
209 len
= sqrt(u
* u
+ v
* v
);
212 glNormal3f(v
, -u
, 0.0);
213 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
214 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
215 glNormal3f(cos(angle
), sin(angle
), 0.0);
216 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
218 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
220 u
= r1
* cos(angle
+ 3 * da
) - r2
* cos(angle
+ 2 * da
);
221 v
= r1
* sin(angle
+ 3 * da
) - r2
* sin(angle
+ 2 * da
);
222 glNormal3f(v
, -u
, 0.0);
223 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
225 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
227 glNormal3f(cos(angle
), sin(angle
), 0.0);
230 glVertex3f(r1
* cos(0), r1
* sin(0), width
* 0.5);
231 glVertex3f(r1
* cos(0), r1
* sin(0), -width
* 0.5);
235 glShadeModel(GL_SMOOTH
);
237 /* draw inside radius cylinder */
238 glBegin(GL_QUAD_STRIP
);
239 for (i
= 0; i
<= teeth
; i
++) {
240 angle
= i
* 2.0 * M_PI
/ teeth
;
241 glNormal3f(-cos(angle
), -sin(angle
), 0.0);
242 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
243 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
252 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
255 glRotatef(view_rotx
, 1.0, 0.0, 0.0);
256 glRotatef(view_roty
, 0.0, 1.0, 0.0);
257 glRotatef(view_rotz
, 0.0, 0.0, 1.0);
260 glTranslatef(-3.0, -2.0, 0.0);
261 glRotatef(angle
, 0.0, 0.0, 1.0);
266 glTranslatef(3.1, -2.0, 0.0);
267 glRotatef(-2.0 * angle
- 9.0, 0.0, 0.0, 1.0);
272 glTranslatef(-3.1, 4.2, 0.0);
273 glRotatef(-2.0 * angle
- 25.0, 0.0, 0.0, 1.0);
285 /* First left eye. */
286 glDrawBuffer(GL_BACK_LEFT
);
288 glMatrixMode(GL_PROJECTION
);
290 glFrustum(left
, right
, -asp
, asp
, 5.0, 60.0);
292 glMatrixMode(GL_MODELVIEW
);
295 glTranslated(+0.5 * eyesep
, 0.0, 0.0);
299 /* Then right eye. */
300 glDrawBuffer(GL_BACK_RIGHT
);
302 glMatrixMode(GL_PROJECTION
);
304 glFrustum(-right
, -left
, -asp
, asp
, 5.0, 60.0);
306 glMatrixMode(GL_MODELVIEW
);
309 glTranslated(-0.5 * eyesep
, 0.0, 0.0);
319 /** Draw single frame, do SwapBuffers, compute FPS */
321 draw_frame(Display
*dpy
, Window win
)
323 static int frames
= 0;
324 static double tRot0
= -1.0, tRate0
= -1.0;
325 double dt
, t
= current_time();
333 /* advance rotation for next frame */
334 angle
+= 70.0 * dt
; /* 70 degrees per second */
340 glXSwapBuffers(dpy
, win
);
346 if (t
- tRate0
>= 5.0) {
347 GLfloat seconds
= t
- tRate0
;
348 GLfloat fps
= frames
/ seconds
;
349 printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames
, seconds
,
357 /* new window size or exposure */
359 reshape(int width
, int height
)
361 glViewport(0, 0, (GLint
) width
, (GLint
) height
);
366 asp
= (GLfloat
) height
/ (GLfloat
) width
;
367 w
= fix_point
* (1.0 / 5.0);
369 left
= -5.0 * ((w
- 0.5 * eyesep
) / fix_point
);
370 right
= 5.0 * ((w
+ 0.5 * eyesep
) / fix_point
);
373 GLfloat h
= (GLfloat
) height
/ (GLfloat
) width
;
375 glMatrixMode(GL_PROJECTION
);
377 glFrustum(-1.0, 1.0, -h
, h
, 5.0, 60.0);
380 glMatrixMode(GL_MODELVIEW
);
382 glTranslatef(0.0, 0.0, -40.0);
390 static GLfloat pos
[4] = { 5.0, 5.0, 10.0, 0.0 };
391 static GLfloat red
[4] = { 0.8, 0.1, 0.0, 1.0 };
392 static GLfloat green
[4] = { 0.0, 0.8, 0.2, 1.0 };
393 static GLfloat blue
[4] = { 0.2, 0.2, 1.0, 1.0 };
395 glLightfv(GL_LIGHT0
, GL_POSITION
, pos
);
396 glEnable(GL_CULL_FACE
);
397 glEnable(GL_LIGHTING
);
399 glEnable(GL_DEPTH_TEST
);
402 gear1
= glGenLists(1);
403 glNewList(gear1
, GL_COMPILE
);
404 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, red
);
405 gear(1.0, 4.0, 1.0, 20, 0.7);
408 gear2
= glGenLists(1);
409 glNewList(gear2
, GL_COMPILE
);
410 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, green
);
411 gear(0.5, 2.0, 2.0, 10, 0.7);
414 gear3
= glGenLists(1);
415 glNewList(gear3
, GL_COMPILE
);
416 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, blue
);
417 gear(1.3, 2.0, 0.5, 10, 0.7);
420 glEnable(GL_NORMALIZE
);
425 * Remove window border/decorations.
428 no_border( Display
*dpy
, Window w
)
430 static const unsigned MWM_HINTS_DECORATIONS
= (1 << 1);
431 static const int PROP_MOTIF_WM_HINTS_ELEMENTS
= 5;
436 unsigned long functions
;
437 unsigned long decorations
;
439 unsigned long status
;
442 PropMotifWmHints motif_hints
;
444 unsigned long flags
= 0;
446 /* setup the property */
447 motif_hints
.flags
= MWM_HINTS_DECORATIONS
;
448 motif_hints
.decorations
= flags
;
450 /* get the atom for the property */
451 prop
= XInternAtom( dpy
, "_MOTIF_WM_HINTS", True
);
453 /* something went wrong! */
457 /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */
460 XChangeProperty( dpy
, w
, /* display, window */
461 prop
, proptype
, /* property, type */
462 32, /* format: 32-bit datums */
463 PropModeReplace
, /* mode */
464 (unsigned char *) &motif_hints
, /* data */
465 PROP_MOTIF_WM_HINTS_ELEMENTS
/* nelements */
471 * Create an RGB, double-buffered window.
472 * Return the window and context handles.
475 make_window( Display
*dpy
, const char *name
,
476 int x
, int y
, int width
, int height
,
477 Window
*winRet
, GLXContext
*ctxRet
)
479 int attribs
[] = { GLX_RGBA
,
486 int stereoAttribs
[] = { GLX_RGBA
,
495 XSetWindowAttributes attr
;
500 XVisualInfo
*visinfo
;
502 scrnum
= DefaultScreen( dpy
);
503 root
= RootWindow( dpy
, scrnum
);
507 width
= DisplayWidth( dpy
, scrnum
);
508 height
= DisplayHeight( dpy
, scrnum
);
512 visinfo
= glXChooseVisual( dpy
, scrnum
, stereoAttribs
);
514 visinfo
= glXChooseVisual( dpy
, scrnum
, attribs
);
517 printf("Error: couldn't get an RGB, "
518 "Double-buffered, Stereo visual\n");
520 printf("Error: couldn't get an RGB, Double-buffered visual\n");
524 /* window attributes */
525 attr
.background_pixel
= 0;
526 attr
.border_pixel
= 0;
527 attr
.colormap
= XCreateColormap( dpy
, root
, visinfo
->visual
, AllocNone
);
528 attr
.event_mask
= StructureNotifyMask
| ExposureMask
| KeyPressMask
;
529 /* XXX this is a bad way to get a borderless window! */
530 mask
= CWBackPixel
| CWBorderPixel
| CWColormap
| CWEventMask
;
532 win
= XCreateWindow( dpy
, root
, x
, y
, width
, height
,
533 0, visinfo
->depth
, InputOutput
,
534 visinfo
->visual
, mask
, &attr
);
539 /* set hints and properties */
541 XSizeHints sizehints
;
544 sizehints
.width
= width
;
545 sizehints
.height
= height
;
546 sizehints
.flags
= USSize
| USPosition
;
547 XSetNormalHints(dpy
, win
, &sizehints
);
548 XSetStandardProperties(dpy
, win
, name
, name
,
549 None
, (char **)NULL
, 0, &sizehints
);
552 ctx
= glXCreateContext( dpy
, visinfo
, NULL
, True
);
554 printf("Error: glXCreateContext failed\n");
566 * Determine whether or not a GLX extension is supported.
569 is_glx_extension_supported(Display
*dpy
, const char *query
)
571 const int scrnum
= DefaultScreen(dpy
);
572 const char *glx_extensions
= NULL
;
573 const size_t len
= strlen(query
);
576 if (glx_extensions
== NULL
) {
577 glx_extensions
= glXQueryExtensionsString(dpy
, scrnum
);
580 ptr
= strstr(glx_extensions
, query
);
581 return ((ptr
!= NULL
) && ((ptr
[len
] == ' ') || (ptr
[len
] == '\0')));
586 * Attempt to determine whether or not the display is synched to vblank.
589 query_vsync(Display
*dpy
)
594 if (is_glx_extension_supported(dpy
, "GLX_MESA_swap_control")) {
595 PFNGLXGETSWAPINTERVALMESAPROC pglXGetSwapIntervalMESA
=
596 (PFNGLXGETSWAPINTERVALMESAPROC
)
597 glXGetProcAddressARB((const GLubyte
*) "glXGetSwapIntervalMESA");
599 interval
= (*pglXGetSwapIntervalMESA
)();
600 } else if (is_glx_extension_supported(dpy
, "GLX_SGI_swap_control")) {
601 /* The default swap interval with this extension is 1. Assume that it
602 * is set to the default.
604 * Many Mesa-based drivers default to 0, but all of these drivers also
605 * export GLX_MESA_swap_control. In that case, this branch will never
606 * be taken, and the correct result should be reported.
613 printf("Running synchronized to the vertical refresh. The framerate should be\n");
615 printf("approximately the same as the monitor refresh rate.\n");
616 } else if (interval
> 1) {
617 printf("approximately 1/%d the monitor refresh rate.\n",
624 * Handle one X event.
625 * \return NOP, EXIT or DRAW
628 handle_event(Display
*dpy
, Window win
, XEvent
*event
)
633 switch (event
->type
) {
636 case ConfigureNotify
:
637 reshape(event
->xconfigure
.width
, event
->xconfigure
.height
);
643 code
= XLookupKeysym(&event
->xkey
, 0);
644 if (code
== XK_Left
) {
647 else if (code
== XK_Right
) {
650 else if (code
== XK_Up
) {
653 else if (code
== XK_Down
) {
657 r
= XLookupString(&event
->xkey
, buffer
, sizeof(buffer
),
659 if (buffer
[0] == 27) {
663 else if (buffer
[0] == 'a' || buffer
[0] == 'A') {
675 event_loop(Display
*dpy
, Window win
)
679 while (!animate
|| XPending(dpy
) > 0) {
681 XNextEvent(dpy
, &event
);
682 op
= handle_event(dpy
, win
, &event
);
689 draw_frame(dpy
, win
);
698 printf(" -display <displayname> set the display to run on\n");
699 printf(" -stereo run in stereo mode\n");
700 printf(" -fullscreen run in fullscreen mode\n");
701 printf(" -info display OpenGL renderer info\n");
702 printf(" -geometry WxH+X+Y window geometry\n");
707 main(int argc
, char *argv
[])
709 unsigned int winWidth
= 300, winHeight
= 300;
714 char *dpyName
= NULL
;
715 GLboolean printInfo
= GL_FALSE
;
718 for (i
= 1; i
< argc
; i
++) {
719 if (strcmp(argv
[i
], "-display") == 0) {
723 else if (strcmp(argv
[i
], "-info") == 0) {
726 else if (strcmp(argv
[i
], "-stereo") == 0) {
729 else if (strcmp(argv
[i
], "-fullscreen") == 0) {
730 fullscreen
= GL_TRUE
;
732 else if (i
< argc
-1 && strcmp(argv
[i
], "-geometry") == 0) {
733 XParseGeometry(argv
[i
+1], &x
, &y
, &winWidth
, &winHeight
);
742 dpy
= XOpenDisplay(dpyName
);
744 printf("Error: couldn't open display %s\n",
745 dpyName
? dpyName
: getenv("DISPLAY"));
749 make_window(dpy
, "glxgears", x
, y
, winWidth
, winHeight
, &win
, &ctx
);
750 XMapWindow(dpy
, win
);
751 glXMakeCurrent(dpy
, win
, ctx
);
755 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER
));
756 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION
));
757 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR
));
758 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS
));
763 /* Set initial projection/viewing transformation.
764 * We can't be sure we'll get a ConfigureNotify event when the window
767 reshape(winWidth
, winHeight
);
769 event_loop(dpy
, win
);
771 glDeleteLists(gear1
, 1);
772 glDeleteLists(gear2
, 1);
773 glDeleteLists(gear3
, 1);
774 glXDestroyContext(dpy
, ctx
);
775 XDestroyWindow(dpy
, win
);