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