
WHAT YOU SEE IN THIS FILE IS (VERY) DISJOINT PIECES OF MY CODE -
DON'T EXPECT IT TO HELP YOU TOO MUCH,
BUT YOU MAY FIND CERTAIN FUNCTIONS, OR LINES OF CODE DO HELP.

THE TEXTURE STUFF IS WORTH LOOKING AT FOR EXAMPLE.

I BROKE MY CODE INTO MULTIPLE .H FILES - WHICH I HIGHLY RECOMMEND - IF EVERYTHING
IS IN THE ONE .cpp FILE IT ALL GETS TOO HARD TO TRACK.


I INCLUDED THIS BECAUSE I HOPE IT WILL GIVE YOU SOME IDEAS - IF IT ONLY CONFUSES YOU THEN DON'T BOTHER TRYING TO MAKE SENSE OF IT. GO TO NATE ROBINS INSTEAD.  :)






/*
***********************************************************************
                 CP2060 - Computer Graphics, School of IT, JCU
                Major Project - Interactive OpenGL, C++ Program
***********************************************************************
                                        Andrew Noske
                                        Cairns
                                        24/10/2003
***********************************************************************

====
Any instructions for interactive things not assigned keys in the assignment specs:
  
	->	There are many keys and many extra features; to see a list of these
		press [F1] (which extracts several pages of information and
		a walkthrough from "information.txt").
	->	Alternatively, press [H] to see a quick keyboard reference chart.

	->	Open READ_ME.txt for more information, & a copy of the walkthrough.

====
What elements of Part B you have attempted and would like marks for:
	
	->	All of them!
	
	->	... except perhaps the first one, I have a face and my little people
		can hold/manipulate swords/records & flowers, but I don't got fingers.
	
	->	NOTE: Also, at the time I decided reading a model from a file wouldn't
		be nearly as cool as loading an animation from a file.

====
Any other noteworthy elements you want looked at:
	
	->	Music playing abilities [D].
	->	Smooth ("Catmull-Rom spline") keyframe animation
		(as well as linear and cubic interpolation).
	->	Several camera viewing modes [E] and controls.
	->	Ability to load & save person animations [D].
	->	Variable play speed (you can rewind etc).
	->	Ability to change person's size, gender, and the items he holds.
	->	Ability to make person animations by setting keyframes.
		->	NOTE: unfortunately I ran out of keys, or else I would have
			allowed the user to "modify", "delete" & "insert" keyframes
			most of the code is there, it just isn't called.
			In fact, there are quite a few methods I've written which
			never get called.  :P
	
	->	Actually - to appreciate all my features just follow the walkthrough
		(loaded from "instructions.txt") - it will be much easier that way.
	
====
Any acknowledgements for code that you have used that you did not write yourself:

	->	Code for texture loading (from a bitmap file) was modified from
		"texture.cpp" by Damiano Vitulli <info@spacesimulator.net>
	->	Code for playing mp3s came from planet source code
		<http://www.planet-source-code.com/>. media.h was written by a guy
		called Lee Trager.

***********************************************************************
                        Lindsay Ward is a great lecturer!
***********************************************************************
*/



/*//------------------------------------------------------------------------
//					|=======================|	
//					| DANCE FACTORY v1.0	|
//					|=======================|
//
//	main.cpp
//
//								by Andrew Noske...  28/8/2003
//			
//	NOTE:
//		This project is currently split into 10 files:
//			
//			o	main.cpp	--	contains main function (YOU ARE HERE)
//			
//			o	display.h	--	contains display related functions.
//			o	input.h		--	contains input related functions.
//			o	texture.h	--	contains function for loading texture. (see credits)
//			
//			
//			o	keyframe.h	--	contains keyframe class & keyframearray class (which
//								provide storage & management for an array of
//								generic keyframes).
//			o	camera.h	--	contains camera class (which is used to look at the world
//								in one of several modes and can also be animated).
//			o	person.h	--	contains person class (which is comprised of an array of
//								bodyPart objects).
//			o	bodypart.h	--	contains bodyPart class.
//			o	items.h		--  contains several methods used to draw various items
//								(including a flower, a record & several others), and
//								the Tcube class.
//			o	animations.h -- contains functions which have pre-coded keyframe animation
//								sequences which can be loaded into the camera and person object.
//			
//	IMPLEMENTED FEATURES:
//
//		o	Follows object oriented principles
//		
//		o	The "person" class contains an arrary of "bodyPart" objects, implemented as
//			a hierarchial structure - a standard left-child right-sibling tree.
//		
//		o	The certain objects animate position/angle in different ways
//			by instanciating the same generic "keyframearray" class - a class which
//			allows linear, cubic or smooth animation tweening for x,y,z positions,
//			angles or even velocity and acceleration.
//		
//		o	Entire person animations (keyframes for the person's position and angles of all
//			body parts) can be created, and then saved to/loaded from a text file.
//		
//		o	The camera can be zoomed using the mouse, and can also animate.
//	
//		o	For more information view instructions.txt
//		
//	PROPOSED FEATURES
//		o	I think I should be using a vector of keyframes (in "keyframearray")
//			is a better option than my
//			array of keyframes.
//		o	I'd like to add a proper GUI interface with buttons so the user can see
//			an click his options like any proper professional application.
//	
//	CREDITS:
//		o	I got ideas for the hierarchial model (in "person.h") from
//			"figuretr.c" by E. Angel
//
//		o	Code for texture loading from a bitmap file ("texture.cpp") was 
//			written by Damiano Vitulli <info@spacesimulator.net>, & only slighly modified.
//
//		o	Code for playing mp3s ("texture.cpp") came from planet source code
//			<http://www.planet-source-code.com/> and was written by Lee Trager.
//
//		o	I found good information about "smooth interpolation of irregularly spaced
//			keyframes" at <http://www.gamedev.net/reference/articles/article1497.asp>
//
//------------------------------------------------------------------------*/



//############################################################
//############################################################

//## INCLUDE LIBRARIES:

#include <GL/glut.h>

#include <stdlib.h>
#include <math.h>

#include <windows.h>		//for message and dialog boxes.
//#include <mmsystem.h>		//for windows media functionality.

#include <iostream>
#include <fstream>			//for file input/ouput.
#include <sstream>			//for formatting string output.
#include <string>

//#include <vector>			//might be better than array?.

using namespace std;

//**************************************************






//############################################################

//-------------
//--	Reshapes display when the user resizes the window.

void init()
{

//## PEOPLE:
	
	//## LOAD TEXTURES:
	texture[WHITE] = loadBitmap				("images/color_white.bmp");
	texture[BLACK] = loadBitmap				("images/color_black.bmp");
	texture[YELLOW] = loadBitmap			("images/color_yellow.bmp");
	texture[SILVER] = loadBitmap			("images/color_silver.bmp");
	
	texture[TEST] = loadBitmap				("images/test--.bmp");
	texture[FLOOR] = loadBitmap				("images/floor_wooden.bmp");
	texture[KEYBOARD_CHART] = loadBitmap	("images/keyboard_chart.bmp");	//NOTE: this one can take almost half a second to load!
	
	texture[LOGO] = loadBitmap				("images/logo.bmp");
	texture[LOGO_SIDES] = loadBitmap		("images/logo_sides.bmp");
	
	texture[SKIN_LIGHT] = loadBitmap				("images/skin_light.bmp");
	texture[SKIN_DARK] = loadBitmap					("images/skin_dark.bmp");
	texture[SKIN_LIGHT_FACE_MALE]  = loadBitmap		("images/skin_light_face_male.bmp");
	texture[SKIN_DARK_FACE_MALE]  = loadBitmap		("images/skin_dark_face_male.bmp");
	texture[SKIN_LIGHT_FACE_FEMALE]  = loadBitmap	("images/skin_light_face_female.bmp");
	texture[SKIN_DARK_FACE_FEMALE]  = loadBitmap	("images/skin_dark_face_female.bmp");
	
	texture[EYE]  = loadBitmap			("images/eye.bmp");
	
	texture[FABRIC_GREY] = loadBitmap	("images/fabric_grey.bmp");
	texture[FABRIC_BLACK] = loadBitmap	("images/fabric_black.bmp");
	texture[FABRIC_RED] = loadBitmap	("images/fabric_red.bmp");
	
	texture[FABRIC_BLACK_CUFF] = loadBitmap("images/fabric_black_suit_cuff.bmp");
	texture[FABRIC_BLACK_FRONT] = loadBitmap("images/fabric_black_suit_front.bmp");
	
		//NOTE: loading larger textures takes considerable time when program starts.
	
	
	//## INITIALIZE ALL PEOPLE IN MAN ARRAY:
	/*for(int i=0; i<MAX_PEOPLE; i++) {
		man[i].init();
	}*/
	
	//## SET DEFAULT POSITIONS OF FIRST FIVE PEOPLE IN A "TRIANGLE" FORMATION:
	man[0].setAbsPos(0,0,0);
	man[1].setAbsPos(10,0,-10);
	man[2].setAbsPos(-10,0,-10);
	man[3].setAbsPos(20,0,-20);
	man[4].setAbsPos(-20,0,-20);
	
	//## SET DEFAULT POSITIONS FOR OTHER PEOPLE IN ROWS:
	for(int i=5; i<MAX_PEOPLE; i++) {
		int column = (i-5) % 10;
		int row = (i-5) / 10;
		man[i].setAbsPos( (-10*column+50), 0, (-10*row-30) );
	}
		//NOTE: these extra people are used for performance testing - the
		//		real application should ony need five
	
	//## CHANGE GENDER AND SIZE OF CERTAIN PEOPLE:
	man[1].setGenderAndSize(false,1);
	man[2].setGenderAndSize(false,0.9);
	man[4].setGenderAndSize(true,0.9);
	
	
	//## CHANGE SKIN OF 3nd AND 4rd PERSON TO DARK:
	man[2].setSkinTexture(SKIN_DARK);
	man[3].setSkinTexture(SKIN_DARK);
	
	

//## OTHER OBJECTS:
	
	//## SET PROPERTIRES OF GENERAL-USE QUADRIC OBJECT:
	gluQuadricDrawStyle ( smoothQO, GLU_FILL );
	gluQuadricNormals   ( smoothQO, GLU_SMOOTH );
	gluQuadricTexture   ( smoothQO, GL_TRUE );
	
	//## SET LOGO CUBE:
	logoCube.init();
	logoCube.setTextures(LOGO, LOGO_SIDES,LOGO_SIDES,LOGO_SIDES,LOGO_SIDES,LOGO_SIDES);
	logoCube.setSideLengths(6,5,2);
	
	
//## MUSIC:
	media = new CMedia;
	sprintf(media->alias, "techno");
	media->open("media/intro.mp3");
	media->play();
	media->repeat(false);
	
	
//## INITIAL ANIMATION:
	
	//## SET KEYFRAMES FOR "FUNNY LIGHT BALL":
	kfaLight1.setCurrValues( 0,5,-5 );
	kfaLight1.pushKeyFrame( 10,10,-10, ABS, 1000 );
	kfaLight1.pushKeyFrame( -10,10,-10, ABS, 1000 );
	kfaLight1.pushKeyFrame( -10,15,-15, ABS, 1000 );
	kfaLight1.pushKeyFrame( 10,20,-20, ABS, 1000 );
	kfaLight1.pushKeyFrame( 10,30,-30, ABS, 500 );
	kfaLight1.pushKeyFrame( 30,40,-20, ABS, 500 );
	kfaLight1.setCycle( true );
	kfaLight1.calcAllFrameNums();
	
	cam.setPosCamera(0,5,20);		
	
	
	
//##LIGHTS:
	
	//## MATERIAL LIGHTING:
	GLfloat mat_specular[] = { 0.2, 0.2, 0.2, 0.9 };
	GLfloat mat_shininess[] = { 0.1 };

	glMaterialfv	(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv	(GL_FRONT, GL_SHININESS, mat_shininess);
	glMaterialfv	(GL_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv	(GL_BACK, GL_SHININESS, mat_shininess);
	
	//## LIGHT 0 (AMBIENCE LIGHT):
	//GLfloat light_pos[] = { 0, 20.0, 0, 0.0 };		//position of main light
	GLfloat light_ambient[] = { 0.3, 0.3, 0.3, 1.0 };
	GLfloat light_diffuse[]= {1.0, 1.0, 1.0, 1.0};				//white light
	
	//glLightfv		(GL_LIGHT0, GL_POSITION, light_pos);
	glLightfv		(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv		(GL_LIGHT0, GL_SPECULAR, mat_specular);
	glLightModelfv	(GL_LIGHT_MODEL_AMBIENT, light_ambient);
	glEnable		(GL_LIGHT0);
	
	//## LIGHT 1 (ANIMATED OMNILIGHT):
	//GLfloat mat1_specular1[]		= { 0.4, 0.4, 0.4, 1.0 };		//specular colour of material
	GLfloat light1_diffuse[]	= { 1.0, 1.0, 1.0, 0.2 };			//the light's diffuse colour
	glLightfv		(GL_LIGHT1, GL_POSITION, light1_pos);			//set position of light
	//glMaterialfv	(GL_FRONT, GL_SPECULAR, mat_specular1);			//set light's specular colour
	glLightfv		(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);		//set light's diffuse colour
	glEnable		(GL_LIGHT1);
	
	//## LIGHT 2 (STATIC YELLOW OMNILIGHT):
	GLfloat light2_pos[] = { 20.0, 50.0, 10, 0.0 };				//position of light
	GLfloat light2_diffuse[]  = { 1.0, 1.0, 0.5, 1.0 };				//yellow
	glLightfv		(GL_LIGHT2, GL_POSITION, light2_pos);		//set position of light
	glLightfv		(GL_LIGHT2, GL_DIFFUSE, light2_diffuse);		//set light's diffuse colour	
	//glEnable		(GL_LIGHT2);
	
	//## LIGHT 3 (STATIC YELLOW OMNILIGHT):
	GLfloat light3_pos[] = { 0.0, 0.0, -10, 0.0 };				//position of light
	GLfloat light3_diffuse[]  = { 0.2, 0.2, 0.8, 0.5 };				//bluish
	glLightfv		(GL_LIGHT3, GL_POSITION, light3_pos);		//set position of light
	glLightfv		(GL_LIGHT3, GL_DIFFUSE, light3_diffuse);		//set light's diffuse colour	
	//glEnable		(GL_LIGHT3);
	
		//NOTE: lights 2 & 3 are off by default.

//## ENVIRONMENT:
	
	//## SET ENVIRONMENT DEFAULTS:
	glEnable(GL_LIGHTING);				//enable lighting.
	glEnable(GL_DEPTH_TEST);			//testing distance from camera essential!
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_MAP2_NORMAL | GL_NORMALIZE);
	
	glEnable(GL_BLEND);									// |-- allows transparency
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	// |
	glShadeModel(GL_SMOOTH);							//set to smooth shading:
	
	glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
	
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);	//texture settings
	
	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Polygon rasterization mode (polygon filled)
	

	/*
	//## SOME INTERESTING STUFF:
	//glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
	//glEnable(GL_TEXTURE_1D);
	//glEnable ( GL_NORMALIZE );
	//glCullFace( GL_BACK );
	//glDepthFunc(GL_LEQUAL);
	//glEnable(GL_TEXTURE_GEN_S);		//FREAKIEST SHIT EVER - swimming textures!
	//glClearColor(0.0, 0.0, 0.0, 0.0);					//set "background color" as black
	//
	*/
	
	
	
//## INITIAL COLOURS:
	glClearColor(1.0, 1.0, 1.0, 1.0);					//set "background color" as white
	glColor3f(1.0, 1.0, 1.0);
	
}

//**************************************************











/*//------------------------------------------------------------------------
//					|=======================|	
//					|	DANCE FACTORY v1.0	|
//					|=======================|
//
//	display.h
//
//								by Andrew Noske...  28/8/2003
//			
//	NOTE:
//		This file represents all the display-related functions
//		incorporated into main.cpp
//
//		See main.cpp for program details.
//
///	CREDITS:
//		the frameRate() function came from
//		http://goanna.cs.rmit.edu.au/~nigels/Interactive3D/lab11/
//
//------------------------------------------------------------------------*/







/* OpenGL/GLUT code for calculation and display of frame rate.
   It needs to be called once per frame and will print to
   stdout about once per second. */

void frameRate()
{
	#define FRAME_RATE_SAMPLES 10
	#define FRAME_RATE_PRINT	1

	/* Static storage for frame-rate information                    */
	/* Note: After 2^31 milliseconds, this approach probably fails! */

	static int frameCount =  0;
	static int frameTime[FRAME_RATE_SAMPLES];
	static int frameIndex = -1;
	static int frameRef	=  0;

	int i;

	/* Initialise frame-rate array the first time through */

	if (frameIndex<0)
	{
		frameRef = frameTime[0] = glutGet(GLUT_ELAPSED_TIME);

		for (i=1; i<FRAME_RATE_SAMPLES; i++)
			frameTime[i] = frameTime[0];

		frameIndex = 0;
	}
	else
	{
		/* Update frame-rate information */
		frameCount++;
		frameTime[frameIndex] = glutGet(GLUT_ELAPSED_TIME);
		
		#ifdef FRAME_RATE_PRINT
		
		/* Print the average frame rate over the previous 
			FRAME_RATE_SAMPLES-1 frames We limit messages 
			to once per second, maximum								*/
		
		if (frameTime[frameIndex]-frameRef>1000)
		{
			frameRef = frameTime[frameIndex];
			
			fps  = (FRAME_RATE_SAMPLES-1)*1000.0/
					 (frameTime[frameIndex]-frameTime[(frameIndex+1)%FRAME_RATE_SAMPLES]);
			
			//cout << fps << endl;		//%%%%%%%%
			/*
			printf("%.1lf fps\n",(FRAME_RATE_SAMPLES-1)*1000.0/
					 (frameTime[frameIndex]-frameTime[(frameIndex+1)%FRAME_RATE_SAMPLES]));
			*/
		}
		
		#endif
		
		/* The sample buffer is circular */
		frameIndex = (frameIndex+1)%FRAME_RATE_SAMPLES;
	}
}



//############################################################

//-------------
//-- Print specifed bitmap string at specifed 3D location

void printBitmapString(float _x, float _y, float _z, float _charSpacing, string str) {
	printBitmapString( _x,_y,_z, _charSpacing, str.c_str() );
}

void printBitmapString(float _x, float _y, float _z, float _charSpacing, const char * s) {
	for (int i = 0; (*s != '\0'); i++, s++) {
		glRasterPos3f(_x + i*_charSpacing,_y, _z);
		glutBitmapCharacter(GLUT_BITMAP_8_BY_13,*s);
	}
}

//############################################################
























void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	
	frameRate();		//count frame rate.
	
	if (helpDisplay) {
		//## CHANGE CAMERA:
		gluLookAt(0.0, 0.0, 10, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
		glColor4f(1,1,1,1);				//white
		
		//## PRINT HELP SCREEN:
		if (winWidth < 500 || winHeight < 500) {
			float offsetX = -charSpacing*6;
			printBitmapString(	offsetX,	 lineSpacing,	0, 	charSpacing, "Window is   " );
			printBitmapString(	offsetX,	 0,				0,	charSpacing, "too small to" );
			printBitmapString(	offsetX,	 -lineSpacing,	0, 	charSpacing, "display help" );
		}
		else {
			printHelpText();
		}
		//printHelpText();
		glutSwapBuffers();
		return;
	}
	
	//## SET CAMERA:
	if (orthoOn) {
		//gluLookAt(0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0);		//?
		//gluLookAt(posCamera[X], posCamera[Y], posCamera[Z], 0, 0,0, 0, 1, 0);
	}
	else {
		
		switch(viewMode) {
			case(CAM_FOCUS_ON_PERSON):
				cam.setPosFocus(man[currP].pos);
				break;
			case(CAM_SHIFT_TOWARDS_PERSON):
				cam.shiftPosFocus(man[currP].pos, 0.1);
				break;
		}
		cam.setGluLookAt();
		
		//## DRAW RED CROSSHAIR AT CAMERA'S FOCUS:
		if (showCamFocus) {
			glPushMatrix();	
			
			glColor3f(1, 0, 0);			//red
			glTranslatef(cam.posFocus[X], cam.posFocus[Y], cam.posFocus[Z]);

			glBegin(GL_LINES);
				// x axis:
				glVertex3f(-2, 0, 0);
				glVertex3f(2, 0, 0);
				// y axis:
				glVertex3f(0, -2, 0);
				glVertex3f(0, 2, 0);
				// z axis:
				glVertex3f(0, 0, -2);
				glVertex3f(0, 0, 2);
			glEnd();
			
			glPopMatrix();
		}
	}
	
	//## DRAW SCENE:
	drawScene();
	
	//## DRAW PEOPLE:
	for(int i=0; i<numP; i++) {
		man[i].drawPerson();
	}

	//## DRAW TRANSPARENT CONE OVER SELECTED PERSON:
	//if(numP > 1) {
		glPushMatrix();	
		
		glTranslatef( man[currP].pos[X], man[currP].pos[Y] + 11, man[currP].pos[Z] );
		glRotatef(90,1,0,0);
		
		glBindTexture ( GL_TEXTURE_2D, texture[YELLOW] );
		glColor4f(1,1,0,0.5);				//yellow (& semi-transparent)
		
		gluCylinder ( smoothQO, 1, 0, 2, 3,1);
		
		glPopMatrix();
	//}
	
	
	//## DRAW LIGHTS:
	drawLights();
	


//** DRAW DIRECTLY IN FRONT OF CAMERA:

	cam.translateAndRotateToCameraPos();	//translate to the camera's position.
	
	
	//## DRAW KEYBOARD CHART:
	if (showKeyBoardChart) {
		glPushMatrix();
		
		glTranslatef(0,0,-5);
		
		glColor4f(1, 1, 1, 0.9);			//white
		glBindTexture(GL_TEXTURE_2D, texture[KEYBOARD_CHART]);
		
		glBegin(GL_QUADS);
			glTexCoord2f(0, 0); glVertex3f(-4, -2.5,  0);
			glTexCoord2f(1, 0); glVertex3f( 4, -2.5,  0);
			glTexCoord2f(1, 1); glVertex3f( 4,  2.5,  0);
			glTexCoord2f(0, 1); glVertex3f(-4,  2.5,  0);
		glEnd();
		
		glPopMatrix();
	}
	
	//## PRINT TEXT:
	if(winWidth > 400 && winHeight > 400 && !showKeyBoardChart) {
		
		glPushMatrix();
		
		glBindTexture(GL_TEXTURE_2D, texture[SILVER]);
		glColor4f(1,1,1,0.9);			//white (& semi-transparent)
		
		//## IF FIRST FEW SECONDS: PRINT "HELPFUL" TEXT IN CENTER:
		if(glutGet(GLUT_ELAPSED_TIME) < 10000) {		//only print for the first 5 seconds.
			glColor4f(1, 1, 1, 1);			//black
			printBitmapString(	-charSpacing*13/5, 0, -2, 	charSpacing/5, "Press [H] or [F1] for help" );
		}
		
		//## PRINT INFO TEXT AT TOP LEFT CORNER:
		float charS = charSpacing/5;
		float lineS = lineSpacing/5;
		
		float offsetX = -1.1*winAspect;
		float offsetX12In = offsetX + (12*charS);
		float offsetY = 1.4/winAspect;

		string animateOnStr = (masterAnimateOn)	? ("animate:    on")		: ("animate:    off");		
		string spinPartStr =  (spinPart)		? ("spin mode:  SPIN_PART")	: ("spin mode:  SPIN_CAMERA");
		
		string fpsStr = cDoubleToString(fps,1);
		string timeSpeedStr = cDoubleToString(timeSpeed);
		
		
		glColor4f(1, 1, 1, 1);			//black
		printBitmapString(	offsetX,		offsetY,			-2, 	charS, "view mode:" );
		printBitmapString(	offsetX12In,	offsetY,			-2, 	charS, viewModeStr[viewMode].c_str() );
		printBitmapString(	offsetX,		offsetY -1*lineS,	-2, 	charS, spinPartStr.c_str() );
		printBitmapString(	offsetX,		offsetY -2*lineS,	-2, 	charS, animateOnStr.c_str() );
	//	printBitmapString(	offsetX,		offsetY -3*lineS, -2, 	charS, "time:" );
	//	printBitmapString(	offsetX12In,	offsetY -3*lineS, -2, 	charS, timeStr.c_str() );
		printBitmapString(	offsetX,		offsetY -4*lineS, -2, 	charS, "fps:" );
		printBitmapString(	offsetX12In,	offsetY -4*lineS, -2, 	charS, fpsStr.c_str() );
		printBitmapString(	offsetX,		offsetY -5*lineS, -2, 	charS, "play rate:" );
		printBitmapString(	offsetX12In,	offsetY -5*lineS, -2, 	charS, timeSpeedStr.c_str() );
		
		glPopMatrix();
	}
	
	glutSwapBuffers();
	
	
}


//**************************************************





















































/*//------------------------------------------------------------------------
//					|=======================|	
//					| DANCE FACTORY v1.0	|
//					|=======================|
//
//	texture.h
//
//								by Andrew Noske...  14/10/2003
//			
//	NOTE:
//		This code might not be ideal, because if the width or height of
//		the bitmap (in pixels) is odd, the texture will usually comes
//		out scramled. Sometimes I have to resize the image a few times
//		before it displays perfectly.
//		
//		See main.cpp for program details.
//
//	CREDITS:
//		o	Most code in this file was written by Damiano Vitulli
//			<info@spacesimulator.net>.
//
//------------------------------------------------------------------------*/



/*
 * ---------------- www.spacesimulator.net --------------
 *   ---- Space simulators and 3d engine tutorials ----
 *
 * by Damiano Vitulli <info@spacesimulator.net>
 *
 * Tutorial 3: 3d engine - Texture mapping with OpenGL!
 * 
 * Include File: texture.h
 */



#include <stdio.h>
#include <windows.h>
#include <GL/glut.h>


/**********************************************************
 *
 * VARIABLES DECLARATION
 *
 *********************************************************/

int num_texture=-1; //Counter to keep track of the last loaded texture



/**********************************************************
 *
 * FUNCTION loadBitmap(char *)
 *
 * This function loads a bitmap file and return the OpenGL reference ID to use that texture
 *
 *********************************************************/

int loadBitmap(char *filename) 
{
    int i, j=0; //Index variables
    FILE *l_file; //File pointer
    unsigned char *l_texture; //The pointer to the memory zone in which we will load the texture
    
    // windows.h gives us these types to work with the Bitmap files
    BITMAPFILEHEADER fileheader; 
    BITMAPINFOHEADER infoheader;
    RGBTRIPLE rgb;
	
    num_texture++; // The counter of the current texture is increased
	
	//## OPEN FILE AND CHECK IF SUCCESSFUL:
    if( (l_file = fopen(filename, "rb"))==NULL) {
		cout << ">> ERROR: '" << filename << "' could not be opened" <<endl;		//%%%
		return (-1);
	}
	
	//if ( info.type != 'BM' )
	//	printf ( "This is not a bitmap\n" );
	
    fread(&fileheader, sizeof(fileheader), 1, l_file); // Read the fileheader
    
    fseek(l_file, sizeof(fileheader), SEEK_SET); // Jump the fileheader
    fread(&infoheader, sizeof(infoheader), 1, l_file); // and read the infoheader
	
    // Now we need to allocate the memory for our image (width * height * color deep)
    l_texture = (byte *) malloc(infoheader.biWidth * infoheader.biHeight * 4);
    // And fill it with zeros
    memset(l_texture, 0, infoheader.biWidth * infoheader.biHeight * 4);
	
    // At this point we can read every pixel of the image
    for (i=0; i < infoheader.biWidth*infoheader.biHeight; i++)
    {            
            // We load an RGB value from the file
            fread(&rgb, sizeof(rgb), 1, l_file); 

            // And store it
            l_texture[j+0] = rgb.rgbtRed; // Red component
            l_texture[j+1] = rgb.rgbtGreen; // Green component
            l_texture[j+2] = rgb.rgbtBlue; // Blue component
            l_texture[j+3] = 255; // Alpha value
            j += 4; // Go to the next position
    }
	
    fclose(l_file); // Closes the file stream
    
    glBindTexture(GL_TEXTURE_2D, num_texture); // Bind the ID texture specified by the 2nd parameter
	
    // The next commands sets the texture parameters
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // If the u,v coordinates overflow the range 0,1 the image is repeated
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // The magnification function ("linear" produces better results)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); //The minifying function
	
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // We don't combine the color with the original surface color, use only the texture map.
	
    // Finally we define the 2d texture
    glTexImage2D(GL_TEXTURE_2D, 0, 4, infoheader.biWidth, infoheader.biHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, l_texture);

    // And create 2d mipmaps for the minifying function
    gluBuild2DMipmaps(GL_TEXTURE_2D, 4, infoheader.biWidth, infoheader.biHeight, GL_RGBA, GL_UNSIGNED_BYTE, l_texture);

    free(l_texture); // Free the memory we used to load the texture
	
	cout << ">> Loaded texture '" << filename << "'" <<endl;		//%%%
	
    return (num_texture); // Returns the current texture OpenGL ID
}























/*//------------------------------------------------------------------------
//					|=======================|	
//					| DANCE FACTORY v1.0	|
//					|=======================|
//
//	bodypart.h
//
//								by Andrew Noske...  28/8/2003
//			
//	NOTE:
//		See main.cpp for program details.
//
//------------------------------------------------------------------------*/



//############################################################
//############################################################

//--------------
/*
typedef struct vertex
{
  GLfloat x;
  GLfloat y;
  GLfloat z;
}vertex;
*/
//--------------

//############################################################

//--------------
typedef struct treenode
{
	GLfloat m[16];
	string partLabel;
	int partID;
	struct treenode *sibling;
	struct treenode *child;
}treenode;
//--------------

//############################################################

//--------------
class bodyPart
{
public:
	
//## DATA:
	
	float height;			// |-- height and radius of part
	float radius;			// |
	
	GLfloat angle[3];		//current angles of part (relative to parent)
	GLfloat pos[3];			//position of part (relative to parent)
	
	GLfloat minAngle[3];	// |-- angle constraints
	GLfloat maxAngle[3];	// |
	bool enableConstaints;	// |
	
	keyframearray kfa;		//stores and mananges angle keyframes
	treenode node;			//node information (which parts are connected to this one)	//MERGE?%
	
	//GLUquadricObj *obj;		//used to feed glu functions
	int texture;			//co-orresponds index in global texture array
	
//## METHODS:
	
	//-------------
	//--	Default constructor
	bodyPart() {
		height = 0;
		radius = 0;
		setSizeAndAngle(0, 0, 0, 0, 0);
		setAngleConstraints( -360,360, -360,360, -360,360);
		enableConstaints = true;
		texture = 0;	//%%%%%%%% SET TO SKIN COLOR
	}
	
//## COMMON MUTATORS:
	
	//-------------
	//--	Sets size and initial angles of part
	//--
	void setSizeAndAngle(float _height, float _radius, GLfloat _angleX, GLfloat _angleY, GLfloat _angleZ) {
		setSize( _height, _radius);
		setAngles( _angleX, _angleY, _angleZ);
	}

.....
.....



















/*
CURRENT HIERARCHY:

		ROOT
		 |
		PELVIS
		 |
		STOMK -----------------	RUL ---	LUL
		 |						 |		 |
		TORSO					RLL		LLL	
		 |						 |		 |
		NECK --	RUA	---	LUA		RF		RF
		 |		 |		 |
		HEAD	RLA		LLA
				 |		 |
				RH		LH
				 |		 |
				*RHI*	*LHI*


NOTE: RHI and LHI represent an item held in the person's hand they are not
      drawn unless the person is holding something.
*/

//############################################################

//## DECLARE CONSTANTS:

const float PI = 3.14159;
const float RADS_TO_DEGS = 180/PI;
const float DEGS_TO_RADS = PI/180;

const float SLICES = 10;
const float STACKS = 6;

typedef GLfloat vertex[3];


const int NUM_ITEMS = 7;
const enum item { NOITEM, BALL, FLOWER, RECORD, SWORD, GUN, BIGGUN };
						//items which the person can hold

const int NUM_BODY_PARTS = 20;
const enum bodypart { ROOT, TORSO, STOMK, PELVIS, NECK, HEAD,	RUA,LUA, RLA,LLA, RH,LH, RUL,LUL, RLL,LLL, RF,LF,   LHI, RHI };
						//used to reference body parts in the body parts array


//############################################################

#include "items.h"
#include "bodypart.h"

//############################################################
//############################################################


class person {

public:
	
//## DATA:
	
	bodyPart part[NUM_BODY_PARTS];		//array of all body parts
	
	vertex pos;							//location of person (ROOT node) in global co-ordinates
	keyframearray kfaPos;				//stores/manages keyframes for the person's position.
	
	int c;								//represents index of currenly selected body part (selected by user)
	int textureSkin;					//indexes person's skin
	
	bool male;
	float size;
	GLfloat colour[3];
	GLfloat alpha;
	
	bool animateOn;						//set to false to disable keyframe animation
	
	bool showFadedKfs;					//show user keyframes by drawing man as partially transparent at all keyframes
	float alphaKf;						//alpha of man as seen at keyframes
	float alphaKfFalloff;				//amount by which alpha of each keyframe (away from the current keyframe) is reduced
	
	int itemLH;							//item in person's left hand
	int itemRH;							//item in person's right hand
	
	Tcube torsoCube;					//person's torso
	
	GLUquadricObj *personQobj;			//used to feed glu functions
	




//## DEFAULT CONSTRUCTOR:
	
	
	//-------------
	//--	Default constructor
	//--
	person () {
		init();
	}
	
	//-------------
	//--	Initializes all data - sets up tree, sets angles and puts back at origin
	//--
	
	void init()	{
	
	//## INITIALIZE VARIABLES:
		
		c = ROOT;
		animateOn = true;
		
		showFadedKfs = false;
		alphaKf = 0.2;
		alphaKfFalloff = 0.05;
		
		itemLH = NOITEM;
		itemRH = NOITEM;
		
		setAbsPos(0,0,0);
		
		alpha = 1;					//default to completely opaque
		textureSkin = SKIN_LIGHT;
		
		/*colour[0] = 0.0;	// |-- default color to blue (which is the default of a male anyway)
		colour[1] = 0.0;	// |
		colour[2] = 1.0;	// |
		colour[3] = 0.8;	// |*/

		
	//## CLEAR KEY FRAMES:
		clearAllKeyFrames();
		
	//## INITIALIZE BODY ARRAY:		
		//## SET SIZE AND ANGLE:
		setInitialAngles();
		setGenderAndSize( true, 1);			//defaults our person a man of size 1
											//(sets size of all body parts)
		
		//## SET ANGLE CONSTRAINTS:
		part[ROOT].setAngleConstraints(-360,360,	-360,360,	-360,360);
		part[PELVIS].setAngleConstraints(-20,20,	-20,20,		-20,20);
		part[STOMK].setAngleConstraints(-45,45,		-35,35,		-35,35);
		part[TORSO].setAngleConstraints(-10,10,		-10,10,		-10,10);
		part[NECK].setAngleConstraints(	-30,30,		-30,30,		-10,10);
		part[HEAD].setAngleConstraints(	-40,40,		-40,40,		-10,10);
		
		part[LUA].setAngleConstraints(	-20,250,	-5,5,		-90,30);
		part[RUA].setAngleConstraints(	-20,250,	-5,5,		-30,90);
		
		part[LLA].setAngleConstraints(	-160,0,		-90,90,		-20,90);
		part[RLA].setAngleConstraints(	-160,0,		-90,90,		-90,20);
		
		part[LH].setAngleConstraints(	-30,90,		-5,5,		-30,30);
		part[RH].setAngleConstraints(	-30,90,		-5,5,		-30,30);
		
		part[LUL].setAngleConstraints(	40,250,		-5,5,		-45,8);
		part[RUL].setAngleConstraints(	40,250,		-5,5,		-8,45);
		
		part[LLL].setAngleConstraints(	0,160,		-5,5,		-8,8);
		part[RLL].setAngleConstraints(	0,160,		-5,5,		-8,8);
		
		part[RF].setAngleConstraints(	-120,0,		-5,5,		-20,20);
		part[LF].setAngleConstraints(	-120,0,		-5,5,		-20,20);
		
		//!
		part[LHI].setAngleConstraints(-360,360,	-360,360,	-360,360);
		part[RHI].setAngleConstraints(-360,360,	-360,360,	-360,360);
		
		
		
	//## SET UP TREE:
		part[ROOT].setNode(				"ROOT", ROOT,			NULL, &part[PELVIS].node);
			part[PELVIS].setNode(		"PELVIS", PELVIS,		NULL, &part[STOMK].node);
				part[STOMK].setNode(	"STOMK", STOMK,			&part[RUL].node, &part[TORSO].node);

					part[TORSO].setNode(			"TORSO", TORSO,		NULL, &part[NECK].node);
						part[NECK].setNode(			"NECK", NECK,		&part[RUA].node, &part[HEAD].node);
							part[HEAD].setNode(		"HEAD", HEAD,		NULL, NULL);
						part[RUA].setNode(			"RUA", RUA,			&part[LUA].node, &part[RLA].node);
							part[RLA].setNode(		"RLA", RLA,			NULL, &part[RH].node);
								part[RH].setNode(	"RH", RH,			NULL, &part[RHI].node);
						part[LUA].setNode(			"LUA", LUA,			NULL, &part[LLA].node);
							part[LLA].setNode(		"LLA", LLA,			NULL, &part[LH].node);
								part[LH].setNode(	"LH", LH,			NULL, &part[LHI].node);

				part[RUL].setNode(			"RUL", RUL,			&part[LUL].node, &part[RLL].node);
					part[RLL].setNode(		"RLL", RLL,			NULL, &part[RF].node);
						part[RF].setNode(	"RF", RF,			NULL, NULL);
				part[LUL].setNode(			"LUL", LUL,			NULL, &part[LLL].node);
					part[LLL].setNode(		"LLL", LLL,			NULL, &part[LF].node);
						part[LF].setNode(	"LF", LF,			NULL, NULL);
		//>>>
		part[LHI].setNode(	"LHI", LHI,		NULL, NULL);
		part[RHI].setNode(	"RHI", RHI,		NULL, NULL);
		//<<<
		

	//## SET ALL RELATIVE POSITONS:
		setRelativePositions();			//already done in setGenderAndSize.
		
		
	//## ALLOCATE QUADRICS WITH FILLED DRAWING STYLE:
		
		setTextures();						//sets texture id for all body parts
											//(according to gender).
		
		personQobj = gluNewQuadric();
		gluQuadricDrawStyle(personQobj , GLU_FILL);
		gluQuadricNormals   ( personQobj, GLU_SMOOTH );
		gluQuadricTexture   ( personQobj, GL_TRUE );
		/*for( int j = 0; j<NUM_BODY_PARTS; j++ ) {
			part[j].obj = gluNewQuadric();
			gluQuadricDrawStyle(part[j].obj , GLU_FILL);
			gluQuadricNormals   ( part[j].obj, GLU_SMOOTH );
			gluQuadricTexture   ( part[j].obj, GL_TRUE );
			glColor3f(1,1,1);
			glBindTexture ( GL_TEXTURE_2D, texture[ part[j].texture] );
		}*/
		
	//## SET INIITIAL LOCAL CO-ORDINATES (MATRIX) FOR ALL BODY PARTS:

		for (int i=0; i < NUM_BODY_PARTS; i++) {
			setPartMatrix(i);
		}
		
	}
	

	//-------------
	//--	Sets appropriate dimensions for a male and a female
	
	void setAbsPos( GLfloat _x, GLfloat _y, GLfloat _z ) {
		pos[X] = _x;
		pos[Y] = _y;
		pos[Z] = _z;
	}
	

	//-------------
	//--	Simple mutators to set the person's colour, alpha
	//--	and skin texture (respectively)
	
	void setAlpha( GLfloat _alpha ) {
		alpha = _alpha;
	}
	void setColour( GLfloat _r, GLfloat _g, GLfloat _b ) {
		colour[0] = _r;
		colour[1] = _g;
		colour[2] = _b;
	}
	void setSkinTexture( texturelist _textureSkin ) {
		if(textureSkin != _textureSkin) {
			textureSkin = _textureSkin;
			setTextures();					//update textures
		}
	}
	

	//-------------
	//--	These methods cycles through/set the item the person
	//--	is holding in each hand
	
	void cycleItemLH( int _change)	{
		setItemLH(	(itemRH + _change) % NUM_ITEMS	);
	}
	void cycleItemRH( int _change)	{
		setItemRH(	(itemRH + _change) % NUM_ITEMS	);
	}

	void setItemLH( int _item ) {
		if (_item < 0 || _item >= NUM_ITEMS) {
			_item = NOITEM;
		}
		itemLH = _item;
	}
	void setItemRH( int _item ) {
		if (_item < 0 || _item >= NUM_ITEMS) {
			_item = NOITEM;
		}
		itemRH = _item;
	}
	
	//-------------
	//--	Sets appropriate dimensions/size for a male or female
	
	void setGenderAndSize( bool _male, float _size ) {
		male = _male;
		size = _size;
		
		//## SET INTITIAL SIZE:
		if (male) {
			setColour(0,0,1);	//set person colour as blue
			
			part[ROOT].setSize(		0.5, 0.5	);
			part[PELVIS].setSize(	1.2, 0.9	);
			part[STOMK].setSize(	2.0, 0.85	);
			part[TORSO].setSize(	2.0, 1.0	);
			part[NECK].setSize(		0.6, 0.4	);
			part[HEAD].setSize(		1.3, 1.0	);
			
			part[LUA].setSize(	3.0, 0.5	);
			part[RUA].setSize(	3.0, 0.5	);
			
			part[LLA].setSize(	2.0, 0.5	);
			part[RLA].setSize(	2.0, 0.5	);
			
			part[LH].setSize(	1.0, 0.3	);
			part[RH].setSize(	1.0, 0.3	);
			
			part[LUL].setSize(	3.0, 0.5	);
			part[RUL].setSize(	3.0, 0.5	);
			
			part[LLL].setSize(	2.3, 0.5	);
			part[RLL].setSize(	2.3, 0.5	);
			
			part[RF].setSize(	1.0, 0.3	);
			part[LF].setSize(	1.0, 0.3	);
		}
		else {
			setColour(1,0,0);	//set person colour t red
			
			part[ROOT].setSize(		0.5, 0.5	);
			part[PELVIS].setSize(	1.2, 0.95	);
			part[STOMK].setSize(	1.7, 0.7	);
			part[TORSO].setSize(	1.9, 0.8	);
			part[NECK].setSize(		0.6, 0.35	);
			part[HEAD].setSize(		1.3, 1.0	);
			
			part[LUA].setSize(	3.0, 0.45	);
			part[RUA].setSize(	3.0, 0.45	);
			
			part[LLA].setSize(	1.9, 0.4	);
			part[RLA].setSize(	1.9, 0.4	);
			
			part[LH].setSize(	0.8, 0.27	);
			part[RH].setSize(	0.8, 0.27	);
			
			part[LUL].setSize(	2.9, 0.4	);
			part[RUL].setSize(	2.9, 0.4	);
			
			part[LLL].setSize(	2.2, 0.35	);
			part[RLL].setSize(	2.2, 0.35	);
			
			part[RF].setSize(	0.7, 0.3	);
			part[LF].setSize(	0.7, 0.3	);
		}

		//## SET INTITIAL SIZE:
		for (int i=0; i < NUM_BODY_PARTS; i++) {
			part[i].setSize(	size*part[i].height, size*part[i].radius	);
		}
		
		setTextures();
		setRelativePositions();
	}
	//-------------
	//--	These methods change gender and size respecitively (but not the other).
	
	void setGender( bool _male ) {
		if (male != _male)
			setGenderAndSize(_male, size);
	}
	void setSize( float _size ) {
		if (size != _size)
			setGenderAndSize(male, _size);
	}








.....
.....



#include <windows.h>		//for message and dialog boxes.

//-------------
//--	Allows user to open an person animation by presenting the "open" 
//--	common dialog box. Returns true is open successful.
//--
bool openAnimationFile(/*string &_fileName*/) {
	
    OPENFILENAME file; BOOL open; char FileName[1000]="";
    memset(&file,0,sizeof(file));    
	
	//## SET PROPERTIES OF OPEN COMMON DIALOG BOX:
    file.lStructSize = sizeof(file); 
    file.Flags = OFN_HIDEREADONLY; 
    file.lpstrFile=FileName; file.nMaxFile=1000;    
    file.lpstrFilter="Text File(*.txt)\0*.txt;*.text\0C Files\0*.c;*.cpp\0";
	
	//## DISPLAY OPEN DIALOG:
    open=GetOpenFileName(&file);
	
	//## IF OPEN WAS UNSUCCESSFUL: RETURN FALSE.
	if(open==FALSE)    {
		cout << "No File Selected" <<endl;
		return 0;
	}
	//## (ELSE) IF OPEN SUCCESSFUL: LOAD ANIMATION FILE INTO CURRENT PERSON.
    else {
		printf("Open file name: %s\n\n",FileName);
		return man[currP].loadPersonAnimationFromFile(FileName);
	}
}












