#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 ¢erX,float ¢erY,float ¢erZ,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();
}
}