Merge commit 'origin/gallium-master-merge'
[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 DrawPiston(eng);
390 glPopMatrix();
391 }
392
393
394 /**
395 * Draw connector plate. Used for crankshaft and connecting rods.
396 */
397 static void
398 DrawConnector(float length, float thickness,
399 float bigEndRadius, float smallEndRadius)
400 {
401 const float bigRadius = 1.2 * bigEndRadius;
402 const float smallRadius = 1.2 * smallEndRadius;
403 const float z0 = -0.5 * thickness, z1 = -z0;
404 GLfloat points[36][2], normals[36][2];
405 int i;
406
407 /* compute vertex locations, normals */
408 for (i = 0; i < 36; i++) {
409 const int angle = i * 10;
410 float x = cos(DEG_TO_RAD(angle));
411 float y = sin(DEG_TO_RAD(angle));
412 normals[i][0] = x;
413 normals[i][1] = y;
414 if (angle >= 0 && angle <= 180) {
415 x *= smallRadius;
416 y = y * smallRadius + length;
417 }
418 else {
419 x *= bigRadius;
420 y *= bigRadius;
421 }
422 points[i][0] = x;
423 points[i][1] = y;
424 }
425
426 /* front face */
427 glNormal3f(0, 0, 1);
428 glBegin(GL_POLYGON);
429 for (i = 0; i < 36; i++) {
430 glVertex3f(points[i][0], points[i][1], z1);
431 }
432 glEnd();
433
434 /* back face */
435 glNormal3f(0, 0, -1);
436 glBegin(GL_POLYGON);
437 for (i = 0; i < 36; i++) {
438 glVertex3f(points[35-i][0], points[35-i][1], z0);
439 }
440 glEnd();
441
442 /* edge */
443 glBegin(GL_QUAD_STRIP);
444 for (i = 0; i <= 36; i++) {
445 const int j = i % 36;
446 glNormal3f(normals[j][0], normals[j][1], 0);
447 glVertex3f(points[j][0], points[j][1], z1);
448 glVertex3f(points[j][0], points[j][1], z0);
449 }
450 glEnd();
451 }
452
453
454 /**
455 * Draw a crankshaft. Shaft lies along +Z axis, starting at zero.
456 */
457 static void
458 DrawCrankshaft(const Engine *eng)
459 {
460 const int slices = 20, stacks = 2;
461 const int n = eng->Cranks * 4 + 1;
462 const float phiStep = 360 / eng->Cranks;
463 float phi = -90.0;
464 int i;
465 float z = 0.0;
466
467 for (i = 0; i < n; i++) {
468 glPushMatrix();
469 glTranslatef(0, 0, z);
470 if (i & 1) {
471 /* draw a crank plate */
472 glRotatef(phi, 0, 0, 1);
473 glTranslatef(0, 0, 0.5 * eng->CrankPlateThickness);
474 DrawConnector(eng->Throw, eng->CrankPlateThickness,
475 eng->CrankJournalRadius, eng->CrankPinRadius);
476 z += 0.2;
477 if (i % 4 == 3)
478 phi += phiStep;
479 }
480 else if (i % 4 == 0) {
481 /* draw crank journal segment */
482 gluCylinder(Q, eng->CrankJournalRadius, eng->CrankJournalRadius,
483 eng->CrankJournalLength, slices, stacks);
484 z += eng->CrankJournalLength;
485 }
486 else if (i % 4 == 2) {
487 /* draw crank pin segment */
488 glRotatef(phi, 0, 0, 1);
489 glTranslatef(0, eng->Throw, 0);
490 gluCylinder(Q, eng->CrankPinRadius, eng->CrankPinRadius,
491 eng->CrankJournalLength, slices, stacks);
492 z += eng->CrankJournalLength;
493 }
494 glPopMatrix();
495 }
496 }
497
498
499 /**
500 * Draw crankshaft at a particular rotation.
501 * \param crankAngle current crankshaft rotation, in radians
502 */
503 static void
504 DrawPositionedCrankshaft(const Engine *eng, float crankAngle)
505 {
506 glPushMatrix();
507 glRotatef(crankAngle, 0, 0, 1);
508 if (eng->CrankList)
509 glCallList(eng->CrankList);
510 else
511 DrawCrankshaft(eng);
512 glPopMatrix();
513 }
514
515
516 /**
517 * Draw a connecting rod at particular position.
518 * \param eng description of connecting rod to draw
519 * \param crankAngle current crankshaft rotation, in radians
520 */
521 static void
522 DrawPositionedConnectingRod(const Engine *eng, float crankAngle)
523 {
524 float x0, y0, x1, y1;
525 float d, phi;
526
527 ComputeConnectingRodPosition(eng->Throw, crankAngle,
528 eng->ConnectingRodLength,
529 &x0, &y0, &x1, &y1);
530 d = sqrt(eng->ConnectingRodLength * eng->ConnectingRodLength - x0 * x0);
531 phi = atan(x0 / d) * 180.0 / M_PI;
532
533 glPushMatrix();
534 glTranslatef(x0, y0, 0);
535 glRotatef(phi, 0, 0, 1);
536 if (eng->ConnRodList)
537 glCallList(eng->ConnRodList);
538 else
539 DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness,
540 eng->CrankPinRadius, eng->WristPinRadius);
541 glPopMatrix();
542 }
543
544
545 /**
546 * Draw a square with a hole in middle.
547 */
548 static void
549 SquareWithHole(float squareSize, float holeRadius)
550 {
551 int i;
552 glBegin(GL_QUAD_STRIP);
553 glNormal3f(0, 0, 1);
554 for (i = 0; i <= 360; i += 5) {
555 const float x1 = holeRadius * cos(DEG_TO_RAD(i));
556 const float y1 = holeRadius * sin(DEG_TO_RAD(i));
557 float x2 = 0.0F, y2 = 0.0F;
558 if (i > 315 || i <= 45) {
559 x2 = squareSize;
560 y2 = squareSize * tan(DEG_TO_RAD(i));
561 }
562 else if (i > 45 && i <= 135) {
563 x2 = -squareSize * tan(DEG_TO_RAD(i - 90));
564 y2 = squareSize;
565 }
566 else if (i > 135 && i <= 225) {
567 x2 = -squareSize;
568 y2 = -squareSize * tan(DEG_TO_RAD(i-180));
569 }
570 else if (i > 225 && i <= 315) {
571 x2 = squareSize * tan(DEG_TO_RAD(i - 270));
572 y2 = -squareSize;
573 }
574 glVertex2f(x1, y1); /* inner circle */
575 glVertex2f(x2, y2); /* outer square */
576 }
577 glEnd();
578 }
579
580
581 /**
582 * Draw block with hole through middle.
583 * Hole is centered on Z axis.
584 * Bottom of block is at z=0, top of block is at z = blockHeight.
585 * index is in [0, count - 1] to determine which block faces are drawn.
586 */
587 static void
588 DrawBlockWithHole(float blockSize, float blockHeight, float holeRadius,
589 int index, int count)
590 {
591 const int slices = 30, stacks = 4;
592 const float x = blockSize;
593 const float y = blockSize;
594 const float z0 = 0;
595 const float z1 = blockHeight;
596
597 assert(index < count);
598 assert(Q);
599 gluQuadricOrientation(Q, GLU_INSIDE);
600
601 glBegin(GL_QUADS);
602 /* +X face */
603 glNormal3f(1, 0, 0);
604 glVertex3f( x, -y, z0);
605 glVertex3f( x, y, z0);
606 glVertex3f( x, y, z1);
607 glVertex3f( x, -y, z1);
608 /* -X face */
609 glNormal3f(-1, 0, 0);
610 glVertex3f(-x, -y, z1);
611 glVertex3f(-x, y, z1);
612 glVertex3f(-x, y, z0);
613 glVertex3f(-x, -y, z0);
614 if (index == 0) {
615 /* +Y face */
616 glNormal3f(0, 1, 0);
617 glVertex3f(-x, y, z1);
618 glVertex3f( x, y, z1);
619 glVertex3f( x, y, z0);
620 glVertex3f(-x, y, z0);
621 }
622 if (index == count - 1) {
623 /* -Y face */
624 glNormal3f(0, -1, 0);
625 glVertex3f(-x, -y, z0);
626 glVertex3f( x, -y, z0);
627 glVertex3f( x, -y, z1);
628 glVertex3f(-x, -y, z1);
629 }
630 glEnd();
631
632 /* cylinder / hole */
633 gluCylinder(Q, holeRadius, holeRadius, blockHeight, slices, stacks);
634
635 /* face at z0 */
636 glPushMatrix();
637 glRotatef(180, 1, 0, 0);
638 SquareWithHole(blockSize, holeRadius);
639 glPopMatrix();
640
641 /* face at z1 */
642 glTranslatef(0, 0, z1);
643 SquareWithHole(blockSize, holeRadius);
644
645 gluQuadricOrientation(Q, GLU_OUTSIDE);
646 }
647
648
649 /**
650 * Draw the engine block.
651 */
652 static void
653 DrawEngineBlock(const Engine *eng)
654 {
655 const float blockHeight = eng->Throw + 1.5 * eng->PistonHeight;
656 const float cylRadius = 1.01 * eng->PistonRadius;
657 const float blockSize = 0.5 * PistonSpacing(eng);
658 const int pistonsPerCrank = eng->Pistons / eng->Cranks;
659 int i;
660
661 for (i = 0; i < eng->Pistons; i++) {
662 const float z = PistonShaftPosition(eng, i);
663 const int crank = i / pistonsPerCrank;
664 int k;
665
666 glPushMatrix();
667 glTranslatef(0, 0, z);
668
669 /* additional rotation for kth piston per crank */
670 k = i % pistonsPerCrank;
671 glRotatef(k * -eng->V_Angle, 0, 0, 1);
672
673 /* the block */
674 glRotatef(-90, 1, 0, 0);
675 glTranslatef(0, 0, eng->Throw * 2);
676 DrawBlockWithHole(blockSize, blockHeight, cylRadius,
677 crank, eng->Cranks);
678 glPopMatrix();
679 }
680 }
681
682
683 /**
684 * Generate display lists for engine parts.
685 */
686 static void
687 GenerateDisplayLists(Engine *eng)
688 {
689 eng->CrankList = glGenLists(1);
690 glNewList(eng->CrankList, GL_COMPILE);
691 DrawCrankshaft(eng);
692 glEndList();
693
694 eng->ConnRodList = glGenLists(1);
695 glNewList(eng->ConnRodList, GL_COMPILE);
696 DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness,
697 eng->CrankPinRadius, eng->WristPinRadius);
698 glEndList();
699
700 eng->PistonList = glGenLists(1);
701 glNewList(eng->PistonList, GL_COMPILE);
702 DrawPiston(eng);
703 glEndList();
704
705 eng->BlockList = glGenLists(1);
706 glNewList(eng->BlockList, GL_COMPILE);
707 DrawEngineBlock(eng);
708 glEndList();
709 }
710
711
712 /**
713 * Free engine display lists (render with immediate mode).
714 */
715 static void
716 FreeDisplayLists(Engine *eng)
717 {
718 glDeleteLists(eng->CrankList, 1);
719 eng->CrankList = 0;
720 glDeleteLists(eng->ConnRodList, 1);
721 eng->ConnRodList = 0;
722 glDeleteLists(eng->PistonList, 1);
723 eng->PistonList = 0;
724 glDeleteLists(eng->BlockList, 1);
725 eng->BlockList = 0;
726 }
727
728
729 /**
730 * Draw complete engine.
731 * \param eng description of engine to draw
732 * \param crankAngle current crankshaft angle, in radians
733 */
734 static void
735 DrawEngine(const Engine *eng, float crankAngle)
736 {
737 const float crankDelta = 360.0 / eng->Cranks;
738 const float crankLen = CrankshaftLength(eng);
739 const int pistonsPerCrank = eng->Pistons / eng->Cranks;
740 int i;
741
742 glPushMatrix();
743 glRotatef(eng->V_Angle * 0.5, 0, 0, 1);
744 glTranslatef(0, 0, -0.5 * crankLen);
745
746 /* crankshaft */
747 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, CrankshaftColor);
748 glColor4fv(CrankshaftColor);
749 DrawPositionedCrankshaft(eng, crankAngle);
750
751 for (i = 0; i < eng->Pistons; i++) {
752 const float z = PistonShaftPosition(eng, i);
753 const int crank = i / pistonsPerCrank;
754 float rot = crankAngle + crank * crankDelta;
755 int k;
756
757 glPushMatrix();
758 glTranslatef(0, 0, z);
759
760 /* additional rotation for kth piston per crank */
761 k = i % pistonsPerCrank;
762 glRotatef(k * -eng->V_Angle, 0, 0, 1);
763 rot += k * eng->V_Angle;
764
765 /* piston */
766 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, PistonColor);
767 glColor4fv(PistonColor);
768 DrawPositionedPiston(eng, rot);
769
770 /* connecting rod */
771 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ConnRodColor);
772 glColor4fv(ConnRodColor);
773 DrawPositionedConnectingRod(eng, rot);
774 glPopMatrix();
775 }
776
777 if (Render.ShowBlock) {
778 const GLboolean blend = glIsEnabled(GL_BLEND);
779
780 glDepthMask(GL_FALSE);
781 if (!blend) {
782 glEnable(GL_BLEND);
783 }
784 glEnable(GL_CULL_FACE);
785
786 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, BlockColor);
787 glColor4fv(BlockColor);
788 if (eng->CrankList)
789 glCallList(eng->BlockList);
790 else
791 DrawEngineBlock(eng);
792
793 glDisable(GL_CULL_FACE);
794 glDepthMask(GL_TRUE);
795 if (!blend) {
796 glDisable(GL_BLEND);
797 }
798 }
799
800 glPopMatrix();
801 }
802
803
804 static void
805 DrawBox(void)
806 {
807 const float xmin = -3.0, xmax = 3.0;
808 const float ymin = -1.0, ymax = 3.0;
809 const float zmin = -4.0, zmax = 4.0;
810 const float step = 0.5;
811 const float d = 0.01;
812 float x, y, z;
813 GLboolean lit = glIsEnabled(GL_LIGHTING);
814 GLboolean tex = glIsEnabled(GL_TEXTURE_2D);
815
816 glDisable(GL_LIGHTING);
817 glDisable(GL_TEXTURE_2D);
818
819 glColor3f(1, 1, 1);
820
821 /* Z min */
822 glBegin(GL_LINES);
823 for (x = xmin; x <= xmax; x += step) {
824 glVertex3f(x, ymin, zmin);
825 glVertex3f(x, ymax, zmin);
826 }
827 glEnd();
828 glBegin(GL_LINES);
829 for (y = ymin; y <= ymax; y += step) {
830 glVertex3f(xmin, y, zmin);
831 glVertex3f(xmax, y, zmin);
832 }
833 glEnd();
834
835 /* Y min */
836 glBegin(GL_LINES);
837 for (x = xmin; x <= xmax; x += step) {
838 glVertex3f(x, ymin, zmin);
839 glVertex3f(x, ymin, zmax);
840 }
841 glEnd();
842 glBegin(GL_LINES);
843 for (z = zmin; z <= zmax; z += step) {
844 glVertex3f(xmin, ymin, z);
845 glVertex3f(xmax, ymin, z);
846 }
847 glEnd();
848
849 /* X min */
850 glBegin(GL_LINES);
851 for (y = ymin; y <= ymax; y += step) {
852 glVertex3f(xmin, y, zmin);
853 glVertex3f(xmin, y, zmax);
854 }
855 glEnd();
856 glBegin(GL_LINES);
857 for (z = zmin; z <= zmax; z += step) {
858 glVertex3f(xmin, ymin, z);
859 glVertex3f(xmin, ymax, z);
860 }
861 glEnd();
862
863 glColor3f(0.4, 0.4, 0.6);
864 glBegin(GL_QUADS);
865 /* xmin */
866 glVertex3f(xmin-d, ymin, zmin);
867 glVertex3f(xmin-d, ymax, zmin);
868 glVertex3f(xmin-d, ymax, zmax);
869 glVertex3f(xmin-d, ymin, zmax);
870 /* ymin */
871 glVertex3f(xmin, ymin-d, zmin);
872 glVertex3f(xmax, ymin-d, zmin);
873 glVertex3f(xmax, ymin-d, zmax);
874 glVertex3f(xmin, ymin-d, zmax);
875 /* zmin */
876 glVertex3f(xmin, ymin, zmin-d);
877 glVertex3f(xmax, ymin, zmin-d);
878 glVertex3f(xmax, ymax, zmin-d);
879 glVertex3f(xmin, ymax, zmin-d);
880 glEnd();
881
882 if (lit)
883 glEnable(GL_LIGHTING);
884 if (tex)
885 glEnable(GL_TEXTURE_2D);
886 }
887
888
889 static void
890 PrintString(const char *s)
891 {
892 while (*s) {
893 glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (int) *s);
894 s++;
895 }
896 }
897
898
899 static int
900 ComputeFPS(void)
901 {
902 static double t0 = -1.0;
903 static int frames = 0;
904 double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
905 static int fps = 0;
906
907 frames++;
908
909 if (t0 < 0.0) {
910 t0 = t;
911 fps = 0;
912 }
913 else if (t - t0 >= 1.0) {
914 fps = (int) (frames / (t - t0) + 0.5);
915 t0 = t;
916 frames = 0;
917 }
918
919 return fps;
920 }
921
922
923 static void
924 Draw(void)
925 {
926 int fps;
927 GLfloat rot[4][4];
928
929 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
930
931 glPushMatrix();
932
933 glTranslatef(0.0, 0.0, -View.Distance);
934 build_rotmatrix(rot, View.CurQuat);
935 glMultMatrixf(&rot[0][0]);
936
937 glPushMatrix();
938 glTranslatef(0, -0.75, 0);
939 if (Render.DrawBox)
940 DrawBox();
941 DrawEngine(Engines + CurEngine, Theta);
942 glPopMatrix();
943
944 glPopMatrix();
945
946 fps = ComputeFPS();
947 if (Render.ShowInfo) {
948 GLboolean lit = glIsEnabled(GL_LIGHTING);
949 GLboolean tex = glIsEnabled(GL_TEXTURE_2D);
950 char s[100];
951 sprintf(s, "%s %d FPS %s", Engines[CurEngine].Name, fps,
952 Render.UseLists ? "Display Lists" : "Immediate mode");
953 glDisable(GL_LIGHTING);
954 glDisable(GL_TEXTURE_2D);
955 glColor3f(1, 1 , 1);
956 glWindowPos2iARB(10, 10);
957 PrintString(s);
958 if (lit)
959 glEnable(GL_LIGHTING);
960 if (tex)
961 glEnable(GL_TEXTURE_2D);
962 }
963
964 glutSwapBuffers();
965 }
966
967
968 /**
969 * Handle window resize.
970 */
971 static void
972 Reshape(int width, int height)
973 {
974 float ar = (float) width / height;
975 float s = 0.5;
976 glViewport(0, 0, width, height);
977 glMatrixMode(GL_PROJECTION);
978 glLoadIdentity();
979 glFrustum(-ar * s, ar * s, -s, s, 2.0, 50.0);
980 glMatrixMode(GL_MODELVIEW);
981 glLoadIdentity();
982 WinWidth = width;
983 WinHeight = height;
984 }
985
986
987 /**
988 * Handle mouse button.
989 */
990 static void
991 Mouse(int button, int state, int x, int y)
992 {
993 if (button == GLUT_LEFT_BUTTON) {
994 if (state == GLUT_DOWN) {
995 View.StartX = x;
996 View.StartY = y;
997 View.Rotating = GL_TRUE;
998 }
999 else if (state == GLUT_UP) {
1000 View.Rotating = GL_FALSE;
1001 }
1002 }
1003 else if (button == GLUT_MIDDLE_BUTTON) {
1004 if (state == GLUT_DOWN) {
1005 View.StartX = x;
1006 View.StartY = y;
1007 View.StartDistance = View.Distance;
1008 View.Translating = GL_TRUE;
1009 }
1010 else if (state == GLUT_UP) {
1011 View.Translating = GL_FALSE;
1012 }
1013 }
1014 }
1015
1016
1017 /**
1018 * Handle mouse motion
1019 */
1020 static void
1021 Motion(int x, int y)
1022 {
1023 int i;
1024 if (View.Rotating) {
1025 float x0 = (2.0 * View.StartX - WinWidth) / WinWidth;
1026 float y0 = (WinHeight - 2.0 * View.StartY) / WinHeight;
1027 float x1 = (2.0 * x - WinWidth) / WinWidth;
1028 float y1 = (WinHeight - 2.0 * y) / WinHeight;
1029 float q[4];
1030
1031 trackball(q, x0, y0, x1, y1);
1032 View.StartX = x;
1033 View.StartY = y;
1034 for (i = 0; i < 1; i++)
1035 add_quats(q, View.CurQuat, View.CurQuat);
1036
1037 glutPostRedisplay();
1038 }
1039 else if (View.Translating) {
1040 float dz = 0.01 * (y - View.StartY);
1041 View.Distance = View.StartDistance + dz;
1042 glutPostRedisplay();
1043 }
1044 }
1045
1046
1047 /**
1048 ** Menu Callbacks
1049 **/
1050
1051 static void
1052 OptAnimation(void)
1053 {
1054 Render.Anim = !Render.Anim;
1055 if (Render.Anim)
1056 glutIdleFunc(Idle);
1057 else
1058 glutIdleFunc(NULL);
1059 }
1060
1061 static void
1062 OptChangeEngine(void)
1063 {
1064 CurEngine = (CurEngine + 1) % NUM_ENGINES;
1065 }
1066
1067 static void
1068 OptRenderMode(void)
1069 {
1070 Render.Mode++;
1071 if (Render.Mode > TEXTURED)
1072 Render.Mode = 0;
1073 SetRenderState(Render.Mode);
1074 }
1075
1076 static void
1077 OptDisplayLists(void)
1078 {
1079 int i;
1080 Render.UseLists = !Render.UseLists;
1081 if (Render.UseLists) {
1082 for (i = 0; i < NUM_ENGINES; i++) {
1083 GenerateDisplayLists(Engines + i);
1084 }
1085 }
1086 else {
1087 for (i = 0; i < NUM_ENGINES; i++) {
1088 FreeDisplayLists(Engines + i);
1089 }
1090 }
1091 }
1092
1093 static void
1094 OptShowBlock(void)
1095 {
1096 Render.ShowBlock = !Render.ShowBlock;
1097 }
1098
1099 static void
1100 OptShowInfo(void)
1101 {
1102 Render.ShowInfo = !Render.ShowInfo;
1103 }
1104
1105 static void
1106 OptShowBox(void)
1107 {
1108 Render.DrawBox = !Render.DrawBox;
1109 }
1110
1111 static void
1112 OptRotate(void)
1113 {
1114 Theta += 5.0;
1115 }
1116
1117 static void
1118 OptExit(void)
1119 {
1120 exit(0);
1121 }
1122
1123
1124 /**
1125 * Define menu entries (w/ keyboard shortcuts)
1126 */
1127
1128 typedef struct
1129 {
1130 const char *Text;
1131 const char Key;
1132 void (*Function)(void);
1133 } MenuInfo;
1134
1135 static const MenuInfo MenuItems[] = {
1136 { "Animation", 'a', OptAnimation },
1137 { "Change Engine", 'e', OptChangeEngine },
1138 { "Rendering Style", 'm', OptRenderMode },
1139 { "Display Lists", 'd', OptDisplayLists },
1140 { "Show Block", 'b', OptShowBlock },
1141 { "Show Info", 'i', OptShowInfo },
1142 { "Show Box", 'x', OptShowBox },
1143 { "Exit", 27, OptExit },
1144 { NULL, 'r', OptRotate },
1145 { NULL, 0, NULL }
1146 };
1147
1148
1149 /**
1150 * Handle menu selection.
1151 */
1152 static void
1153 MenuHandler(int entry)
1154 {
1155 MenuItems[entry].Function();
1156 glutPostRedisplay();
1157 }
1158
1159
1160 /**
1161 * Make pop-up menu.
1162 */
1163 static void
1164 MakeMenu(void)
1165 {
1166 int i;
1167 glutCreateMenu(MenuHandler);
1168 for (i = 0; MenuItems[i].Text; i++) {
1169 glutAddMenuEntry(MenuItems[i].Text, i);
1170 }
1171 glutAttachMenu(GLUT_RIGHT_BUTTON);
1172 }
1173
1174
1175 /**
1176 * Handle keyboard event.
1177 */
1178 static void
1179 Key(unsigned char key, int x, int y)
1180 {
1181 int i;
1182 (void) x; (void) y;
1183 for (i = 0; MenuItems[i].Key; i++) {
1184 if (MenuItems[i].Key == key) {
1185 MenuItems[i].Function();
1186 glutPostRedisplay();
1187 break;
1188 }
1189 }
1190 }
1191
1192
1193 static
1194 void LoadTexture(void)
1195 {
1196 GLboolean convolve = GL_FALSE;
1197
1198 glGenTextures(1, &TextureObj);
1199 glBindTexture(GL_TEXTURE_2D, TextureObj);
1200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1202 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
1203 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
1204
1205 if (convolve) {
1206 #define FILTER_SIZE 7
1207 /* use convolution to blur the texture to simulate a dull finish
1208 * on the object.
1209 */
1210 GLubyte *img;
1211 GLenum format;
1212 GLint w, h;
1213 GLfloat filter[FILTER_SIZE][FILTER_SIZE][4];
1214
1215 for (h = 0; h < FILTER_SIZE; h++) {
1216 for (w = 0; w < FILTER_SIZE; w++) {
1217 const GLfloat k = 1.0 / (FILTER_SIZE * FILTER_SIZE);
1218 filter[h][w][0] = k;
1219 filter[h][w][1] = k;
1220 filter[h][w][2] = k;
1221 filter[h][w][3] = k;
1222 }
1223 }
1224
1225 glEnable(GL_CONVOLUTION_2D);
1226 glConvolutionParameteri(GL_CONVOLUTION_2D,
1227 GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER);
1228 glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_RGBA,
1229 FILTER_SIZE, FILTER_SIZE,
1230 GL_RGBA, GL_FLOAT, filter);
1231
1232 img = LoadRGBImage(TEXTURE_FILE, &w, &h, &format);
1233 if (!img) {
1234 printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE);
1235 exit(1);
1236 }
1237
1238 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
1239 format, GL_UNSIGNED_BYTE, img);
1240 free(img);
1241 }
1242 else {
1243 if (!LoadRGBMipmaps(TEXTURE_FILE, GL_RGB)) {
1244 printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE);
1245 exit(1);
1246 }
1247 }
1248 }
1249
1250
1251 static void
1252 Init(void)
1253 {
1254 const GLfloat lightColor[4] = { 0.7, 0.7, 0.7, 1.0 };
1255 const GLfloat specular[4] = { 0.8, 0.8, 0.8, 1.0 };
1256 const GLfloat backColor[4] = { 1, 1, 0, 0 };
1257
1258 Q = gluNewQuadric();
1259 gluQuadricNormals(Q, GLU_SMOOTH);
1260
1261 LoadTexture();
1262
1263 glClearColor(0.3, 0.3, 0.3, 0.0);
1264 glEnable(GL_DEPTH_TEST);
1265 glEnable(GL_LIGHTING);
1266 glEnable(GL_LIGHT0);
1267 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
1268 glMaterialf(GL_FRONT, GL_SHININESS, 40);
1269 glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
1270 glEnable(GL_NORMALIZE);
1271
1272 glMaterialfv(GL_BACK, GL_DIFFUSE, backColor);
1273 #if 0
1274 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
1275 #endif
1276 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1277
1278 InitViewInfo(&View);
1279 InitRenderInfo(&Render);
1280 }
1281
1282
1283 int
1284 main(int argc, char *argv[])
1285 {
1286 glutInit(&argc, argv);
1287 glutInitWindowSize(WinWidth, WinHeight);
1288 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
1289 glutCreateWindow("OpenGL Engine Demo");
1290 glewInit();
1291 glutReshapeFunc(Reshape);
1292 glutMouseFunc(Mouse);
1293 glutMotionFunc(Motion);
1294 glutKeyboardFunc(Key);
1295 glutDisplayFunc(Draw);
1296 MakeMenu();
1297 Init();
1298 if (Render.Anim)
1299 glutIdleFunc(Idle);
1300 glutMainLoop();
1301 return 0;
1302 }