#include "FatApp.h"

bool FatApp::Initiate(unsigned int numArguments, char* arguments[])
{
	// Set the physical step size.
	// 1/100 tends to be an easy time step to deal
	// with in physics systems.
	physicsStepSize = 1.0f/100.0f;

	// Clear out some hacky mouse-movement stuff.
	bClicked = false;
	lmx = lmy = mx = my = 0;
	fRotationYaw = 20.0f;
	fRotationPitch = -30.0f;

	// Set the default animation switch/state info.
	runswitch = false;
	runinterp=0.0f;
	initialInterp=0.0f;
	
	// Start out playing.
	bPlay = true;
	
	// Start out with the heavier man.
	bHighFat = true;
	
	// Render status.
	bOnlyFat=false; // Don't show just fat, show the whole man.
	bRenderFat=true; // Show the fat layer.
	bRenderFatLayer=false; // Don't render the fat outline
	bXRayFatLayer=false; // Don't xray the fat.
	
	return true;
}

bool FatApp::InitiateGraphics()
{
	// Reference meshes: thin/fat.
	normalPose.Load("RunningMan_Bind.mesh");
	fatPose.Load("RunningMan_Fat_Bind.mesh");
	fatPose2.Load("RunningMan_Fat2_Bind.mesh");
	
	// Animated meshes.
	runningMan.Load("RunningMan_Run.amsh");
	runningMan.Play();
	runningMan2.Load("RunningMan_RunLeft.amsh");
	runningMan2.Play();
	
	// Makes the connection between the
	// thin and fat mesh. This works by comparing
	// the t-pose with the fat-t-pose and determining
	// where there is enough of a gap for a fat sim.
	fatSystem.BakeFat(normalPose, fatPose2);
	
	finalMesh.SetGLStreaming(true,true); // normals & positions streaming.
	finalMesh.Copy(normalPose); // finalMesh is rendered, copy the t-pose initially.
	finalMesh.PopGLData(); // ensures that next call will load the copied normal pose.

	// temporary mesh to store the output of
	// regular animation
	combinedAnimation.SetGLStreaming(true, true);
	combinedAnimation.Copy(runningMan.Mesh());
	combinedAnimation.PopGLData();
	
	return true;
}

void FatApp::UpdateFixed(float timeStep)
{
	// All of the code at the head of this function
	// is just updating a really basic animation
	// blending system. This data is used to blend
	// between T-Pose, Running-Straight and Running-
	// Left. It's necessary to keep from snapping
	// the fat simulation.

	// Which run are we in? Right or left?
	if( runswitch ) // interping to right-left
		runinterp += 1.0f/25.0f;
	else // interping to run-straight
		runinterp -= 1.0f/25.0f;
	// clamp the run data
	runinterp = Min(1.0f, Max(0.0f, runinterp));
	
	// Interp between t-post and animation.
	initialInterp += 1.0f/25.0f;
	initialInterp = Min(1.0f, initialInterp);
	
	// Update run-straight if we're not completely
	// running left.
	if( runinterp != 1.0f )
		runningMan.Update(1.0f/100.0f);
	// Update run-left if we're not completely
	// running forward.
	if( runinterp != 0.0f )
		runningMan2.Update(1.0f/100.0f);
	
	// Because the animation system is MESH based
	// and not skeletal based, we have to do mesh
	// copies for animation interpolation as opposed
	// to skeletal lerping/copying.

	// First, combine the run animations.
	if( runinterp == 0.0f ) // running-straight
		combinedAnimation.Copy(runningMan.Mesh());
	else if( runinterp == 1.0f ) // running-left
		combinedAnimation.Copy(runningMan2.Mesh());
	else // blending between runs.
		Interp(runningMan.Mesh(), runningMan2.Mesh(), runinterp, combinedAnimation);
	
	// Finally, combine the t-pose with the run
	// animations.
	Interp(normalPose, combinedAnimation, initialInterp, finalMesh);
	
	// Run the fat simulation step. It is a non-linear function
	// and we're running in. We use the final animated mesh as
	// an input which provides vertex positions for one side of
	// the layer of fat.
	fatSystem.UpdateFat(physicsStepSize, finalMesh);
	
	// Apply the fat over the animated mesh. This works by
	// indexing into the mesh directly and replacing lower
	// vertices with the positions at the fat layer.
	fatSystem.OverlayFat(finalMesh);

	// Recalculate normals for more accurate lighting.
	finalMesh.CalculateNormals();
}

void FatApp::OnKeyboard(unsigned char key, int x, int y)
{
	switch( key )
	{
		case 'o':
		case 'O':
			// Toggle animation playback
			bPlay = !bPlay;
			if( bPlay )
			{
				runningMan.Play();
				runningMan2.Play();
			}
			else
			{
				runningMan.Stop();
				runningMan2.Stop();
			}
			break;
		case 'b':
		case 'B':
			// Toggle rendering JUST the fat layer.
			bOnlyFat = !bOnlyFat;
			break;
		case 'w':
		case 'W':
			// Switch between playing run-left or run-straight.
			runswitch = !runswitch;
			break;
		case 'i':
		case 'I':
			// There are two fat meshes, a low fat and
			// high fat mesh. Toggle between them.
			bHighFat = !bHighFat;
			// This toggle requires the mesh to rebake
			// the layer of fat by comparing the t-pose
			// with the fat t-pose.
			if( bHighFat )
				fatSystem.BakeFat(normalPose, fatPose2);
			else
				fatSystem.BakeFat(normalPose, fatPose);
			// reset to the t-pose to mitigate the possibility
			// of a physics explosion.
			initialInterp = 0.0f;
			break;
		case 'r':
		case 'R':
			// Reset the system.
			// Rebake the fat by comparing the t-pose
			// with the fat t-pose.
			if( bHighFat )
				fatSystem.BakeFat(normalPose, fatPose2);
			else
				fatSystem.BakeFat(normalPose, fatPose);
			// Reset to the t-pose to mitigate the possibility
			/// of a physics explosion.
			initialInterp = 0.0f;
			break;
		case 'e':
		case 'E':
			// Toggles rendering with fat or
			// just the original animation.
			bRenderFat = !bRenderFat;
			break;
		case 't':
		case 'T':
			// Toggle rendering lines that represents
			// part of the fat springs.
			bRenderFatLayer = !bRenderFatLayer;
			break;
		case 'y':
		case 'Y':
			// Disable depth testing to see the lower
			// layer of fat against the mesh animation.
			bXRayFatLayer = !bXRayFatLayer;
			break;
	}
}

void FatApp::OnMouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		// Do camera rotation if the left mouse button is
		// down. To avoid camera jumpy-ness, we set all the
		// last-next-delta settings to the current state of
		// the mouse.
		bClicked = true;
		lmx = mx = x; 
		lmy = my = y;
		dx = dy = 0;
	}
	else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
	{
		// Don't do any camera movement when the left mouse
		// button is not clicked.
		bClicked = false;
		lmx = lmy = mx = my = dx = dy = 0;
	}
}

void FatApp::OnMouseMotion(int x, int y)
{
	// Don't do camera movement if the
	// left mouse button is not clicked.
	if( !bClicked ) return;
	
	// mx/my represent this moment's
	// mouse position.
	mx = x;
	my = y;
	
	// delta = this - last(lx/ly)
	dx = mx - lmx;
	dy = my - lmy;
	
	// Apply the delta parameters directly
	// to the camera's yaw and pitch about
	// the focal point (the running man).
	fRotationYaw += dx;
	fRotationPitch += dy;
	
	// Set last mouse status to this mouse status.
	lmx = mx;
	lmy = my;
}

void FatApp::GetViewPoint(float &eyeX,float &eyeY,float &eyeZ,float &centerX,float &centerY,float &centerZ,float &upX,float &upY,float &upZ)
{
	// These represent the initial camera settings.
	Cooney::Math::Vec3 eyePosition(0.0f, 0.0f, 2.0f); // looking from here
	Cooney::Math::Vec3 centerPosition(0.0f, 0.9f, 0.0f); // looking at here
	Cooney::Math::Vec3 upDirection(0.0f,1.0f,0.0f); // up
	
	// Create a rotational matrix around the X and Y axis.
	Cooney::Math::Mat4 pitchRotation = Cooney::Math::RotationalX4(COONEY_MATH_DEGREES_TO_RADIANS(fRotationPitch));
	Cooney::Math::Mat4 yawRotation = Cooney::Math::RotationalY4(COONEY_MATH_DEGREES_TO_RADIANS(fRotationYaw));
	
	// Transform the eye's position. This camera system is always about the center point.
	// so we don't need very complex transformations.
	eyePosition = ToVector3(yawRotation * pitchRotation * ToVector4(eyePosition,1.0f));
	
	// Offset the eye's position based on the focal point.
	// Pass this to the output parameters that GLUT framework uses.
	eyeX = eyePosition.x + centerPosition.x;
	eyeY = eyePosition.y + centerPosition.y;
	eyeZ = eyePosition.z + centerPosition.z;
	
	// Pass the center position to the out parameters that the GLUT framework uses.
	centerX = centerPosition.x;
	centerY = centerPosition.y;
	centerZ = centerPosition.z;
	
	// Pass the up position to the out parameters that the GLUT framework uses.
	upX = upDirection.x;
	upY = upDirection.y;
	upZ = upDirection.z;
}

void FatApp::DrawGeometry()
{
	// Draw the ground quad.
	// Receives shadows, etc etc.
	glBegin(GL_QUADS);
	glColor3f(1,1,1);
	glNormal3f(0,1,0);
	glVertex3f(20,0.0f,-20);
	glVertex3f(-20,0.0f,-20);
	glVertex3f(-20,0.0f,20);
	glVertex3f(20,0.0f,20);
	glEnd();

	// As long as we don't have a request
	// to render just the fat layer we render
	// the model.
	if( !bOnlyFat )
	{
		if( bRenderFat ) // fat + animation
			finalMesh.PushGL(true,true);
		else // no fat, animation only
			combinedAnimation.PushGL(true,true);
	}
}

void FatApp::PostRender()
{
	// The post render pass is outside the shadows and lighting pass.
	// We have pretty much full control over how it's presented
	// Here we are rendering lines that explain how the fat system
	// works (if they are enabled).

	if( bRenderFat && bRenderFatLayer || bOnlyFat )
	{
		// enable depth testing unless we want to
		// see how the lines connect inside the mesh.
		if( !bXRayFatLayer )
			glEnable(GL_DEPTH_TEST);
		
		// Push the fat system to opengl (not the
		// same as pushing the animations).
		fatSystem.PushGL();
	}
}