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