Description: Table Tennis Simulator

Video:

Source Code (Visual Studio 2010 Solution): Source (Zip) or from github

 

Doxygen

Header Files:

Collision.h:

/**
	Author: Matthew Vlietstra
	FileName: Collision.h
	Version: 0.1
	Purpose: Provides verious collision detection methods.

	Description:

*/

#ifndef COLLISION_H
#define COLLISION_H

#include 
#include "gl/glut.h"
#include "Objects.h"
#include 

int sphereCollisionDetection(vector3Df aLocation, float aRadius, vector3Df bLocation, float bRadius);
int boxAndSpehereCollision(	obj *sphere, const float location[3], const float XYZ[3]);
int collision222(obj *temp, const float location[3], const float XYZ[3], float coef);
void collisionDetection();
void racketCollision();

#endif

Controls.h:

/**
	Author: Matthew Vlietstra
	FileName: Controls.h
	Version: 0.1
	Purpose: Provides various input methods used to control aspects of the program.

	Description:

*/

#ifndef CONTROLS_H
#define CONTROLS_H

#include 
#include "gl/glut.h" 
#include "Menu.h"
#include 
#include "Objects.h"
#include "MenuItems.h"
#include "Misc.h"

//	
int keys[256]; //An array to keep track of keys pressed 🙂
//	

void kbSpecialFunc(int key, int x, int y);
void kbUp(unsigned char key, int x, int y);
void kbDown(unsigned char key, int x, int y);
void centerMouse();
void checkKeyPressed();
void passiveMouse(int x, int y);
void mouseFunc(int button, int state, int x, int y);

#endif

glm.h:

/*    
      glm.h
      Nate Robins, 1997, 2000
      nate@pobox.com, http://www.pobox.com/~nate

      Wavefront OBJ model file format reader/writer/manipulator.

      Includes routines for generating smooth normals with
      preservation of edges, welding redundant vertices & texture
      coordinate generation (spheremap and planar projections) + more.

 */

#include 

#ifndef M_PI
#define M_PI 3.14159265f
#endif

#define GLM_NONE     (0)            /* render with only vertices */
#define GLM_FLAT     (1 << 0)       /* render with facet normals */
#define GLM_SMOOTH   (1 << 1)       /* render with vertex normals */
#define GLM_TEXTURE  (1 << 2)       /* render with texture coords */
#define GLM_COLOR    (1 << 3)       /* render with colors */
#define GLM_MATERIAL (1 << 4)       /* render with materials */

/* GLMmaterial: Structure that defines a material in a model. 
 */
typedef struct _GLMmaterial
{
  char* name;                   /* name of material */
  GLfloat diffuse[4];           /* diffuse component */
  GLfloat ambient[4];           /* ambient component */
  GLfloat specular[4];          /* specular component */
  GLfloat emmissive[4];         /* emmissive component */
  GLfloat shininess;            /* specular exponent */
} GLMmaterial;

/* GLMtriangle: Structure that defines a triangle in a model.
 */
typedef struct _GLMtriangle {
  GLuint vindices[3];           /* array of triangle vertex indices */
  GLuint nindices[3];           /* array of triangle normal indices */
  GLuint tindices[3];           /* array of triangle texcoord indices*/
  GLuint findex;                /* index of triangle facet normal */
} GLMtriangle;

/* GLMgroup: Structure that defines a group in a model.
 */
typedef struct _GLMgroup {
  char*             name;           /* name of this group */
  GLuint            numtriangles;   /* number of triangles in this group */
  GLuint*           triangles;      /* array of triangle indices */
  GLuint            material;       /* index to material for group */
  struct _GLMgroup* next;           /* pointer to next group in model */
} GLMgroup;

/* GLMmodel: Structure that defines a model.
 */
typedef struct _GLMmodel {
  char*    pathname;            /* path to this model */
  char*    mtllibname;          /* name of the material library */

  GLuint   numvertices;         /* number of vertices in model */
  GLfloat* vertices;            /* array of vertices  */

  GLuint   numnormals;          /* number of normals in model */
  GLfloat* normals;             /* array of normals */

  GLuint   numtexcoords;        /* number of texcoords in model */
  GLfloat* texcoords;           /* array of texture coordinates */

  GLuint   numfacetnorms;       /* number of facetnorms in model */
  GLfloat* facetnorms;          /* array of facetnorms */

  GLuint       numtriangles;    /* number of triangles in model */
  GLMtriangle* triangles;       /* array of triangles */

  GLuint       nummaterials;    /* number of materials in model */
  GLMmaterial* materials;       /* array of materials */

  GLuint       numgroups;       /* number of groups in model */
  GLMgroup*    groups;          /* linked list of groups */

  GLfloat position[3];          /* position of the model */

} GLMmodel;

/* glmUnitize: "unitize" a model by translating it to the origin and
 * scaling it to fit in a unit cube around the origin.  Returns the
 * scalefactor used.
 *
 * model - properly initialized GLMmodel structure 
 */
GLfloat
glmUnitize(GLMmodel* model);

/* glmDimensions: Calculates the dimensions (width, height, depth) of
 * a model.
 *
 * model      - initialized GLMmodel structure
 * dimensions - array of 3 GLfloats (GLfloat dimensions[3])
 */
GLvoid
glmDimensions(GLMmodel* model, GLfloat* dimensions);

/* glmScale: Scales a model by a given amount.
 * 
 * model - properly initialized GLMmodel structure
 * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
 */
GLvoid
glmScale(GLMmodel* model, GLfloat scale);

/* glmReverseWinding: Reverse the polygon winding for all polygons in
 * this model.  Default winding is counter-clockwise.  Also changes
 * the direction of the normals.
 * 
 * model - properly initialized GLMmodel structure 
 */
GLvoid
glmReverseWinding(GLMmodel* model);

/* glmFacetNormals: Generates facet normals for a model (by taking the
 * cross product of the two vectors derived from the sides of each
 * triangle).  Assumes a counter-clockwise winding.
 *
 * model - initialized GLMmodel structure
 */
GLvoid
glmFacetNormals(GLMmodel* model);

/* glmVertexNormals: Generates smooth vertex normals for a model.
 * First builds a list of all the triangles each vertex is in.  Then
 * loops through each vertex in the the list averaging all the facet
 * normals of the triangles each vertex is in.  Finally, sets the
 * normal index in the triangle for the vertex to the generated smooth
 * normal.  If the dot product of a facet normal and the facet normal
 * associated with the first triangle in the list of triangles the
 * current vertex is in is greater than the cosine of the angle
 * parameter to the function, that facet normal is not added into the
 * average normal calculation and the corresponding vertex is given
 * the facet normal.  This tends to preserve hard edges.  The angle to
 * use depends on the model, but 90 degrees is usually a good start.
 *
 * model - initialized GLMmodel structure
 * angle - maximum angle (in degrees) to smooth across
 */
GLvoid
glmVertexNormals(GLMmodel* model, GLfloat angle);

/* glmLinearTexture: Generates texture coordinates according to a
 * linear projection of the texture map.  It generates these by
 * linearly mapping the vertices onto a square.
 *
 * model - pointer to initialized GLMmodel structure
 */
GLvoid
glmLinearTexture(GLMmodel* model);

/* glmSpheremapTexture: Generates texture coordinates according to a
 * spherical projection of the texture map.  Sometimes referred to as
 * spheremap, or reflection map texture coordinates.  It generates
 * these by using the normal to calculate where that vertex would map
 * onto a sphere.  Since it is impossible to map something flat
 * perfectly onto something spherical, there is distortion at the
 * poles.  This particular implementation causes the poles along the X
 * axis to be distorted.
 *
 * model - pointer to initialized GLMmodel structure
 */
GLvoid
glmSpheremapTexture(GLMmodel* model);

/* glmDelete: Deletes a GLMmodel structure.
 *
 * model - initialized GLMmodel structure
 */
GLvoid
glmDelete(GLMmodel* model);

/* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
 * Returns a pointer to the created object which should be free'd with
 * glmDelete().
 *
 * filename - name of the file containing the Wavefront .OBJ format data.  
 */
GLMmodel* 
glmReadOBJ(char* filename);

/* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
 * a file.
 *
 * model    - initialized GLMmodel structure
 * filename - name of the file to write the Wavefront .OBJ format data to
 * mode     - a bitwise or of values describing what is written to the file
 *            GLM_NONE    -  write only vertices
 *            GLM_FLAT    -  write facet normals
 *            GLM_SMOOTH  -  write vertex normals
 *            GLM_TEXTURE -  write texture coords
 *            GLM_FLAT and GLM_SMOOTH should not both be specified.
 */
GLvoid
glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode);

/* glmDraw: Renders the model to the current OpenGL context using the
 * mode specified.
 *
 * model    - initialized GLMmodel structure
 * mode     - a bitwise OR of values describing what is to be rendered.
 *            GLM_NONE    -  render with only vertices
 *            GLM_FLAT    -  render with facet normals
 *            GLM_SMOOTH  -  render with vertex normals
 *            GLM_TEXTURE -  render with texture coords
 *            GLM_FLAT and GLM_SMOOTH should not both be specified.
 */
GLvoid
glmDraw(GLMmodel* model, GLuint mode);

/* glmList: Generates and returns a display list for the model using
 * the mode specified.
 *
 * model    - initialized GLMmodel structure
 * mode     - a bitwise OR of values describing what is to be rendered.
 *            GLM_NONE    -  render with only vertices
 *            GLM_FLAT    -  render with facet normals
 *            GLM_SMOOTH  -  render with vertex normals
 *            GLM_TEXTURE -  render with texture coords
 *            GLM_FLAT and GLM_SMOOTH should not both be specified.  
 */
GLuint
glmList(GLMmodel* model, GLuint mode);

/* glmWeld: eliminate (weld) vectors that are within an epsilon of
 * each other.
 *
 * model      - initialized GLMmodel structure
 * epsilon    - maximum difference between vertices
 *              ( 0.00001 is a good start for a unitized model)
 *
 */
GLvoid
glmWeld(GLMmodel* model, GLfloat epsilon);

/* glmReadPPM: read a PPM raw (type P6) file.  The PPM file has a header
 * that should look something like:
 *
 *    P6
 *    # comment
 *    width height max_value
 *    rgbrgbrgb...
 *
 * where "P6" is the magic cookie which identifies the file type and
 * should be the only characters on the first line followed by a
 * carriage return.  Any line starting with a # mark will be treated
 * as a comment and discarded.   After the magic cookie, three integer
 * values are expected: width, height of the image and the maximum
 * value for a pixel (max_value must be < 256 for PPM raw files).  The
 * data section consists of width*height rgb triplets (one byte each)
 * in binary format (i.e., such as that written with fwrite() or
 * equivalent).
 *
 * The rgb data is returned as an array of unsigned chars (packed
 * rgb).  The malloc()'d memory should be free()'d by the caller.  If
 * an error occurs, an error message is sent to stderr and NULL is
 * returned.
 *
 * filename   - name of the .ppm file.
 * width      - will contain the width of the image on return.
 * height     - will contain the height of the image on return.
 *
 */
GLubyte* 
glmReadPPM(char* filename, int* width, int* height);

IMGLoader.h:

/**
	Author: Matthew Vlietstra
	FileName: IMGLoader.h
	Version: 0.1
	Purpose: 

	Description:

*/

#ifndef IMGLOADER_H
#define IMGLOADER_H

#include 
#include "GL\glut.h"
#include 
#include 
#include 

#define MAXROW 250
#define MAXCOL 200

int r,c;
typedef unsigned char pixel;
pixel* loadIMG(char fileName[], const int xMax, const int yMax);

#endif

Menu.h:

/**
	Author: Matthew Vlietstra
	FileName: Menu.h
	Version: 0.1
	Purpose: To provide various methods for displaying a menu's & other things

	Description:
	This contains an array of function pointers, & an array of text.
	Thus the porgrammer can create a new menu, then add menu items which point to other methods which input new pointers & text.
	Thefor complex menu systems can be implemented.
*/

#ifndef MENU_H
#define MENU_H

#include 
#include "gl/glut.h"

#define MAX_MENU 10

int menuStartXY[2];
float menuHW[2];
int menuCount;
int menuEffect;
int selectedMenu;
int menuFade;
void *previousMenu(void);

float colour[3];
//void backObject;

enum MENU_EFFECTS {NO_EFFECT, MENU_FADE_EFFECT, etc};

void (*menuFuncArray[MAX_MENU])(void); //Array of function Pointers
void (*menuFuncPoint)(void); //Called when the menu is printed
char menuStringTxt[MAX_MENU][20]; //2 dimensional array of characters
void printTextXY(const char text[], int x, int y, int font);
void pressEnter();
void incrementSelectedMenu();
void decreaseSelectedMenu();

void newMenu(int x, int y, float height, float width, int effect, void *func);
void newMenuSelect(int x, int y, float height, float width, int effect, void *func, int select);
void addMenuItem(const char text[], void *func);
void printMenu();
void printnl(char text[], int x, int y, int font, int spacing);

#endif

MenuItems.h:

/**
	Author: Matthew Vlietstra
	FileName: MenuItems.h
	Version: 0.1
	Purpose: To display various menu's

	Description:
	This file implements the menu. It contains the various menuItems and so forth
*/

#ifndef MENUITEMS_H
#define MENUITEMS_H

#include 
#include "gl/glut.h"
#include 
#include "Menu.h"
#include "Objects.h"
#include "Misc.h"
#include "IMGLoader.h"

void mainMenu();
void aboutCustom();
void exitIMG();
void exitCustomFunc();
void exitGame();
void physicsMenu();
void options();

int gravityIsOn;
float gravity;
float friction;
pixel *img;

int exitMenu;

float ballMass;
float racketMass;

int menuGravSelect;
int gameIsPaused;

int sideViewEnabled;

#endif

Misc.h:

/**
	Author: Matthew Vlietstra
	FileName: Misc.h
	Version: 0.1
	Purpose: 

	Description:

*/

#ifndef MISC_H
#define MISC_H

#include "MenuItems.h"
#include "gl/glut.h"
#include "Objects.h"

float RGB(int x);
float degToRad(float degrees);
void resetBall();
void pauseGame();
void resetBall();
void ballBounce();

#endif

Objects.h:

/**
	Author: Matthew Vlietstra
	FileName: Objects.h
	Version: 0.1
	Purpose: 

	Description:

*/

#ifndef OBJECTS_H
#define OBJECTS_H

#include 
#include "gl/glut.h" 
#include "glm.h"
#include "Misc.h"

typedef float vector3Df[3];
typedef float vector2Df[2];
typedef int vector2Di[2];

typedef struct
{
	GLfloat location[3];
	GLfloat oldLocation[3];
	GLfloat velocity[3];
	GLfloat mass;
	GLfloat rotation[2];
	GLfloat radius;
}obj;

obj ball;
obj racket;

typedef struct
{
	GLfloat center[3];
	GLfloat XYZ[3];
	GLfloat mass;
	GLfloat coef;
}rect3D;

rect3D net;
rect3D tableDown;
rect3D tableUp;

void drawPlane();
void drawShadows();
void drawRacketShadow();
void drawBallShadow();
void drawBall();
void drawRacket(float x, float y, float z);
void drawTable();
void drawNet();

#endif

 

Source Files:

Collision.c:

 

#include "Collision.h"

///Collision Detection between spheres. Need the absolute values.
int sphereCollisionDetection(vector3Df aLocation, float aRadius, vector3Df bLocation, float bRadius)
{
	GLfloat  a, b, c, h;
	a = aLocation[0] - bLocation[0];
	b = aLocation[1] - bLocation[1];
	c = aLocation[2] - bLocation[2];

	h = a*a + b*b + c*c; //3D pythagoras: www.odeion.org/pythagoras/pythag3d.html

	if(aRadius * bRadius >= h) //Doesn't use the square root function (More efficient)
	{
		return(1);
	}

	return(0);
}

///Determines if a box has collided with a sphere
int boxAndSpehereCollision(	obj *sphere,		//The sphere itself
							const float location[3],	//Location xyz of sphere (3D box)
							const float XYZ[3])		//Length Width Height							
{
	int index, j;
	int collided = 0;
	float	x1, x2, y1, y2, z1, z2;

	x1 = location[0]-(XYZ[0]*0.5);
	x2 = location[0]+(XYZ[0]*0.5);

	y1 = location[1]-(XYZ[1]*0.5);
	y2 = location[1]+(XYZ[1]*0.5);

	z1 = location[2]-(XYZ[2]*0.5);
	z2 = location[2]+(XYZ[2]*0.5);

	if(y1 <= sphere->location[1]+sphere->radius &&sphere->location[1]-sphere->radius <= y2)
	{
		if(x1 <= sphere->location[0]+sphere->radius && sphere->location[0]-sphere->radius <= x2)
		{
			if(z1 <= sphere->location[2]+sphere->radius && sphere->location[2]-sphere->radius <= z2)
			{
				return 1; //The Sphere did collide with the box
			}
		}
	}
	return 0; //The sphere did not collide with the box
}

//Epic Collision Detection 🙂
///Determines which direction the sphere collided with object and acts accordingly with the coef
int collision222(obj *temp, const float location[3], const float XYZ[3], float coef)
{
	int y, i;

	if(boxAndSpehereCollision(temp, location, XYZ))
	{
		y = -1;

		for(i = 0; i < 3; i++)
		{
			temp->location[i] = temp->oldLocation[i];

			if(!boxAndSpehereCollision(temp, location, XYZ) && y == -1)
			{
				y = i;
			}
		}

		temp->velocity[y] = coef*temp->velocity[y];
		return 1; //If the ball did collide
	}
	return 0; //If the ball didn't collide
}

///Calls the collision detection functions for the table tops & racket
void collisionDetection()
{
	//y Floor - down
	//const float location[] = {12.5, -0.1, 13}; //Center of Object
	//const float XYZ[] = {25, 0.4, 26};
	const float location[] = {12.5, -0.1, 13}; //Center of Object
	const float XYZ[] = {25, 0.4, 26};
	int i = 1;

	//Up
	const float location2[] = {12.5, 13, -0.1};
	const float XYZ2[] = {25.0, 26.0, 0.4};

	const float RacketBounding[] = {3.7, 2, 3};

	const float netLocation[] = {13, 2.5, 2};
	const float NetXYZ[] = {26.0, 5.0, 0.4};

	float ballOldVelocity[3];

	float tempy;
	obj lolcat;
	int y = 0;

	//testtest();
	//boxCollisionDetect(location, XYZ, 0.8, &ball, 0);	

	collision222(&ball, tableDown.center, tableDown.XYZ, tableDown.coef);
	collision222(&ball, tableUp.center, tableUp.XYZ, tableUp.coef);

	if(collision222(&ball, net.center, net.XYZ, net.coef))
	{
		printf("Net Collision!!!\n");
	}

	if(collision222(&ball, racket.location, RacketBounding, -1.0))
	{
		for(i = 0; i < 3; i++)
		{
			ballOldVelocity[i] = ball.velocity[i];
		}

		//ball.velocity[0] = ball.velocity[0]+(racket.velocity[0]*1.5);
		//ball.velocity[1] = ball.velocity[1]+10;
		//ball.velocity[2] = ball.velocity[2]+(racket.velocity[2]*1.5);

		ball.velocity[0] = ball.velocity[0]+(racket.velocity[0]*1.5);
		ball.velocity[1] = -(ball.velocity[1]+(racket.velocity[2]*1.6));
		ball.velocity[2] = ball.velocity[2]+(racket.velocity[2]);

		racket.velocity[0] = -(ballOldVelocity[0]*0.5); // Move the racket back (Prevents another collision, buggy)
		//racket.velocity[1] = -(ballOldVelocity[1]*0.5);
		racket.velocity[2] = -(ballOldVelocity[2]*0.5);

	}

	//Old Racket Collision Detection using spheres
	////boxCollisionDetect(location, XYZ, 0.8, &ball);

	//if(sphereCollisionDetection(ball.location, ball.radius, racket.location, racket.radius))
	//{
	//	if(ball.location[2] >= racket.location[2]-2)
	//	{
	//		//ball.location[2] = racket.location[2]-40;
	//		ball.velocity[0] =-ball.velocity[0]+racket.velocity[0];
	//		ball.velocity[1] =-ball.velocity[1]+20;
	//		ball.velocity[2] =-ball.velocity[2]+racket.velocity[2]-25;
	//	}
	//}

	return;
}

///Need to be updated to the new collision detection method.
void racketCollision()
{

	//const float location[] = {12.5, -0.1, 13}; //Center of Object
	//const float XYZ[] = {25, 0.4, 26};

	if (racket.location[0] <= 0.0)
	{
		racket.velocity[0] = 0;
		racket.location[0] = 0;
	}
	else if (racket.location[0] >= 25.0)
	{
		racket.velocity[0] = 0;
		racket.location[0] = 25;
	}

	if (racket.location[2] <= 20.0)
	{
		racket.velocity[2] = 0;
		racket.location[2] = 20;
	}
	else if (racket.location[2] >= 32.0)
	{
		racket.velocity[2] = 0;
		racket.location[2] = 32;
	}

	if (racket.location[1] <= 2.0)
	{
		racket.velocity[1] = 0;
		racket.location[1] = 2;
	}
	else if (racket.location[1] >= 10.0)
	{
		racket.velocity[1] = 0;
		racket.location[1] = 9.9;
	}
	return;
}

Controls.h

#include "Controls.h"

void centerMouse()
{
	glutWarpPointer((GLint)(glutGet(GLUT_WINDOW_WIDTH)/2), (GLint)(glutGet(GLUT_WINDOW_HEIGHT)/2));
}

void kbSpecialFunc(int key, int x, int y)
{
	if(gameIsPaused)
	{
		switch(key)
		{
		case GLUT_KEY_DOWN:
			incrementSelectedMenu();
			break;

		case GLUT_KEY_UP:
			decreaseSelectedMenu();
			break;
		}
	}
}

void kbUp(unsigned char key, int x, int y)
{
	keys[(int)tolower(key)] = 0;
}

void kbDown(unsigned char key, int x, int y)
{
	keys[(int)tolower(key)] = 1;
	//printf("%c ", key);
}

void mouseFunc(int button, int state, int x, int y)
{
	//printf("%d %d\n", x, y);
	if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN && exitMenu == 1)
	{
		if(x >= 250 && y >= 130 && x <= 450 && y <= 380)
			exit(0);
	}
}

void checkKeyPressed()
{

	if(gameIsPaused)
	{
		if(keys[13]) //Enter
		{
			pressEnter();
			keys[13] = 0;
		}

	}
	else
	{

		if(keys[(int)'w'])
		{
			racket.velocity[2]--;
			//racket.location[2]-=0.5;
			racket.velocity[2] =-20.5;
			//keys[(int)'w'] = 0;
		}

		if(keys[(int)'s'])
		{
			racket.velocity[2]++;
			//racket.location[2]+=0.5;
			racket.velocity[2] =20.5;
			//keys[(int)'s'] = 0;
		}

		if(keys[(int)'d'])
		{
			racket.velocity[0]++;
			//racket.location[0]+=0.5;
			//racket.velocity[0] =20.5;
			//keys[(int)'d'] = 0;
		}

		if(keys[(int)'a'])
		{
			//racket.location[0]-=0.5;
			racket.velocity[0]--;
			racket.velocity[0] =-20.5;
			//keys[(int)'a'] = 0;
		}

		if(keys[(int)' '])
		{
			racket.velocity[1]+=3;
		}

		if(keys[(int)'r'] && !keys[(int)'g'])
		{
			resetBall();
		}

		if(keys[(int)'b'])
		{
			ballBounce();
		}

		//if(keys[(int)'r'] && keys[(int)'g'])
		//{
		//	gravity =  1;
		//}

		if(keys[(int)'g'] && keys[(int)'-']) //Decrease Grav
		{

			gravity-= 0.5;
			printf("Gravity Decrease! Now: %f \n", gravity);
			///keys[(int)'g'] = 0;
			keys[(int)'-'] = 0;
		}

		if(keys[(int)'g'] && keys[(int)'=']) //Increase Grav: + show u don't have to press shift
		{
			gravity+= 0.5;
			printf("Gravity Increase! Now: %f \n", gravity);
			//keys[(int)'g'] = 0;
			keys[(int)'='] = 0;
		}

		//if(keys[(int)'g'] && keys[(int)'d']) //+ show u don't have to press shift
		//{
		//	//gravity+= 0.5;
		//	gravityIsOn = !gravityIsOn;
		//	//keys[(int)'g'] = 0;
		//	keys[(int)'d'] = 0;
		//}

		if(keys[(int)'g'] && keys[(int)'r'] && !keys[(int)'='] && !keys[(int)'-']) //Reset Grav
		{
			gravity =  1.0;
			printf("Gravity Reset! Now: %f \n", gravity);
		}

		if(keys[(int)'f'] && keys[(int)'-']) //Decrease Friction
		{

			if(friction > 0 )
			{
				friction-= 0.1;
				printf("Friction Decrease! Now: %f \n", friction);
			}
			///keys[(int)'g'] = 0;
			keys[(int)'-'] = 0;
		}

		if(keys[(int)'f'] && keys[(int)'=']) //Increase Friction: + show u don't have to press shift
		{
			friction+= 0.1;
			printf("Friction Increase! Now: %f \n", friction);
			//keys[(int)'g'] = 0;
			keys[(int)'='] = 0;
		}

		if(keys[(int)'f'] && keys[(int)'d']) //Disable/Reset Friction
		{
			if(friction != 0.0)
				friction = 0.0;
			else
				friction = 0.09;

			//keys[(int)'g'] = 0;
			keys[(int)'d'] = 0;
		}

		if(keys[27]) //Esc
		{
			pauseGame();
			mainMenu();
			keys[27] = 0;
		}
	}

}

/*	Problem with this is when the centerMouse function is called glut 
 *	recalls this function,so ill use this method until i find a better 
 *	way of doing this...
*/
void passiveMouse(int x, int y) //called when the mouse is moved
{
	static float lastTime = 0.0f, presentTime = 0.0f;
	float dt;
	GLint centerIt = 0;
	int winX = glutGet(GLUT_WINDOW_WIDTH);
	int winY = glutGet(GLUT_WINDOW_HEIGHT);

	float tmpX, tmpY;

	presentTime = glutGet(GLUT_ELAPSED_TIME);

	if(gameIsPaused)
	{
		lastTime = presentTime;
		return;
	}

	dt = 0.001*((float)(presentTime - lastTime));
	//printf("Test%f\n", 30.0/(float)winX*x);

	//tmpX = 30.0/(float)winX*x; 
	//tmpY = 20.0/(float)(winY-200)*y;	

	tmpX = 30.0/(float)winX*x; 
	tmpY = 20.0/(float)(winY-200)*y;	

	racket.location[0] = tmpX;
	racket.location[2] = tmpY;

	//racket.velocity[0] = tmpX-(tmpX*0.5);
	//racket.velocity[2] = tmpY-(tmpY*0.5);
	//racket.velocity[0] = -(15 - tmpX);
	//racket.velocity[2] = -(10 - tmpY);

	//printf("%f %f\n", , );

	//racket.location[0] = x*dt*0.5;
	//racket.location[2] = y*dt*0.5;

	lastTime = presentTime;
	return;

	//Need to fix this up...
	if((GLint)(glutGet(GLUT_WINDOW_WIDTH)/2) > x)
	{
		racket.velocity[0]-=5;
		centerIt = 1;
	} 

	if((GLint)(glutGet(GLUT_WINDOW_WIDTH)/2) < x)
	{
		racket.velocity[0]+=5;
		centerIt = 1;
	}

	if((GLint)(glutGet(GLUT_WINDOW_HEIGHT)/2) > y) //Up: 500/2 = 250 > 249
	{
		racket.velocity[2]-=4;
		centerIt = 1;
	}

	if((GLint)(glutGet(GLUT_WINDOW_HEIGHT)/2) < y)
	{
		racket.velocity[2]+=4;
		centerIt = 1;
	}

	//if(centerIt){centerMouse();} //Center the mouse if it has moved

}

///*	Problem with this is when the centerMouse function is called glut 
// *	recalls this function,so ill use this method until i find a better 
// *	way of doing this...
//*/
//void passiveMouse(int x, int y) //called when the mouse is moved
//{
//	GLint centerIt = 0;
//	
//	if(gameIsPaused)
//		return;
//
//
//	//Need to fix this up...
//	if((GLint)(glutGet(GLUT_WINDOW_WIDTH)/2) > x)
//	{
//		racket.velocity[0]-=5;
//		centerIt = 1;
//	} 
//	
//	if((GLint)(glutGet(GLUT_WINDOW_WIDTH)/2) < x)
//	{
//		racket.velocity[0]+=5;
//		centerIt = 1;
//	}
//	
//	if((GLint)(glutGet(GLUT_WINDOW_HEIGHT)/2) > y) //Up: 500/2 = 250 > 249
//	{
//		racket.velocity[2]-=4;
//		centerIt = 1;
//	}
//	
//	if((GLint)(glutGet(GLUT_WINDOW_HEIGHT)/2) < y)
//	{
//		racket.velocity[2]+=4;
//		centerIt = 1;
//	}
//	
//	if(centerIt){centerMouse();} //Center the mouse if it has moved
//	
//}
//

glm.c:

/*    
      glm.c
      Nate Robins, 1997, 2000
      nate@pobox.com, http://www.pobox.com/~nate

      Wavefront OBJ model file format reader/writer/manipulator.

      Includes routines for generating smooth normals with
      preservation of edges, welding redundant vertices & texture
      coordinate generation (spheremap and planar projections) + more.

*/

#include 
#include 
#include 
#include 
#include 
#include "glm.h"

#define T(x) (model->triangles[(x)])

/* _GLMnode: general purpose node */
typedef struct _GLMnode {
    GLuint         index;
    GLboolean      averaged;
    struct _GLMnode* next;
} GLMnode;

/* glmMax: returns the maximum of two floats */
static GLfloat
glmMax(GLfloat a, GLfloat b) 
{
    if (b > a)
        return b;
    return a;
}

/* glmAbs: returns the absolute value of a float */
static GLfloat
glmAbs(GLfloat f)
{
    if (f < 0)
        return -f;
    return f;
}

/* glmDot: compute the dot product of two vectors
 *
 * u - array of 3 GLfloats (GLfloat u[3])
 * v - array of 3 GLfloats (GLfloat v[3])
 */
static GLfloat
glmDot(GLfloat* u, GLfloat* v)
{
    assert(u); assert(v);

    return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
}

/* glmCross: compute the cross product of two vectors
 *
 * u - array of 3 GLfloats (GLfloat u[3])
 * v - array of 3 GLfloats (GLfloat v[3])
 * n - array of 3 GLfloats (GLfloat n[3]) to return the cross product in
 */
static GLvoid
glmCross(GLfloat* u, GLfloat* v, GLfloat* n)
{
    assert(u); assert(v); assert(n);

    n[0] = u[1]*v[2] - u[2]*v[1];
    n[1] = u[2]*v[0] - u[0]*v[2];
    n[2] = u[0]*v[1] - u[1]*v[0];
}

/* glmNormalize: normalize a vector
 *
 * v - array of 3 GLfloats (GLfloat v[3]) to be normalized
 */
static GLvoid
glmNormalize(GLfloat* v)
{
    GLfloat l;

    assert(v);

    l = (GLfloat)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
    v[0] /= l;
    v[1] /= l;
    v[2] /= l;
}

/* glmEqual: compares two vectors and returns GL_TRUE if they are
 * equal (within a certain threshold) or GL_FALSE if not. An epsilon
 * that works fairly well is 0.000001.
 *
 * u - array of 3 GLfloats (GLfloat u[3])
 * v - array of 3 GLfloats (GLfloat v[3]) 
 */
static GLboolean
glmEqual(GLfloat* u, GLfloat* v, GLfloat epsilon)
{
    if (glmAbs(u[0] - v[0]) < epsilon &&
        glmAbs(u[1] - v[1]) < epsilon &&
        glmAbs(u[2] - v[2]) < epsilon) 
    {
        return GL_TRUE;
    }
    return GL_FALSE;
}

/* glmWeldVectors: eliminate (weld) vectors that are within an
 * epsilon of each other.
 *
 * vectors     - array of GLfloat[3]'s to be welded
 * numvectors - number of GLfloat[3]'s in vectors
 * epsilon     - maximum difference between vectors 
 *
 */
GLfloat*
glmWeldVectors(GLfloat* vectors, GLuint* numvectors, GLfloat epsilon)
{
    GLfloat* copies;
    GLuint   copied;
    GLuint   i, j;

    copies = (GLfloat*)malloc(sizeof(GLfloat) * 3 * (*numvectors + 1));
    memcpy(copies, vectors, (sizeof(GLfloat) * 3 * (*numvectors + 1)));

    copied = 1;
    for (i = 1; i <= *numvectors; i++) {
        for (j = 1; j <= copied; j++) {
            if (glmEqual(&vectors[3 * i], &copies[3 * j], epsilon)) {
                goto duplicate;
            }
        }

        /* must not be any duplicates -- add to the copies array */
        copies[3 * copied + 0] = vectors[3 * i + 0];
        copies[3 * copied + 1] = vectors[3 * i + 1];
        copies[3 * copied + 2] = vectors[3 * i + 2];
        j = copied;             /* pass this along for below */
        copied++;

duplicate:
/* set the first component of this vector to point at the correct
        index into the new copies array */
        vectors[3 * i + 0] = (GLfloat)j;
    }

    *numvectors = copied-1;
    return copies;
}

/* glmFindGroup: Find a group in the model */
GLMgroup*
glmFindGroup(GLMmodel* model, char* name)
{
    GLMgroup* group;

    assert(model);

    group = model->groups;
    while(group) {
        if (!strcmp(name, group->name))
            break;
        group = group->next;
    }

    return group;
}

/* glmAddGroup: Add a group to the model */
GLMgroup*
glmAddGroup(GLMmodel* model, char* name)
{
    GLMgroup* group;

    group = glmFindGroup(model, name);
    if (!group) {
        group = (GLMgroup*)malloc(sizeof(GLMgroup));
        group->name = strdup(name);
        group->material = 0;
        group->numtriangles = 0;
        group->triangles = NULL;
        group->next = model->groups;
        model->groups = group;
        model->numgroups++;
    }

    return group;
}

/* glmFindGroup: Find a material in the model */
GLuint
glmFindMaterial(GLMmodel* model, char* name)
{
    GLuint i;

    /* XXX doing a linear search on a string key'd list is pretty lame,
    but it works and is fast enough for now. */
    for (i = 0; i < model->nummaterials; i++) {
        if (!strcmp(model->materials[i].name, name))
            goto found;
    }

    /* didn't find the name, so print a warning and return the default
    material (0). */
    printf("glmFindMaterial():  can't find material \"%s\".\n", name);
    i = 0;

found:
    return i;
}

/* glmDirName: return the directory given a path
 *
 * path - filesystem path
 *
 * NOTE: the return value should be free'd.
 */
static char*
glmDirName(char* path)
{
    char* dir;
    char* s;

    dir = strdup(path);

    s = strrchr(dir, '/');
    if (s)
        s[1] = '\0';
    else
        dir[0] = '\0';

    return dir;
}

/* glmReadMTL: read a wavefront material library file
 *
 * model - properly initialized GLMmodel structure
 * name  - name of the material library
 */
static GLvoid
glmReadMTL(GLMmodel* model, char* name)
{
    FILE* file;
    char* dir;
    char* filename;
    char    buf[128];
    GLuint nummaterials, i;

    dir = glmDirName(model->pathname);
    filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(name) + 1));
    strcpy(filename, dir);
    strcat(filename, name);
    free(dir);

    file = fopen(filename, "r");
    if (!file) {
        fprintf(stderr, "glmReadMTL() failed: can't open material file \"%s\".\n",
            filename);
        exit(1);
    }
    free(filename);

    /* count the number of materials in the file */
    nummaterials = 1;
    while(fscanf(file, "%s", buf) != EOF) {
        switch(buf[0]) {
        case '#':               /* comment */
            /* eat up rest of line */
            fgets(buf, sizeof(buf), file);
            break;
        case 'n':               /* newmtl */
            fgets(buf, sizeof(buf), file);
            nummaterials++;
            sscanf(buf, "%s %s", buf, buf);
            break;
        default:
            /* eat up rest of line */
            fgets(buf, sizeof(buf), file);
            break;
        }
    }

    rewind(file);

    model->materials = (GLMmaterial*)malloc(sizeof(GLMmaterial) * nummaterials);
    model->nummaterials = nummaterials;

    /* set the default material */
    for (i = 0; i < nummaterials; i++) {
        model->materials[i].name = NULL;
        model->materials[i].shininess = 65.0;
        model->materials[i].diffuse[0] = 0.8;
        model->materials[i].diffuse[1] = 0.8;
        model->materials[i].diffuse[2] = 0.8;
        model->materials[i].diffuse[3] = 1.0;
        model->materials[i].ambient[0] = 0.2;
        model->materials[i].ambient[1] = 0.2;
        model->materials[i].ambient[2] = 0.2;
        model->materials[i].ambient[3] = 1.0;
        model->materials[i].specular[0] = 0.0;
        model->materials[i].specular[1] = 0.0;
        model->materials[i].specular[2] = 0.0;
        model->materials[i].specular[3] = 1.0;
    }
    model->materials[0].name = strdup("default");

    /* now, read in the data */
    nummaterials = 0;
    while(fscanf(file, "%s", buf) != EOF) {
        switch(buf[0]) {
        case '#':               /* comment */
            /* eat up rest of line */
            fgets(buf, sizeof(buf), file);
            break;
        case 'n':               /* newmtl */
            fgets(buf, sizeof(buf), file);
            sscanf(buf, "%s %s", buf, buf);
            nummaterials++;
            model->materials[nummaterials].name = strdup(buf);
            break;
        case 'N':
            fscanf(file, "%f", &model->materials[nummaterials].shininess);
            /* wavefront shininess is from [0, 1000], so scale for OpenGL */
            model->materials[nummaterials].shininess /= 1000.0;
            model->materials[nummaterials].shininess *= 128.0;
            break;
        case 'K':
            switch(buf[1]) {
            case 'd':
                fscanf(file, "%f %f %f",
                    &model->materials[nummaterials].diffuse[0],
                    &model->materials[nummaterials].diffuse[1],
                    &model->materials[nummaterials].diffuse[2]);
                break;
            case 's':
                fscanf(file, "%f %f %f",
                    &model->materials[nummaterials].specular[0],
                    &model->materials[nummaterials].specular[1],
                    &model->materials[nummaterials].specular[2]);
                break;
            case 'a':
                fscanf(file, "%f %f %f",
                    &model->materials[nummaterials].ambient[0],
                    &model->materials[nummaterials].ambient[1],
                    &model->materials[nummaterials].ambient[2]);
                break;
            default:
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
            }
            break;
            default:
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
        }
    }
}

/* glmWriteMTL: write a wavefront material library file
 *
 * model   - properly initialized GLMmodel structure
 * modelpath  - pathname of the model being written
 * mtllibname - name of the material library to be written
 */
static GLvoid
glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname)
{
    FILE* file;
    char* dir;
    char* filename;
    GLMmaterial* material;
    GLuint i;

    dir = glmDirName(modelpath);
    filename = (char*)malloc(sizeof(char) * (strlen(dir)+strlen(mtllibname)));
    strcpy(filename, dir);
    strcat(filename, mtllibname);
    free(dir);

    /* open the file */
    file = fopen(filename, "w");
    if (!file) {
        fprintf(stderr, "glmWriteMTL() failed: can't open file \"%s\".\n",
            filename);
        exit(1);
    }
    free(filename);

    /* spit out a header */
    fprintf(file, "#  \n");
    fprintf(file, "#  Wavefront MTL generated by GLM library\n");
    fprintf(file, "#  \n");
    fprintf(file, "#  GLM library\n");
    fprintf(file, "#  Nate Robins\n");
    fprintf(file, "#  ndr@pobox.com\n");
    fprintf(file, "#  http://www.pobox.com/~ndr\n");
    fprintf(file, "#  \n\n");

    for (i = 0; i < model->nummaterials; i++) {
        material = &model->materials[i];
        fprintf(file, "newmtl %s\n", material->name);
        fprintf(file, "Ka %f %f %f\n", 
            material->ambient[0], material->ambient[1], material->ambient[2]);
        fprintf(file, "Kd %f %f %f\n", 
            material->diffuse[0], material->diffuse[1], material->diffuse[2]);
        fprintf(file, "Ks %f %f %f\n", 
            material->specular[0],material->specular[1],material->specular[2]);
        fprintf(file, "Ns %f\n", material->shininess / 128.0 * 1000.0);
        fprintf(file, "\n");
    }
}

/* glmFirstPass: first pass at a Wavefront OBJ file that gets all the
 * statistics of the model (such as #vertices, #normals, etc)
 *
 * model - properly initialized GLMmodel structure
 * file  - (fopen'd) file descriptor 
 */
static GLvoid
glmFirstPass(GLMmodel* model, FILE* file) 
{
    GLuint  numvertices;        /* number of vertices in model */
    GLuint  numnormals;         /* number of normals in model */
    GLuint  numtexcoords;       /* number of texcoords in model */
    GLuint  numtriangles;       /* number of triangles in model */
    GLMgroup* group;            /* current group */
    unsigned    v, n, t;
    char        buf[128];

    /* make a default group */
    group = glmAddGroup(model, "default");

    numvertices = numnormals = numtexcoords = numtriangles = 0;
    while(fscanf(file, "%s", buf) != EOF) {
        switch(buf[0]) {
        case '#':               /* comment */
            /* eat up rest of line */
            fgets(buf, sizeof(buf), file);
            break;
        case 'v':               /* v, vn, vt */
            switch(buf[1]) {
            case '\0':          /* vertex */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                numvertices++;
                break;
            case 'n':           /* normal */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                numnormals++;
                break;
            case 't':           /* texcoord */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                numtexcoords++;
                break;
            default:
                printf("glmFirstPass(): Unknown token \"%s\".\n", buf);
                exit(1);
                break;
            }
            break;
            case 'm':
                fgets(buf, sizeof(buf), file);
                sscanf(buf, "%s %s", buf, buf);
                model->mtllibname = strdup(buf);
                glmReadMTL(model, buf);
                break;
            case 'u':
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
            case 'g':               /* group */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
#if SINGLE_STRING_GROUP_NAMES
                sscanf(buf, "%s", buf);
#else
                buf[strlen(buf)-1] = '\0';  /* nuke '\n' */
#endif
                group = glmAddGroup(model, buf);
                break;
            case 'f':               /* face */
                v = n = t = 0;
                fscanf(file, "%s", buf);
                /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
                if (strstr(buf, "//")) {
                    /* v//n */
                    sscanf(buf, "%d//%d", &v, &n);
                    fscanf(file, "%d//%d", &v, &n);
                    fscanf(file, "%d//%d", &v, &n);
                    numtriangles++;
                    group->numtriangles++;
                    while(fscanf(file, "%d//%d", &v, &n) > 0) {
                        numtriangles++;
                        group->numtriangles++;
                    }
                } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
                    /* v/t/n */
                    fscanf(file, "%d/%d/%d", &v, &t, &n);
                    fscanf(file, "%d/%d/%d", &v, &t, &n);
                    numtriangles++;
                    group->numtriangles++;
                    while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
                        numtriangles++;
                        group->numtriangles++;
                    }
                } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
                    /* v/t */
                    fscanf(file, "%d/%d", &v, &t);
                    fscanf(file, "%d/%d", &v, &t);
                    numtriangles++;
                    group->numtriangles++;
                    while(fscanf(file, "%d/%d", &v, &t) > 0) {
                        numtriangles++;
                        group->numtriangles++;
                    }
                } else {
                    /* v */
                    fscanf(file, "%d", &v);
                    fscanf(file, "%d", &v);
                    numtriangles++;
                    group->numtriangles++;
                    while(fscanf(file, "%d", &v) > 0) {
                        numtriangles++;
                        group->numtriangles++;
                    }
                }
                break;

            default:
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
        }
  }

  /* set the stats in the model structure */
  model->numvertices  = numvertices;
  model->numnormals   = numnormals;
  model->numtexcoords = numtexcoords;
  model->numtriangles = numtriangles;

  /* allocate memory for the triangles in each group */
  group = model->groups;
  while(group) {
      group->triangles = (GLuint*)malloc(sizeof(GLuint) * group->numtriangles);
      group->numtriangles = 0;
      group = group->next;
  }
}

/* glmSecondPass: second pass at a Wavefront OBJ file that gets all
 * the data.
 *
 * model - properly initialized GLMmodel structure
 * file  - (fopen'd) file descriptor 
 */
static GLvoid
glmSecondPass(GLMmodel* model, FILE* file) 
{
    GLuint  numvertices;        /* number of vertices in model */
    GLuint  numnormals;         /* number of normals in model */
    GLuint  numtexcoords;       /* number of texcoords in model */
    GLuint  numtriangles;       /* number of triangles in model */
    GLfloat*    vertices;           /* array of vertices  */
    GLfloat*    normals;            /* array of normals */
    GLfloat*    texcoords;          /* array of texture coordinates */
    GLMgroup* group;            /* current group pointer */
    GLuint  material;           /* current material */
    GLuint  v, n, t;
    char        buf[128];

    /* set the pointer shortcuts */
    vertices       = model->vertices;
    normals    = model->normals;
    texcoords    = model->texcoords;
    group      = model->groups;

    /* on the second pass through the file, read all the data into the
    allocated arrays */
    numvertices = numnormals = numtexcoords = 1;
    numtriangles = 0;
    material = 0;
    while(fscanf(file, "%s", buf) != EOF) {
        switch(buf[0]) {
        case '#':               /* comment */
            /* eat up rest of line */
            fgets(buf, sizeof(buf), file);
            break;
        case 'v':               /* v, vn, vt */
            switch(buf[1]) {
            case '\0':          /* vertex */
                fscanf(file, "%f %f %f", 
                    &vertices[3 * numvertices + 0], 
                    &vertices[3 * numvertices + 1], 
                    &vertices[3 * numvertices + 2]);
                numvertices++;
                break;
            case 'n':           /* normal */
                fscanf(file, "%f %f %f", 
                    &normals[3 * numnormals + 0],
                    &normals[3 * numnormals + 1], 
                    &normals[3 * numnormals + 2]);
                numnormals++;
                break;
            case 't':           /* texcoord */
                fscanf(file, "%f %f", 
                    &texcoords[2 * numtexcoords + 0],
                    &texcoords[2 * numtexcoords + 1]);
                numtexcoords++;
                break;
            }
            break;
            case 'u':
                fgets(buf, sizeof(buf), file);
                sscanf(buf, "%s %s", buf, buf);
                group->material = material = glmFindMaterial(model, buf);
                break;
            case 'g':               /* group */
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
#if SINGLE_STRING_GROUP_NAMES
                sscanf(buf, "%s", buf);
#else
                buf[strlen(buf)-1] = '\0';  /* nuke '\n' */
#endif
                group = glmFindGroup(model, buf);
                group->material = material;
                break;
            case 'f':               /* face */
                v = n = t = 0;
                fscanf(file, "%s", buf);
                /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
                if (strstr(buf, "//")) {
                    /* v//n */
                    sscanf(buf, "%d//%d", &v, &n);
                    T(numtriangles).vindices[0] = v;
                    T(numtriangles).nindices[0] = n;
                    fscanf(file, "%d//%d", &v, &n);
                    T(numtriangles).vindices[1] = v;
                    T(numtriangles).nindices[1] = n;
                    fscanf(file, "%d//%d", &v, &n);
                    T(numtriangles).vindices[2] = v;
                    T(numtriangles).nindices[2] = n;
                    group->triangles[group->numtriangles++] = numtriangles;
                    numtriangles++;
                    while(fscanf(file, "%d//%d", &v, &n) > 0) {
                        T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                        T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
                        T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                        T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
                        T(numtriangles).vindices[2] = v;
                        T(numtriangles).nindices[2] = n;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                    }
                } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
                    /* v/t/n */
                    T(numtriangles).vindices[0] = v;
                    T(numtriangles).tindices[0] = t;
                    T(numtriangles).nindices[0] = n;
                    fscanf(file, "%d/%d/%d", &v, &t, &n);
                    T(numtriangles).vindices[1] = v;
                    T(numtriangles).tindices[1] = t;
                    T(numtriangles).nindices[1] = n;
                    fscanf(file, "%d/%d/%d", &v, &t, &n);
                    T(numtriangles).vindices[2] = v;
                    T(numtriangles).tindices[2] = t;
                    T(numtriangles).nindices[2] = n;
                    group->triangles[group->numtriangles++] = numtriangles;
                    numtriangles++;
                    while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
                        T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                        T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
                        T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
                        T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                        T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
                        T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
                        T(numtriangles).vindices[2] = v;
                        T(numtriangles).tindices[2] = t;
                        T(numtriangles).nindices[2] = n;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                    }
                } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
                    /* v/t */
                    T(numtriangles).vindices[0] = v;
                    T(numtriangles).tindices[0] = t;
                    fscanf(file, "%d/%d", &v, &t);
                    T(numtriangles).vindices[1] = v;
                    T(numtriangles).tindices[1] = t;
                    fscanf(file, "%d/%d", &v, &t);
                    T(numtriangles).vindices[2] = v;
                    T(numtriangles).tindices[2] = t;
                    group->triangles[group->numtriangles++] = numtriangles;
                    numtriangles++;
                    while(fscanf(file, "%d/%d", &v, &t) > 0) {
                        T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                        T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
                        T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                        T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
                        T(numtriangles).vindices[2] = v;
                        T(numtriangles).tindices[2] = t;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                    }
                } else {
                    /* v */
                    sscanf(buf, "%d", &v);
                    T(numtriangles).vindices[0] = v;
                    fscanf(file, "%d", &v);
                    T(numtriangles).vindices[1] = v;
                    fscanf(file, "%d", &v);
                    T(numtriangles).vindices[2] = v;
                    group->triangles[group->numtriangles++] = numtriangles;
                    numtriangles++;
                    while(fscanf(file, "%d", &v) > 0) {
                        T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
                        T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
                        T(numtriangles).vindices[2] = v;
                        group->triangles[group->numtriangles++] = numtriangles;
                        numtriangles++;
                    }
                }
                break;

            default:
                /* eat up rest of line */
                fgets(buf, sizeof(buf), file);
                break;
    }
  }

#if 0
  /* announce the memory requirements */
  printf(" Memory: %d bytes\n",
      numvertices  * 3*sizeof(GLfloat) +
      numnormals   * 3*sizeof(GLfloat) * (numnormals ? 1 : 0) +
      numtexcoords * 3*sizeof(GLfloat) * (numtexcoords ? 1 : 0) +
      numtriangles * sizeof(GLMtriangle));
#endif
}

/* public functions */

/* glmUnitize: "unitize" a model by translating it to the origin and
 * scaling it to fit in a unit cube around the origin.   Returns the
 * scalefactor used.
 *
 * model - properly initialized GLMmodel structure 
 */
GLfloat
glmUnitize(GLMmodel* model)
{
    GLuint  i;
    GLfloat maxx, minx, maxy, miny, maxz, minz;
    GLfloat cx, cy, cz, w, h, d;
    GLfloat scale;

    assert(model);
    assert(model->vertices);

    /* get the max/mins */
    maxx = minx = model->vertices[3 + 0];
    maxy = miny = model->vertices[3 + 1];
    maxz = minz = model->vertices[3 + 2];
    for (i = 1; i <= model->numvertices; i++) {
        if (maxx < model->vertices[3 * i + 0])
            maxx = model->vertices[3 * i + 0];
        if (minx > model->vertices[3 * i + 0])
            minx = model->vertices[3 * i + 0];

        if (maxy < model->vertices[3 * i + 1])
            maxy = model->vertices[3 * i + 1];
        if (miny > model->vertices[3 * i + 1])
            miny = model->vertices[3 * i + 1];

        if (maxz < model->vertices[3 * i + 2])
            maxz = model->vertices[3 * i + 2];
        if (minz > model->vertices[3 * i + 2])
            minz = model->vertices[3 * i + 2];
    }

    /* calculate model width, height, and depth */
    w = glmAbs(maxx) + glmAbs(minx);
    h = glmAbs(maxy) + glmAbs(miny);
    d = glmAbs(maxz) + glmAbs(minz);

    /* calculate center of the model */
    cx = (maxx + minx) / 2.0;
    cy = (maxy + miny) / 2.0;
    cz = (maxz + minz) / 2.0;

    /* calculate unitizing scale factor */
    scale = 2.0 / glmMax(glmMax(w, h), d);

    /* translate around center then scale */
    for (i = 1; i <= model->numvertices; i++) {
        model->vertices[3 * i + 0] -= cx;
        model->vertices[3 * i + 1] -= cy;
        model->vertices[3 * i + 2] -= cz;
        model->vertices[3 * i + 0] *= scale;
        model->vertices[3 * i + 1] *= scale;
        model->vertices[3 * i + 2] *= scale;
    }

    return scale;
}

/* glmDimensions: Calculates the dimensions (width, height, depth) of
 * a model.
 *
 * model   - initialized GLMmodel structure
 * dimensions - array of 3 GLfloats (GLfloat dimensions[3])
 */
GLvoid
glmDimensions(GLMmodel* model, GLfloat* dimensions)
{
    GLuint i;
    GLfloat maxx, minx, maxy, miny, maxz, minz;

    assert(model);
    assert(model->vertices);
    assert(dimensions);

    /* get the max/mins */
    maxx = minx = model->vertices[3 + 0];
    maxy = miny = model->vertices[3 + 1];
    maxz = minz = model->vertices[3 + 2];
    for (i = 1; i <= model->numvertices; i++) {
        if (maxx < model->vertices[3 * i + 0])
            maxx = model->vertices[3 * i + 0];
        if (minx > model->vertices[3 * i + 0])
            minx = model->vertices[3 * i + 0];

        if (maxy < model->vertices[3 * i + 1])
            maxy = model->vertices[3 * i + 1];
        if (miny > model->vertices[3 * i + 1])
            miny = model->vertices[3 * i + 1];

        if (maxz < model->vertices[3 * i + 2])
            maxz = model->vertices[3 * i + 2];
        if (minz > model->vertices[3 * i + 2])
            minz = model->vertices[3 * i + 2];
    }

    /* calculate model width, height, and depth */
    dimensions[0] = glmAbs(maxx) + glmAbs(minx);
    dimensions[1] = glmAbs(maxy) + glmAbs(miny);
    dimensions[2] = glmAbs(maxz) + glmAbs(minz);
}

/* glmScale: Scales a model by a given amount.
 * 
 * model - properly initialized GLMmodel structure
 * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
 */
GLvoid
glmScale(GLMmodel* model, GLfloat scale)
{
    GLuint i;

    for (i = 1; i <= model->numvertices; i++) {
        model->vertices[3 * i + 0] *= scale;
        model->vertices[3 * i + 1] *= scale;
        model->vertices[3 * i + 2] *= scale;
    }
}

/* glmReverseWinding: Reverse the polygon winding for all polygons in
 * this model.   Default winding is counter-clockwise.  Also changes
 * the direction of the normals.
 * 
 * model - properly initialized GLMmodel structure 
 */
GLvoid
glmReverseWinding(GLMmodel* model)
{
    GLuint i, swap;

    assert(model);

    for (i = 0; i < model->numtriangles; i++) {
        swap = T(i).vindices[0];
        T(i).vindices[0] = T(i).vindices[2];
        T(i).vindices[2] = swap;

        if (model->numnormals) {
            swap = T(i).nindices[0];
            T(i).nindices[0] = T(i).nindices[2];
            T(i).nindices[2] = swap;
        }

        if (model->numtexcoords) {
            swap = T(i).tindices[0];
            T(i).tindices[0] = T(i).tindices[2];
            T(i).tindices[2] = swap;
        }
    }

    /* reverse facet normals */
    for (i = 1; i <= model->numfacetnorms; i++) {
        model->facetnorms[3 * i + 0] = -model->facetnorms[3 * i + 0];
        model->facetnorms[3 * i + 1] = -model->facetnorms[3 * i + 1];
        model->facetnorms[3 * i + 2] = -model->facetnorms[3 * i + 2];
    }

    /* reverse vertex normals */
    for (i = 1; i <= model->numnormals; i++) {
        model->normals[3 * i + 0] = -model->normals[3 * i + 0];
        model->normals[3 * i + 1] = -model->normals[3 * i + 1];
        model->normals[3 * i + 2] = -model->normals[3 * i + 2];
    }
}

/* glmFacetNormals: Generates facet normals for a model (by taking the
 * cross product of the two vectors derived from the sides of each
 * triangle).  Assumes a counter-clockwise winding.
 *
 * model - initialized GLMmodel structure
 */
GLvoid
glmFacetNormals(GLMmodel* model)
{
    GLuint  i;
    GLfloat u[3];
    GLfloat v[3];

    assert(model);
    assert(model->vertices);

    /* clobber any old facetnormals */
    if (model->facetnorms)
        free(model->facetnorms);

    /* allocate memory for the new facet normals */
    model->numfacetnorms = model->numtriangles;
    model->facetnorms = (GLfloat*)malloc(sizeof(GLfloat) *
                       3 * (model->numfacetnorms + 1));

    for (i = 0; i < model->numtriangles; i++) {
        model->triangles[i].findex = i+1;

        u[0] = model->vertices[3 * T(i).vindices[1] + 0] -
            model->vertices[3 * T(i).vindices[0] + 0];
        u[1] = model->vertices[3 * T(i).vindices[1] + 1] -
            model->vertices[3 * T(i).vindices[0] + 1];
        u[2] = model->vertices[3 * T(i).vindices[1] + 2] -
            model->vertices[3 * T(i).vindices[0] + 2];

        v[0] = model->vertices[3 * T(i).vindices[2] + 0] -
            model->vertices[3 * T(i).vindices[0] + 0];
        v[1] = model->vertices[3 * T(i).vindices[2] + 1] -
            model->vertices[3 * T(i).vindices[0] + 1];
        v[2] = model->vertices[3 * T(i).vindices[2] + 2] -
            model->vertices[3 * T(i).vindices[0] + 2];

        glmCross(u, v, &model->facetnorms[3 * (i+1)]);
        glmNormalize(&model->facetnorms[3 * (i+1)]);
    }
}

/* glmVertexNormals: Generates smooth vertex normals for a model.
 * First builds a list of all the triangles each vertex is in.   Then
 * loops through each vertex in the the list averaging all the facet
 * normals of the triangles each vertex is in.   Finally, sets the
 * normal index in the triangle for the vertex to the generated smooth
 * normal.   If the dot product of a facet normal and the facet normal
 * associated with the first triangle in the list of triangles the
 * current vertex is in is greater than the cosine of the angle
 * parameter to the function, that facet normal is not added into the
 * average normal calculation and the corresponding vertex is given
 * the facet normal.  This tends to preserve hard edges.  The angle to
 * use depends on the model, but 90 degrees is usually a good start.
 *
 * model - initialized GLMmodel structure
 * angle - maximum angle (in degrees) to smooth across
 */
GLvoid
glmVertexNormals(GLMmodel* model, GLfloat angle)
{
    GLMnode*    node;
    GLMnode*    tail;
    GLMnode** members;
    GLfloat*    normals;
    GLuint  numnormals;
    GLfloat average[3];
    GLfloat dot, cos_angle;
    GLuint  i, avg;

    assert(model);
    assert(model->facetnorms);

    /* calculate the cosine of the angle (in degrees) */
    cos_angle = cos(angle * M_PI / 180.0);

    /* nuke any previous normals */
    if (model->normals)
        free(model->normals);

    /* allocate space for new normals */
    model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */
    model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));

    /* allocate a structure that will hold a linked list of triangle
    indices for each vertex */
    members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1));
    for (i = 1; i <= model->numvertices; i++)
        members[i] = NULL;

    /* for every triangle, create a node for each vertex in it */
    for (i = 0; i < model->numtriangles; i++) {
        node = (GLMnode*)malloc(sizeof(GLMnode));
        node->index = i;
        node->next  = members[T(i).vindices[0]];
        members[T(i).vindices[0]] = node;

        node = (GLMnode*)malloc(sizeof(GLMnode));
        node->index = i;
        node->next  = members[T(i).vindices[1]];
        members[T(i).vindices[1]] = node;

        node = (GLMnode*)malloc(sizeof(GLMnode));
        node->index = i;
        node->next  = members[T(i).vindices[2]];
        members[T(i).vindices[2]] = node;
    }

    /* calculate the average normal for each vertex */
    numnormals = 1;
    for (i = 1; i <= model->numvertices; i++) {
    /* calculate an average normal for this vertex by averaging the
        facet normal of every triangle this vertex is in */
        node = members[i];
        if (!node)
            fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n");
        average[0] = 0.0; average[1] = 0.0; average[2] = 0.0;
        avg = 0;
        while (node) {
        /* only average if the dot product of the angle between the two
        facet normals is greater than the cosine of the threshold
        angle -- or, said another way, the angle between the two
            facet normals is less than (or equal to) the threshold angle */
            dot = glmDot(&model->facetnorms[3 * T(node->index).findex],
                &model->facetnorms[3 * T(members[i]->index).findex]);
            if (dot > cos_angle) {
                node->averaged = GL_TRUE;
                average[0] += model->facetnorms[3 * T(node->index).findex + 0];
                average[1] += model->facetnorms[3 * T(node->index).findex + 1];
                average[2] += model->facetnorms[3 * T(node->index).findex + 2];
                avg = 1;            /* we averaged at least one normal! */
            } else {
                node->averaged = GL_FALSE;
            }
            node = node->next;
        }

        if (avg) {
            /* normalize the averaged normal */
            glmNormalize(average);

            /* add the normal to the vertex normals list */
            model->normals[3 * numnormals + 0] = average[0];
            model->normals[3 * numnormals + 1] = average[1];
            model->normals[3 * numnormals + 2] = average[2];
            avg = numnormals;
            numnormals++;
        }

        /* set the normal of this vertex in each triangle it is in */
        node = members[i];
        while (node) {
            if (node->averaged) {
                /* if this node was averaged, use the average normal */
                if (T(node->index).vindices[0] == i)
                    T(node->index).nindices[0] = avg;
                else if (T(node->index).vindices[1] == i)
                    T(node->index).nindices[1] = avg;
                else if (T(node->index).vindices[2] == i)
                    T(node->index).nindices[2] = avg;
            } else {
                /* if this node wasn't averaged, use the facet normal */
                model->normals[3 * numnormals + 0] = 
                    model->facetnorms[3 * T(node->index).findex + 0];
                model->normals[3 * numnormals + 1] = 
                    model->facetnorms[3 * T(node->index).findex + 1];
                model->normals[3 * numnormals + 2] = 
                    model->facetnorms[3 * T(node->index).findex + 2];
                if (T(node->index).vindices[0] == i)
                    T(node->index).nindices[0] = numnormals;
                else if (T(node->index).vindices[1] == i)
                    T(node->index).nindices[1] = numnormals;
                else if (T(node->index).vindices[2] == i)
                    T(node->index).nindices[2] = numnormals;
                numnormals++;
            }
            node = node->next;
        }
    }

    model->numnormals = numnormals - 1;

    /* free the member information */
    for (i = 1; i <= model->numvertices; i++) {
        node = members[i];
        while (node) {
            tail = node;
            node = node->next;
            free(tail);
        }
    }
    free(members);

    /* pack the normals array (we previously allocated the maximum
    number of normals that could possibly be created (numtriangles *
    3), so get rid of some of them (usually alot unless none of the
    facet normals were averaged)) */
    normals = model->normals;
    model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));
    for (i = 1; i <= model->numnormals; i++) {
        model->normals[3 * i + 0] = normals[3 * i + 0];
        model->normals[3 * i + 1] = normals[3 * i + 1];
        model->normals[3 * i + 2] = normals[3 * i + 2];
    }
    free(normals);
}

/* glmLinearTexture: Generates texture coordinates according to a
 * linear projection of the texture map.  It generates these by
 * linearly mapping the vertices onto a square.
 *
 * model - pointer to initialized GLMmodel structure
 */
GLvoid
glmLinearTexture(GLMmodel* model)
{
    GLMgroup *group;
    GLfloat dimensions[3];
    GLfloat x, y, scalefactor;
    GLuint i;

    assert(model);

    if (model->texcoords)
        free(model->texcoords);
    model->numtexcoords = model->numvertices;
    model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));

    glmDimensions(model, dimensions);
    scalefactor = 2.0 / 
        glmAbs(glmMax(glmMax(dimensions[0], dimensions[1]), dimensions[2]));

    /* do the calculations */
    for(i = 1; i <= model->numvertices; i++) {
        x = model->vertices[3 * i + 0] * scalefactor;
        y = model->vertices[3 * i + 2] * scalefactor;
        model->texcoords[2 * i + 0] = (x + 1.0) / 2.0;
        model->texcoords[2 * i + 1] = (y + 1.0) / 2.0;
    }

    /* go through and put texture coordinate indices in all the triangles */
    group = model->groups;
    while(group) {
        for(i = 0; i < group->numtriangles; i++) {
            T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0];
            T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1];
            T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2];
        }    
        group = group->next;
    }

#if 0
    printf("glmLinearTexture(): generated %d linear texture coordinates\n",
        model->numtexcoords);
#endif
}

/* glmSpheremapTexture: Generates texture coordinates according to a
 * spherical projection of the texture map.  Sometimes referred to as
 * spheremap, or reflection map texture coordinates.  It generates
 * these by using the normal to calculate where that vertex would map
 * onto a sphere.  Since it is impossible to map something flat
 * perfectly onto something spherical, there is distortion at the
 * poles.  This particular implementation causes the poles along the X
 * axis to be distorted.
 *
 * model - pointer to initialized GLMmodel structure
 */
GLvoid
glmSpheremapTexture(GLMmodel* model)
{
    GLMgroup* group;
    GLfloat theta, phi, rho, x, y, z, r;
    GLuint i;

    assert(model);
    assert(model->normals);

    if (model->texcoords)
        free(model->texcoords);
    model->numtexcoords = model->numnormals;
    model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));

    for (i = 1; i <= model->numnormals; i++) {
        z = model->normals[3 * i + 0];  /* re-arrange for pole distortion */
        y = model->normals[3 * i + 1];
        x = model->normals[3 * i + 2];
        r = sqrt((x * x) + (y * y));
        rho = sqrt((r * r) + (z * z));

        if(r == 0.0) {
            theta = 0.0;
            phi = 0.0;
        } else {
            if(z == 0.0)
                phi = 3.14159265 / 2.0;
            else
                phi = acos(z / rho);

            if(y == 0.0)
                theta = 3.141592365 / 2.0;
            else
                theta = asin(y / r) + (3.14159265 / 2.0);
        }

        model->texcoords[2 * i + 0] = theta / 3.14159265;
        model->texcoords[2 * i + 1] = phi / 3.14159265;
    }

    /* go through and put texcoord indices in all the triangles */
    group = model->groups;
    while(group) {
        for (i = 0; i < group->numtriangles; i++) {
            T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0];
            T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1];
            T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2];
        }
        group = group->next;
    }
}

/* glmDelete: Deletes a GLMmodel structure.
 *
 * model - initialized GLMmodel structure
 */
GLvoid
glmDelete(GLMmodel* model)
{
    GLMgroup* group;
    GLuint i;

    assert(model);

    if (model->pathname)     free(model->pathname);
    if (model->mtllibname) free(model->mtllibname);
    if (model->vertices)     free(model->vertices);
    if (model->normals)  free(model->normals);
    if (model->texcoords)  free(model->texcoords);
    if (model->facetnorms) free(model->facetnorms);
    if (model->triangles)  free(model->triangles);
    if (model->materials) {
        for (i = 0; i < model->nummaterials; i++)
            free(model->materials[i].name);
    }
    free(model->materials);
    while(model->groups) {
        group = model->groups;
        model->groups = model->groups->next;
        free(group->name);
        free(group->triangles);
        free(group);
    }

    free(model);
}

/* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
 * Returns a pointer to the created object which should be free'd with
 * glmDelete().
 *
 * filename - name of the file containing the Wavefront .OBJ format data.  
 */
GLMmodel* 
glmReadOBJ(char* filename)
{
    GLMmodel* model;
    FILE*   file;

    /* open the file */
    file = fopen(filename, "r");
    if (!file) {
        fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
            filename);
        exit(1);
    }

    /* allocate a new model */
    model = (GLMmodel*)malloc(sizeof(GLMmodel));
    model->pathname    = strdup(filename);
    model->mtllibname    = NULL;
    model->numvertices   = 0;
    model->vertices    = NULL;
    model->numnormals    = 0;
    model->normals     = NULL;
    model->numtexcoords  = 0;
    model->texcoords       = NULL;
    model->numfacetnorms = 0;
    model->facetnorms    = NULL;
    model->numtriangles  = 0;
    model->triangles       = NULL;
    model->nummaterials  = 0;
    model->materials       = NULL;
    model->numgroups       = 0;
    model->groups      = NULL;
    model->position[0]   = 0.0;
    model->position[1]   = 0.0;
    model->position[2]   = 0.0;

    /* make a first pass through the file to get a count of the number
    of vertices, normals, texcoords & triangles */
    glmFirstPass(model, file);

    /* allocate memory */
    model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
        3 * (model->numvertices + 1));
    model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
        model->numtriangles);
    if (model->numnormals) {
        model->normals = (GLfloat*)malloc(sizeof(GLfloat) *
            3 * (model->numnormals + 1));
    }
    if (model->numtexcoords) {
        model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) *
            2 * (model->numtexcoords + 1));
    }

    /* rewind to beginning of file and read in the data this pass */
    rewind(file);

    glmSecondPass(model, file);

    /* close the file */
    fclose(file);

    return model;
}

/* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
 * a file.
 *
 * model - initialized GLMmodel structure
 * filename - name of the file to write the Wavefront .OBJ format data to
 * mode  - a bitwise or of values describing what is written to the file
 *             GLM_NONE     -  render with only vertices
 *             GLM_FLAT     -  render with facet normals
 *             GLM_SMOOTH   -  render with vertex normals
 *             GLM_TEXTURE  -  render with texture coords
 *             GLM_COLOR    -  render with colors (color material)
 *             GLM_MATERIAL -  render with materials
 *             GLM_COLOR and GLM_MATERIAL should not both be specified.  
 *             GLM_FLAT and GLM_SMOOTH should not both be specified.  
 */
GLvoid
glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode)
{
    GLuint  i;
    FILE*   file;
    GLMgroup* group;

    assert(model);

    /* do a bit of warning */
    if (mode & GLM_FLAT && !model->facetnorms) {
        printf("glmWriteOBJ() warning: flat normal output requested "
            "with no facet normals defined.\n");
        mode &= ~GLM_FLAT;
    }
    if (mode & GLM_SMOOTH && !model->normals) {
        printf("glmWriteOBJ() warning: smooth normal output requested "
            "with no normals defined.\n");
        mode &= ~GLM_SMOOTH;
    }
    if (mode & GLM_TEXTURE && !model->texcoords) {
        printf("glmWriteOBJ() warning: texture coordinate output requested "
            "with no texture coordinates defined.\n");
        mode &= ~GLM_TEXTURE;
    }
    if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
        printf("glmWriteOBJ() warning: flat normal output requested "
            "and smooth normal output requested (using smooth).\n");
        mode &= ~GLM_FLAT;
    }
    if (mode & GLM_COLOR && !model->materials) {
        printf("glmWriteOBJ() warning: color output requested "
            "with no colors (materials) defined.\n");
        mode &= ~GLM_COLOR;
    }
    if (mode & GLM_MATERIAL && !model->materials) {
        printf("glmWriteOBJ() warning: material output requested "
            "with no materials defined.\n");
        mode &= ~GLM_MATERIAL;
    }
    if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
        printf("glmWriteOBJ() warning: color and material output requested "
            "outputting only materials.\n");
        mode &= ~GLM_COLOR;
    }

    /* open the file */
    file = fopen(filename, "w");
    if (!file) {
        fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
            filename);
        exit(1);
    }

    /* spit out a header */
    fprintf(file, "#  \n");
    fprintf(file, "#  Wavefront OBJ generated by GLM library\n");
    fprintf(file, "#  \n");
    fprintf(file, "#  GLM library\n");
    fprintf(file, "#  Nate Robins\n");
    fprintf(file, "#  ndr@pobox.com\n");
    fprintf(file, "#  http://www.pobox.com/~ndr\n");
    fprintf(file, "#  \n");

    if (mode & GLM_MATERIAL && model->mtllibname) {
        fprintf(file, "\nmtllib %s\n\n", model->mtllibname);
        glmWriteMTL(model, filename, model->mtllibname);
    }

    /* spit out the vertices */
    fprintf(file, "\n");
    fprintf(file, "# %d vertices\n", model->numvertices);
    for (i = 1; i <= model->numvertices; i++) {
        fprintf(file, "v %f %f %f\n", 
            model->vertices[3 * i + 0],
            model->vertices[3 * i + 1],
            model->vertices[3 * i + 2]);
    }

    /* spit out the smooth/flat normals */
    if (mode & GLM_SMOOTH) {
        fprintf(file, "\n");
        fprintf(file, "# %d normals\n", model->numnormals);
        for (i = 1; i <= model->numnormals; i++) {
            fprintf(file, "vn %f %f %f\n", 
                model->normals[3 * i + 0],
                model->normals[3 * i + 1],
                model->normals[3 * i + 2]);
        }
    } else if (mode & GLM_FLAT) {
        fprintf(file, "\n");
        fprintf(file, "# %d normals\n", model->numfacetnorms);
        for (i = 1; i <= model->numnormals; i++) {
            fprintf(file, "vn %f %f %f\n", 
                model->facetnorms[3 * i + 0],
                model->facetnorms[3 * i + 1],
                model->facetnorms[3 * i + 2]);
        }
    }

    /* spit out the texture coordinates */
    if (mode & GLM_TEXTURE) {
        fprintf(file, "\n");
        fprintf(file, "# %d texcoords\n", model->texcoords);
        for (i = 1; i <= model->numtexcoords; i++) {
            fprintf(file, "vt %f %f\n", 
                model->texcoords[2 * i + 0],
                model->texcoords[2 * i + 1]);
        }
    }

    fprintf(file, "\n");
    fprintf(file, "# %d groups\n", model->numgroups);
    fprintf(file, "# %d faces (triangles)\n", model->numtriangles);
    fprintf(file, "\n");

    group = model->groups;
    while(group) {
        fprintf(file, "g %s\n", group->name);
        if (mode & GLM_MATERIAL)
            fprintf(file, "usemtl %s\n", model->materials[group->material].name);
        for (i = 0; i < group->numtriangles; i++) {
            if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) {
                fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
                    T(group->triangles[i]).vindices[0], 
                    T(group->triangles[i]).nindices[0], 
                    T(group->triangles[i]).tindices[0],
                    T(group->triangles[i]).vindices[1],
                    T(group->triangles[i]).nindices[1],
                    T(group->triangles[i]).tindices[1],
                    T(group->triangles[i]).vindices[2],
                    T(group->triangles[i]).nindices[2],
                    T(group->triangles[i]).tindices[2]);
            } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) {
                fprintf(file, "f %d/%d %d/%d %d/%d\n",
                    T(group->triangles[i]).vindices[0],
                    T(group->triangles[i]).findex,
                    T(group->triangles[i]).vindices[1],
                    T(group->triangles[i]).findex,
                    T(group->triangles[i]).vindices[2],
                    T(group->triangles[i]).findex);
            } else if (mode & GLM_TEXTURE) {
                fprintf(file, "f %d/%d %d/%d %d/%d\n",
                    T(group->triangles[i]).vindices[0],
                    T(group->triangles[i]).tindices[0],
                    T(group->triangles[i]).vindices[1],
                    T(group->triangles[i]).tindices[1],
                    T(group->triangles[i]).vindices[2],
                    T(group->triangles[i]).tindices[2]);
            } else if (mode & GLM_SMOOTH) {
                fprintf(file, "f %d//%d %d//%d %d//%d\n",
                    T(group->triangles[i]).vindices[0],
                    T(group->triangles[i]).nindices[0],
                    T(group->triangles[i]).vindices[1],
                    T(group->triangles[i]).nindices[1],
                    T(group->triangles[i]).vindices[2], 
                    T(group->triangles[i]).nindices[2]);
            } else if (mode & GLM_FLAT) {
                fprintf(file, "f %d//%d %d//%d %d//%d\n",
                    T(group->triangles[i]).vindices[0], 
                    T(group->triangles[i]).findex,
                    T(group->triangles[i]).vindices[1],
                    T(group->triangles[i]).findex,
                    T(group->triangles[i]).vindices[2],
                    T(group->triangles[i]).findex);
            } else {
                fprintf(file, "f %d %d %d\n",
                    T(group->triangles[i]).vindices[0],
                    T(group->triangles[i]).vindices[1],
                    T(group->triangles[i]).vindices[2]);
            }
        }
        fprintf(file, "\n");
        group = group->next;
    }

    fclose(file);
}

/* glmDraw: Renders the model to the current OpenGL context using the
 * mode specified.
 *
 * model - initialized GLMmodel structure
 * mode  - a bitwise OR of values describing what is to be rendered.
 *             GLM_NONE     -  render with only vertices
 *             GLM_FLAT     -  render with facet normals
 *             GLM_SMOOTH   -  render with vertex normals
 *             GLM_TEXTURE  -  render with texture coords
 *             GLM_COLOR    -  render with colors (color material)
 *             GLM_MATERIAL -  render with materials
 *             GLM_COLOR and GLM_MATERIAL should not both be specified.  
 *             GLM_FLAT and GLM_SMOOTH should not both be specified.  
 */
GLvoid
glmDraw(GLMmodel* model, GLuint mode)
{
    static GLuint i;
    static GLMgroup* group;
    static GLMtriangle* triangle;
    static GLMmaterial* material;

    assert(model);
    assert(model->vertices);

    /* do a bit of warning */
    if (mode & GLM_FLAT && !model->facetnorms) {
        printf("glmDraw() warning: flat render mode requested "
            "with no facet normals defined.\n");
        mode &= ~GLM_FLAT;
    }
    if (mode & GLM_SMOOTH && !model->normals) {
        printf("glmDraw() warning: smooth render mode requested "
            "with no normals defined.\n");
        mode &= ~GLM_SMOOTH;
    }
    if (mode & GLM_TEXTURE && !model->texcoords) {
        printf("glmDraw() warning: texture render mode requested "
            "with no texture coordinates defined.\n");
        mode &= ~GLM_TEXTURE;
    }
    if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
        printf("glmDraw() warning: flat render mode requested "
            "and smooth render mode requested (using smooth).\n");
        mode &= ~GLM_FLAT;
    }
    if (mode & GLM_COLOR && !model->materials) {
        printf("glmDraw() warning: color render mode requested "
            "with no materials defined.\n");
        mode &= ~GLM_COLOR;
    }
    if (mode & GLM_MATERIAL && !model->materials) {
        printf("glmDraw() warning: material render mode requested "
            "with no materials defined.\n");
        mode &= ~GLM_MATERIAL;
    }
    if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
        printf("glmDraw() warning: color and material render mode requested "
            "using only material mode.\n");
        mode &= ~GLM_COLOR;
    }
    if (mode & GLM_COLOR)
        glEnable(GL_COLOR_MATERIAL);
    else if (mode & GLM_MATERIAL)
        glDisable(GL_COLOR_MATERIAL);

    /* perhaps this loop should be unrolled into material, color, flat,
       smooth, etc. loops?  since most cpu's have good branch prediction
       schemes (and these branches will always go one way), probably
       wouldn't gain too much?  */

    group = model->groups;
    while (group) {
        if (mode & GLM_MATERIAL) {
            material = &model->materials[group->material];
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material->ambient);
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material->diffuse);
            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material->specular);
            glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material->shininess);
        }

        if (mode & GLM_COLOR) {
            glColor3fv(material->diffuse);
        }

        glBegin(GL_TRIANGLES);
        for (i = 0; i < group->numtriangles; i++) {
            triangle = &T(group->triangles[i]);

            if (mode & GLM_FLAT)
                glNormal3fv(&model->facetnorms[3 * triangle->findex]);

            if (mode & GLM_SMOOTH)
                glNormal3fv(&model->normals[3 * triangle->nindices[0]]);
            if (mode & GLM_TEXTURE)
                glTexCoord2fv(&model->texcoords[2 * triangle->tindices[0]]);
            glVertex3fv(&model->vertices[3 * triangle->vindices[0]]);

            if (mode & GLM_SMOOTH)
                glNormal3fv(&model->normals[3 * triangle->nindices[1]]);
            if (mode & GLM_TEXTURE)
                glTexCoord2fv(&model->texcoords[2 * triangle->tindices[1]]);
            glVertex3fv(&model->vertices[3 * triangle->vindices[1]]);

            if (mode & GLM_SMOOTH)
                glNormal3fv(&model->normals[3 * triangle->nindices[2]]);
            if (mode & GLM_TEXTURE)
                glTexCoord2fv(&model->texcoords[2 * triangle->tindices[2]]);
            glVertex3fv(&model->vertices[3 * triangle->vindices[2]]);

        }
        glEnd();

        group = group->next;
    }
}

/* glmList: Generates and returns a display list for the model using
 * the mode specified.
 *
 * model - initialized GLMmodel structure
 * mode  - a bitwise OR of values describing what is to be rendered.
 *             GLM_NONE     -  render with only vertices
 *             GLM_FLAT     -  render with facet normals
 *             GLM_SMOOTH   -  render with vertex normals
 *             GLM_TEXTURE  -  render with texture coords
 *             GLM_COLOR    -  render with colors (color material)
 *             GLM_MATERIAL -  render with materials
 *             GLM_COLOR and GLM_MATERIAL should not both be specified.  
 * GLM_FLAT and GLM_SMOOTH should not both be specified.  
 */
GLuint
glmList(GLMmodel* model, GLuint mode)
{
    GLuint list;

    list = glGenLists(1);
    glNewList(list, GL_COMPILE);
    glmDraw(model, mode);
    glEndList();

    return list;
}

/* glmWeld: eliminate (weld) vectors that are within an epsilon of
 * each other.
 *
 * model   - initialized GLMmodel structure
 * epsilon     - maximum difference between vertices
 *               ( 0.00001 is a good start for a unitized model)
 *
 */
GLvoid
glmWeld(GLMmodel* model, GLfloat epsilon)
{
    GLfloat* vectors;
    GLfloat* copies;
    GLuint   numvectors;
    GLuint   i;

    /* vertices */
    numvectors = model->numvertices;
    vectors  = model->vertices;
    copies = glmWeldVectors(vectors, &numvectors, epsilon);

#if 0
    printf("glmWeld(): %d redundant vertices.\n", 
        model->numvertices - numvectors - 1);
#endif

    for (i = 0; i < model->numtriangles; i++) {
        T(i).vindices[0] = (GLuint)vectors[3 * T(i).vindices[0] + 0];
        T(i).vindices[1] = (GLuint)vectors[3 * T(i).vindices[1] + 0];
        T(i).vindices[2] = (GLuint)vectors[3 * T(i).vindices[2] + 0];
    }

    /* free space for old vertices */
    free(vectors);

    /* allocate space for the new vertices */
    model->numvertices = numvectors;
    model->vertices = (GLfloat*)malloc(sizeof(GLfloat) * 
        3 * (model->numvertices + 1));

    /* copy the optimized vertices into the actual vertex list */
    for (i = 1; i <= model->numvertices; i++) {
        model->vertices[3 * i + 0] = copies[3 * i + 0];
        model->vertices[3 * i + 1] = copies[3 * i + 1];
        model->vertices[3 * i + 2] = copies[3 * i + 2];
    }

    free(copies);
}

/* glmReadPPM: read a PPM raw (type P6) file.  The PPM file has a header
 * that should look something like:
 *
 *    P6
 *    # comment
 *    width height max_value
 *    rgbrgbrgb...
 *
 * where "P6" is the magic cookie which identifies the file type and
 * should be the only characters on the first line followed by a
 * carriage return.  Any line starting with a # mark will be treated
 * as a comment and discarded.   After the magic cookie, three integer
 * values are expected: width, height of the image and the maximum
 * value for a pixel (max_value must be < 256 for PPM raw files).  The
 * data section consists of width*height rgb triplets (one byte each)
 * in binary format (i.e., such as that written with fwrite() or
 * equivalent).
 *
 * The rgb data is returned as an array of unsigned chars (packed
 * rgb).  The malloc()'d memory should be free()'d by the caller.  If
 * an error occurs, an error message is sent to stderr and NULL is
 * returned.
 *
 * filename   - name of the .ppm file.
 * width      - will contain the width of the image on return.
 * height     - will contain the height of the image on return.
 *
 */
GLubyte* 
glmReadPPM(char* filename, int* width, int* height)
{
    FILE* fp;
    int i, w, h, d;
    unsigned char* image;
    char head[70];          /* max line <= 70 in PPM (per spec). */

    fp = fopen(filename, "rb");
    if (!fp) {
        perror(filename);
        return NULL;
    }

    /* grab first two chars of the file and make sure that it has the
       correct magic cookie for a raw PPM file. */
    fgets(head, 70, fp);
    if (strncmp(head, "P6", 2)) {
        fprintf(stderr, "%s: Not a raw PPM file\n", filename);
        return NULL;
    }

    /* grab the three elements in the header (width, height, maxval). */
    i = 0;
    while(i < 3) {
        fgets(head, 70, fp);
        if (head[0] == '#')     /* skip comments. */
            continue;
        if (i == 0)
            i += sscanf(head, "%d %d %d", &w, &h, &d);
        else if (i == 1)
            i += sscanf(head, "%d %d", &h, &d);
        else if (i == 2)
            i += sscanf(head, "%d", &d);
    }

    /* grab all the image data in one fell swoop. */
    image = (unsigned char*)malloc(sizeof(unsigned char)*w*h*3);
    fread(image, sizeof(unsigned char), w*h*3, fp);
    fclose(fp);

    *width = w;
    *height = h;
    return image;
}

#if 0
/* normals */
if (model->numnormals) {
    numvectors = model->numnormals;
    vectors  = model->normals;
    copies = glmOptimizeVectors(vectors, &numvectors);

    printf("glmOptimize(): %d redundant normals.\n", 
        model->numnormals - numvectors);

    for (i = 0; i < model->numtriangles; i++) {
        T(i).nindices[0] = (GLuint)vectors[3 * T(i).nindices[0] + 0];
        T(i).nindices[1] = (GLuint)vectors[3 * T(i).nindices[1] + 0];
        T(i).nindices[2] = (GLuint)vectors[3 * T(i).nindices[2] + 0];
    }

    /* free space for old normals */
    free(vectors);

    /* allocate space for the new normals */
    model->numnormals = numvectors;
    model->normals = (GLfloat*)malloc(sizeof(GLfloat) * 
        3 * (model->numnormals + 1));

    /* copy the optimized vertices into the actual vertex list */
    for (i = 1; i <= model->numnormals; i++) {
        model->normals[3 * i + 0] = copies[3 * i + 0];
        model->normals[3 * i + 1] = copies[3 * i + 1];
        model->normals[3 * i + 2] = copies[3 * i + 2];
    }

    free(copies);
}

/* texcoords */
if (model->numtexcoords) {
    numvectors = model->numtexcoords;
    vectors  = model->texcoords;
    copies = glmOptimizeVectors(vectors, &numvectors);

    printf("glmOptimize(): %d redundant texcoords.\n", 
        model->numtexcoords - numvectors);

    for (i = 0; i < model->numtriangles; i++) {
        for (j = 0; j < 3; j++) {
            T(i).tindices[j] = (GLuint)vectors[3 * T(i).tindices[j] + 0];
        }
    }

    /* free space for old texcoords */
    free(vectors);

    /* allocate space for the new texcoords */
    model->numtexcoords = numvectors;
    model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) * 
        2 * (model->numtexcoords + 1));

    /* copy the optimized vertices into the actual vertex list */
    for (i = 1; i <= model->numtexcoords; i++) {
        model->texcoords[2 * i + 0] = copies[2 * i + 0];
        model->texcoords[2 * i + 1] = copies[2 * i + 1];
    }

    free(copies);
}
#endif

#if 0
/* look for unused vertices */
/* look for unused normals */
/* look for unused texcoords */
for (i = 1; i <= model->numvertices; i++) {
    for (j = 0; j < model->numtriangles; i++) {
        if (T(j).vindices[0] == i || 
            T(j).vindices[1] == i || 
            T(j).vindices[1] == i)
            break;
    }
}
#endif

IMGLoader.c:

#include "IMGLoader.h"

///Loads a raw image (note doesn't switch it around...) and returns a pointer to it.
pixel* loadIMG(char fileName[], const int xMax, const int yMax)
{
	FILE *readIMG;
	pixel *tmpIMG;
	pixel *tmpBuff;
	int i;
	int offset;
	int x, y;
	int charin;
	tmpBuff = malloc( 3*xMax*yMax*sizeof(pixel) );

	if ((readIMG = fopen(fileName, "rb")) == NULL) /* open failed */
	{
		printf("ERROR: cannot open: %s", fileName);
		getchar();
		exit(1); /* terminate execution */
	}

	offset = 0;
	for( i = 0; i < 3; i++)
	{
		for ( y = 0; y < yMax; y++ )
		{
			for ( x = 0; x < xMax; x++)
			{
				//printf("r%d %d\n",r, c);
				if((charin=fgetc(readIMG))==EOF) /* if read failed */
				{
					printf("Error Reading file! \n");
					charin = getchar();
					exit(1); /* terminate execution */
				}
				//imageRGB[i][r]

= charin;
tmpBuff[xMax*offset + x] = charin;

}
offset++;
}
}

return(tmpBuff);
}
Main.c:

/*
Author: Matthew Vlietstra
Version: 0.1

References:

*/

#include "Objects.h"
#include 
#include "gl/glut.h"
#include 
#include 
#include 
#include 
//#include "glm.h"
//#include "assert.h"
#include "MenuItems.h"
#include "Misc.h"
#include "Controls.h"
#include "Collision.h"
#include "IMGLoader.h"

#define WINDOW_NAME "Ping Pong"

//	
void display(void);
void myReshape(int w, int h);
void camera();
void mouseFunc(int button, int state, int x, int y);
void idleFunc();
//void kbSpecialFunc();
//	

void initializeVariables()
{
	gravity = 1;
	// 
	gameIsPaused = 1;
	gravityIsOn = 1;

	img = NULL;
	exitMenu = 0;

	friction = 0.10;

	racket.location[0] = 12.5;
	racket.location[1] = 10;
	racket.location[2] = 35;
	racket.radius = 20;
	racket.mass = 0.03;

	racket.velocity[0] = 0;
	racket.velocity[1] = 0;
	racket.velocity[2] = 0;
	// 

	// 
	resetBall();
	//ball.location[0] = 12.5;
	//ball.location[1] = 20;
	//ball.location[2] = 2;

	//ball.velocity[0] = 0;
	//ball.velocity[1] = -30;
	//ball.velocity[2] = -20;

	ball.radius = 0.5;
	ball.mass = 0.01;
	// 

	// 
	net.center[0] = 13;
	net.center[1] = 2.5;
	net.center[2] = 2;

	net.XYZ[0] = 26.0;
	net.XYZ[1] = 5.0;
	net.XYZ[2] = 0.4;

	net.coef = -0.3;

	// 

	// 
	tableUp.center[0] = 12.5;
	tableUp.center[1] = 13;
	tableUp.center[2] = -0.1;

	tableUp.XYZ[0] = 25.0;
	tableUp.XYZ[1] = 26.0;
	tableUp.XYZ[2] = 0.4;

	tableUp.coef = -0.8;
	// 

	// 

	tableDown.center[0] = 12.5;
	tableDown.center[1] = -0.1;
	tableDown.center[2] = 13;

	tableDown.XYZ[0] = 70;
	tableDown.XYZ[1] = 0.4;
	tableDown.XYZ[2] = 70;

	//tableDown.XYZ[0] = 25;
	//tableDown.XYZ[1] = 0.4;
	//tableDown.XYZ[2] = 26;

	tableDown.coef = -0.8;

	// 
}

int main()
{
	//setUpScreen();
	initializeVariables();
	mainMenu(); //Setup the main menu

	//glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); //For some reason enabling double buffering messes up collision detection/the ball doesn't stop moving.... i assume it's because single buffering calls idle func less or something...
	glutInitWindowSize(640, 480);
	glutCreateWindow( WINDOW_NAME );
	glutReshapeFunc(myReshape);

	glutDisplayFunc(display);

	glClearColor(RGB(0x4A), RGB(0x77), RGB(0xD5), 0);

	glutSpecialFunc(kbSpecialFunc);
	glutKeyboardFunc(kbDown);
	glutKeyboardUpFunc(kbUp);
	glutIdleFunc(idleFunc);
	glutMouseFunc(mouseFunc);	

	pauseGame();

	glEnable(GL_DEPTH_TEST);

	glutMainLoop();

	return 0;
}

void camera()
{
		//Front View
		gluLookAt(	12.5,		10.0,		50.0, //Cam Location
					12.5,	0.0,	0.0, //LookAt
					0.0,	1.0,	0.0	);

		//Site View
		//gluLookAt(	-20.5,		1.0,		12.0, //Cam Location
		//			12.5,	0.0,	12.0, //LookAt
		//			0.0,	1.0,	0.0	);

	glutPostRedisplay();
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glViewport(0, 0, glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));

	checkKeyPressed();

	if(gameIsPaused)
	{
		printMenu();
		glFlush();
		glutSwapBuffers();
		return;
	}

	camera();
	drawBall();
	drawShadows();
	drawTable();

	drawPlane();

	glEnable(GL_LIGHTING);
	drawRacket(racket.location[0], racket.location[1], racket.location[2]);

	glDisable(GL_LIGHTING);

	//glutSetCursor(GLUT_CURSOR_NONE); //Gets rid of the cursor (it's annoying..)

	glFlush();

	glutSwapBuffers();

	return;
}

/** 
 * When the window is resized this is called...
 */
void myReshape(int width, int height)
{
   //www.nehe.gamedev.net/data/lessons/lesson.asp?lesson=01
	if (height==0)								// Prevent A Divide By Zero By
	{
		height=1;							// Making Height Equal One
	}

	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();							// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window

	if(gameIsPaused)
	{
		gluOrtho2D(0.0,768.0, 0.0,480.0);
		glutReshapeWindow(640, 480);
	}
	else
	{
		gluPerspective(45.0f,((GLfloat)width)/((GLfloat)height+40),0.01f,100.0f);
		glutReshapeWindow(768,480);
	}
	glMatrixMode(GL_MODELVIEW);						// Select The Modelview Matrix
	glLoadIdentity();
}

/** 
 * Adds graivity to y
 */
float forces(int i, int j)
{
	if(!1) //
		return(0.0);
	else if (j==1)
		return(-gravity);
	else
		return(0.0);
}

/** 
 * Adds graivity to y and frictions... needs testing......
 */
float forces222(int i, int j, float velocity)
{
	float frict = 1;
	if(velocity > 0)
	{
		frict = -friction;
	}
	else if(velocity < 0)
	{
		frict = friction;
	}
	//friction

	if(j==1) //Y, so applie gravity + friction
	{
		return((-(gravity-frict)));
	}

	return(frict);
}

/** 
 * Text book Example on particle physics (Interactive Computer Graphics A Top Down Approach, fith edition PG 574),
 * adapted to work with spheres & incorparate better collision detection
 */
void idleFunc()
{
	static float lastTime = 0.0f, presentTime = 0.0f;
	static abcd = 0;
	int i = 0, j;
	float dt;

	presentTime = glutGet(GLUT_ELAPSED_TIME);

	if(gameIsPaused)
	{
		glutPostRedisplay();
		lastTime = presentTime;
		return;
	}

	dt = 0.001*(presentTime - lastTime);
	//printf(">%d\n", dt);
	//abcd++;
	//printf("%d %f\n", abcd, dt);
	//racket.location[1]+=dt*racket.velocity[1];

	for(j=0; j<3;j++)
	{
		ball.oldLocation[j] = ball.location[j];

		ball.location[j]+=dt*ball.velocity[j];
		ball.velocity[j]+=dt*forces222(i, j, ball.velocity[j])/ball.mass;
		//ball.velocity[j]+=dt*forces222(i, j, ball.velocity[j])/ball.mass;
		//ball.velocity[j] = ball.velocity[j] * 0.95;
		//printf("%f\n",spheres[i].location[j]);

		racket.location[j]+=dt*racket.velocity[j];
		racket.velocity[j]+=dt*forces(i, j)/racket.mass;

		//racket.velocity[j] = racket.velocity[j] * 0.95; //slow down the racket
		//racket.velocity[j] = racket.velocity[j] * 0.98;
	}

	racket.velocity[0]*=0.9; 
	racket.velocity[2]*=0.9; 
	//racket.location[j]+=dt*racket.velocity[j];
	//racket.velocity[j]+=dt*forces222(i, j, racket.velocity[j])/racket.mass;

	collisionDetection();
	racketCollision();

	/*
	//x
	racket.location[0]+=dt*racket.velocity[0];
	racket.velocity[0]+=dt*0.0/racket.mass;

	racket.velocity[0] = racket.velocity[0] * 0.95; //slow down the racket(Friction)

	//y
	racket.location[2]+=dt*racket.velocity[2];
	racket.velocity[2]+=dt*0.0/racket.mass;

	racket.velocity[2] = racket.velocity[2] * 0.95; //slow down the racket(Friction)
	*/

	if(ball.velocity[2] > 0)
	{
		if(ball.location[2] > racket.location[2]-10 && ball.location[2] < racket.location[2]+10)
		{
			if(racket.location[1] < ball.location[1])
			{
				//racket.velocity[1]=ball.velocity[1];
				racket.location[1] = ball.location[1];
			}

			//printf("lol");
		}
	}

	if(ball.location[1] >= 5)
	{
		//racket.location[1] = ball.location[1];
		//if(racket.location[1] <= ball.location[1]+2.5)
		//{
			//racket.velocity[1]+=ball.location[1]-racket.location[1];
		//}
	}

	//Need to change this to the new method of collision detection
	if(ball.location[2] <= -4.1 || ball.location[2] >= 50 || ball.location[1] <= -10.1)
	{
		//ball.location[0] = 12.5;
		//ball.location[1] = 5;
		//ball.location[2] = 3;

		//ball.velocity[1] = 30;
		//ball.velocity[0] = 0;
		//ball.velocity[2] = 20;

		//ball.radius = 0.5;
		//ball.mass = 0.01;

		resetBall();
	}

	lastTime = presentTime;
	glutPostRedisplay();
}

Menu.c:

#include "Menu.h"

/**
 * Same as newMenu, except that this has an extra parameter to select a specific menu item
 * The first menu is selected by default for newMenu.
*/
void newMenuSelect(int x, int y, float height, float width, int effect, void *func, int select) 
{
	newMenu(x, y, height, width, effect, func);
	selectedMenu = select; //Select a menu
}

/**
 * Empties/Prepaires for a new menu
*/
void newMenu(int x, int y, float height, float width, int effect, void *func) //void *object
{
	menuStartXY[0] = x;
	menuStartXY[1] = y;

	menuHW[0] = height;
	menuHW[1] = width;

	menuEffect = effect;
	menuCount = 0;
	selectedMenu = 0;
	menuFade = 1;

	colour[0] = 0.0;
	colour[1] = 0.0;
	colour[2] = 0.0;

	menuFuncPoint = func;
}

///Adds a new menu item, only needs to be called once per menu item.
void addMenuItem(const char text[], void *func)
{
	if(menuCount < MAX_MENU)
	{
		//menuFuncArray[menuCount]
		menuFuncArray[menuCount] = func; //The Function to call when the menu item is clicked/selected
		strcpy(menuStringTxt[menuCount], text); //Copy the text for the menu
		menuCount++; //Increment MenuCount	
	}
}

void incrementSelectedMenu() { selectedMenu++; }
void decreaseSelectedMenu() { selectedMenu--; }

void pressEnter()
{
	//previousMenu
	(*menuFuncArray[selectedMenu])(); //Excute the specific function that corresponds to the selected text.
}

/**
 *Prints text at a specified xy location as well as accepting a specified font.
 *Note this method loops until it find the \0 characer (same method that strcpy uses)
*/
void printTextXY(const char text[], int x, int y, int font)
{
	int i;
	glRasterPos2i(x, y);
	for (i = 0; text[i] != '\0'; i++) //Somewhat dangerous 🙂
		glutBitmapCharacter(font, text[i]); //GLUT_BITMAP_HELVETICA_18
}

/**
 *This method accepts a string of text as well as a location/ spacing, 
 *it then splits the text up when it finds a \n character - so that it can print text line by line
 *Thus it allows the user to print lines of text easily.
*/
void printnl(char text[], int x, int y, int font, int spacing)
{
	int i;
	int prev = 0;
	char *pch;
	char *str = strdup(text); //Duplicate the string

	pch = strtok (str,"\n");

	while (pch != NULL)
	{
		printTextXY(pch, x, y-=spacing, font);

		pch = strtok (NULL, "\n");
	}
}

///Just a basic rectange to indicate a selected menu item.
void rectangle()
{
    glBegin(GL_QUADS);
        glVertex2f(0.0,		0.0);
        glVertex2f(0.0,		32.0);
		glVertex2f(200.0,	32.0);
		glVertex2f(200.0,	0.0);
	glEnd();	
}

///This is called in the display, it prints the menu items.
void printMenu()
{
	static float lastTime = 0.0f, presentTime = 0.0f;
	int i,j;
	float fade = 0.0;
	float tp;

	presentTime = glutGet(GLUT_ELAPSED_TIME);

	tp = (presentTime - lastTime);

	//presentTime = glutGet(GLUT_ELAPSED_TIME);

	fade = 0.0006 * tp;

	if(tp > 1000)
	{
		fade = 0.01;
		//lastTime = presentTime;
	}

	lastTime = presentTime;

	(*menuFuncPoint)(); //Excute The Function
	//(*menuFuncArray[selectedMenu])();

	if(selectedMenu >= menuCount) { selectedMenu = 0; }
	else if (selectedMenu <= -1) { selectedMenu = menuCount-1; }

	for(i = 0; i < menuCount; i++)
	{
		glColor3f(0.0, 0.0, 0.0);
		if(selectedMenu == i)
		{
			for(j = 0; j < 3; j++)
			{
				if(colour[j] >= 1.0){ menuFade = 0; } 
				else if(colour[j] <= 0.0){ menuFade = 1; }

				if(menuFade){ colour[j]+=fade; }//Need to fix this so that it depends on time...
				else{ colour[j]-=fade; }
			}

			glColor3f(colour[0], colour[1], colour[2]); //Selected menu item Fade
			printTextXY(menuStringTxt[i],  menuStartXY[0], menuStartXY[1]-(i*30), (int)GLUT_BITMAP_HELVETICA_18);	//Printing it here, prints it on top of the box (DEPTH TESTING MUST ENABLED)

			//Draw / Fade Box arround the selected menu item
			glColor3f(1.0-colour[0], 1.0-colour[1], 1.0-colour[2]);
			glPushMatrix();
				glTranslated(menuStartXY[0]-10, menuStartXY[1]-10-(i*30), 0.0);
				rectangle();
			glPopMatrix();
		}
		else
		{
			printTextXY(menuStringTxt[i],  menuStartXY[0], menuStartXY[1]-(i*30), (int)GLUT_BITMAP_HELVETICA_18);	
		}
	}
}

MenuItems.c:

#include "MenuItems.h"

void customFunction()
{

}

void aboutCustom()
{
	glColor3f(0.0, 0.0, 0.0);

	//printTextXY("Ping Pong", 200, 430, 8);
	//printTextXY("Coded by Matthew Vlietstra", 200, 400, 7);

	printnl("	Ping! Pong!, is a 3D simulator coded in C, which utilizes the API glut.\nThe purpose of this game is to simulate physics, aswell as allow the user(s) to change parameters \nthat effect the game i.e. gravity.\n \nCoded By Matthew Vlietstra (matthewvlietstra@gmail.com) \n \n \nReferences\n + OBJ Loader (glm.h & glm.c) - Nate Robins\n + particle physics (Adapted to work with spheres & added better collision detection) - Interactive\n   Computer Graphics, A top Down Approache(Fith Edition) PG 574, 1997 - 2000", 100, 430, 7, 15);
}

void exitIMG()
{

}

void exitCustomFunc()
{
	if(img == NULL)
	{
		img = loadIMG("200x250rgbR.raw", 200, 250);
	}
	glColor3f(RGB(0x00), RGB(0x00), RGB(0x00));
	printnl("Are you sure you want to exit?", 100, 430, 8, 15);

	//Image of myself, when the user left clicks on it the program exits
	glRasterPos2i( 300, 100 );
	glDrawPixels( MAXCOL, MAXROW, GL_RGB, GL_UNSIGNED_BYTE, img );

	printnl("Author: Matthew Vlietstra - matthewvlietstra@gmail.com\nLeft click on the image or select \"Yes\", then press enter to exit.", 230, 100, 7, 15);
}

void exitGame()
{
	exitMenu = 1;
	newMenu(100, 370, 30.0, 100.0, 1, exitCustomFunc);
	addMenuItem("No", mainMenu);
	addMenuItem("Yes", exit);
}

//Not The best Way of doing it.. but meh
void changeRes768x480(){glutReshapeWindow(768,480);}
void changeRes1024x640(){glutReshapeWindow(1024,640);}
void changeRes1152x720(){glutReshapeWindow(1152,720);}
void changeRes1280x800(){glutReshapeWindow(1280,800);}
void changeRes1440x900(){glutReshapeWindow(1440,900);}
void changeRes1920x1200(){glutReshapeWindow(1920,1200);}

void fullScreenMode()
{
	glutFullScreen	(	);
}

void resoultionMenu() //Need to find a better way of doing this...
{
	newMenu(100, 400, 30.0, 100.0, 1, customFunction);
	addMenuItem("768x480", changeRes768x480);
	addMenuItem("1024x640", changeRes1024x640);
	addMenuItem("1152x720", changeRes1152x720);
	addMenuItem("1280x800", changeRes1280x800);
	addMenuItem("1440x900", changeRes1440x900);
	addMenuItem("1920x1200", changeRes1920x1200);
	addMenuItem("Full Screen", fullScreenMode);
	addMenuItem("Back", mainMenu);
}

void enableDisableGrav()
{
	menuGravSelect = 3;
	gravityIsOn = !gravityIsOn;
	physicsMenu(); //Need to recall that menu to update the text
}

void enableDisableSideView()
{
	menuGravSelect = 2;
	sideViewEnabled = !sideViewEnabled;
	physicsMenu(); //Need to recall that menu to update the text
}

void increaseGrav()
{
	gravity+=0.5;
	menuGravSelect = 4;

	if(gravity > 5.0)
	{
		gravity = 1.0;
	}

	physicsMenu(); //Need to recall that menu to update the text
}

void increaseBallMass()
{
	menuGravSelect = 0;
	ballMass = ballMass + 0.01;

	if(ballMass > 3.0)
	{
		ballMass = 0.00;
	}
	physicsMenu();
}

void increaseRacketMass()
{
	menuGravSelect = 1;
	racketMass = racketMass + 0.01;

	if(racketMass > 3.0)
	{
		racketMass = 0.00;
	}
	physicsMenu();
}

void menuBack()
{

}

void physicsMenu()
{
	char gravOnOff[30];
	char grav[30];

	char sideView[30];

	//newMenu(100, 400, 30.0, 100.0, 1, customFunction);
	newMenuSelect(100, 400, 30.0, 100.0, 1, customFunction, menuGravSelect);

	sprintf(grav,"Gravity: %.2f", gravity);
	addMenuItem(grav, increaseGrav);

	//addMenuItem("Friction: ON", increaseGrav);

	addMenuItem("Back", options);
}

void controlsCustom()
{
	glColor3f(0.0, 0.0, 0.0);

	//printTextXY("Ping Pong", 200, 430, 8);
	//printTextXY("Coded by Matthew Vlietstra", 200, 400, 7);

	printnl("W - Moves the racket Forwards\nS - Moves the Racket Backwards\nD - Moves the racket to the right\nA - Moves the racket to the left\nR - Resets the ball\nB - Ball Bounce\n \nG & R - Reset Gravity\nG & R - Reset Gravity\nG & - - Decrease Gravity\nG & + - Increase Gravity\nC - Disable/Enable Collision Detection\nF & D - Disable/Reset Friction\nG & - - Decrease Friction\nG & + - Increase Friction\n \nEsc - Pauses the game/ allows the user to twink variables", 100, 430, 7, 15);
}

void controlsMenu()
{
	newMenu(20, 20, 30.0, 100.0, 1, controlsCustom);
	addMenuItem("Back", mainMenu);

}

void options()
{
	newMenu(100, 400, 30.0, 100.0, 1, customFunction);
	//addMenuItem("Resolution", resoultionMenu);
	addMenuItem("Controls", controlsMenu);
	//addMenuItem("Physics", physicsMenu);
	addMenuItem("Back", mainMenu);
}

void about()
{
	newMenu(20, 20, 30.0, 100.0, 1, aboutCustom);
	addMenuItem("Back", mainMenu);
}

void helpMenuCustom()
{
	glColor3f(RGB(0x00), RGB(0x00), RGB(0x00));
	printTextXY("You Need Help?!!!", 100, 400, 8);
	printnl("With our advanced AI, we are able to determine and help you with your predicament.\nPlease convey your predicament into your microphone, and we'll get back to you...\n \nShortly...", 100, 390, 7, 15);

}

void helpMenu()
{
	newMenu(20, 20, 30.0, 100.0, 1, helpMenuCustom);
	addMenuItem("Back", mainMenu);
}

void startGame()
{
	glEnable(GL_DEPTH_TEST);
	glClearColor(RGB(0x4A), RGB(0x77), RGB(0xD5), 0);
	gameIsPaused = 0;
	glutReshapeWindow(768,480);
}

void mainMenu()
{
	exitMenu = 0;
	newMenu(100, 400, 30.0, 100.0, 1, customFunction);
	addMenuItem("Start Simulation", startGame);
	//addMenuItem("Options", options);
	addMenuItem("Controls", controlsMenu);
	addMenuItem("About", about);
	addMenuItem("Help", helpMenu);
	addMenuItem("Exit", exitGame);
}

Misc.c:

#include "Misc.h"

float RGB(int x)
{
	float y = 0.0;
	if(x != 0)
		y = (float)x/255.0;
	return y;
}

float degToRad(float degrees)
{
	return(degrees * 3.1415926535/180); //www.teacherschoice.com.au/Maths_Library/Angles/Angles.htm
}

void pauseGame()
{
	glClearColor(RGB(0xFF), RGB(0xFF), RGB(0xFF), 0);
	gameIsPaused = 1;

	glutReshapeWindow(glutGet(GLUT_INIT_WINDOW_WIDTH)+1, glutGet(GLUT_INIT_WINDOW_HEIGHT));
}

void ballBounce()
{
	ball.location[0] = 6.5;
	ball.location[1] = 20;
	ball.location[2] = 5;

	ball.velocity[0] = 0;
	ball.velocity[1] = 1;
	ball.velocity[2] = 0;

}

void resetBall()
{
	ball.location[0] = 12.5;
	ball.location[1] = 7;
	ball.location[2] = 1;

	ball.velocity[0] = 0;
	ball.velocity[1] = 10;
	ball.velocity[2] = 35;
}

Objects.c:

#include "Objects.h"

///Draws a net...
void drawNet()
{
 	int x = 2, y = 2;
	float test;
	float startX = 0, startY = 0, startZ = 2;

	glColor3f(RGB(0xDD), RGB(0xDD), RGB(0xDD));
	//Right Side of net
	glBegin(GL_POLYGON);
		glVertex3f(startX,			startY+2,	startZ);
		glVertex3f(startX,			startY,		startZ);
		glVertex3f(startX+0.3,		startY,		startZ);
		glVertex3f(startX+0.3,		startY+2,	startZ);	
	glEnd();

	//Left Side of net
	glBegin(GL_POLYGON);
		glVertex3f(startX+25.7,			startY+2,	startZ);
		glVertex3f(startX+25.7,			startY,		startZ);
		glVertex3f(startX+26.0,		startY,		startZ);
		glVertex3f(startX+26.0,		startY+2,	startZ);	
	glEnd();

	//Top Of Net
	glBegin(GL_POLYGON);
		glVertex3f(startX,		startY+2,	startZ);
		glVertex3f(startX,		startY+2.3,		startZ);
		glVertex3f(startX+26,		startY+2.3,		startZ);
		glVertex3f(startX+26,		startY+2,	startZ);		
	glEnd();

	//Grid
	test = 0;
	for(y=0; y <= 5; y++) 
	{
		glPushMatrix();
			for(x=0; x <= 63; x++)
			{

				glTranslated(0.4,test,0);
				glBegin(GL_POLYGON); 
					glColor3f(RGB(0x7D), RGB(0x9A), RGB(0x7C));
					glVertex3f(startX,		startY-0.02,	startZ);
					glVertex3f(startX,		startY+0.2,		startZ);
					glVertex3f(startX+0.2,	startY+0.2,		startZ);
					glVertex3f(startX+0.2,	startY-0.02,	startZ);
				glEnd();
				test = 0;
			}
		glPopMatrix();
		test = 0.4*(float)y;
	}
	//glEnd();
}

///Draws the tableand then calls the net draw function...
void drawTable()
{
	//TableTopDown
 	glBegin(GL_POLYGON); 
		glColor3f(RGB(0x6D), RGB(0x8A), RGB(0x6C));
		glVertex3f(0.3,	-0.01,	0);
		glVertex3f(0.3,	-0.01,	26);
		glVertex3f(25.7,	-0.01,	26);
		glVertex3f(25.7,	-0.01,	0);
	glEnd();

	//WhiteLines
 	glBegin(GL_POLYGON); 
		glColor3f(1.0,		1.0,		1.0);
		glVertex3f(0,	-0.011,		0.0);
		glVertex3f(0,	-0.011,		26.3);
		glVertex3f(26,		-0.011,		26.3);
		glVertex3f(26,		-0.011,		0.0);
	glEnd();

	//TableTopUp
 	glBegin(GL_POLYGON); //Plane
		glColor3f(RGB(0x7A), RGB(0x7A), RGB(0x6C));
		glVertex3f(0.3,-0.01,0);
		glVertex3f(0.3,26,0);
		glVertex3f(25.7,26,0);
		glVertex3f(25.7,-0.01,0);
	glEnd();

	//WhiteLines
 	glBegin(GL_POLYGON); //Plane
		glColor3f(1.0, 1.0, 1.0);
		glVertex3f(0.0,0.0,-0.01);
		glVertex3f(0.0,26.3,-0.01);
		glVertex3f(26,26.3,-0.01);
		glVertex3f(26,0.0,-0.01);
	glEnd();

	//Net
	drawNet();
}

/** 
 * This method uses methods in the glm.c 
 * file to read the racket obj and display it.
 */
void drawRacket(float x, float y, float z)
{
	static GLMmodel* pmodel = NULL;

	glPushMatrix();
	//glRotatef(90,1.0,0.0,0.0);
	glTranslated(x,y,z);
	//glutSolidSphere(1,30,30);
	glColor3f(RGB(0xFF), RGB(0xFF), RGB(255));
    if (!pmodel) {
        pmodel = glmReadOBJ("racket3.obj");
        if (!pmodel) exit(0);
		glmUnitize(pmodel);
        glmFacetNormals(pmodel);
        glmVertexNormals(pmodel, 90.0);
		glmScale(pmodel,2);
	}
	glmDraw(pmodel, GLM_SMOOTH | GLM_MATERIAL);
	glPopMatrix();
}

///Draws the ping pong ball at the location specified by the ball struct
void drawBall()
{
	glPushMatrix();
		glTranslated(ball.location[0], ball.location[1], ball.location[2]);
		glColor3f(1.0, 1.0, 1.0);
		glutSolidSphere(ball.radius,30,30);
	glPopMatrix();
}

///Draws the ping pong balls shadow at the location specified by the ball struct
void drawBallShadow()
{
	int i;

	glPushMatrix();
	glTranslated(ball.location[0], 0.2, ball.location[2]);
	for(i = 0; i < 4; i++)
	{
		glRotated(90.0, 0.0, 1.0, 0.0);
 		glBegin(GL_POLYGON); 
				glColor3f(0.0,		0.0,		0.0);
			glVertex3f(0,		0.0,		0.0);
				glColor3f(RGB(0x6D), RGB(0x8A), RGB(0x6C));

			glVertex3f(0,		0.0,		0.5);
			glVertex3f(0.5,		0.0,		0.5);
			glVertex3f(0.5,		0.0,		0.0);
		glEnd();
	}
	glPopMatrix();
}

///Draws the rackets shadow at the location specified by the racket struct
void drawRacketShadow()
{
	int i;

	glPushMatrix();
	glTranslated(racket.location[0], 0.2, racket.location[2]);
	for(i = 0; i < 4; i++)
	{
		glRotated(180.0, 0.0, 1.0, 0.0);
 		glBegin(GL_POLYGON); 
				glColor3f(0.0,		0.0,		0.0);
			glVertex3f(0,		0.0,		0.0);
				glColor3f(RGB(0x6D), RGB(0x8A), RGB(0x6C));

			glVertex3f(0,		0.0,		0.5);
			glVertex3f(0.5,		0.0,		0.5);
			glVertex3f(2.0,		0.0,		0.0);
		glEnd();

 		glBegin(GL_POLYGON); 
				glColor3f(0.0,		0.0,		0.0);
			glVertex3f(0,		0.0,		0.0);
				glColor3f(RGB(0x6D), RGB(0x8A), RGB(0x6C));

			glVertex3f(0,		0.0,		0.5);
			glVertex3f(-0.5,		0.0,		0.5);
			glVertex3f(-2.0,		0.0,		0.0);
		glEnd();

	}
	glPopMatrix();
}

///Draws the balls and rackets shadow...
void drawShadows()
{
	drawRacketShadow();
	drawBallShadow();
}

void drawPlane()
{
	//Draw Plane
	glPushMatrix();
		glTranslated(12.5,-0.02,12.5);

 		glBegin(GL_POLYGON); 
			glColor3f(RGB(0x6D), RGB(0x8A), RGB(0x6C));
			glVertex3f(-50,	-0.02,	-50);
			glVertex3f(-50,	-0.02,	50);
			glVertex3f(50.0,	-0.02,	50);
			glVertex3f(50.0,	-0.02,	-50);
		glEnd();
	glPopMatrix();
}