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