~ubuntu-branches/ubuntu/dapper/xscreensaver/dapper-updates

« back to all changes in this revision

Viewing changes to hacks/glx/pinion.c

  • Committer: Bazaar Package Importer
  • Author(s): Ralf Hildebrandt
  • Date: 2005-04-09 00:06:43 UTC
  • mfrom: (1.1.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20050409000643-z0abtifbt9s20pcc
Tags: 4.21-3
Patch by Joachim Breitner to check more frequently if DPMS kicked in (closes: #303374, #286664).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* pinion, Copyright (c) 2004 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
 
 
12
#include <X11/Intrinsic.h>
 
13
 
 
14
extern XtAppContext app;
 
15
 
 
16
#define PROGCLASS       "Pinion"
 
17
#define HACK_INIT       init_pinion
 
18
#define HACK_DRAW       draw_pinion
 
19
#define HACK_RESHAPE    reshape_pinion
 
20
#define HACK_HANDLE_EVENT pinion_handle_event
 
21
#define EVENT_MASK      PointerMotionMask
 
22
#define sws_opts        xlockmore_opts
 
23
 
 
24
#define DEF_SPIN_SPEED   "1.0"
 
25
#define DEF_SCROLL_SPEED "1.0"
 
26
#define DEF_GEAR_SIZE    "1.0"
 
27
#define DEF_MAX_RPM      "900"
 
28
 
 
29
#define DEFAULTS        "*delay:        15000              \n" \
 
30
                        "*showFPS:      False              \n" \
 
31
                        "*wireframe:    False              \n" \
 
32
                        "*titleFont:  -*-times-bold-r-normal-*-180-*\n" \
 
33
                        "*titleFont2: -*-times-bold-r-normal-*-120-*\n" \
 
34
                        "*titleFont3: -*-times-bold-r-normal-*-80-*\n"  \
 
35
 
 
36
#undef countof
 
37
#define countof(x) (sizeof((x))/sizeof((*x)))
 
38
 
 
39
#undef BELLRAND
 
40
#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
 
41
 
 
42
#include "xlockmore.h"
 
43
#include "normals.h"
 
44
#include "gltrackball.h"
 
45
#include "glxfonts.h"
 
46
#include <ctype.h>
 
47
 
 
48
#ifdef USE_GL /* whole file */
 
49
 
 
50
#include <GL/glu.h>
 
51
 
 
52
typedef struct {
 
53
  unsigned long id;          /* unique name */
 
54
  double x, y, z;            /* position */
 
55
  double r;                  /* radius of the gear, at middle of teeth */
 
56
  double th;                 /* rotation (degrees) */
 
57
 
 
58
  GLint nteeth;              /* how many teeth */
 
59
  double tooth_w, tooth_h;   /* size of teeth */
 
60
 
 
61
  double inner_r;            /* radius of the (larger) inside hole */
 
62
  double inner_r2;           /* radius of the (smaller) inside hole, if any */
 
63
  double inner_r3;           /* yet another */
 
64
 
 
65
  double thickness;          /* height of the edge */
 
66
  double thickness2;         /* height of the (smaller) inside disc if any */
 
67
  double thickness3;         /* yet another */
 
68
  int spokes;                /* how many spokes inside, if any */
 
69
  int nubs;                  /* how many little nubbly bits, if any */
 
70
  double spoke_thickness;    /* spoke versus hole */
 
71
  GLfloat wobble;            /* factory defect! */
 
72
  int motion_blur_p;         /* whether it's spinning too fast to draw */
 
73
  int polygons;              /* how many polys in this gear */
 
74
 
 
75
  double ratio;              /* gearing ratio with previous gears */
 
76
  double rpm;                /* approximate revolutions per minute */
 
77
 
 
78
  Bool base_p;               /* whether this gear begins a new train */
 
79
  int coax_p;                /* whether this is one of a pair of bound gears.
 
80
                                1 for first, 2 for second. */
 
81
  double coax_thickness;     /* thickness of the other gear in the pair */
 
82
  enum { SMALL, MEDIUM, LARGE } size;   /* Controls complexity of mesh. */
 
83
  GLfloat color[4];
 
84
  GLfloat color2[4];
 
85
 
 
86
  GLuint dlist;
 
87
} gear;
 
88
 
 
89
 
 
90
typedef struct {
 
91
  GLXContext *glx_context;
 
92
  GLfloat vp_left, vp_right, vp_top, vp_bottom;    /* default visible area */
 
93
  GLfloat vp_width, vp_height;
 
94
  GLfloat render_left, render_right;  /* area in which gears are displayed */
 
95
  GLfloat layout_left, layout_right;  /* layout region, on the right side  */
 
96
 
 
97
  int ngears;
 
98
  int gears_size;
 
99
  gear **gears;
 
100
 
 
101
  trackball_state *trackball;
 
102
  Bool button_down_p;
 
103
  unsigned long mouse_gear_id;
 
104
 
 
105
  XFontStruct *xfont1, *xfont2, *xfont3;
 
106
  GLuint font1_dlist, font2_dlist, font3_dlist;
 
107
  GLuint title_list;
 
108
 
 
109
} pinion_configuration;
 
110
 
 
111
 
 
112
static pinion_configuration *pps = NULL;
 
113
static GLfloat spin_speed, scroll_speed, max_rpm, gear_size;
 
114
static GLfloat plane_displacement = 0.1;  /* distance between coaxial gears */
 
115
 
 
116
static Bool verbose_p = False;            /* print progress on stderr */
 
117
static Bool debug_placement_p = False;    /* extreme verbosity on stderr */
 
118
static Bool debug_p = False;              /* render as flat schematic */
 
119
static Bool debug_one_gear_p = False;     /* draw one big stationary gear */
 
120
static Bool wire_all_p = False;           /* in wireframe, do not abbreviate */
 
121
 
 
122
static int debug_size_failures;           /* for debugging messages */
 
123
static int debug_position_failures;
 
124
static unsigned long current_length;      /* gear count in current train */
 
125
static unsigned long current_blur_length; /* how long have we been blurring? */
 
126
 
 
127
 
 
128
static XrmOptionDescRec opts[] = {
 
129
  { "-spin",   ".spinSpeed",   XrmoptionSepArg, 0 },
 
130
  { "-scroll", ".scrollSpeed", XrmoptionSepArg, 0 },
 
131
  { "-size",   ".gearSize",    XrmoptionSepArg, 0 },
 
132
  { "-max-rpm",".maxRPM",      XrmoptionSepArg, 0 },
 
133
  { "-debug",  ".debug",       XrmoptionNoArg, "True" },
 
134
  { "-verbose",".verbose",     XrmoptionNoArg, "True" },
 
135
};
 
136
 
 
137
static argtype vars[] = {
 
138
  {&spin_speed,   "spinSpeed",   "SpinSpeed",   DEF_SPIN_SPEED,   t_Float},
 
139
  {&scroll_speed, "scrollSpeed", "ScrollSpeed", DEF_SCROLL_SPEED, t_Float},
 
140
  {&gear_size,    "gearSize",    "GearSize",    DEF_GEAR_SIZE,    t_Float},
 
141
  {&max_rpm,      "maxRPM",      "MaxRPM",      DEF_MAX_RPM,      t_Float},
 
142
  {&debug_p,      "debug",       "Debug",       "False",          t_Bool},
 
143
  {&verbose_p,    "verbose",     "Verbose",     "False",          t_Bool},
 
144
};
 
145
 
 
146
ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
147
 
 
148
 
 
149
/* Font stuff
 
150
 */
 
151
 
 
152
static void
 
153
load_fonts (ModeInfo *mi)
 
154
{
 
155
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
156
  load_font (mi->dpy, "titleFont",  &pp->xfont1, &pp->font1_dlist);
 
157
  load_font (mi->dpy, "titleFont2", &pp->xfont2, &pp->font2_dlist);
 
158
  load_font (mi->dpy, "titleFont3", &pp->xfont3, &pp->font3_dlist);
 
159
}
 
160
 
 
161
 
 
162
 
 
163
static void rpm_string (double rpm, char *s);
 
164
 
 
165
static void
 
166
new_label (ModeInfo *mi)
 
167
{
 
168
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
169
  char label[1024];
 
170
  int i;
 
171
  gear *g = 0;
 
172
 
 
173
  if (pp->mouse_gear_id)
 
174
    for (i = 0; i < pp->ngears; i++)
 
175
      if (pp->gears[i]->id == pp->mouse_gear_id)
 
176
        {
 
177
          g = pp->gears[i];
 
178
          break;
 
179
        }
 
180
 
 
181
  if (!g)
 
182
    *label = 0;
 
183
  else
 
184
    {
 
185
      sprintf (label, "%d teeth\n", g->nteeth);
 
186
      rpm_string (g->rpm, label + strlen(label));
 
187
      if (debug_p)
 
188
        sprintf (label + strlen (label), "\nPolys:  %d\nModel:  %s  (%.2f)\n",
 
189
                 g->polygons,
 
190
                 (g->size == SMALL ? "small" : g->size == MEDIUM ? "medium"
 
191
                  : "large"),
 
192
                 g->tooth_h * MI_HEIGHT(mi));
 
193
    }
 
194
 
 
195
  glNewList (pp->title_list, GL_COMPILE);
 
196
  if (*label)
 
197
    {
 
198
      XFontStruct *f;
 
199
      GLuint fl;
 
200
      if (MI_WIDTH(mi) >= 500 && MI_HEIGHT(mi) >= 375)
 
201
        f = pp->xfont1, fl = pp->font1_dlist;                  /* big font */
 
202
      else if (MI_WIDTH(mi) >= 350 && MI_HEIGHT(mi) >= 260)
 
203
        f = pp->xfont2, fl = pp->font2_dlist;                  /* small font */
 
204
      else
 
205
        f = pp->xfont3, fl = pp->font3_dlist;                  /* tiny font */
 
206
 
 
207
      glColor3f (0.8, 0.8, 0);
 
208
      print_gl_string (mi->dpy, f, fl,
 
209
                       mi->xgwa.width, mi->xgwa.height,
 
210
                       10, mi->xgwa.height - 10,
 
211
                       label);
 
212
    }
 
213
  glEndList ();
 
214
}
 
215
 
 
216
 
 
217
/* Some utilities
 
218
 */
 
219
 
 
220
 
 
221
/* Find the gear in the scene that is farthest to the right or left.
 
222
 */
 
223
static gear *
 
224
farthest_gear (ModeInfo *mi, Bool left_p)
 
225
{
 
226
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
227
  int i;
 
228
  gear *rg = 0;
 
229
  double x = (left_p ? 999999 : -999999);
 
230
  for (i = 0; i < pp->ngears; i++)
 
231
    {
 
232
      gear *g = pp->gears[i];
 
233
      double gx = g->x + ((g->r + g->tooth_h) * (left_p ? -1 : 1));
 
234
      if (left_p ? (x > gx) : (x < gx))
 
235
        {
 
236
          rg = g;
 
237
          x = gx;
 
238
        }
 
239
    }
 
240
  return rg;
 
241
}
 
242
 
 
243
 
 
244
/* Compute the revolutions per minute of a gear.
 
245
 */
 
246
static void
 
247
compute_rpm (ModeInfo *mi, gear *g)
 
248
{
 
249
  double fps, rpf, rps;
 
250
  fps = (MI_PAUSE(mi) == 0 ? 999999 : 1000000.0 / MI_PAUSE(mi));
 
251
 
 
252
  if (fps > 150) fps = 150;  /* let's be reasonable... */
 
253
  if (fps < 10)  fps = 10;
 
254
 
 
255
  rpf    = (g->ratio * spin_speed) / 360.0;   /* rotations per frame */
 
256
  rps    = rpf * fps;                         /* rotations per second */
 
257
  g->rpm = rps * 60;
 
258
}
 
259
 
 
260
/* Prints the RPM into a string, doing fancy float formatting.
 
261
 */
 
262
static void
 
263
rpm_string (double rpm, char *s)
 
264
{
 
265
  char buf[30];
 
266
  int L;
 
267
  if (rpm >= 0.1)          sprintf (buf, "%.2f", rpm);
 
268
  else if (rpm >= 0.001)   sprintf (buf, "%.4f", rpm);
 
269
  else if (rpm >= 0.00001) sprintf (buf, "%.8f", rpm);
 
270
  else                     sprintf (buf, "%.16f",rpm);
 
271
 
 
272
  L = strlen(buf);
 
273
  while (buf[L-1] == '0') buf[--L] = 0;
 
274
  if (buf[L-1] == '.') buf[--L] = 0;
 
275
  strcpy (s, buf);
 
276
  strcat (s, " RPM");
 
277
}
 
278
 
 
279
 
 
280
 
 
281
/* Which of the gear's inside rings is the biggest? 
 
282
 */
 
283
static int
 
284
biggest_ring (gear *g, double *posP, double *sizeP, double *heightP)
 
285
{
 
286
  double r0 = (g->r - g->tooth_h/2);
 
287
  double r1 = g->inner_r;
 
288
  double r2 = g->inner_r2;
 
289
  double r3 = g->inner_r3;
 
290
  double w1 = (r1 ? r0 - r1 : r0);
 
291
  double w2 = (r2 ? r1 - r2 : 0);
 
292
  double w3 = (r3 ? r2 - r3 : 0);
 
293
  double h1 = g->thickness;
 
294
  double h2 = g->thickness2;
 
295
  double h3 = g->thickness3;
 
296
 
 
297
  if (g->spokes) w2 = 0;
 
298
 
 
299
  if (w1 > w2 && w1 > w3)
 
300
    {
 
301
      if (posP)    *posP = (r0+r1)/2;
 
302
      if (sizeP)   *sizeP = w1;
 
303
      if (heightP) *heightP = h1;
 
304
      return 0;
 
305
    }
 
306
  else if (w2 > w1 && w2 > w3)
 
307
    {
 
308
      if (posP)  *posP = (r1+r2)/2;
 
309
      if (sizeP) *sizeP = w2;
 
310
      if (heightP) *heightP = h2;
 
311
      return 1;
 
312
    }
 
313
  else
 
314
    {
 
315
      if (posP)  *posP = (r2+r3)/2;
 
316
      if (sizeP) *sizeP = w3;
 
317
      if (heightP) *heightP = h3;
 
318
      return 1;
 
319
    }
 
320
}
 
321
 
 
322
 
 
323
/* Layout and stuff.
 
324
 */
 
325
 
 
326
 
 
327
/* Create and return a new gear sized for placement next to or on top of
 
328
   the given parent gear (if any.)  Returns 0 if out of memory.
 
329
 */
 
330
static gear *
 
331
new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p)
 
332
{
 
333
  gear *g = (gear *) calloc (1, sizeof (*g));
 
334
  int loop_count = 0;
 
335
  static unsigned long id = 0;
 
336
 
 
337
  if (!g) return 0;
 
338
  if (coaxial_p && !parent) abort();
 
339
  g->id = ++id;
 
340
 
 
341
  while (1)
 
342
    {
 
343
      loop_count++;
 
344
      if (loop_count > 1000)
 
345
        /* The only time we loop in here is when making a coaxial gear, and
 
346
           trying to pick a radius that is either significantly larger or
 
347
           smaller than its parent.  That shouldn't be hard, so something
 
348
           must be really wrong if we can't do that in only a few tries.
 
349
         */
 
350
        abort();
 
351
 
 
352
      /* Pick the size of the teeth.
 
353
       */
 
354
      if (parent && !coaxial_p) /* adjascent gears need matching teeth */
 
355
        {
 
356
          g->tooth_w = parent->tooth_w;
 
357
          g->tooth_h = parent->tooth_h;
 
358
          g->thickness  = parent->thickness;
 
359
          g->thickness2 = parent->thickness2;
 
360
          g->thickness3 = parent->thickness3;
 
361
        }
 
362
      else                 /* gears that begin trains get any size they want */
 
363
        {
 
364
          double scale = (1.0 + BELLRAND(4.0)) * gear_size;
 
365
          g->tooth_w = 0.007 * scale;
 
366
          g->tooth_h = 0.005 * scale;
 
367
          g->thickness  = g->tooth_h * (0.1 + BELLRAND(1.5));
 
368
          g->thickness2 = g->thickness / 4;
 
369
          g->thickness3 = g->thickness;
 
370
        }
 
371
 
 
372
      /* Pick the number of teeth, and thus, the radius.
 
373
       */
 
374
      {
 
375
        double c;
 
376
 
 
377
      AGAIN:
 
378
        g->nteeth = 3 + (random() % 97);    /* from 3 to 100 teeth */
 
379
 
 
380
        if (g->nteeth < 7 && (random() % 5) != 0)
 
381
          goto AGAIN;   /* Let's make very small tooth-counts more rare */
 
382
 
 
383
        c = g->nteeth * g->tooth_w * 2;     /* circumference = teeth + gaps */
 
384
        g->r = c / (M_PI * 2);              /* c = 2 pi r  */
 
385
      }
 
386
 
 
387
 
 
388
      /* Are we done now?
 
389
       */
 
390
      if (! coaxial_p) break;   /* yes */
 
391
      if (g->nteeth == parent->nteeth) continue; /* ugly */
 
392
      if (g->r  < parent->r * 0.6) break;  /* g much smaller than parent */
 
393
      if (parent->r < g->r  * 0.6) break;  /* g much larger than parent  */
 
394
    }
 
395
 
 
396
  /* Colorize
 
397
   */
 
398
  g->color[0] = 0.5 + frand(0.5);
 
399
  g->color[1] = 0.5 + frand(0.5);
 
400
  g->color[2] = 0.5 + frand(0.5);
 
401
  g->color[3] = 1.0;
 
402
 
 
403
  g->color2[0] = g->color[0] * 0.85;
 
404
  g->color2[1] = g->color[1] * 0.85;
 
405
  g->color2[2] = g->color[2] * 0.85;
 
406
  g->color2[3] = g->color[3];
 
407
 
 
408
 
 
409
  /* Decide on shape of gear interior:
 
410
     - just a ring with teeth;
 
411
     - that, plus a thinner in-set "plate" in the middle;
 
412
     - that, plus a thin raised "lip" on the inner plate;
 
413
     - or, a wide lip (really, a thicker third inner plate.)
 
414
   */
 
415
  if ((random() % 10) == 0)
 
416
    {
 
417
      /* inner_r can go all the way in; there's no inset disc. */
 
418
      g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8);
 
419
      g->inner_r2 = 0;
 
420
      g->inner_r3 = 0;
 
421
    }
 
422
  else
 
423
    {
 
424
      /* inner_r doesn't go in very far; inner_r2 is an inset disc. */
 
425
      g->inner_r  = (g->r * 0.5)  + frand((g->r - g->tooth_h) * 0.4);
 
426
      g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5);
 
427
      g->inner_r3 = 0;
 
428
 
 
429
      if (g->inner_r2 > (g->r * 0.2))
 
430
        {
 
431
          int nn = (random() % 10);
 
432
          if (nn <= 2)
 
433
            g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2);
 
434
          else if (nn <= 7 && g->inner_r2 >= 0.1)
 
435
            g->inner_r3 = g->inner_r2 - 0.01;
 
436
        }
 
437
    }
 
438
 
 
439
  /* Coaxial gears need to have the same innermost hole size (for the axle.)
 
440
     Use whichever of the two is smaller.  (Modifies parent.)
 
441
   */
 
442
  if (coaxial_p)
 
443
    {
 
444
      double hole1 = (g->inner_r3 ? g->inner_r3 :
 
445
                      g->inner_r2 ? g->inner_r2 :
 
446
                      g->inner_r);
 
447
      double hole2 = (parent->inner_r3 ? parent->inner_r3 :
 
448
                      parent->inner_r2 ? parent->inner_r2 :
 
449
                      parent->inner_r);
 
450
      double hole = (hole1 < hole2 ? hole1 : hole2);
 
451
      if (hole <= 0) abort();
 
452
 
 
453
      if      (g->inner_r3) g->inner_r3 = hole;
 
454
      else if (g->inner_r2) g->inner_r2 = hole;
 
455
      else                  g->inner_r  = hole;
 
456
 
 
457
      if      (parent->inner_r3) parent->inner_r3 = hole;
 
458
      else if (parent->inner_r2) parent->inner_r2 = hole;
 
459
      else                       parent->inner_r  = hole;
 
460
    }
 
461
 
 
462
  /* If we have three discs, sometimes make the middle disc be spokes.
 
463
   */
 
464
  if (g->inner_r3 && ((random() % 5) == 0))
 
465
    {
 
466
      g->spokes = 2 + BELLRAND (5);
 
467
      g->spoke_thickness = 1 + frand(7.0);
 
468
      if (g->spokes == 2 && g->spoke_thickness < 2)
 
469
        g->spoke_thickness += 1;
 
470
    }
 
471
 
 
472
  /* Sometimes add little nubbly bits, if there is room.
 
473
   */
 
474
  if (g->nteeth > 5)
 
475
    {
 
476
      double size = 0;
 
477
      biggest_ring (g, 0, &size, 0);
 
478
      if (size > g->r * 0.2 && (random() % 5) == 0)
 
479
        {
 
480
          g->nubs = 1 + (random() % 16);
 
481
          if (g->nubs > 8) g->nubs = 1;
 
482
        }
 
483
    }
 
484
 
 
485
  if (g->inner_r3 > g->inner_r2) abort();
 
486
  if (g->inner_r2 > g->inner_r) abort();
 
487
  if (g->inner_r  > g->r) abort();
 
488
 
 
489
  /* Decide how complex the polygon model should be.
 
490
   */
 
491
  {
 
492
    double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */
 
493
    if (pix <= 2.5)      g->size = SMALL;
 
494
    else if (pix <= 3.5) g->size = MEDIUM;
 
495
    else                 g->size = LARGE;
 
496
  }
 
497
 
 
498
  g->base_p = !parent;
 
499
 
 
500
  return g;
 
501
}
 
502
 
 
503
 
 
504
/* Given a newly-created gear, place it next to its parent in the scene,
 
505
   with its teeth meshed and the proper velocity.  Returns False if it
 
506
   didn't work.  (Call this a bunch of times until either it works, or
 
507
   you decide it's probably not going to.)
 
508
 */
 
509
static Bool
 
510
place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p)
 
511
{
 
512
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
513
 
 
514
  /* If this gear takes up more than 1/3rd of the screen, it's no good.
 
515
   */
 
516
  if (((g->r + g->tooth_h) * (6 / gear_size)) >= pp->vp_width ||
 
517
      ((g->r + g->tooth_h) * (6 / gear_size)) >= pp->vp_height)
 
518
    {
 
519
      if (verbose_p && debug_placement_p && 0)
 
520
        fprintf (stderr,
 
521
                 "%s: placement: too big: %.2f @ %.2f vs %.2f x %.2f\n",
 
522
                 progname,
 
523
                 (g->r + g->tooth_h), gear_size,
 
524
                 pp->vp_width, pp->vp_height);
 
525
      debug_size_failures++;
 
526
      return False;
 
527
    }
 
528
 
 
529
  /* Compute this gear's velocity.
 
530
   */
 
531
  if (! parent)
 
532
    {
 
533
      g->ratio = 0.8 + BELLRAND(0.4);  /* 0.8-1.2 = 8-12rpm @ 60fps */
 
534
      g->th = frand (90) * ((random() & 1) ? 1.0 : -1.0);
 
535
    }
 
536
  else if (coaxial_p)
 
537
    {
 
538
      g->ratio = parent->ratio; /* bound gears have the same ratio */
 
539
      g->th = parent->th;
 
540
      g->rpm = parent->rpm;
 
541
      g->wobble = parent->wobble;
 
542
    }
 
543
  else
 
544
    {
 
545
      /* Gearing ratio is the ratio of the number of teeth to previous gear
 
546
         (which is also the ratio of the circumferences.)
 
547
       */
 
548
      g->ratio = (double) parent->nteeth / (double) g->nteeth;
 
549
 
 
550
      /* Set our initial rotation to match that of the previous gear,
 
551
         multiplied by the gearing ratio.  (This is finessed later,
 
552
         once we know the exact position of the gear relative to its
 
553
         parent.)
 
554
      */
 
555
      g->th = -(parent->th * g->ratio);
 
556
 
 
557
      if (g->nteeth & 1)    /* rotate 1/2 tooth-size if odd number of teeth */
 
558
        {
 
559
          double off = (180.0 / g->nteeth);
 
560
          if (g->th > 0)
 
561
            g->th += off;
 
562
          else
 
563
            g->th -= off;
 
564
        }
 
565
 
 
566
      /* ratios are cumulative for all gears in the train. */
 
567
      g->ratio *= parent->ratio;
 
568
    }
 
569
 
 
570
 
 
571
  /* Place the gear relative to the parent.
 
572
   */
 
573
 
 
574
  if (! parent)
 
575
    {
 
576
      gear *rg = farthest_gear (mi, False);
 
577
      double right = (rg ? rg->x + rg->r + rg->tooth_h : 0);
 
578
      if (right < pp->layout_left) /* place off screen */
 
579
        right = pp->layout_left;
 
580
 
 
581
      g->x = right + g->r + g->tooth_h + (0.01 / gear_size);
 
582
      g->y = 0;
 
583
      g->z = 0;
 
584
 
 
585
      if (debug_one_gear_p)
 
586
        g->x = 0;
 
587
    }
 
588
  else if (coaxial_p)
 
589
    {
 
590
      double off = plane_displacement;
 
591
 
 
592
      g->x = parent->x;
 
593
      g->y = parent->y;
 
594
      g->z = parent->z + (g->r > parent->r      /* small gear on top */
 
595
                          ? -off : off);
 
596
 
 
597
      if (parent->r > g->r)     /* mark which is top and which is bottom */
 
598
        {
 
599
          parent->coax_p = 1;
 
600
          g->coax_p      = 2;
 
601
          parent->wobble = 0;   /* looks bad when axle moves */
 
602
        }
 
603
      else
 
604
        {
 
605
          parent->coax_p = 2;
 
606
          g->coax_p      = 1;
 
607
          g->wobble      = 0;
 
608
        }
 
609
 
 
610
      g->coax_thickness      = parent->thickness;
 
611
      parent->coax_thickness = g->thickness;
 
612
 
 
613
      /* Don't let the train get too close to or far from the screen.
 
614
         If we're getting too close, give up on this gear.
 
615
         (But getting very far away is fine.)
 
616
       */
 
617
      if (g->z >=  off * 4 ||
 
618
          g->z <= -off * 4)
 
619
        {
 
620
          if (verbose_p && debug_placement_p)
 
621
            fprintf (stderr, "%s: placement: bad depth: %.2f\n",
 
622
                     progname, g->z);
 
623
          debug_position_failures++;
 
624
          return False;
 
625
        }
 
626
    }
 
627
  else                          /* position it somewhere next to the parent. */
 
628
    {
 
629
      double r_off = parent->r + g->r;
 
630
      int angle;
 
631
 
 
632
      if ((random() % 3) != 0)
 
633
        angle = (random() % 240) - 120;   /* mostly -120 to +120 degrees */
 
634
      else
 
635
        angle = (random() % 360) - 180;   /* sometimes -180 to +180 degrees */
 
636
 
 
637
      g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off);
 
638
      g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off);
 
639
      g->z = parent->z;
 
640
 
 
641
      /* If the angle we picked would have positioned this gear
 
642
         more than halfway off screen, that's no good. */
 
643
      if (g->y > pp->vp_top ||
 
644
          g->y < pp->vp_bottom)
 
645
        {
 
646
          if (verbose_p && debug_placement_p)
 
647
            fprintf (stderr, "%s: placement: out of bounds: %s\n",
 
648
                     progname, (g->y > pp->vp_top ? "top" : "bottom"));
 
649
          debug_position_failures++;
 
650
          return False;
 
651
        }
 
652
 
 
653
      /* avoid accidentally changing sign of "th" in the math below. */
 
654
      g->th += (g->th > 0 ? 360 : -360);
 
655
 
 
656
      /* Adjust the rotation of the gear so that its teeth line up with its
 
657
         parent, based on the position of the gear and the current rotation
 
658
         of the parent.
 
659
       */
 
660
      {
 
661
        double p_c = 2 * M_PI * parent->r;  /* circumference of parent */
 
662
        double g_c = 2 * M_PI * g->r;       /* circumference of g  */
 
663
 
 
664
        double p_t = p_c * (angle/360.0);   /* distance travelled along
 
665
                                               circumference of parent when
 
666
                                               moving "angle" degrees along
 
667
                                               parent. */
 
668
        double g_rat = p_t / g_c;           /* if travelling that distance
 
669
                                               along circumference of g,
 
670
                                               ratio of g's circumference
 
671
                                               travelled. */
 
672
        double g_th = 360.0 * g_rat;        /* that ratio in degrees */
 
673
 
 
674
        g->th += angle + g_th;
 
675
      }
 
676
    }
 
677
 
 
678
  if (debug_one_gear_p)
 
679
    {
 
680
      compute_rpm (mi, g);
 
681
      return True;
 
682
    }
 
683
 
 
684
  /* If the position we picked for this gear would cause it to already
 
685
     be visible on the screen, give up.  This can happen when the train
 
686
     is growing backwards, and we don't want to see gears flash into
 
687
     existence.
 
688
   */
 
689
  if (g->x - g->r - g->tooth_h < pp->render_right)
 
690
    {
 
691
      if (verbose_p && debug_placement_p)
 
692
        fprintf (stderr, "%s: placement: out of bounds: left\n", progname);
 
693
      debug_position_failures++;
 
694
      return False;
 
695
    }
 
696
 
 
697
  /* If the position we picked for this gear causes it to overlap
 
698
     with any earlier gear in the train, give up.
 
699
   */
 
700
  {
 
701
    int i;
 
702
 
 
703
    for (i = pp->ngears-1; i >= 0; i--)
 
704
      {
 
705
        gear *og = pp->gears[i];
 
706
 
 
707
        if (og == g) continue;
 
708
        if (og == parent) continue;
 
709
        if (g->z != og->z) continue;    /* Ignore unless on same layer */
 
710
 
 
711
        /* Collision detection without sqrt:
 
712
             d = sqrt(a^2 + b^2)   d^2 = a^2 + b^2
 
713
             d < r1 + r2           d^2 < (r1 + r2)^2
 
714
         */
 
715
        if (((g->x - og->x) * (g->x - og->x) +
 
716
             (g->y - og->y) * (g->y - og->y)) <
 
717
            ((g->r + g->tooth_h + og->r + og->tooth_h) *
 
718
             (g->r + g->tooth_h + og->r + og->tooth_h)))
 
719
          {
 
720
            if (verbose_p && debug_placement_p)
 
721
              fprintf (stderr, "%s: placement: collision with %lu\n",
 
722
                       progname, og->id);
 
723
            debug_position_failures++;
 
724
            return False;
 
725
          }
 
726
      }
 
727
  }
 
728
 
 
729
  compute_rpm (mi, g);
 
730
 
 
731
 
 
732
  /* Make deeper gears be darker.
 
733
   */
 
734
  {
 
735
    double depth = g->z / plane_displacement;
 
736
    double brightness = 1 + (depth / 6);
 
737
    double limit = 0.4;
 
738
    if (brightness < limit)   brightness = limit;
 
739
    if (brightness > 1/limit) brightness = 1/limit;
 
740
    g->color[0]  *= brightness;
 
741
    g->color[1]  *= brightness;
 
742
    g->color[2]  *= brightness;
 
743
    g->color2[0] *= brightness;
 
744
    g->color2[1] *= brightness;
 
745
    g->color2[2] *= brightness;
 
746
  }
 
747
 
 
748
  /* If a single frame of animation would cause the gear to rotate by
 
749
     more than 1/2 the size of a single tooth, then it won't look right:
 
750
     the gear will appear to be turning at some lower harmonic of its
 
751
     actual speed.
 
752
   */
 
753
  {
 
754
    double ratio = g->ratio * spin_speed;
 
755
    double blur_limit = 180.0 / g->nteeth;
 
756
 
 
757
    if (ratio > blur_limit)
 
758
      g->motion_blur_p = 1;
 
759
 
 
760
    if (!coaxial_p)
 
761
      {
 
762
        /* ride until the wheels fall off... */
 
763
        if (ratio > blur_limit * 0.7) g->wobble += (random() % 2);
 
764
        if (ratio > blur_limit * 0.9) g->wobble += (random() % 2);
 
765
        if (ratio > blur_limit * 1.1) g->wobble += (random() % 2);
 
766
        if (ratio > blur_limit * 1.3) g->wobble += (random() % 2);
 
767
        if (ratio > blur_limit * 1.5) g->wobble += (random() % 2);
 
768
        if (ratio > blur_limit * 1.7) g->wobble += (random() % 2);
 
769
      }
 
770
  }
 
771
 
 
772
  return True;
 
773
}
 
774
 
 
775
static void
 
776
free_gear (gear *g)
 
777
{
 
778
  if (g->dlist)
 
779
    glDeleteLists (g->dlist, 1);
 
780
  free (g);
 
781
}
 
782
 
 
783
 
 
784
/* Make a new gear, place it next to its parent in the scene,
 
785
   with its teeth meshed and the proper velocity.  Returns the gear;
 
786
   or 0 if it didn't work.  (Call this a bunch of times until either
 
787
   it works, or you decide it's probably not going to.)
 
788
 */
 
789
static gear *
 
790
place_new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p)
 
791
{
 
792
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
793
  int loop_count = 0;
 
794
  gear *g = 0;
 
795
 
 
796
  while (1)
 
797
    {
 
798
      loop_count++;
 
799
      if (loop_count >= 100)
 
800
        {
 
801
          if (g)
 
802
            free_gear (g);
 
803
          g = 0;
 
804
          break;
 
805
        }
 
806
 
 
807
      g = new_gear (mi, parent, coaxial_p);
 
808
      if (!g) return 0;  /* out of memory? */
 
809
 
 
810
      if (place_gear (mi, g, parent, coaxial_p))
 
811
        break;
 
812
    }
 
813
 
 
814
  if (! g) return 0;
 
815
 
 
816
  /* We got a gear, and it is properly positioned.
 
817
     Insert it in the scene.
 
818
   */
 
819
  if (pp->ngears + 2 >= pp->gears_size)
 
820
    {
 
821
      pp->gears_size += 100;
 
822
      pp->gears = (gear **) realloc (pp->gears,
 
823
                                     pp->gears_size * sizeof (*pp->gears));
 
824
      if (! pp->gears)
 
825
        {
 
826
          fprintf (stderr, "%s: out of memory (%d gears)\n",
 
827
                   progname, pp->gears_size);
 
828
        }
 
829
    }
 
830
 
 
831
  pp->gears[pp->ngears++] = g;
 
832
  return g;
 
833
}
 
834
 
 
835
 
 
836
static void delete_gear (ModeInfo *mi, gear *g);
 
837
 
 
838
static void
 
839
push_gear (ModeInfo *mi)
 
840
{
 
841
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
842
  gear *g;
 
843
  gear *parent = (pp->ngears <= 0 ? 0 : pp->gears[pp->ngears-1]);
 
844
 
 
845
  Bool tried_coaxial_p = False;
 
846
  Bool coaxial_p = False;
 
847
  Bool last_ditch_coax_p = False;
 
848
  int loop_count = 0;
 
849
 
 
850
  debug_size_failures = 0;
 
851
  debug_position_failures = 0;
 
852
 
 
853
 AGAIN:
 
854
  loop_count++;
 
855
  if (loop_count > 100) abort();  /* we're doomed! */
 
856
  
 
857
  g = 0;
 
858
 
 
859
  /* If the gears are turning at LUDICROUS SPEED, unhook the train to
 
860
     reset things to a sane velocity.
 
861
 
 
862
     10,000 RPM at 30 FPS = 5.5 rotations per frame.
 
863
      1,000 RPM at 30 FPS = 0.5 rotations per frame.
 
864
        600 RPM at 30 FPS =  3 frames per rotation.
 
865
        200 RPM at 30 FPS =  9 frames per rotation.
 
866
        100 RPM at 30 FPS = 18 frames per rotation.
 
867
         50 RPM at 30 FPS = 36 frames per rotation.
 
868
         10 RPM at 30 FPS =  3 sec per rotation.
 
869
          1 RPM at 30 FPS = 30 sec per rotation.
 
870
         .5 RPM at 30 FPS =  1 min per rotation.
 
871
         .1 RPM at 30 FPS =  5 min per rotation.
 
872
   */
 
873
  if (parent && parent->rpm > max_rpm)
 
874
    {
 
875
      if (verbose_p)
 
876
        {
 
877
          char buf[100];
 
878
          rpm_string (parent->rpm, buf);
 
879
          fprintf (stderr, "%s: ludicrous speed!  %s\n\n", progname, buf);
 
880
        }
 
881
      parent = 0;
 
882
    }
 
883
 
 
884
  /* If the last N gears we've placed have all been motion-blurred, then
 
885
     it's a safe guess that we've wandered off into the woods and aren't
 
886
     coming back.  Bail on this train.
 
887
   */
 
888
  if (current_blur_length >= 10)
 
889
    {
 
890
      if (verbose_p)
 
891
        fprintf (stderr, "%s: it's a blurpocalypse!\n\n", progname);
 
892
      parent = 0;
 
893
    }
 
894
 
 
895
 
 
896
 
 
897
  /* Sometimes, try to make a coaxial gear.
 
898
   */
 
899
  if (parent && !parent->coax_p && (random() % 40) == 0)
 
900
    {
 
901
      tried_coaxial_p = True;
 
902
      coaxial_p = True;
 
903
      g = place_new_gear (mi, parent, coaxial_p);
 
904
    }
 
905
 
 
906
  /* Try to make a regular gear.
 
907
   */
 
908
  if (!g)
 
909
    {
 
910
      coaxial_p = False;
 
911
      g = place_new_gear (mi, parent, coaxial_p);
 
912
    }
 
913
 
 
914
  /* If we couldn't make a regular gear, then try to make a coxial gear
 
915
     (unless we already tried that.)
 
916
   */
 
917
  if (!g && !tried_coaxial_p && parent && !parent->coax_p)
 
918
    {
 
919
      tried_coaxial_p = True;
 
920
      coaxial_p = True;
 
921
      g = place_new_gear (mi, parent, coaxial_p);
 
922
      if (g)
 
923
        last_ditch_coax_p = True;
 
924
    }
 
925
 
 
926
  /* If we couldn't do that either, then the train has hit a dead end:
 
927
     start a new train.
 
928
   */
 
929
  if (!g)
 
930
    {
 
931
      coaxial_p = False;
 
932
      if (verbose_p)
 
933
        fprintf (stderr, "%s: dead end!\n\n", progname);
 
934
      parent = 0;
 
935
      g = place_new_gear (mi, parent, coaxial_p);
 
936
    }
 
937
 
 
938
  if (! g)
 
939
    {
 
940
      /* Unable to make/place any gears at all!
 
941
         This can happen if we've backed ourself into a corner very near
 
942
         the top-right or bottom-right corner of the growth zone.
 
943
         It's time to add a gear, but there's no room to add one!
 
944
         In that case, let's just wipe all the gears that are in the
 
945
         growth zone and try again.
 
946
       */
 
947
      int i;
 
948
 
 
949
      if (verbose_p && debug_placement_p)
 
950
        fprintf (stderr,
 
951
                 "%s: placement: resetting growth zone!  "
 
952
                 "failed: %d size, %d pos\n",
 
953
                 progname,
 
954
                 debug_size_failures, debug_position_failures);
 
955
      for (i = pp->ngears-1; i >= 0; i--)
 
956
        {
 
957
          gear *g = pp->gears[i];
 
958
          if (g->x - g->r - g->tooth_h < pp->render_left)
 
959
            delete_gear (mi, g);
 
960
        }
 
961
      goto AGAIN;
 
962
    }
 
963
 
 
964
  if (g->coax_p)
 
965
    {
 
966
      if (g->x != parent->x) abort();
 
967
      if (g->y != parent->y) abort();
 
968
      if (g->z == parent->z) abort();
 
969
      if (g->r == parent->r) abort();
 
970
      if (g->th != parent->th) abort();
 
971
      if (g->ratio != parent->ratio) abort();
 
972
      if (g->rpm != parent->rpm) abort();
 
973
    }
 
974
 
 
975
  if (verbose_p)
 
976
    {
 
977
      fprintf (stderr, "%s: %5lu ", progname, g->id);
 
978
 
 
979
      fputc ((g->motion_blur_p ? '*' : ' '), stderr);
 
980
      fputc (((g->coax_p && last_ditch_coax_p) ? '2' :
 
981
              g->coax_p ? '1' : ' '),
 
982
             stderr);
 
983
 
 
984
      fprintf (stderr, " %2d%%",
 
985
               (int) (g->r * 2 * 100 / pp->vp_height));
 
986
      fprintf (stderr, "  %2d teeth", g->nteeth);
 
987
      fprintf (stderr, " %3.0f rpm;", g->rpm);
 
988
 
 
989
      {
 
990
        char buf1[50], buf2[50], buf3[100];
 
991
        *buf1 = 0; *buf2 = 0; *buf3 = 0;
 
992
        if (debug_size_failures)
 
993
          sprintf (buf1, "%3d sz", debug_size_failures);
 
994
        if (debug_position_failures)
 
995
          sprintf (buf2, "%2d pos", debug_position_failures);
 
996
        if (*buf1 || *buf2)
 
997
          sprintf (buf3, " tries: %-7s%s", buf1, buf2);
 
998
        fprintf (stderr, "%-21s", buf3);
 
999
      }
 
1000
 
 
1001
      if (g->base_p) fprintf (stderr, " RESET %lu", current_length);
 
1002
      fprintf (stderr, "\n");
 
1003
    }
 
1004
 
 
1005
  if (g->base_p)
 
1006
    current_length = 1;
 
1007
  else
 
1008
    current_length++;
 
1009
 
 
1010
  if (g->motion_blur_p)
 
1011
    current_blur_length++;
 
1012
  else
 
1013
    current_blur_length = 0;
 
1014
}
 
1015
 
 
1016
 
 
1017
 
 
1018
/* Remove the given gear from the scene and free it.
 
1019
 */
 
1020
static void
 
1021
delete_gear (ModeInfo *mi, gear *g)
 
1022
{
 
1023
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
1024
  int i;
 
1025
 
 
1026
  for (i = 0; i < pp->ngears; i++)      /* find this gear in the list */
 
1027
    if (pp->gears[i] == g) break;
 
1028
  if (pp->gears[i] != g) abort();
 
1029
 
 
1030
  for (; i < pp->ngears-1; i++)         /* pull later entries forward */
 
1031
    pp->gears[i] = pp->gears[i+1];
 
1032
  pp->gears[i] = 0;
 
1033
  pp->ngears--;
 
1034
  free_gear (g);
 
1035
}
 
1036
 
 
1037
 
 
1038
/* Update the position of each gear in the scene.
 
1039
 */
 
1040
static void
 
1041
scroll_gears (ModeInfo *mi)
 
1042
{
 
1043
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
1044
  int i;
 
1045
 
 
1046
  for (i = 0; i < pp->ngears; i++)
 
1047
    pp->gears[i]->x -= (scroll_speed * 0.002);
 
1048
 
 
1049
  /* if the right edge of any gear is off screen to the left, delete it.
 
1050
   */
 
1051
  for (i = pp->ngears-1; i >= 0; i--)
 
1052
    {
 
1053
      gear *g = pp->gears[i];
 
1054
      if (g->x + g->r + g->tooth_h < pp->render_left)
 
1055
        delete_gear (mi, g);
 
1056
    }
 
1057
 
 
1058
  /* if the right edge of the last-added gear is left of the right edge
 
1059
     of the layout area, add another gear.
 
1060
   */
 
1061
  i = 0;
 
1062
  while (1)
 
1063
    {
 
1064
      gear *g = (pp->ngears <= 0 ? 0 : pp->gears[pp->ngears-1]);
 
1065
      if (!g || g->x + g->r + g->tooth_h < pp->layout_right)
 
1066
        push_gear (mi);
 
1067
      else
 
1068
        break;
 
1069
      i++;
 
1070
      if (debug_one_gear_p) break;
 
1071
    }
 
1072
 
 
1073
  /*
 
1074
  if (i > 1 && verbose_p)
 
1075
    fprintf (stderr, "%s: pushed %d gears\n", progname, i);
 
1076
   */
 
1077
}
 
1078
 
 
1079
 
 
1080
/* Update the rotation of each gear in the scene.
 
1081
 */
 
1082
static void
 
1083
spin_gears (ModeInfo *mi)
 
1084
{
 
1085
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
1086
  int i;
 
1087
 
 
1088
  for (i = 0; i < pp->ngears; i++)
 
1089
    {
 
1090
      gear *g = pp->gears[i];
 
1091
      double off = (g->ratio * spin_speed);
 
1092
 
 
1093
      if (g->th > 0)
 
1094
        g->th += off;
 
1095
      else
 
1096
        g->th -= off;
 
1097
    }
 
1098
}
 
1099
 
 
1100
 
 
1101
/* Run the animation fast (without displaying anything) until the first
 
1102
   gear is just about to come on screen.  This is to avoid a big delay
 
1103
   with a blank screen when -scroll is low.
 
1104
 */
 
1105
static void
 
1106
ffwd (ModeInfo *mi)
 
1107
{
 
1108
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
1109
  if (debug_one_gear_p) return;
 
1110
  while (1)
 
1111
    {
 
1112
      gear *g = farthest_gear (mi, True);
 
1113
      if (g && g->x - g->r - g->tooth_h/2 <= pp->vp_right * 0.88)
 
1114
        break;
 
1115
      scroll_gears (mi);
 
1116
    }
 
1117
}
 
1118
 
 
1119
 
 
1120
 
 
1121
/* Rendering the 3D objects into the scene.
 
1122
 */
 
1123
 
 
1124
 
 
1125
/* Draws an uncapped tube of the given radius extending from top to bottom,
 
1126
   with faces pointing either in or out.
 
1127
 */
 
1128
static int
 
1129
draw_ring (ModeInfo *mi, int segments,
 
1130
           GLfloat r, GLfloat top, GLfloat bottom, Bool in_p)
 
1131
{
 
1132
  int i;
 
1133
  int polys = 0;
 
1134
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1135
  GLfloat width = M_PI * 2 / segments;
 
1136
 
 
1137
  if (top != bottom)
 
1138
    {
 
1139
      glFrontFace (in_p ? GL_CCW : GL_CW);
 
1140
      glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
1141
      for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
 
1142
        {
 
1143
          GLfloat th = i * width;
 
1144
          GLfloat cth = cos(th);
 
1145
          GLfloat sth = sin(th);
 
1146
          if (in_p)
 
1147
            glNormal3f (-cth, -sth, 0);
 
1148
          else
 
1149
            glNormal3f (cth, sth, 0);
 
1150
          glVertex3f (cth * r, sth * r, top);
 
1151
          glVertex3f (cth * r, sth * r, bottom);
 
1152
        }
 
1153
      polys += segments;
 
1154
      glEnd();
 
1155
    }
 
1156
 
 
1157
  if (wire_p)
 
1158
    {
 
1159
      glBegin (GL_LINE_LOOP);
 
1160
      for (i = 0; i < segments; i++)
 
1161
        {
 
1162
          GLfloat th = i * width;
 
1163
          glVertex3f (cos(th) * r, sin(th) * r, top);
 
1164
        }
 
1165
      glEnd();
 
1166
      glBegin (GL_LINE_LOOP);
 
1167
      for (i = 0; i < segments; i++)
 
1168
        {
 
1169
          GLfloat th = i * width;
 
1170
          glVertex3f (cos(th) * r, sin(th) * r, bottom);
 
1171
        }
 
1172
      glEnd();
 
1173
    }
 
1174
 
 
1175
  return polys;
 
1176
}
 
1177
 
 
1178
 
 
1179
/* Draws a donut-shaped disc between the given radii,
 
1180
   with faces pointing either up or down.
 
1181
   The first radius may be 0, in which case, a filled disc is drawn.
 
1182
 */
 
1183
static int
 
1184
draw_disc (ModeInfo *mi, int segments,
 
1185
           GLfloat ra, GLfloat rb, GLfloat z, Bool up_p)
 
1186
{
 
1187
  int i;
 
1188
  int polys = 0;
 
1189
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1190
  GLfloat width = M_PI * 2 / segments;
 
1191
 
 
1192
  if (ra <  0) abort();
 
1193
  if (rb <= 0) abort();
 
1194
 
 
1195
  if (ra == 0)
 
1196
    glFrontFace (up_p ? GL_CW : GL_CCW);
 
1197
  else
 
1198
    glFrontFace (up_p ? GL_CCW : GL_CW);
 
1199
 
 
1200
  if (ra == 0)
 
1201
    glBegin (wire_p ? GL_LINES : GL_TRIANGLE_FAN);
 
1202
  else
 
1203
    glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
1204
 
 
1205
  glNormal3f (0, 0, (up_p ? -1 : 1));
 
1206
 
 
1207
  if (ra == 0 && !wire_p)
 
1208
    glVertex3f (0, 0, z);
 
1209
 
 
1210
  for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
 
1211
    {
 
1212
      GLfloat th = i * width;
 
1213
      GLfloat cth = cos(th);
 
1214
      GLfloat sth = sin(th);
 
1215
      if (wire_p || ra != 0)
 
1216
        glVertex3f (cth * ra, sth * ra, z);
 
1217
      glVertex3f (cth * rb, sth * rb, z);
 
1218
    }
 
1219
  polys += segments;
 
1220
  glEnd();
 
1221
  return polys;
 
1222
}
 
1223
 
 
1224
 
 
1225
/* Draws N thick radial lines between the given radii,
 
1226
   with faces pointing either up or down.
 
1227
 */
 
1228
static int
 
1229
draw_spokes (ModeInfo *mi, int n, GLfloat thickness, int segments,
 
1230
             GLfloat ra, GLfloat rb, GLfloat z1, GLfloat z2)
 
1231
{
 
1232
  int i;
 
1233
  int polys = 0;
 
1234
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1235
  GLfloat width;
 
1236
  int segments2 = 0;
 
1237
  int insegs, outsegs;
 
1238
  int tick;
 
1239
  int state;
 
1240
 
 
1241
  if (ra <= 0 || rb <= 0) abort();
 
1242
 
 
1243
  segments *= 3;
 
1244
 
 
1245
  while (segments2 < segments) /* need a multiple of N >= segments */
 
1246
    segments2 += n;            /* (yes, this is a moronic way to find that) */
 
1247
 
 
1248
  insegs  = ((float) (segments2 / n) + 0.5) / thickness;
 
1249
  outsegs = (segments2 / n) - insegs;
 
1250
  if (insegs  <= 0) insegs = 1;
 
1251
  if (outsegs <= 0) outsegs = 1;
 
1252
 
 
1253
  segments2 = (insegs + outsegs) * n;
 
1254
  width = M_PI * 2 / segments2;
 
1255
 
 
1256
  tick = 0;
 
1257
  state = 0;
 
1258
  for (i = 0; i < segments2; i++, tick++)
 
1259
    {
 
1260
      GLfloat th1 = i * width;
 
1261
      GLfloat th2 = th1 + width;
 
1262
      GLfloat cth1 = cos(th1);
 
1263
      GLfloat sth1 = sin(th1);
 
1264
      GLfloat cth2 = cos(th2);
 
1265
      GLfloat sth2 = sin(th2);
 
1266
      GLfloat orb = rb;
 
1267
 
 
1268
      int changed = (i == 0);
 
1269
 
 
1270
      if (state == 0 && tick == insegs)
 
1271
        tick = 0, state = 1, changed = 1;
 
1272
      else if (state == 1 && tick == outsegs)
 
1273
        tick = 0, state = 0, changed = 1;
 
1274
 
 
1275
      if ((state == 1 ||                /* in */
 
1276
           (state == 0 && changed)) &&
 
1277
          (!wire_p || wire_all_p))
 
1278
        {
 
1279
          /* top */
 
1280
          glFrontFace (GL_CCW);
 
1281
          glBegin (wire_p ? GL_LINES : GL_QUADS);
 
1282
          glNormal3f (0, 0, -1);
 
1283
          glVertex3f (cth1 * ra, sth1 * ra, z1);
 
1284
          glVertex3f (cth1 * rb, sth1 * rb, z1);
 
1285
          glVertex3f (cth2 * rb, sth2 * rb, z1);
 
1286
          glVertex3f (cth2 * ra, sth2 * ra, z1);
 
1287
          polys++;
 
1288
          glEnd();
 
1289
 
 
1290
          /* bottom */
 
1291
          glFrontFace (GL_CW);
 
1292
          glBegin (wire_p ? GL_LINES : GL_QUADS);
 
1293
          glNormal3f (0, 0, 1);
 
1294
          glVertex3f (cth1 * ra, sth1 * ra, z2);
 
1295
          glVertex3f (cth1 * rb, sth1 * rb, z2);
 
1296
          glVertex3f (cth2 * rb, sth2 * rb, z2);
 
1297
          glVertex3f (cth2 * ra, sth2 * ra, z2);
 
1298
          polys++;
 
1299
          glEnd();
 
1300
        }
 
1301
 
 
1302
      if (state == 1 && changed)   /* entering "in" state */
 
1303
        {
 
1304
          /* left */
 
1305
          glFrontFace (GL_CW);
 
1306
          glBegin (wire_p ? GL_LINES : GL_QUADS);
 
1307
          do_normal (cth1 * rb, sth1 * rb, z1,
 
1308
                     cth1 * ra, sth1 * ra, z1,
 
1309
                     cth1 * rb, sth1 * rb, z2);
 
1310
          glVertex3f (cth1 * ra, sth1 * ra, z1);
 
1311
          glVertex3f (cth1 * rb, sth1 * rb, z1);
 
1312
          glVertex3f (cth1 * rb, sth1 * rb, z2);
 
1313
          glVertex3f (cth1 * ra, sth1 * ra, z2);
 
1314
          polys++;
 
1315
          glEnd();
 
1316
        }
 
1317
 
 
1318
      if (state == 0 && changed)   /* entering "out" state */
 
1319
        {
 
1320
          /* right */
 
1321
          glFrontFace (GL_CCW);
 
1322
          glBegin (wire_p ? GL_LINES : GL_QUADS);
 
1323
          do_normal (cth2 * ra, sth2 * ra, z1,
 
1324
                     cth2 * rb, sth2 * rb, z1,
 
1325
                     cth2 * rb, sth2 * rb, z2);
 
1326
          glVertex3f (cth2 * ra, sth2 * ra, z1);
 
1327
          glVertex3f (cth2 * rb, sth2 * rb, z1);
 
1328
          glVertex3f (cth2 * rb, sth2 * rb, z2);
 
1329
          glVertex3f (cth2 * ra, sth2 * ra, z2);
 
1330
          polys++;
 
1331
          glEnd();
 
1332
        }
 
1333
 
 
1334
      rb = orb;
 
1335
    }
 
1336
  glEnd();
 
1337
  return polys;
 
1338
}
 
1339
 
 
1340
 
 
1341
/* Draws some bumps (embedded cylinders) on the gear.
 
1342
 */
 
1343
static int
 
1344
draw_gear_nubs (ModeInfo *mi, gear *g)
 
1345
{
 
1346
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1347
  int polys = 0;
 
1348
  int i;
 
1349
  int steps = (g->size != LARGE ? 5 : 20);
 
1350
  double r, size, height;
 
1351
  GLfloat *cc;
 
1352
  int which;
 
1353
  GLfloat width, off;
 
1354
 
 
1355
  if (! g->nubs) return 0;
 
1356
 
 
1357
  which = biggest_ring (g, &r, &size, &height);
 
1358
  size /= 5;
 
1359
  height *= 0.7;
 
1360
 
 
1361
  cc = (which == 1 ? g->color : g->color2);
 
1362
  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cc);
 
1363
 
 
1364
  width = M_PI * 2 / g->nubs;
 
1365
  off = M_PI / (g->nteeth * 2);  /* align first nub with a tooth */
 
1366
 
 
1367
  for (i = 0; i < g->nubs; i++)
 
1368
    {
 
1369
      GLfloat th = (i * width) + off;
 
1370
      glPushMatrix();
 
1371
      glTranslatef (cos(th) * r, sin(th) * r, 0);
 
1372
 
 
1373
      if (wire_p && !wire_all_p)
 
1374
        polys += draw_ring (mi, (g->size == LARGE ? steps/2 : steps),
 
1375
                            size, 0, 0, False);
 
1376
      else
 
1377
        {
 
1378
          polys += draw_disc (mi, steps, 0, size, -height, True);
 
1379
          polys += draw_disc (mi, steps, 0, size,  height, False);
 
1380
          polys += draw_ring (mi, steps, size, -height, height, False);
 
1381
        }
 
1382
      glPopMatrix();
 
1383
    }
 
1384
  return polys;
 
1385
}
 
1386
 
 
1387
 
 
1388
 
 
1389
/* Draws a much simpler representation of a gear.
 
1390
 */
 
1391
static int
 
1392
draw_gear_schematic (ModeInfo *mi, gear *g)
 
1393
{
 
1394
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1395
  int polys = 0;
 
1396
  int i;
 
1397
  GLfloat width = M_PI * 2 / g->nteeth;
 
1398
 
 
1399
  if (!wire_p) glDisable(GL_LIGHTING);
 
1400
  glColor3f (g->color[0] * 0.6, g->color[1] * 0.6, g->color[2] * 0.6);
 
1401
 
 
1402
  glBegin (GL_LINES);
 
1403
  for (i = 0; i < g->nteeth; i++)
 
1404
    {
 
1405
      GLfloat th = (i * width) + (width/4);
 
1406
      glVertex3f (0, 0, -g->thickness/2);
 
1407
      glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
 
1408
    }
 
1409
  polys += g->nteeth;
 
1410
  glEnd();
 
1411
 
 
1412
  glBegin (GL_LINE_LOOP);
 
1413
  for (i = 0; i < g->nteeth; i++)
 
1414
    {
 
1415
      GLfloat th = (i * width) + (width/4);
 
1416
      glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
 
1417
    }
 
1418
  polys += g->nteeth;
 
1419
  glEnd();
 
1420
 
 
1421
  if (!wire_p) glEnable(GL_LIGHTING);
 
1422
  return polys;
 
1423
}
 
1424
 
 
1425
 
 
1426
/* Renders all the interior (non-toothy) parts of a gear:
 
1427
   the discs, axles, etc.
 
1428
 */
 
1429
static int
 
1430
draw_gear_interior (ModeInfo *mi, gear *g)
 
1431
{
 
1432
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1433
  int polys = 0;
 
1434
 
 
1435
  int steps = g->nteeth * 2;
 
1436
  if (steps < 10) steps = 10;
 
1437
  if ((wire_p && !wire_all_p) || g->size != LARGE) steps /= 2;
 
1438
  if (g->size != LARGE && steps > 16) steps = 16;
 
1439
 
 
1440
  /* ring 1 (facing in) is done in draw_gear_teeth */
 
1441
 
 
1442
  /* ring 2 (facing in) and disc 2
 
1443
   */
 
1444
  if (g->inner_r2)
 
1445
    {
 
1446
      GLfloat ra = g->inner_r * 1.04;  /* slightly larger than inner_r */
 
1447
      GLfloat rb = g->inner_r2;        /*  since the points don't line up */
 
1448
      GLfloat za = -g->thickness2/2;
 
1449
      GLfloat zb =  g->thickness2/2;
 
1450
 
 
1451
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2);
 
1452
 
 
1453
      if ((g->coax_p != 1 && !g->inner_r3) ||
 
1454
          (wire_p && wire_all_p))
 
1455
        polys += draw_ring (mi, steps, rb, za, zb, True);  /* ring facing in */
 
1456
 
 
1457
      if (wire_p && wire_all_p)
 
1458
        polys += draw_ring (mi, steps, ra, za, zb, True);  /* ring facing in */
 
1459
 
 
1460
      if (g->spokes)
 
1461
        polys += draw_spokes (mi, g->spokes, g->spoke_thickness,
 
1462
                              steps, ra, rb, za, zb);
 
1463
      else if (!wire_p || wire_all_p)
 
1464
        {
 
1465
          polys += draw_disc (mi, steps, ra, rb, za, True);  /* top plate */
 
1466
          polys += draw_disc (mi, steps, ra, rb, zb, False); /* bottom plate */
 
1467
        }
 
1468
    }
 
1469
 
 
1470
  /* ring 3 (facing in and out) and disc 3
 
1471
   */
 
1472
  if (g->inner_r3)
 
1473
    {
 
1474
      GLfloat ra = g->inner_r2;
 
1475
      GLfloat rb = g->inner_r3;
 
1476
      GLfloat za = -g->thickness3/2;
 
1477
      GLfloat zb =  g->thickness3/2;
 
1478
 
 
1479
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
 
1480
 
 
1481
      polys += draw_ring (mi, steps, ra, za, zb, False);  /* ring facing out */
 
1482
 
 
1483
      if (g->coax_p != 1 || (wire_p && wire_all_p))
 
1484
        polys += draw_ring (mi, steps, rb, za, zb, True);  /* ring facing in */
 
1485
 
 
1486
      if (!wire_p || wire_all_p)
 
1487
        {
 
1488
          polys += draw_disc (mi, steps, ra, rb, za, True);  /* top plate */
 
1489
          polys += draw_disc (mi, steps, ra, rb, zb, False); /* bottom plate */
 
1490
        }
 
1491
    }
 
1492
 
 
1493
  /* axle tube
 
1494
   */
 
1495
  if (g->coax_p == 1)
 
1496
    {
 
1497
      GLfloat cap_height = g->coax_thickness/3;
 
1498
 
 
1499
      GLfloat ra = (g->inner_r3 ? g->inner_r3 :
 
1500
                    g->inner_r2 ? g->inner_r2 :
 
1501
                    g->inner_r);
 
1502
      GLfloat za = -(g->thickness/2 + cap_height);
 
1503
      GLfloat zb = g->coax_thickness/2 + plane_displacement + cap_height;
 
1504
 
 
1505
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
 
1506
 
 
1507
      if (wire_p && !wire_all_p) steps /= 2;
 
1508
 
 
1509
      polys += draw_ring (mi, steps, ra, za, zb, False);  /* ring facing out */
 
1510
 
 
1511
      if (!wire_p || wire_all_p)
 
1512
        {
 
1513
          polys += draw_disc (mi, steps, 0,  ra, za, True);  /* top plate */
 
1514
          polys += draw_disc (mi, steps, 0,  ra, zb, False); /* bottom plate */
 
1515
        }
 
1516
    }
 
1517
  return polys;
 
1518
}
 
1519
 
 
1520
 
 
1521
/* gear_teeth_geometry computes the vertices and normals of the teeth
 
1522
   of a gear.  This is the heavy lifting: there are a ton of polygons
 
1523
   around the perimiter of a gear, and the normals are difficult (not
 
1524
   radial or right angles.)
 
1525
 
 
1526
   It would be nice if we could cache this, but the numbers are
 
1527
   different for essentially every gear:
 
1528
 
 
1529
      - Every gear has a different inner_r, so the vertices of the
 
1530
        inner ring (and thus, the triangle fans on the top and bottom
 
1531
        faces) are different in a non-scalable way.
 
1532
 
 
1533
      - If the ratio between tooth_w and tooth_h changes, the normals
 
1534
        on the outside edges of the teeth are invalid (this can happen
 
1535
        every time we start a new train.)
 
1536
 
 
1537
   So instead, we rely on OpenGL display lists to do the cacheing for
 
1538
   us -- we only compute all these normals once per gear, instead of
 
1539
   once per gear per frame.
 
1540
 */
 
1541
 
 
1542
typedef struct {
 
1543
  int npoints;
 
1544
  XYZ *points;
 
1545
  XYZ *fnormals;  /* face normals */
 
1546
  XYZ *pnormals;  /* point normals */
 
1547
} tooth_face;
 
1548
 
 
1549
 
 
1550
static void
 
1551
tooth_normals (tooth_face *f)
 
1552
{
 
1553
  int i;
 
1554
 
 
1555
  /* Compute the face normals for each facet. */
 
1556
  for (i = 0; i < f->npoints; i++)
 
1557
    {
 
1558
      XYZ p1, p2, p3;
 
1559
      int a = i;
 
1560
      int b = (i == f->npoints-1 ? 0 : i+1);
 
1561
      p1 = f->points[a];
 
1562
      p2 = f->points[b];
 
1563
      p3 = p1;
 
1564
      p3.z++;
 
1565
      f->fnormals[i] = calc_normal (p1, p2, p3);
 
1566
    }
 
1567
 
 
1568
  /* From the face normals, compute the vertex normals
 
1569
     (by averaging the normals of adjascent faces.)
 
1570
   */
 
1571
  for (i = 0; i < f->npoints; i++)
 
1572
    {
 
1573
      int a = (i == 0 ? f->npoints-1 : i-1);
 
1574
      int b = i;
 
1575
      XYZ n1 = f->fnormals[a];   /* normal of [i-1 - i] face */
 
1576
      XYZ n2 = f->fnormals[b];   /* normal of [i - i+1] face */
 
1577
      f->pnormals[i].x = (n1.x + n2.x) / 2;
 
1578
      f->pnormals[i].y = (n1.y + n2.y) / 2;
 
1579
      f->pnormals[i].z = (n1.z + n2.z) / 2;
 
1580
    }
 
1581
}
 
1582
 
 
1583
 
 
1584
static void
 
1585
gear_teeth_geometry (ModeInfo *mi, gear *g,
 
1586
                     tooth_face *orim,      /* outer rim (the teeth) */
 
1587
                     tooth_face *irim)      /* inner rim (the hole)  */
 
1588
{
 
1589
  int i;
 
1590
  int ppt = 9;   /* max points per tooth */
 
1591
  GLfloat width = M_PI * 2 / g->nteeth;
 
1592
  GLfloat rh = g->tooth_h;
 
1593
  GLfloat tw = width;
 
1594
 
 
1595
  /* Approximate shape of an "involute" gear tooth.
 
1596
 
 
1597
                                 (TH)
 
1598
                 th0 th1 th2 th3 th4 th5 th6 th7 th8   th9    th10
 
1599
                   :  :  :   :    :    :   :  :  :      :      :
 
1600
                   :  :  :   :    :    :   :  :  :      :      :
 
1601
        r0 ........:..:..:...___________...:..:..:......:......:..
 
1602
                   :  :  :  /:    :    :\  :  :  :      :      :
 
1603
                   :  :  : / :    :    : \ :  :  :      :      :
 
1604
                   :  :  :/  :    :    :  \:  :  :      :      :
 
1605
        r1 ........:.....@...:....:....:...@..:..:......:......:..
 
1606
                   :  : @:   :    :    :   :@ :  :      :      :
 
1607
    (R) ...........:...@.:...:....:....:...:.@..........:......:......
 
1608
                   :  :@ :   :    :    :   : @:  :      :      :
 
1609
        r2 ........:..@..:...:....:....:...:..@:........:......:..
 
1610
                   : /:  :   :    :    :   :  :\ :      :      :
 
1611
                   :/ :  :   :    :    :   :  : \:      :      : /
 
1612
        r3 ......__/..:..:...:....:....:...:..:..\______________/
 
1613
                   :  :  :   :    :    :   :  :  :      :      :
 
1614
                   |  :  :   :    :    :   :  :  |      :      :
 
1615
                   :  :  :   :    :    :   :  :  :      :      :
 
1616
                   |  :  :   :    :    :   :  :  |      :      :
 
1617
        r4 ......__:_____________________________:________________
 
1618
   */
 
1619
 
 
1620
  GLfloat r[20];
 
1621
  GLfloat th[20];
 
1622
  GLfloat R = g->r;
 
1623
 
 
1624
  r[0] = R + (rh * 0.5);
 
1625
  r[1] = R + (rh * 0.25);
 
1626
  r[2] = R - (r[1]-R);
 
1627
  r[3] = R - (r[0]-R);
 
1628
  r[4] = g->inner_r;
 
1629
 
 
1630
  th[0] = -tw * (g->size == SMALL ? 0.5 : g->size == MEDIUM ? 0.41 : 0.45);
 
1631
  th[1] = -tw * 0.30;
 
1632
  th[2] = -tw * (g->nteeth >= 5 ? 0.16 : 0.12);
 
1633
  th[3] = -tw * (g->size == MEDIUM ? 0.1 : 0.04);
 
1634
  th[4] =  0;
 
1635
  th[5] =  -th[3];
 
1636
  th[6] =  -th[2];
 
1637
  th[7] =  -th[1];
 
1638
  th[8] =  -th[0];
 
1639
  th[9] =  width / 2;
 
1640
  th[10]=  th[0] + width;
 
1641
 
 
1642
  orim->npoints  = 0;
 
1643
  orim->points   = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->points));
 
1644
  orim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->fnormals));
 
1645
  orim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->pnormals));
 
1646
 
 
1647
  irim->npoints  = 0;
 
1648
  irim->points   = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->points));
 
1649
  irim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->fnormals));
 
1650
  irim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->pnormals));
 
1651
 
 
1652
  if (!orim->points || !orim->pnormals || !orim->fnormals ||
 
1653
      !irim->points || !irim->pnormals || !irim->fnormals)
 
1654
    {
 
1655
      fprintf (stderr, "%s: out of memory\n", progname);
 
1656
      exit (1);
 
1657
    }
 
1658
 
 
1659
  /* First, compute the coordinates of every point used for every tooth.
 
1660
   */
 
1661
  for (i = 0; i < g->nteeth; i++)
 
1662
    {
 
1663
      GLfloat TH = (i * width) + (width/4);
 
1664
 
 
1665
#     undef PUSH
 
1666
#     define PUSH(OPR,IPR,PTH) \
 
1667
        orim->points[orim->npoints].x = cos(TH+th[(PTH)]) * r[(OPR)]; \
 
1668
        orim->points[orim->npoints].y = sin(TH+th[(PTH)]) * r[(OPR)]; \
 
1669
        orim->npoints++; \
 
1670
        irim->points[irim->npoints].x = cos(TH+th[(PTH)]) * r[(IPR)]; \
 
1671
        irim->points[irim->npoints].y = sin(TH+th[(PTH)]) * r[(IPR)]; \
 
1672
        irim->npoints++
 
1673
 
 
1674
      if (g->size == SMALL)
 
1675
        {
 
1676
          PUSH(3, 4, 0);       /* tooth left 1 */
 
1677
          PUSH(0, 4, 4);       /* tooth top middle */
 
1678
        }
 
1679
      else if (g->size == MEDIUM)
 
1680
        {
 
1681
          PUSH(3, 4, 0);       /* tooth left 1 */
 
1682
          PUSH(0, 4, 3);       /* tooth top left */
 
1683
          PUSH(0, 4, 5);       /* tooth top right */
 
1684
          PUSH(3, 4, 8);       /* tooth right 3 */
 
1685
        }
 
1686
      else if (g->size == LARGE)
 
1687
        {
 
1688
          PUSH(3, 4, 0);       /* tooth left 1 */
 
1689
          PUSH(2, 4, 1);       /* tooth left 2 */
 
1690
          PUSH(1, 4, 2);       /* tooth left 3 */
 
1691
          PUSH(0, 4, 3);       /* tooth top left */
 
1692
          PUSH(0, 4, 5);       /* tooth top right */
 
1693
          PUSH(1, 4, 6);       /* tooth right 1 */
 
1694
          PUSH(2, 4, 7);       /* tooth right 2 */
 
1695
          PUSH(3, 4, 8);       /* tooth right 3 */
 
1696
          PUSH(3, 4, 9);       /* gap top */
 
1697
        }
 
1698
      else
 
1699
        abort();
 
1700
#     undef PUSH
 
1701
 
 
1702
      if (i == 0 && orim->npoints > ppt) abort();  /* go update "ppt"! */
 
1703
    }
 
1704
 
 
1705
  tooth_normals (orim);
 
1706
  tooth_normals (irim);
 
1707
}
 
1708
 
 
1709
 
 
1710
/* Renders all teeth of a gear.
 
1711
 */
 
1712
static int
 
1713
draw_gear_teeth (ModeInfo *mi, gear *g)
 
1714
{
 
1715
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1716
  Bool show_normals_p = False;
 
1717
  int polys = 0;
 
1718
  int i;
 
1719
 
 
1720
  GLfloat z1 = -g->thickness/2;
 
1721
  GLfloat z2 =  g->thickness/2;
 
1722
 
 
1723
  tooth_face orim, irim;
 
1724
  gear_teeth_geometry (mi, g, &orim, &irim);
 
1725
 
 
1726
  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
 
1727
 
 
1728
  /* Draw the outer rim (the teeth)
 
1729
     (In wire mode, this draws just the upright lines.)
 
1730
   */
 
1731
  glFrontFace (GL_CW);
 
1732
  glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
1733
  for (i = 0; i < orim.npoints; i++)
 
1734
    {
 
1735
      glNormal3f (orim.pnormals[i].x, orim.pnormals[i].y, orim.pnormals[i].z);
 
1736
      glVertex3f (orim.points[i].x, orim.points[i].y, z1);
 
1737
      glVertex3f (orim.points[i].x, orim.points[i].y, z2);
 
1738
 
 
1739
      /* Show the face normal vectors */
 
1740
      if (wire_p && show_normals_p)
 
1741
        {
 
1742
          XYZ n = orim.fnormals[i];
 
1743
          int a = i;
 
1744
          int b = (i == orim.npoints-1 ? 0 : i+1);
 
1745
          GLfloat x = (orim.points[a].x + orim.points[b].x) / 2;
 
1746
          GLfloat y = (orim.points[a].y + orim.points[b].y) / 2;
 
1747
          GLfloat z  = (z1 + z2) / 2;
 
1748
          glVertex3f (x, y, z);
 
1749
          glVertex3f (x + n.x, y + n.y, z);
 
1750
        }
 
1751
 
 
1752
      /* Show the vertex normal vectors */
 
1753
      if (wire_p && show_normals_p)
 
1754
        {
 
1755
          XYZ n = orim.pnormals[i];
 
1756
          GLfloat x = orim.points[i].x;
 
1757
          GLfloat y = orim.points[i].y;
 
1758
          GLfloat z  = (z1 + z2) / 2;
 
1759
          glVertex3f (x, y, z);
 
1760
          glVertex3f (x + n.x, y + n.y, z);
 
1761
        }
 
1762
    }
 
1763
 
 
1764
  if (!wire_p)  /* close the quad loop */
 
1765
    {
 
1766
      glNormal3f (orim.pnormals[0].x, orim.pnormals[0].y, orim.pnormals[0].z);
 
1767
      glVertex3f (orim.points[0].x, orim.points[0].y, z1);
 
1768
      glVertex3f (orim.points[0].x, orim.points[0].y, z2);
 
1769
    }
 
1770
  polys += orim.npoints;
 
1771
  glEnd();
 
1772
 
 
1773
  /* Draw the outer rim circles, in wire mode */
 
1774
  if (wire_p)
 
1775
    {
 
1776
      glBegin (GL_LINE_LOOP);
 
1777
      for (i = 0; i < orim.npoints; i++)
 
1778
        glVertex3f (orim.points[i].x, orim.points[i].y, z1);
 
1779
      glEnd();
 
1780
      glBegin (GL_LINE_LOOP);
 
1781
      for (i = 0; i < orim.npoints; i++)
 
1782
        glVertex3f (orim.points[i].x, orim.points[i].y, z2);
 
1783
      glEnd();
 
1784
    }
 
1785
 
 
1786
 
 
1787
  /* Draw the inner rim (the hole)
 
1788
     (In wire mode, this draws just the upright lines.)
 
1789
   */
 
1790
  glFrontFace (GL_CCW);
 
1791
  glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
1792
  for (i = 0; i < irim.npoints; i++)
 
1793
    {
 
1794
      glNormal3f(-irim.pnormals[i].x, -irim.pnormals[i].y,-irim.pnormals[i].z);
 
1795
      glVertex3f (irim.points[i].x, irim.points[i].y, z1);
 
1796
      glVertex3f (irim.points[i].x, irim.points[i].y, z2);
 
1797
 
 
1798
      /* Show the face normal vectors */
 
1799
      if (wire_p && show_normals_p)
 
1800
        {
 
1801
          XYZ n = irim.fnormals[i];
 
1802
          int a = i;
 
1803
          int b = (i == irim.npoints-1 ? 0 : i+1);
 
1804
          GLfloat x = (irim.points[a].x + irim.points[b].x) / 2;
 
1805
          GLfloat y = (irim.points[a].y + irim.points[b].y) / 2;
 
1806
          GLfloat z  = (z1 + z2) / 2;
 
1807
          glVertex3f (x, y, z);
 
1808
          glVertex3f (x - n.x, y - n.y, z);
 
1809
        }
 
1810
 
 
1811
      /* Show the vertex normal vectors */
 
1812
      if (wire_p && show_normals_p)
 
1813
        {
 
1814
          XYZ n = irim.pnormals[i];
 
1815
          GLfloat x = irim.points[i].x;
 
1816
          GLfloat y = irim.points[i].y;
 
1817
          GLfloat z  = (z1 + z2) / 2;
 
1818
          glVertex3f (x, y, z);
 
1819
          glVertex3f (x - n.x, y - n.y, z);
 
1820
        }
 
1821
    }
 
1822
 
 
1823
  if (!wire_p)  /* close the quad loop */
 
1824
    {
 
1825
      glNormal3f (-irim.pnormals[0].x,-irim.pnormals[0].y,-irim.pnormals[0].z);
 
1826
      glVertex3f (irim.points[0].x, irim.points[0].y, z1);
 
1827
      glVertex3f (irim.points[0].x, irim.points[0].y, z2);
 
1828
    }
 
1829
  polys += irim.npoints;
 
1830
  glEnd();
 
1831
 
 
1832
  /* Draw the inner rim circles, in wire mode
 
1833
   */
 
1834
  if (wire_p)
 
1835
    {
 
1836
      glBegin (GL_LINE_LOOP);
 
1837
      for (i = 0; i < irim.npoints; i++)
 
1838
        glVertex3f (irim.points[i].x, irim.points[i].y, z1);
 
1839
      glEnd();
 
1840
      glBegin (GL_LINE_LOOP);
 
1841
      for (i = 0; i < irim.npoints; i++)
 
1842
        glVertex3f (irim.points[i].x, irim.points[i].y, z2);
 
1843
      glEnd();
 
1844
    }
 
1845
 
 
1846
 
 
1847
  /* Draw the side (the flat bit)
 
1848
   */
 
1849
  if (!wire_p || wire_all_p)
 
1850
    {
 
1851
      GLfloat z;
 
1852
      if (irim.npoints != orim.npoints) abort();
 
1853
      for (z = z1; z <= z2; z += z2-z1)
 
1854
        {
 
1855
          glFrontFace (z == z1 ? GL_CCW : GL_CW);
 
1856
          glNormal3f (0, 0, z);
 
1857
          glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
1858
          for (i = 0; i < orim.npoints; i++)
 
1859
            {
 
1860
              glVertex3f (orim.points[i].x, orim.points[i].y, z);
 
1861
              glVertex3f (irim.points[i].x, irim.points[i].y, z);
 
1862
            }
 
1863
          if (!wire_p)  /* close the quad loop */
 
1864
            {
 
1865
              glVertex3f (orim.points[0].x, orim.points[0].y, z);
 
1866
              glVertex3f (irim.points[0].x, irim.points[0].y, z);
 
1867
            }
 
1868
          polys += orim.npoints;
 
1869
          glEnd();
 
1870
        }
 
1871
    }
 
1872
 
 
1873
  free (irim.points);
 
1874
  free (irim.fnormals);
 
1875
  free (irim.pnormals);
 
1876
 
 
1877
  free (orim.points);
 
1878
  free (orim.fnormals);
 
1879
  free (orim.pnormals);
 
1880
 
 
1881
  return polys;
 
1882
}
 
1883
 
 
1884
 
 
1885
/* Render one gear, unrotated at 0,0.
 
1886
 */
 
1887
static int
 
1888
draw_gear_1 (ModeInfo *mi, gear *g)
 
1889
{
 
1890
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1891
  int polys = 0;
 
1892
 
 
1893
  static GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
 
1894
  static GLfloat shiny   = 128.0;
 
1895
 
 
1896
  glMaterialfv (GL_FRONT, GL_SPECULAR,  spec);
 
1897
  glMateriali  (GL_FRONT, GL_SHININESS, shiny);
 
1898
  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
 
1899
  glColor3f (g->color[0], g->color[1], g->color[2]);
 
1900
 
 
1901
  if (debug_p && wire_p)
 
1902
    polys += draw_gear_schematic (mi, g);
 
1903
  else
 
1904
    {
 
1905
      glPushMatrix();
 
1906
      glRotatef (g->wobble, 1, 0, 0);
 
1907
      polys += draw_gear_teeth (mi, g);
 
1908
      polys += draw_gear_interior (mi, g);
 
1909
      polys += draw_gear_nubs (mi, g);
 
1910
      glPopMatrix();
 
1911
    }
 
1912
  return polys;
 
1913
}
 
1914
 
 
1915
 
 
1916
/* Render one gear in the proper position, creating the gear's
 
1917
   display list first if necessary.
 
1918
 */
 
1919
static void
 
1920
draw_gear (ModeInfo *mi, int which)
 
1921
{
 
1922
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
1923
  gear *g = pp->gears[which];
 
1924
  GLfloat th;
 
1925
 
 
1926
  Bool visible_p = (g->x + g->r + g->tooth_h >= pp->render_left &&
 
1927
                    g->x - g->r - g->tooth_h <= pp->render_right);
 
1928
 
 
1929
  if (!visible_p && !debug_p)
 
1930
    return;
 
1931
 
 
1932
  if (! g->dlist)
 
1933
    {
 
1934
      g->dlist = glGenLists (1);
 
1935
      if (! g->dlist)
 
1936
        {
 
1937
          /* I don't know how many display lists a GL implementation
 
1938
             is supposed to provide, but hopefully it's more than
 
1939
             "a few hundred", or we'll be in trouble...
 
1940
           */
 
1941
          check_gl_error ("glGenLists");
 
1942
          abort();
 
1943
        }
 
1944
 
 
1945
      glNewList (g->dlist, GL_COMPILE);
 
1946
      g->polygons = draw_gear_1 (mi, g);
 
1947
      glEndList ();
 
1948
    }
 
1949
 
 
1950
  glPushMatrix();
 
1951
 
 
1952
  glTranslatef (g->x, g->y, g->z);
 
1953
 
 
1954
  if (g->motion_blur_p && !pp->button_down_p)
 
1955
    {
 
1956
      /* If we're in motion-blur mode, then draw the gear so that each
 
1957
         frame rotates it by exactly one half tooth-width, so that it
 
1958
         looks flickery around the edges.  But, revert to the normal
 
1959
         way when the mouse button is down lest the user see overlapping
 
1960
         polygons.
 
1961
       */
 
1962
      th = g->motion_blur_p * 180.0 / g->nteeth * (g->th > 0 ? 1 : -1);
 
1963
      g->motion_blur_p++;
 
1964
    }
 
1965
  else
 
1966
    th = g->th;
 
1967
 
 
1968
  glRotatef (th, 0, 0, 1);
 
1969
 
 
1970
  glPushName (g->id);
 
1971
 
 
1972
  if (! visible_p)
 
1973
    mi->polygon_count += draw_gear_schematic (mi, g);
 
1974
  else
 
1975
    {
 
1976
      glCallList (g->dlist);
 
1977
      mi->polygon_count += g->polygons;
 
1978
    }
 
1979
 
 
1980
  glPopName();
 
1981
  glPopMatrix();
 
1982
}
 
1983
 
 
1984
 
 
1985
/* Render all gears.
 
1986
 */
 
1987
static void
 
1988
draw_gears (ModeInfo *mi)
 
1989
{
 
1990
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
1991
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
1992
  int i;
 
1993
 
 
1994
  glColor4f (1, 1, 0.8, 1);
 
1995
 
 
1996
  glInitNames();
 
1997
 
 
1998
  for (i = 0; i < pp->ngears; i++)
 
1999
    draw_gear (mi, i);
 
2000
 
 
2001
  /* draw a line connecting gears that are, uh, geared. */
 
2002
  if (debug_p)
 
2003
    {
 
2004
      static GLfloat color[4] = {1.0, 0.0, 0.0, 1.0};
 
2005
      GLfloat off = 0.1;
 
2006
      GLfloat ox=0, oy=0, oz=0;
 
2007
 
 
2008
      if (!wire_p) glDisable(GL_LIGHTING);
 
2009
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
 
2010
      glColor3f (color[0], color[1], color[2]);
 
2011
 
 
2012
      for (i = 0; i < pp->ngears; i++)
 
2013
        {
 
2014
          gear *g = pp->gears[i];
 
2015
          glBegin(GL_LINE_STRIP);
 
2016
          glVertex3f (g->x, g->y, g->z - off);
 
2017
          glVertex3f (g->x, g->y, g->z + off);
 
2018
          if (i > 0 && !g->base_p)
 
2019
            glVertex3f (ox, oy, oz + off);
 
2020
          glEnd();
 
2021
          ox = g->x;
 
2022
          oy = g->y;
 
2023
          oz = g->z;
 
2024
        }
 
2025
      if (!wire_p) glEnable(GL_LIGHTING);
 
2026
    }
 
2027
}
 
2028
 
 
2029
 
 
2030
/* Mouse hit detection
 
2031
 */
 
2032
void
 
2033
find_mouse_gear (ModeInfo *mi)
 
2034
{
 
2035
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
2036
  int screen_width = MI_WIDTH (mi);
 
2037
  int screen_height = MI_HEIGHT (mi);
 
2038
  GLfloat h = (GLfloat) screen_height / (GLfloat) screen_width;
 
2039
  int x, y;
 
2040
  int hits;
 
2041
 
 
2042
  pp->mouse_gear_id = 0;
 
2043
 
 
2044
  /* Poll mouse position */
 
2045
  {
 
2046
    Window r, c;
 
2047
    int rx, ry;
 
2048
    unsigned int m;
 
2049
    XQueryPointer (MI_DISPLAY (mi), MI_WINDOW (mi),
 
2050
                   &r, &c, &rx, &ry, &x, &y, &m);
 
2051
  }
 
2052
 
 
2053
  if (x < 0 || y < 0 || x > screen_width || y > screen_height)
 
2054
    return;  /* out of window */
 
2055
 
 
2056
  /* Run OpenGL hit detector */
 
2057
  {
 
2058
    GLint vp[4];
 
2059
    GLuint selbuf[512];
 
2060
 
 
2061
    glSelectBuffer (sizeof(selbuf), selbuf);  /* set up "select" mode */
 
2062
    glRenderMode (GL_SELECT);
 
2063
    glMatrixMode (GL_PROJECTION);
 
2064
    glPushMatrix();
 
2065
    glLoadIdentity();
 
2066
    glGetIntegerv (GL_VIEWPORT, vp);         /* save old vp */
 
2067
    gluPickMatrix (x, vp[3]-y, 5, 5, vp);
 
2068
    gluPerspective (30.0, 1/h, 1.0, 100.0);  /* must match reshape_pinion() */
 
2069
    glMatrixMode (GL_MODELVIEW);
 
2070
 
 
2071
    draw_gears (mi);                         /* render into "select" buffer */
 
2072
 
 
2073
    glMatrixMode (GL_PROJECTION);            /* restore old vp */
 
2074
    glPopMatrix ();
 
2075
    glMatrixMode (GL_MODELVIEW);
 
2076
    glFlush();
 
2077
    hits = glRenderMode (GL_RENDER);         /* done selecting */
 
2078
 
 
2079
    if (hits > 0)
 
2080
      {
 
2081
        int i;
 
2082
        GLuint name_count = 0;
 
2083
        GLuint *p = (GLuint *) selbuf;
 
2084
        GLuint *pnames = 0;
 
2085
        GLuint min_z = ~0;
 
2086
 
 
2087
        for (i = 0; i < hits; i++)
 
2088
          {     
 
2089
            int names = *p++;
 
2090
            if (*p < min_z)                  /* find match closest to screen */
 
2091
              {
 
2092
                name_count = names;
 
2093
                min_z = *p;
 
2094
                pnames = p+2;
 
2095
              }
 
2096
            p += names+2;
 
2097
          }
 
2098
 
 
2099
        if (name_count > 0)                  /* take first hit */
 
2100
          pp->mouse_gear_id = pnames[0];
 
2101
      }
 
2102
  }
 
2103
}
 
2104
 
 
2105
 
 
2106
/* Window management, etc
 
2107
 */
 
2108
void
 
2109
reshape_pinion (ModeInfo *mi, int width, int height)
 
2110
{
 
2111
  GLfloat h = (GLfloat) height / (GLfloat) width;
 
2112
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
2113
 
 
2114
  glViewport (0, 0, (GLint) width, (GLint) height);
 
2115
 
 
2116
  glMatrixMode(GL_PROJECTION);
 
2117
  glLoadIdentity();
 
2118
  gluPerspective (30.0, 1/h, 1.0, 100.0);
 
2119
 
 
2120
  glMatrixMode(GL_MODELVIEW);
 
2121
  glLoadIdentity();
 
2122
  gluLookAt( 0.0, 0.0, 30.0,
 
2123
             0.0, 0.0, 0.0,
 
2124
             0.0, 1.0, 0.0);
 
2125
 
 
2126
  glClear(GL_COLOR_BUFFER_BIT);
 
2127
 
 
2128
  {
 
2129
    GLfloat render_width, layout_width;
 
2130
    pp->vp_height = 1.0;
 
2131
    pp->vp_width  = 1/h;
 
2132
 
 
2133
    pp->vp_left   = -pp->vp_width/2;
 
2134
    pp->vp_right  =  pp->vp_width/2;
 
2135
    pp->vp_top    =  pp->vp_height/2;
 
2136
    pp->vp_bottom = -pp->vp_height/2;
 
2137
 
 
2138
    render_width = pp->vp_width * 2;
 
2139
    layout_width = pp->vp_width * 0.8 * gear_size;
 
2140
 
 
2141
    pp->render_left  = -render_width/2;
 
2142
    pp->render_right =  render_width/2;
 
2143
 
 
2144
    pp->layout_left  = pp->render_right;
 
2145
    pp->layout_right = pp->layout_left + layout_width;
 
2146
  }
 
2147
}
 
2148
 
 
2149
 
 
2150
Bool
 
2151
pinion_handle_event (ModeInfo *mi, XEvent *event)
 
2152
{
 
2153
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
2154
 
 
2155
  if (event->xany.type == ButtonPress &&
 
2156
      event->xbutton.button == Button1)
 
2157
    {
 
2158
      pp->button_down_p = True;
 
2159
      gltrackball_start (pp->trackball,
 
2160
                         event->xbutton.x, event->xbutton.y,
 
2161
                         MI_WIDTH (mi), MI_HEIGHT (mi));
 
2162
      return True;
 
2163
    }
 
2164
  else if (event->xany.type == ButtonRelease &&
 
2165
           event->xbutton.button == Button1)
 
2166
    {
 
2167
      pp->button_down_p = False;
 
2168
      return True;
 
2169
    }
 
2170
  else if (event->xany.type == ButtonPress &&
 
2171
           (event->xbutton.button == Button4 ||
 
2172
            event->xbutton.button == Button5))
 
2173
    {
 
2174
      gltrackball_mousewheel (pp->trackball, event->xbutton.button, 5,
 
2175
                              !!event->xbutton.state);
 
2176
      return True;
 
2177
    }
 
2178
  else if (event->xany.type == MotionNotify &&
 
2179
           pp->button_down_p)
 
2180
    {
 
2181
      gltrackball_track (pp->trackball,
 
2182
                         event->xmotion.x, event->xmotion.y,
 
2183
                         MI_WIDTH (mi), MI_HEIGHT (mi));
 
2184
      return True;
 
2185
    }
 
2186
  else if (event->xany.type == KeyPress)
 
2187
    {
 
2188
      KeySym keysym;
 
2189
      char c = 0;
 
2190
      XLookupString (&event->xkey, &c, 1, &keysym, 0);
 
2191
      if (c == ' ' && debug_one_gear_p && pp->ngears)
 
2192
        {
 
2193
          delete_gear (mi, pp->gears[0]);
 
2194
          return True;
 
2195
        }
 
2196
    }
 
2197
 
 
2198
  return False;
 
2199
}
 
2200
 
 
2201
 
 
2202
void 
 
2203
init_pinion (ModeInfo *mi)
 
2204
{
 
2205
  pinion_configuration *pp;
 
2206
  int wire = MI_IS_WIREFRAME(mi);
 
2207
 
 
2208
  if (!pps) {
 
2209
    pps = (pinion_configuration *)
 
2210
      calloc (MI_NUM_SCREENS(mi), sizeof (pinion_configuration));
 
2211
    if (!pps) {
 
2212
      fprintf(stderr, "%s: out of memory\n", progname);
 
2213
      exit(1);
 
2214
    }
 
2215
 
 
2216
    pp = &pps[MI_SCREEN(mi)];
 
2217
  }
 
2218
 
 
2219
  pp = &pps[MI_SCREEN(mi)];
 
2220
 
 
2221
  pp->glx_context = init_GL(mi);
 
2222
 
 
2223
  load_fonts (mi);
 
2224
  reshape_pinion (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
 
2225
 
 
2226
  pp->title_list  = glGenLists (1);
 
2227
 
 
2228
  pp->ngears = 0;
 
2229
  pp->gears_size = 0;
 
2230
  pp->gears = 0;
 
2231
 
 
2232
  plane_displacement *= gear_size;
 
2233
 
 
2234
  if (!wire)
 
2235
    {
 
2236
      GLfloat pos[4] = {-3.0, 1.0, 1.0, 0.0};
 
2237
      GLfloat amb[4] = { 0.0, 0.0, 0.0, 1.0};
 
2238
      GLfloat dif[4] = { 1.0, 1.0, 1.0, 1.0};
 
2239
      GLfloat spc[4] = { 1.0, 1.0, 1.0, 1.0};
 
2240
 
 
2241
      glEnable(GL_LIGHTING);
 
2242
      glEnable(GL_LIGHT0);
 
2243
      glEnable(GL_DEPTH_TEST);
 
2244
      glEnable(GL_CULL_FACE);
 
2245
 
 
2246
      glLightfv(GL_LIGHT0, GL_POSITION, pos);
 
2247
      glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
 
2248
      glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
 
2249
      glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
 
2250
    }
 
2251
 
 
2252
  pp->trackball = gltrackball_init ();
 
2253
 
 
2254
  ffwd (mi);
 
2255
}
 
2256
 
 
2257
 
 
2258
void
 
2259
draw_pinion (ModeInfo *mi)
 
2260
{
 
2261
  pinion_configuration *pp = &pps[MI_SCREEN(mi)];
 
2262
  Display *dpy = MI_DISPLAY(mi);
 
2263
  Window window = MI_WINDOW(mi);
 
2264
  Bool wire_p = MI_IS_WIREFRAME(mi);
 
2265
  static int tick = 0;
 
2266
 
 
2267
  if (!pp->glx_context)
 
2268
    return;
 
2269
 
 
2270
  if (!pp->button_down_p)
 
2271
    {
 
2272
      if (!debug_one_gear_p || pp->ngears == 0)
 
2273
        scroll_gears (mi);
 
2274
      spin_gears (mi);
 
2275
    }
 
2276
 
 
2277
  glShadeModel(GL_SMOOTH);
 
2278
 
 
2279
  glEnable(GL_DEPTH_TEST);
 
2280
  glEnable(GL_NORMALIZE);
 
2281
  glEnable(GL_CULL_FACE);
 
2282
 
 
2283
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
2284
 
 
2285
  glPushMatrix ();
 
2286
  {
 
2287
    gltrackball_rotate (pp->trackball);
 
2288
    mi->polygon_count = 0;
 
2289
 
 
2290
    glScalef (16, 16, 16);   /* map vp_width/height to the screen */
 
2291
 
 
2292
    if (debug_one_gear_p)    /* zoom in */
 
2293
      glScalef (3, 3, 3);
 
2294
    else if (debug_p)        /* show the "visible" and "layout" areas */
 
2295
      {
 
2296
        GLfloat ow = pp->layout_right - pp->render_left;
 
2297
        GLfloat rw = pp->render_right - pp->render_left;
 
2298
        GLfloat s = (pp->vp_width / ow) * 0.85;
 
2299
        glScalef (s, s, s);
 
2300
        glTranslatef (-(ow - rw) / 2, 0, 0);
 
2301
      }
 
2302
    else
 
2303
      {
 
2304
        GLfloat s = 1.2;
 
2305
        glScalef (s, s, s);           /* zoom in a little more */
 
2306
        glRotatef (-35, 1, 0, 0);     /* tilt back */
 
2307
        glRotatef (  8, 0, 1, 0);     /* tilt left */
 
2308
        glTranslatef (0.02, 0.1, 0);  /* pan up */
 
2309
      }
 
2310
 
 
2311
    draw_gears (mi);
 
2312
 
 
2313
    if (debug_p)
 
2314
      {
 
2315
        if (!wire_p) glDisable(GL_LIGHTING);
 
2316
        glColor3f (0.6, 0, 0);
 
2317
        glBegin(GL_LINE_LOOP);
 
2318
        glVertex3f (pp->render_left,  pp->vp_top,    0);
 
2319
        glVertex3f (pp->render_right, pp->vp_top,    0);
 
2320
        glVertex3f (pp->render_right, pp->vp_bottom, 0);
 
2321
        glVertex3f (pp->render_left,  pp->vp_bottom, 0);
 
2322
        glEnd();
 
2323
        glColor3f (0.4, 0, 0);
 
2324
        glBegin(GL_LINES);
 
2325
        glVertex3f (pp->vp_left,      pp->vp_top,    0);
 
2326
        glVertex3f (pp->vp_left,      pp->vp_bottom, 0);
 
2327
        glVertex3f (pp->vp_right,     pp->vp_top,    0);
 
2328
        glVertex3f (pp->vp_right,     pp->vp_bottom, 0);
 
2329
        glEnd();
 
2330
        glColor3f (0, 0.4, 0);
 
2331
        glBegin(GL_LINE_LOOP);
 
2332
        glVertex3f (pp->layout_left,  pp->vp_top,    0);
 
2333
        glVertex3f (pp->layout_right, pp->vp_top,    0);
 
2334
        glVertex3f (pp->layout_right, pp->vp_bottom, 0);
 
2335
        glVertex3f (pp->layout_left,  pp->vp_bottom, 0);
 
2336
        glEnd();
 
2337
        if (!wire_p) glEnable(GL_LIGHTING);
 
2338
      }
 
2339
 
 
2340
    if (tick++ > 10)   /* only do this every N frames */
 
2341
      {
 
2342
        tick = 0;
 
2343
        find_mouse_gear (mi);
 
2344
        new_label (mi);
 
2345
      }
 
2346
  }
 
2347
  glPopMatrix ();
 
2348
 
 
2349
  glCallList (pp->title_list);
 
2350
 
 
2351
  if (mi->fps_p) do_fps (mi);
 
2352
  glFinish();
 
2353
 
 
2354
  glXSwapBuffers(dpy, window);
 
2355
}
 
2356
 
 
2357
#endif /* USE_GL */