//////////////////////////////////////////////////////////////////////////////
/// \file GLUTFramework.cpp
///
/// Fast prototyping interface for GLUT applications.
/// Coupled with BaseApp.h.
///
/// Provides the lowest level of application interface.
/// The implementation at this level is very monolithic,
/// sitting on top of glut and providing many of the
/// base graphics features.
///
/// Most of the major settings and changes are applied through
/// the use of defines at the top of this file and those settings
/// propogate through the app code implementation.
///
/// Stephen Timothy Cooney, 2009
//////////////////////////////////////////////////////////////////////////////

/// Common libs
#include <cstdlib>
#include <iostream>

/// Include OpenGL
/// Helps link up all of the extension
/// functions that we want.
#include "GLee.h"
/// Include GLUT
#ifdef __APPLE__ /// apple has a unique pathing system
#include <GLUT/glut.h>
#else /// linux & pc
#include <GL/glut.h>
#endif

///__________________________
/// Critical Application Plug
#include "BaseApp.h"
/// Include your app here
/// ex:
/// #include "YourApp.h" // has yourapp derived from baseapp as <class YourApp : public BaseApp>
/// #define APP_IMPLEMENTATION YourApp

/// Implementing the fat simulation application.
#include "FatApp.h"
#define APP_IMPLEMENTATION FatApp

/// The application will still run if no application
/// derived from BaseApp is defined. It will, however
/// be exceptionally boring.
#ifdef APP_IMPLEMENTATION
BaseApp* gApplication = new APP_IMPLEMENTATION();
#else
BaseApp* gApplication = NULL;
#endif

///________________________
/// Application information
#define APPNAME "Fat Simulation"
#define DEVNAME "Stephen Timothy Cooney"
#define DEVDATE "2009"
#define COPYRIGHT 0

///_________________
/// Window dimensions
/// Change WINDOW_WIDTH and WINDOW_HEIGHT to set the
/// initial size of the window
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
unsigned int gWindowWidth = WINDOW_WIDTH; // dynamic width value, check me, not the define
unsigned int gWindowHeight = WINDOW_HEIGHT; // dynamic height value, check me, not the define

///____________________________________
/// OpenGL buffer and state information
#define BUFFER_FORMAT (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH)
#define FIELD_OF_VIEW 60.0f
#define NEAR_BUFFER 0.1f
#define FAR_BUFFER 100.0f
#define CLEAR_COLOR 0.0f, 0.0f, 0.0f, 0.0f
#define CLEAR_DEPTH 1.0f
#define CLEAR_STENCIL 0

///________________________
/// Initial Camera Settings
/// The BaseApp that GLUTFramework implements overrides
/// these settings if they are set. This just provides
/// easy setup for rapid-prototype applications.
#define INITIALVIEWPOINT_POSITION 1.0f, 1.0f, 2.0f
#define INITIALVIEWPOINT_TARGET 0.0f, 1.0f, 0.0f
#define INITIALVIEWPOINT_UP 0.0f, 1.0f, 0.0f

///___________________________________
/// Render Unhinged (similar to vsync)
/// Whether or not the application renders
/// during idle (if true) or in the OnTimerDefault
/// function. The graphics settings on your system
/// may not allow the renderer to go faster than
/// 60fps.
#define RENDER_UNHINGED false
bool gbRenderUnhinged = RENDER_UNHINGED;

///____
/// Fog
#define RENDER_FOG true
bool gbRenderFog = RENDER_FOG;
/// Settings
#define FOG_MODE GL_LINEAR // GL_EXP GL_EXP2 GL_LINEAR
#define FOG_COLOR 0.0, 0.0, 0.0
#define FOG_DENSITY 1.0
#define FOG_START 5
#define FOG_END 10

///_____________________
/// Lighting and Shadows
/// GLUTFramework only provides an
/// interface for rendering with one
/// light that is considered a sunlight.
///
/// It also provides a basic interface
/// for shadow map shadowing.
#define RENDER_SHADOWS true
bool gbRenderShadows = RENDER_SHADOWS;
/// For debugging purposes, it is optional
/// to display the shadow map.
#define RENDER_SHADOWMAP false
bool gbRenderShadowmap = RENDER_SHADOWMAP;
/// Lighting/shadow settings
#define SHADOW_RESOLUTION 512 // TODO: Don't make this larger than the window width/height, not rendering offscreen yet.
#define SHADOW_EXTENT_X 1.5f
#define SHADOW_EXTENT_Y 1.5f
#define SHADOW_EXTENT_Z 1.5f
#define SHADOW_FACTOR 10.0f
#define SUN_DIRECTION_X 0.5f
#define SUN_DIRECTION_Y 0.75f
#define SUN_DIRECTION_Z 0.75f
#define SUN_COLOR 1.0, 1.0, 1.0
#define AMBIENT_COLOR 0.2, 0.2, 0.2

///________________________
/// Multiple Render Targets
/// Allows up to 4 offscreen MRTS
/// at the same time. Currently
/// The implementation doesn't use
/// more than 1 right now however
/// and it's not enabled by default.
#define RENDER_MRT false
bool gbRenderMRT = RENDER_MRT;
/// Settings
#define MRT_NUMBER 1 // don't use more than 4 for now...
#define MRT_FORMAT_INTERNAL GL_RGBA8
#define MRT_FORMAT_LOGICAL GL_RGBA
#define MRT_FORMAT_PIXEL GL_UNSIGNED_BYTE
/// mrtTargets Maps an indexible array to
/// GLenums(easier to use).
GLenum mrtTargets[] =
{
	GL_COLOR_ATTACHMENT0_EXT,
	GL_COLOR_ATTACHMENT1_EXT,
	GL_COLOR_ATTACHMENT2_EXT,
	GL_COLOR_ATTACHMENT3_EXT,
};
/// MRT resources.
GLuint mrtframebuffer, mrtrenderbuffer;
GLuint mrttextures[MRT_NUMBER];

///___________
/// Render FPS
/// If true, renders the frames per second on the screen
/// using the glut string rendering functionality.
#define RENDER_FPS_DEFAULT true
#define RENDER_FPS_GRAPH_DEFAULT false;
#define BAD_FPS 25
#define GREAT_FPS 60
bool gbRenderFPS = RENDER_FPS_DEFAULT;
bool gbRenderFPSGraph = RENDER_FPS_GRAPH_DEFAULT;

///_____________
/// Help Display
/// Render help by default
/// renders the help screen by default
/// (activate/deactivate with h or H)
#define RENDER_HELP_DEFAULT true
bool gbRenderHelp = RENDER_HELP_DEFAULT;

///____________________
/// GLUT Core Functions
/// Redefining these symbols up here
/// will automatically be connected
/// to glut in the main initialization.
/// (see main at base)
#define IDLE_FUNCTION OnIdle
#define KEYBOARD_FUNCTION OnKeyboard
#define MOUSE_FUNCTION OnMouse
#define MOUSE_MOTION_FUNCTION OnMouseMotion
#define RENDER_FUNCTION OnRender
#define TIMER_FUNCTION OnTimer
#define RESHAPE_FUNCTION OnReshape

///_______
/// Timers
/// Timer function type definition. Any functions
/// that want their own routine cycle should follow
/// this declaration/definition.
typedef void(*OnTimerFunction)(float timeStep);
/// Default Timer
/// This timer, alongside the idle function
/// is the routine update operator. (typically
/// physics should run under this timer).
#define TIMER_DEFAULT 0 /// Timer index
#define TIMER_DEFAULT_RATE 100 /// 100 updates per second
void OnTimerDefault(float); /// Time declaration.
#define TIMER_DEFAULT_FUNC ((unsigned long long)OnTimerDefault)
/// Timers list
static const int TIMERS_MEMBERS = 3; /// Members per timer (Index, Rate, Function)
/// The TIMERS array constains a list of all
/// timer operations that exist under glut's
/// control.
static const unsigned long long TIMERS[] = {TIMER_DEFAULT, TIMER_DEFAULT_RATE, TIMER_DEFAULT_FUNC};

///_______________
//////////////////
/// IMPLEMENTATION

/// OnIdle
/// Called whenever the application doesn't really have anything
/// else to do. The regular update routes through here.
void OnIdle()
{
	static unsigned int lastTime=glutGet(GLUT_ELAPSED_TIME), thisTime=glutGet(GLUT_ELAPSED_TIME);

	thisTime = glutGet(GLUT_ELAPSED_TIME);
	unsigned int deltaMilliseconds = thisTime - lastTime;
	lastTime = thisTime;
	float deltaSeconds = deltaMilliseconds / (float) 1000.0f;

	if( gApplication )
	{
		gApplication->Update(deltaSeconds);
		
		if( gApplication->ShouldQuit() )
			exit(0);
	}

	/// If 'unhinged' then we render whenever we have the
	/// opportunity to do so.
	if( gbRenderUnhinged )
		glutPostRedisplay();
}

/// OnTimerDefault
/// Good to iterate physics in here since the step time can be relatively concrete.
void OnTimerDefault(float timeStep)
{	
	if( gApplication )
		gApplication->UpdateFixed(timeStep/1000.0f);

	/// if not 'unhinged' then we only render at a consistent
	/// rate with the default timer.
	if( !gbRenderUnhinged )
		glutPostRedisplay();
}

/// OnKeyboard
/// Receives any key input, override for yourself!
void OnKeyboard(unsigned char key, int x, int y)
{
	if( gApplication )
		gApplication->OnKeyboard(key, x, y);
	
	switch(key)
	{
		case 'p':
		case 'P':
			gbRenderFPS = !gbRenderFPS;
			break;
		case 'g':
		case 'G':
			gbRenderFPSGraph = !gbRenderFPSGraph;
			break;
		case 'h':
		case 'H':
			gbRenderHelp = !gbRenderHelp;
			break;
		case 'u':
		case 'U':
			gbRenderUnhinged = !gbRenderUnhinged;
			break;
		case 's':
			gbRenderShadows = !gbRenderShadows;
			break;
		case 'S':
			gbRenderShadowmap = !gbRenderShadowmap;
			break;
		case 'f':
		case 'F':
			{
				static bool bFullScreen = false;
				if( !bFullScreen )
				{
					glutFullScreen();
					bFullScreen = true;
				}
				else
				{
					glutReshapeWindow(WINDOW_WIDTH, WINDOW_HEIGHT);
					bFullScreen = false;
				}
			}
			break;
		case 'q':
		case 'Q':
		case 27: // esc - quit
			exit(0);
			break;
	}
}

/// OnMouse
/// Recieves mouse input.
void OnMouse(int button, int state, int x, int y)
{
	if( gApplication )
		gApplication->OnMouse(button, state, x, y);
}

/// OnMouseMotion
/// Receives input when the mouse moves.
void OnMouseMotion(int x, int y)
{
	if( gApplication )
		gApplication->OnMouseMotion(x, y);
}

///_______________
/// Graphics PLUGS

GLuint shadowmaptexture; /// The texture that contains the shadow map data
int shadowmapsize = SHADOW_RESOLUTION; /// the texture is created at shadowmapsize x shadowmapsize
GLfloat lightModelView[16], lightProjection[16]; /// Store the matrixes that represent the light's point of view.
/// Bind the shadowmap as a texture to OpenGL.
/// For shadowmaps, you need to bind and project the texture onto the geometry that
/// you expect to see interact with shadows.
void BindShadowmap()
{
	/// Automatically generate the texture
	/// coordinates. Project onto the scene
	/// using the matrix transformations that
	/// mimic the light.
	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	/// translating and scaling assuming
	/// that the shadow zone is of size 1.
	/// Centers the projection origin.
	glTranslatef(0.5f,0.5f,0.5f);
	glScalef(0.5f,0.5f,0.5f);
	/// Multiply in the light's transformations.
	glMultMatrixf(lightProjection);
	glMultMatrixf(lightModelView);
	
	/// Enable and bind the shadowmap
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, shadowmaptexture);
	
	/// Using a border color of white (or max distance in
	/// shadowmap space) so that outside of the projection
	/// there is no shadow.
	GLfloat borderColor[4] = {1,1,1,1};
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
	glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
	
	/// mipmapping and magmapping are terrible for shadowmaps so
	/// we don't allow them.
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); /// shadowmap comparison
	glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	
	/// Since all the easy math is done by the light's
	/// projection above, we can automatically generate
	/// super simple texture coordinates that directly
	/// corrolate to spacial units.
	GLfloat sPlane[] = {1,0,0,0};
	GLfloat tPlane[] = {0,1,0,0};
	GLfloat rPlane[] = {0,0,1,0};
	GLfloat qPlane[] = {0,0,0,1};
	///
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glEnable(GL_TEXTURE_GEN_R);
	glEnable(GL_TEXTURE_GEN_Q);
	glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
	glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
	glTexGenfv(GL_R, GL_EYE_PLANE, rPlane);
	glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane);
}


/// Create the multiple render targets and prep their settings.
void InitiateMRT()
{
	GLenum status;

	// Construct the MRT framebuffer.
	glGenFramebuffersEXT(1, &mrtframebuffer);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mrtframebuffer);
	
	// Initiate the renderbuffer (specifically needed for the depth/stencil)
	glGenRenderbuffersEXT(1, &mrtrenderbuffer);
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, mrtrenderbuffer);
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, gWindowWidth, gWindowHeight); // 24 bit depth
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mrtrenderbuffer); // bind it as depth

	// Set up the mrt textures on the framebuffer
	glGenTextures(MRT_NUMBER, mrttextures);
	for(unsigned int i=0; i<MRT_NUMBER; ++i)
	{
		// Initiate the texture for each fb
		glBindTexture(GL_TEXTURE_2D, mrttextures[i]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, MRT_FORMAT_INTERNAL, gWindowWidth, gWindowHeight, 0, MRT_FORMAT_LOGICAL, MRT_FORMAT_PIXEL, NULL);
		
		// Hook the texture into each fb mrt target
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, mrtTargets[i], GL_TEXTURE_2D, mrttextures[i], 0);
	}
	
	// Error Checking
	status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
		std::cout << "Could not create the framebuffer!\n";
	else
		std::cout << "Initiated the framebuffer!\n";

	// Go back and unbind all the framebuffer/renderbuffer information
	// until we need the MRTs later.
	glBindTexture(GL_TEXTURE_2D, 0);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

/// Destroy the framebuffer/renderbuffer and MRT resources.
void ShutdownMRT()
{
	glDeleteTextures(MRT_NUMBER, mrttextures);
	glDeleteRenderbuffersEXT(1, &mrtrenderbuffer);
	glDeleteFramebuffersEXT(1, &mrtframebuffer);
}

/// At this point the graphics system is enabled and gpu preparation
/// may begin.
void InitiateGraphics()
{	
	glGenTextures(1, &shadowmaptexture);
	
	if( gApplication )
		gApplication->InitiateGraphics();
}

/// Shutdown all of the graphics and cleanup the GPU. This is called
/// before the graphics context is completely lost.
void ShutdownGraphics()
{
	ShutdownMRT();
	
	// kill the shadow thing.
	glDeleteTextures(1, &shadowmaptexture);
	
	if( gApplication )
		gApplication->ShutdownGraphics();
}

// Render helper functions
// defined below the OnRender function...
void RenderShadowmap();
void RenderText(float x, float y, char *szString, bool bAutoProjection);
void UpdateFPS(); // called even if not rendering the FPS...
void RenderFPS();
void RenderFPSGraph();
void RenderHelp();

/// Prepare for a basic pass. A basic pass
/// does not include shadows and does an
/// ambient and goroud pass at once.
void PrepareBasicPass()
{
	// Enable Depth
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);
	
	// Enable Sun
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_COLOR_MATERIAL);
	
	// Enable Ambient
	GLfloat ambientLight[] = {AMBIENT_COLOR, 1.0f};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
	
	/// Set Sun Color
	GLfloat sunLight[] = {SUN_COLOR, 1.0f};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, sunLight);
	glLightfv(GL_LIGHT0, GL_SPECULAR, sunLight);
	
	// Set Sun Position
	GLfloat lightPosition[] = {SUN_DIRECTION_X, SUN_DIRECTION_Y, SUN_DIRECTION_Z, 0.0f};
	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
	
	// Set Material Settings
	GLfloat allLight[] = {1.0f, 1.0f, 1.0f, 1.0f};
	// turn on full specular and color by default in material
	glMateriali(GL_FRONT, GL_SHININESS, 64);
	glMaterialfv(GL_FRONT, GL_SPECULAR, allLight);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, allLight);
	glMaterialfv(GL_FRONT, GL_AMBIENT, allLight);
	
	// Make sure we're on the modelview mode
	glMatrixMode(GL_MODELVIEW);
	
	if( gbRenderFog )
	{
		// Enable and Set Fog
		GLfloat fogColor[] = { FOG_COLOR, 1.0 };
		
		glEnable(GL_FOG);
		glFogi(GL_FOG_MODE, FOG_MODE);
		glFogfv(GL_FOG_COLOR, fogColor);
		glFogf(GL_FOG_DENSITY, FOG_DENSITY);	
		glFogf(GL_FOG_START, FOG_START);		
		glFogf(GL_FOG_END, FOG_END);		
	}
	else
	{
		// No Fog
		glDisable(GL_FOG);
	}
}

// Disable the basic pass.
void PopBasicPass()
{
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}


/// Prepare for shadow generation.
/// The shadow generate pass renders the geometry from the camera's point
/// of view and saves it to a shadowmap texture.
void PrepareShadowGeneratePass()
{
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	// Shadow extent represents the box that a shadowmap encompasses
	float shadowextentx = SHADOW_EXTENT_X, shadowextenty = SHADOW_EXTENT_Y, shadowextentz = SHADOW_EXTENT_Z;
	
	// The target is where the shadow is centered around.
	// The main object in the scene should be the target.
	float xTarget, yTarget, zTarget;
	if( gApplication )
		gApplication->GetShadowTarget(xTarget, yTarget, zTarget); /// The application can specify a custom target.
	
	// Back and front for shadows
	glDisable(GL_CULL_FACE);
	
	// Resize the viewport to the shadowsize.
	// (right now the shadowmap size MUST be less than the window size)
	glViewport(0,0,shadowmapsize,shadowmapsize);

	// Erase the depth buffer
	glClear(GL_DEPTH_BUFFER_BIT);
	// enable the depth buffer
	glEnable(GL_DEPTH_TEST);
	
	// REALLY basic rendering, no lighting.
	glShadeModel(GL_FLAT);
	glDisable(GL_LIGHTING);
	glDisable(GL_COLOR_MATERIAL);
	glDisable(GL_NORMALIZE);
	glColorMask(0,0,0,0);
	
	// Offset the polygons in the depth buffer
	// so that objects don't create artifacts
	// through over-self shadowing.
	glEnable(GL_POLYGON_OFFSET_FILL);
	glPolygonOffset(SHADOW_FACTOR, 0.0f);
	
	// Create the projection as if it's from
	// the light.
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(-shadowextentx, shadowextentx, -shadowextentz, shadowextentz); // It is a sun, so parallel lines (ortho).
	glScalef(1,1,1.0f/(float)(shadowextenty*2.0f));
	
	// Copy and store the projection matrix so that we can
	// apply it to texture coordinate generation later.
	glGetFloatv(GL_PROJECTION_MATRIX, lightProjection);
	
	// Move the world to be focused around the target since
	// the range of a clean shadowmap is very limited.
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	gluLookAt(SUN_DIRECTION_X*SHADOW_EXTENT_Y + xTarget,SUN_DIRECTION_Y*SHADOW_EXTENT_Y + yTarget,SUN_DIRECTION_Z*SHADOW_EXTENT_Y + zTarget, xTarget,yTarget,zTarget, -1,0,0);
	
	// Copy and sotre the projection matrix so that we can
	// apply it to the texture coordinate generation later.
	glGetFloatv(GL_MODELVIEW_MATRIX, lightModelView);
}

// Go back and pop any settings that
// we needed for generating the shadowmap.
void PopShadowGeneratePass()
{
	glEnable(GL_CULL_FACE);
	
	// Reset the viewport
	glViewport(0,0,gWindowWidth,gWindowHeight);
	
	glDisable(GL_DEPTH_TEST);
	
	// Regular lighting settings
	glShadeModel(GL_SMOOTH);
	glEnable(GL_LIGHTING);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_NORMALIZE);
	glColorMask(0,0,1,1);
	
	// Disable the shadow offset.
	glDisable(GL_POLYGON_OFFSET_FILL);
	glPolygonOffset(0.0f, 0.0f);
	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	
	// Copy the generated shadow render to an object that can
	// be applied as a texture for shadow tests.
	glBindTexture(GL_TEXTURE_2D, shadowmaptexture);
	glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, shadowmapsize, shadowmapsize, 0);
	glBindTexture(GL_TEXTURE_2D, 0);
	
	glPopAttrib();
}

/// Prepare an ambient-only pass (no diffuse).
/// When rendering with shadows, we do a seperate ambient
/// and diffuse pass.
void PrepareAmbientPass()
{
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	// Clean slate
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Enable Depth
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	
	// Cull the back-faces
	glEnable(GL_CULL_FACE);
	
	// Disable the Sun
	glEnable(GL_LIGHTING);
	glDisable(GL_LIGHT0);
	glEnable(GL_COLOR_MATERIAL);
	
	// Enable Ambient
	GLfloat ambientLight[] = {AMBIENT_COLOR,1.0f};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
	
	// Prep the material
	GLfloat allLight[] = {1,1,1,1};
	// Full ambient material
	glMaterialfv(GL_FRONT, GL_AMBIENT, allLight);
	// No specular or diffuse material
	GLfloat noLight[] = {0,0,0,1};
	glMaterialfv(GL_FRONT, GL_SPECULAR, noLight);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, noLight);
	
	if( gbRenderFog )
	{
		// Enable and Set fog
		GLfloat fogColor[] = { FOG_COLOR, 1.0 };
		
		glEnable(GL_FOG);
		glFogi(GL_FOG_MODE, FOG_MODE);
		glFogfv(GL_FOG_COLOR, fogColor);
		glFogf(GL_FOG_DENSITY, FOG_DENSITY);	
		glFogf(GL_FOG_START, FOG_START);		
		glFogf(GL_FOG_END, FOG_END);		
	}
	else
	{
		// No Fog
		glDisable(GL_FOG);
	}

	// Make sure that we are on the modelview matrix channel.
	glMatrixMode(GL_MODELVIEW);
}

// Remove any ambient pass settings that need manual clearing.
void PopAmbientPass()
{
	glPopAttrib();
}

// Prepare the diffuse(sun) pass.
// When rendering with shadows, the ambient and diffuse pass
// are done seperately.
void PrepareLightPass()
{	
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	// Enable Depth
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	
	// Enable Culling
	glEnable(GL_CULL_FACE);
	
	// Enable an additive blend with the ambient pass.
	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);
	
	// Enable Sun
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_COLOR_MATERIAL);
	
	// Disable Ambient
	GLfloat noLight[] = {0.0f, 0.0f, 0.0f, 1.0f};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, noLight);
	glLightfv(GL_LIGHT0, GL_AMBIENT, noLight);
	
	// Prep the Sun Color
	GLfloat sunLight[] = {SUN_COLOR, 1.0f};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, sunLight);
	glLightfv(GL_LIGHT0, GL_SPECULAR, sunLight);
	
	// Set the Sun Position
	GLfloat lightPosition[] = {SUN_DIRECTION_X, SUN_DIRECTION_Y, SUN_DIRECTION_Z, 0.0f};
	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
	
	// Bind the shadowmap that effectively masks
	// the sun when in shadow.
	BindShadowmap();
	
	// Prep the material.
	GLfloat allLight[] = {1,1,1,1};
	// turn on full specular and color by default in material
	glMateriali(GL_FRONT, GL_SHININESS, 64);
	glMaterialfv(GL_FRONT, GL_SPECULAR, allLight);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, allLight);
	// no ambient material
	glMaterialfv(GL_FRONT, GL_AMBIENT, noLight);
	
	if( gbRenderFog )
	{
		// Enable Fog
		GLfloat fogColor[] = { FOG_COLOR, 1.0 };
		
		glEnable(GL_FOG);
		glFogi(GL_FOG_MODE, FOG_MODE);
		glFogfv(GL_FOG_COLOR, fogColor);
		glFogf(GL_FOG_DENSITY, FOG_DENSITY);	
		glFogf(GL_FOG_START, FOG_START);		
		glFogf(GL_FOG_END, FOG_END);		
	}
	else
	{
		// Disable Fog
		glDisable(GL_FOG);
	}

	// Ensure our later transformations are
	// on the modelview matrix channel.
	glMatrixMode(GL_MODELVIEW);
}

// Clear any settings from the diffuse pass.
void PopLightPass()
{
	glPopAttrib();
}

// Draw scene geometry. This is called whenever
// an application derived from BaseApp is not
// specified. Therefore, it's possible to create
// a simple graphical demo entirely in GLUTFramework.s
void DrawGeometry()
{
}


// After everything is drawn, if MRTs are enabled
// one more pass is run that draws the scene as a
// quad and uses the lowest mrt as a texture. The
// BaseApp derivation may apply post processing to
// this. (Bloom, DoF, Deferred rendering, etc)
void RenderMRTPostProcess()
{
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	// No projection, drawing a full screen quad
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(-1, 1, -1, 1);
	
	// Clear any view transformations.
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	
	// No lighting
	glDisable(GL_LIGHTING);
	
	// Bind the first mrt texture
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, mrttextures[0]);
	
	// Enable blending since the post-process shader
	// may want to blend the quad with the original
	// screen content.
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	
	// Let the BaseApp implementation do any prep (load a shader)
	if( gApplication )
		gApplication->SetupPostProcess();
	
	// Render the full-screen quad.
	glBegin(GL_QUADS);
	glColor3f(1,1,1);	 
	glTexCoord2f(0,0);
	glVertex3f(-1,-1,0);
	glTexCoord2f(1,0);
	glVertex3f(1,-1,0);
	glTexCoord2f(1,1);
	glVertex3f(1,1,0);
	glTexCoord2f(0,1);
	glVertex3f(-1,1,0);
	glEnd();
	
	// Done with the quad. unload anything that was loaded
	// such as a post-process shader.
	if( gApplication )
		gApplication->CleanupPostProcess();
	
	// Pop transformations, we were never here!
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	
	glPopAttrib();
}

/// Serves as the root of all rendering and controls render flow.
/// Custom rendering should not appear in here as this provides
/// a framework to do custom rendering elsewhere.
void OnRender()
{	
	// Prepare/Clear
	glClearColor(CLEAR_COLOR);
	glClearDepth(CLEAR_DEPTH);
	glClearStencil(CLEAR_STENCIL);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	
	// If we have an application instantiation,
	// let it define the view.
	if( gApplication )
	{
		float eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ;
		gApplication->GetViewPoint(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
		
		if( upX == 0.0f && upY == 0.0f && upZ == 0.0f )
			gluLookAt(INITIALVIEWPOINT_POSITION, INITIALVIEWPOINT_TARGET, INITIALVIEWPOINT_UP);
		else
			gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
	}
	else
		gluLookAt(INITIALVIEWPOINT_POSITION, INITIALVIEWPOINT_TARGET, INITIALVIEWPOINT_UP);
	
	// If we're rendering to an MRT first,
	// then we need to prep it as well.
	// Begin rendering to MRT.
	if( gbRenderMRT )
	{
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mrtframebuffer);
		glDrawBuffers(MRT_NUMBER, mrtTargets);	
		glViewport(0,0,gWindowWidth, gWindowHeight);
		
		// Prepare/Clear
		glClearColor(CLEAR_COLOR);
		glClearDepth(CLEAR_DEPTH);
		glClearStencil(CLEAR_STENCIL);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
		
	}
	
	// Pre Framework Rendering
	// The application may want to do it's own rendering before any
	// standard passes are executed.
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	if( gApplication )
		gApplication->PreRender();
	glPopAttrib();
	
	// Done rendering to MRT for now
	if( gbRenderMRT )
	{
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
		glViewport(0,0,gWindowWidth,gWindowHeight);
	}
	
	// Begin Framework Rendering
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	if( gbRenderShadows ) // Rendering with shadows?
	{
		// Render the shadow generation pass
		glPushAttrib(GL_ALL_ATTRIB_BITS);
		PrepareShadowGeneratePass();
		DrawGeometry();
		if( gApplication )
			gApplication->DrawGeometry();
		PopShadowGeneratePass();
		
		// Begin rendering to the MRT
		if( gbRenderMRT )
		{
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mrtframebuffer);
			glDrawBuffers(MRT_NUMBER, mrtTargets);
			glViewport(0,0,gWindowWidth, gWindowHeight);
		}
		
		// Render the ambient pass
		PrepareAmbientPass();
		DrawGeometry();
		if( gApplication )
			gApplication->DrawGeometry();
		PopAmbientPass();
	
		// Render the diffuse pass (masked by the shadow texture)
		PrepareLightPass();
		DrawGeometry();
		if( gApplication )
			gApplication->DrawGeometry();
		PopLightPass();
		glPopAttrib();
		
		// Stop rendering to the MRT
		if( gbRenderMRT )
		{
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
			glViewport(0,0,gWindowWidth, gWindowHeight);
		}
		
		// Render the shadowmap texture as a quad on the screen
		// if we are debugging it.
		if( gbRenderShadowmap )
			RenderShadowmap();
	}
	else // Basic pass, no shadows
	{
		// Begin rendering to the mrts
		if( gbRenderMRT )
		{
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mrtframebuffer);
			glDrawBuffers(MRT_NUMBER, mrtTargets);
			glViewport(0,0,gWindowWidth, gWindowHeight);			
		}
		
		// Render the normal pass
		PrepareBasicPass();
		DrawGeometry();
		if( gApplication )
			gApplication->DrawGeometry();
		PopBasicPass();
		
		// Stop rendering to the mrts
		if( gbRenderMRT )
		{
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
			glViewport(0,0,gWindowWidth, gWindowHeight);
		}
	}
	
	glPopAttrib();
	
	// Begin rendering to the MRTs
	if( gbRenderMRT )
	{
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mrtframebuffer);
		glDrawBuffers(MRT_NUMBER, mrtTargets);
		glViewport(0,0,gWindowWidth, gWindowHeight);
	}
	
	// Post Framework Rendering
	// The baseapp derivation can do it's custom rendering
	// after all framework assisted rendering is done.
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	if( gApplication )
		gApplication->PostRender();
	glPopAttrib();
	
	// Stop rendering to the MRTs
	if( gbRenderMRT )
	{
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
		glViewport(0,0,gWindowWidth, gWindowHeight);
	}
	
	// Now we're completely finished rendering to the MRTs.
	// Render it as a screenspace quad to the real framebuffer
	// and allow the application derivation to do any post
	// processing.
	if( gbRenderMRT )
		RenderMRTPostProcess();
	
	// GUI Rendering
	UpdateFPS();
	if( gbRenderFPS )
		RenderFPS();
	if( gbRenderFPSGraph && gbRenderFPS )
		RenderFPSGraph();
	if( gbRenderHelp )
		RenderHelp();
	
	// Post Framework GUI Rendering
	// The application derivation can specify to
	// render to the framework after EVERYTHING
	// is done here.
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	if( gApplication )
		gApplication->PostGUIRender();
	glPopAttrib();
	
	// Done, pop transformations.
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	
	// Clear/flush
	if( BUFFER_FORMAT & GLUT_DOUBLE )
		glutSwapBuffers(); // implicitly calls glFlush()
	else
		glFlush(); 
}

/// RenderText is a utility function that sits on top of glut and
/// automatically positions and renders strings.
void RenderText(float x, float y, char* szString, bool bAutoProjection = true)
{
	// If autoprojection is true, it sets up
	// the projection matrix as Ortho.
	if( bAutoProjection )
	{
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		
		gluOrtho2D(0, gWindowWidth, gWindowHeight, 0);
	}
	
	// Clear any view-transformations
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glColor3f(1.0f, 1.0f, 1.0f);
	
	// Move the rendering position to the x,y coordinate passed in.
	glRasterPos2f(x, y);
	
	// Render the string
	char* c=szString;
	for(; *c != '\0'; c++)
	{
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *c);
	}

	// pop all changes
	
	glPopAttrib();
	glPopMatrix();
	
	if( bAutoProjection )
	{
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();	
	}
}

// FPS information
// some of the glut fps code from:
// http://www.lighthouse3d.com/opengl/glut/index.php?fps
// Most from Stephen Timothy Cooney.
float fps;
const unsigned int uiFPSHistoryCount = 100;
float pFPSHistory[uiFPSHistoryCount] = {0};
unsigned int uiFPSNowIndex = 0;
char szFPS[32] = {0};

/// UpdateFPS keeps track of how long between each render update is executed
/// and calculates the frames per second.
/// Called even if FPS isn't rendering so that it may store the FPS information.
void UpdateFPS()
{
	static bool bInitiatedFPS = false;
	if( !bInitiatedFPS )
	{
		// initiate
		fps = 0;
		szFPS[0] = 0;
		
		memset(pFPSHistory, 0, sizeof(float) * uiFPSHistoryCount);
		
		bInitiatedFPS = true;
	}
	
	static unsigned int frame=0, time=0, timebase=0;
	
	++frame;
	time=glutGet(GLUT_ELAPSED_TIME);
	
	if( time - timebase > 100 )
	{
		++uiFPSNowIndex;
		if( uiFPSNowIndex >= uiFPSHistoryCount )
			uiFPSNowIndex = 0;
		
		fps = frame * 1000.0f / (float)(time-timebase);
		pFPSHistory[uiFPSNowIndex] = fps;
		
		timebase = time;
		frame = 0;
		
		sprintf(szFPS, "%4.2f", fps);
	}
}

/// RenderFPS renders the FPS to the screen for
/// debugging purposes. Called in OnRender if
/// the RENDER_FPS definition is set to true.
void RenderFPS()
{	
	// Prep the projection as an ortho view
	// scaled to the screen size.
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0, gWindowWidth, gWindowHeight, 0);
	
	// Clear any view transformations.
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glColor3f(1.0f, 1.0f, 1.0f);
	
	// Set the starting render position.
	int xPos=10;
	int yPos=28;
	glRasterPos2f(xPos, yPos);
	
	// Render the FPS string.
	char* c=szFPS;
	for(; *c != '\0'; c++)
	{
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *c);
	}

	// Pop all changes.
	
	glPopAttrib();
	glPopMatrix();
	
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();	
}

/// Render a graph that shows the history of FPS changes.
/// Useful for debugging how changes in the render state
/// affects user experience.
void RenderFPSGraph()
{
	// Prep the projection to an ortho that
	// easily fits graph dimensions.
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	
	gluOrtho2D(-0.1f, 1.1f, -0.1f, 3.1f); // odd numbers but bent so the graph is drawn between 0-1 and it still fits on the screen
	

	// Clear all view transformations.
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	// Draw the graph box edge
	glBegin(GL_LINE_LOOP);
	glColor3f(1.0f, 1.0f, 1.0f);
	glVertex2f(0.0f, -0.05f);
	glVertex2f(1.0f, -0.05f);
	glVertex2f(1.0f, 1.05f);
	glVertex2f(0.0f, 1.05f);
	glEnd();
	
	// Draw a semi transparent graph
	// box background
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//
	glBegin(GL_QUADS);
	glColor4f(1.0f,1.0f,1.0f,0.2f);
	glVertex2f(0.0f, -0.05f);
	glVertex2f(1.0f, -0.05f);
	glVertex2f(1.0f, 1.05f);
	glVertex2f(0.0f, 1.05f);
	glEnd();
	
	// Lines that denote the lowest FPS point.
	// Sits just above the box base.
	glBegin(GL_LINES);
	glColor4f(1.0f,1.0f,1.0f,0.2f);
	glVertex2f(0.0f, 0.0f);
	glVertex2f(1.0f, 0.0f);
	glVertex2f(1.0f, 1.0f);
	glVertex2f(0.0f, 1.0f);
	glEnd();
	
	glDisable(GL_BLEND);

	// Determine the minimum and maximum fps
	// value so that we can scale the size of
	// the graph to fit.
	float fMinVal=pFPSHistory[0];
	float fMaxVal=pFPSHistory[0];
	for(unsigned int i=0; i<uiFPSHistoryCount; ++i )
	{
		if( pFPSHistory[i] < fMinVal )
			fMinVal = pFPSHistory[i];
		
		if( pFPSHistory[i] > fMaxVal )
		{
			fMaxVal = pFPSHistory[i];
		}
	}
	
	// hackery to push and pull
	// the graph within the box.
	fMinVal -= 10.0f;
	fMaxVal += 10.0f;
	if( fMinVal < 0.0f )
		fMinVal = 0.0f;
	
	glBegin(GL_LINE_STRIP);
	int iStart = uiFPSNowIndex;
	int i = iStart;
	int iFinish = i + 1;
	if( iFinish >= uiFPSHistoryCount )
		iFinish = 0;
	int counter = 0;
	// Step through the FPS history and draw the
	// actual graph.
	while( true )
	{
		float fFPS = pFPSHistory[i];
		
		// Change the color of the line depending
		// upon how low or high it is.
		if( fFPS >= GREAT_FPS )
			glColor3f(0.0f, 1.0f, 0.0f);
		else if ( fFPS <= BAD_FPS )
			glColor3f(1.0f, 0.0f, 0.0f);
		else
			glColor3f(1.0f,1.0f, 0.0f);
		
		// Figure out where we are drawing on the graph
		float xPos = 1.0f - (counter/(float)(uiFPSHistoryCount-1));
		float yPos = (fFPS - fMinVal)/(float)(fMaxVal-fMinVal);
		
		// add the line point.
		glVertex2f(xPos, yPos);
		
		// iterate
		++counter;
		--i;
		if( i< 0 )
			i = uiFPSHistoryCount-1;
		
		if( i == iStart )
			break;
	}
	glEnd();
	
	glPopAttrib();
	glPopMatrix();

	// Display the min and max as text
	char szFPSMin[32] = {0};
	char szFPSMax[32] = {0};
	sprintf(szFPSMin,"%4.2f",fMinVal);
	sprintf(szFPSMax,"%4.2f",fMaxVal);
	// render
	RenderText(1.01,0.9,szFPSMax,false);
	RenderText(1.01,0,szFPSMin,false);
	// re-render the current fps
	RenderText(1.01,0.45,szFPS,false);
	
	// pop all settings

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

/// Render any help text specified by the framework and the application instantiation.
void RenderHelp()
{
#define RH_EASYINC(val) val += 20;
	
	int TextPos = 70;

	// Render Default Help
	RenderText(10, TextPos, (char*)"H - Toggle Help");
	RH_EASYINC(TextPos);
	RenderText(10, TextPos, (char*)"F - Toggle Fullscreen");
	RH_EASYINC(TextPos);
	RenderText(10, TextPos, (char*)"P - Toggle Frames Per Second");
	RH_EASYINC(TextPos);
	if( gbRenderFPS )
	{
		RenderText(10, TextPos, (char*)"G - Toggle FPS Graph");
		RH_EASYINC(TextPos);
	}
	RenderText(10, TextPos, (char*)"U - Render Unhinged");
	RH_EASYINC(TextPos);
	RenderText(10, TextPos, (char*)"s - Render Shadows");
	RH_EASYINC(TextPos);
	RenderText(10, TextPos, (char*)"S - Render Shadowmap");
	RH_EASYINC(TextPos);
	RenderText(10, TextPos, (char*)"Q/Escape - Quit/Escape");
	
	// The application instantiation may specify
	// custom help. Render these one by one.
	if( gApplication )
	{
		unsigned int iNumHelp = gApplication->GetNumHelpItems();
		
		if( iNumHelp > 0 )
		{
			// Creating a space between default framework help and application specific help
			RH_EASYINC(TextPos);
		
			for(unsigned int i=0; i<iNumHelp; ++i)
			{
				RH_EASYINC(TextPos);
				RenderText(10, TextPos, gApplication->GetHelpItem(i));
			}
		}
	}
	
#undef RH_EASYINC
}

/// Render a quad on the screen displaying the shadowmap for debugging purposes.
void RenderShadowmap()
{
	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(-0.1f, 2.0f, -0.1f, 2.0f);
	
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	
	glDisable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, shadowmaptexture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
	
	glBegin(GL_QUADS);
	glColor3f(1.0f,1.0f,1.0f);
	
	glTexCoord2f(0.0f,0.0f);
	glVertex2f(0.0f, 0.0f);
	
	glTexCoord2f(1.0f,0.0f);
	glVertex2f(1.0f, 0.0f);
	
	glTexCoord2f(1.0f,1.0f);
	glVertex2f(1.0f, 1.0f);
	
	glTexCoord2f(0.0f,1.0f);
	glVertex2f(0.0f, 1.0f);
	
	glEnd();
	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

/// OnTimer automatically calls the timer functions
/// that are designated in the TIMERS array above.
/// plug your custom timer functions in there.
void OnTimer(int inValue)
{	
	// Go through each timer.
	unsigned int uiNumTimers = sizeof(TIMERS) / (sizeof(unsigned long long) * TIMERS_MEMBERS);
	for(unsigned int i=0; i<uiNumTimers; ++i)
	{
		unsigned int rate = TIMERS[i*TIMERS_MEMBERS+1];
		unsigned int h = (unsigned int)((1.0f/(float)rate)*1000);
		unsigned int value = (int)TIMERS[i*TIMERS_MEMBERS];
		
		if( inValue == value )
		{
			void (*TimerFunc)(float);
			TimerFunc = (OnTimerFunction)(void*)(TIMERS[i*TIMERS_MEMBERS+2]);
			
			TimerFunc(h);
			glutTimerFunc(h, OnTimer, (int)value);
		}
	}
}

/// OnReshape sets the OpenGL state up correctly whenever
/// the window resizes (this includes the first time
/// the window is created). You probably don't need
/// to edit this, but anything that would go into
/// initializing openGL should probably go in here.
void OnReshape(int width, int height)
{	
	ShutdownMRT();
	
	gWindowWidth = width;
	gWindowHeight = height;
	
	// have to reinitialize the MRTs so
	// that they match the screen size...
	InitiateMRT();
				   
	glViewport(0,0,width,height);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	gluPerspective((float)FIELD_OF_VIEW, (float)width / (float)height, (float)NEAR_BUFFER, (float)FAR_BUFFER);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	if( gApplication )
		gApplication->Reshape(width, height);
}

/// main plug for the program, it initiates glut and hooks up all the
/// standard operations functions. The defines pretty much automate
/// what functions are plugged in.
int main(int argc, char* argv[])
{
#if COPYRIGHT
	std::cout << APPNAME << "\n" << "Copyright (C) " << DEVNAME << ", " << DEVDATE << "\n";
#else
	std::cout << APPNAME << "\n" << DEVNAME << ", " << DEVDATE << "\n";
#endif

	for(unsigned int i=0; i<(unsigned int)argc; ++i)
	{
		std::cout << "Argument " << i << ": " << argv[i] << "\n";
	}
	
	// Start up glut
	glutInit(&argc, argv);
	
	// Prep glut
	glutInitDisplayMode(BUFFER_FORMAT);
	glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
	glutCreateWindow(APPNAME);
	
	// Hook up glut callback functions

#ifdef IDLE_FUNCTION
	glutIdleFunc(OnIdle);
#endif
	
#ifdef TIMER_FUNCTION
	unsigned int uiNumTimers = sizeof(TIMERS) / (sizeof(unsigned long long) * TIMERS_MEMBERS);

	std::cout << "NumTimers: " << uiNumTimers << "\n";
	
	for(unsigned int i=0; i<uiNumTimers; ++i)
	{
		unsigned int rate = TIMERS[i*TIMERS_MEMBERS+1];
		unsigned int h = (unsigned int)((1.0f/(float)rate)*1000);
		unsigned int value = (int)TIMERS[i*TIMERS_MEMBERS];

		std::cout << "Timer: " << i << " Rate: " << rate << " Step: " << h << " Value: " << value << "\n";
		
		glutTimerFunc(h, OnTimer, (int)value);
	}
#endif
	
#ifdef RENDER_FUNCTION
	glutDisplayFunc(RENDER_FUNCTION);
#endif
	
#ifdef RESHAPE_FUNCTION
	glutReshapeFunc(RESHAPE_FUNCTION);
#endif
	
#ifdef KEYBOARD_FUNCTION
	glutKeyboardFunc(KEYBOARD_FUNCTION);
#endif

#ifdef MOUSE_FUNCTION
	glutMouseFunc(MOUSE_FUNCTION);
#endif
	
#ifdef MOUSE_MOTION_FUNCTION
	glutMotionFunc(MOUSE_MOTION_FUNCTION);
#endif
	
	if( gApplication )
		gApplication->Initiate((unsigned int)argc, argv);

	InitiateGraphics();
	
	glutMainLoop();
	
	if( gApplication )
		gApplication->Shutdown();
	
	ShutdownGraphics();
	
	return 0;
}