http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.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:           10000        \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, (caddr_t) "True"  },
83   {"+move",    ".engine.move",   XrmoptionNoArg, (caddr_t) "False" },
84   {"-spin",    ".engine.spin",   XrmoptionNoArg, (caddr_t) "True"  },
85   {"+spin",    ".engine.spin",   XrmoptionNoArg, (caddr_t) "False" },
86   { "-titles", ".engine.titles", XrmoptionNoArg, (caddr_t) "True"  },
87   { "+titles", ".engine.titles", XrmoptionNoArg, (caddr_t) "False" },
88 };
89
90 static argtype vars[] = {
91   {(caddr_t *) &which_engine, "engine", "Engine", DEF_ENGINE, t_String},
92   {(caddr_t *) &move,         "move",   "Move",   DEF_WANDER, t_Bool},
93   {(caddr_t *) &spin,         "spin",   "Spin",   DEF_SPIN,   t_Bool},
94   {(caddr_t *) &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 find_engine(const char *name)
255 {
256   unsigned int i;
257
258   if (!name || !*name || !strcasecmp (name, "(none)"))
259     return (random() % countof(engines));
260
261   for (i = 0; i < countof(engines); i++) {
262     if (!strcasecmp(name, engines[i].engineName))
263       return i;
264   }
265
266   fprintf (stderr, "%s: unknown engine type \"%s\"\n", progname, name);
267   fprintf (stderr, "%s: available models are:\n", progname);
268   for (i = 0; i < countof(engines); i++) {
269     fprintf (stderr, "\t %-13s (%d cylinders",
270              engines[i].engineName, engines[i].cylinders);
271     if (engines[i].includedAngle == 0)
272       fprintf (stderr, ")\n");
273     else if (engines[i].includedAngle == 180)
274       fprintf (stderr, ", flat)\n");
275     else
276       fprintf (stderr, ", V)\n");
277   }
278   exit(1);
279 }
280
281 /* we use trig tables to speed things up - 200 calls to sin()
282  in one frame can be a bit harsh..
283 */
284
285 void make_tables(void)
286 {
287   int i;
288   float f;
289
290   f = ONEREV / (M_PI * 2);
291   for (i = 0 ; i <= TWOREV ; i++) {
292     sin_table[i] = sin(i/f);
293   }
294   for (i = 0 ; i <= TWOREV ; i++) {
295     cos_table[i] = cos(i/f);
296   }
297   for (i = 0 ; i <= TWOREV ; i++) {
298     tan_table[i] = tan(i/f);
299   }
300 }
301
302 /* if inner and outer are the same, we draw a cylinder, not a tube */
303 /* for a tube, endcaps is 0 (none), 1 (left), 2 (right) or 3(both) */
304 /* angle is how far around the axis to go (up to 360) */
305
306 void cylinder (GLfloat x, GLfloat y, GLfloat z, 
307     float length, float outer, float inner, int endcaps, int sang, int eang)
308 {
309   int a; /* current angle around cylinder */
310   int b = 0; /* previous */
311   int angle, norm, step, sangle;
312   float z1, y1, z2, y2, ex=0;
313   float y3, z3;
314   float Z1, Y1, Z2, Y2, xl, Y3, Z3;
315   GLfloat y2c[TWOREV], z2c[TWOREV];
316   GLfloat ony, onz; /* previous normals */
317   int nsegs, tube = 0;
318
319   glPushMatrix();
320   nsegs = outer*(MAX(win_w, win_h)/200);
321   nsegs = MAX(nsegs, 6);
322   nsegs = MAX(nsegs, 40);
323   if (nsegs % 2)
324      nsegs += 1;
325   sangle = sang;
326   angle = eang;
327   ony = onz = 0;
328   z1 = cos_table[sangle]*outer+z; y1 = sin_table[sangle] * outer+y;
329   Z1 = cos_table[sangle] * inner+z; Y1 = sin_table[sangle]*inner+y ; 
330   Z2 = z;
331   Y2 = y;
332   xl = x + length;
333   if (inner < outer && endcaps < 3) tube = 1;
334   step = ONEREV/nsegs;
335
336   glBegin(GL_QUADS);
337   for (a = sangle ; a <= angle || b <= angle ; a+= step) {
338     y2=outer*(float)sin_table[a]+y;
339     z2=outer*(float)cos_table[a]+z;
340     y3=outer*(float)sin_table[a+step]+y;
341     z3=outer*(float)cos_table[a+step]+z;
342     if (endcaps)
343        y2c[a] = y2; z2c[a] = z2; /* cache for later */
344     if (tube) {
345       Y2=inner*(float)sin_table[a]+y;
346       Z2=inner*(float)cos_table[a]+z;
347       Y3=inner*(float)sin_table[a+step]+y;
348       Z3=inner*(float)cos_table[a+step]+z;
349     }
350     glNormal3f(0, y1, z1);
351     glVertex3f(x,y1,z1);
352     glVertex3f(xl,y1,z1);
353     glNormal3f(0, y2, z2);
354     glVertex3f(xl,y2,z2);
355     glVertex3f(x,y2,z2);
356     if (a == sangle && angle - sangle < ONEREV) {
357       if (tube)
358         glVertex3f(x, Y1, Z1);
359       else
360         glVertex3f(x, y, z);
361       glVertex3f(x, y1, z1);
362       glVertex3f(xl, y1, z1);
363       if (tube)
364         glVertex3f(xl, Z1, Z1);
365       else
366         glVertex3f(xl, y, z);
367     }
368     if (tube) {
369       if (endcaps != 1) {
370         glNormal3f(-1, 0, 0); /* left end */
371         glVertex3f(x, y1, z1);
372         glVertex3f(x, y2, z2);
373         glVertex3f(x, Y2, Z2);
374         glVertex3f(x, Y1, Z1);
375       }
376
377       glNormal3f(0, -Y1, -Z1); /* inner surface */
378       glVertex3f(x, Y1, Z1);
379       glVertex3f(xl, Y1, Z1);
380       glNormal3f(0, -Y2, -Z2);
381       glVertex3f(xl, Y2, Z2);
382       glVertex3f(x, Y2, Z2);
383
384       if (endcaps != 2) {
385         glNormal3f(1, 0, 0); /* right end */
386         glVertex3f(xl, y1, z1);
387         glVertex3f(xl, y2, z2);
388         glVertex3f(xl, Y2, Z2);
389         glVertex3f(xl, Y1, Z1);
390       }
391     }
392
393     z1=z2; y1=y2;
394     Z1=Z2; Y1=Y2;
395     b = a;
396   }
397   glEnd();
398
399   if (angle - sangle < ONEREV) {
400     GLfloat nx, ny, nz;
401     GLfloat v1[3], v2[3], v3[3];
402     v1[0] = x; v1[1] = y; v1[2] = z;
403     v2[0] = x; v2[1] = y1; v2[2] = z1;
404     v3[0] = xl; v3[1] = y1; v3[2] = z1;
405     normal(&v2[0], &v1[0], &v3[0], &nx, &ny, &nz);
406     glBegin(GL_QUADS);
407     glNormal3f(nx, ny, nz);
408     glVertex3f(x, y, z);
409     glVertex3f(x, y1, z1);
410     glVertex3f(xl, y1, z1);
411     glVertex3f(xl, y, z);
412     glEnd();
413   }
414   if (endcaps) {
415     GLfloat end, start;
416     if (tube) {
417       if (endcaps == 1) {
418         end = 0;
419         start = 0;
420       } else if (endcaps == 2) {
421         start = end = length+0.01;
422       } else {
423         end = length+0.02; start = -0.01;
424       }
425       norm = (ex == length+0.01) ? -1 : 1;
426     } else  {
427       end = length;
428       start = 0;
429       norm = -1;
430     }
431
432     for(ex = start ; ex <= end ; ex += length) {
433       z1 = outer*cos_table[sangle]+z;
434       y1 = y+sin_table[sangle]*outer;
435       step = ONEREV/nsegs;
436       glBegin(GL_TRIANGLES);
437       b = 0;
438       for (a = sangle ; a <= angle || b <= angle; a+= step) {
439           glNormal3f(norm, 0, 0);
440           glVertex3f(x+ex,y, z);
441           glVertex3f(x+ex,y1,z1);
442           glVertex3f(x+ex,y2c[a],z2c[a]);
443         y1 = y2c[a]; z1 = z2c[a];
444         b = a;
445       }
446       if (!tube) norm = 1;
447       glEnd();
448     }
449   }
450   glPopMatrix();
451 }
452
453 /* this is just a convenience function to make a solid rod */
454 void rod (GLfloat x, GLfloat y, GLfloat z, float length, float diameter)
455 {
456     cylinder(x, y, z, length, diameter, diameter, 3, 0, ONEREV);
457 }
458
459 GLvoid normal(GLfloat v1[], GLfloat v2[], GLfloat v3[], 
460                   GLfloat *nx, GLfloat *ny, GLfloat *nz)
461 {
462    GLfloat x, y, z, X, Y, Z;
463
464    x = v2[0]-v1[0];
465    y = v2[1]-v1[1];
466    z = v2[2]-v1[2];
467    X = v3[0]-v1[0];
468    Y = v3[1]-v1[1];
469    Z = v3[2]-v1[2];
470
471    *nx = Y*z - Z*y;
472    *ny = Z*x - X*z;
473    *nz = X*y - Y*x;
474
475
476
477
478
479 void Rect(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h,
480             GLfloat t)
481 {
482   GLfloat yh;
483   GLfloat xw;
484   GLfloat zt;
485
486   yh = y+h; xw = x+w; zt = z - t;
487
488   glBegin(GL_QUADS); /* front */
489     glNormal3f(0, 0, 1);
490     glVertex3f(x, y, z);
491     glVertex3f(x, yh, z);
492     glVertex3f(xw, yh, z);
493     glVertex3f(xw, y, z);
494   /* back */
495     glNormal3f(0, 0, -1);
496     glVertex3f(x, y, zt);
497     glVertex3f(x, yh, zt);
498     glVertex3f(xw, yh, zt);
499     glVertex3f(xw, y, zt);
500   /* top */
501     glNormal3f(0, 1, 0);
502     glVertex3f(x, yh, z);
503     glVertex3f(x, yh, zt);
504     glVertex3f(xw, yh, zt);
505     glVertex3f(xw, yh, z);
506   /* bottom */
507     glNormal3f(0, -1, 0);
508     glVertex3f(x, y, z);
509     glVertex3f(x, y, zt);
510     glVertex3f(xw, y, zt);
511     glVertex3f(xw, y, z);
512   /* left */
513     glNormal3f(-1, 0, 0);
514     glVertex3f(x, y, z);
515     glVertex3f(x, y, zt);
516     glVertex3f(x, yh, zt);
517     glVertex3f(x, yh, z);
518   /* right */
519     glNormal3f(1, 0, 0);
520     glVertex3f(xw, y, z);
521     glVertex3f(xw, y, zt);
522     glVertex3f(xw, yh, zt);
523     glVertex3f(xw, yh, z);
524   glEnd();
525 }
526
527 void makepiston(void)
528 {
529   GLfloat colour[] = {0.6, 0.6, 0.6, 1.0};
530   int i;
531   
532   i = glGenLists(1);
533   glNewList(i, GL_COMPILE);
534   glRotatef(90, 0, 0, 1);
535   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, colour);
536   glMaterialfv(GL_FRONT, GL_SPECULAR, colour);
537   glMateriali(GL_FRONT, GL_SHININESS, 20);
538   cylinder(0, 0, 0, 2, 1, 0.7, 2, 0, ONEREV); /* body */
539   colour[0] = colour[1] = colour[2] = 0.2;
540   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, colour);
541   cylinder(1.6, 0, 0, 0.1, 1.05, 1.05, 0, 0, ONEREV); /* ring */
542   cylinder(1.8, 0, 0, 0.1, 1.05, 1.05, 0, 0, ONEREV); /* ring */
543   glEndList();
544 }
545
546 void CrankBit(GLfloat x)
547 {
548   Rect(x, -1.4, 0.5, 0.2, 1.8, 1);
549   cylinder(x, -0.5, 0, 0.2, 2, 2, 1, 60, 120);
550 }
551
552 void boom(GLfloat x, GLfloat y, int s)
553 {
554   static GLfloat red[] = {0, 0, 0, 0.9};
555   static GLfloat lpos[] = {0, 0, 0, 1};
556   static GLfloat d = 0, wd;
557   int flameOut = 720/ENG.speed/ENG.cylinders;
558   static int time = 0;
559
560   if (time == 0 && s) {
561     red[0] = red[1] = 0;
562     d = 0.05;
563     time++;
564     glEnable(GL_LIGHT1); 
565   } else if (time == 0 && !s) {
566     return;
567   } else if (time >= 8 && time < flameOut && !s) {
568     time++;
569     red[0] -= 0.2; red[1] -= 0.1;
570     d-= 0.04;
571   } else if (time >= flameOut) {
572     time = 0;
573     glDisable(GL_LIGHT1);
574     return;
575   } else {
576     red[0] += 0.2; red[1] += 0.1;
577     d+= 0.04;
578     time++;
579   }
580   lpos[0] = x-d; lpos[1] = y;
581   glLightfv(GL_LIGHT1, GL_POSITION, lpos);
582   glLightfv(GL_LIGHT1, GL_DIFFUSE, red);
583   glLightfv(GL_LIGHT1, GL_SPECULAR, red);
584   glLighti(GL_LIGHT1, GL_LINEAR_ATTENUATION, 1.3);
585   glLighti(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 0);
586
587   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
588   wd = d*3;
589   if (wd > 0.7) wd = 0.7;
590   glEnable(GL_BLEND);
591   glDepthMask(GL_FALSE);
592   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
593   rod(x, y, 0, d, wd);
594   glDepthMask(GL_TRUE);
595   glDisable(GL_BLEND);
596 }
597
598 void display(Engine *e)
599 {
600   static int a = 0;
601   GLfloat zb, yb;
602   static GLfloat ln[730], yp[730], ang[730];
603   static int ln_init = 0;
604   static int lastPlug = 0;
605   int half;
606   int sides;
607   int j, b;
608   static float rightSide;
609
610   glEnable(GL_LIGHTING);
611   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
612   glLoadIdentity();
613   gluLookAt(viewer[0], viewer[1], viewer[2], lookat[0], lookat[1], lookat[2], 
614         0.0, 1.0, 0.0);
615   glPushMatrix();
616   glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
617   glLightfv(GL_LIGHT0, GL_SPECULAR, light_sp);
618   glLightfv(GL_LIGHT0, GL_DIFFUSE, light_sp);
619
620   if (move) {
621     double x, y, z;
622     get_position (e->rot, &x, &y, &z, !e->button_down_p);
623     glTranslatef(x*16-9, y*14-7, z*16-10);
624   }
625   if (spin) {
626     double x, y, z;
627     gltrackball_rotate (e->trackball);
628     get_rotation(e->rot, &x, &y, &z, !e->button_down_p);
629     glRotatef(x*ONEREV, 1.0, 0.0, 0.0);
630     glRotatef(y*ONEREV, 0.0, 1.0, 0.0);
631     glRotatef(x*ONEREV, 0.0, 0.0, 1.0);
632   }
633
634 /* So the rotation appears around the centre of the engine */
635   glTranslatef(-5, 0, 0); 
636
637 /* crankshaft */
638   glPushMatrix();
639   glRotatef(a, 1, 0, 0);
640   glCallList(1);
641   glPopMatrix();
642
643   /* init the ln[] matrix for speed */
644   if (ln_init == 0) {
645     for (ln_init = 0 ; ln_init < 730 ; ln_init++) {
646       zb = sin_table[ln_init];
647       yb = cos_table[ln_init];
648       /* y ordinate of piston */
649       yp[ln_init] = yb + sqrt(25 - (zb*zb)); 
650       /* length of rod */
651       ln[ln_init] = sqrt(zb*zb + (yb-yp[ln_init])*(yb-yp[ln_init])); 
652       /* angle of connecting rod */
653       ang[ln_init] = asin(zb/5)*57; 
654       ang[ln_init] *= -1;
655     }
656   }
657
658   glPushMatrix();
659   sides = (ENG.includedAngle == 0) ? 1 : 2;
660   for (half = 0; half < sides; half++, glRotatef(ENG.includedAngle,1,0,0))
661   {
662     /* pistons */
663       /* glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white); */
664       for (j = 0; j < ENG.cylinders; j += sides)
665       {
666         b = (a + ENG.pistonAngle[j+half]) % ONEREV;
667         glPushMatrix();
668         glTranslatef(crankWidth/2 + crankOffset*(j+half), yp[b]-0.3, 0);
669         glCallList(2);
670         glPopMatrix();
671       }
672     /* spark plugs */
673       glPushMatrix();
674       glRotatef(90, 0, 0, 1); 
675       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
676       for (j = 0; j < ENG.cylinders; j += sides) 
677       {
678         cylinder(8.5, -crankWidth/2-crankOffset*(j+half), 0, 
679             0.5, 0.4, 0.3, 1, 0, ONEREV); 
680       }
681       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white);
682       for (j = 0; j < ENG.cylinders; j += sides)
683       {
684         rod(8, -crankWidth/2-crankOffset*(j+half), 0, 0.5, 0.2); 
685         rod(9, -crankWidth/2-crankOffset*(j+half), 0, 1, 0.15); 
686       }   
687
688      /* rod */
689       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
690       for (j = 0; j < ENG.cylinders; j += sides)
691       {
692           b = (a+HALFREV+ENG.pistonAngle[j+half]) % TWOREV; 
693           glPushMatrix();
694           glRotatef(ang[b], 0, 1, 0);
695           rod(-cos_table[b],
696               -crankWidth/2-crankOffset*(j+half),
697               -sin_table[b], 
698               ln[b], 0.2);
699           glPopMatrix();
700       }
701       glPopMatrix();
702
703         /* engine block */
704       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellow_t);
705       glEnable(GL_BLEND);
706       glDepthMask(GL_FALSE);
707       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
708       rightSide = (sides > 1) ? 0 : 1.6;
709     /* left plate */
710       Rect(-crankWidth/2, -0.5,  1, 0.2, 9, 2);
711     /* right plate */
712       Rect(0.3+crankOffset*ENG.cylinders-rightSide, -0.5, 1, 0.2, 9, 2);
713     /* head plate */
714       Rect(-crankWidth/2+0.2, 8.3, 1, 
715             crankWidth/2+0.1+crankOffset*ENG.cylinders-rightSide, 0.2, 2);
716     /* front rail */
717       Rect(-crankWidth/2+0.2, 3, 1, 
718             crankWidth/2+0.1+crankOffset*ENG.cylinders-rightSide, 0.2, 0.2);
719     /* back rail */
720       Rect(-crankWidth/2+0.2, 3, -1+0.2, 
721             crankWidth/2+0.1+crankOffset*ENG.cylinders-rightSide, 0.2, 0.2);
722     /* plates between cylinders */
723       for (j=0; j < ENG.cylinders - (sides == 1); j += sides)
724         Rect(0.4+crankWidth+crankOffset*(j-half), 3, 1, 1, 5.3, 2);
725       glDepthMask(GL_TRUE);
726   }
727   glPopMatrix();
728
729         /* see which of our plugs should fire now, if any */
730   for (j = 0; j < ENG.cylinders; j++)
731   {
732     if (0 == ((a + ENG.pistonAngle[j]) % TWOREV))
733     {
734         glPushMatrix();
735         if (j & 1) 
736             glRotatef(ENG.includedAngle,1,0,0);
737         glRotatef(90, 0, 0, 1); 
738         boom(8, -crankWidth/2-crankOffset*j, 1); 
739         lastPlug = j;
740         glPopMatrix();
741     }
742   }
743
744   if (lastPlug != j)
745   {
746     /* this code causes the last plug explosion to dim gradually */
747     if (lastPlug & 1) 
748       glRotatef(ENG.includedAngle, 1, 0, 0);
749     glRotatef(90, 0, 0, 1); 
750     boom(8, -crankWidth/2-crankOffset*lastPlug, 0); 
751   }
752   glDisable(GL_BLEND);
753
754   a += ENG.speed; 
755   if (a >= TWOREV) 
756     a = 0;
757   glPopMatrix();
758   glFlush();
759 }
760
761 void makeshaft (void)
762 {
763   int i;
764   int j;
765   static const float crankThick = 0.2;
766   static const float crankDiam = 0.3;
767
768   i = glGenLists(1);
769   glNewList(i, GL_COMPILE);
770
771   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
772   /* draw the flywheel */
773   cylinder(-2.5, 0, 0, 1, 3, 2.5, 0, 0, ONEREV);
774   Rect(-2, -0.3, 2.8, 0.5, 0.6, 5.6);
775   Rect(-2, -2.8, 0.3, 0.5, 5.6, 0.6);
776
777   /* now make each of the shaft bits between the cranks, 
778    * starting from the flywheel end which is at X-coord 0. 
779    * the first cranskhaft bit is always 2 units long 
780    */
781   rod(-2, 0, 0, 2, crankDiam);
782
783   /* Each crank is crankWidth units wide and the total width of a
784    * cylinder assembly is 3.3 units. For inline engines, there is just
785    * a single crank per cylinder width.  For other engine
786    * configurations, there is a crank between each pair of adjacent
787    * cylinders on one side of the engine, so the crankOffset length is
788    * halved.
789    */
790   crankOffset = 3.3;
791   if (ENG.includedAngle != 0)
792     crankOffset /= 2;
793   for (j = 0; j < ENG.cylinders - 1; j++)
794     rod(crankWidth - crankThick + crankOffset*j, 0, 0, 
795         crankOffset - crankWidth + 2 * crankThick, crankDiam);
796   /* the last bit connects to the engine wall on the non-flywheel end */
797   rod(crankWidth - crankThick + crankOffset*j, 0, 0, 0.9, crankDiam);
798
799
800   for (j = 0; j < ENG.cylinders; j++)
801   {
802     glPushMatrix();
803     if (j & 1)
804         glRotatef(HALFREV+ENG.pistonAngle[j]+ENG.includedAngle,1,0,0);
805     else
806         glRotatef(HALFREV+ENG.pistonAngle[j],1,0,0);
807     /* draw wrist pin */
808     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
809     rod(crankOffset*j, -1.0, 0.0, crankWidth, crankDiam);
810     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
811     /* draw right part of crank */
812     CrankBit(crankOffset*j); 
813     /* draw left part of crank */
814     CrankBit(crankWidth-crankThick+crankOffset*j);
815     glPopMatrix();
816   }
817   glEndList();
818 }
819
820 static void
821 load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP)
822 {
823   const char *font = get_string_resource (res, "Font");
824   XFontStruct *f;
825   Font id;
826   int first, last;
827
828   if (!font) font = "-*-times-bold-r-normal-*-180-*";
829
830   f = XLoadQueryFont(mi->dpy, font);
831   if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
832
833   id = f->fid;
834   first = f->min_char_or_byte2;
835   last = f->max_char_or_byte2;
836   
837   clear_gl_error ();
838   *dlistP = glGenLists ((GLuint) last+1);
839   check_gl_error ("glGenLists");
840   glXUseXFont(id, first, last-first+1, *dlistP + first);
841   check_gl_error ("glXUseXFont");
842
843   *fontP = f;
844 }
845
846
847 static void
848 print_title_string (ModeInfo *mi, const char *string, GLfloat x, GLfloat y)
849 {
850   Engine *e = &engine[MI_SCREEN(mi)];
851   XFontStruct *font = e->xfont;
852   GLfloat line_height = font->ascent + font->descent;
853
854   y -= line_height;
855
856   glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
857                 GL_ENABLE_BIT);     /* for various glDisable calls */
858   glDisable (GL_LIGHTING);
859   glDisable (GL_DEPTH_TEST);
860   {
861     glMatrixMode(GL_PROJECTION);
862     glPushMatrix();
863     {
864       glLoadIdentity();
865
866       glMatrixMode(GL_MODELVIEW);
867       glPushMatrix();
868       {
869         unsigned int i;
870         int x2 = x;
871         glLoadIdentity();
872
873         gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
874
875         glRasterPos2f (x, y);
876         for (i = 0; i < strlen(string); i++)
877           {
878             char c = string[i];
879             if (c == '\n')
880               {
881                 glRasterPos2f (x, (y -= line_height));
882                 x2 = x;
883               }
884             else
885               {
886                 glCallList (e->font_dlist + (int)(c));
887                 x2 += (font->per_char
888                        ? font->per_char[c - font->min_char_or_byte2].width
889                        : font->min_bounds.width);
890               }
891           }
892       }
893       glPopMatrix();
894     }
895     glMatrixMode(GL_PROJECTION);
896     glPopMatrix();
897   }
898   glPopAttrib();
899
900   glMatrixMode(GL_MODELVIEW);
901 }
902
903
904
905 void reshape_engine(ModeInfo *mi, int width, int height)
906 {
907  glViewport(0,0,(GLint)width, (GLint) height);
908  glMatrixMode(GL_PROJECTION);
909  glLoadIdentity();
910  glFrustum(-1.0,1.0,-1.0,1.0,1.5,70.0);
911  glMatrixMode(GL_MODELVIEW);
912  win_h = height; win_w = width;
913 }
914
915
916 void init_engine(ModeInfo *mi)
917 {
918   int screen = MI_SCREEN(mi);
919   Engine *e;
920
921  if (engine == NULL) {
922    if ((engine = (Engine *) calloc(MI_NUM_SCREENS(mi),
923                                         sizeof(Engine))) == NULL)
924           return;
925  }
926  e = &engine[screen];
927  e->window = MI_WINDOW(mi);
928
929  e->x = e->y = e->z = e->a = e->an1 = e->nx = e->ny = e->nz = 
930  e->dx = e->dy = e->dz = e->da = 0;
931
932  if (move) {
933    e->dx = (float)(random() % 1000)/30000;
934    e->dy = (float)(random() % 1000)/30000;
935    e->dz = (float)(random() % 1000)/30000;
936  } else {
937   viewer[0] = 0; viewer[1] = 2; viewer[2] = 18;
938   lookat[0] = 0; lookat[1] = 0; lookat[2] = 0; 
939
940  }
941  if (spin) {
942    e->da = (float)(random() % 1000)/125 - 4;
943    e->nx = (float)(random() % 100) / 100;
944    e->ny = (float)(random() % 100) / 100;
945    e->nz = (float)(random() % 100) / 100;
946  }
947
948  {
949    double spin_speed = 1.0;
950    double wander_speed = 0.03;
951
952  e->rot = make_rotator (spin ? spin_speed : 0,
953                         spin ? spin_speed : 0,
954                         spin ? spin_speed : 0,
955                         1.0,
956                         move ? wander_speed : 0,
957                         True);
958
959     e->trackball = gltrackball_init ();
960  }
961
962  if ((e->glx_context = init_GL(mi)) != NULL) {
963       reshape_engine(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
964  } else {
965      MI_CLEARWINDOW(mi);
966  }
967  glClearColor(0.0,0.0,0.0,0.0);
968  glShadeModel(GL_SMOOTH);
969  glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
970  glEnable(GL_DEPTH_TEST);
971  glEnable(GL_LIGHTING);
972  glEnable(GL_LIGHT0);
973  glEnable(GL_NORMALIZE);
974  make_tables();
975  engineType = find_engine(which_engine);
976
977  e->engine_name = malloc(200);
978  sprintf (e->engine_name,
979           "%s\n%s%d%s",
980           engines[engineType].engineName,
981           (engines[engineType].includedAngle == 0 ? "" :
982            engines[engineType].includedAngle == 180 ? "Flat " : "V"),
983           engines[engineType].cylinders,
984           (engines[engineType].includedAngle == 0 ? " Cylinder" : "")
985           );
986
987  makeshaft();
988  makepiston();
989  load_font (mi, "titleFont", &e->xfont, &e->font_dlist);
990 }
991
992 Bool engine_handle_event (ModeInfo *mi, XEvent *event)
993 {
994    Engine *e = &engine[MI_SCREEN(mi)];
995
996    if (event->xany.type == ButtonPress &&
997        event->xbutton.button & Button1)
998    {
999        e->button_down_p = True;
1000        gltrackball_start (e->trackball,
1001                           event->xbutton.x, event->xbutton.y,
1002                           MI_WIDTH (mi), MI_HEIGHT (mi));
1003        movepaused = 1;
1004        return True;
1005    }
1006    else if (event->xany.type == ButtonRelease &&
1007             event->xbutton.button & Button1) {
1008        e->button_down_p = False;
1009        movepaused = 0;
1010        return True;
1011    }
1012    else if (event->xany.type == MotionNotify &&
1013             e->button_down_p) {
1014       gltrackball_track (e->trackball,
1015                          event->xmotion.x, event->xmotion.y,
1016                          MI_WIDTH (mi), MI_HEIGHT (mi));
1017       return True;
1018    }
1019   return False;
1020 }
1021
1022 void draw_engine(ModeInfo *mi)
1023 {
1024   Engine *e = &engine[MI_SCREEN(mi)];
1025   Window w = MI_WINDOW(mi);
1026   Display *disp = MI_DISPLAY(mi);
1027
1028   if (!e->glx_context)
1029     return;
1030
1031   glXMakeCurrent(disp, w, *(e->glx_context));
1032
1033
1034   display(e);
1035
1036   if (do_titles)
1037     print_title_string (mi, e->engine_name,
1038                         10, mi->xgwa.height - 10);
1039
1040   if(mi->fps_p) do_fps(mi);
1041   glFinish(); 
1042   glXSwapBuffers(disp, w);
1043 }
1044
1045 void release_engine(ModeInfo *mi) 
1046 {
1047   if (engine != NULL) {
1048    (void) free((void *) engine);
1049    engine = NULL;
1050   }
1051   FreeAllGL(MI);
1052 }
1053
1054 #endif