http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / glx / gears.c
1 /* gears, Copyright (c) 2007-2008 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Originally written by Brian Paul in 1996 or earlier;
12  * rewritten by jwz in Nov 2007.
13  */
14
15 #define DEFAULTS        "*delay:        30000       \n" \
16                         "*count:        0           \n" \
17                         "*showFPS:      False       \n" \
18                         "*wireframe:    False       \n" \
19
20 # define refresh_gears 0
21 # define release_gears 0
22 #undef countof
23 #define countof(x) (sizeof((x))/sizeof((*x)))
24
25 #include "xlockmore.h"
26 #include "involute.h"
27 #include "normals.h"
28 #include "tube.h"
29 #include "rotator.h"
30 #include "gltrackball.h"
31 #include <ctype.h>
32
33 #ifdef USE_GL /* whole file */
34
35 #undef BELLRAND
36 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
37
38 #define DEF_SPIN        "True"
39 #define DEF_WANDER      "True"
40 #define DEF_SPEED       "1.0"
41
42 typedef struct {
43   GLXContext *glx_context;
44   rotator *rot;
45   trackball_state *trackball;
46   Bool button_down_p;
47   Bool planetary_p;
48
49   int ngears;
50   gear **gears;
51
52   GLuint armature_dlist;
53   int armature_polygons;
54
55   struct { GLfloat x1, y1, x2, y2; } bbox;
56
57 } gears_configuration;
58
59 static gears_configuration *bps = NULL;
60
61 static Bool do_spin;
62 static GLfloat speed;
63 static Bool do_wander;
64
65 static XrmOptionDescRec opts[] = {
66   { "-spin",   ".spin",   XrmoptionNoArg, "True"  },
67   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
68   { "-speed",  ".speed",  XrmoptionSepArg, 0      },
69   { "-wander", ".wander", XrmoptionNoArg, "True"  },
70   { "+wander", ".wander", XrmoptionNoArg, "False" },
71 };
72
73 static argtype vars[] = {
74   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
75   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
76   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
77 };
78
79 ENTRYPOINT ModeSpecOpt gears_opts = {countof(opts), opts, countof(vars), vars, NULL};
80
81
82 /* Window management, etc
83  */
84 ENTRYPOINT void
85 reshape_gears (ModeInfo *mi, int width, int height)
86 {
87   GLfloat h = (GLfloat) height / (GLfloat) width;
88
89   glViewport (0, 0, (GLint) width, (GLint) height);
90
91   glMatrixMode(GL_PROJECTION);
92   glLoadIdentity();
93   gluPerspective (30.0, 1/h, 1.0, 100.0);
94
95   glMatrixMode(GL_MODELVIEW);
96   glLoadIdentity();
97   gluLookAt( 0.0, 0.0, 30.0,
98              0.0, 0.0, 0.0,
99              0.0, 1.0, 0.0);
100
101   glClear(GL_COLOR_BUFFER_BIT);
102 }
103
104
105 ENTRYPOINT Bool
106 gears_handle_event (ModeInfo *mi, XEvent *event)
107 {
108   gears_configuration *bp = &bps[MI_SCREEN(mi)];
109
110   if (event->xany.type == ButtonPress &&
111       event->xbutton.button == Button1)
112     {
113       bp->button_down_p = True;
114       gltrackball_start (bp->trackball,
115                          event->xbutton.x, event->xbutton.y,
116                          MI_WIDTH (mi), MI_HEIGHT (mi));
117       return True;
118     }
119   else if (event->xany.type == ButtonRelease &&
120            event->xbutton.button == Button1)
121     {
122       bp->button_down_p = False;
123       return True;
124     }
125   else if (event->xany.type == ButtonPress &&
126            (event->xbutton.button == Button4 ||
127             event->xbutton.button == Button5 ||
128             event->xbutton.button == Button6 ||
129             event->xbutton.button == Button7))
130     {
131       gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
132                               !!event->xbutton.state);
133       return True;
134     }
135   else if (event->xany.type == MotionNotify &&
136            bp->button_down_p)
137     {
138       gltrackball_track (bp->trackball,
139                          event->xmotion.x, event->xmotion.y,
140                          MI_WIDTH (mi), MI_HEIGHT (mi));
141       return True;
142     }
143
144   return False;
145 }
146
147
148
149 static void
150 free_gear (gear *g)
151 {
152   if (g->dlist)
153     glDeleteLists (g->dlist, 1);
154   free (g);
155 }
156
157
158 /* Create and return a new gear sized for placement next to or on top of
159    the given parent gear (if any.)  Returns 0 if out of memory.
160    [Mostly lifted from pinion.c]
161  */
162 static gear *
163 new_gear (ModeInfo *mi, gear *parent)
164 {
165   gears_configuration *bp = &bps[MI_SCREEN(mi)];
166   gear *g = (gear *) calloc (1, sizeof (*g));
167   static unsigned long id = 0;  /* only used in debugging output */
168
169   if (!g) return 0;
170   g->id = ++id;
171
172   /* Pick the size of the teeth.
173    */
174   if (parent) /* adjascent gears need matching teeth */
175     {
176       g->tooth_w = parent->tooth_w;
177       g->tooth_h = parent->tooth_h;
178       g->tooth_slope = -parent->tooth_slope;
179     }
180   else                 /* gears that begin trains get any size they want */
181     {
182       g->tooth_w = 0.007 * (1.0 + BELLRAND(4.0));
183       g->tooth_h = 0.005 * (1.0 + BELLRAND(8.0));
184 /*
185       g->tooth_slope = ((random() % 8)
186                         ? 0
187                         : 0.5 + BELLRAND(1));
188  */
189     }
190
191   /* Pick the number of teeth, and thus, the radius.
192    */
193   {
194     double c;
195
196     if (!parent || bp->ngears > 4)
197       g->nteeth = 5 + BELLRAND (20);
198     else
199       g->nteeth = parent->nteeth * (0.5 + BELLRAND(2));
200
201     c = g->nteeth * g->tooth_w * 2;     /* circumference = teeth + gaps */
202     g->r = c / (M_PI * 2);              /* c = 2 pi r  */
203   }
204
205   g->thickness  = g->tooth_w + frand (g->r);
206   g->thickness2 = g->thickness * 0.7;
207   g->thickness3 = g->thickness;
208
209   /* Colorize
210    */
211   g->color[0] = 0.5 + frand(0.5);
212   g->color[1] = 0.5 + frand(0.5);
213   g->color[2] = 0.5 + frand(0.5);
214   g->color[3] = 1.0;
215
216   g->color2[0] = g->color[0] * 0.85;
217   g->color2[1] = g->color[1] * 0.85;
218   g->color2[2] = g->color[2] * 0.85;
219   g->color2[3] = g->color[3];
220
221
222   /* Decide on shape of gear interior:
223      - just a ring with teeth;
224      - that, plus a thinner in-set "plate" in the middle;
225      - that, plus a thin raised "lip" on the inner plate;
226      - or, a wide lip (really, a thicker third inner plate.)
227    */
228   if ((random() % 10) == 0)
229     {
230       /* inner_r can go all the way in; there's no inset disc. */
231       g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8);
232       g->inner_r2 = 0;
233       g->inner_r3 = 0;
234     }
235   else
236     {
237       /* inner_r doesn't go in very far; inner_r2 is an inset disc. */
238       g->inner_r  = (g->r * 0.5)  + frand((g->r - g->tooth_h) * 0.4);
239       g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5);
240       g->inner_r3 = 0;
241
242       if (g->inner_r2 > (g->r * 0.2))
243         {
244           int nn = (random() % 10);
245           if (nn <= 2)
246             g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2);
247           else if (nn <= 7 && g->inner_r2 >= 0.1)
248             g->inner_r3 = g->inner_r2 - 0.01;
249         }
250     }
251
252   /* If we have three discs, sometimes make the middle disc be spokes.
253    */
254   if (g->inner_r3 && ((random() % 5) == 0))
255     {
256       g->spokes = 2 + BELLRAND (5);
257       g->spoke_thickness = 1 + frand(7.0);
258       if (g->spokes == 2 && g->spoke_thickness < 2)
259         g->spoke_thickness += 1;
260     }
261
262   /* Sometimes add little nubbly bits, if there is room.
263    */
264   if (g->nteeth > 5)
265     {
266       double size = 0;
267       involute_biggest_ring (g, 0, &size, 0);
268       if (size > g->r * 0.2 && (random() % 5) == 0)
269         {
270           g->nubs = 1 + (random() % 16);
271           if (g->nubs > 8) g->nubs = 1;
272         }
273     }
274
275   if (g->inner_r3 > g->inner_r2) abort();
276   if (g->inner_r2 > g->inner_r) abort();
277   if (g->inner_r  > g->r) abort();
278
279   /* Decide how complex the polygon model should be.
280    */
281   {
282     double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */
283     if (pix <= 2.5)      g->size = INVOLUTE_SMALL;
284     else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM;
285     else                 g->size = INVOLUTE_LARGE;
286   }
287
288   g->base_p = !parent;
289
290   return g;
291 }
292
293
294 /* Given a newly-created gear, place it next to its parent in the scene,
295    with its teeth meshed and the proper velocity.  Returns False if it
296    didn't work.  (Call this a bunch of times until either it works, or
297    you decide it's probably not going to.)
298    [Mostly lifted from pinion.c]
299  */
300 static Bool
301 place_gear (ModeInfo *mi, gear *g, gear *parent)
302 {
303   gears_configuration *bp = &bps[MI_SCREEN(mi)];
304
305   /* Compute this gear's velocity.
306    */
307   if (! parent)
308     {
309       g->ratio = 0.8 + BELLRAND(0.4);  /* 0.8-1.2 = 8-12rpm @ 60fps */
310       g->th = 1; /* not 0 */
311     }
312   else
313     {
314       /* Gearing ratio is the ratio of the number of teeth to previous gear
315          (which is also the ratio of the circumferences.)
316        */
317       g->ratio = (double) parent->nteeth / (double) g->nteeth;
318
319       /* Set our initial rotation to match that of the previous gear,
320          multiplied by the gearing ratio.  (This is finessed later,
321          once we know the exact position of the gear relative to its
322          parent.)
323       */
324       g->th = -(parent->th * g->ratio);
325
326       if (g->nteeth & 1)    /* rotate 1/2 tooth-size if odd number of teeth */
327         {
328           double off = (180.0 / g->nteeth);
329           if (g->th > 0)
330             g->th += off;
331           else
332             g->th -= off;
333         }
334
335       /* ratios are cumulative for all gears in the train. */
336       g->ratio *= parent->ratio;
337     }
338
339
340   if (parent)   /* Place the gear next to the parent. */
341     {
342       double r_off = parent->r + g->r;
343       int angle;
344
345       angle = (random() % 360) - 180;   /* -180 to +180 degrees */
346
347       g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off);
348       g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off);
349       g->z = parent->z;
350
351       /* avoid accidentally changing sign of "th" in the math below. */
352       g->th += (g->th > 0 ? 360 : -360);
353
354       /* Adjust the rotation of the gear so that its teeth line up with its
355          parent, based on the position of the gear and the current rotation
356          of the parent.
357        */
358       {
359         double p_c = 2 * M_PI * parent->r;  /* circumference of parent */
360         double g_c = 2 * M_PI * g->r;       /* circumference of g  */
361
362         double p_t = p_c * (angle/360.0);   /* distance travelled along
363                                                circumference of parent when
364                                                moving "angle" degrees along
365                                                parent. */
366         double g_rat = p_t / g_c;           /* if travelling that distance
367                                                along circumference of g,
368                                                ratio of g's circumference
369                                                travelled. */
370         double g_th = 360.0 * g_rat;        /* that ratio in degrees */
371
372         g->th += angle + g_th;
373       }
374     }
375
376   /* If the position we picked for this gear causes it to overlap
377      with any earlier gear in the train, give up.
378    */
379   {
380     int i;
381
382     for (i = bp->ngears-1; i >= 0; i--)
383       {
384         gear *og = bp->gears[i];
385
386         if (og == g) continue;
387         if (og == parent) continue;
388         if (g->z != og->z) continue;    /* Ignore unless on same layer */
389
390         /* Collision detection without sqrt:
391              d = sqrt(a^2 + b^2)   d^2 = a^2 + b^2
392              d < r1 + r2           d^2 < (r1 + r2)^2
393          */
394         if (((g->x - og->x) * (g->x - og->x) +
395              (g->y - og->y) * (g->y - og->y)) <
396             ((g->r + g->tooth_h + og->r + og->tooth_h) *
397              (g->r + g->tooth_h + og->r + og->tooth_h)))
398           return False;
399       }
400   }
401
402   return True;
403 }
404
405
406 /* Make a new gear, place it next to its parent in the scene,
407    with its teeth meshed and the proper velocity.  Returns the gear;
408    or 0 if it didn't work.  (Call this a bunch of times until either
409    it works, or you decide it's probably not going to.)
410    [Mostly lifted from pinion.c]
411  */
412 static gear *
413 place_new_gear (ModeInfo *mi, gear *parent)
414 {
415   gears_configuration *bp = &bps[MI_SCREEN(mi)];
416   int loop_count = 0;
417   gear *g = 0;
418
419   while (1)
420     {
421       loop_count++;
422       if (loop_count >= 100)
423         {
424           if (g)
425             free_gear (g);
426           g = 0;
427           break;
428         }
429
430       g = new_gear (mi, parent);
431       if (!g) return 0;  /* out of memory? */
432
433       if (place_gear (mi, g, parent))
434         break;
435     }
436
437   if (! g) return 0;
438
439   /* We got a gear, and it is properly positioned.
440      Insert it in the scene.
441    */
442   bp->gears[bp->ngears++] = g;
443   return g;
444 }
445
446
447 static int
448 arm (GLfloat length,
449      GLfloat width1, GLfloat height1,
450      GLfloat width2, GLfloat height2,
451      Bool wire)
452 {
453   int polys = 0;
454   glShadeModel(GL_FLAT);
455
456 #if 0  /* don't need these - they're embedded in other objects */
457   /* draw end 1 */
458   glFrontFace(GL_CW);
459   glNormal3f(-1, 0, 0);
460   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
461   glVertex3f(-length/2, -width1/2, -height1/2);
462   glVertex3f(-length/2,  width1/2, -height1/2);
463   glVertex3f(-length/2,  width1/2,  height1/2);
464   glVertex3f(-length/2, -width1/2,  height1/2);
465   polys++;
466   glEnd();
467
468   /* draw end 2 */
469   glFrontFace(GL_CCW);
470   glNormal3f(1, 0, 0);
471   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
472   glVertex3f(length/2, -width2/2, -height2/2);
473   glVertex3f(length/2,  width2/2, -height2/2);
474   glVertex3f(length/2,  width2/2,  height2/2);
475   glVertex3f(length/2, -width2/2,  height2/2);
476   polys++;
477   glEnd();
478 #endif
479
480   /* draw top */
481   glFrontFace(GL_CCW);
482   glNormal3f(0, 0, -1);
483   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
484   glVertex3f(-length/2, -width1/2, -height1/2);
485   glVertex3f(-length/2,  width1/2, -height1/2);
486   glVertex3f( length/2,  width2/2, -height2/2);
487   glVertex3f( length/2, -width2/2, -height2/2);
488   polys++;
489   glEnd();
490
491   /* draw bottom */
492   glFrontFace(GL_CW);
493   glNormal3f(0, 0, 1);
494   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
495   glVertex3f(-length/2, -width1/2, height1/2);
496   glVertex3f(-length/2,  width1/2, height1/2);
497   glVertex3f( length/2,  width2/2, height2/2);
498   glVertex3f( length/2, -width2/2, height2/2);
499   polys++;
500   glEnd();
501
502   /* draw left */
503   glFrontFace(GL_CW);
504   glNormal3f(0, -1, 0);
505   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
506   glVertex3f(-length/2, -width1/2, -height1/2);
507   glVertex3f(-length/2, -width1/2,  height1/2);
508   glVertex3f( length/2, -width2/2,  height2/2);
509   glVertex3f( length/2, -width2/2, -height2/2);
510   polys++;
511   glEnd();
512
513   /* draw right */
514   glFrontFace(GL_CCW);
515   glNormal3f(0, 1, 0);
516   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
517   glVertex3f(-length/2,  width1/2, -height1/2);
518   glVertex3f(-length/2,  width1/2,  height1/2);
519   glVertex3f( length/2,  width2/2,  height2/2);
520   glVertex3f( length/2,  width2/2, -height2/2);
521   polys++;
522   glEnd();
523
524   glFrontFace(GL_CCW);
525
526   return polys;
527 }
528
529
530 static int
531 ctube (GLfloat diameter, GLfloat width, Bool wire)
532 {
533   tube (0, 0,  width/2,
534         0, 0, -width/2,
535         diameter, 0, 
536         32, True, True, wire);
537   return 0; /* #### */
538 }
539
540 static void
541 armature (ModeInfo *mi)
542 {
543   gears_configuration *bp = &bps[MI_SCREEN(mi)];
544   int wire = MI_IS_WIREFRAME(mi);
545
546   static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
547   GLfloat shiny = 128.0;
548   GLfloat color[4];
549
550   color[0] = 0.5 + frand(0.5);
551   color[1] = 0.5 + frand(0.5);
552   color[2] = 0.5 + frand(0.5);
553   color[3] = 1.0;
554
555   bp->armature_polygons = 0;
556
557   bp->armature_dlist = glGenLists (1);
558   if (! bp->armature_dlist)
559     {
560       check_gl_error ("glGenLists");
561       abort();
562     }
563
564   glNewList (bp->armature_dlist, GL_COMPILE);
565
566   glMaterialfv (GL_FRONT, GL_SPECULAR,  spec);
567   glMateriali  (GL_FRONT, GL_SHININESS, shiny);
568   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
569   glColor3f (color[0], color[1], color[2]);
570
571   glPushMatrix();
572
573   {
574     GLfloat s = bp->gears[0]->r * 2.7;
575     s = s/5.6;
576     glScalef (s, s, s);
577   }
578
579   glTranslatef (0, 0, 1.4 + bp->gears[0]->thickness);
580   glRotatef (30, 0, 0, 1);
581
582   bp->armature_polygons += ctube (0.5, 10, wire);       /* center axle */
583
584   glPushMatrix();
585   glTranslatef(0.0, 4.2, -1);
586   bp->armature_polygons += ctube (0.5, 3, wire);       /* axle 1 */
587   glTranslatef(0, 0, 1.8);
588   bp->armature_polygons += ctube (0.7, 0.7, wire);
589   glPopMatrix();
590
591   glPushMatrix();
592   glRotatef(120, 0.0, 0.0, 1.0);
593   glTranslatef(0.0, 4.2, -1);
594   bp->armature_polygons += ctube (0.5, 3, wire);       /* axle 2 */
595   glTranslatef(0, 0, 1.8);
596   bp->armature_polygons += ctube (0.7, 0.7, wire);
597   glPopMatrix();
598
599   glPushMatrix();
600   glRotatef(240, 0.0, 0.0, 1.0);
601   glTranslatef(0.0, 4.2, -1);
602   bp->armature_polygons += ctube (0.5, 3, wire);       /* axle 3 */
603   glTranslatef(0, 0, 1.8);
604   bp->armature_polygons += ctube (0.7, 0.7, wire);
605   glPopMatrix();
606
607   glTranslatef(0, 0, 1.5);                            /* center disk */
608   bp->armature_polygons += ctube (1.5, 2, wire);
609
610   glPushMatrix();
611   glRotatef(270, 0, 0, 1);
612   glRotatef(-10, 0, 1, 0);
613   glTranslatef(-2.2, 0, 0);
614   bp->armature_polygons += arm (4.0, 1.0, 0.5,
615                                 2.0, 1.0, wire);        /* arm 1 */
616   glPopMatrix();
617
618   glPushMatrix();
619   glRotatef(30, 0, 0, 1);
620   glRotatef(-10, 0, 1, 0);
621   glTranslatef(-2.2, 0, 0);
622   bp->armature_polygons += arm (4.0, 1.0, 0.5,
623                                 2.0, 1.0, wire);        /* arm 2 */
624   glPopMatrix();
625
626   glPushMatrix();
627   glRotatef(150, 0, 0, 1);
628   glRotatef(-10, 0, 1, 0);
629   glTranslatef(-2.2, 0, 0);
630   bp->armature_polygons += arm (4.0, 1.0, 0.5,
631                                 2.0, 1.0, wire);        /* arm 3 */
632   glPopMatrix();
633
634   glPopMatrix();
635
636   glEndList ();
637 }
638
639
640 static void
641 planetary_gears (ModeInfo *mi)
642 {
643   gears_configuration *bp = &bps[MI_SCREEN(mi)];
644   gear *g0, *g1, *g2, *g3, *g4;
645   GLfloat distance = 2.02;
646
647   bp->planetary_p = True;
648
649   g0 = new_gear (mi, 0);
650   g1 = new_gear (mi, 0);
651   g2 = new_gear (mi, 0);
652   g3 = new_gear (mi, 0);
653   g4 = new_gear (mi, 0);
654
655   if (! place_gear (mi, g0, 0)) abort();
656   if (! place_gear (mi, g1, 0)) abort();
657   if (! place_gear (mi, g2, 0)) abort();
658   if (! place_gear (mi, g3, 0)) abort();
659   if (! place_gear (mi, g4, 0)) abort();
660
661   g0->nteeth = 12 + (3 * (random() % 10));  /* must be multiple of 3 */
662   g0->tooth_w = g0->r / g0->nteeth;
663   g0->tooth_h = g0->tooth_w * 2.8;
664
665 # define COPY(F) g4->F = g3->F = g2->F = g1->F = g0->F
666   COPY(r);
667   COPY(th);
668   COPY(nteeth);
669   COPY(tooth_w);
670   COPY(tooth_h);
671   COPY(tooth_slope);
672   COPY(inner_r);
673   COPY(inner_r2);
674   COPY(inner_r3);
675   COPY(thickness);
676   COPY(thickness2);
677   COPY(thickness3);
678   COPY(ratio);
679   COPY(size);
680 # undef COPY
681
682   g1->x = cos (M_PI * 2 / 3) * g1->r * distance;
683   g1->y = sin (M_PI * 2 / 3) * g1->r * distance;
684
685   g2->x = cos (M_PI * 4 / 3) * g2->r * distance;
686   g2->y = sin (M_PI * 4 / 3) * g2->r * distance;
687
688   g3->x = cos (M_PI * 6 / 3) * g3->r * distance;
689   g3->y = sin (M_PI * 6 / 3) * g3->r * distance;
690
691   g4->x = 0;
692   g4->y = 0;
693   g4->th = -g3->th;
694
695   /* rotate central gear 1/2 tooth-size if odd number of teeth */
696   if (g4->nteeth & 1)
697     g4->th -= (180.0 / g4->nteeth);
698
699   g0->inverted_p  = True;
700   g0->x           = 0;
701   g0->y           = 0;
702   g0->nteeth      = g1->nteeth * 3;
703   g0->r           = g1->r * 3.05;
704   g0->inner_r     = g0->r * 0.8;
705   g0->inner_r2    = 0;
706   g0->inner_r3    = 0;
707   g0->th          = g1->th + (180 / g0->nteeth);
708   g0->ratio       = g1->ratio / 3;
709
710   g0->tooth_slope = 0;
711   g0->nubs        = 3;
712   g0->spokes      = 0;
713   g0->size        = INVOLUTE_LARGE;
714
715   bp->gears = (gear **) calloc (6, sizeof(**bp->gears));
716   bp->ngears = 0;
717
718   bp->gears[bp->ngears++] = g1;
719   bp->gears[bp->ngears++] = g2;
720   bp->gears[bp->ngears++] = g3;
721   bp->gears[bp->ngears++] = g4;
722   bp->gears[bp->ngears++] = g0;
723 }
724
725
726
727
728 ENTRYPOINT void 
729 init_gears (ModeInfo *mi)
730 {
731   gears_configuration *bp;
732   int wire = MI_IS_WIREFRAME(mi);
733   int i;
734
735   if (!bps) {
736     bps = (gears_configuration *)
737       calloc (MI_NUM_SCREENS(mi), sizeof (gears_configuration));
738     if (!bps) {
739       fprintf(stderr, "%s: out of memory\n", progname);
740       exit(1);
741     }
742
743     bp = &bps[MI_SCREEN(mi)];
744   }
745
746   bp = &bps[MI_SCREEN(mi)];
747
748   bp->glx_context = init_GL(mi);
749
750   reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
751
752   if (!wire)
753     {
754       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
755       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
756       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
757       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
758
759       glEnable(GL_LIGHTING);
760       glEnable(GL_LIGHT0);
761       glEnable(GL_DEPTH_TEST);
762       glEnable(GL_CULL_FACE);
763
764       glLightfv(GL_LIGHT0, GL_POSITION, pos);
765       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
766       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
767       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
768     }
769
770   {
771     double spin_speed   = 0.5;
772     double wander_speed = 0.01;
773     double spin_accel   = 0.25;
774
775     bp->rot = make_rotator (do_spin ? spin_speed : 0,
776                             do_spin ? spin_speed : 0,
777                             do_spin ? spin_speed : 0,
778                             spin_accel,
779                             do_wander ? wander_speed : 0,
780                             True
781                             );
782     bp->trackball = gltrackball_init ();
783   }
784
785   if (!(random() % 8))
786     {
787       planetary_gears (mi);
788     }
789   else
790     {
791       gear *g = 0;
792       int total_gears = MI_COUNT (mi);
793       int i;
794       if (total_gears <= 0)
795         total_gears = 3 + abs (BELLRAND (8) - 4);  /* 3 - 7, mostly 3. */
796
797       bp->gears = (gear **) calloc (total_gears+2, sizeof(**bp->gears));
798       bp->ngears = 0;
799
800       for (i = 0; i < total_gears; i++)
801         g = place_new_gear (mi, g);
802     }
803
804
805   /* Center gears in scene. */
806   {
807     GLfloat minx=99999, miny=99999, maxx=-99999, maxy=-99999;
808     int i;
809     for (i = 0; i < bp->ngears; i++)
810       {
811         gear *g = bp->gears[i];
812         if (g->x - g->r < minx) minx = g->x - g->r;
813         if (g->x + g->r > maxx) maxx = g->x + g->r;
814         if (g->y - g->r < miny) miny = g->y - g->r;
815         if (g->y + g->r > maxy) maxy = g->y + g->r;
816       }
817     bp->bbox.x1 = minx;
818     bp->bbox.y1 = miny;
819     bp->bbox.x2 = maxx;
820     bp->bbox.y2 = maxy;
821   }
822
823   /* Now render each gear into its display list.
824    */
825   for (i = 0; i < bp->ngears; i++)
826     {
827       gear *g = bp->gears[i];
828       g->dlist = glGenLists (1);
829       if (! g->dlist)
830         {
831           check_gl_error ("glGenLists");
832           abort();
833         }
834
835       glNewList (g->dlist, GL_COMPILE);
836       g->polygons += draw_involute_gear (g, wire);
837       glEndList ();
838     }
839   if (bp->planetary_p)
840     armature (mi);
841 }
842
843
844 ENTRYPOINT void
845 draw_gears (ModeInfo *mi)
846 {
847   gears_configuration *bp = &bps[MI_SCREEN(mi)];
848   Display *dpy = MI_DISPLAY(mi);
849   Window window = MI_WINDOW(mi);
850   int i;
851
852   if (!bp->glx_context)
853     return;
854
855   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
856
857   glShadeModel(GL_SMOOTH);
858
859   glEnable(GL_DEPTH_TEST);
860   glEnable(GL_NORMALIZE);
861   glEnable(GL_CULL_FACE);
862
863   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
864
865   glPushMatrix ();
866
867   {
868     double x, y, z;
869     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
870     glTranslatef ((x - 0.5) * 4,
871                   (y - 0.5) * 4,
872                   (z - 0.5) * 7);
873
874     gltrackball_rotate (bp->trackball);
875
876     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
877
878     /* add a little rotation for -no-spin mode */
879     x -= 0.14;
880     y -= 0.06;
881
882     glRotatef (x * 360, 1.0, 0.0, 0.0);
883     glRotatef (y * 360, 0.0, 1.0, 0.0);
884     glRotatef (z * 360, 0.0, 0.0, 1.0);
885   }
886
887   /* Center the scene's bounding box in the window,
888      and scale it to fit. 
889    */
890   {
891     GLfloat w = bp->bbox.x2 - bp->bbox.x1;
892     GLfloat h = bp->bbox.y2 - bp->bbox.y1;
893     GLfloat s = 10.0 / (w > h ? w : h);
894     glScalef (s, s, s);
895     glTranslatef (-(bp->bbox.x1 + w/2),
896                   -(bp->bbox.y1 + h/2),
897                   0);
898   }
899
900   mi->polygon_count = 0;
901
902   for (i = 0; i < bp->ngears; i++)
903     {
904       gear *g = bp->gears[i];
905
906       glPushMatrix();
907
908       glTranslatef (g->x, g->y, g->z);
909       glRotatef (g->th, 0, 0, 1);
910
911       glCallList (g->dlist);
912       mi->polygon_count += g->polygons;
913
914       glPopMatrix ();
915     }
916
917   if (bp->planetary_p)
918     {
919       glCallList (bp->armature_dlist);
920       mi->polygon_count += bp->armature_polygons;
921     }
922
923   glPopMatrix ();
924
925   /* spin gears */
926   if (!bp->button_down_p)
927     for (i = 0; i < bp->ngears; i++)
928       {
929         gear *g = bp->gears[i];
930         double off = g->ratio * 5 * speed;
931         if (g->th > 0)
932           g->th += off;
933         else
934           g->th -= off;
935       }
936
937   if (mi->fps_p) do_fps (mi);
938   glFinish();
939
940   glXSwapBuffers(dpy, window);
941 }
942
943 XSCREENSAVER_MODULE ("Gears", gears)
944
945 #endif /* USE_GL */