http://ftp.x.org/contrib/applications/xscreensaver-3.09.tar.gz
[xscreensaver] / hacks / glx / gears.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* gears --- 3D gear wheels */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)gears.c       4.07 97/11/24 xlockmore";
6
7 #endif
8
9 /*-
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Revision History:
23  * 10-May-97: Compatible with xscreensaver
24  * 22-Mar-97: Added support for -mono mode, and monochrome X servers.
25  *              Ed Mackey, emackey@netaxs.com
26  * 13-Mar-97: Memory leak fix by Tom Schmidt <tschmidt@micron.com>
27  * 1996: "written" by Danny Sung <dannys@ucla.edu>
28  *       Based on 3-D gear wheels by Brian Paul which is in the public domain.
29  */
30
31 /*-
32  * PURIFY 3.0a on SunOS4 reports an unitialized memory read on each of
33  * the glCallList() functions below when using MesaGL 2.1.  This has
34  * been fixed in MesaGL 2.2 and later releases.
35  */
36
37 /*-
38  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
39  * otherwise caddr_t is not defined correctly
40  */
41
42 #include <X11/Intrinsic.h>
43
44 #ifdef STANDALONE
45 # define PROGCLASS                                      "Gears"
46 # define HACK_INIT                                      init_gears
47 # define HACK_DRAW                                      draw_gears
48 # define gears_opts                                     xlockmore_opts
49 # define DEFAULTS       "*count:                1       \n"                     \
50                                         "*cycles:               2       \n"                     \
51                                         "*delay:                100     \n"                     \
52                                         "*wireframe:    False   \n"
53 # include "xlockmore.h"                         /* from the xscreensaver distribution */
54 #else  /* !STANDALONE */
55 # include "xlock.h"                                     /* from the xlockmore distribution */
56 #endif /* !STANDALONE */
57
58 #ifdef USE_GL
59
60 ModeSpecOpt gears_opts =
61 {0, NULL, 0, NULL, NULL};
62
63 #ifdef USE_MODULES
64 ModStruct   gears_description =
65 {"gears", "init_gears", "draw_gears", "release_gears",
66  "draw_gears", "init_gears", NULL, &gears_opts,
67  1000, 1, 2, 1, 4, 1.0, "",
68  "Shows GL's gears", 0, NULL};
69
70 #endif
71
72 typedef struct {
73         GLfloat     view_rotx, view_roty, view_rotz;
74         GLuint      gear1, gear2, gear3;
75         GLfloat     angle;
76         GLXContext *glx_context;
77         Window      window;
78 #if 0
79         Window      win;
80 #endif
81 } gearsstruct;
82
83 static gearsstruct *gears = NULL;
84
85 /*-
86  * Draw a gear wheel.  You'll probably want to call this function when
87  * building a display list since we do a lot of trig here.
88  *
89  * Input:  inner_radius - radius of hole at center
90  *         outer_radius - radius at center of teeth
91  *         width - width of gear
92  *         teeth - number of teeth
93  *         tooth_depth - depth of tooth
94  *         wire - true for wireframe mode
95  */
96 static void
97 gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
98      GLint teeth, GLfloat tooth_depth, Bool wire)
99 {
100         GLint       i;
101         GLfloat     r0, r1, r2;
102         GLfloat     angle, da;
103         GLfloat     u, v, len;
104
105         r0 = inner_radius;
106         r1 = outer_radius - tooth_depth / 2.0;
107         r2 = outer_radius + tooth_depth / 2.0;
108
109         da = 2.0 * M_PI / teeth / 4.0;
110
111         glShadeModel(GL_FLAT);
112
113         /* This subroutine got kind of messy when I added all the checks
114          * for wireframe mode.  A much cleaner solution that I sometimes
115          * use is to have a variable hold the value GL_LINE_LOOP when
116          * in wireframe mode, or hold the value GL_POLYGON otherwise.
117          * Then I just call glBegin(that_variable), give my polygon
118          * coordinates, and glEnd().  Pretty neat eh?  Too bad I couldn't
119          * integrate that trick here.
120          *                                  --Ed.
121          */
122
123         if (!wire)
124                 glNormal3f(0.0, 0.0, 1.0);
125
126         /* draw front face */
127         if (!wire)
128                 glBegin(GL_QUAD_STRIP);
129         for (i = 0; i <= teeth; i++) {
130                 if (wire)
131                         glBegin(GL_LINES);
132                 angle = i * 2.0 * M_PI / teeth;
133                 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
134                 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
135                 if (!wire) {
136                         glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
137                         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
138                 } else {
139                         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
140                         glVertex3f(r1 * cos(angle + 4 * da), r1 * sin(angle + 4 * da), width * 0.5);
141                         glEnd();
142                 }
143         }
144         if (!wire)
145                 glEnd();
146
147         /* draw front sides of teeth */
148         if (!wire)
149                 glBegin(GL_QUADS);
150         da = 2.0 * M_PI / teeth / 4.0;
151         for (i = 0; i < teeth; i++) {
152                 angle = i * 2.0 * M_PI / teeth;
153
154                 if (wire)
155                         glBegin(GL_LINE_LOOP);
156                 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
157                 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
158                 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
159                 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
160                 if (wire)
161                         glEnd();
162         }
163         if (!wire)
164                 glEnd();
165
166
167         if (!wire)
168                 glNormal3f(0.0, 0.0, -1.0);
169
170         /* draw back face */
171         if (!wire)
172                 glBegin(GL_QUAD_STRIP);
173         for (i = 0; i <= teeth; i++) {
174                 angle = i * 2.0 * M_PI / teeth;
175                 if (wire)
176                         glBegin(GL_LINES);
177                 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
178                 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
179                 if (!wire) {
180                         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
181                         glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
182                 } else {
183                         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
184                         glVertex3f(r1 * cos(angle + 4 * da), r1 * sin(angle + 4 * da), -width * 0.5);
185                         glEnd();
186                 }
187         }
188         if (!wire)
189                 glEnd();
190
191         /* draw back sides of teeth */
192         if (!wire)
193                 glBegin(GL_QUADS);
194         da = 2.0 * M_PI / teeth / 4.0;
195         for (i = 0; i < teeth; i++) {
196                 angle = i * 2.0 * M_PI / teeth;
197
198                 if (wire)
199                         glBegin(GL_LINE_LOOP);
200                 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
201                 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
202                 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
203                 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
204                 if (wire)
205                         glEnd();
206         }
207         if (!wire)
208                 glEnd();
209
210
211         /* draw outward faces of teeth */
212         if (!wire)
213                 glBegin(GL_QUAD_STRIP);
214         for (i = 0; i < teeth; i++) {
215                 angle = i * 2.0 * M_PI / teeth;
216
217                 if (wire)
218                         glBegin(GL_LINES);
219                 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
220                 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
221                 u = r2 * cos(angle + da) - r1 * cos(angle);
222                 v = r2 * sin(angle + da) - r1 * sin(angle);
223                 len = sqrt(u * u + v * v);
224                 u /= len;
225                 v /= len;
226                 glNormal3f(v, -u, 0.0);
227                 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
228                 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
229                 glNormal3f(cos(angle), sin(angle), 0.0);
230                 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
231                 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
232                 u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
233                 v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
234                 glNormal3f(v, -u, 0.0);
235                 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
236                 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
237                 glNormal3f(cos(angle), sin(angle), 0.0);
238                 if (wire)
239                         glEnd();
240         }
241
242         if (!wire) {
243                 glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5);
244                 glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5);
245                 glEnd();
246         }
247         if (!wire)
248                 glShadeModel(GL_SMOOTH);
249
250         /* draw inside radius cylinder */
251         if (!wire)
252                 glBegin(GL_QUAD_STRIP);
253         for (i = 0; i <= teeth; i++) {
254                 angle = i * 2.0 * M_PI / teeth;
255                 if (wire)
256                         glBegin(GL_LINES);
257                 glNormal3f(-cos(angle), -sin(angle), 0.0);
258                 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
259                 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
260                 if (wire) {
261                         glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
262                         glVertex3f(r0 * cos(angle + 4 * da), r0 * sin(angle + 4 * da), -width * 0.5);
263                         glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
264                         glVertex3f(r0 * cos(angle + 4 * da), r0 * sin(angle + 4 * da), width * 0.5);
265                         glEnd();
266                 }
267         }
268         if (!wire)
269                 glEnd();
270
271 }
272
273 static void
274 draw(ModeInfo * mi)
275 {
276         gearsstruct *gp = &gears[MI_SCREEN(mi)];
277         int         wire = MI_IS_WIREFRAME(mi);
278
279         if (!wire) {
280                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
281         } else {
282                 glClear(GL_COLOR_BUFFER_BIT);
283         }
284
285         glPushMatrix();
286         glRotatef(gp->view_rotx, 1.0, 0.0, 0.0);
287         glRotatef(gp->view_roty, 0.0, 1.0, 0.0);
288         glRotatef(gp->view_rotz, 0.0, 0.0, 1.0);
289
290         glPushMatrix();
291         glTranslatef(-3.0, -2.0, 0.0);
292         glRotatef(gp->angle, 0.0, 0.0, 1.0);
293 /* PURIFY 4.0.1 reports an unitialized memory read on the next line when using
294    * MesaGL 2.2 and -mono.  This has been fixed in MesaGL 2.3 and later. */
295         glCallList(gp->gear1);
296         glPopMatrix();
297
298         glPushMatrix();
299         glTranslatef(3.1, -2.0, 0.0);
300         glRotatef(-2.0 * gp->angle - 9.0, 0.0, 0.0, 1.0);
301         glCallList(gp->gear2);
302         glPopMatrix();
303
304         glPushMatrix();
305         glTranslatef(-3.1, 4.2, 0.0);
306         glRotatef(-2.0 * gp->angle - 25.0, 0.0, 0.0, 1.0);
307         glCallList(gp->gear3);
308         glPopMatrix();
309
310         glPopMatrix();
311 }
312
313
314
315 /* new window size or exposure */
316 static void
317 reshape(int width, int height)
318 {
319         GLfloat     h = (GLfloat) height / (GLfloat) width;
320
321         glViewport(0, 0, (GLint) width, (GLint) height);
322         glMatrixMode(GL_PROJECTION);
323         glLoadIdentity();
324         glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
325         glMatrixMode(GL_MODELVIEW);
326         glLoadIdentity();
327         glTranslatef(0.0, 0.0, -40.0);
328
329         /* The depth buffer will be cleared, if needed, before the
330          * next frame.  Right now we just want to black the screen.
331          */
332         glClear(GL_COLOR_BUFFER_BIT);
333
334 }
335
336
337 static void
338 pinit(ModeInfo * mi)
339 {
340         gearsstruct *gp = &gears[MI_SCREEN(mi)];
341         static GLfloat pos[4] =
342         {5.0, 5.0, 10.0, 1.0};
343         static GLfloat red[4] =
344         {0.8, 0.1, 0.0, 1.0};
345         static GLfloat green[4] =
346         {0.0, 0.8, 0.2, 1.0};
347         static GLfloat blue[4] =
348         {0.2, 0.2, 1.0, 1.0};
349         static GLfloat gray[4] =
350         {0.5, 0.5, 0.5, 1.0};
351         static GLfloat white[4] =
352         {1.0, 1.0, 1.0, 1.0};
353         int         wire = MI_IS_WIREFRAME(mi);
354         int         mono = MI_IS_MONO(mi);
355
356         if (!wire) {
357                 glLightfv(GL_LIGHT0, GL_POSITION, pos);
358                 glEnable(GL_CULL_FACE);
359                 glEnable(GL_LIGHTING);
360                 glEnable(GL_LIGHT0);
361                 glEnable(GL_DEPTH_TEST);
362         }
363 #if 0
364 /*-
365  * Messes up on multiscreen Pseudocolor:0 StaticGray(monochrome):1
366  * 2nd time mode is run it is Grayscale on PseudoColor.
367  * The code below forces monochrome on TrueColor.
368  */
369         if (MI_IS_MONO(mi)) {
370                 red[0] = red[1] = red[2] = 1.0;
371                 green[0] = green[1] = green[2] = 1.0;
372                 blue[0] = blue[1] = blue[2] = 1.0;
373         }
374 #endif
375
376         /* make the gears */
377         gp->gear1 = glGenLists(1);
378         glNewList(gp->gear1, GL_COMPILE);
379         if (wire) {
380                 if (mono)
381                         glColor4fv(white);
382                 else
383                         glColor4fv(red);
384         } else {
385                 if (mono)
386                         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray);
387                 else
388                         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
389         }
390         gear(1.0, 4.0, 1.0, 20, 0.7, wire);
391         glEndList();
392
393         gp->gear2 = glGenLists(1);
394         glNewList(gp->gear2, GL_COMPILE);
395         if (wire) {
396                 if (mono)
397                         glColor4fv(white);
398                 else
399                         glColor4fv(green);
400         } else {
401                 if (mono)
402                         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray);
403                 else
404                         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
405         }
406         gear(0.5, 2.0, 2.0, 10, 0.7, wire);
407         glEndList();
408
409         gp->gear3 = glGenLists(1);
410         glNewList(gp->gear3, GL_COMPILE);
411         if (wire) {
412                 if (mono)
413                         glColor4fv(white);
414                 else
415                         glColor4fv(blue);
416         } else {
417                 if (mono)
418                         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray);
419                 else
420                         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
421         }
422         gear(1.3, 2.0, 0.5, 10, 0.7, wire);
423         glEndList();
424         if (!wire)
425                 glEnable(GL_NORMALIZE);
426 }
427
428 void
429 init_gears(ModeInfo * mi)
430 {
431         int         screen = MI_SCREEN(mi);
432
433         /*Colormap    cmap; */
434         /* Boolean     rgba, doublebuffer, cmap_installed; */
435         gearsstruct *gp;
436
437         if (gears == NULL) {
438                 if ((gears = (gearsstruct *) calloc(MI_NUM_SCREENS(mi),
439                                               sizeof (gearsstruct))) == NULL)
440                         return;
441         }
442         gp = &gears[screen];
443
444         gp->window = MI_WINDOW(mi);
445         gp->view_rotx = NRAND(360);
446         gp->view_roty = NRAND(360);
447         gp->view_rotz = NRAND(360);
448         gp->angle = NRAND(360);
449
450         if ((gp->glx_context = init_GL(mi)) != NULL) {
451                 reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
452                 pinit(mi);
453         } else {
454                 MI_CLEARWINDOW(mi);
455         }
456 }
457
458 void
459 draw_gears(ModeInfo * mi)
460 {
461         gearsstruct *gp = &gears[MI_SCREEN(mi)];
462         Display    *display = MI_DISPLAY(mi);
463         Window      window = MI_WINDOW(mi);
464         int         angle_incr = MI_CYCLES(mi) ? MI_CYCLES(mi) : 2;
465         int         rot_incr = MI_COUNT(mi) ? MI_COUNT(mi) : 1;
466
467         if (!gp->glx_context)
468                 return;
469
470         glDrawBuffer(GL_BACK);
471
472         glXMakeCurrent(display, window, *(gp->glx_context));
473         draw(mi);
474
475         /* let's do something so we don't get bored */
476         gp->angle = (int) (gp->angle + angle_incr) % 360;
477         gp->view_rotx = (int) (gp->view_rotx + rot_incr) % 360;
478         gp->view_roty = (int) (gp->view_roty + rot_incr) % 360;
479         gp->view_rotz = (int) (gp->view_rotz + rot_incr) % 360;
480
481         glFinish();
482         glXSwapBuffers(display, window);
483 }
484
485 void
486 release_gears(ModeInfo * mi)
487 {
488         if (gears != NULL) {
489                 int         screen;
490
491                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
492                         gearsstruct *gp = &gears[screen];
493
494                         if (gp->glx_context) {
495                                 /* Display lists MUST be freed while their glXContext is current. */
496                                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
497
498                                 if (glIsList(gp->gear1))
499                                         glDeleteLists(gp->gear1, 1);
500                                 if (glIsList(gp->gear2))
501                                         glDeleteLists(gp->gear2, 1);
502                                 if (glIsList(gp->gear3))
503                                         glDeleteLists(gp->gear3, 1);
504
505                         }
506                 }
507                 (void) free((void *) gears);
508                 gears = NULL;
509         }
510         FreeAllGL(mi);
511 }
512
513
514 /*********************************************************/
515
516 #endif