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