engine.c revision 32001f49
132001f49Smrg/**
232001f49Smrg * Simple engine demo (crankshaft, pistons, connecting rods)
332001f49Smrg *
432001f49Smrg * Brian Paul
532001f49Smrg * June 2006
632001f49Smrg */
732001f49Smrg
832001f49Smrg#include <assert.h>
932001f49Smrg#include <stdio.h>
1032001f49Smrg#include <stdlib.h>
1132001f49Smrg#include <math.h>
1232001f49Smrg#include <GL/glew.h>
1332001f49Smrg#include "glut_wrap.h"
1432001f49Smrg#include "readtex.h"
1532001f49Smrg#include "trackball.h"
1632001f49Smrg
1732001f49Smrg
1832001f49Smrg#ifndef M_PI
1932001f49Smrg#define M_PI 3.14159265358979323846
2032001f49Smrg#endif
2132001f49Smrg
2232001f49Smrg#define DEG_TO_RAD(DEG)  ((DEG) * M_PI / 180.0)
2332001f49Smrg
2432001f49Smrg#define TEXTURE_FILE DEMOS_DATA_DIR "reflect.rgb"
2532001f49Smrg
2632001f49Smrg/* Target engine speed: */
2732001f49Smrgconst int RPM = 100.0;
2832001f49Smrg
2932001f49Smrgstatic int Win = 0;
3032001f49Smrg
3132001f49Smrg
3232001f49Smrg/**
3332001f49Smrg * Engine description.
3432001f49Smrg */
3532001f49Smrgtypedef struct
3632001f49Smrg{
3732001f49Smrg   const char *Name;
3832001f49Smrg   int Pistons;
3932001f49Smrg   int Cranks;
4032001f49Smrg   float V_Angle;
4132001f49Smrg   float PistonRadius;
4232001f49Smrg   float PistonHeight;
4332001f49Smrg   float WristPinRadius;
4432001f49Smrg   float Throw;
4532001f49Smrg   float CrankPlateThickness;
4632001f49Smrg   float CrankPinRadius;
4732001f49Smrg   float CrankJournalRadius;
4832001f49Smrg   float CrankJournalLength;
4932001f49Smrg   float ConnectingRodLength;
5032001f49Smrg   float ConnectingRodThickness;
5132001f49Smrg   /* display list IDs */
5232001f49Smrg   GLuint CrankList;
5332001f49Smrg   GLuint ConnRodList;
5432001f49Smrg   GLuint PistonList;
5532001f49Smrg   GLuint BlockList;
5632001f49Smrg} Engine;
5732001f49Smrg
5832001f49Smrg
5932001f49Smrgtypedef struct
6032001f49Smrg{
6132001f49Smrg   float CurQuat[4];
6232001f49Smrg   float Distance;
6332001f49Smrg   /* When mouse is moving: */
6432001f49Smrg   GLboolean Rotating, Translating;
6532001f49Smrg   GLint StartX, StartY;
6632001f49Smrg   float StartDistance;
6732001f49Smrg} ViewInfo;
6832001f49Smrg
6932001f49Smrg
7032001f49Smrgtypedef enum
7132001f49Smrg{
7232001f49Smrg   LIT,
7332001f49Smrg   WIREFRAME,
7432001f49Smrg   TEXTURED
7532001f49Smrg} RenderMode;
7632001f49Smrg
7732001f49Smrg
7832001f49Smrgtypedef struct
7932001f49Smrg{
8032001f49Smrg   RenderMode Mode;
8132001f49Smrg   GLboolean Anim;
8232001f49Smrg   GLboolean Wireframe;
8332001f49Smrg   GLboolean Blend;
8432001f49Smrg   GLboolean Antialias;
8532001f49Smrg   GLboolean Texture;
8632001f49Smrg   GLboolean UseLists;
8732001f49Smrg   GLboolean DrawBox;
8832001f49Smrg   GLboolean ShowInfo;
8932001f49Smrg   GLboolean ShowBlock;
9032001f49Smrg} RenderInfo;
9132001f49Smrg
9232001f49Smrg
9332001f49Smrgstatic GLUquadric *Q;
9432001f49Smrg
9532001f49Smrgstatic GLfloat Theta = 0.0;
9632001f49Smrg
9732001f49Smrgstatic const GLfloat PistonColor[4] = { 1.0, 0.5, 0.5, 1.0 };
9832001f49Smrgstatic const GLfloat ConnRodColor[4] = { 0.7, 1.0, 0.7, 1.0 };
9932001f49Smrgstatic const GLfloat CrankshaftColor[4] = { 0.7, 0.7, 1.0, 1.0 };
10032001f49Smrgstatic const GLfloat BlockColor[4] = {0.8, 0.8, 0.8, 0.75 };
10132001f49Smrg
10232001f49Smrgstatic GLuint TextureObj;
10332001f49Smrgstatic GLint WinWidth = 800, WinHeight = 500;
10432001f49Smrg
10532001f49Smrgstatic ViewInfo View;
10632001f49Smrgstatic RenderInfo Render;
10732001f49Smrg
10832001f49Smrg#define NUM_ENGINES 3
10932001f49Smrgstatic Engine Engines[NUM_ENGINES] =
11032001f49Smrg{
11132001f49Smrg   {
11232001f49Smrg      "V-6",
11332001f49Smrg      6,    /* Pistons */
11432001f49Smrg      3,    /* Cranks */
11532001f49Smrg      90.0, /* V_Angle */
11632001f49Smrg      0.5,  /* PistonRadius */
11732001f49Smrg      0.6,  /* PistonHeight */
11832001f49Smrg      0.1,  /* WristPinRadius */
11932001f49Smrg      0.5,  /* Throw */
12032001f49Smrg      0.2,  /* CrankPlateThickness */
12132001f49Smrg      0.25, /* CrankPinRadius */
12232001f49Smrg      0.3,  /* CrankJournalRadius */
12332001f49Smrg      0.4,  /* CrankJournalLength */
12432001f49Smrg      1.5,  /* ConnectingRodLength */
12532001f49Smrg      0.1,  /* ConnectingRodThickness */
12632001f49Smrg      0,    /* CrankList */
12732001f49Smrg      0,    /* ConnRodList */
12832001f49Smrg      0,    /* PistonList */
12932001f49Smrg      0     /* BlockList */
13032001f49Smrg   },
13132001f49Smrg   {
13232001f49Smrg      "Inline-4",
13332001f49Smrg      4,    /* Pistons */
13432001f49Smrg      4,    /* Cranks */
13532001f49Smrg      0.0,  /* V_Angle */
13632001f49Smrg      0.5,  /* PistonRadius */
13732001f49Smrg      0.6,  /* PistonHeight */
13832001f49Smrg      0.1,  /* WristPinRadius */
13932001f49Smrg      0.5,  /* Throw */
14032001f49Smrg      0.2,  /* CrankPlateThickness */
14132001f49Smrg      0.25, /* CrankPinRadius */
14232001f49Smrg      0.3,  /* CrankJournalRadius */
14332001f49Smrg      0.4,  /* CrankJournalLength */
14432001f49Smrg      1.5,  /* ConnectingRodLength */
14532001f49Smrg      0.1,  /* ConnectingRodThickness */
14632001f49Smrg      0,    /* CrankList */
14732001f49Smrg      0,    /* ConnRodList */
14832001f49Smrg      0,    /* PistonList */
14932001f49Smrg      0     /* BlockList */
15032001f49Smrg   },
15132001f49Smrg   {
15232001f49Smrg      "Boxer-6",
15332001f49Smrg      6,    /* Pistons */
15432001f49Smrg      3,    /* Cranks */
15532001f49Smrg      180.0,/* V_Angle */
15632001f49Smrg      0.5,  /* PistonRadius */
15732001f49Smrg      0.6,  /* PistonHeight */
15832001f49Smrg      0.1,  /* WristPinRadius */
15932001f49Smrg      0.5,  /* Throw */
16032001f49Smrg      0.2,  /* CrankPlateThickness */
16132001f49Smrg      0.25, /* CrankPinRadius */
16232001f49Smrg      0.3,  /* CrankJournalRadius */
16332001f49Smrg      0.4,  /* CrankJournalLength */
16432001f49Smrg      1.5,  /* ConnectingRodLength */
16532001f49Smrg      0.1,  /* ConnectingRodThickness */
16632001f49Smrg      0,    /* CrankList */
16732001f49Smrg      0,    /* ConnRodList */
16832001f49Smrg      0,    /* PistonList */
16932001f49Smrg      0     /* BlockList */
17032001f49Smrg   }
17132001f49Smrg};
17232001f49Smrg
17332001f49Smrgstatic int CurEngine = 0;
17432001f49Smrg
17532001f49Smrg
17632001f49Smrg
17732001f49Smrgstatic void
17832001f49SmrgInitViewInfo(ViewInfo *view)
17932001f49Smrg{
18032001f49Smrg   view->Rotating = GL_FALSE;
18132001f49Smrg   view->Translating = GL_FALSE;
18232001f49Smrg   view->StartX = view->StartY = 0;
18332001f49Smrg   view->Distance = 12.0;
18432001f49Smrg   view->StartDistance = 0.0;
18532001f49Smrg   view->CurQuat[0] = -0.194143;
18632001f49Smrg   view->CurQuat[1] = 0.507848;
18732001f49Smrg   view->CurQuat[2] = 0.115245;
18832001f49Smrg   view->CurQuat[3] = 0.831335;
18932001f49Smrg}
19032001f49Smrg
19132001f49Smrg
19232001f49Smrgstatic void
19332001f49SmrgInitRenderInfo(RenderInfo *render)
19432001f49Smrg{
19532001f49Smrg   render->Mode = LIT;
19632001f49Smrg   render->Anim = GL_TRUE;
19732001f49Smrg   render->Wireframe = GL_FALSE;
19832001f49Smrg   render->Blend = GL_FALSE;
19932001f49Smrg   render->Antialias = GL_FALSE;
20032001f49Smrg   render->Texture = GL_FALSE;
20132001f49Smrg   render->DrawBox = GL_FALSE;
20232001f49Smrg   render->ShowInfo = GL_TRUE;
20332001f49Smrg   render->ShowBlock = GL_FALSE;
20432001f49Smrg   render->UseLists = GL_FALSE;
20532001f49Smrg}
20632001f49Smrg
20732001f49Smrg
20832001f49Smrg/**
20932001f49Smrg * Set GL for given rendering mode.
21032001f49Smrg */
21132001f49Smrgstatic void
21232001f49SmrgSetRenderState(RenderMode mode)
21332001f49Smrg{
21432001f49Smrg   static const GLfloat gray2[4] = { 0.2, 0.2, 0.2, 1.0 };
21532001f49Smrg   static const GLfloat gray4[4] = { 0.4, 0.4, 0.4, 1.0 };
21632001f49Smrg
21732001f49Smrg   /* defaults */
21832001f49Smrg   glDisable(GL_LIGHTING);
21932001f49Smrg   glDisable(GL_TEXTURE_2D);
22032001f49Smrg   glDisable(GL_BLEND);
22132001f49Smrg   glDisable(GL_LINE_SMOOTH);
22232001f49Smrg   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
22332001f49Smrg   glDisable(GL_TEXTURE_GEN_S);
22432001f49Smrg   glDisable(GL_TEXTURE_GEN_T);
22532001f49Smrg   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, gray2);
22632001f49Smrg
22732001f49Smrg   switch (mode) {
22832001f49Smrg   case LIT:
22932001f49Smrg      glEnable(GL_LIGHTING);
23032001f49Smrg      break;
23132001f49Smrg   case WIREFRAME:
23232001f49Smrg      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
23332001f49Smrg      glEnable(GL_LINE_SMOOTH);
23432001f49Smrg      glEnable(GL_BLEND);
23532001f49Smrg      glLineWidth(1.5);
23632001f49Smrg      break;
23732001f49Smrg   case TEXTURED:
23832001f49Smrg      glEnable(GL_LIGHTING);
23932001f49Smrg      glEnable(GL_TEXTURE_2D);
24032001f49Smrg      glEnable(GL_TEXTURE_GEN_S);
24132001f49Smrg      glEnable(GL_TEXTURE_GEN_T);
24232001f49Smrg      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, gray4);
24332001f49Smrg      break;
24432001f49Smrg   default:
24532001f49Smrg      ;
24632001f49Smrg   }
24732001f49Smrg}
24832001f49Smrg
24932001f49Smrg
25032001f49Smrg/**
25132001f49Smrg * Animate the engine parts.
25232001f49Smrg */
25332001f49Smrgstatic void
25432001f49SmrgIdle(void)
25532001f49Smrg{
25632001f49Smrg   /* convert degrees per millisecond to RPM: */
25732001f49Smrg   const float m = 360.0 / 1000.0 / 60.0;
25832001f49Smrg   GLint t = glutGet(GLUT_ELAPSED_TIME);
25932001f49Smrg   Theta = ((int) (t * RPM * m)) % 360;
26032001f49Smrg   glutPostRedisplay();
26132001f49Smrg}
26232001f49Smrg
26332001f49Smrg
26432001f49Smrg/**
26532001f49Smrg * Compute piston's position along its stroke.
26632001f49Smrg */
26732001f49Smrgstatic float
26832001f49SmrgPistonStrokePosition(float throwDist, float crankAngle, float connRodLength)
26932001f49Smrg{
27032001f49Smrg   float x = throwDist * cos(DEG_TO_RAD(crankAngle));
27132001f49Smrg   float y = throwDist * sin(DEG_TO_RAD(crankAngle));
27232001f49Smrg   float pos = y + sqrt(connRodLength * connRodLength - x * x);
27332001f49Smrg   return pos;
27432001f49Smrg}
27532001f49Smrg
27632001f49Smrg
27732001f49Smrg/**
27832001f49Smrg * Compute position of nth piston along the crankshaft.
27932001f49Smrg */
28032001f49Smrgstatic float
28132001f49SmrgPistonShaftPosition(const Engine *eng, int piston)
28232001f49Smrg{
28332001f49Smrg   const int i = piston / (eng->Pistons / eng->Cranks);
28432001f49Smrg   float z;
28532001f49Smrg   assert(piston < eng->Pistons);
28632001f49Smrg   z = 1.5 * eng->CrankJournalLength + eng->CrankPlateThickness
28732001f49Smrg      + i * (2.0 * (eng->CrankJournalLength + eng->CrankPlateThickness));
28832001f49Smrg   if (eng->Pistons > eng->Cranks) {
28932001f49Smrg      if (piston & 1)
29032001f49Smrg         z += eng->ConnectingRodThickness;
29132001f49Smrg      else
29232001f49Smrg         z -= eng->ConnectingRodThickness;
29332001f49Smrg   }
29432001f49Smrg   return z;
29532001f49Smrg}
29632001f49Smrg
29732001f49Smrg
29832001f49Smrg/**
29932001f49Smrg * Compute distance between two adjacent pistons
30032001f49Smrg */
30132001f49Smrgstatic float
30232001f49SmrgPistonSpacing(const Engine *eng)
30332001f49Smrg{
30432001f49Smrg   const int pistonsPerCrank = eng->Pistons / eng->Cranks;
30532001f49Smrg   const float z0 = PistonShaftPosition(eng, 0);
30632001f49Smrg   const float z1 = PistonShaftPosition(eng, pistonsPerCrank);
30732001f49Smrg   return z1 - z0;
30832001f49Smrg}
30932001f49Smrg
31032001f49Smrg
31132001f49Smrg/**
31232001f49Smrg * (x0, y0) = position of big end on crankshaft
31332001f49Smrg * (x1, y1) = position of small end on piston
31432001f49Smrg */
31532001f49Smrgstatic void
31632001f49SmrgComputeConnectingRodPosition(float throwDist, float crankAngle,
31732001f49Smrg                             float connRodLength,
31832001f49Smrg                             float *x0, float *y0, float *x1, float *y1)
31932001f49Smrg{
32032001f49Smrg   *x0 = throwDist * cos(DEG_TO_RAD(crankAngle));
32132001f49Smrg   *y0 = throwDist * sin(DEG_TO_RAD(crankAngle));
32232001f49Smrg   *x1 = 0.0;
32332001f49Smrg   *y1 = PistonStrokePosition(throwDist, crankAngle, connRodLength);
32432001f49Smrg}
32532001f49Smrg
32632001f49Smrg
32732001f49Smrg/**
32832001f49Smrg * Compute total length of the crankshaft.
32932001f49Smrg */
33032001f49Smrgstatic float
33132001f49SmrgCrankshaftLength(const Engine *eng)
33232001f49Smrg{
33332001f49Smrg   float len = (eng->Cranks * 2 + 1) * eng->CrankJournalLength
33432001f49Smrg             + 2 * eng->Cranks * eng->CrankPlateThickness;
33532001f49Smrg   return len;
33632001f49Smrg}
33732001f49Smrg
33832001f49Smrg
33932001f49Smrg/**
34032001f49Smrg * Draw a piston.
34132001f49Smrg * Axis of piston = Z axis.  Wrist pin is centered on (0, 0, 0).
34232001f49Smrg */
34332001f49Smrgstatic void
34432001f49SmrgDrawPiston(const Engine *eng)
34532001f49Smrg{
34632001f49Smrg   const int slices = 30, stacks = 4, loops = 4;
34732001f49Smrg   const float innerRadius = 0.9 * eng->PistonRadius;
34832001f49Smrg   const float innerHeight = eng->PistonHeight - 0.15;
34932001f49Smrg   const float wristPinLength = 1.8 * eng->PistonRadius;
35032001f49Smrg
35132001f49Smrg   assert(Q);
35232001f49Smrg
35332001f49Smrg   glPushMatrix();
35432001f49Smrg   glTranslatef(0, 0, -1.1 * eng->WristPinRadius);
35532001f49Smrg
35632001f49Smrg   gluQuadricOrientation(Q, GLU_INSIDE);
35732001f49Smrg
35832001f49Smrg   /* bottom rim */
35932001f49Smrg   gluDisk(Q, innerRadius, eng->PistonRadius, slices, 1/*loops*/);
36032001f49Smrg
36132001f49Smrg   /* inner cylinder */
36232001f49Smrg   gluCylinder(Q, innerRadius, innerRadius, innerHeight, slices, stacks);
36332001f49Smrg
36432001f49Smrg   /* inside top */
36532001f49Smrg   glPushMatrix();
36632001f49Smrg   glTranslatef(0, 0, innerHeight);
36732001f49Smrg   gluDisk(Q, 0, innerRadius, slices, loops);
36832001f49Smrg   glPopMatrix();
36932001f49Smrg
37032001f49Smrg   gluQuadricOrientation(Q, GLU_OUTSIDE);
37132001f49Smrg
37232001f49Smrg   /* outer cylinder */
37332001f49Smrg   gluCylinder(Q, eng->PistonRadius, eng->PistonRadius, eng->PistonHeight,
37432001f49Smrg               slices, stacks);
37532001f49Smrg
37632001f49Smrg   /* top */
37732001f49Smrg   glTranslatef(0, 0, eng->PistonHeight);
37832001f49Smrg   gluDisk(Q, 0, eng->PistonRadius, slices, loops);
37932001f49Smrg
38032001f49Smrg   glPopMatrix();
38132001f49Smrg
38232001f49Smrg   /* wrist pin */
38332001f49Smrg   glPushMatrix();
38432001f49Smrg   glTranslatef(0, 0.5 * wristPinLength, 0.0);
38532001f49Smrg   glRotatef(90, 1, 0, 0);
38632001f49Smrg   gluCylinder(Q, eng->WristPinRadius, eng->WristPinRadius, wristPinLength,
38732001f49Smrg               slices, stacks);
38832001f49Smrg   glPopMatrix();
38932001f49Smrg}
39032001f49Smrg
39132001f49Smrg
39232001f49Smrg/**
39332001f49Smrg * Draw piston at particular position.
39432001f49Smrg */
39532001f49Smrgstatic void
39632001f49SmrgDrawPositionedPiston(const Engine *eng, float crankAngle)
39732001f49Smrg{
39832001f49Smrg   const float pos = PistonStrokePosition(eng->Throw, crankAngle,
39932001f49Smrg                                          eng->ConnectingRodLength);
40032001f49Smrg   glPushMatrix();
40132001f49Smrg      glRotatef(-90, 1, 0, 0);
40232001f49Smrg      glTranslatef(0, 0, pos);
40332001f49Smrg      if (eng->PistonList)
40432001f49Smrg         glCallList(eng->PistonList);
40532001f49Smrg      else
40632001f49Smrg         DrawPiston(eng);
40732001f49Smrg   glPopMatrix();
40832001f49Smrg}
40932001f49Smrg
41032001f49Smrg
41132001f49Smrg/**
41232001f49Smrg * Draw connector plate.  Used for crankshaft and connecting rods.
41332001f49Smrg */
41432001f49Smrgstatic void
41532001f49SmrgDrawConnector(float length, float thickness,
41632001f49Smrg              float bigEndRadius, float smallEndRadius)
41732001f49Smrg{
41832001f49Smrg   const float bigRadius = 1.2 * bigEndRadius;
41932001f49Smrg   const float smallRadius = 1.2 * smallEndRadius;
42032001f49Smrg   const float z0 = -0.5 * thickness, z1 = -z0;
42132001f49Smrg   GLfloat points[36][2], normals[36][2];
42232001f49Smrg   int i;
42332001f49Smrg
42432001f49Smrg   /* compute vertex locations, normals */
42532001f49Smrg   for (i = 0; i < 36; i++) {
42632001f49Smrg      const int angle = i * 10;
42732001f49Smrg      float x = cos(DEG_TO_RAD(angle));
42832001f49Smrg      float y = sin(DEG_TO_RAD(angle));
42932001f49Smrg      normals[i][0] = x;
43032001f49Smrg      normals[i][1] = y;
43132001f49Smrg      if (angle >= 0 && angle <= 180) {
43232001f49Smrg         x *= smallRadius;
43332001f49Smrg         y = y * smallRadius + length;
43432001f49Smrg      }
43532001f49Smrg      else {
43632001f49Smrg         x *= bigRadius;
43732001f49Smrg         y *= bigRadius;
43832001f49Smrg      }
43932001f49Smrg      points[i][0] = x;
44032001f49Smrg      points[i][1] = y;
44132001f49Smrg   }
44232001f49Smrg
44332001f49Smrg   /* front face */
44432001f49Smrg   glNormal3f(0, 0, 1);
44532001f49Smrg   glBegin(GL_POLYGON);
44632001f49Smrg   for (i = 0; i < 36; i++) {
44732001f49Smrg      glVertex3f(points[i][0], points[i][1], z1);
44832001f49Smrg   }
44932001f49Smrg   glEnd();
45032001f49Smrg
45132001f49Smrg   /* back face */
45232001f49Smrg   glNormal3f(0, 0, -1);
45332001f49Smrg   glBegin(GL_POLYGON);
45432001f49Smrg   for (i = 0; i < 36; i++) {
45532001f49Smrg      glVertex3f(points[35-i][0], points[35-i][1], z0);
45632001f49Smrg   }
45732001f49Smrg   glEnd();
45832001f49Smrg
45932001f49Smrg   /* edge */
46032001f49Smrg   glBegin(GL_QUAD_STRIP);
46132001f49Smrg   for (i = 0; i <= 36; i++) {
46232001f49Smrg      const int j = i % 36;
46332001f49Smrg      glNormal3f(normals[j][0], normals[j][1], 0);
46432001f49Smrg      glVertex3f(points[j][0], points[j][1], z1);
46532001f49Smrg      glVertex3f(points[j][0], points[j][1], z0);
46632001f49Smrg   }
46732001f49Smrg   glEnd();
46832001f49Smrg}
46932001f49Smrg
47032001f49Smrg
47132001f49Smrg/**
47232001f49Smrg * Draw a crankshaft.  Shaft lies along +Z axis, starting at zero.
47332001f49Smrg */
47432001f49Smrgstatic void
47532001f49SmrgDrawCrankshaft(const Engine *eng)
47632001f49Smrg{
47732001f49Smrg   const int slices = 20, stacks = 2;
47832001f49Smrg   const int n = eng->Cranks * 4 + 1;
47932001f49Smrg   const float phiStep = 360 / eng->Cranks;
48032001f49Smrg   float phi = -90.0;
48132001f49Smrg   int i;
48232001f49Smrg   float z = 0.0;
48332001f49Smrg
48432001f49Smrg   for (i = 0; i < n; i++) {
48532001f49Smrg      glPushMatrix();
48632001f49Smrg      glTranslatef(0, 0, z);
48732001f49Smrg      if (i & 1) {
48832001f49Smrg         /* draw a crank plate */
48932001f49Smrg         glRotatef(phi, 0, 0, 1);
49032001f49Smrg         glTranslatef(0, 0, 0.5 * eng->CrankPlateThickness);
49132001f49Smrg         DrawConnector(eng->Throw, eng->CrankPlateThickness,
49232001f49Smrg                       eng->CrankJournalRadius, eng->CrankPinRadius);
49332001f49Smrg         z += 0.2;
49432001f49Smrg         if (i % 4 == 3)
49532001f49Smrg            phi += phiStep;
49632001f49Smrg      }
49732001f49Smrg      else if (i % 4 == 0) {
49832001f49Smrg         /* draw crank journal segment */
49932001f49Smrg         gluCylinder(Q, eng->CrankJournalRadius, eng->CrankJournalRadius,
50032001f49Smrg                     eng->CrankJournalLength, slices, stacks);
50132001f49Smrg         z += eng->CrankJournalLength;
50232001f49Smrg      }
50332001f49Smrg      else if (i % 4 == 2) {
50432001f49Smrg         /* draw crank pin segment */
50532001f49Smrg         glRotatef(phi, 0, 0, 1);
50632001f49Smrg         glTranslatef(0, eng->Throw, 0);
50732001f49Smrg         gluCylinder(Q, eng->CrankPinRadius, eng->CrankPinRadius,
50832001f49Smrg                     eng->CrankJournalLength, slices, stacks);
50932001f49Smrg         z += eng->CrankJournalLength;
51032001f49Smrg      }
51132001f49Smrg      glPopMatrix();
51232001f49Smrg   }
51332001f49Smrg}
51432001f49Smrg
51532001f49Smrg
51632001f49Smrg/**
51732001f49Smrg * Draw crankshaft at a particular rotation.
51832001f49Smrg * \param crankAngle  current crankshaft rotation, in radians
51932001f49Smrg */
52032001f49Smrgstatic void
52132001f49SmrgDrawPositionedCrankshaft(const Engine *eng, float crankAngle)
52232001f49Smrg{
52332001f49Smrg   glPushMatrix();
52432001f49Smrg      glRotatef(crankAngle, 0, 0, 1);
52532001f49Smrg      if (eng->CrankList)
52632001f49Smrg         glCallList(eng->CrankList);
52732001f49Smrg      else
52832001f49Smrg         DrawCrankshaft(eng);
52932001f49Smrg   glPopMatrix();
53032001f49Smrg}
53132001f49Smrg
53232001f49Smrg
53332001f49Smrg/**
53432001f49Smrg * Draw a connecting rod at particular position.
53532001f49Smrg * \param eng  description of connecting rod to draw
53632001f49Smrg * \param crankAngle  current crankshaft rotation, in radians
53732001f49Smrg */
53832001f49Smrgstatic void
53932001f49SmrgDrawPositionedConnectingRod(const Engine *eng, float crankAngle)
54032001f49Smrg{
54132001f49Smrg   float x0, y0, x1, y1;
54232001f49Smrg   float d, phi;
54332001f49Smrg
54432001f49Smrg   ComputeConnectingRodPosition(eng->Throw, crankAngle,
54532001f49Smrg                                eng->ConnectingRodLength,
54632001f49Smrg                                &x0, &y0, &x1, &y1);
54732001f49Smrg   d = sqrt(eng->ConnectingRodLength * eng->ConnectingRodLength - x0 * x0);
54832001f49Smrg   phi = atan(x0 / d) * 180.0 / M_PI;
54932001f49Smrg
55032001f49Smrg   glPushMatrix();
55132001f49Smrg      glTranslatef(x0, y0, 0);
55232001f49Smrg      glRotatef(phi, 0, 0, 1);
55332001f49Smrg      if (eng->ConnRodList)
55432001f49Smrg         glCallList(eng->ConnRodList);
55532001f49Smrg      else
55632001f49Smrg         DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness,
55732001f49Smrg                       eng->CrankPinRadius, eng->WristPinRadius);
55832001f49Smrg   glPopMatrix();
55932001f49Smrg}
56032001f49Smrg
56132001f49Smrg
56232001f49Smrg/**
56332001f49Smrg * Draw a square with a hole in middle.
56432001f49Smrg */
56532001f49Smrgstatic void
56632001f49SmrgSquareWithHole(float squareSize, float holeRadius)
56732001f49Smrg{
56832001f49Smrg   int i;
56932001f49Smrg   glBegin(GL_QUAD_STRIP);
57032001f49Smrg   glNormal3f(0, 0, 1);
57132001f49Smrg   for (i = 0; i <= 360; i += 5) {
57232001f49Smrg      const float x1 = holeRadius * cos(DEG_TO_RAD(i));
57332001f49Smrg      const float y1 = holeRadius * sin(DEG_TO_RAD(i));
57432001f49Smrg      float x2 = 0.0F, y2 = 0.0F;
57532001f49Smrg      if (i > 315 || i <= 45) {
57632001f49Smrg         x2 = squareSize;
57732001f49Smrg         y2 = squareSize * tan(DEG_TO_RAD(i));
57832001f49Smrg      }
57932001f49Smrg      else if (i > 45 && i <= 135) {
58032001f49Smrg         x2 = -squareSize * tan(DEG_TO_RAD(i - 90));
58132001f49Smrg         y2 = squareSize;
58232001f49Smrg      }
58332001f49Smrg      else if (i > 135 && i <= 225) {
58432001f49Smrg         x2 = -squareSize;
58532001f49Smrg         y2 = -squareSize * tan(DEG_TO_RAD(i-180));
58632001f49Smrg      }
58732001f49Smrg      else if (i > 225 && i <= 315) {
58832001f49Smrg         x2 = squareSize * tan(DEG_TO_RAD(i - 270));
58932001f49Smrg         y2 = -squareSize;
59032001f49Smrg      }
59132001f49Smrg      glVertex2f(x1, y1); /* inner circle */
59232001f49Smrg      glVertex2f(x2, y2); /* outer square */
59332001f49Smrg   }
59432001f49Smrg   glEnd();
59532001f49Smrg}
59632001f49Smrg
59732001f49Smrg
59832001f49Smrg/**
59932001f49Smrg * Draw block with hole through middle.
60032001f49Smrg * Hole is centered on Z axis.
60132001f49Smrg * Bottom of block is at z=0, top of block is at z = blockHeight.
60232001f49Smrg * index is in [0, count - 1] to determine which block faces are drawn.
60332001f49Smrg */
60432001f49Smrgstatic void
60532001f49SmrgDrawBlockWithHole(float blockSize, float blockHeight, float holeRadius,
60632001f49Smrg                  int index, int count)
60732001f49Smrg{
60832001f49Smrg   const int slices = 30, stacks = 4;
60932001f49Smrg   const float x = blockSize;
61032001f49Smrg   const float y = blockSize;
61132001f49Smrg   const float z0 = 0;
61232001f49Smrg   const float z1 = blockHeight;
61332001f49Smrg
61432001f49Smrg   assert(index < count);
61532001f49Smrg   assert(Q);
61632001f49Smrg   gluQuadricOrientation(Q, GLU_INSIDE);
61732001f49Smrg
61832001f49Smrg   glBegin(GL_QUADS);
61932001f49Smrg   /* +X face */
62032001f49Smrg   glNormal3f(1, 0, 0);
62132001f49Smrg   glVertex3f( x, -y, z0);
62232001f49Smrg   glVertex3f( x, y, z0);
62332001f49Smrg   glVertex3f( x, y, z1);
62432001f49Smrg   glVertex3f( x, -y, z1);
62532001f49Smrg   /* -X face */
62632001f49Smrg   glNormal3f(-1, 0, 0);
62732001f49Smrg   glVertex3f(-x, -y, z1);
62832001f49Smrg   glVertex3f(-x, y, z1);
62932001f49Smrg   glVertex3f(-x, y, z0);
63032001f49Smrg   glVertex3f(-x, -y, z0);
63132001f49Smrg   if (index == 0) {
63232001f49Smrg      /* +Y face */
63332001f49Smrg      glNormal3f(0, 1, 0);
63432001f49Smrg      glVertex3f(-x, y, z1);
63532001f49Smrg      glVertex3f( x, y, z1);
63632001f49Smrg      glVertex3f( x, y, z0);
63732001f49Smrg      glVertex3f(-x, y, z0);
63832001f49Smrg   }
63932001f49Smrg   if (index == count - 1) {
64032001f49Smrg      /* -Y face */
64132001f49Smrg      glNormal3f(0, -1, 0);
64232001f49Smrg      glVertex3f(-x, -y, z0);
64332001f49Smrg      glVertex3f( x, -y, z0);
64432001f49Smrg      glVertex3f( x, -y, z1);
64532001f49Smrg      glVertex3f(-x, -y, z1);
64632001f49Smrg   }
64732001f49Smrg   glEnd();
64832001f49Smrg
64932001f49Smrg   /* cylinder / hole */
65032001f49Smrg   gluCylinder(Q, holeRadius, holeRadius, blockHeight, slices, stacks);
65132001f49Smrg
65232001f49Smrg   /* face at z0 */
65332001f49Smrg   glPushMatrix();
65432001f49Smrg   glRotatef(180, 1, 0, 0);
65532001f49Smrg   SquareWithHole(blockSize, holeRadius);
65632001f49Smrg   glPopMatrix();
65732001f49Smrg
65832001f49Smrg   /* face at z1 */
65932001f49Smrg   glTranslatef(0, 0, z1);
66032001f49Smrg   SquareWithHole(blockSize, holeRadius);
66132001f49Smrg
66232001f49Smrg   gluQuadricOrientation(Q, GLU_OUTSIDE);
66332001f49Smrg}
66432001f49Smrg
66532001f49Smrg
66632001f49Smrg/**
66732001f49Smrg * Draw the engine block.
66832001f49Smrg */
66932001f49Smrgstatic void
67032001f49SmrgDrawEngineBlock(const Engine *eng)
67132001f49Smrg{
67232001f49Smrg   const float blockHeight = eng->Throw + 1.5 * eng->PistonHeight;
67332001f49Smrg   const float cylRadius = 1.01 * eng->PistonRadius;
67432001f49Smrg   const float blockSize = 0.5 * PistonSpacing(eng);
67532001f49Smrg   const int pistonsPerCrank = eng->Pistons / eng->Cranks;
67632001f49Smrg   int i;
67732001f49Smrg
67832001f49Smrg   for (i = 0; i < eng->Pistons; i++) {
67932001f49Smrg      const float z = PistonShaftPosition(eng, i);
68032001f49Smrg      const int crank = i / pistonsPerCrank;
68132001f49Smrg      int k;
68232001f49Smrg
68332001f49Smrg      glPushMatrix();
68432001f49Smrg         glTranslatef(0, 0, z);
68532001f49Smrg
68632001f49Smrg         /* additional rotation for kth piston per crank */
68732001f49Smrg         k = i % pistonsPerCrank;
68832001f49Smrg         glRotatef(k * -eng->V_Angle, 0, 0, 1);
68932001f49Smrg
69032001f49Smrg         /* the block */
69132001f49Smrg         glRotatef(-90, 1, 0, 0);
69232001f49Smrg         glTranslatef(0, 0, eng->Throw * 2);
69332001f49Smrg         DrawBlockWithHole(blockSize, blockHeight, cylRadius,
69432001f49Smrg                           crank, eng->Cranks);
69532001f49Smrg      glPopMatrix();
69632001f49Smrg   }
69732001f49Smrg}
69832001f49Smrg
69932001f49Smrg
70032001f49Smrg/**
70132001f49Smrg * Generate display lists for engine parts.
70232001f49Smrg */
70332001f49Smrgstatic void
70432001f49SmrgGenerateDisplayLists(Engine *eng)
70532001f49Smrg{
70632001f49Smrg   eng->CrankList = glGenLists(1);
70732001f49Smrg   glNewList(eng->CrankList, GL_COMPILE);
70832001f49Smrg   DrawCrankshaft(eng);
70932001f49Smrg   glEndList();
71032001f49Smrg
71132001f49Smrg   eng->ConnRodList = glGenLists(1);
71232001f49Smrg   glNewList(eng->ConnRodList, GL_COMPILE);
71332001f49Smrg   DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness,
71432001f49Smrg                 eng->CrankPinRadius, eng->WristPinRadius);
71532001f49Smrg   glEndList();
71632001f49Smrg
71732001f49Smrg   eng->PistonList = glGenLists(1);
71832001f49Smrg   glNewList(eng->PistonList, GL_COMPILE);
71932001f49Smrg   DrawPiston(eng);
72032001f49Smrg   glEndList();
72132001f49Smrg
72232001f49Smrg   eng->BlockList = glGenLists(1);
72332001f49Smrg   glNewList(eng->BlockList, GL_COMPILE);
72432001f49Smrg   DrawEngineBlock(eng);
72532001f49Smrg   glEndList();
72632001f49Smrg}
72732001f49Smrg
72832001f49Smrg
72932001f49Smrg/**
73032001f49Smrg * Free engine display lists (render with immediate mode).
73132001f49Smrg */
73232001f49Smrgstatic void
73332001f49SmrgFreeDisplayLists(Engine *eng)
73432001f49Smrg{
73532001f49Smrg   glDeleteLists(eng->CrankList, 1);
73632001f49Smrg   eng->CrankList = 0;
73732001f49Smrg   glDeleteLists(eng->ConnRodList, 1);
73832001f49Smrg   eng->ConnRodList = 0;
73932001f49Smrg   glDeleteLists(eng->PistonList, 1);
74032001f49Smrg   eng->PistonList = 0;
74132001f49Smrg   glDeleteLists(eng->BlockList, 1);
74232001f49Smrg   eng->BlockList = 0;
74332001f49Smrg}
74432001f49Smrg
74532001f49Smrg
74632001f49Smrg/**
74732001f49Smrg * Draw complete engine.
74832001f49Smrg * \param eng  description of engine to draw
74932001f49Smrg * \param crankAngle  current crankshaft angle, in radians
75032001f49Smrg */
75132001f49Smrgstatic void
75232001f49SmrgDrawEngine(const Engine *eng, float crankAngle)
75332001f49Smrg{
75432001f49Smrg   const float crankDelta = 360.0 / eng->Cranks;
75532001f49Smrg   const float crankLen = CrankshaftLength(eng);
75632001f49Smrg   const int pistonsPerCrank = eng->Pistons / eng->Cranks;
75732001f49Smrg   int i;
75832001f49Smrg
75932001f49Smrg   glPushMatrix();
76032001f49Smrg   glRotatef(eng->V_Angle * 0.5, 0, 0, 1);
76132001f49Smrg   glTranslatef(0, 0, -0.5 * crankLen);
76232001f49Smrg
76332001f49Smrg   /* crankshaft */
76432001f49Smrg   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, CrankshaftColor);
76532001f49Smrg   glColor4fv(CrankshaftColor);
76632001f49Smrg   DrawPositionedCrankshaft(eng, crankAngle);
76732001f49Smrg
76832001f49Smrg   for (i = 0; i < eng->Pistons; i++) {
76932001f49Smrg      const float z = PistonShaftPosition(eng, i);
77032001f49Smrg      const int crank = i / pistonsPerCrank;
77132001f49Smrg      float rot = crankAngle + crank * crankDelta;
77232001f49Smrg      int k;
77332001f49Smrg
77432001f49Smrg      glPushMatrix();
77532001f49Smrg         glTranslatef(0, 0, z);
77632001f49Smrg
77732001f49Smrg         /* additional rotation for kth piston per crank */
77832001f49Smrg         k = i % pistonsPerCrank;
77932001f49Smrg         glRotatef(k * -eng->V_Angle, 0, 0, 1);
78032001f49Smrg         rot += k * eng->V_Angle;
78132001f49Smrg
78232001f49Smrg         /* piston */
78332001f49Smrg         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, PistonColor);
78432001f49Smrg         glColor4fv(PistonColor);
78532001f49Smrg         DrawPositionedPiston(eng, rot);
78632001f49Smrg
78732001f49Smrg         /* connecting rod */
78832001f49Smrg         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ConnRodColor);
78932001f49Smrg         glColor4fv(ConnRodColor);
79032001f49Smrg         DrawPositionedConnectingRod(eng, rot);
79132001f49Smrg      glPopMatrix();
79232001f49Smrg   }
79332001f49Smrg
79432001f49Smrg   if (Render.ShowBlock) {
79532001f49Smrg      const GLboolean blend = glIsEnabled(GL_BLEND);
79632001f49Smrg
79732001f49Smrg      glDepthMask(GL_FALSE);
79832001f49Smrg      if (!blend) {
79932001f49Smrg         glEnable(GL_BLEND);
80032001f49Smrg      }
80132001f49Smrg      glEnable(GL_CULL_FACE);
80232001f49Smrg
80332001f49Smrg      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, BlockColor);
80432001f49Smrg      glColor4fv(BlockColor);
80532001f49Smrg      if (eng->CrankList)
80632001f49Smrg         glCallList(eng->BlockList);
80732001f49Smrg      else
80832001f49Smrg         DrawEngineBlock(eng);
80932001f49Smrg
81032001f49Smrg      glDisable(GL_CULL_FACE);
81132001f49Smrg      glDepthMask(GL_TRUE);
81232001f49Smrg      if (!blend) {
81332001f49Smrg         glDisable(GL_BLEND);
81432001f49Smrg      }
81532001f49Smrg   }
81632001f49Smrg
81732001f49Smrg   glPopMatrix();
81832001f49Smrg}
81932001f49Smrg
82032001f49Smrg
82132001f49Smrgstatic void
82232001f49SmrgDrawBox(void)
82332001f49Smrg{
82432001f49Smrg   const float xmin = -3.0, xmax = 3.0;
82532001f49Smrg   const float ymin = -1.0, ymax = 3.0;
82632001f49Smrg   const float zmin = -4.0, zmax = 4.0;
82732001f49Smrg   const float step = 0.5;
82832001f49Smrg   const float d = 0.01;
82932001f49Smrg   float x, y, z;
83032001f49Smrg   GLboolean lit = glIsEnabled(GL_LIGHTING);
83132001f49Smrg   GLboolean tex = glIsEnabled(GL_TEXTURE_2D);
83232001f49Smrg
83332001f49Smrg   glDisable(GL_LIGHTING);
83432001f49Smrg   glDisable(GL_TEXTURE_2D);
83532001f49Smrg   glLineWidth(1.0);
83632001f49Smrg
83732001f49Smrg   glColor3f(1, 1, 1);
83832001f49Smrg
83932001f49Smrg   /* Z min */
84032001f49Smrg   glBegin(GL_LINES);
84132001f49Smrg   for (x = xmin; x <= xmax; x += step) {
84232001f49Smrg      glVertex3f(x, ymin, zmin);
84332001f49Smrg      glVertex3f(x, ymax, zmin);
84432001f49Smrg   }
84532001f49Smrg   glEnd();
84632001f49Smrg   glBegin(GL_LINES);
84732001f49Smrg   for (y = ymin; y <= ymax; y += step) {
84832001f49Smrg      glVertex3f(xmin, y, zmin);
84932001f49Smrg      glVertex3f(xmax, y, zmin);
85032001f49Smrg   }
85132001f49Smrg   glEnd();
85232001f49Smrg
85332001f49Smrg   /* Y min */
85432001f49Smrg   glBegin(GL_LINES);
85532001f49Smrg   for (x = xmin; x <= xmax; x += step) {
85632001f49Smrg      glVertex3f(x, ymin, zmin);
85732001f49Smrg      glVertex3f(x, ymin, zmax);
85832001f49Smrg   }
85932001f49Smrg   glEnd();
86032001f49Smrg   glBegin(GL_LINES);
86132001f49Smrg   for (z = zmin; z <= zmax; z += step) {
86232001f49Smrg      glVertex3f(xmin, ymin, z);
86332001f49Smrg      glVertex3f(xmax, ymin, z);
86432001f49Smrg   }
86532001f49Smrg   glEnd();
86632001f49Smrg
86732001f49Smrg   /* X min */
86832001f49Smrg   glBegin(GL_LINES);
86932001f49Smrg   for (y = ymin; y <= ymax; y += step) {
87032001f49Smrg      glVertex3f(xmin, y, zmin);
87132001f49Smrg      glVertex3f(xmin, y, zmax);
87232001f49Smrg   }
87332001f49Smrg   glEnd();
87432001f49Smrg   glBegin(GL_LINES);
87532001f49Smrg   for (z = zmin; z <= zmax; z += step) {
87632001f49Smrg      glVertex3f(xmin, ymin, z);
87732001f49Smrg      glVertex3f(xmin, ymax, z);
87832001f49Smrg   }
87932001f49Smrg   glEnd();
88032001f49Smrg
88132001f49Smrg   glColor3f(0.4, 0.4, 0.6);
88232001f49Smrg   glBegin(GL_QUADS);
88332001f49Smrg   /* xmin */
88432001f49Smrg   glVertex3f(xmin-d, ymin, zmin);
88532001f49Smrg   glVertex3f(xmin-d, ymax, zmin);
88632001f49Smrg   glVertex3f(xmin-d, ymax, zmax);
88732001f49Smrg   glVertex3f(xmin-d, ymin, zmax);
88832001f49Smrg   /* ymin */
88932001f49Smrg   glVertex3f(xmin, ymin-d, zmin);
89032001f49Smrg   glVertex3f(xmax, ymin-d, zmin);
89132001f49Smrg   glVertex3f(xmax, ymin-d, zmax);
89232001f49Smrg   glVertex3f(xmin, ymin-d, zmax);
89332001f49Smrg   /* zmin */
89432001f49Smrg   glVertex3f(xmin, ymin, zmin-d);
89532001f49Smrg   glVertex3f(xmax, ymin, zmin-d);
89632001f49Smrg   glVertex3f(xmax, ymax, zmin-d);
89732001f49Smrg   glVertex3f(xmin, ymax, zmin-d);
89832001f49Smrg   glEnd();
89932001f49Smrg
90032001f49Smrg   if (lit)
90132001f49Smrg      glEnable(GL_LIGHTING);
90232001f49Smrg   if (tex)
90332001f49Smrg      glEnable(GL_TEXTURE_2D);
90432001f49Smrg}
90532001f49Smrg
90632001f49Smrg
90732001f49Smrgstatic void
90832001f49SmrgPrintString(const char *s)
90932001f49Smrg{
91032001f49Smrg   while (*s) {
91132001f49Smrg      glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (int) *s);
91232001f49Smrg      s++;
91332001f49Smrg   }
91432001f49Smrg}
91532001f49Smrg
91632001f49Smrg
91732001f49Smrgstatic int
91832001f49SmrgComputeFPS(void)
91932001f49Smrg{
92032001f49Smrg   static double t0 = -1.0;
92132001f49Smrg   static int frames = 0;
92232001f49Smrg   double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
92332001f49Smrg   static int fps = 0;
92432001f49Smrg
92532001f49Smrg   frames++;
92632001f49Smrg
92732001f49Smrg   if (t0 < 0.0) {
92832001f49Smrg      t0 = t;
92932001f49Smrg      fps = 0;
93032001f49Smrg   }
93132001f49Smrg   else if (t - t0 >= 1.0) {
93232001f49Smrg      fps = (int) (frames / (t - t0) + 0.5);
93332001f49Smrg      t0 = t;
93432001f49Smrg      frames = 0;
93532001f49Smrg   }
93632001f49Smrg
93732001f49Smrg   return fps;
93832001f49Smrg}
93932001f49Smrg
94032001f49Smrg
94132001f49Smrgstatic void
94232001f49SmrgDraw(void)
94332001f49Smrg{
94432001f49Smrg   int fps;
94532001f49Smrg   GLfloat rot[4][4];
94632001f49Smrg
94732001f49Smrg   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
94832001f49Smrg
94932001f49Smrg   glPushMatrix();
95032001f49Smrg
95132001f49Smrg      glTranslatef(0.0, 0.0, -View.Distance);
95232001f49Smrg      build_rotmatrix(rot, View.CurQuat);
95332001f49Smrg      glMultMatrixf(&rot[0][0]);
95432001f49Smrg
95532001f49Smrg      glPushMatrix();
95632001f49Smrg         glTranslatef(0, -0.75, 0);
95732001f49Smrg         if (Render.DrawBox)
95832001f49Smrg            DrawBox();
95932001f49Smrg         DrawEngine(Engines + CurEngine, Theta);
96032001f49Smrg      glPopMatrix();
96132001f49Smrg
96232001f49Smrg   glPopMatrix();
96332001f49Smrg
96432001f49Smrg   fps = ComputeFPS();
96532001f49Smrg   if (Render.ShowInfo) {
96632001f49Smrg      GLboolean lit = glIsEnabled(GL_LIGHTING);
96732001f49Smrg      GLboolean tex = glIsEnabled(GL_TEXTURE_2D);
96832001f49Smrg      char s[100];
96932001f49Smrg      sprintf(s, "%s  %d FPS  %s", Engines[CurEngine].Name, fps,
97032001f49Smrg              Render.UseLists ? "Display Lists" : "Immediate mode");
97132001f49Smrg      glDisable(GL_LIGHTING);
97232001f49Smrg      glDisable(GL_TEXTURE_2D);
97332001f49Smrg      glColor3f(1, 1 , 1);
97432001f49Smrg      glWindowPos2iARB(10, 10);
97532001f49Smrg      PrintString(s);
97632001f49Smrg      if (lit)
97732001f49Smrg	 glEnable(GL_LIGHTING);
97832001f49Smrg      if (tex)
97932001f49Smrg	 glEnable(GL_TEXTURE_2D);
98032001f49Smrg   }
98132001f49Smrg
98232001f49Smrg   /* also print out a periodic fps to stdout.  useful for trying to
98332001f49Smrg    * figure out the performance impact of rendering the string above
98432001f49Smrg    * with glBitmap.
98532001f49Smrg    */
98632001f49Smrg   {
98732001f49Smrg      static GLint T0 = 0;
98832001f49Smrg      static GLint Frames = 0;
98932001f49Smrg      GLint t = glutGet(GLUT_ELAPSED_TIME);
99032001f49Smrg
99132001f49Smrg      Frames++;
99232001f49Smrg
99332001f49Smrg      if (t - T0 >= 5000) {
99432001f49Smrg         GLfloat seconds = (t - T0) / 1000.0;
99532001f49Smrg         GLfloat fps = Frames / seconds;
99632001f49Smrg         printf("%d frames in %6.3f seconds = %6.3f FPS\n", Frames, seconds, fps);
99732001f49Smrg         fflush(stdout);
99832001f49Smrg         T0 = t;
99932001f49Smrg         Frames = 0;
100032001f49Smrg      }
100132001f49Smrg   }
100232001f49Smrg
100332001f49Smrg
100432001f49Smrg   glutSwapBuffers();
100532001f49Smrg}
100632001f49Smrg
100732001f49Smrg
100832001f49Smrg/**
100932001f49Smrg * Handle window resize.
101032001f49Smrg */
101132001f49Smrgstatic void
101232001f49SmrgReshape(int width, int height)
101332001f49Smrg{
101432001f49Smrg   float ar = (float) width / height;
101532001f49Smrg   float s = 0.5;
101632001f49Smrg   glViewport(0, 0, width, height);
101732001f49Smrg   glMatrixMode(GL_PROJECTION);
101832001f49Smrg   glLoadIdentity();
101932001f49Smrg   glFrustum(-ar * s, ar * s, -s, s, 2.0, 50.0);
102032001f49Smrg   glMatrixMode(GL_MODELVIEW);
102132001f49Smrg   glLoadIdentity();
102232001f49Smrg   WinWidth = width;
102332001f49Smrg   WinHeight = height;
102432001f49Smrg}
102532001f49Smrg
102632001f49Smrg
102732001f49Smrg/**
102832001f49Smrg * Handle mouse button.
102932001f49Smrg */
103032001f49Smrgstatic void
103132001f49SmrgMouse(int button, int state, int x, int y)
103232001f49Smrg{
103332001f49Smrg   if (button == GLUT_LEFT_BUTTON) {
103432001f49Smrg      if (state == GLUT_DOWN) {
103532001f49Smrg         View.StartX = x;
103632001f49Smrg         View.StartY = y;
103732001f49Smrg         View.Rotating = GL_TRUE;
103832001f49Smrg      }
103932001f49Smrg      else if (state == GLUT_UP) {
104032001f49Smrg         View.Rotating = GL_FALSE;
104132001f49Smrg      }
104232001f49Smrg   }
104332001f49Smrg   else if (button == GLUT_MIDDLE_BUTTON) {
104432001f49Smrg      if (state == GLUT_DOWN) {
104532001f49Smrg         View.StartX = x;
104632001f49Smrg         View.StartY = y;
104732001f49Smrg         View.StartDistance = View.Distance;
104832001f49Smrg         View.Translating = GL_TRUE;
104932001f49Smrg      }
105032001f49Smrg      else if (state == GLUT_UP) {
105132001f49Smrg         View.Translating = GL_FALSE;
105232001f49Smrg      }
105332001f49Smrg   }
105432001f49Smrg}
105532001f49Smrg
105632001f49Smrg
105732001f49Smrg/**
105832001f49Smrg * Handle mouse motion
105932001f49Smrg */
106032001f49Smrgstatic void
106132001f49SmrgMotion(int x, int y)
106232001f49Smrg{
106332001f49Smrg   int i;
106432001f49Smrg   if (View.Rotating) {
106532001f49Smrg      float x0 = (2.0 * View.StartX - WinWidth) / WinWidth;
106632001f49Smrg      float y0 = (WinHeight - 2.0 * View.StartY) / WinHeight;
106732001f49Smrg      float x1 = (2.0 * x - WinWidth) / WinWidth;
106832001f49Smrg      float y1 = (WinHeight - 2.0 * y) / WinHeight;
106932001f49Smrg      float q[4];
107032001f49Smrg
107132001f49Smrg      trackball(q, x0, y0, x1, y1);
107232001f49Smrg      View.StartX = x;
107332001f49Smrg      View.StartY = y;
107432001f49Smrg      for (i = 0; i < 1; i++)
107532001f49Smrg         add_quats(q, View.CurQuat, View.CurQuat);
107632001f49Smrg
107732001f49Smrg      glutPostRedisplay();
107832001f49Smrg   }
107932001f49Smrg   else if (View.Translating) {
108032001f49Smrg      float dz = 0.01 * (y - View.StartY);
108132001f49Smrg      View.Distance = View.StartDistance + dz;
108232001f49Smrg      glutPostRedisplay();
108332001f49Smrg   }
108432001f49Smrg}
108532001f49Smrg
108632001f49Smrg
108732001f49Smrg/**
108832001f49Smrg ** Menu Callbacks
108932001f49Smrg **/
109032001f49Smrg
109132001f49Smrgstatic void
109232001f49SmrgOptAnimation(void)
109332001f49Smrg{
109432001f49Smrg   Render.Anim = !Render.Anim;
109532001f49Smrg   if (Render.Anim)
109632001f49Smrg      glutIdleFunc(Idle);
109732001f49Smrg   else
109832001f49Smrg      glutIdleFunc(NULL);
109932001f49Smrg}
110032001f49Smrg
110132001f49Smrgstatic void
110232001f49SmrgOptChangeEngine(void)
110332001f49Smrg{
110432001f49Smrg   CurEngine = (CurEngine + 1) % NUM_ENGINES;
110532001f49Smrg}
110632001f49Smrg
110732001f49Smrgstatic void
110832001f49SmrgOptRenderMode(void)
110932001f49Smrg{
111032001f49Smrg   Render.Mode++;
111132001f49Smrg   if (Render.Mode > TEXTURED)
111232001f49Smrg      Render.Mode = 0;
111332001f49Smrg   SetRenderState(Render.Mode);
111432001f49Smrg}
111532001f49Smrg
111632001f49Smrgstatic void
111732001f49SmrgOptDisplayLists(void)
111832001f49Smrg{
111932001f49Smrg   int i;
112032001f49Smrg   Render.UseLists = !Render.UseLists;
112132001f49Smrg   if (Render.UseLists) {
112232001f49Smrg      for (i = 0; i < NUM_ENGINES; i++) {
112332001f49Smrg         GenerateDisplayLists(Engines + i);
112432001f49Smrg      }
112532001f49Smrg   }
112632001f49Smrg   else {
112732001f49Smrg      for (i = 0; i < NUM_ENGINES; i++) {
112832001f49Smrg         FreeDisplayLists(Engines + i);
112932001f49Smrg      }
113032001f49Smrg   }
113132001f49Smrg}
113232001f49Smrg
113332001f49Smrgstatic void
113432001f49SmrgOptShowBlock(void)
113532001f49Smrg{
113632001f49Smrg   Render.ShowBlock = !Render.ShowBlock;
113732001f49Smrg}
113832001f49Smrg
113932001f49Smrgstatic void
114032001f49SmrgOptShowInfo(void)
114132001f49Smrg{
114232001f49Smrg   Render.ShowInfo = !Render.ShowInfo;
114332001f49Smrg}
114432001f49Smrg
114532001f49Smrgstatic void
114632001f49SmrgOptShowBox(void)
114732001f49Smrg{
114832001f49Smrg   Render.DrawBox = !Render.DrawBox;
114932001f49Smrg}
115032001f49Smrg
115132001f49Smrgstatic void
115232001f49SmrgOptRotate(void)
115332001f49Smrg{
115432001f49Smrg   Theta += 5.0;
115532001f49Smrg}
115632001f49Smrg
115732001f49Smrgstatic void
115832001f49SmrgOptExit(void)
115932001f49Smrg{
116032001f49Smrg   glutDestroyWindow(Win);
116132001f49Smrg   exit(0);
116232001f49Smrg}
116332001f49Smrg
116432001f49Smrg
116532001f49Smrg/**
116632001f49Smrg * Define menu entries (w/ keyboard shortcuts)
116732001f49Smrg */
116832001f49Smrg
116932001f49Smrgtypedef struct
117032001f49Smrg{
117132001f49Smrg   const char *Text;
117232001f49Smrg   const char Key;
117332001f49Smrg   void (*Function)(void);
117432001f49Smrg} MenuInfo;
117532001f49Smrg
117632001f49Smrgstatic const MenuInfo MenuItems[] = {
117732001f49Smrg   { "Animation", 'a', OptAnimation },
117832001f49Smrg   { "Change Engine", 'e', OptChangeEngine },
117932001f49Smrg   { "Rendering Style", 'm', OptRenderMode },
118032001f49Smrg   { "Display Lists", 'd', OptDisplayLists },
118132001f49Smrg   { "Show Block", 'b', OptShowBlock },
118232001f49Smrg   { "Show Info", 'i', OptShowInfo },
118332001f49Smrg   { "Show Box", 'x', OptShowBox },
118432001f49Smrg   { "Exit", 27, OptExit },
118532001f49Smrg   { NULL, 'r', OptRotate },
118632001f49Smrg   { NULL, 0, NULL }
118732001f49Smrg};
118832001f49Smrg
118932001f49Smrg
119032001f49Smrg/**
119132001f49Smrg * Handle menu selection.
119232001f49Smrg */
119332001f49Smrgstatic void
119432001f49SmrgMenuHandler(int entry)
119532001f49Smrg{
119632001f49Smrg   MenuItems[entry].Function();
119732001f49Smrg   glutPostRedisplay();
119832001f49Smrg}
119932001f49Smrg
120032001f49Smrg
120132001f49Smrg/**
120232001f49Smrg * Make pop-up menu.
120332001f49Smrg */
120432001f49Smrgstatic void
120532001f49SmrgMakeMenu(void)
120632001f49Smrg{
120732001f49Smrg   int i;
120832001f49Smrg   glutCreateMenu(MenuHandler);
120932001f49Smrg   for (i = 0; MenuItems[i].Text; i++) {
121032001f49Smrg      glutAddMenuEntry(MenuItems[i].Text, i);
121132001f49Smrg   }
121232001f49Smrg   glutAttachMenu(GLUT_RIGHT_BUTTON);
121332001f49Smrg}
121432001f49Smrg
121532001f49Smrg
121632001f49Smrg/**
121732001f49Smrg * Handle keyboard event.
121832001f49Smrg */
121932001f49Smrgstatic void
122032001f49SmrgKey(unsigned char key, int x, int y)
122132001f49Smrg{
122232001f49Smrg   int i;
122332001f49Smrg   (void) x; (void) y;
122432001f49Smrg   for (i = 0; MenuItems[i].Key; i++) {
122532001f49Smrg      if (MenuItems[i].Key == key) {
122632001f49Smrg         MenuItems[i].Function();
122732001f49Smrg         glutPostRedisplay();
122832001f49Smrg         break;
122932001f49Smrg      }
123032001f49Smrg   }
123132001f49Smrg}
123232001f49Smrg
123332001f49Smrg
123432001f49Smrgstatic
123532001f49Smrgvoid LoadTexture(void)
123632001f49Smrg{
123732001f49Smrg   GLboolean convolve = GL_FALSE;
123832001f49Smrg
123932001f49Smrg   glGenTextures(1, &TextureObj);
124032001f49Smrg   glBindTexture(GL_TEXTURE_2D, TextureObj);
124132001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
124232001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
124332001f49Smrg   glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
124432001f49Smrg   glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
124532001f49Smrg
124632001f49Smrg   if (convolve) {
124732001f49Smrg#define FILTER_SIZE 7
124832001f49Smrg      /* use convolution to blur the texture to simulate a dull finish
124932001f49Smrg       * on the object.
125032001f49Smrg       */
125132001f49Smrg      GLubyte *img;
125232001f49Smrg      GLenum format;
125332001f49Smrg      GLint w, h;
125432001f49Smrg      GLfloat filter[FILTER_SIZE][FILTER_SIZE][4];
125532001f49Smrg
125632001f49Smrg      for (h = 0; h < FILTER_SIZE; h++) {
125732001f49Smrg         for (w = 0; w < FILTER_SIZE; w++) {
125832001f49Smrg            const GLfloat k = 1.0 / (FILTER_SIZE * FILTER_SIZE);
125932001f49Smrg            filter[h][w][0] = k;
126032001f49Smrg            filter[h][w][1] = k;
126132001f49Smrg            filter[h][w][2] = k;
126232001f49Smrg            filter[h][w][3] = k;
126332001f49Smrg         }
126432001f49Smrg      }
126532001f49Smrg
126632001f49Smrg      glEnable(GL_CONVOLUTION_2D);
126732001f49Smrg      glConvolutionParameteri(GL_CONVOLUTION_2D,
126832001f49Smrg                              GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER);
126932001f49Smrg      glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_RGBA,
127032001f49Smrg                            FILTER_SIZE, FILTER_SIZE,
127132001f49Smrg                            GL_RGBA, GL_FLOAT, filter);
127232001f49Smrg
127332001f49Smrg      img = LoadRGBImage(TEXTURE_FILE, &w, &h, &format);
127432001f49Smrg      if (!img) {
127532001f49Smrg         printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE);
127632001f49Smrg         exit(1);
127732001f49Smrg      }
127832001f49Smrg
127932001f49Smrg      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
128032001f49Smrg                   format, GL_UNSIGNED_BYTE, img);
128132001f49Smrg      free(img);
128232001f49Smrg   }
128332001f49Smrg   else {
128432001f49Smrg      if (!LoadRGBMipmaps(TEXTURE_FILE, GL_RGB)) {
128532001f49Smrg         printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE);
128632001f49Smrg         exit(1);
128732001f49Smrg      }
128832001f49Smrg   }
128932001f49Smrg}
129032001f49Smrg
129132001f49Smrg
129232001f49Smrgstatic void
129332001f49SmrgInit(void)
129432001f49Smrg{
129532001f49Smrg   const GLfloat lightColor[4] = { 0.7, 0.7, 0.7, 1.0 };
129632001f49Smrg   const GLfloat specular[4] = { 0.8, 0.8, 0.8, 1.0 };
129732001f49Smrg   const GLfloat backColor[4] = { 1, 1, 0, 0 };
129832001f49Smrg
129932001f49Smrg   Q = gluNewQuadric();
130032001f49Smrg   gluQuadricNormals(Q, GLU_SMOOTH);
130132001f49Smrg
130232001f49Smrg   LoadTexture();
130332001f49Smrg
130432001f49Smrg   glClearColor(0.3, 0.3, 0.3, 0.0);
130532001f49Smrg   glEnable(GL_DEPTH_TEST);
130632001f49Smrg   glEnable(GL_LIGHTING);
130732001f49Smrg   glEnable(GL_LIGHT0);
130832001f49Smrg   glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
130932001f49Smrg   glMaterialf(GL_FRONT, GL_SHININESS, 40);
131032001f49Smrg   glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
131132001f49Smrg   glEnable(GL_NORMALIZE);
131232001f49Smrg
131332001f49Smrg   glMaterialfv(GL_BACK, GL_DIFFUSE, backColor);
131432001f49Smrg#if 0
131532001f49Smrg   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
131632001f49Smrg#endif
131732001f49Smrg   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
131832001f49Smrg
131932001f49Smrg   InitViewInfo(&View);
132032001f49Smrg   InitRenderInfo(&Render);
132132001f49Smrg}
132232001f49Smrg
132332001f49Smrg
132432001f49Smrgint
132532001f49Smrgmain(int argc, char *argv[])
132632001f49Smrg{
132732001f49Smrg   glutInitWindowSize(WinWidth, WinHeight);
132832001f49Smrg   glutInit(&argc, argv);
132932001f49Smrg   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
133032001f49Smrg   Win = glutCreateWindow("OpenGL Engine Demo");
133132001f49Smrg   glewInit();
133232001f49Smrg   glutReshapeFunc(Reshape);
133332001f49Smrg   glutMouseFunc(Mouse);
133432001f49Smrg   glutMotionFunc(Motion);
133532001f49Smrg   glutKeyboardFunc(Key);
133632001f49Smrg   glutDisplayFunc(Draw);
133732001f49Smrg   MakeMenu();
133832001f49Smrg   Init();
133932001f49Smrg   if (Render.Anim)
134032001f49Smrg      glutIdleFunc(Idle);
134132001f49Smrg   glutMainLoop();
134232001f49Smrg   return 0;
134332001f49Smrg}
1344