egl: remove duplicate ARRAY_SIZE() macro declaration
[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 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1102 OptChangeEngine(void)
1103 {
1104 CurEngine = (CurEngine + 1) % NUM_ENGINES;
1105 }
1106
1107 static 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
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
1134 OptShowBlock(void)
1135 {
1136 Render.ShowBlock = !Render.ShowBlock;
1137 }
1138
1139 static void
1140 OptShowInfo(void)
1141 {
1142 Render.ShowInfo = !Render.ShowInfo;
1143 }
1144
1145 static void
1146 OptShowBox(void)
1147 {
1148 Render.DrawBox = !Render.DrawBox;
1149 }
1150
1151 static void
1152 OptRotate(void)
1153 {
1154 Theta += 5.0;
1155 }
1156
1157 static 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
1194 MenuHandler(int entry)
1195 {
1196 MenuItems[entry].Function();
1197 glutPostRedisplay();
1198 }
1199
1200
1201 /**
1202 * Make pop-up menu.
1203 */
1204 static 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
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
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
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
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 glewInit();
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 }