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