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