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