8c44bff3983a777886742774909a8bc07954c901
[xscreensaver] / hacks / glx / engine.c
1 /*
2  * engine.c - GL representation of a 4 stroke engine
3  *
4  * version 2.00
5  *
6  * Copyright (C) 2001 Ben Buxton (bb@cactii.net)
7  * modified by Ed Beroset (beroset@mindspring.com)
8  *  new to 2.0 version is:
9  *  - command line argument to specify number of cylinders
10  *  - command line argument to specify included angle of engine
11  *  - removed broken command line argument to specify rotation speed
12  *  - included crankshaft shapes and firing orders for real engines
13  *    verified using the Bosch _Automotive Handbook_, 5th edition, pp 402,403
14  *
15  * Permission to use, copy, modify, distribute, and sell this software and its
16  * documentation for any purpose is hereby granted without fee, provided that
17  * the above copyright notice appear in all copies and that both that
18  * copyright notice and this permission notice appear in supporting
19  * documentation.  No representations are made about the suitability of this
20  * software for any purpose.  It is provided "as is" without express or
21  * implied warranty.
22  */
23
24 #ifdef STANDALONE
25 #define DEFAULTS        "*delay:           30000        \n" \
26                         "*showFPS:         False        \n" \
27                         "*titleFont:  -*-helvetica-medium-r-normal-*-180-*\n" \
28
29 # define refresh_engine 0
30 # include "xlockmore.h"              /* from the xscreensaver distribution */
31 #else  /* !STANDALONE */
32 # include "xlock.h"                  /* from the xlockmore distribution */
33 #endif /* !STANDALONE */
34
35 #include "glxfonts.h"
36 #include "rotator.h"
37 #include "gltrackball.h"
38
39 /* lifted from lament.c */
40 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
41 #define RANDSIGN() ((random() & 1) ? 1 : -1)
42
43
44 #ifdef USE_GL
45
46 #define DEF_ENGINE "(none)"
47 #define DEF_TITLES "False"
48 #define DEF_SPIN   "True"
49 #define DEF_MOVE   "True"
50
51 #undef countof
52 #define countof(x) (sizeof((x))/sizeof((*x)))
53
54 static char *which_engine;
55 static int move;
56 static int spin;
57 static Bool do_titles;
58
59 static XrmOptionDescRec opts[] = {
60   {"-engine",  ".engine.engine", XrmoptionSepArg, DEF_ENGINE },
61   {"-move",    ".engine.move",   XrmoptionNoArg, "True"  },
62   {"+move",    ".engine.move",   XrmoptionNoArg, "False" },
63   {"-spin",    ".engine.spin",   XrmoptionNoArg, "True"  },
64   {"+spin",    ".engine.spin",   XrmoptionNoArg, "False" },
65   { "-titles", ".engine.titles", XrmoptionNoArg, "True"  },
66   { "+titles", ".engine.titles", XrmoptionNoArg, "False" },
67 };
68
69 static argtype vars[] = {
70   {&which_engine, "engine", "Engine", DEF_ENGINE, t_String},
71   {&move,         "move",   "Move",   DEF_MOVE,   t_Bool},
72   {&spin,         "spin",   "Spin",   DEF_SPIN,   t_Bool},
73   {&do_titles,    "titles", "Titles", DEF_TITLES, t_Bool},
74 };
75
76 ENTRYPOINT ModeSpecOpt engine_opts = {countof(opts), opts, countof(vars), vars, NULL};
77
78 #ifdef USE_MODULES
79 ModStruct   engine_description =
80 {"engine", "init_engine", "draw_engine", "release_engine",
81  "draw_engine", "init_engine", NULL, &engine_opts,
82  1000, 1, 2, 1, 4, 1.0, "",
83  "A four stroke engine", 0, NULL};
84
85 #endif
86
87 /* these defines are used to provide symbolic means
88  * by which to refer to various portions or multiples
89  * of a cyle in degrees
90  */
91 #define HALFREV 180
92 #define ONEREV 360
93 #define TWOREV 720
94
95 #define MOVE_MULT 0.05
96
97 #define RAND_RANGE(min, max) ((min) + (max - min) * f_rand())
98
99
100 typedef struct {
101   GLXContext *glx_context;
102   Window window;
103   GLfloat x, y, z; /* position */
104   GLfloat dx, dy, dz; /* position */
105   GLfloat an1, an2, an3; /* internal angle */
106   GLfloat nx, ny, nz; /* spin vector */
107   GLfloat a; /* spin angle */
108   GLfloat da; /* spin speed */
109   rotator *rot;
110   trackball_state *trackball;
111   Bool button_down_p;
112 # ifdef HAVE_GLBITMAP
113   XFontStruct *xfont;
114   GLuint font_dlist;
115 # else
116   texture_font_data *font_data;
117 # endif
118   char *engine_name;
119   int engineType;
120   int movepaused;
121
122   float crankOffset;
123   float crankWidth;
124
125   int win_w, win_h;
126
127   float sin_table[TWOREV];
128   float cos_table[TWOREV];
129   float tan_table[TWOREV];
130
131   GLfloat boom_red[4];
132   GLfloat boom_lpos[4];
133   GLfloat boom_d, boom_wd;
134   int boom_time;
135
136   GLfloat viewer[3], lookat[3];
137
138   int display_a;
139   GLfloat ln[730], yp[730], ang[730];
140   int ln_init;
141   int lastPlug;
142
143   GLuint shaft_list, piston_list;
144   int shaft_polys, piston_polys;
145
146 } Engine;
147
148 static Engine *engine = NULL;
149
150 static const GLfloat lightpos[] = {7.0, 7.0, 12, 1.0};
151 static const GLfloat light_sp[] = {0.8, 0.8, 0.8, 0.5};
152 static const GLfloat red[] = {1.0, 0, 0, 1.0};
153 static const GLfloat green[] = {0.0, 1, 0, 1.0};
154 static const GLfloat blue[] = {0, 0, 1, 1.0};
155 static const GLfloat white[] = {1.0, 1, 1, 1.0};
156 static const GLfloat yellow_t[] = {1, 1, 0, 0.4};
157
158 static GLvoid normal(GLfloat [], GLfloat [], GLfloat [], 
159                   GLfloat *, GLfloat *, GLfloat *);
160
161 /* 
162  * this table represents both the firing order and included angle of engine.
163  * To simplify things, we always number from 0 starting at the flywheel and 
164  * moving down the crankshaft toward the back of the engine.  This doesn't
165  * always match manufacturer's schemes.  For example, the Porsche 911 engine
166  * is a flat six with the following configuration (Porsche's numbering):
167  *
168  *    3  2  1
169  *  |=            firing order is 1-6-2-4-3-5 in this diagram
170  *     6  5  4
171  *
172  * We renumber these using our scheme but preserve the effective firing order:
173  *
174  *   0  2  4
175  * |=             firing order is 4-1-2-5-0-3 in this diagram
176  *    1  3  5
177  *
178  * To avoid going completely insane, we also reorder these so the newly 
179  * renumbered cylinder 0 is always first: 0-3-4-1-2-5
180  *   
181  * For a flat 6, the included angle is 180 degrees (0 would be a inline
182  * engine).  Because these are all four-stroke engines, each piston goes
183  * through 720 degrees of rotation for each time the spark plug sparks,
184  * so in this case, we would use the following angles:
185  *
186  * cylinder     firing order     angle
187  * --------     ------------     -----
188  *    0               0             0
189  *    1               3           360
190  *    2               4           240
191  *    3               1           600
192  *    4               2           480
193  *    5               5           120
194  *
195  */
196
197 typedef struct 
198 {
199     int cylinders;
200     int includedAngle;
201     int pistonAngle[12];  /* twelve cylinders should suffice... */
202     int speed;          /*  step size in degrees for engine speed */
203     const char *engineName;  /* currently unused */
204 } engine_type;
205
206 static const engine_type engines[] = {
207     { 3,   0, { 0, 240, 480,   0,   0,   0,
208                 0,   0,   0,   0,   0,   0 }, 12,
209      "Honda Insight" },
210     { 4,   0, { 0, 180, 540, 360,   0,   0,
211                 0,   0,   0,   0,   0,   0 }, 12,
212      "BMW M3" },
213     { 4, 180, { 0, 360, 180, 540,   0,   0,
214                 0,   0,   0,   0,   0,   0 }, 12,
215      "VW Beetle" },
216     { 5,   0, { 0, 576, 144, 432, 288,   0,
217                 0,   0,   0,   0,   0,   0 }, 12,
218      "Audi Quattro" },
219     { 6,   0, { 0, 240, 480, 120, 600, 360,
220                 0,   0,   0,   0,   0,   0 }, 12,
221      "BMW M5" },
222     { 6,  90, { 0, 360, 480, 120, 240, 600,
223                 0,   0,   0,   0,   0,   0 }, 12,
224      "Subaru XT" },
225     { 6, 180, { 0, 360, 240, 600, 480, 120,
226                 0,   0,   0,   0,   0,   0 }, 12,
227      "Porsche 911" },
228     { 8,  90, { 0, 450,  90, 180, 270, 360,
229               540, 630,   0,   0,   0,   0 }, 15,
230      "Corvette Z06" },
231     {10,  90, { 0,  72, 432, 504, 288, 360,
232               144, 216, 576, 648,   0,   0 }, 12,
233      "Dodge Viper" },
234     {12,  60, { 0, 300, 240, 540, 480,  60,
235               120, 420, 600, 180, 360, 660 }, 12,
236      "Jaguar XKE" },
237 };
238
239 /* this define is just a little shorter way of referring to members of the 
240  * table above
241  */
242 #define ENG engines[e->engineType]
243
244 /* given a number of cylinders and an included angle, finds matching engine */
245 static int
246 find_engine(char *name)
247 {
248   unsigned int i;
249   char *s;
250
251   if (!name || !*name || !strcasecmp (name, "(none)"))
252     return (random() % countof(engines));
253
254   for (s = name; *s; s++)
255     if (*s == '-' || *s == '_') *s = ' ';
256
257   for (i = 0; i < countof(engines); i++) {
258     if (!strcasecmp(name, engines[i].engineName))
259       return i;
260   }
261
262   fprintf (stderr, "%s: unknown engine type \"%s\"\n", progname, name);
263   fprintf (stderr, "%s: available models are:\n", progname);
264   for (i = 0; i < countof(engines); i++) {
265     fprintf (stderr, "\t %-13s (%d cylinders",
266              engines[i].engineName, engines[i].cylinders);
267     if (engines[i].includedAngle == 0)
268       fprintf (stderr, ")\n");
269     else if (engines[i].includedAngle == 180)
270       fprintf (stderr, ", flat)\n");
271     else
272       fprintf (stderr, ", V)\n");
273   }
274   exit(1);
275 }
276
277 /* we use trig tables to speed things up - 200 calls to sin()
278  in one frame can be a bit harsh..
279 */
280
281 static void make_tables(Engine *e)
282 {
283   int i;
284   float f;
285
286   f = ONEREV / (M_PI * 2);
287   for (i = 0 ; i <= TWOREV ; i++) {
288     e->sin_table[i] = sin(i/f);
289   }
290   for (i = 0 ; i <= TWOREV ; i++) {
291     e->cos_table[i] = cos(i/f);
292   }
293   for (i = 0 ; i <= TWOREV ; i++) {
294     e->tan_table[i] = tan(i/f);
295   }
296 }
297
298 /* if inner and outer are the same, we draw a cylinder, not a tube */
299 /* for a tube, endcaps is 0 (none), 1 (left), 2 (right) or 3(both) */
300 /* angle is how far around the axis to go (up to 360) */
301
302 static int cylinder (Engine *e, GLfloat x, GLfloat y, GLfloat z, 
303     float length, float outer, float inner, int endcaps, int sang, int eang)
304 {
305   int polys = 0;
306   int a; /* current angle around cylinder */
307   int b = 0; /* previous */
308   int angle, norm, step, sangle;
309   float z1, y1, z2, y2, ex=0;
310   float Z1, Y1, Z2, Y2, xl;
311   GLfloat y2c[TWOREV], z2c[TWOREV];
312   int nsegs, tube = 0;
313
314   glPushMatrix();
315   nsegs = outer*(MAX(e->win_w, e->win_h)/200);
316   nsegs = MAX(nsegs, 6);
317   nsegs = MAX(nsegs, 40);
318   if (nsegs % 2)
319      nsegs += 1;
320   sangle = sang;
321   angle = eang;
322   z1 = e->cos_table[sangle]*outer+z; y1 = e->sin_table[sangle] * outer+y;
323   Z1 = e->cos_table[sangle] * inner+z; Y1 = e->sin_table[sangle]*inner+y ; 
324   Z2 = z;
325   Y2 = y;
326   xl = x + length;
327   if (inner < outer && endcaps < 3) tube = 1;
328   step = ONEREV/nsegs;
329
330   glBegin(GL_QUADS);
331   for (a = sangle ; a <= angle || b <= angle ; a+= step) {
332     y2=outer*(float)e->sin_table[a]+y;
333     z2=outer*(float)e->cos_table[a]+z;
334     if (endcaps)
335        y2c[a] = y2; z2c[a] = z2; /* cache for later */
336     if (tube) {
337       Y2=inner*(float)e->sin_table[a]+y;
338       Z2=inner*(float)e->cos_table[a]+z;
339     }
340     glNormal3f(0, y1, z1);
341     glVertex3f(x,y1,z1);
342     glVertex3f(xl,y1,z1);
343     glNormal3f(0, y2, z2);
344     glVertex3f(xl,y2,z2);
345     glVertex3f(x,y2,z2);
346     polys++;
347     if (a == sangle && angle - sangle < ONEREV) {
348       if (tube)
349         glVertex3f(x, Y1, Z1);
350       else
351         glVertex3f(x, y, z);
352       glVertex3f(x, y1, z1);
353       glVertex3f(xl, y1, z1);
354       if (tube)
355         glVertex3f(xl, Z1, Z1);
356       else
357         glVertex3f(xl, y, z);
358       polys++;
359     }
360     if (tube) {
361       if (endcaps != 1) {
362         glNormal3f(-1, 0, 0); /* left end */
363         glVertex3f(x, y1, z1);
364         glVertex3f(x, y2, z2);
365         glVertex3f(x, Y2, Z2);
366         glVertex3f(x, Y1, Z1);
367         polys++;
368       }
369
370       glNormal3f(0, -Y1, -Z1); /* inner surface */
371       glVertex3f(x, Y1, Z1);
372       glVertex3f(xl, Y1, Z1);
373       glNormal3f(0, -Y2, -Z2);
374       glVertex3f(xl, Y2, Z2);
375       glVertex3f(x, Y2, Z2);
376       polys++;
377
378       if (endcaps != 2) {
379         glNormal3f(1, 0, 0); /* right end */
380         glVertex3f(xl, y1, z1);
381         glVertex3f(xl, y2, z2);
382         glVertex3f(xl, Y2, Z2);
383         glVertex3f(xl, Y1, Z1);
384         polys++;
385       }
386     }
387
388     z1=z2; y1=y2;
389     Z1=Z2; Y1=Y2;
390     b = a;
391   }
392   glEnd();
393
394   if (angle - sangle < ONEREV) {
395     GLfloat nx, ny, nz;
396     GLfloat v1[3], v2[3], v3[3];
397     v1[0] = x; v1[1] = y; v1[2] = z;
398     v2[0] = x; v2[1] = y1; v2[2] = z1;
399     v3[0] = xl; v3[1] = y1; v3[2] = z1;
400     normal(&v2[0], &v1[0], &v3[0], &nx, &ny, &nz);
401     glBegin(GL_QUADS);
402     glNormal3f(nx, ny, nz);
403     glVertex3f(x, y, z);
404     glVertex3f(x, y1, z1);
405     glVertex3f(xl, y1, z1);
406     glVertex3f(xl, y, z);
407     polys++;
408     glEnd();
409   }
410   if (endcaps) {
411     GLfloat end, start;
412     if (tube) {
413       if (endcaps == 1) {
414         end = 0;
415         start = 0;
416       } else if (endcaps == 2) {
417         start = end = length+0.01;
418       } else {
419         end = length+0.02; start = -0.01;
420       }
421       norm = (ex == length+0.01) ? -1 : 1;
422     } else  {
423       end = length;
424       start = 0;
425       norm = -1;
426     }
427
428     for(ex = start ; ex <= end ; ex += length) {
429       z1 = outer*e->cos_table[sangle]+z;
430       y1 = y+e->sin_table[sangle]*outer;
431       step = ONEREV/nsegs;
432       glBegin(GL_TRIANGLES);
433       b = 0;
434       for (a = sangle ; a <= angle || b <= angle; a+= step) {
435           glNormal3f(norm, 0, 0);
436           glVertex3f(x+ex,y, z);
437           glVertex3f(x+ex,y1,z1);
438           glVertex3f(x+ex,y2c[a],z2c[a]);
439           polys++;
440         y1 = y2c[a]; z1 = z2c[a];
441         b = a;
442       }
443       if (!tube) norm = 1;
444       glEnd();
445     }
446   }
447   glPopMatrix();
448   return polys;
449 }
450
451 /* this is just a convenience function to make a solid rod */
452 static int rod (Engine *e, GLfloat x, GLfloat y, GLfloat z, float length, float diameter)
453 {
454     return cylinder(e, x, y, z, length, diameter, diameter, 3, 0, ONEREV);
455 }
456
457 static GLvoid normal(GLfloat v1[], GLfloat v2[], GLfloat v3[], 
458                   GLfloat *nx, GLfloat *ny, GLfloat *nz)
459 {
460    GLfloat x, y, z, X, Y, Z;
461
462    x = v2[0]-v1[0];
463    y = v2[1]-v1[1];
464    z = v2[2]-v1[2];
465    X = v3[0]-v1[0];
466    Y = v3[1]-v1[1];
467    Z = v3[2]-v1[2];
468
469    *nx = Y*z - Z*y;
470    *ny = Z*x - X*z;
471    *nz = X*y - Y*x;
472
473
474
475
476
477 static int Rect(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h,
478             GLfloat t)
479 {
480   int polys = 0;
481   GLfloat yh;
482   GLfloat xw;
483   GLfloat zt;
484
485   yh = y+h; xw = x+w; zt = z - t;
486
487   glBegin(GL_QUADS); /* front */
488     glNormal3f(0, 0, 1);
489     glVertex3f(x, y, z);
490     glVertex3f(x, yh, z);
491     glVertex3f(xw, yh, z);
492     glVertex3f(xw, y, z);
493     polys++;
494   /* back */
495     glNormal3f(0, 0, -1);
496     glVertex3f(x, y, zt);
497     glVertex3f(x, yh, zt);
498     glVertex3f(xw, yh, zt);
499     glVertex3f(xw, y, zt);
500     polys++;
501   /* top */
502     glNormal3f(0, 1, 0);
503     glVertex3f(x, yh, z);
504     glVertex3f(x, yh, zt);
505     glVertex3f(xw, yh, zt);
506     glVertex3f(xw, yh, z);
507     polys++;
508   /* bottom */
509     glNormal3f(0, -1, 0);
510     glVertex3f(x, y, z);
511     glVertex3f(x, y, zt);
512     glVertex3f(xw, y, zt);
513     glVertex3f(xw, y, z);
514     polys++;
515   /* left */
516     glNormal3f(-1, 0, 0);
517     glVertex3f(x, y, z);
518     glVertex3f(x, y, zt);
519     glVertex3f(x, yh, zt);
520     glVertex3f(x, yh, z);
521     polys++;
522   /* right */
523     glNormal3f(1, 0, 0);
524     glVertex3f(xw, y, z);
525     glVertex3f(xw, y, zt);
526     glVertex3f(xw, yh, zt);
527     glVertex3f(xw, yh, z);
528     polys++;
529   glEnd();
530   return polys;
531 }
532
533 static int makepiston(Engine *e)
534 {
535   int polys = 0;
536   GLfloat colour[] = {0.6, 0.6, 0.6, 1.0};
537   
538   e->piston_list = glGenLists(1);
539   glNewList(e->piston_list, GL_COMPILE);
540   glRotatef(90, 0, 0, 1);
541   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, colour);
542   glMaterialfv(GL_FRONT, GL_SPECULAR, colour);
543   glMateriali(GL_FRONT, GL_SHININESS, 20);
544   polys += cylinder(e, 0, 0, 0, 2, 1, 0.7, 2, 0, ONEREV); /* body */
545   colour[0] = colour[1] = colour[2] = 0.2;
546   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, colour);
547   polys += cylinder(e, 1.6, 0, 0, 0.1, 1.05, 1.05, 0, 0, ONEREV); /* ring */
548   polys += cylinder(e, 1.8, 0, 0, 0.1, 1.05, 1.05, 0, 0, ONEREV); /* ring */
549   glEndList();
550   return polys;
551 }
552
553 static int CrankBit(Engine *e, GLfloat x)
554 {
555   int polys = 0;
556   polys += Rect(x, -1.4, 0.5, 0.2, 1.8, 1);
557   polys += cylinder(e, x, -0.5, 0, 0.2, 2, 2, 1, 60, 120);
558   return polys;
559 }
560
561 static int boom(Engine *e, GLfloat x, GLfloat y, int s)
562 {
563   int polys = 0;
564   int flameOut = 720/ENG.speed/ENG.cylinders;
565
566   if (e->boom_time == 0 && s) {
567     e->boom_red[0] = e->boom_red[1] = 0;
568     e->boom_d = 0.05;
569     e->boom_time++;
570     glEnable(GL_LIGHT1); 
571   } else if (e->boom_time == 0 && !s) {
572     return polys;
573   } else if (e->boom_time >= 8 && e->boom_time < flameOut && !s) {
574     e->boom_time++;
575     e->boom_red[0] -= 0.2; e->boom_red[1] -= 0.1;
576     e->boom_d-= 0.04;
577   } else if (e->boom_time >= flameOut) {
578     e->boom_time = 0;
579     glDisable(GL_LIGHT1);
580     return polys;
581   } else {
582     e->boom_red[0] += 0.2; e->boom_red[1] += 0.1;
583     e->boom_d += 0.04;
584     e->boom_time++;
585   }
586   e->boom_lpos[0] = x-e->boom_d; e->boom_lpos[1] = y;
587   glLightfv(GL_LIGHT1, GL_POSITION, e->boom_lpos);
588   glLightfv(GL_LIGHT1, GL_DIFFUSE, e->boom_red);
589   glLightfv(GL_LIGHT1, GL_SPECULAR, e->boom_red);
590   glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 1.3);
591   glLighti(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 0);
592
593   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, e->boom_red);
594   e->boom_wd = e->boom_d*3;
595   if (e->boom_wd > 0.7) e->boom_wd = 0.7;
596   glEnable(GL_BLEND);
597   glDepthMask(GL_FALSE);
598   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
599   polys += rod(e, x, y, 0, e->boom_d, e->boom_wd);
600   glDepthMask(GL_TRUE);
601   glDisable(GL_BLEND);
602   return polys;
603 }
604
605 static int display(Engine *e)
606 {
607   int polys = 0;
608   GLfloat zb, yb;
609   float rightSide;
610   int half;
611   int sides;
612   int j, b;
613
614   glEnable(GL_LIGHTING);
615   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
616   glLoadIdentity();
617   gluLookAt(e->viewer[0], e->viewer[1], e->viewer[2], 
618             e->lookat[0], e->lookat[1], e->lookat[2], 
619             0.0, 1.0, 0.0);
620   glPushMatrix();
621
622   glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
623   glLightfv(GL_LIGHT0, GL_SPECULAR, light_sp);
624   glLightfv(GL_LIGHT0, GL_DIFFUSE, light_sp);
625
626   if (move) {
627     double x, y, z;
628     get_position (e->rot, &x, &y, &z, !e->button_down_p);
629     glTranslatef(x*16-9, y*14-7, z*16-10);
630   }
631
632   if (spin) {
633     double x, y, z;
634
635     /* Do it twice because we don't track the device's orientation. */
636     glRotatef( current_device_rotation(), 0, 0, 1);
637     gltrackball_rotate (e->trackball);
638     glRotatef(-current_device_rotation(), 0, 0, 1);
639
640     get_rotation(e->rot, &x, &y, &z, !e->button_down_p);
641     glRotatef(x*ONEREV, 1.0, 0.0, 0.0);
642     glRotatef(y*ONEREV, 0.0, 1.0, 0.0);
643     glRotatef(x*ONEREV, 0.0, 0.0, 1.0);
644   }
645
646 /* So the rotation appears around the centre of the engine */
647   glTranslatef(-5, 0, 0); 
648
649 /* crankshaft */
650   glPushMatrix();
651   glRotatef(e->display_a, 1, 0, 0);
652   glCallList(e->shaft_list);
653   polys += e->shaft_polys;
654   glPopMatrix();
655
656   /* init the ln[] matrix for speed */
657   if (e->ln_init == 0) {
658     for (e->ln_init = 0 ; e->ln_init < 730 ; e->ln_init++) {
659       zb = e->sin_table[e->ln_init];
660       yb = e->cos_table[e->ln_init];
661       /* y ordinate of piston */
662       e->yp[e->ln_init] = yb + sqrt(25 - (zb*zb)); 
663       /* length of rod */
664       e->ln[e->ln_init] = sqrt(zb*zb + (yb-e->yp[e->ln_init])*(yb-e->yp[e->ln_init])); 
665       /* angle of connecting rod */
666       e->ang[e->ln_init] = asin(zb/5)*57; 
667       e->ang[e->ln_init] *= -1;
668     }
669   }
670
671   glPushMatrix();
672   sides = (ENG.includedAngle == 0) ? 1 : 2;
673   for (half = 0; half < sides; half++, glRotatef(ENG.includedAngle,1,0,0))
674   {
675     /* pistons */
676       /* glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white); */
677       for (j = 0; j < ENG.cylinders; j += sides)
678       {
679         b = (e->display_a + ENG.pistonAngle[j+half]) % ONEREV;
680         glPushMatrix();
681         glTranslatef(e->crankWidth/2 + e->crankOffset*(j+half), e->yp[b]-0.3, 0);
682         glCallList(e->piston_list);
683         polys += e->piston_polys;
684         glPopMatrix();
685       }
686     /* spark plugs */
687       glPushMatrix();
688       glRotatef(90, 0, 0, 1); 
689       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
690       for (j = 0; j < ENG.cylinders; j += sides) 
691       {
692         polys += cylinder(e, 8.5, -e->crankWidth/2-e->crankOffset*(j+half), 0, 
693             0.5, 0.4, 0.3, 1, 0, ONEREV); 
694       }
695       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white);
696       for (j = 0; j < ENG.cylinders; j += sides)
697       {
698         polys += rod(e, 8, -e->crankWidth/2-e->crankOffset*(j+half), 0, 0.5, 0.2); 
699         polys += rod(e, 9, -e->crankWidth/2-e->crankOffset*(j+half), 0, 1, 0.15); 
700       }   
701
702      /* rod */
703       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
704       for (j = 0; j < ENG.cylinders; j += sides)
705       {
706           b = (e->display_a+HALFREV+ENG.pistonAngle[j+half]) % TWOREV; 
707           glPushMatrix();
708           glRotatef(e->ang[b], 0, 1, 0);
709           polys += rod(e, 
710               -e->cos_table[b],
711               -e->crankWidth/2-e->crankOffset*(j+half),
712               -e->sin_table[b], 
713               e->ln[b], 0.2);
714           glPopMatrix();
715       }
716       glPopMatrix();
717
718         /* engine block */
719       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellow_t);
720       glEnable(GL_BLEND);
721       glDepthMask(GL_FALSE);
722       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
723       rightSide = (sides > 1) ? 0 : 1.6;
724     /* left plate */
725       polys += Rect(-e->crankWidth/2, -0.5,  1, 0.2, 9, 2);
726     /* right plate */
727       polys += Rect(0.3+e->crankOffset*ENG.cylinders-rightSide, -0.5, 1, 0.2, 9, 2);
728     /* head plate */
729       polys += Rect(-e->crankWidth/2+0.2, 8.3, 1, 
730             e->crankWidth/2+0.1+e->crankOffset*ENG.cylinders-rightSide, 0.2, 2);
731     /* front rail */
732       polys += Rect(-e->crankWidth/2+0.2, 3, 1, 
733             e->crankWidth/2+0.1+e->crankOffset*ENG.cylinders-rightSide, 0.2, 0.2);
734     /* back rail */
735       polys += Rect(-e->crankWidth/2+0.2, 3, -1+0.2, 
736             e->crankWidth/2+0.1+e->crankOffset*ENG.cylinders-rightSide, 0.2, 0.2);
737     /* plates between cylinders */
738       for (j=0; j < ENG.cylinders - (sides == 1); j += sides)
739         polys += Rect(0.4+e->crankWidth+e->crankOffset*(j-half), 3, 1, 1, 5.3, 2);
740       glDepthMask(GL_TRUE);
741   }
742   glPopMatrix();
743
744         /* see which of our plugs should fire now, if any */
745   for (j = 0; j < ENG.cylinders; j++)
746   {
747     if (0 == ((e->display_a + ENG.pistonAngle[j]) % TWOREV))
748     {
749         glPushMatrix();
750         if (j & 1) 
751             glRotatef(ENG.includedAngle,1,0,0);
752         glRotatef(90, 0, 0, 1); 
753         polys += boom(e, 8, -e->crankWidth/2-e->crankOffset*j, 1); 
754         e->lastPlug = j;
755         glPopMatrix();
756     }
757   }
758
759   if (e->lastPlug != j)
760   {
761     /* this code causes the last plug explosion to dim gradually */
762     if (e->lastPlug & 1) 
763       glRotatef(ENG.includedAngle, 1, 0, 0);
764     glRotatef(90, 0, 0, 1); 
765     polys += boom(e, 8, -e->crankWidth/2-e->crankOffset*e->lastPlug, 0); 
766   }
767   glDisable(GL_BLEND);
768
769   e->display_a += ENG.speed; 
770   if (e->display_a >= TWOREV) 
771     e->display_a = 0;
772   glPopMatrix();
773   glFlush();
774   return polys;
775 }
776
777 static int makeshaft (Engine *e)
778 {
779   int polys = 0;
780   int j;
781   float crankThick = 0.2;
782   float crankDiam = 0.3;
783
784   e->shaft_list = glGenLists(1);
785   glNewList(e->shaft_list, GL_COMPILE);
786
787   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
788   /* draw the flywheel */
789   polys += cylinder(e, -2.5, 0, 0, 1, 3, 2.5, 0, 0, ONEREV);
790   polys += Rect(-2, -0.3, 2.8, 0.5, 0.6, 5.6);
791   polys += Rect(-2, -2.8, 0.3, 0.5, 5.6, 0.6);
792
793   /* now make each of the shaft bits between the cranks, 
794    * starting from the flywheel end which is at X-coord 0. 
795    * the first cranskhaft bit is always 2 units long 
796    */
797   polys += rod(e, -2, 0, 0, 2, crankDiam);
798
799   /* Each crank is crankWidth units wide and the total width of a
800    * cylinder assembly is 3.3 units. For inline engines, there is just
801    * a single crank per cylinder width.  For other engine
802    * configurations, there is a crank between each pair of adjacent
803    * cylinders on one side of the engine, so the crankOffset length is
804    * halved.
805    */
806   e->crankOffset = 3.3;
807   if (ENG.includedAngle != 0)
808     e->crankOffset /= 2;
809   for (j = 0; j < ENG.cylinders - 1; j++)
810     polys += rod(e,
811         e->crankWidth - crankThick + e->crankOffset*j, 0, 0, 
812         e->crankOffset - e->crankWidth + 2 * crankThick, crankDiam);
813   /* the last bit connects to the engine wall on the non-flywheel end */
814   polys += rod(e, e->crankWidth - crankThick + e->crankOffset*j, 0, 0, 0.9, crankDiam);
815
816
817   for (j = 0; j < ENG.cylinders; j++)
818   {
819     glPushMatrix();
820     if (j & 1)
821         glRotatef(HALFREV+ENG.pistonAngle[j]+ENG.includedAngle,1,0,0);
822     else
823         glRotatef(HALFREV+ENG.pistonAngle[j],1,0,0);
824     /* draw wrist pin */
825     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
826     polys += rod(e, e->crankOffset*j, -1.0, 0.0, e->crankWidth, crankDiam);
827     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
828     /* draw right part of crank */
829     polys += CrankBit(e, e->crankOffset*j); 
830     /* draw left part of crank */
831     polys += CrankBit(e, e->crankWidth-crankThick+e->crankOffset*j);
832     glPopMatrix();
833   }
834   glEndList();
835   return polys;
836 }
837
838
839 ENTRYPOINT void reshape_engine(ModeInfo *mi, int width, int height)
840 {
841  Engine *e = &engine[MI_SCREEN(mi)];
842  glViewport(0,0,(GLint)width, (GLint) height);
843  glMatrixMode(GL_PROJECTION);
844  glLoadIdentity();
845  glFrustum(-1.0,1.0,-1.0,1.0,1.5,70.0);
846  glMatrixMode(GL_MODELVIEW);
847  e->win_h = height; 
848  e->win_w = width;
849 }
850
851
852 ENTRYPOINT void init_engine(ModeInfo *mi)
853 {
854   int screen = MI_SCREEN(mi);
855   Engine *e;
856
857  if (engine == NULL) {
858    if ((engine = (Engine *) calloc(MI_NUM_SCREENS(mi),
859                                         sizeof(Engine))) == NULL)
860           return;
861  }
862  e = &engine[screen];
863  e->window = MI_WINDOW(mi);
864
865  e->x = e->y = e->z = e->a = e->an1 = e->nx = e->ny = e->nz = 
866  e->dx = e->dy = e->dz = e->da = 0;
867
868  if (move) {
869    e->dx = (float)(random() % 1000)/30000;
870    e->dy = (float)(random() % 1000)/30000;
871    e->dz = (float)(random() % 1000)/30000;
872  } else {
873   e->viewer[0] = 0; e->viewer[1] = 2; e->viewer[2] = 18;
874   e->lookat[0] = 0; e->lookat[1] = 0; e->lookat[2] = 0; 
875
876  }
877  if (spin) {
878    e->da = (float)(random() % 1000)/125 - 4;
879    e->nx = (float)(random() % 100) / 100;
880    e->ny = (float)(random() % 100) / 100;
881    e->nz = (float)(random() % 100) / 100;
882  }
883
884  {
885    double spin_speed = 0.5;
886    double wander_speed = 0.01;
887
888  e->crankWidth = 1.5;
889  e->boom_red[3] = 0.9;
890  e->boom_lpos[3] = 1;
891
892  e->viewer[2] = 30;
893
894  e->rot = make_rotator (spin ? spin_speed : 0,
895                         spin ? spin_speed : 0,
896                         spin ? spin_speed : 0,
897                         1.0,
898                         move ? wander_speed : 0,
899                         True);
900
901     e->trackball = gltrackball_init ();
902  }
903
904  if ((e->glx_context = init_GL(mi)) != NULL) {
905       reshape_engine(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
906  } else {
907      MI_CLEARWINDOW(mi);
908  }
909  glShadeModel(GL_SMOOTH);
910  glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
911  glEnable(GL_DEPTH_TEST);
912  glEnable(GL_LIGHTING);
913  glEnable(GL_LIGHT0);
914  glEnable(GL_NORMALIZE);
915  make_tables(e);
916  e->engineType = find_engine(which_engine);
917
918  e->engine_name = malloc(200);
919  sprintf (e->engine_name,
920           "%s\n%s%d%s",
921           engines[e->engineType].engineName,
922           (engines[e->engineType].includedAngle == 0 ? "" :
923            engines[e->engineType].includedAngle == 180 ? "Flat " : "V"),
924           engines[e->engineType].cylinders,
925           (engines[e->engineType].includedAngle == 0 ? " Cylinder" : "")
926           );
927
928  e->shaft_polys = makeshaft(e);
929  e->piston_polys = makepiston(e);
930
931 #ifdef HAVE_GLBITMAP
932  load_font (mi->dpy, "titleFont", &e->xfont, &e->font_dlist);
933 #else
934  e->font_data = load_texture_font (mi->dpy, "Font");
935 #endif
936 }
937
938 ENTRYPOINT Bool
939 engine_handle_event (ModeInfo *mi, XEvent *event)
940 {
941    Engine *e = &engine[MI_SCREEN(mi)];
942
943    if (event->xany.type == ButtonPress &&
944        event->xbutton.button == Button1)
945    {
946        e->button_down_p = True;
947        gltrackball_start (e->trackball,
948                           event->xbutton.x, event->xbutton.y,
949                           MI_WIDTH (mi), MI_HEIGHT (mi));
950        e->movepaused = 1;
951        return True;
952    }
953    else if (event->xany.type == ButtonRelease &&
954             event->xbutton.button == Button1) {
955        e->button_down_p = False;
956        e->movepaused = 0;
957        return True;
958    }
959   else if (event->xany.type == ButtonPress &&
960            (event->xbutton.button == Button4 ||
961             event->xbutton.button == Button5 ||
962             event->xbutton.button == Button6 ||
963             event->xbutton.button == Button7))
964     {
965       gltrackball_mousewheel (e->trackball, event->xbutton.button, 10,
966                               !!event->xbutton.state);
967       return True;
968     }
969    else if (event->xany.type == MotionNotify &&
970             e->button_down_p) {
971       gltrackball_track (e->trackball,
972                          event->xmotion.x, event->xmotion.y,
973                          MI_WIDTH (mi), MI_HEIGHT (mi));
974       return True;
975    }
976   return False;
977 }
978
979 ENTRYPOINT void draw_engine(ModeInfo *mi)
980 {
981   Engine *e = &engine[MI_SCREEN(mi)];
982   Window w = MI_WINDOW(mi);
983   Display *disp = MI_DISPLAY(mi);
984
985   if (!e->glx_context)
986     return;
987
988   glXMakeCurrent(disp, w, *(e->glx_context));
989
990
991   mi->polygon_count = display(e);
992
993   glColor3f (1, 1, 0);
994   if (do_titles)
995       print_gl_string (mi->dpy, 
996 # ifdef HAVE_GLBITMAP
997                        e->xfont, e->font_dlist,
998 # else
999                        e->font_data,
1000 # endif
1001                        mi->xgwa.width, mi->xgwa.height,
1002                        10, mi->xgwa.height - 10,
1003                        e->engine_name, False);
1004
1005   if(mi->fps_p) do_fps(mi);
1006   glFinish(); 
1007   glXSwapBuffers(disp, w);
1008 }
1009
1010 ENTRYPOINT void
1011 release_engine(ModeInfo *mi) 
1012 {
1013   if (engine != NULL) {
1014    (void) free((void *) engine);
1015    engine = NULL;
1016   }
1017   FreeAllGL(mi);
1018 }
1019
1020 XSCREENSAVER_MODULE ("Engine", engine)
1021
1022 #endif