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