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