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