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