From 1bca1d61d8c20699d22d5c13167d7481bbddead1 Mon Sep 17 00:00:00 2001 From: Ash Tyndall Date: Thu, 20 Oct 2011 09:46:15 +0800 Subject: [PATCH] --- scene.c | 1404 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 765 insertions(+), 639 deletions(-) diff --git a/scene.c b/scene.c index 71523bc..f8dbc57 100644 --- a/scene.c +++ b/scene.c @@ -1,525 +1,619 @@ -/** - * CITS2231 Graphics Scene Editor - * @author Ashley Tyndall (20915779) - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bitmap.h" - -// Type definitions for vertex-coordinates, normals, texture-coordinates, -// and triangles (via the indices of 3 vertices). -typedef GLfloat vertex[3]; -typedef GLfloat normal[3]; -typedef GLfloat texCoord[2]; -typedef GLint vertexIndex; -typedef vertexIndex triangle[3]; - -// A type for a mesh -typedef struct { - int nVertices; // The number of vertices in the mesh - vertex* vertices; // Array with coordinates of vertices - normal* normals; // Array with normals of vertices - texCoord* texCoords; // Array with texture-coordinates of vertices - int nTriangles; // The number of triangles in the mesh - triangle* triangles; // Array of trangles via 3 indices into "vertices" -} mesh; - -#define NMESH 54 // The number of meshes (in the models-textures dir) -mesh* meshes[NMESH]; // An array of pointers to the meshes - see getMesh - -// A type for a 2D texture, with height and width in pixels -typedef struct { - int height; - int width; - GLubyte *rgbData; // Array of bytes with the colour data for the texture -} texture; - -#define NTEXTURE 30 // The number of textures (in the models-textures dir) -texture* textures[NTEXTURE]; // An array of texture pointers - see getTexture - -typedef struct { - // You'll need to add scale, rotation, material, mesh number, etc., - // to this structure - float x,y,z; -} SceneObject; - -// Menu enum -enum menu { - // Main menu - ROTATE_MOVE_CAMERA, - POSITION_SCALE, - ROTATION_TEXTURE_SCALE, - EXIT, - - // Material submenu - MATERIAL_ALL_RGB, - MATERIAL_AMBIENT_RGB, - MATERIAL_DIFFUSE_RGB, - MATERIAL_SPECULAR_RGB, - MATERIAL_ALL_ADSS, - MATERIAL_RED_ADSS, - MATERIAL_GREEN_ADSS, - MATERIAL_BLUE_ADSS, - - // Light submenu - LIGHT_MOVE_LIGHT_1, - LIGHT_RGBALL_LIGHT_1, - LIGHT_MOVE_LIGHT_2, - LIGHT_RGBALL_LIGHT_2 -}; +/* Copyright (c) Mark J. Kilgard, 1994, 1997. */ -// Menu arrays -const char *textureMenuEntries[NTEXTURE] = { - "1 Plain", "2 Rust", "3 Concrete", "4 Carpet", "5 Beach Sand", - "6 Rocky", "7 Brick", "8 Water", "9 Paper", "10 Marble", - "11 Wood", "12 Scales", "13 Fur", "14 Denim", "15 Hessian", - "16 Orange Peel", "17 Ice Crystals", "18 Grass", "19 Corrugated Iron", "20 Styrofoam", - "21 Bubble Wrap", "22 Leather", "23 Camouflage", "24 Asphalt", "25 Scratched Ice", - "26 Rattan", "27 Snow", "28 Dry Mud", "29 Old Concrete", "30 Leopard Skin" -}; +/* This program is freely distributable without licensing fees + and is provided without guarantee or warrantee expressed or + implied. This program is -not- in the public domain. */ -const char *objectMenuEntries[NMESH] = { - "1 Thin Dinosaur","2 Big Dog","3 Saddle Dinosaur", "4 Dragon", "5 Cleopatra", - "6 Bone I", "7 Bone II", "8 Rabbit", "9 Long Dragon", "10 Buddha", - "11 Sitting Rabbit", "12 Frog", "13 Cow", "14 Monster", "15 Sea Horse", - "16 Head", "17 Pelican", "18 Horse", "19 Kneeling Angel", "20 Porsche I", - "21 Truck", "22 Statue of Liberty", "23 Sitting Angel", "24 Metal Part", "25 Car", - "26 Apatosaurus", "27 Airliner", "28 Motorbike", "29 Dolphin", "30 Spaceman", - "31 Winnie the Pooh", "32 Shark", "33 Crocodile", "34 Toddler", "35 Fat Dinosaur", - "36 Chihuahua", "37 Sabre-toothed Tiger", "38 Lioness", "39 Fish", "40 Horse (head down)", - "41 Horse (head up)", "42 Skull", "43 Fighter Jet I", "44 Toad", "45 Convertible", - "46 Porsche II", "47 Hare", "48 Vintage Car", "49 Fighter Jet II", "50 Winged Monkey", - "51 Chef", "52 Parasaurolophus", "53 Rooster", "54 T-rex" -}; +/* Example for PC game developers to show how to *combine* texturing, + reflections, and projected shadows all in real-time with OpenGL. + Robust reflections use stenciling. Robust projected shadows + use both stenciling and polygon offset. PC game programmers + should realize that neither stenciling nor polygon offset are + supported by Direct3D, so these real-time rendering algorithms + are only really viable with OpenGL. -#define MAXOBJECTS 256 -SceneObject sceneObjs[MAXOBJECTS]; // An array with details of the objects in a scene -int nObjects=0; // How many objects there are in the scene currently. + The program has modes for disabling the stenciling and polygon + offset uses. It is worth running this example with these features + toggled off so you can see the sort of artifacts that result. -// Directories containing models -char *dirDefault1 = "models-textures"; -char *dirDefault2 = "/cslinux/examples/CITS2231/project-files/models-textures"; + Notice that the floor texturing, reflections, and shadowing + all co-exist properly. */ -char dataDir[200]; // Stores the directory name for the meshes and textures. +/* When you run this program: Left mouse button controls the + view. Middle mouse button controls light position (left & + right rotates light around dino; up & down moves light + position up and down). Right mouse button pops up menu. */ -static GLfloat lightColor[] = {1.0, 1.0, 1.0, 1.0}; // White light -static GLfloat lightPosition[4]; +/* Check out the comments in the "redraw" routine to see how the + reflection blending and surface stenciling is done. You can + also see in "redraw" how the projected shadows are rendered, -int moving, startx, starty; -int lightMoving = 0, lightStartX, lightStartY; + including the use of stenciling and polygon offset. */ + +/* This program is derived from glutdino.c */ + +/* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */ + +#include +#include +#include +#include /* for cos(), sin(), and sqrt() */ +#include /* OpenGL Utility Toolkit header */ + +/* Some files do not define M_PI... */ +#ifndef M_PI +#define M_PI 3.14159265 +#endif + +/* Variable controlling various rendering modes. */ +static int stencilReflection = 1, stencilShadow = 1, offsetShadow = 1; +static int renderShadow = 1, renderDinosaur = 1, renderReflection = 1; +static int linearFiltering = 0, useMipmaps = 0, useTexture = 1; +static int reportSpeed = 0; +static int animation = 1; +static GLboolean lightSwitch = GL_TRUE; +static int directionalLight = 1; +static int forceExtension = 0; /* Time varying or user-controled variables. */ static float jump = 0.0; -static float lightAngle = 0.0, lightHeight = 5; +static float lightAngle = 0.0, lightHeight = 20; GLfloat angle = -150; /* in degrees */ GLfloat angle2 = 30; /* in degrees */ -/* Near and far parameters */ -GLfloat near = -100; -GLfloat far = 100; - -/* Zoom factor for mouse movements */ -GLfloat zoomFactor = 1.0; - -/* Recursion level for floor drawing */ -int drawFloorRecurse = 5; - -/* Size of floor, from -n to n */ -int floorSize = 100; - -/* Light 0 parameters */ -GLfloat diffuse0[] = {1.0, 1.0, 1.0, 1.0}; -GLfloat ambient0[] = {0.0, 0.0, 0.0, 1.0}; -GLfloat specular0[] = {1.0, 1.0, 1.0, 1.0}; -GLfloat emission0[] = {0.0, 0.0, 0.0, 0.0}; -GLfloat light0_pos[] ={1.0, 1.0, 0,0, 1.0}; -GLfloat glightmodel[] = {0.2,0.2,0.2,1}; - - -/** - * Prints out error message when file cannot be read - * @param fileName Name of file that could not be read - */ -void fileErr(char* fileName) { - printf("Error reading file: %s\n", fileName); - printf("If not in the CSSE labs, you will need to include the directory containing\n"); - printf("the models on the command line, or put it in the same folder as the exectutable."); - exit(EXIT_FAILURE); -} - -/** - * Reads .bmp texture files and converts them to a texture object - * @param fileName .bmp texture file - * @return texture object - */ -texture* loadTexture(char *fileName) { - texture* t = malloc(sizeof (texture)); - BITMAPINFO *info; - - t->rgbData = LoadDIBitmap(fileName, &info); - t->height=info->bmiHeader.biHeight; - t->width=info->bmiHeader.biWidth; - - return t; -} +int moving, startx, starty; +int lightMoving = 0, lightStartX, lightStartY; -/** - * Reads .x files and converts them to a mesh object - * @param fileName .x mesh file - * @return mesh object - */ -mesh* loadMesh(char* fileName) { - mesh* m = malloc(sizeof (mesh)); - FILE* fp = fopen(fileName, "r"); - char line[256] = ""; - int lineBuffSize = 256; - - if(fp == NULL) fileErr(fileName); - - while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 ) - fgets(line, lineBuffSize, fp); - - fscanf(fp, "%d;\n", &(m->nVertices)); - m->vertices = malloc(m->nVertices * sizeof(vertex)); - for(int i=0; i < m->nVertices; i++) - fscanf(fp, "%f; %f; %f;%*[,;]\n", &(m->vertices[i][0]), &(m->vertices[i][1]), &(m->vertices[i][2]) ); - - fscanf(fp, "%d;\n", &(m->nTriangles)); - m->triangles = malloc(m->nTriangles * sizeof(triangle)); - for(int i=0; i < m->nTriangles; i++) - fscanf(fp, "%*d; %d, %d, %d;%*[;,]", m->triangles[i], m->triangles[i]+1, m->triangles[i]+2); - - while(strcmp(line," MeshNormals {\r\n") != 0 && strcmp(line," MeshNormals {\n") != 0) - fgets(line, lineBuffSize, fp); - - fgets(line, lineBuffSize, fp); - m->normals = malloc(m->nVertices * sizeof(normal)); - for(int i=0; i < m->nVertices; i++) - fscanf(fp, "%f; %f; %f;%*[;,]\n", - &(m->normals[i][0]), &(m->normals[i][1]), &(m->normals[i][2])); - - while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0) - fgets(line, lineBuffSize, fp); - - fgets(line, lineBuffSize, fp); - m->texCoords = malloc(m->nVertices * sizeof(texCoord)); - for(int i=0; i < m->nVertices; i++) - fscanf(fp, "%f;%f;%*[,;]\n", &(m->texCoords[i][0]), &(m->texCoords[i][1]) ); - fclose(fp); - - return m; -} +enum { + MISSING, EXTENSION, ONE_DOT_ONE +}; +int polygonOffsetVersion; + +static GLdouble bodyWidth = 3.0; +/* *INDENT-OFF* */ +static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5}, + {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16}, + {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2}, + {1, 2} }; +static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9}, + {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10}, + {13, 9}, {11, 11}, {9, 11} }; +static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0}, + {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} }; +static GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15}, + {9.6, 15.25}, {9, 15.25} }; +static GLfloat lightPosition[4]; +static GLfloat lightColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */ +static GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0}; +/* *INDENT-ON* */ + +/* Nice floor texture tiling pattern. */ +static char *circles[] = { + "....xxxx........", + "..xxxxxxxx......", + ".xxxxxxxxxx.....", + ".xxx....xxx.....", + "xxx......xxx....", + "xxx......xxx....", + "xxx......xxx....", + "xxx......xxx....", + ".xxx....xxx.....", + ".xxxxxxxxxx.....", + "..xxxxxxxx......", + "....xxxx........", + "................", + "................", + "................", + "................", +}; -// [You may want to add to this function.] -/** - * Loads mesh[i] if it isn't already loaded. - * You must call getMesh(i) at least once before using mesh[i]. - * - * @param i Mesh ID - */ -void getMesh(int i) { // getMesh(i) loads mesh[i] if it isn't already loaded. - char fileName[220]; - if(i>=NMESH || i<0) { - printf("Error in getMesh - wrong model number"); - exit(1); +static void +makeFloorTexture(void) +{ + GLubyte floorTexture[16][16][3]; + GLubyte *loc; + int s, t; + + /* Setup RGB image for the texture. */ + loc = (GLubyte*) floorTexture; + for (t = 0; t < 16; t++) { + for (s = 0; s < 16; s++) { + if (circles[t][s] == 'x') { + /* Nice green. */ + loc[0] = 0x1f; + loc[1] = 0x8f; + loc[2] = 0x1f; + } else { + /* Light gray. */ + loc[0] = 0xaa; + loc[1] = 0xaa; + loc[2] = 0xaa; + } + loc += 3; } - if(meshes[i] != NULL) - return; - sprintf(fileName, "%s/model%d.x", dataDir, i+1); - meshes[i] = loadMesh(fileName); -} + } -/** - * Loads texture i if it isn't already loaded - * - * After calling getTexture(i), you can make texture i the current texture using - * glBindTexture(GL_TEXTURE_2D, i); - * Use i=0 to return to the default plain texture. - * - * You can then scale the texture via: - * glMatrixMode(GL_TEXTURE); - * See the textbook, section 8.8.3. - * - * You must call getTexture(i) at least once before using texture i. - * @param i Texture ID - */ -void getTexture(int i) { - char fileName[220]; - if(i<1 || i>NTEXTURE) { - printf("Error in getTexture - wrong texture number"); - exit(1); - } - if(textures[i-1] != NULL) - return; - sprintf(fileName, "%s/texture%d.bmp", dataDir, i); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - textures[i-1] = loadTexture(fileName); + if (useMipmaps) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16, + GL_RGB, GL_UNSIGNED_BYTE, floorTexture); + } else { + if (linearFiltering) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0, + GL_RGB, GL_UNSIGNED_BYTE, floorTexture); + } +} - glBindTexture(GL_TEXTURE_2D, i); +enum { + X, Y, Z, W +}; +enum { + A, B, C, D +}; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textures[i-1]->width, textures[i-1]->height, - 0, GL_RGB, GL_UNSIGNED_BYTE, textures[i-1]->rgbData); - gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, textures[i-1]->width, textures[i-1]->height, GL_RGB, - GL_UNSIGNED_BYTE, textures[i-1]->rgbData); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +/* Create a matrix that will project the desired shadow. */ +void +shadowMatrix(GLfloat shadowMat[4][4], + GLfloat groundplane[4], + GLfloat lightpos[4]) +{ + GLfloat dot; + + /* Find dot product between light position vector and ground plane normal. */ + dot = groundplane[X] * lightpos[X] + + groundplane[Y] * lightpos[Y] + + groundplane[Z] * lightpos[Z] + + groundplane[W] * lightpos[W]; + + shadowMat[0][0] = dot - lightpos[X] * groundplane[X]; + shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y]; + shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z]; + shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W]; + + shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X]; + shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y]; + shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z]; + shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W]; + + shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X]; + shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y]; + shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z]; + shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W]; + + shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X]; + shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y]; + shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z]; + shadowMat[3][3] = dot - lightpos[W] * groundplane[W]; - glBindTexture(GL_TEXTURE_2D, 0); // Back to default texture } -/** - * Event hander for main menu events - * @param id ID of menu item selected - */ -void processMainEvents(int id) { - switch (id) { - case ROTATE_MOVE_CAMERA: - // Do stuff - break; +/* Find the plane equation given 3 points. */ +void +findPlane(GLfloat plane[4], + GLfloat v0[3], GLfloat v1[3], GLfloat v2[3]) +{ + GLfloat vec0[3], vec1[3]; - case POSITION_SCALE: - // Do stuff - break; + /* Need 2 vectors to find cross product. */ + vec0[X] = v1[X] - v0[X]; + vec0[Y] = v1[Y] - v0[Y]; + vec0[Z] = v1[Z] - v0[Z]; - case ROTATION_TEXTURE_SCALE: - // Do stuff - break; + vec1[X] = v2[X] - v0[X]; + vec1[Y] = v2[Y] - v0[Y]; + vec1[Z] = v2[Z] - v0[Z]; - case EXIT: - exit(EXIT_SUCCESS); + /* find cross product to get A, B, and C of plane equation */ + plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y]; + plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]); + plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X]; - } + plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]); } -/** - * Event hander for materials menu events - * @param id ID of menu item selected - */ -void processMaterialEvents(int id) { - switch (id) { - case MATERIAL_ALL_RGB: - // Do stuff - break; - - case MATERIAL_AMBIENT_RGB: - // Do stuff - break; +void +extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize, + GLdouble thickness, GLuint side, GLuint edge, GLuint whole) +{ + static GLUtriangulatorObj *tobj = NULL; + GLdouble vertex[3], dx, dy, len; + int i; + int count = (int) (dataSize / (2 * sizeof(GLfloat))); + + if (tobj == NULL) { + tobj = gluNewTess(); /* create and initialize a GLU + polygon tesselation object */ + gluTessCallback(tobj, GLU_BEGIN, glBegin); + gluTessCallback(tobj, GLU_VERTEX, glVertex2fv); /* semi-tricky */ + gluTessCallback(tobj, GLU_END, glEnd); + } + glNewList(side, GL_COMPILE); + glShadeModel(GL_SMOOTH); /* smooth minimizes seeing + tessellation */ + gluBeginPolygon(tobj); + for (i = 0; i < count; i++) { + vertex[0] = data[i][0]; + vertex[1] = data[i][1]; + vertex[2] = 0; + gluTessVertex(tobj, vertex, data[i]); + } + gluEndPolygon(tobj); + glEndList(); + glNewList(edge, GL_COMPILE); + glShadeModel(GL_FLAT); /* flat shade keeps angular hands + from being "smoothed" */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= count; i++) { + /* mod function handles closing the edge */ + glVertex3f(data[i % count][0], data[i % count][1], 0.0); + glVertex3f(data[i % count][0], data[i % count][1], thickness); + /* Calculate a unit normal by dividing by Euclidean + distance. We * could be lazy and use + glEnable(GL_NORMALIZE) so we could pass in * arbitrary + normals for a very slight performance hit. */ + dx = data[(i + 1) % count][1] - data[i % count][1]; + dy = data[i % count][0] - data[(i + 1) % count][0]; + len = sqrt(dx * dx + dy * dy); + glNormal3f(dx / len, dy / len, 0.0); + } + glEnd(); + glEndList(); + glNewList(whole, GL_COMPILE); + glFrontFace(GL_CW); + glCallList(edge); + glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */ + glCallList(side); + glPushMatrix(); + glTranslatef(0.0, 0.0, thickness); + glFrontFace(GL_CCW); + glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */ + glCallList(side); + glPopMatrix(); + glEndList(); +} - case MATERIAL_DIFFUSE_RGB: - // Do stuff - break; +/* Enumerants for refering to display lists. */ +typedef enum { + RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE, + LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE +} displayLists; + +static void +makeDinosaur(void) +{ + extrudeSolidFromPolygon(body, sizeof(body), bodyWidth, + BODY_SIDE, BODY_EDGE, BODY_WHOLE); + extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4, + ARM_SIDE, ARM_EDGE, ARM_WHOLE); + extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2, + LEG_SIDE, LEG_EDGE, LEG_WHOLE); + extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2, + EYE_SIDE, EYE_EDGE, EYE_WHOLE); +} - case MATERIAL_SPECULAR_RGB: - // Do stuff - break; +static void +drawDinosaur(void) - case MATERIAL_ALL_ADSS: - // Do stuff - break; +{ + glPushMatrix(); + /* Translate the dinosaur to be at (0,8,0). */ + glTranslatef(-8, 0, -bodyWidth / 2); + glTranslatef(0.0, jump, 0.0); + glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor); + glCallList(BODY_WHOLE); + glTranslatef(0.0, 0.0, bodyWidth); + glCallList(ARM_WHOLE); + glCallList(LEG_WHOLE); + glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4); + glCallList(ARM_WHOLE); + glTranslatef(0.0, 0.0, -bodyWidth / 4); + glCallList(LEG_WHOLE); + glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1); + glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor); + glCallList(EYE_WHOLE); + glPopMatrix(); +} - case MATERIAL_RED_ADSS: - // Do stuff - break; +static GLfloat floorVertices[4][3] = { + { -20.0, 0.0, 20.0 }, + { 20.0, 0.0, 20.0 }, + { 20.0, 0.0, -20.0 }, + { -20.0, 0.0, -20.0 }, +}; - case MATERIAL_GREEN_ADSS: - // Do stuff - break; +/* Draw a floor (possibly textured). */ +static void +drawFloor(void) +{ + glDisable(GL_LIGHTING); - case MATERIAL_BLUE_ADSS: - // Do stuff - break; + if (useTexture) { + glEnable(GL_TEXTURE_2D); + } + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); + glVertex3fv(floorVertices[0]); + glTexCoord2f(0.0, 16.0); + glVertex3fv(floorVertices[1]); + glTexCoord2f(16.0, 16.0); + glVertex3fv(floorVertices[2]); + glTexCoord2f(16.0, 0.0); + glVertex3fv(floorVertices[3]); + glEnd(); + + if (useTexture) { + glDisable(GL_TEXTURE_2D); } + + glEnable(GL_LIGHTING); } -/** - * Event hander for light menu events - * @param id ID of menu item selected - */ -void processLightEvents(int id) { - switch (id) { - case LIGHT_MOVE_LIGHT_1: - // Do stuff - break; +static GLfloat floorPlane[4]; +static GLfloat floorShadow[4][4]; - case LIGHT_RGBALL_LIGHT_1: - // Do stuff - break; +static void +redraw(void) +{ + int start, end; - case LIGHT_MOVE_LIGHT_2: - // Do stuff - break; + if (reportSpeed) { + start = glutGet(GLUT_ELAPSED_TIME); + } - case LIGHT_RGBALL_LIGHT_2: - // Do stuff - break; + /* Clear; default stencil clears to zero. */ + if ((stencilReflection && renderReflection) || (stencilShadow && renderShadow)) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } else { + /* Avoid clearing stencil when not using it. */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + /* Reposition the light source. */ + lightPosition[0] = 12*cos(lightAngle); + lightPosition[1] = lightHeight; + lightPosition[2] = 12*sin(lightAngle); + if (directionalLight) { + lightPosition[3] = 0.0; + } else { + lightPosition[3] = 1.0; } -} -/** - * Event hander for object menu events - * @param id ID of object selected - */ -void processObjectEvents(int id) { + shadowMatrix(floorShadow, floorPlane, lightPosition); -} + glPushMatrix(); + /* Perform scene rotations based on user mouse input. */ + glRotatef(angle2, 1.0, 0.0, 0.0); + glRotatef(angle, 0.0, 1.0, 0.0); -/** - * Event hander for texture menu events - * @param id ID of texutre selected - */ -void processTextureEvents(int id) { + /* Tell GL new light source position. */ + glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); -} + if (renderReflection) { + if (stencilReflection) { + /* We can eliminate the visual "artifact" of seeing the "flipped" + dinosaur underneath the floor by using stencil. The idea is + draw the floor without color or depth update but so that + a stencil value of one is where the floor will be. Later when + rendering the dinosaur reflection, we will only update pixels + with a stencil value of 1 to make sure the reflection only + lives on the floor, not below the floor. */ -/** - * Event hander for ground texture menu events - * @param id ID of ground texture selected - */ -void processGTextureEvents(int id) { + /* Don't update color or depth. */ + glDisable(GL_DEPTH_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); -} + /* Draw 1 into the stencil buffer. */ + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, 0xffffffff); -/** - * Rounds up numbers, from http://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number - * @param numToRound Number to round - * @param multiple Multiple to round up to - * @return Rounded number - */ -int roundUp(int numToRound, int multiple) { - if(multiple == 0) { - return numToRound; - } + /* Now render floor; floor pixels just get their stencil set to 1. */ + drawFloor(); - int remainder = numToRound % multiple; - if (remainder == 0) - return numToRound; - return numToRound + multiple - remainder; -} + /* Re-enable update of color and depth. */ + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glEnable(GL_DEPTH_TEST); + + /* Now, only render where stencil is set to 1. */ + glStencilFunc(GL_EQUAL, 1, 0xffffffff); /* draw if ==1 */ + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + } + + glPushMatrix(); + + /* The critical reflection step: Reflect dinosaur through the floor + (the Y=0 plane) to make a relection. */ + glScalef(1.0, -1.0, 1.0); + + /* Reflect the light position. */ + glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); + + /* To avoid our normals getting reversed and hence botched lighting + on the reflection, turn on normalize. */ + glEnable(GL_NORMALIZE); + glCullFace(GL_FRONT); -/** - * Makes a submenu from an array of items, splitting the list into subsubmenus - * of only 10 items. - * @param menuEntries Array of menu items - * @param menuEntriesSize Size of menuEntries - * @param callback Callback function for this array of menu items - * @return Reference to menu created - */ -int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) { - if ( menuEntriesSize == 0 ) return -1; - - int menuNumber = roundUp(menuEntriesSize, 10) / 10; - int submenuObjects[menuNumber-1]; - - for( int i = 0; i < menuNumber; i++ ) { - submenuObjects[i] = glutCreateMenu(callback); - int startNum = i*11 - (i-1); - for ( int j = startNum - 1; j < (startNum+9); j++ ) { - if ( j == menuEntriesSize ) break; // Detect if we've reached the end of the array - glutAddMenuEntry( menuEntries[j], j + 1 ); + /* Draw the reflected dinosaur. */ + drawDinosaur(); + + /* Disable noramlize again and re-enable back face culling. */ + glDisable(GL_NORMALIZE); + glCullFace(GL_BACK); + + glPopMatrix(); + + /* Switch back to the unreflected light position. */ + glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); + + if (stencilReflection) { + glDisable(GL_STENCIL_TEST); + } } - } - - int mainMenu = glutCreateMenu(callback); - for ( int i = 0; i < menuNumber; i++ ) { - char name[10]; // buffer to hold name - int startNum = i*11 - (i-1); - int endNum = startNum + 9; - if ( i == menuNumber - 1 ) { // We're on the last one - endNum = startNum + (menuEntriesSize - startNum); // Work out final number + + /* Back face culling will get used to only draw either the top or the + bottom floor. This let's us get a floor with two distinct + appearances. The top floor surface is reflective and kind of red. + The bottom floor surface is not reflective and blue. */ + + /* Draw "bottom" of floor in blue. */ + glFrontFace(GL_CW); /* Switch face orientation. */ + glColor4f(0.1, 0.1, 0.7, 1.0); + drawFloor(); + glFrontFace(GL_CCW); + + if (renderShadow) { + if (stencilShadow) { + /* Draw the floor with stencil value 3. This helps us only + draw the shadow once per floor pixel (and only on the + floor pixels). */ + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 3, 0xffffffff); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + } } - sprintf(name, "%d-%d", startNum, endNum); - glutAddSubMenu( name, submenuObjects[i] ); - } - return mainMenu; -} + /* Draw "top" of floor. Use blending to blend in reflection. */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0.7, 0.0, 0.0, 0.3); + glColor4f(1.0, 1.0, 1.0, 0.3); + drawFloor(); + glDisable(GL_BLEND); -/** - * Creates menu for program - */ -void makeMenu() { - // Construct material menu - int materialMenu = glutCreateMenu(processMaterialEvents); - glutAddMenuEntry("All R/G/B", MATERIAL_ALL_RGB); - glutAddMenuEntry("Ambient R/G/B", MATERIAL_AMBIENT_RGB); - glutAddMenuEntry("Diffuse R/G/B", MATERIAL_DIFFUSE_RGB); - glutAddMenuEntry("Specular R/G/B", MATERIAL_SPECULAR_RGB); - glutAddMenuEntry("All Amb/Diff/Spec/Shine", MATERIAL_ALL_ADSS); - glutAddMenuEntry("Red Amb/Diff/Spec/Shine", MATERIAL_RED_ADSS); - glutAddMenuEntry("Green Amb/Diff/Spec/Shine", MATERIAL_GREEN_ADSS); - glutAddMenuEntry("Blue Amb/Diff/Spec/Shine", MATERIAL_BLUE_ADSS); - - // Construct light menu - int lightMenu = glutCreateMenu(processLightEvents); - glutAddMenuEntry("Move Light 1", LIGHT_MOVE_LIGHT_1); - glutAddMenuEntry("R/G/B/All Light 1", LIGHT_RGBALL_LIGHT_1); - glutAddMenuEntry("Move Light 2", LIGHT_MOVE_LIGHT_2); - glutAddMenuEntry("R/G/B/All Light 2", LIGHT_RGBALL_LIGHT_2); - - // Construct object menu - int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]); - int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents ); - - // Construct texture / ground texture menus - int textureMenuEntriesSize = sizeof(textureMenuEntries) / sizeof(textureMenuEntries[0]); - int textureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processTextureEvents ); - int gTextureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processGTextureEvents ); - - // Construct main menu - glutCreateMenu(processMainEvents); - //glutAddMenuEntry("Rotate/Move Camera", ROTATE_MOVE_CAMERA); - //glutAddSubMenu("Add object", objectMenu); - //glutAddMenuEntry("Position/Scale", POSITION_SCALE); - //glutAddMenuEntry("Rotation/Texture Scale", ROTATION_TEXTURE_SCALE); - //glutAddSubMenu("Material", materialMenu); - //glutAddSubMenu("Texture", textureMenu); - //glutAddSubMenu("Ground texture", gTextureMenu); - //glutAddSubMenu("Lights", lightMenu); - glutAddMenuEntry("Exit", EXIT); - - // Bind to right mouse button - glutAttachMenu(GLUT_RIGHT_BUTTON); -} + if (renderDinosaur) { + /* Draw "actual" dinosaur, not its reflection. */ + drawDinosaur(); + } -/** - * Called when window is resized - * @param w New width - * @param h New height - */ -void windowReshape(int w, int h) { - glViewport(0, 0, (GLsizei) w, (GLsizei) h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - if (w <= h) - glOrtho(near, far, near*(GLfloat)h/(GLfloat)w, - far*(GLfloat)h/(GLfloat)w, near, far); - else - glOrtho(near*(GLfloat)w/(GLfloat)h, - far*(GLfloat)w/(GLfloat)h, near, far, near, far); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + if (renderShadow) { + + /* Render the projected shadow. */ + + if (stencilShadow) { + + /* Now, only render where stencil is set above 2 (ie, 3 where + the top floor is). Update stencil with 2 where the shadow + gets drawn so we don't redraw (and accidently reblend) the + shadow). */ + glStencilFunc(GL_LESS, 2, 0xffffffff); /* draw if ==1 */ + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + } + + /* To eliminate depth buffer artifacts, we use polygon offset + to raise the depth of the projected shadow slightly so + that it does not depth buffer alias with the floor. */ + if (offsetShadow) { + switch (polygonOffsetVersion) { + case EXTENSION: +#ifdef GL_EXT_polygon_offset + glEnable(GL_POLYGON_OFFSET_EXT); + break; +#endif +#ifdef GL_VERSION_1_1 + case ONE_DOT_ONE: + glEnable(GL_POLYGON_OFFSET_FILL); + break; +#endif + case MISSING: + /* Oh well. */ + break; + } + } + + /* Render 50% black shadow color on top of whatever the + floor appareance is. */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_LIGHTING); /* Force the 50% black. */ + glColor4f(0.0, 0.0, 0.0, 0.5); + + glPushMatrix(); + /* Project the shadow. */ + glMultMatrixf((GLfloat *) floorShadow); + drawDinosaur(); + glPopMatrix(); + + glDisable(GL_BLEND); + glEnable(GL_LIGHTING); + + if (offsetShadow) { + switch (polygonOffsetVersion) { +#ifdef GL_EXT_polygon_offset + case EXTENSION: + glDisable(GL_POLYGON_OFFSET_EXT); + break; +#endif +#ifdef GL_VERSION_1_1 + case ONE_DOT_ONE: + glDisable(GL_POLYGON_OFFSET_FILL); + break; +#endif + case MISSING: + /* Oh well. */ + break; + } + } + if (stencilShadow) { + glDisable(GL_STENCIL_TEST); + } + } + + glPushMatrix(); + glDisable(GL_LIGHTING); + glColor3f(1.0, 1.0, 0.0); + if (directionalLight) { + /* Draw an arrowhead. */ + glDisable(GL_CULL_FACE); + glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]); + glRotatef(lightAngle * -180.0 / M_PI, 0, 1, 0); + glRotatef(atan(lightHeight/12) * 180.0 / M_PI, 0, 0, 1); + glBegin(GL_TRIANGLE_FAN); + glVertex3f(0, 0, 0); + glVertex3f(2, 1, 1); + glVertex3f(2, -1, 1); + glVertex3f(2, -1, -1); + glVertex3f(2, 1, -1); + glVertex3f(2, 1, 1); + glEnd(); + /* Draw a white line from light direction. */ + glColor3f(1.0, 1.0, 1.0); + glBegin(GL_LINES); + glVertex3f(0, 0, 0); + glVertex3f(5, 0, 0); + glEnd(); + glEnable(GL_CULL_FACE); + } else { + /* Draw a yellow ball at the light source. */ + glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]); + glutSolidSphere(1.0, 5, 5); + } + glEnable(GL_LIGHTING); + glPopMatrix(); + + glPopMatrix(); + + if (reportSpeed) { + glFinish(); + end = glutGet(GLUT_ELAPSED_TIME); + printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start); + } + + glutSwapBuffers(); } -/** - * Called when mouse event occurs - * @param btn Mouse button - * @param state State of mouse button - * @param x Mouse x position - * @param y Mouse y position - */ -void mouse(int button, int state, int x, int y) { +/* ARGSUSED2 */ +static void +mouse(int button, int state, int x, int y) +{ if (button == GLUT_LEFT_BUTTON) { if (state == GLUT_DOWN) { moving = 1; @@ -542,12 +636,10 @@ void mouse(int button, int state, int x, int y) { } } -/** - * Called when motion event occurs - * @param x Mouse x position - * @param y Mouse y position - */ -void motion(int x, int y) { +/* ARGSUSED1 */ +static void +motion(int x, int y) +{ if (moving) { angle = angle + (x - startx); angle2 = angle2 + (y - starty); @@ -564,203 +656,237 @@ void motion(int x, int y) { } } -/** - * Recursive function to draw a square by drawing smaller and smaller - * divisions of the square, determined by drawFloorRecurse. - * @param recurseLevel Current level of recursion, only pass 0 - * @param x1 top-left x - * @param z1 top-left z - * @param x2 bottom-left x - * @param z2 bottom-left z - */ -void drawSquare(int recurseLevel, float x1, float z1, float x2, float z2) { - - if ( drawFloorRecurse != recurseLevel ) { - // Calculate middle points - float xm = (x1 + x2) / 2.0; - float zm = (z1 + z2) / 2.0; - - // Increment recursion level - int rnew = recurseLevel + 1; - - // Split into four sub-quads - drawSquare(rnew, x1, z1, xm, zm); - drawSquare(rnew, x1, zm, xm, z2); - drawSquare(rnew, xm, zm, x2, z2); - drawSquare(rnew, xm, z1, x2, zm); +/* Advance time varying state when idle callback registered. */ +static void +idle(void) +{ + static float time = 0.0; - } else { - // Draw square. - // **NOTE: Is the polygon facing in the right direction? - glBegin(GL_QUADS); - glVertex3f(x1, 0.0, z1); - glVertex3f(x1, 0.0, z2); - glVertex3f(x2, 0.0, z2); - glVertex3f(x2, 0.0, z1); - glEnd(); - } + time = glutGet(GLUT_ELAPSED_TIME) / 500.0; + jump = 4.0 * fabs(sin(time)*0.5); + if (!lightMoving) { + lightAngle += 0.03; + } + glutPostRedisplay(); } -/** - * Draw a floor by calling the drawSquare recursion - */ -void drawFloor() { - drawSquare(0, -floorSize, -floorSize, floorSize, floorSize); -} +enum { + M_NONE, M_MOTION, M_LIGHT, M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR, + M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW, + M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE +}; -/** - * Draw x, z axis on floor - */ -void drawLine() { - // **NOTE: fix function - glDisable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4ub( 0.0, 0.0, 0.0, 0.5 ); +static void +controlLights(int value) +{ + switch (value) { + case M_NONE: + return; + case M_MOTION: + animation = 1 - animation; + if (animation) { + glutIdleFunc(idle); + } else { + glutIdleFunc(NULL); + } + break; + case M_LIGHT: + lightSwitch = !lightSwitch; + if (lightSwitch) { + glEnable(GL_LIGHT0); + } else { + glDisable(GL_LIGHT0); + } + break; + case M_TEXTURE: + useTexture = !useTexture; + break; + case M_SHADOWS: + renderShadow = 1 - renderShadow; + break; + case M_REFLECTION: + renderReflection = 1 - renderReflection; + break; + case M_DINOSAUR: + renderDinosaur = 1 - renderDinosaur; + break; + case M_STENCIL_REFLECTION: + stencilReflection = 1 - stencilReflection; + break; + case M_STENCIL_SHADOW: + stencilShadow = 1 - stencilShadow; + break; + case M_OFFSET_SHADOW: + offsetShadow = 1 - offsetShadow; + break; + case M_POSITIONAL: + directionalLight = 0; + break; + case M_DIRECTIONAL: + directionalLight = 1; + break; + case M_PERFORMANCE: + reportSpeed = 1 - reportSpeed; + break; + } + glutPostRedisplay(); +} - glBegin(GL_LINES); - glVertex3i( 10.0, 0.1, 10.0); - glVertex3i( -10.0, 0.1, -10.0); - glEnd(); +/* When not visible, stop animating. Restart when visible again. */ +static void +visible(int vis) +{ + if (vis == GLUT_VISIBLE) { + if (animation) + glutIdleFunc(idle); + } else { + if (!animation) + glutIdleFunc(NULL); + } +} - glDisable(GL_BLEND); - glEnable(GL_TEXTURE_2D); +/* Press any key to redraw; good when motion stopped and + performance reporting on. */ +/* ARGSUSED */ +static void +key(unsigned char c, int x, int y) +{ + if (c == 27) { + exit(0); /* IRIS GLism, Escape quits. */ + } + glutPostRedisplay(); } -/** - * Display function - */ -void display() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); - gluLookAt( - 0.0, 0.0, 10.0, /* eye is at (x,y,z) */ - 0.0, 0.0, 0.0, /* center is at (x,y,z) */ - 0.0, 1.0, 0.0 /* up is in postivie Y direction */ - ); +/* Press any key to redraw; good when motion stopped and + performance reporting on. */ +/* ARGSUSED */ +static void +special(int k, int x, int y) +{ + glutPostRedisplay(); +} +static int +supportsOneDotOne(void) +{ + const char *version; + int major, minor; - // **NOTE: Currently this rotation function is all that moves the camera off - // the flat surface. Need to integrate function into gluLookAt - glRotatef(30.0, 1.0, 0.0, 0.0); + version = (char *) glGetString(GL_VERSION); + if (sscanf(version, "%d.%d", &major, &minor) == 2) + return major >= 1 && minor >= 1; + return 0; /* OpenGL version string malformed! */ +} - /* Reposition the light source. */ - lightPosition[0] = 12*cos(lightAngle); - lightPosition[1] = lightHeight; - lightPosition[2] = 12*sin(lightAngle); - lightPosition[3] = 0.0; +int +main(int argc, char **argv) +{ + int i; - glPushMatrix(); + glutInit(&argc, argv); - /* Perform scene rotations based on user mouse input. */ - glRotatef(angle, 0.0, 1.0, 0.0); - //glRotatef(angle2, 1.0, 0.0, 0.0); **NOTE: Only one degree of freedom + for (i=1; i=2 rgb double depth"); +#endif - drawLine(); - - // Draw teapot for a test object - glPushMatrix(); - glTranslatef(0.0, 0.5, 0.0); // **NOTE: Teapot does not rest on surface - glColor3f(0.5, 0.5, 0.5); - glutSolidTeapot(1); - glPopMatrix(); + glutCreateWindow("Shadowy Leapin' Lizards"); - // Draw a white ball over the light source - glPushMatrix(); - glDisable(GL_LIGHTING); - glColor3f(1.0, 1.0, 1.0); - glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]); - glutSolidSphere(1.0, 50, 50); - glEnable(GL_LIGHTING); - glPopMatrix(); + if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) { + printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n"); + exit(1); + } - glPopMatrix(); + /* Register GLUT callbacks. */ + glutDisplayFunc(redraw); + glutMouseFunc(mouse); + glutMotionFunc(motion); + glutVisibilityFunc(visible); + glutKeyboardFunc(key); + glutSpecialFunc(special); + + glutCreateMenu(controlLights); + + glutAddMenuEntry("Toggle motion", M_MOTION); + glutAddMenuEntry("-----------------------", M_NONE); + glutAddMenuEntry("Toggle light", M_LIGHT); + glutAddMenuEntry("Toggle texture", M_TEXTURE); + glutAddMenuEntry("Toggle shadows", M_SHADOWS); + glutAddMenuEntry("Toggle reflection", M_REFLECTION); + glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR); + glutAddMenuEntry("-----------------------", M_NONE); + glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION); + glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW); + glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW); + glutAddMenuEntry("----------------------", M_NONE); + glutAddMenuEntry("Positional light", M_POSITIONAL); + glutAddMenuEntry("Directional light", M_DIRECTIONAL); + glutAddMenuEntry("-----------------------", M_NONE); + glutAddMenuEntry("Toggle performance", M_PERFORMANCE); + glutAttachMenu(GLUT_RIGHT_BUTTON); + makeDinosaur(); + +#ifdef GL_VERSION_1_1 + if (supportsOneDotOne() && !forceExtension) { + polygonOffsetVersion = ONE_DOT_ONE; + glPolygonOffset(-2.0, -1.0); + } else +#endif + { +#ifdef GL_EXT_polygon_offset + /* check for the polygon offset extension */ + if (glutExtensionSupported("GL_EXT_polygon_offset")) { + polygonOffsetVersion = EXTENSION; + glPolygonOffsetEXT(-0.1, -0.002); + } else +#endif + { + polygonOffsetVersion = MISSING; + printf("\ndinoshine: Missing polygon offset.\n"); + printf(" Expect shadow depth aliasing artifacts.\n\n"); + } + } - glutSwapBuffers(); -} + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glLineWidth(3.0); -/** - * init function; sets initial OpenGL state - */ -void init() { glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - //gluPerspective( - // 60.0, /* field of view in degree */ - // 1.0, /* aspect ratio */ - // near, /* Z near */ - // far /* Z far */ - // ); - - - glLightfv(GL_LIGHT0, GL_POSITION, light0_pos); - glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0); - glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0); - glLightfv(GL_LIGHT0, GL_SPECULAR, specular0); - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, glightmodel); + gluPerspective( /* field of view in degree */ 40.0, + /* aspect ratio */ 1.0, + /* Z near */ 20.0, /* Z far */ 100.0); + glMatrixMode(GL_MODELVIEW); + gluLookAt(0.0, 8.0, 60.0, /* eye is at (0,8,60) */ + 0.0, 8.0, 0.0, /* center is at (0,8,0) */ + 0.0, 1.0, 0.); /* up is in postivie Y direction */ + + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); + glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor); + glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1); + glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); - glEnable(GL_COLOR_MATERIAL); - glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); - glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular0); - glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, emission0); - - - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - -/** - * Main function - * @param argc Number of arguments - * @param argv Array of arguments - * @return Program exit code - */ -int main(int argc, char **argv) { - if(argc>1) - strcpy(dataDir, argv[1]); - else if(opendir(dirDefault1)) - strcpy(dataDir, dirDefault1); - else if(opendir(dirDefault2)) - strcpy(dataDir, dirDefault2); - else fileErr(dirDefault1); - - for(int i=0; i