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