1 /**
2  * Simple engine demo (crankshaft, pistons, connecting rods)
3  *
4  * Brian Paul
5  * June 2006
6  */
7 
8 #include <assert.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <math.h>
12 #include <glad/glad.h>
13 #include "glut_wrap.h"
14 #include "readtex.h"
15 #include "trackball.h"
16 
17 
18 #ifndef M_PI
19 #define M_PI 3.14159265358979323846
20 #endif
21 
22 #define DEG_TO_RAD(DEG)  ((DEG) * M_PI / 180.0)
23 
24 #define TEXTURE_FILE DEMOS_DATA_DIR "reflect.rgb"
25 
26 /* Target engine speed: */
27 const int RPM = 100.0;
28 
29 static int Win = 0;
30 
31 
32 /**
33  * Engine description.
34  */
35 typedef struct
36 {
37    const char *Name;
38    int Pistons;
39    int Cranks;
40    float V_Angle;
41    float PistonRadius;
42    float PistonHeight;
43    float WristPinRadius;
44    float Throw;
45    float CrankPlateThickness;
46    float CrankPinRadius;
47    float CrankJournalRadius;
48    float CrankJournalLength;
49    float ConnectingRodLength;
50    float ConnectingRodThickness;
51    /* display list IDs */
52    GLuint CrankList;
53    GLuint ConnRodList;
54    GLuint PistonList;
55    GLuint BlockList;
56 } Engine;
57 
58 
59 typedef struct
60 {
61    float CurQuat[4];
62    float Distance;
63    /* When mouse is moving: */
64    GLboolean Rotating, Translating;
65    GLint StartX, StartY;
66    float StartDistance;
67 } ViewInfo;
68 
69 
70 typedef enum
71 {
72    LIT,
73    WIREFRAME,
74    TEXTURED
75 } RenderMode;
76 
77 
78 typedef struct
79 {
80    RenderMode Mode;
81    GLboolean Anim;
82    GLboolean Wireframe;
83    GLboolean Blend;
84    GLboolean Antialias;
85    GLboolean Texture;
86    GLboolean UseLists;
87    GLboolean DrawBox;
88    GLboolean ShowInfo;
89    GLboolean ShowBlock;
90 } RenderInfo;
91 
92 
93 static GLUquadric *Q;
94 
95 static GLfloat Theta = 0.0;
96 
97 static const GLfloat PistonColor[4] = { 1.0, 0.5, 0.5, 1.0 };
98 static const GLfloat ConnRodColor[4] = { 0.7, 1.0, 0.7, 1.0 };
99 static const GLfloat CrankshaftColor[4] = { 0.7, 0.7, 1.0, 1.0 };
100 static const GLfloat BlockColor[4] = {0.8, 0.8, 0.8, 0.75 };
101 
102 static GLuint TextureObj;
103 static GLint WinWidth = 800, WinHeight = 500;
104 
105 static ViewInfo View;
106 static RenderInfo Render;
107 
108 #define NUM_ENGINES 3
109 static Engine Engines[NUM_ENGINES] =
110 {
111    {
112       "V-6",
113       6,    /* Pistons */
114       3,    /* Cranks */
115       90.0, /* V_Angle */
116       0.5,  /* PistonRadius */
117       0.6,  /* PistonHeight */
118       0.1,  /* WristPinRadius */
119       0.5,  /* Throw */
120       0.2,  /* CrankPlateThickness */
121       0.25, /* CrankPinRadius */
122       0.3,  /* CrankJournalRadius */
123       0.4,  /* CrankJournalLength */
124       1.5,  /* ConnectingRodLength */
125       0.1,  /* ConnectingRodThickness */
126       0,    /* CrankList */
127       0,    /* ConnRodList */
128       0,    /* PistonList */
129       0     /* BlockList */
130    },
131    {
132       "Inline-4",
133       4,    /* Pistons */
134       4,    /* Cranks */
135       0.0,  /* V_Angle */
136       0.5,  /* PistonRadius */
137       0.6,  /* PistonHeight */
138       0.1,  /* WristPinRadius */
139       0.5,  /* Throw */
140       0.2,  /* CrankPlateThickness */
141       0.25, /* CrankPinRadius */
142       0.3,  /* CrankJournalRadius */
143       0.4,  /* CrankJournalLength */
144       1.5,  /* ConnectingRodLength */
145       0.1,  /* ConnectingRodThickness */
146       0,    /* CrankList */
147       0,    /* ConnRodList */
148       0,    /* PistonList */
149       0     /* BlockList */
150    },
151    {
152       "Boxer-6",
153       6,    /* Pistons */
154       3,    /* Cranks */
155       180.0,/* V_Angle */
156       0.5,  /* PistonRadius */
157       0.6,  /* PistonHeight */
158       0.1,  /* WristPinRadius */
159       0.5,  /* Throw */
160       0.2,  /* CrankPlateThickness */
161       0.25, /* CrankPinRadius */
162       0.3,  /* CrankJournalRadius */
163       0.4,  /* CrankJournalLength */
164       1.5,  /* ConnectingRodLength */
165       0.1,  /* ConnectingRodThickness */
166       0,    /* CrankList */
167       0,    /* ConnRodList */
168       0,    /* PistonList */
169       0     /* BlockList */
170    }
171 };
172 
173 static int CurEngine = 0;
174 
175 
176 
177 static void
InitViewInfo(ViewInfo * view)178 InitViewInfo(ViewInfo *view)
179 {
180    view->Rotating = GL_FALSE;
181    view->Translating = GL_FALSE;
182    view->StartX = view->StartY = 0;
183    view->Distance = 12.0;
184    view->StartDistance = 0.0;
185    view->CurQuat[0] = -0.194143;
186    view->CurQuat[1] = 0.507848;
187    view->CurQuat[2] = 0.115245;
188    view->CurQuat[3] = 0.831335;
189 }
190 
191 
192 static void
InitRenderInfo(RenderInfo * render)193 InitRenderInfo(RenderInfo *render)
194 {
195    render->Mode = LIT;
196    render->Anim = GL_TRUE;
197    render->Wireframe = GL_FALSE;
198    render->Blend = GL_FALSE;
199    render->Antialias = GL_FALSE;
200    render->Texture = GL_FALSE;
201    render->DrawBox = GL_FALSE;
202    render->ShowInfo = GL_TRUE;
203    render->ShowBlock = GL_FALSE;
204    render->UseLists = GL_FALSE;
205 }
206 
207 
208 /**
209  * Set GL for given rendering mode.
210  */
211 static void
SetRenderState(RenderMode mode)212 SetRenderState(RenderMode mode)
213 {
214    static const GLfloat gray2[4] = { 0.2, 0.2, 0.2, 1.0 };
215    static const GLfloat gray4[4] = { 0.4, 0.4, 0.4, 1.0 };
216 
217    /* defaults */
218    glDisable(GL_LIGHTING);
219    glDisable(GL_TEXTURE_2D);
220    glDisable(GL_BLEND);
221    glDisable(GL_LINE_SMOOTH);
222    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
223    glDisable(GL_TEXTURE_GEN_S);
224    glDisable(GL_TEXTURE_GEN_T);
225    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, gray2);
226 
227    switch (mode) {
228    case LIT:
229       glEnable(GL_LIGHTING);
230       break;
231    case WIREFRAME:
232       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
233       glEnable(GL_LINE_SMOOTH);
234       glEnable(GL_BLEND);
235       glLineWidth(1.5);
236       break;
237    case TEXTURED:
238       glEnable(GL_LIGHTING);
239       glEnable(GL_TEXTURE_2D);
240       glEnable(GL_TEXTURE_GEN_S);
241       glEnable(GL_TEXTURE_GEN_T);
242       glLightModelfv(GL_LIGHT_MODEL_AMBIENT, gray4);
243       break;
244    default:
245       ;
246    }
247 }
248 
249 
250 /**
251  * Animate the engine parts.
252  */
253 static void
Idle(void)254 Idle(void)
255 {
256    /* convert degrees per millisecond to RPM: */
257    const float m = 360.0 / 1000.0 / 60.0;
258    GLint t = glutGet(GLUT_ELAPSED_TIME);
259    Theta = ((int) (t * RPM * m)) % 360;
260    glutPostRedisplay();
261 }
262 
263 
264 /**
265  * Compute piston's position along its stroke.
266  */
267 static float
PistonStrokePosition(float throwDist,float crankAngle,float connRodLength)268 PistonStrokePosition(float throwDist, float crankAngle, float connRodLength)
269 {
270    float x = throwDist * cos(DEG_TO_RAD(crankAngle));
271    float y = throwDist * sin(DEG_TO_RAD(crankAngle));
272    float pos = y + sqrt(connRodLength * connRodLength - x * x);
273    return pos;
274 }
275 
276 
277 /**
278  * Compute position of nth piston along the crankshaft.
279  */
280 static float
PistonShaftPosition(const Engine * eng,int piston)281 PistonShaftPosition(const Engine *eng, int piston)
282 {
283    const int i = piston / (eng->Pistons / eng->Cranks);
284    float z;
285    assert(piston < eng->Pistons);
286    z = 1.5 * eng->CrankJournalLength + eng->CrankPlateThickness
287       + i * (2.0 * (eng->CrankJournalLength + eng->CrankPlateThickness));
288    if (eng->Pistons > eng->Cranks) {
289       if (piston & 1)
290          z += eng->ConnectingRodThickness;
291       else
292          z -= eng->ConnectingRodThickness;
293    }
294    return z;
295 }
296 
297 
298 /**
299  * Compute distance between two adjacent pistons
300  */
301 static float
PistonSpacing(const Engine * eng)302 PistonSpacing(const Engine *eng)
303 {
304    const int pistonsPerCrank = eng->Pistons / eng->Cranks;
305    const float z0 = PistonShaftPosition(eng, 0);
306    const float z1 = PistonShaftPosition(eng, pistonsPerCrank);
307    return z1 - z0;
308 }
309 
310 
311 /**
312  * (x0, y0) = position of big end on crankshaft
313  * (x1, y1) = position of small end on piston
314  */
315 static void
ComputeConnectingRodPosition(float throwDist,float crankAngle,float connRodLength,float * x0,float * y0,float * x1,float * y1)316 ComputeConnectingRodPosition(float throwDist, float crankAngle,
317                              float connRodLength,
318                              float *x0, float *y0, float *x1, float *y1)
319 {
320    *x0 = throwDist * cos(DEG_TO_RAD(crankAngle));
321    *y0 = throwDist * sin(DEG_TO_RAD(crankAngle));
322    *x1 = 0.0;
323    *y1 = PistonStrokePosition(throwDist, crankAngle, connRodLength);
324 }
325 
326 
327 /**
328  * Compute total length of the crankshaft.
329  */
330 static float
CrankshaftLength(const Engine * eng)331 CrankshaftLength(const Engine *eng)
332 {
333    float len = (eng->Cranks * 2 + 1) * eng->CrankJournalLength
334              + 2 * eng->Cranks * eng->CrankPlateThickness;
335    return len;
336 }
337 
338 
339 /**
340  * Draw a piston.
341  * Axis of piston = Z axis.  Wrist pin is centered on (0, 0, 0).
342  */
343 static void
DrawPiston(const Engine * eng)344 DrawPiston(const Engine *eng)
345 {
346    const int slices = 30, stacks = 4, loops = 4;
347    const float innerRadius = 0.9 * eng->PistonRadius;
348    const float innerHeight = eng->PistonHeight - 0.15;
349    const float wristPinLength = 1.8 * eng->PistonRadius;
350 
351    assert(Q);
352 
353    glPushMatrix();
354    glTranslatef(0, 0, -1.1 * eng->WristPinRadius);
355 
356    gluQuadricOrientation(Q, GLU_INSIDE);
357 
358    /* bottom rim */
359    gluDisk(Q, innerRadius, eng->PistonRadius, slices, 1/*loops*/);
360 
361    /* inner cylinder */
362    gluCylinder(Q, innerRadius, innerRadius, innerHeight, slices, stacks);
363 
364    /* inside top */
365    glPushMatrix();
366    glTranslatef(0, 0, innerHeight);
367    gluDisk(Q, 0, innerRadius, slices, loops);
368    glPopMatrix();
369 
370    gluQuadricOrientation(Q, GLU_OUTSIDE);
371 
372    /* outer cylinder */
373    gluCylinder(Q, eng->PistonRadius, eng->PistonRadius, eng->PistonHeight,
374                slices, stacks);
375 
376    /* top */
377    glTranslatef(0, 0, eng->PistonHeight);
378    gluDisk(Q, 0, eng->PistonRadius, slices, loops);
379 
380    glPopMatrix();
381 
382    /* wrist pin */
383    glPushMatrix();
384    glTranslatef(0, 0.5 * wristPinLength, 0.0);
385    glRotatef(90, 1, 0, 0);
386    gluCylinder(Q, eng->WristPinRadius, eng->WristPinRadius, wristPinLength,
387                slices, stacks);
388    glPopMatrix();
389 }
390 
391 
392 /**
393  * Draw piston at particular position.
394  */
395 static void
DrawPositionedPiston(const Engine * eng,float crankAngle)396 DrawPositionedPiston(const Engine *eng, float crankAngle)
397 {
398    const float pos = PistonStrokePosition(eng->Throw, crankAngle,
399                                           eng->ConnectingRodLength);
400    glPushMatrix();
401       glRotatef(-90, 1, 0, 0);
402       glTranslatef(0, 0, pos);
403       if (eng->PistonList)
404          glCallList(eng->PistonList);
405       else
406          DrawPiston(eng);
407    glPopMatrix();
408 }
409 
410 
411 /**
412  * Draw connector plate.  Used for crankshaft and connecting rods.
413  */
414 static void
DrawConnector(float length,float thickness,float bigEndRadius,float smallEndRadius)415 DrawConnector(float length, float thickness,
416               float bigEndRadius, float smallEndRadius)
417 {
418    const float bigRadius = 1.2 * bigEndRadius;
419    const float smallRadius = 1.2 * smallEndRadius;
420    const float z0 = -0.5 * thickness, z1 = -z0;
421    GLfloat points[36][2], normals[36][2];
422    int i;
423 
424    /* compute vertex locations, normals */
425    for (i = 0; i < 36; i++) {
426       const int angle = i * 10;
427       float x = cos(DEG_TO_RAD(angle));
428       float y = sin(DEG_TO_RAD(angle));
429       normals[i][0] = x;
430       normals[i][1] = y;
431       if (angle >= 0 && angle <= 180) {
432          x *= smallRadius;
433          y = y * smallRadius + length;
434       }
435       else {
436          x *= bigRadius;
437          y *= bigRadius;
438       }
439       points[i][0] = x;
440       points[i][1] = y;
441    }
442 
443    /* front face */
444    glNormal3f(0, 0, 1);
445    glBegin(GL_POLYGON);
446    for (i = 0; i < 36; i++) {
447       glVertex3f(points[i][0], points[i][1], z1);
448    }
449    glEnd();
450 
451    /* back face */
452    glNormal3f(0, 0, -1);
453    glBegin(GL_POLYGON);
454    for (i = 0; i < 36; i++) {
455       glVertex3f(points[35-i][0], points[35-i][1], z0);
456    }
457    glEnd();
458 
459    /* edge */
460    glBegin(GL_QUAD_STRIP);
461    for (i = 0; i <= 36; i++) {
462       const int j = i % 36;
463       glNormal3f(normals[j][0], normals[j][1], 0);
464       glVertex3f(points[j][0], points[j][1], z1);
465       glVertex3f(points[j][0], points[j][1], z0);
466    }
467    glEnd();
468 }
469 
470 
471 /**
472  * Draw a crankshaft.  Shaft lies along +Z axis, starting at zero.
473  */
474 static void
DrawCrankshaft(const Engine * eng)475 DrawCrankshaft(const Engine *eng)
476 {
477    const int slices = 20, stacks = 2;
478    const int n = eng->Cranks * 4 + 1;
479    const float phiStep = 360 / eng->Cranks;
480    float phi = -90.0;
481    int i;
482    float z = 0.0;
483 
484    for (i = 0; i < n; i++) {
485       glPushMatrix();
486       glTranslatef(0, 0, z);
487       if (i & 1) {
488          /* draw a crank plate */
489          glRotatef(phi, 0, 0, 1);
490          glTranslatef(0, 0, 0.5 * eng->CrankPlateThickness);
491          DrawConnector(eng->Throw, eng->CrankPlateThickness,
492                        eng->CrankJournalRadius, eng->CrankPinRadius);
493          z += 0.2;
494          if (i % 4 == 3)
495             phi += phiStep;
496       }
497       else if (i % 4 == 0) {
498          /* draw crank journal segment */
499          gluCylinder(Q, eng->CrankJournalRadius, eng->CrankJournalRadius,
500                      eng->CrankJournalLength, slices, stacks);
501          z += eng->CrankJournalLength;
502       }
503       else if (i % 4 == 2) {
504          /* draw crank pin segment */
505          glRotatef(phi, 0, 0, 1);
506          glTranslatef(0, eng->Throw, 0);
507          gluCylinder(Q, eng->CrankPinRadius, eng->CrankPinRadius,
508                      eng->CrankJournalLength, slices, stacks);
509          z += eng->CrankJournalLength;
510       }
511       glPopMatrix();
512    }
513 }
514 
515 
516 /**
517  * Draw crankshaft at a particular rotation.
518  * \param crankAngle  current crankshaft rotation, in radians
519  */
520 static void
DrawPositionedCrankshaft(const Engine * eng,float crankAngle)521 DrawPositionedCrankshaft(const Engine *eng, float crankAngle)
522 {
523    glPushMatrix();
524       glRotatef(crankAngle, 0, 0, 1);
525       if (eng->CrankList)
526          glCallList(eng->CrankList);
527       else
528          DrawCrankshaft(eng);
529    glPopMatrix();
530 }
531 
532 
533 /**
534  * Draw a connecting rod at particular position.
535  * \param eng  description of connecting rod to draw
536  * \param crankAngle  current crankshaft rotation, in radians
537  */
538 static void
DrawPositionedConnectingRod(const Engine * eng,float crankAngle)539 DrawPositionedConnectingRod(const Engine *eng, float crankAngle)
540 {
541    float x0, y0, x1, y1;
542    float d, phi;
543 
544    ComputeConnectingRodPosition(eng->Throw, crankAngle,
545                                 eng->ConnectingRodLength,
546                                 &x0, &y0, &x1, &y1);
547    d = sqrt(eng->ConnectingRodLength * eng->ConnectingRodLength - x0 * x0);
548    phi = atan(x0 / d) * 180.0 / M_PI;
549 
550    glPushMatrix();
551       glTranslatef(x0, y0, 0);
552       glRotatef(phi, 0, 0, 1);
553       if (eng->ConnRodList)
554          glCallList(eng->ConnRodList);
555       else
556          DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness,
557                        eng->CrankPinRadius, eng->WristPinRadius);
558    glPopMatrix();
559 }
560 
561 
562 /**
563  * Draw a square with a hole in middle.
564  */
565 static void
SquareWithHole(float squareSize,float holeRadius)566 SquareWithHole(float squareSize, float holeRadius)
567 {
568    int i;
569    glBegin(GL_QUAD_STRIP);
570    glNormal3f(0, 0, 1);
571    for (i = 0; i <= 360; i += 5) {
572       const float x1 = holeRadius * cos(DEG_TO_RAD(i));
573       const float y1 = holeRadius * sin(DEG_TO_RAD(i));
574       float x2 = 0.0F, y2 = 0.0F;
575       if (i > 315 || i <= 45) {
576          x2 = squareSize;
577          y2 = squareSize * tan(DEG_TO_RAD(i));
578       }
579       else if (i > 45 && i <= 135) {
580          x2 = -squareSize * tan(DEG_TO_RAD(i - 90));
581          y2 = squareSize;
582       }
583       else if (i > 135 && i <= 225) {
584          x2 = -squareSize;
585          y2 = -squareSize * tan(DEG_TO_RAD(i-180));
586       }
587       else if (i > 225 && i <= 315) {
588          x2 = squareSize * tan(DEG_TO_RAD(i - 270));
589          y2 = -squareSize;
590       }
591       glVertex2f(x1, y1); /* inner circle */
592       glVertex2f(x2, y2); /* outer square */
593    }
594    glEnd();
595 }
596 
597 
598 /**
599  * Draw block with hole through middle.
600  * Hole is centered on Z axis.
601  * Bottom of block is at z=0, top of block is at z = blockHeight.
602  * index is in [0, count - 1] to determine which block faces are drawn.
603  */
604 static void
DrawBlockWithHole(float blockSize,float blockHeight,float holeRadius,int index,int count)605 DrawBlockWithHole(float blockSize, float blockHeight, float holeRadius,
606                   int index, int count)
607 {
608    const int slices = 30, stacks = 4;
609    const float x = blockSize;
610    const float y = blockSize;
611    const float z0 = 0;
612    const float z1 = blockHeight;
613 
614    assert(index < count);
615    assert(Q);
616    gluQuadricOrientation(Q, GLU_INSIDE);
617 
618    glBegin(GL_QUADS);
619    /* +X face */
620    glNormal3f(1, 0, 0);
621    glVertex3f( x, -y, z0);
622    glVertex3f( x, y, z0);
623    glVertex3f( x, y, z1);
624    glVertex3f( x, -y, z1);
625    /* -X face */
626    glNormal3f(-1, 0, 0);
627    glVertex3f(-x, -y, z1);
628    glVertex3f(-x, y, z1);
629    glVertex3f(-x, y, z0);
630    glVertex3f(-x, -y, z0);
631    if (index == 0) {
632       /* +Y face */
633       glNormal3f(0, 1, 0);
634       glVertex3f(-x, y, z1);
635       glVertex3f( x, y, z1);
636       glVertex3f( x, y, z0);
637       glVertex3f(-x, y, z0);
638    }
639    if (index == count - 1) {
640       /* -Y face */
641       glNormal3f(0, -1, 0);
642       glVertex3f(-x, -y, z0);
643       glVertex3f( x, -y, z0);
644       glVertex3f( x, -y, z1);
645       glVertex3f(-x, -y, z1);
646    }
647    glEnd();
648 
649    /* cylinder / hole */
650    gluCylinder(Q, holeRadius, holeRadius, blockHeight, slices, stacks);
651 
652    /* face at z0 */
653    glPushMatrix();
654    glRotatef(180, 1, 0, 0);
655    SquareWithHole(blockSize, holeRadius);
656    glPopMatrix();
657 
658    /* face at z1 */
659    glTranslatef(0, 0, z1);
660    SquareWithHole(blockSize, holeRadius);
661 
662    gluQuadricOrientation(Q, GLU_OUTSIDE);
663 }
664 
665 
666 /**
667  * Draw the engine block.
668  */
669 static void
DrawEngineBlock(const Engine * eng)670 DrawEngineBlock(const Engine *eng)
671 {
672    const float blockHeight = eng->Throw + 1.5 * eng->PistonHeight;
673    const float cylRadius = 1.01 * eng->PistonRadius;
674    const float blockSize = 0.5 * PistonSpacing(eng);
675    const int pistonsPerCrank = eng->Pistons / eng->Cranks;
676    int i;
677 
678    for (i = 0; i < eng->Pistons; i++) {
679       const float z = PistonShaftPosition(eng, i);
680       const int crank = i / pistonsPerCrank;
681       int k;
682 
683       glPushMatrix();
684          glTranslatef(0, 0, z);
685 
686          /* additional rotation for kth piston per crank */
687          k = i % pistonsPerCrank;
688          glRotatef(k * -eng->V_Angle, 0, 0, 1);
689 
690          /* the block */
691          glRotatef(-90, 1, 0, 0);
692          glTranslatef(0, 0, eng->Throw * 2);
693          DrawBlockWithHole(blockSize, blockHeight, cylRadius,
694                            crank, eng->Cranks);
695       glPopMatrix();
696    }
697 }
698 
699 
700 /**
701  * Generate display lists for engine parts.
702  */
703 static void
GenerateDisplayLists(Engine * eng)704 GenerateDisplayLists(Engine *eng)
705 {
706    eng->CrankList = glGenLists(1);
707    glNewList(eng->CrankList, GL_COMPILE);
708    DrawCrankshaft(eng);
709    glEndList();
710 
711    eng->ConnRodList = glGenLists(1);
712    glNewList(eng->ConnRodList, GL_COMPILE);
713    DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness,
714                  eng->CrankPinRadius, eng->WristPinRadius);
715    glEndList();
716 
717    eng->PistonList = glGenLists(1);
718    glNewList(eng->PistonList, GL_COMPILE);
719    DrawPiston(eng);
720    glEndList();
721 
722    eng->BlockList = glGenLists(1);
723    glNewList(eng->BlockList, GL_COMPILE);
724    DrawEngineBlock(eng);
725    glEndList();
726 }
727 
728 
729 /**
730  * Free engine display lists (render with immediate mode).
731  */
732 static void
FreeDisplayLists(Engine * eng)733 FreeDisplayLists(Engine *eng)
734 {
735    glDeleteLists(eng->CrankList, 1);
736    eng->CrankList = 0;
737    glDeleteLists(eng->ConnRodList, 1);
738    eng->ConnRodList = 0;
739    glDeleteLists(eng->PistonList, 1);
740    eng->PistonList = 0;
741    glDeleteLists(eng->BlockList, 1);
742    eng->BlockList = 0;
743 }
744 
745 
746 /**
747  * Draw complete engine.
748  * \param eng  description of engine to draw
749  * \param crankAngle  current crankshaft angle, in radians
750  */
751 static void
DrawEngine(const Engine * eng,float crankAngle)752 DrawEngine(const Engine *eng, float crankAngle)
753 {
754    const float crankDelta = 360.0 / eng->Cranks;
755    const float crankLen = CrankshaftLength(eng);
756    const int pistonsPerCrank = eng->Pistons / eng->Cranks;
757    int i;
758 
759    glPushMatrix();
760    glRotatef(eng->V_Angle * 0.5, 0, 0, 1);
761    glTranslatef(0, 0, -0.5 * crankLen);
762 
763    /* crankshaft */
764    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, CrankshaftColor);
765    glColor4fv(CrankshaftColor);
766    DrawPositionedCrankshaft(eng, crankAngle);
767 
768    for (i = 0; i < eng->Pistons; i++) {
769       const float z = PistonShaftPosition(eng, i);
770       const int crank = i / pistonsPerCrank;
771       float rot = crankAngle + crank * crankDelta;
772       int k;
773 
774       glPushMatrix();
775          glTranslatef(0, 0, z);
776 
777          /* additional rotation for kth piston per crank */
778          k = i % pistonsPerCrank;
779          glRotatef(k * -eng->V_Angle, 0, 0, 1);
780          rot += k * eng->V_Angle;
781 
782          /* piston */
783          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, PistonColor);
784          glColor4fv(PistonColor);
785          DrawPositionedPiston(eng, rot);
786 
787          /* connecting rod */
788          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ConnRodColor);
789          glColor4fv(ConnRodColor);
790          DrawPositionedConnectingRod(eng, rot);
791       glPopMatrix();
792    }
793 
794    if (Render.ShowBlock) {
795       const GLboolean blend = glIsEnabled(GL_BLEND);
796 
797       glDepthMask(GL_FALSE);
798       if (!blend) {
799          glEnable(GL_BLEND);
800       }
801       glEnable(GL_CULL_FACE);
802 
803       glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, BlockColor);
804       glColor4fv(BlockColor);
805       if (eng->CrankList)
806          glCallList(eng->BlockList);
807       else
808          DrawEngineBlock(eng);
809 
810       glDisable(GL_CULL_FACE);
811       glDepthMask(GL_TRUE);
812       if (!blend) {
813          glDisable(GL_BLEND);
814       }
815    }
816 
817    glPopMatrix();
818 }
819 
820 
821 static void
DrawBox(void)822 DrawBox(void)
823 {
824    const float xmin = -3.0, xmax = 3.0;
825    const float ymin = -1.0, ymax = 3.0;
826    const float zmin = -4.0, zmax = 4.0;
827    const float step = 0.5;
828    const float d = 0.01;
829    float x, y, z;
830    GLboolean lit = glIsEnabled(GL_LIGHTING);
831    GLboolean tex = glIsEnabled(GL_TEXTURE_2D);
832 
833    glDisable(GL_LIGHTING);
834    glDisable(GL_TEXTURE_2D);
835    glLineWidth(1.0);
836 
837    glColor3f(1, 1, 1);
838 
839    /* Z min */
840    glBegin(GL_LINES);
841    for (x = xmin; x <= xmax; x += step) {
842       glVertex3f(x, ymin, zmin);
843       glVertex3f(x, ymax, zmin);
844    }
845    glEnd();
846    glBegin(GL_LINES);
847    for (y = ymin; y <= ymax; y += step) {
848       glVertex3f(xmin, y, zmin);
849       glVertex3f(xmax, y, zmin);
850    }
851    glEnd();
852 
853    /* Y min */
854    glBegin(GL_LINES);
855    for (x = xmin; x <= xmax; x += step) {
856       glVertex3f(x, ymin, zmin);
857       glVertex3f(x, ymin, zmax);
858    }
859    glEnd();
860    glBegin(GL_LINES);
861    for (z = zmin; z <= zmax; z += step) {
862       glVertex3f(xmin, ymin, z);
863       glVertex3f(xmax, ymin, z);
864    }
865    glEnd();
866 
867    /* X min */
868    glBegin(GL_LINES);
869    for (y = ymin; y <= ymax; y += step) {
870       glVertex3f(xmin, y, zmin);
871       glVertex3f(xmin, y, zmax);
872    }
873    glEnd();
874    glBegin(GL_LINES);
875    for (z = zmin; z <= zmax; z += step) {
876       glVertex3f(xmin, ymin, z);
877       glVertex3f(xmin, ymax, z);
878    }
879    glEnd();
880 
881    glColor3f(0.4, 0.4, 0.6);
882    glBegin(GL_QUADS);
883    /* xmin */
884    glVertex3f(xmin-d, ymin, zmin);
885    glVertex3f(xmin-d, ymax, zmin);
886    glVertex3f(xmin-d, ymax, zmax);
887    glVertex3f(xmin-d, ymin, zmax);
888    /* ymin */
889    glVertex3f(xmin, ymin-d, zmin);
890    glVertex3f(xmax, ymin-d, zmin);
891    glVertex3f(xmax, ymin-d, zmax);
892    glVertex3f(xmin, ymin-d, zmax);
893    /* zmin */
894    glVertex3f(xmin, ymin, zmin-d);
895    glVertex3f(xmax, ymin, zmin-d);
896    glVertex3f(xmax, ymax, zmin-d);
897    glVertex3f(xmin, ymax, zmin-d);
898    glEnd();
899 
900    if (lit)
901       glEnable(GL_LIGHTING);
902    if (tex)
903       glEnable(GL_TEXTURE_2D);
904 }
905 
906 
907 static void
PrintString(const char * s)908 PrintString(const char *s)
909 {
910    while (*s) {
911       glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (int) *s);
912       s++;
913    }
914 }
915 
916 
917 static int
ComputeFPS(void)918 ComputeFPS(void)
919 {
920    static double t0 = -1.0;
921    static int frames = 0;
922    double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
923    static int fps = 0;
924 
925    frames++;
926 
927    if (t0 < 0.0) {
928       t0 = t;
929       fps = 0;
930    }
931    else if (t - t0 >= 1.0) {
932       fps = (int) (frames / (t - t0) + 0.5);
933       t0 = t;
934       frames = 0;
935    }
936 
937    return fps;
938 }
939 
940 
941 static void
Draw(void)942 Draw(void)
943 {
944    int fps;
945    GLfloat rot[4][4];
946 
947    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
948 
949    glPushMatrix();
950 
951       glTranslatef(0.0, 0.0, -View.Distance);
952       build_rotmatrix(rot, View.CurQuat);
953       glMultMatrixf(&rot[0][0]);
954 
955       glPushMatrix();
956          glTranslatef(0, -0.75, 0);
957          if (Render.DrawBox)
958             DrawBox();
959          DrawEngine(Engines + CurEngine, Theta);
960       glPopMatrix();
961 
962    glPopMatrix();
963 
964    fps = ComputeFPS();
965    if (Render.ShowInfo) {
966       GLboolean lit = glIsEnabled(GL_LIGHTING);
967       GLboolean tex = glIsEnabled(GL_TEXTURE_2D);
968       char s[100];
969       sprintf(s, "%s  %d FPS  %s", Engines[CurEngine].Name, fps,
970               Render.UseLists ? "Display Lists" : "Immediate mode");
971       glDisable(GL_LIGHTING);
972       glDisable(GL_TEXTURE_2D);
973       glColor3f(1, 1 , 1);
974       glWindowPos2iARB(10, 10);
975       PrintString(s);
976       if (lit)
977 	 glEnable(GL_LIGHTING);
978       if (tex)
979 	 glEnable(GL_TEXTURE_2D);
980    }
981 
982    /* also print out a periodic fps to stdout.  useful for trying to
983     * figure out the performance impact of rendering the string above
984     * with glBitmap.
985     */
986    {
987       static GLint T0 = 0;
988       static GLint Frames = 0;
989       GLint t = glutGet(GLUT_ELAPSED_TIME);
990 
991       Frames++;
992 
993       if (t - T0 >= 5000) {
994          GLfloat seconds = (t - T0) / 1000.0;
995          GLfloat fps = Frames / seconds;
996          printf("%d frames in %6.3f seconds = %6.3f FPS\n", Frames, seconds, fps);
997          fflush(stdout);
998          T0 = t;
999          Frames = 0;
1000       }
1001    }
1002 
1003 
1004    glutSwapBuffers();
1005 }
1006 
1007 
1008 /**
1009  * Handle window resize.
1010  */
1011 static void
Reshape(int width,int height)1012 Reshape(int width, int height)
1013 {
1014    float ar = (float) width / height;
1015    float s = 0.5;
1016    glViewport(0, 0, width, height);
1017    glMatrixMode(GL_PROJECTION);
1018    glLoadIdentity();
1019    glFrustum(-ar * s, ar * s, -s, s, 2.0, 50.0);
1020    glMatrixMode(GL_MODELVIEW);
1021    glLoadIdentity();
1022    WinWidth = width;
1023    WinHeight = height;
1024 }
1025 
1026 
1027 /**
1028  * Handle mouse button.
1029  */
1030 static void
Mouse(int button,int state,int x,int y)1031 Mouse(int button, int state, int x, int y)
1032 {
1033    if (button == GLUT_LEFT_BUTTON) {
1034       if (state == GLUT_DOWN) {
1035          View.StartX = x;
1036          View.StartY = y;
1037          View.Rotating = GL_TRUE;
1038       }
1039       else if (state == GLUT_UP) {
1040          View.Rotating = GL_FALSE;
1041       }
1042    }
1043    else if (button == GLUT_MIDDLE_BUTTON) {
1044       if (state == GLUT_DOWN) {
1045          View.StartX = x;
1046          View.StartY = y;
1047          View.StartDistance = View.Distance;
1048          View.Translating = GL_TRUE;
1049       }
1050       else if (state == GLUT_UP) {
1051          View.Translating = GL_FALSE;
1052       }
1053    }
1054 }
1055 
1056 
1057 /**
1058  * Handle mouse motion
1059  */
1060 static void
Motion(int x,int y)1061 Motion(int x, int y)
1062 {
1063    int i;
1064    if (View.Rotating) {
1065       float x0 = (2.0 * View.StartX - WinWidth) / WinWidth;
1066       float y0 = (WinHeight - 2.0 * View.StartY) / WinHeight;
1067       float x1 = (2.0 * x - WinWidth) / WinWidth;
1068       float y1 = (WinHeight - 2.0 * y) / WinHeight;
1069       float q[4];
1070 
1071       trackball(q, x0, y0, x1, y1);
1072       View.StartX = x;
1073       View.StartY = y;
1074       for (i = 0; i < 1; i++)
1075          add_quats(q, View.CurQuat, View.CurQuat);
1076 
1077       glutPostRedisplay();
1078    }
1079    else if (View.Translating) {
1080       float dz = 0.01 * (y - View.StartY);
1081       View.Distance = View.StartDistance + dz;
1082       glutPostRedisplay();
1083    }
1084 }
1085 
1086 
1087 /**
1088  ** Menu Callbacks
1089  **/
1090 
1091 static void
OptAnimation(void)1092 OptAnimation(void)
1093 {
1094    Render.Anim = !Render.Anim;
1095    if (Render.Anim)
1096       glutIdleFunc(Idle);
1097    else
1098       glutIdleFunc(NULL);
1099 }
1100 
1101 static void
OptChangeEngine(void)1102 OptChangeEngine(void)
1103 {
1104    CurEngine = (CurEngine + 1) % NUM_ENGINES;
1105 }
1106 
1107 static void
OptRenderMode(void)1108 OptRenderMode(void)
1109 {
1110    Render.Mode++;
1111    if (Render.Mode > TEXTURED)
1112       Render.Mode = 0;
1113    SetRenderState(Render.Mode);
1114 }
1115 
1116 static void
OptDisplayLists(void)1117 OptDisplayLists(void)
1118 {
1119    int i;
1120    Render.UseLists = !Render.UseLists;
1121    if (Render.UseLists) {
1122       for (i = 0; i < NUM_ENGINES; i++) {
1123          GenerateDisplayLists(Engines + i);
1124       }
1125    }
1126    else {
1127       for (i = 0; i < NUM_ENGINES; i++) {
1128          FreeDisplayLists(Engines + i);
1129       }
1130    }
1131 }
1132 
1133 static void
OptShowBlock(void)1134 OptShowBlock(void)
1135 {
1136    Render.ShowBlock = !Render.ShowBlock;
1137 }
1138 
1139 static void
OptShowInfo(void)1140 OptShowInfo(void)
1141 {
1142    Render.ShowInfo = !Render.ShowInfo;
1143 }
1144 
1145 static void
OptShowBox(void)1146 OptShowBox(void)
1147 {
1148    Render.DrawBox = !Render.DrawBox;
1149 }
1150 
1151 static void
OptRotate(void)1152 OptRotate(void)
1153 {
1154    Theta += 5.0;
1155 }
1156 
1157 static void
OptExit(void)1158 OptExit(void)
1159 {
1160    glutDestroyWindow(Win);
1161    exit(0);
1162 }
1163 
1164 
1165 /**
1166  * Define menu entries (w/ keyboard shortcuts)
1167  */
1168 
1169 typedef struct
1170 {
1171    const char *Text;
1172    const char Key;
1173    void (*Function)(void);
1174 } MenuInfo;
1175 
1176 static const MenuInfo MenuItems[] = {
1177    { "Animation", 'a', OptAnimation },
1178    { "Change Engine", 'e', OptChangeEngine },
1179    { "Rendering Style", 'm', OptRenderMode },
1180    { "Display Lists", 'd', OptDisplayLists },
1181    { "Show Block", 'b', OptShowBlock },
1182    { "Show Info", 'i', OptShowInfo },
1183    { "Show Box", 'x', OptShowBox },
1184    { "Exit", 27, OptExit },
1185    { NULL, 'r', OptRotate },
1186    { NULL, 0, NULL }
1187 };
1188 
1189 
1190 /**
1191  * Handle menu selection.
1192  */
1193 static void
MenuHandler(int entry)1194 MenuHandler(int entry)
1195 {
1196    MenuItems[entry].Function();
1197    glutPostRedisplay();
1198 }
1199 
1200 
1201 /**
1202  * Make pop-up menu.
1203  */
1204 static void
MakeMenu(void)1205 MakeMenu(void)
1206 {
1207    int i;
1208    glutCreateMenu(MenuHandler);
1209    for (i = 0; MenuItems[i].Text; i++) {
1210       glutAddMenuEntry(MenuItems[i].Text, i);
1211    }
1212    glutAttachMenu(GLUT_RIGHT_BUTTON);
1213 }
1214 
1215 
1216 /**
1217  * Handle keyboard event.
1218  */
1219 static void
Key(unsigned char key,int x,int y)1220 Key(unsigned char key, int x, int y)
1221 {
1222    int i;
1223    (void) x; (void) y;
1224    for (i = 0; MenuItems[i].Key; i++) {
1225       if (MenuItems[i].Key == key) {
1226          MenuItems[i].Function();
1227          glutPostRedisplay();
1228          break;
1229       }
1230    }
1231 }
1232 
1233 
1234 static
LoadTexture(void)1235 void LoadTexture(void)
1236 {
1237    GLboolean convolve = GL_FALSE;
1238 
1239    glGenTextures(1, &TextureObj);
1240    glBindTexture(GL_TEXTURE_2D, TextureObj);
1241    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1242    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1243    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
1244    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
1245 
1246    if (convolve) {
1247 #define FILTER_SIZE 7
1248       /* use convolution to blur the texture to simulate a dull finish
1249        * on the object.
1250        */
1251       GLubyte *img;
1252       GLenum format;
1253       GLint w, h;
1254       GLfloat filter[FILTER_SIZE][FILTER_SIZE][4];
1255 
1256       for (h = 0; h < FILTER_SIZE; h++) {
1257          for (w = 0; w < FILTER_SIZE; w++) {
1258             const GLfloat k = 1.0 / (FILTER_SIZE * FILTER_SIZE);
1259             filter[h][w][0] = k;
1260             filter[h][w][1] = k;
1261             filter[h][w][2] = k;
1262             filter[h][w][3] = k;
1263          }
1264       }
1265 
1266       glEnable(GL_CONVOLUTION_2D);
1267       glConvolutionParameteri(GL_CONVOLUTION_2D,
1268                               GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER);
1269       glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_RGBA,
1270                             FILTER_SIZE, FILTER_SIZE,
1271                             GL_RGBA, GL_FLOAT, filter);
1272 
1273       img = LoadRGBImage(TEXTURE_FILE, &w, &h, &format);
1274       if (!img) {
1275          printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE);
1276          exit(1);
1277       }
1278 
1279       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
1280                    format, GL_UNSIGNED_BYTE, img);
1281       free(img);
1282    }
1283    else {
1284       if (!LoadRGBMipmaps(TEXTURE_FILE, GL_RGB)) {
1285          printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE);
1286          exit(1);
1287       }
1288    }
1289 }
1290 
1291 
1292 static void
Init(void)1293 Init(void)
1294 {
1295    const GLfloat lightColor[4] = { 0.7, 0.7, 0.7, 1.0 };
1296    const GLfloat specular[4] = { 0.8, 0.8, 0.8, 1.0 };
1297    const GLfloat backColor[4] = { 1, 1, 0, 0 };
1298 
1299    Q = gluNewQuadric();
1300    gluQuadricNormals(Q, GLU_SMOOTH);
1301 
1302    LoadTexture();
1303 
1304    glClearColor(0.3, 0.3, 0.3, 0.0);
1305    glEnable(GL_DEPTH_TEST);
1306    glEnable(GL_LIGHTING);
1307    glEnable(GL_LIGHT0);
1308    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
1309    glMaterialf(GL_FRONT, GL_SHININESS, 40);
1310    glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
1311    glEnable(GL_NORMALIZE);
1312 
1313    glMaterialfv(GL_BACK, GL_DIFFUSE, backColor);
1314 #if 0
1315    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
1316 #endif
1317    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1318 
1319    InitViewInfo(&View);
1320    InitRenderInfo(&Render);
1321 }
1322 
1323 
1324 int
main(int argc,char * argv[])1325 main(int argc, char *argv[])
1326 {
1327    glutInitWindowSize(WinWidth, WinHeight);
1328    glutInit(&argc, argv);
1329    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
1330    Win = glutCreateWindow("OpenGL Engine Demo");
1331    gladLoadGL();
1332    glutReshapeFunc(Reshape);
1333    glutMouseFunc(Mouse);
1334    glutMotionFunc(Motion);
1335    glutKeyboardFunc(Key);
1336    glutDisplayFunc(Draw);
1337    MakeMenu();
1338    Init();
1339    if (Render.Anim)
1340       glutIdleFunc(Idle);
1341    glutMainLoop();
1342    return 0;
1343 }
1344