From http://www.jwz.org/xscreensaver/xscreensaver-5.35.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   if (!bps) {
702     bps = (gears_configuration *)
703       calloc (MI_NUM_SCREENS(mi), sizeof (gears_configuration));
704     if (!bps) {
705       fprintf(stderr, "%s: out of memory\n", progname);
706       exit(1);
707     }
708   }
709
710   bp = &bps[MI_SCREEN(mi)];
711
712   bp->glx_context = init_GL(mi);
713
714   reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
715
716   if (!wire)
717     {
718       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
719       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
720       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
721       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
722
723       glEnable(GL_LIGHTING);
724       glEnable(GL_LIGHT0);
725       glEnable(GL_DEPTH_TEST);
726       glEnable(GL_CULL_FACE);
727
728       glLightfv(GL_LIGHT0, GL_POSITION, pos);
729       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
730       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
731       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
732     }
733
734   if (! bp->rot)
735     {
736       double spin_speed   = 0.5;
737       double wander_speed = 0.01;
738       double spin_accel   = 0.25;
739
740       bp->rot = make_rotator (do_spin ? spin_speed : 0,
741                               do_spin ? spin_speed : 0,
742                               do_spin ? spin_speed : 0,
743                               spin_accel,
744                               do_wander ? wander_speed : 0,
745                               True
746                               );
747       bp->trackball = gltrackball_init (True);
748     }
749
750   if (bp->gears)
751     {
752       for (i = 0; i < bp->ngears; i++)
753         free_gear (bp->gears[i]);
754       free (bp->gears);
755       bp->gears = 0;
756       bp->ngears = 0;
757     }
758
759   if (!(random() % 8))
760     {
761       planetary_gears (mi);
762     }
763   else
764     {
765       gear *g = 0;
766       int total_gears = MI_COUNT (mi);
767
768       bp->planetary_p = False;
769
770       if (total_gears <= 0)
771         total_gears = 3 + fabs (BELLRAND (8) - 4);  /* 3 - 7, mostly 3. */
772       bp->gears = (gear **) calloc (total_gears+2, sizeof(**bp->gears));
773       bp->ngears = 0;
774
775       for (i = 0; i < total_gears; i++)
776         g = place_new_gear (mi, g);
777     }
778
779
780   /* Center gears in scene. */
781   {
782     GLfloat minx=99999, miny=99999, maxx=-99999, maxy=-99999;
783     int i;
784     for (i = 0; i < bp->ngears; i++)
785       {
786         gear *g = bp->gears[i];
787         if (g->x - g->r < minx) minx = g->x - g->r;
788         if (g->x + g->r > maxx) maxx = g->x + g->r;
789         if (g->y - g->r < miny) miny = g->y - g->r;
790         if (g->y + g->r > maxy) maxy = g->y + g->r;
791       }
792     bp->bbox.x1 = minx;
793     bp->bbox.y1 = miny;
794     bp->bbox.x2 = maxx;
795     bp->bbox.y2 = maxy;
796   }
797
798   /* Now render each gear into its display list.
799    */
800   for (i = 0; i < bp->ngears; i++)
801     {
802       gear *g = bp->gears[i];
803       g->dlist = glGenLists (1);
804       if (! g->dlist)
805         {
806           check_gl_error ("glGenLists");
807           abort();
808         }
809
810       glNewList (g->dlist, GL_COMPILE);
811       g->polygons += draw_involute_gear (g, wire);
812       glEndList ();
813     }
814   if (bp->planetary_p)
815     armature (mi);
816 }
817
818
819 ENTRYPOINT void
820 draw_gears (ModeInfo *mi)
821 {
822   gears_configuration *bp = &bps[MI_SCREEN(mi)];
823   Display *dpy = MI_DISPLAY(mi);
824   Window window = MI_WINDOW(mi);
825   int i;
826
827   if (!bp->glx_context)
828     return;
829
830   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
831
832   glShadeModel(GL_SMOOTH);
833
834   glEnable(GL_DEPTH_TEST);
835   glEnable(GL_NORMALIZE);
836   glEnable(GL_CULL_FACE);
837
838   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
839
840   glPushMatrix ();
841
842   {
843     double x, y, z;
844     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
845     glTranslatef ((x - 0.5) * 4,
846                   (y - 0.5) * 4,
847                   (z - 0.5) * 7);
848
849     gltrackball_rotate (bp->trackball);
850
851     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
852
853     /* add a little rotation for -no-spin mode */
854     x -= 0.14;
855     y -= 0.06;
856
857     glRotatef (x * 360, 1.0, 0.0, 0.0);
858     glRotatef (y * 360, 0.0, 1.0, 0.0);
859     glRotatef (z * 360, 0.0, 0.0, 1.0);
860   }
861
862   /* Center the scene's bounding box in the window,
863      and scale it to fit. 
864    */
865   {
866     GLfloat w = bp->bbox.x2 - bp->bbox.x1;
867     GLfloat h = bp->bbox.y2 - bp->bbox.y1;
868     GLfloat s = 10.0 / (w > h ? w : h);
869     glScalef (s, s, s);
870     glTranslatef (-(bp->bbox.x1 + w/2),
871                   -(bp->bbox.y1 + h/2),
872                   0);
873   }
874
875   mi->polygon_count = 0;
876
877   for (i = 0; i < bp->ngears; i++)
878     {
879       gear *g = bp->gears[i];
880
881       glPushMatrix();
882
883       glTranslatef (g->x, g->y, g->z);
884       glRotatef (g->th, 0, 0, 1);
885
886       glCallList (g->dlist);
887       mi->polygon_count += g->polygons;
888
889       glPopMatrix ();
890     }
891
892   if (bp->planetary_p)
893     {
894       glCallList (bp->armature_dlist);
895       mi->polygon_count += bp->armature_polygons;
896     }
897
898   glPopMatrix ();
899
900   /* spin gears */
901   if (!bp->button_down_p)
902     for (i = 0; i < bp->ngears; i++)
903       {
904         gear *g = bp->gears[i];
905         double off = g->ratio * 5 * speed;
906         if (g->th > 0)
907           g->th += off;
908         else
909           g->th -= off;
910       }
911
912   if (mi->fps_p) do_fps (mi);
913   glFinish();
914
915   glXSwapBuffers(dpy, window);
916 }
917
918 ENTRYPOINT Bool
919 gears_handle_event (ModeInfo *mi, XEvent *event)
920 {
921   gears_configuration *bp = &bps[MI_SCREEN(mi)];
922
923   if (gltrackball_event_handler (event, bp->trackball,
924                                  MI_WIDTH (mi), MI_HEIGHT (mi),
925                                  &bp->button_down_p))
926     return True;
927   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
928     {
929       init_gears (mi);
930       return True;
931     }
932
933   return False;
934 }
935
936 XSCREENSAVER_MODULE ("Gears", gears)
937
938 #endif /* USE_GL */