diff options
author | Brian Paul <[email protected]> | 2006-07-04 21:43:21 +0000 |
---|---|---|
committer | Brian Paul <[email protected]> | 2006-07-04 21:43:21 +0000 |
commit | 1ff8daf21e570fe9525bc5075ecdd52d424891c8 (patch) | |
tree | 0aff7cee95d66230c3d26559aef984acbb45d387 /progs/demos/engine.c | |
parent | 1ad914575a89f3fdbfa5bce863e14f8d4679ed3b (diff) |
New animated engine demo.
Diffstat (limited to 'progs/demos/engine.c')
-rw-r--r-- | progs/demos/engine.c | 1099 |
1 files changed, 1099 insertions, 0 deletions
diff --git a/progs/demos/engine.c b/progs/demos/engine.c new file mode 100644 index 00000000000..8ef73462177 --- /dev/null +++ b/progs/demos/engine.c @@ -0,0 +1,1099 @@ +/** + * Simple engine demo (crankshaft, pistons, connecting rods) + * + * Brian Paul + * June 2006 + */ + +#define GL_GLEXT_PROTOTYPES + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <GL/glut.h> +#include "readtex.h" +#include "trackball.h" + + +#define DEG_TO_RAD(DEG) ((DEG) * M_PI / 180.0) + +#define TEXTURE_FILE "../images/reflect.rgb" + +/* Target engine speed: */ +const int RPM = 100.0; + + +/** + * Engine description. + */ +typedef struct +{ + const char *Name; + int Pistons; + int Cranks; + float V_Angle; + float PistonRadius; + float PistonHeight; + float WristPinRadius; + float Throw; + float CrankPlateThickness; + float CrankPinRadius; + float CrankJournalRadius; + float CrankJournalLength; + float ConnectingRodLength; + float ConnectingRodThickness; + /* display list IDs */ + GLuint CrankList; + GLuint ConnRodList; + GLuint PistonList; +} Engine; + + +typedef struct +{ + float CurQuat[4]; + float Distance; + /* When mouse is moving: */ + GLboolean Rotating, Translating; + GLint StartX, StartY; + float StartDistance; +} ViewInfo; + + +typedef enum +{ + LIT, + WIREFRAME, + TEXTURED +} RenderMode; + + +typedef struct +{ + RenderMode Mode; + GLboolean Anim; + GLboolean Wireframe; + GLboolean Blend; + GLboolean Antialias; + GLboolean Texture; + GLboolean UseLists; + GLboolean DrawBox; + GLboolean ShowInfo; +} RenderInfo; + + +static GLUquadric *Q; + +static GLfloat Theta = 0.0; + +static GLfloat PistonColor[4] = { 1.0, 0.5, 0.5, 1.0 }; +static GLfloat ConnRodColor[4] = { 0.7, 1.0, 0.7, 1.0 }; +static GLfloat CrankshaftColor[4] = { 0.7, 0.7, 1.0, 1.0 }; + +static GLuint TextureObj; +static GLint WinWidth = 800, WinHeight = 500; + +static ViewInfo View; +static RenderInfo Render; + +#define NUM_ENGINES 3 +static Engine Engines[NUM_ENGINES] = +{ + { + "V-6", + 6, /* Pistons */ + 3, /* Cranks */ + 90.0, /* V_Angle */ + 0.5, /* PistonRadius */ + 0.6, /* PistonHeight */ + 0.1, /* WristPinRadius */ + 0.5, /* Throw */ + 0.2, /* CrankPlateThickness */ + 0.25, /* CrankPinRadius */ + 0.3, /* CrankJournalRadius */ + 0.4, /* CrankJournalLength */ + 1.3, /* ConnectingRodLength */ + 0.1 /* ConnectingRodThickness */ + }, + { + "Inline-4", + 4, /* Pistons */ + 4, /* Cranks */ + 0.0, /* V_Angle */ + 0.5, /* PistonRadius */ + 0.6, /* PistonHeight */ + 0.1, /* WristPinRadius */ + 0.5, /* Throw */ + 0.2, /* CrankPlateThickness */ + 0.25, /* CrankPinRadius */ + 0.3, /* CrankJournalRadius */ + 0.4, /* CrankJournalLength */ + 1.3, /* ConnectingRodLength */ + 0.1 /* ConnectingRodThickness */ + }, + { + "Boxer-6", + 6, /* Pistons */ + 3, /* Cranks */ + 180.0,/* V_Angle */ + 0.5, /* PistonRadius */ + 0.6, /* PistonHeight */ + 0.1, /* WristPinRadius */ + 0.5, /* Throw */ + 0.2, /* CrankPlateThickness */ + 0.25, /* CrankPinRadius */ + 0.3, /* CrankJournalRadius */ + 0.4, /* CrankJournalLength */ + 1.3, /* ConnectingRodLength */ + 0.1 /* ConnectingRodThickness */ + } +}; + +static int CurEngine = 0; + + + +static void +InitViewInfo(ViewInfo *view) +{ + view->Rotating = GL_FALSE; + view->Translating = GL_FALSE; + view->StartX = view->StartY = 0; + view->Distance = 12.0; + view->StartDistance = 0.0; + view->CurQuat[0] = -0.194143; + view->CurQuat[1] = 0.507848; + view->CurQuat[2] = 0.115245; + view->CurQuat[3] = 0.831335; +} + + +static void +InitRenderInfo(RenderInfo *render) +{ + render->Mode = LIT; + render->Anim = GL_TRUE; + render->Wireframe = GL_FALSE; + render->Blend = GL_FALSE; + render->Antialias = GL_FALSE; + render->Texture = GL_FALSE; + render->DrawBox = GL_FALSE; + render->ShowInfo = GL_TRUE; + render->UseLists = GL_FALSE; +} + + +/** + * Set GL for given rendering mode. + */ +static void +SetRenderState(RenderMode mode) +{ + /* defaults */ + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + + switch (mode) { + case LIT: + glEnable(GL_LIGHTING); + break; + case WIREFRAME: + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glLineWidth(1.5); + break; + case TEXTURED: + glEnable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + break; + default: + ; + } +} + + +/** + * Animate the engine parts. + */ +static void +Idle(void) +{ + /* convert degrees per millisecond to RPM: */ + const float m = 360.0 / 1000.0 / 60.0; + GLint t = glutGet(GLUT_ELAPSED_TIME); + Theta = ((int) (t * RPM * m)) % 360; + glutPostRedisplay(); +} + + +/** + * Compute piston's position along its stroke. + */ +static float +PistonStrokePosition(float throwDist, float crankAngle, float connRodLength) +{ + float x = throwDist * cos(DEG_TO_RAD(crankAngle)); + float y = throwDist * sin(DEG_TO_RAD(crankAngle)); + float pos = y + sqrt(connRodLength * connRodLength - x * x); + return pos; +} + + +/** + * Compute position of nth piston along the crankshaft. + */ +static float +PistonShaftPosition(const Engine *eng, int piston) +{ + const int i = piston / (eng->Pistons / eng->Cranks); + float z; + assert(piston < eng->Pistons); + z = 1.5 * eng->CrankJournalLength + eng->CrankPlateThickness + + i * (2.0 * (eng->CrankJournalLength + eng->CrankPlateThickness)); + if (eng->Pistons > eng->Cranks) { + if (piston & 1) + z += eng->ConnectingRodThickness; + else + z -= eng->ConnectingRodThickness; + } + return z; +} + + +/** + * (x0, y0) = position of big end on crankshaft + * (x1, y1) = position of small end on piston + */ +static void +ComputeConnectingRodPosition(float throwDist, float crankAngle, + float connRodLength, + float *x0, float *y0, float *x1, float *y1) +{ + *x0 = throwDist * cos(DEG_TO_RAD(crankAngle)); + *y0 = throwDist * sin(DEG_TO_RAD(crankAngle)); + *x1 = 0.0; + *y1 = PistonStrokePosition(throwDist, crankAngle, connRodLength); +} + + +/** + * Compute total length of the crankshaft. + */ +static float +CrankshaftLength(const Engine *eng) +{ + float len = (eng->Cranks * 2 + 1) * eng->CrankJournalLength + + 2 * eng->Cranks * eng->CrankPlateThickness; + return len; +} + + +/** + * Draw a piston. + * Axis of piston = Z axis. Wrist pin is centered on (0, 0, 0). + */ +static void +DrawPiston(const Engine *eng) +{ + const int slices = 30, stacks = 4, loops = 4; + const float innerRadius = 0.9 * eng->PistonRadius; + const float innerHeight = eng->PistonHeight - 0.15; + const float wristPinLength = 1.8 * eng->PistonRadius; + + assert(Q); + + glPushMatrix(); + glTranslatef(0, 0, -1.1 * eng->WristPinRadius); + + gluQuadricOrientation(Q, GLU_INSIDE); + + /* bottom rim */ + gluDisk(Q, innerRadius, eng->PistonRadius, slices, 1/*loops*/); + + /* inner cylinder */ + gluCylinder(Q, innerRadius, innerRadius, innerHeight, slices, stacks); + + /* inside top */ + glPushMatrix(); + glTranslatef(0, 0, innerHeight); + gluDisk(Q, 0, innerRadius, slices, loops); + glPopMatrix(); + + gluQuadricOrientation(Q, GLU_OUTSIDE); + + /* outer cylinder */ + gluCylinder(Q, eng->PistonRadius, eng->PistonRadius, eng->PistonHeight, + slices, stacks); + + /* top */ + glTranslatef(0, 0, eng->PistonHeight); + gluDisk(Q, 0, eng->PistonRadius, slices, loops); + + glPopMatrix(); + + /* wrist pin */ + glPushMatrix(); + glTranslatef(0, 0.5 * wristPinLength, 0.0); + glRotatef(90, 1, 0, 0); + gluCylinder(Q, eng->WristPinRadius, eng->WristPinRadius, wristPinLength, + slices, stacks); + glPopMatrix(); +} + + +/** + * Draw piston at particular position. + */ +static void +DrawPositionedPiston(const Engine *eng, float crankAngle) +{ + const float pos = PistonStrokePosition(eng->Throw, crankAngle, + eng->ConnectingRodLength); + glPushMatrix(); + glRotatef(-90, 1, 0, 0); + glTranslatef(0, 0, pos); + DrawPiston(eng); + glPopMatrix(); +} + + +/** + * Draw connector plate. Used for crankshaft and connecting rods. + */ +static void +DrawConnector(float length, float thickness, + float bigEndRadius, float smallEndRadius) +{ + const float bigRadius = 1.2 * bigEndRadius; + const float smallRadius = 1.2 * smallEndRadius; + const float z0 = -0.5 * thickness, z1 = -z0; + GLfloat points[36][2], normals[36][2]; + int i; + + /* compute vertex locations, normals */ + for (i = 0; i < 36; i++) { + const int angle = i * 10; + float x = cos(DEG_TO_RAD(angle)); + float y = sin(DEG_TO_RAD(angle)); + normals[i][0] = x; + normals[i][1] = y; + if (angle >= 0 && angle <= 180) { + x *= smallRadius; + y = y * smallRadius + length; + } + else { + x *= bigRadius; + y *= bigRadius; + } + points[i][0] = x; + points[i][1] = y; + } + + /* front face */ + glNormal3f(0, 0, 1); + glBegin(GL_POLYGON); + for (i = 0; i < 36; i++) { + glVertex3f(points[i][0], points[i][1], z1); + } + glEnd(); + + /* back face */ + glNormal3f(0, 0, -1); + glBegin(GL_POLYGON); + for (i = 0; i < 36; i++) { + glVertex3f(points[35-i][0], points[35-i][1], z0); + } + glEnd(); + + /* edge */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= 36; i++) { + const int j = i % 36; + glNormal3f(normals[j][0], normals[j][1], 0); + glVertex3f(points[j][0], points[j][1], z0); + glVertex3f(points[j][0], points[j][1], z1); + } + glEnd(); +} + + +/** + * Draw a crankshaft. Shaft lies along +Z axis, starting at zero. + */ +static void +DrawCrankshaft(const Engine *eng) +{ + const int slices = 20, stacks = 2; + const int n = eng->Cranks * 4 + 1; + const float phiStep = 360 / eng->Cranks; + float phi = -90.0; + int i; + float z = 0.0; + + for (i = 0; i < n; i++) { + glPushMatrix(); + glTranslatef(0, 0, z); + if (i & 1) { + /* draw a crank plate */ + glRotatef(phi, 0, 0, 1); + glTranslatef(0, 0, 0.5 * eng->CrankPlateThickness); + DrawConnector(eng->Throw, eng->CrankPlateThickness, + eng->CrankJournalRadius, eng->CrankPinRadius); + z += 0.2; + if (i % 4 == 3) + phi += phiStep; + } + else if (i % 4 == 0) { + /* draw crank journal segment */ + gluCylinder(Q, eng->CrankJournalRadius, eng->CrankJournalRadius, + eng->CrankJournalLength, slices, stacks); + z += eng->CrankJournalLength; + } + else if (i % 4 == 2) { + /* draw crank pin segment */ + glRotatef(phi, 0, 0, 1); + glTranslatef(0, eng->Throw, 0); + gluCylinder(Q, eng->CrankPinRadius, eng->CrankPinRadius, + eng->CrankJournalLength, slices, stacks); + z += eng->CrankJournalLength; + } + glPopMatrix(); + } +} + + +/** + * Draw crankshaft at a particular rotation. + * \param crankAngle current crankshaft rotation, in radians + */ +static void +DrawPositionedCrankshaft(const Engine *eng, float crankAngle) +{ + glPushMatrix(); + glRotatef(crankAngle, 0, 0, 1); + if (eng->CrankList) + glCallList(eng->CrankList); + else + DrawCrankshaft(eng); + glPopMatrix(); +} + + +/** + * Draw a connecting rod at particular position. + * \param eng description of connecting rod to draw + * \param crankAngle current crankshaft rotation, in radians + */ +static void +DrawPositionedConnectingRod(const Engine *eng, float crankAngle) +{ + float x0, y0, x1, y1; + float d, phi; + + ComputeConnectingRodPosition(eng->Throw, crankAngle, + eng->ConnectingRodLength, + &x0, &y0, &x1, &y1); + d = sqrt(eng->ConnectingRodLength * eng->ConnectingRodLength - x0 * x0); + phi = atan(x0 / d) * 180.0 / M_PI; + + glPushMatrix(); + glTranslatef(x0, y0, 0); + glRotatef(phi, 0, 0, 1); + if (eng->ConnRodList) + glCallList(eng->ConnRodList); + else + DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness, + eng->CrankPinRadius, eng->WristPinRadius); + glPopMatrix(); +} + + +/** + * Generate display lists for engine parts. + */ +static void +GenerateDisplayLists(Engine *eng) +{ + eng->CrankList = glGenLists(1); + glNewList(eng->CrankList, GL_COMPILE); + DrawCrankshaft(eng); + glEndList(); + + eng->ConnRodList = glGenLists(1); + glNewList(eng->ConnRodList, GL_COMPILE); + DrawConnector(eng->ConnectingRodLength, eng->ConnectingRodThickness, + eng->CrankPinRadius, eng->WristPinRadius); + glEndList(); + + eng->PistonList = glGenLists(1); + glNewList(eng->PistonList, GL_COMPILE); + DrawPiston(eng); + glEndList(); +} + + +/** + * Free engine display lists (render with immediate mode). + */ +static void +FreeDisplayLists(Engine *eng) +{ + glDeleteLists(eng->CrankList, 1); + eng->CrankList = 0; + glDeleteLists(eng->ConnRodList, 1); + eng->ConnRodList = 0; + glDeleteLists(eng->PistonList, 1); + eng->PistonList = 0; +} + + + +/** + * Draw complete engine. + * \param eng description of engine to draw + * \param crankAngle current crankshaft angle, in radians + */ +static void +DrawEngine(const Engine *eng, float crankAngle) +{ + const float crankDelta = 360.0 / eng->Cranks; + const float crankLen = CrankshaftLength(eng); + const int pistonsPerCrank = eng->Pistons / eng->Cranks; + int i; + + glPushMatrix(); + glRotatef(eng->V_Angle * 0.5, 0, 0, 1); + glTranslatef(0, 0, -0.5 * crankLen); + + /* crankshaft */ + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, CrankshaftColor); + glColor4fv(CrankshaftColor); + DrawPositionedCrankshaft(eng, crankAngle); + + for (i = 0; i < eng->Pistons; i++) { + const float z = PistonShaftPosition(eng, i); + const int crank = i / pistonsPerCrank; + float rot = crankAngle + crank * crankDelta; + int k; + + glPushMatrix(); + glTranslatef(0, 0, z); + + /* additional rotation for kth piston per crank */ + k = i % pistonsPerCrank; + glRotatef(k * -eng->V_Angle, 0, 0, 1); + rot += k * eng->V_Angle; + + /* piston */ + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, PistonColor); + glColor4fv(PistonColor); + DrawPositionedPiston(eng, rot); + + /* connecting rod */ + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ConnRodColor); + glColor4fv(ConnRodColor); + DrawPositionedConnectingRod(eng, rot); + + glPopMatrix(); + } + + glPopMatrix(); +} + + +static void +DrawBox(void) +{ + const float xmin = -3.0, xmax = 3.0; + const float ymin = -1.0, ymax = 3.0; + const float zmin = -4.0, zmax = 4.0; + const float step = 0.5; + const float d = 0.01; + float x, y, z; + GLboolean lit = glIsEnabled(GL_LIGHTING); + GLboolean tex = glIsEnabled(GL_TEXTURE_2D); + + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + + glColor3f(1, 1, 1); + + /* Z min */ + glBegin(GL_LINES); + for (x = xmin; x <= xmax; x += step) { + glVertex3f(x, ymin, zmin); + glVertex3f(x, ymax, zmin); + } + glEnd(); + glBegin(GL_LINES); + for (y = ymin; y <= ymax; y += step) { + glVertex3f(xmin, y, zmin); + glVertex3f(xmax, y, zmin); + } + glEnd(); + + /* Y min */ + glBegin(GL_LINES); + for (x = xmin; x <= xmax; x += step) { + glVertex3f(x, ymin, zmin); + glVertex3f(x, ymin, zmax); + } + glEnd(); + glBegin(GL_LINES); + for (z = zmin; z <= zmax; z += step) { + glVertex3f(xmin, ymin, z); + glVertex3f(xmax, ymin, z); + } + glEnd(); + + /* X min */ + glBegin(GL_LINES); + for (y = ymin; y <= ymax; y += step) { + glVertex3f(xmin, y, zmin); + glVertex3f(xmin, y, zmax); + } + glEnd(); + glBegin(GL_LINES); + for (z = zmin; z <= zmax; z += step) { + glVertex3f(xmin, ymin, z); + glVertex3f(xmin, ymax, z); + } + glEnd(); + + glColor3f(0.4, 0.4, 0.6); + glBegin(GL_QUADS); + /* xmin */ + glVertex3f(xmin-d, ymin, zmin); + glVertex3f(xmin-d, ymax, zmin); + glVertex3f(xmin-d, ymax, zmax); + glVertex3f(xmin-d, ymin, zmax); + /* ymin */ + glVertex3f(xmin, ymin-d, zmin); + glVertex3f(xmax, ymin-d, zmin); + glVertex3f(xmax, ymin-d, zmax); + glVertex3f(xmin, ymin-d, zmax); + /* zmin */ + glVertex3f(xmin, ymin, zmin-d); + glVertex3f(xmax, ymin, zmin-d); + glVertex3f(xmax, ymax, zmin-d); + glVertex3f(xmin, ymax, zmin-d); + glEnd(); + + if (lit) + glEnable(GL_LIGHTING); + if (tex) + glEnable(GL_TEXTURE_2D); +} + + +static void +PrintString(const char *s) +{ + while (*s) { + glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (int) *s); + s++; + } +} + + +static int +ComputeFPS(void) +{ + static double t0 = -1.0; + static int frames = 0; + double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; + static int fps = 0; + + frames++; + + if (t0 < 0.0) { + t0 = t; + fps = 0; + } + else if (t - t0 >= 1.0) { + fps = (int) (frames / (t - t0) + 0.5); + t0 = t; + frames = 0; + } + + return fps; +} + + +static void +Draw(void) +{ + int fps; + GLfloat rot[4][4]; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + + glTranslatef(0.0, 0.0, -View.Distance); + build_rotmatrix(rot, View.CurQuat); + glMultMatrixf(&rot[0][0]); + + glPushMatrix(); + glTranslatef(0, -0.75, 0); + DrawEngine(Engines + CurEngine, Theta); + if (Render.DrawBox) + DrawBox(); + glPopMatrix(); + + glPopMatrix(); + + fps = ComputeFPS(); + if (Render.ShowInfo) { + GLboolean lit = glIsEnabled(GL_LIGHTING); + GLboolean tex = glIsEnabled(GL_TEXTURE_2D); + char s[100]; + sprintf(s, "%s %d FPS %s", Engines[CurEngine].Name, fps, + Render.UseLists ? "Display Lists" : "Immediate mode"); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glColor3f(1, 1 , 1); + glWindowPos2iARB(10, 10); + PrintString(s); + if (lit) + glEnable(GL_LIGHTING); + if (tex) + glEnable(GL_TEXTURE_2D); + } + + glutSwapBuffers(); +} + + +/** + * Handle window resize. + */ +static void +Reshape(int width, int height) +{ + float ar = (float) width / height; + float s = 0.5; + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-ar * s, ar * s, -s, s, 2.0, 50.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + WinWidth = width; + WinHeight = height; +} + + +/** + * Handle mouse button. + */ +static void +Mouse(int button, int state, int x, int y) +{ + if (button == GLUT_LEFT_BUTTON) { + if (state == GLUT_DOWN) { + View.StartX = x; + View.StartY = y; + View.Rotating = GL_TRUE; + } + else if (state == GLUT_UP) { + View.Rotating = GL_FALSE; + } + } + else if (button == GLUT_MIDDLE_BUTTON) { + if (state == GLUT_DOWN) { + View.StartX = x; + View.StartY = y; + View.StartDistance = View.Distance; + View.Translating = GL_TRUE; + } + else if (state == GLUT_UP) { + View.Translating = GL_FALSE; + } + } +} + + +/** + * Handle mouse motion + */ +static void +Motion(int x, int y) +{ + int i; + if (View.Rotating) { + float x0 = (2.0 * View.StartX - WinWidth) / WinWidth; + float y0 = (WinHeight - 2.0 * View.StartY) / WinHeight; + float x1 = (2.0 * x - WinWidth) / WinWidth; + float y1 = (WinHeight - 2.0 * y) / WinHeight; + float q[4]; + + trackball(q, x0, y0, x1, y1); + View.StartX = x; + View.StartY = y; + for (i = 0; i < 1; i++) + add_quats(q, View.CurQuat, View.CurQuat); + + glutPostRedisplay(); + } + else if (View.Translating) { + float dz = 0.01 * (y - View.StartY); + View.Distance = View.StartDistance + dz; + glutPostRedisplay(); + } +} + + +/** + ** Menu Callbacks + **/ + +static void +OptAnimation(void) +{ + Render.Anim = !Render.Anim; + if (Render.Anim) + glutIdleFunc(Idle); + else + glutIdleFunc(NULL); +} + +static void +OptChangeEngine(void) +{ + CurEngine = (CurEngine + 1) % NUM_ENGINES; +} + +static void +OptRenderMode(void) +{ + Render.Mode++; + if (Render.Mode > TEXTURED) + Render.Mode = 0; + SetRenderState(Render.Mode); +} + +static void +OptDisplayLists(void) +{ + int i; + Render.UseLists = !Render.UseLists; + if (Render.UseLists) { + for (i = 0; i < NUM_ENGINES; i++) { + GenerateDisplayLists(Engines + i); + } + } + else { + for (i = 0; i < NUM_ENGINES; i++) { + FreeDisplayLists(Engines + i); + } + } +} + +static void +OptShowInfo(void) +{ + Render.ShowInfo = !Render.ShowInfo; +} + +static void +OptShowBox(void) +{ + Render.DrawBox = !Render.DrawBox; +} + +static void +OptRotate(void) +{ + Theta += 5.0; +} + +static void +OptExit(void) +{ + exit(0); +} + + +/** + * Define menu entries (w/ keyboard shortcuts) + */ + +typedef struct +{ + const char *Text; + const char Key; + void (*Function)(void); +} MenuInfo; + +static const MenuInfo MenuItems[] = { + { "Animation", 'a', OptAnimation }, + { "Change Engine", 'e', OptChangeEngine }, + { "Rendering Style", 'm', OptRenderMode }, + { "Display Lists", 'd', OptDisplayLists }, + { "Show Info", 'i', OptShowInfo }, + { "Show Box", 'b', OptShowBox }, + { "Exit", 27, OptExit }, + { NULL, 'r', OptRotate }, + { NULL, 0, NULL } +}; + + +/** + * Handle menu selection. + */ +static void +MenuHandler(int entry) +{ + MenuItems[entry].Function(); + glutPostRedisplay(); +} + + +/** + * Make pop-up menu. + */ +static void +MakeMenu(void) +{ + int i; + glutCreateMenu(MenuHandler); + for (i = 0; MenuItems[i].Text; i++) { + glutAddMenuEntry(MenuItems[i].Text, i); + } + glutAttachMenu(GLUT_RIGHT_BUTTON); +} + + +/** + * Handle keyboard event. + */ +static void +Key(unsigned char key, int x, int y) +{ + int i; + (void) x; (void) y; + for (i = 0; MenuItems[i].Key; i++) { + if (MenuItems[i].Key == key) { + MenuItems[i].Function(); + glutPostRedisplay(); + break; + } + } +} + + +static +void LoadTexture(void) +{ + GLboolean convolve = GL_FALSE; + + glGenTextures(1, &TextureObj); + glBindTexture(GL_TEXTURE_2D, TextureObj); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + + if (convolve) { +#define FILTER_SIZE 7 + /* use convolution to blur the texture to simulate a dull finish + * on the object. + */ + GLubyte *img; + GLenum format; + GLint w, h; + GLfloat filter[FILTER_SIZE][FILTER_SIZE][4]; + + for (h = 0; h < FILTER_SIZE; h++) { + for (w = 0; w < FILTER_SIZE; w++) { + const GLfloat k = 1.0 / (FILTER_SIZE * FILTER_SIZE); + filter[h][w][0] = k; + filter[h][w][1] = k; + filter[h][w][2] = k; + filter[h][w][3] = k; + } + } + + glEnable(GL_CONVOLUTION_2D); + glConvolutionParameteri(GL_CONVOLUTION_2D, + GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER); + glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_RGBA, + FILTER_SIZE, FILTER_SIZE, + GL_RGBA, GL_FLOAT, filter); + + img = LoadRGBImage(TEXTURE_FILE, &w, &h, &format); + if (!img) { + printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE); + exit(1); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, + format, GL_UNSIGNED_BYTE, img); + free(img); + } + else { + if (!LoadRGBMipmaps(TEXTURE_FILE, GL_RGB)) { + printf("Error: couldn't load texture image file %s\n", TEXTURE_FILE); + exit(1); + } + } +} + + +static void +Init(void) +{ + const GLfloat lightColor[4] = { 0.7, 0.7, 0.7, 1.0 }; + const GLfloat specular[4] = { 0.8, 0.8, 0.8, 1.0 }; + + Q = gluNewQuadric(); + gluQuadricNormals(Q, GLU_SMOOTH); + + LoadTexture(); + + glClearColor(0.3, 0.3, 0.3, 0.0); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 40); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); + glEnable(GL_NORMALIZE); + + glBlendFunc(GL_SRC_ALPHA, GL_ZERO); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + InitViewInfo(&View); + InitRenderInfo(&Render); +} + + +int +main(int argc, char *argv[]) +{ + glutInit(&argc, argv); + glutInitWindowSize(WinWidth, WinHeight); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); + glutCreateWindow("OpenGL Engine Demo"); + glutReshapeFunc(Reshape); + glutMouseFunc(Mouse); + glutMotionFunc(Motion); + glutKeyboardFunc(Key); + glutDisplayFunc(Draw); + MakeMenu(); + Init(); + if (Render.Anim) + glutIdleFunc(Idle); + glutMainLoop(); + return 0; +} |