//////////////////////////////////////////////////////////////////////////////
/// \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;
}