//***********************************************************************//
//																		 //
//		- "Talk to me like a 3 year old!" Programming Lessons -			 //
//                                                                       //
//		$Author:		DigiBen			DigiBen@GameTutorials.com		 //
//																		 //
//		$Program:		HeightMap3										 //
//																		 //
//		$Description:	Show how to apply a detail texture to a terrain  //
//																		 //
//		$Date:			5/28/02											 //
//																		 //
//***********************************************************************//

#pragma comment(lib, "winmm.lib")

#include "main.h"
#include "Camera.h"



// We increased the speed a bit from the Camera Strafing Tutorial
// This is how fast our camera moves
#define kSpeed	50.0f									
int collide = 0;

// Our global float that stores the elapsed time between the current and last frame
float g_FrameInterval = 0.0f;


///////////////////////////////// CALCULATE FRAME RATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This function calculates the frame rate and time intervals between frames
/////
///////////////////////////////// CALCULATE FRAME RATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CalculateFrameRate()
{
	static float framesPerSecond    = 0.0f;		// This will store our fps
    static float lastTime			= 0.0f;		// This will hold the time from the last frame
	static char strFrameRate[50] = {0};			// We will store the string here for the window title

	static float frameTime = 0.0f;				// This stores the last frame's time

	// Get the current time in seconds
    float currentTime = timeGetTime() * 0.001f;				

	// Here we store the elapsed time between the current and last frame,
	// then keep the current frame in our static variable for the next frame.
 	g_FrameInterval = currentTime - frameTime;
	frameTime = currentTime;

	// Increase the frame counter
    ++framesPerSecond;

	// Now we want to subtract the current time by the last time that was stored
	// to see if the time elapsed has been over a second, which means we found our FPS.
    if( currentTime - lastTime > 1.0f )
    {
		// Here we set the lastTime to the currentTime
	    lastTime = currentTime;
		
		// Copy the frames per second into a string to display in the window title bar
		sprintf(strFrameRate, "Current Frames Per Second: %d", int(framesPerSecond));

		// Set the window title bar to our string
//		SetWindowText(g_hWnd, strFrameRate);

		// Reset the frames per second
        framesPerSecond = 0;
    }
}


/////////////////////////////////////// CROSS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This returns a perpendicular vector from 2 given vectors by taking the cross product.
/////
/////////////////////////////////////// CROSS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
												
CVector3 Cross(CVector3 vVector1, CVector3 vVector2)
{
	CVector3 vNormal;	

	// Calculate the cross product with the non communitive equation
	vNormal.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
	vNormal.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
	vNormal.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));

	// Return the cross product
	return vNormal;										 
}


/////////////////////////////////////// MAGNITUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This returns the magnitude of a vector
/////
/////////////////////////////////////// MAGNITUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

float Magnitude(CVector3 vNormal)
{
	// Here is the equation:  magnitude = sqrt(V.x^2 + V.y^2 + V.z^2) : Where V is the vector
	return (float)sqrt( (vNormal.x * vNormal.x) + 
						(vNormal.y * vNormal.y) + 
						(vNormal.z * vNormal.z) );
}


/////////////////////////////////////// NORMALIZE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This returns a normalize vector (A vector exactly of length 1)
/////
/////////////////////////////////////// NORMALIZE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CVector3 Normalize(CVector3 vVector)
{
	// Get the magnitude of our normal
	float magnitude = Magnitude(vVector);				

	// Now that we have the magnitude, we can divide our vector by that magnitude.
	// That will make our vector a total length of 1.  
	vVector = vVector / magnitude;		
	
	// Finally, return our normalized vector
	return vVector;										
}


///////////////////////////////// CCAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This is the class constructor
/////
///////////////////////////////// CCAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CCamera::CCamera()
{
	CVector3 vZero = CVector3(0.0, 0.0, 0.0);		// Init a vector to 0 0 0 for our position
	CVector3 vView = CVector3(0.0, 1.0, 0.5);		// Init a starting view vector (looking up and out the screen) 
	CVector3 vUp   = CVector3(0.0, 0.0, 1.0);		// Init a standard up vector (Rarely ever changes)

	m_vPosition	= vZero;					// Init the position to zero
	m_vView		= vView;					// Init the view to a std starting view
	m_vUpVector	= vUp;						// Init the UpVector

	kartPosTL = vZero;
	kartPosTR = vZero;
	kartPosBL = vZero;
	kartPosBR = vZero;
}



///////////////////////////////// POSITION CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This function sets the camera's position and view and up vector.
/////
///////////////////////////////// POSITION CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::PositionCamera(float positionX, float positionY, float positionZ,
				  		     float viewX,     float viewY,     float viewZ,
							 float upVectorX, float upVectorY, float upVectorZ)
{
	CVector3 vPosition	= CVector3(positionX, positionY, positionZ);
	CVector3 vView		= CVector3(viewX, viewY, viewZ);
	CVector3 vUpVector	= CVector3(upVectorX, upVectorY, upVectorZ);

	// The code above just makes it cleaner to set the variables.
	// Otherwise we would have to set each variable x y and z.

	m_vPosition = vPosition;					// Assign the position
	m_vView     = vView;						// Assign the view
	m_vUpVector = vUpVector;					// Assign the up vector
}

///////////////////////////////// POSITION GoKart \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This function sets the GoKart's position  
/////
///////////////////////////////// POSITION GoKart \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::PositionGoKart(float x1, float y1, float z1, 
				  float x2, float y2, float z2, 
				  float x3, float y3, float z3, 
				  float x4, float y4, float z4) 
{
	CVector3 p1	= CVector3(x1, y1, z1);
	CVector3 p2	= CVector3(x2, y2, z2);
	CVector3 p3	= CVector3(x3, y3, z3);
	CVector3 p4	= CVector3(x4, y4, z4);
	
	kartPosTL = p1;
	kartPosTR = p2;
	kartPosBL = p3;
	kartPosBR = p4;
}

///////////////////////////////// SET VIEW BY MOUSE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This allows us to look around using the mouse, like in most first person games.
/////
///////////////////////////////// SET VIEW BY MOUSE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::SetViewByMouse()
{
	POINT mousePos;									// This is a window structure that holds an X and Y
	int middleX = SCREEN_WIDTH  >> 1;				// This is a binary shift to get half the width
	int middleY = SCREEN_HEIGHT >> 1;				// This is a binary shift to get half the height
	float angleY = 0.0f;							// This is the direction for looking up or down
	float angleZ = 0.0f;							// This will be the value we need to rotate around the Y axis (Left and Right)
	static float currentRotX = 0.0f;
	
	// Get the mouse's current X,Y position
	GetCursorPos(&mousePos);						
	
	// If our cursor is still in the middle, we never moved... so don't update the screen
	if( (mousePos.x == middleX) && (mousePos.y == middleY) ) return;

	// Set the mouse position to the middle of our window
	SetCursorPos(middleX, middleY);							

	// Get the direction the mouse moved in, but bring the number down to a reasonable amount
	angleY = (float)( (middleX - mousePos.x) ) / 500.0f;		
	angleZ = (float)( (middleY - mousePos.y) ) / 500.0f;		

	// Here we keep track of the current rotation (for up and down) so that
	// we can restrict the camera from doing a full 360 loop.
	currentRotX -= angleZ;  

	// If the current rotation (in radians) is greater than 1.0, we want to cap it.
	if(currentRotX > 1.0f)
		currentRotX = 1.0f;
	// Check if the rotation is below -1.0, if so we want to make sure it doesn't continue
	else if(currentRotX < -1.0f)
		currentRotX = -1.0f;
	// Otherwise, we can rotate the view around our position
	else
	{
		// To find the axis we need to rotate around for up and down
		// movements, we need to get a perpendicular vector from the
		// camera's view vector and up vector.  This will be the axis.
		CVector3 vAxis = Cross(m_vView - m_vPosition, m_vUpVector);
		vAxis = Normalize(vAxis);

		// Rotate around our perpendicular axis and along the y-axis
		RotateView(angleZ, vAxis.x, vAxis.y, vAxis.z);
		RotateView(angleY, 0, 1, 0);
		RotateViewGoKart(angleY, 0, 1, 0);

	}

	// Rotate around the y axis no matter what the currentRotX is
//	RotateView(angleY, 0, 1, 0);
}


///////////////////////////////// ROTATE VIEW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This rotates the view around the position using an axis-angle rotation
/////
///////////////////////////////// ROTATE VIEW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::RotateView(float angle, float x, float y, float z)
{
	CVector3 vNewView;

	// Get the view vector (The direction we are facing)
	CVector3 vView = m_vView - m_vPosition;		

	// Calculate the sine and cosine of the angle once
	float cosTheta = (float)cos(angle);
	float sinTheta = (float)sin(angle);

	// Find the new x position for the new rotated point
	vNewView.x  = (cosTheta + (1 - cosTheta) * x * x)		* vView.x;
	vNewView.x += ((1 - cosTheta) * x * y - z * sinTheta)	* vView.y;
	vNewView.x += ((1 - cosTheta) * x * z + y * sinTheta)	* vView.z;

	// Find the new y position for the new rotated point
	vNewView.y  = ((1 - cosTheta) * x * y + z * sinTheta)	* vView.x;
	vNewView.y += (cosTheta + (1 - cosTheta) * y * y)		* vView.y;
	vNewView.y += ((1 - cosTheta) * y * z - x * sinTheta)	* vView.z;

	// Find the new z position for the new rotated point
	vNewView.z  = ((1 - cosTheta) * x * z - y * sinTheta)	* vView.x;
	vNewView.z += ((1 - cosTheta) * y * z + x * sinTheta)	* vView.y;
	vNewView.z += (cosTheta + (1 - cosTheta) * z * z)		* vView.z;

	// Now we just add the newly rotated vector to our position to set
	// our new rotated view of our camera.
	m_vView = m_vPosition + vNewView;
}

void CCamera::RotateViewGoKart(float angle, float x, float y, float z)
{
	CVector3 pos;

	// Get the view vector (The direction we are facing)
	CVector3 vPos = kartPosTL - m_vPosition;		

	// Calculate the sine and cosine of the angle once
	float cosTheta = (float)cos(angle);
	float sinTheta = (float)sin(angle);

	// Find the new x position for the new rotated point
	pos.x  = (cosTheta + (1 - cosTheta) * x * x)		* vPos.x;
	pos.x += ((1 - cosTheta) * x * y - z * sinTheta)	* vPos.y;
	pos.x += ((1 - cosTheta) * x * z + y * sinTheta)	* vPos.z;

	// Find the new y position for the new rotated point
	pos.y  = ((1 - cosTheta) * x * y + z * sinTheta)	* vPos.x;
	pos.y += (cosTheta + (1 - cosTheta) * y * y)		* vPos.y;
	pos.y += ((1 - cosTheta) * y * z - x * sinTheta)	* vPos.z;

	// Find the new z position for the new rotated point
	pos.z  = ((1 - cosTheta) * x * z - y * sinTheta)	* vPos.x;
	pos.z += ((1 - cosTheta) * y * z + x * sinTheta)	* vPos.y;
	pos.z += (cosTheta + (1 - cosTheta) * z * z)		* vPos.z;

	kartPosTL = m_vPosition + pos;

	//===============================================================
	vPos = kartPosTR - m_vPosition;		

	// Find the new x position for the new rotated point
	pos.x  = (cosTheta + (1 - cosTheta) * x * x)		* vPos.x;
	pos.x += ((1 - cosTheta) * x * y - z * sinTheta)	* vPos.y;
	pos.x += ((1 - cosTheta) * x * z + y * sinTheta)	* vPos.z;

	// Find the new y position for the new rotated point
	pos.y  = ((1 - cosTheta) * x * y + z * sinTheta)	* vPos.x;
	pos.y += (cosTheta + (1 - cosTheta) * y * y)		* vPos.y;
	pos.y += ((1 - cosTheta) * y * z - x * sinTheta)	* vPos.z;

	// Find the new z position for the new rotated point
	pos.z  = ((1 - cosTheta) * x * z - y * sinTheta)	* vPos.x;
	pos.z += ((1 - cosTheta) * y * z + x * sinTheta)	* vPos.y;
	pos.z += (cosTheta + (1 - cosTheta) * z * z)		* vPos.z;

	kartPosTR = m_vPosition + pos;

	//===============================================================
	vPos = kartPosBL - m_vPosition;		

	// Find the new x position for the new rotated point
	pos.x  = (cosTheta + (1 - cosTheta) * x * x)		* vPos.x;
	pos.x += ((1 - cosTheta) * x * y - z * sinTheta)	* vPos.y;
	pos.x += ((1 - cosTheta) * x * z + y * sinTheta)	* vPos.z;

	// Find the new y position for the new rotated point
	pos.y  = ((1 - cosTheta) * x * y + z * sinTheta)	* vPos.x;
	pos.y += (cosTheta + (1 - cosTheta) * y * y)		* vPos.y;
	pos.y += ((1 - cosTheta) * y * z - x * sinTheta)	* vPos.z;

	// Find the new z position for the new rotated point
	pos.z  = ((1 - cosTheta) * x * z - y * sinTheta)	* vPos.x;
	pos.z += ((1 - cosTheta) * y * z + x * sinTheta)	* vPos.y;
	pos.z += (cosTheta + (1 - cosTheta) * z * z)		* vPos.z;

	kartPosBL = m_vPosition + pos;

	//===============================================================
	vPos = kartPosBR - m_vPosition;		

	// Find the new x position for the new rotated point
	pos.x  = (cosTheta + (1 - cosTheta) * x * x)		* vPos.x;
	pos.x += ((1 - cosTheta) * x * y - z * sinTheta)	* vPos.y;
	pos.x += ((1 - cosTheta) * x * z + y * sinTheta)	* vPos.z;

	// Find the new y position for the new rotated point
	pos.y  = ((1 - cosTheta) * x * y + z * sinTheta)	* vPos.x;
	pos.y += (cosTheta + (1 - cosTheta) * y * y)		* vPos.y;
	pos.y += ((1 - cosTheta) * y * z - x * sinTheta)	* vPos.z;

	// Find the new z position for the new rotated point
	pos.z  = ((1 - cosTheta) * x * z - y * sinTheta)	* vPos.x;
	pos.z += ((1 - cosTheta) * y * z + x * sinTheta)	* vPos.y;
	pos.z += (cosTheta + (1 - cosTheta) * z * z)		* vPos.z;

	kartPosBR = m_vPosition + pos;
}


///////////////////////////////// MOVE CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This will move the camera forward or backward depending on the speed
/////
///////////////////////////////// MOVE CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::MoveCamera(float speed)
{
	// Get the current view vector (the direction we are looking)
	CVector3 vVector = m_vView - m_vPosition;
	vVector = Normalize(vVector);

	m_vPosition.x += vVector.x * speed;		// Add our acceleration to our position's X
	m_vPosition.y += vVector.y * speed;			// Add our acceleration to our view's Y
//	m_vPosition.y -= 100;
	m_vPosition.z += vVector.z * speed;		// Add our acceleration to our position's Z
	m_vView.x += vVector.x * speed;			// Add our acceleration to our view's X
	m_vView.y += vVector.y * speed;			// Add our acceleration to our view's Y
//	m_vView.y -= 100;
	m_vView.z += vVector.z * speed;			// Add our acceleration to our view's Z

	kartPosTL.x += vVector.x * speed;
	kartPosTL.z += vVector.z * speed;
	kartPosTR.x += vVector.x * speed;
	kartPosTR.z += vVector.z * speed;
	kartPosBL.x += vVector.x * speed;
	kartPosBL.z += vVector.z * speed;
	kartPosBR.x += vVector.x * speed;
	kartPosBR.z += vVector.z * speed;

}

void CCamera::Rotate_View(float speed)
{
	CVector3 vVector = m_vView - m_vPosition;	// Get the view vector

	m_vView.z = (float)(m_vPosition.z + sin(speed)*vVector.x + cos(speed)*vVector.z);
	m_vView.x = (float)(m_vPosition.x + cos(speed)*vVector.x - sin(speed)*vVector.z);

	vVector = kartPosTL - m_vPosition;	// Get the view vector

	kartPosTL.z = (float)(m_vPosition.z + sin(speed)*vVector.x + cos(speed)*vVector.z);
	kartPosTL.x = (float)(m_vPosition.x + cos(speed)*vVector.x - sin(speed)*vVector.z);

	vVector = kartPosTR - m_vPosition;	// Get the view vector

	kartPosTR.z = (float)(m_vPosition.z + sin(speed)*vVector.x + cos(speed)*vVector.z);
	kartPosTR.x = (float)(m_vPosition.x + cos(speed)*vVector.x - sin(speed)*vVector.z);

	vVector = kartPosBL - m_vPosition;	// Get the view vector

	kartPosBL.z = (float)(m_vPosition.z + sin(speed)*vVector.x + cos(speed)*vVector.z);
	kartPosBL.x = (float)(m_vPosition.x + cos(speed)*vVector.x - sin(speed)*vVector.z);

	vVector = kartPosBR - m_vPosition;	// Get the view vector

	kartPosBR.z = (float)(m_vPosition.z + sin(speed)*vVector.x + cos(speed)*vVector.z);
	kartPosBR.x = (float)(m_vPosition.x + cos(speed)*vVector.x - sin(speed)*vVector.z);
}

//////////////////////////// CHECK FOR MOVEMENT \\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This function handles the input faster than in the WinProc()
/////
//////////////////////////// CHECK FOR MOVEMENT \\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::CheckForMovement()
{	
	// Once we have the frame interval, we find the current speed
	float speed = kSpeed * g_FrameInterval;
	static char str[60];

	// Check if we hit the Up arrow or the 'w' key
	if(GetKeyState(VK_UP) & 0x80 || GetKeyState('W') & 0x80) {				
		if (Collision_BoundingBox(MAXBOX, m_vPosition) != -1)
			collide=1;
		else if (Collision_BoundingBox(MAXBOX, m_vPosition) == -1) {
			// Move our camera forward by a positive SPEED
			collide=0;
			MoveCamera(speed);				
		}
	}

	// Check if we hit the Down arrow or the 's' key
	if(GetKeyState(VK_DOWN) & 0x80 || GetKeyState('S') & 0x80) {			
		if (Collision_BoundingBox(MAXBOX, m_vView) != -1)
			collide=1;
		else if (Collision_BoundingBox(MAXBOX, m_vView) == -1) {
		// Move our camera backward by a negative SPEED
			collide=0;
			MoveCamera(-speed);
		}
	}

	// Check if we hit the Left arrow or the 'a' key
	if(GetKeyState(VK_LEFT) & 0x80 || GetKeyState('A') & 0x80) {			
		Rotate_View(-speed/50);
	}

	// Check if we hit the Right arrow or the 'd' key
	if(GetKeyState(VK_RIGHT) & 0x80 || GetKeyState('D') & 0x80) {			
		Rotate_View(speed/50);
	}	

	if (collide==1)
		sprintf(str,"collision occured, try to move to another direction");
	else
		sprintf(str,"PosX=%.1f PosY=%.1f PosZ=%.1f ViewX=%.1f ViewY=%.1f ViewZ=%.1f",m_vPosition.x,m_vPosition.y,m_vPosition.z,m_vView.x,m_vView.y,m_vView.z);
	SetWindowText(g_hWnd,str);
}


///////////////////////////////// UPDATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This updates the camera's view and strafe vector
/////
///////////////////////////////// UPDATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::Update() 
{
	// Initialize a variable for the cross product result
	CVector3 vCross = Cross(m_vView - m_vPosition, m_vUpVector);

	// Move the camera's view by the mouse
	SetViewByMouse();

	// This checks to see if the keyboard was pressed
	CheckForMovement();
	
	// Calculate our frame rate and set our frame interval for time-based movement
	CalculateFrameRate();
}


///////////////////////////////// LOOK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This updates the camera according to the 
/////
///////////////////////////////// LOOK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCamera::Look()
{
	// Give openGL our camera position, then camera view, then camera up vector
	gluLookAt(m_vPosition.x, m_vPosition.y, m_vPosition.z,	
			  m_vView.x,	 m_vView.y,     m_vView.z,	
			  m_vUpVector.x, m_vUpVector.y, m_vUpVector.z);
}


/////////////////////////////////////////////////////////////////////////////////
//
// * QUICK NOTES * 
//
// Nothing was changed for the camera code in this tutorial.
//
//
// Ben Humphrey (DigiBen)
// Game Programmer
// DigiBen@GameTutorials.com
// Co-Web Host of www.GameTutorials.com
//
//
