//Copyright (C) 2010-2012 by Jason L. McKesson
//This file is licensed under the MIT License.
#include <string>
#include <vector>
#include <stack>
#include <math.h>
#include <stdio.h>
#include <glload/gl_3_3.h>
#include <glutil/glutil.h>
#include <GL/freeglut.h>
#include "../framework/framework.h"
#include "../framework/Mesh.h"
#include "../framework/MousePole.h"
#include "../framework/Timer.h"
#include "../framework/UniformBlockArray.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
struct ProgramMeshData
{
GLuint theProgram;
GLuint modelToCameraMatrixUnif;
GLuint normalModelToCameraMatrixUnif;
};
struct ProgramImposData
{
GLuint theProgram;
GLuint sphereRadiusUnif;
GLuint cameraSpherePosUnif;
};
struct UnlitProgData
{
GLuint theProgram;
GLuint objectColorUnif;
GLuint modelToCameraMatrixUnif;
};
float g_fzNear = 1.0f;
float g_fzFar = 1000.0f;
enum Impostors
{
IMP_BASIC,
IMP_PERSPECTIVE,
IMP_DEPTH,
IMP_NUM_IMPOSTORS,
};
ProgramMeshData g_litMeshProg;
ProgramImposData g_litImpProgs[IMP_NUM_IMPOSTORS];
UnlitProgData g_Unlit;
const int g_materialBlockIndex = 0;
const int g_lightBlockIndex = 1;
const int g_projectionBlockIndex = 2;
UnlitProgData LoadUnlitProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
{
std::vector<GLuint> shaderList;
shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
UnlitProgData data;
data.theProgram = Framework::CreateProgram(shaderList);
data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");
GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
return data;
}
ProgramMeshData LoadLitMeshProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
{
std::vector<GLuint> shaderList;
shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
ProgramMeshData data;
data.theProgram = Framework::CreateProgram(shaderList);
data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");
GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
return data;
}
ProgramImposData LoadLitImposProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
{
std::vector<GLuint> shaderList;
shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
ProgramImposData data;
data.theProgram = Framework::CreateProgram(shaderList);
data.sphereRadiusUnif = glGetUniformLocation(data.theProgram, "sphereRadius");
data.cameraSpherePosUnif = glGetUniformLocation(data.theProgram, "cameraSpherePos");
GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
return data;
}
const char *g_impShaderNames[IMP_NUM_IMPOSTORS * 2] =
{
"BasicImpostor.vert", "BasicImpostor.frag",
"PerspImpostor.vert", "PerspImpostor.frag",
"DepthImpostor.vert", "DepthImpostor.frag",
};
void InitializePrograms()
{
g_litMeshProg = LoadLitMeshProgram("PN.vert", "Lighting.frag");
for(int iLoop = 0; iLoop < IMP_NUM_IMPOSTORS; iLoop++)
{
g_litImpProgs[iLoop] = LoadLitImposProgram(
g_impShaderNames[iLoop * 2], g_impShaderNames[iLoop * 2 + 1]);
}
g_Unlit = LoadUnlitProgram("Unlit.vert", "Unlit.frag");
}
///////////////////////////////////////////////
// View/Object Setup
glutil::ViewData g_initialViewData =
{
glm::vec3(0.0f, 30.0f, 25.0f),
glm::fquat(0.92387953f, 0.3826834f, 0.0f, 0.0f),
10.0f,
0.0f
};
glutil::ViewScale g_viewScale =
{
3.0f, 70.0f,
3.5f, 1.5f,
5.0f, 1.0f,
90.0f/250.0f
};
glutil::ViewPole g_viewPole = glutil::ViewPole(g_initialViewData,
g_viewScale, glutil::MB_LEFT_BTN);
namespace
{
void MouseMotion(int x, int y)
{
Framework::ForwardMouseMotion(g_viewPole, x, y);
glutPostRedisplay();
}
void MouseButton(int button, int state, int x, int y)
{
Framework::ForwardMouseButton(g_viewPole, button, state, x, y);
glutPostRedisplay();
}
void MouseWheel(int wheel, int direction, int x, int y)
{
Framework::ForwardMouseWheel(g_viewPole, wheel, direction, x, y);
glutPostRedisplay();
}
}
struct ProjectionBlock
{
glm::mat4 cameraToClipMatrix;
};
struct PerLight
{
glm::vec4 cameraSpaceLightPos;
glm::vec4 lightIntensity;
};
const int NUMBER_OF_LIGHTS = 2;
struct LightBlock
{
glm::vec4 ambientIntensity;
float lightAttenuation;
float padding[3];
PerLight lights[NUMBER_OF_LIGHTS];
};
struct MaterialBlock
{
glm::vec4 diffuseColor;
glm::vec4 specularColor;
float specularShininess;
float padding[3];
};
Framework::Mesh *g_pPlaneMesh = NULL;
Framework::Mesh *g_pSphereMesh = NULL;
Framework::Mesh *g_pCubeMesh = NULL;
GLuint g_lightUniformBuffer = 0;
GLuint g_projectionUniformBuffer = 0;
GLuint g_materialUniformBuffer = 0;
int g_materialBlockOffset = 0;
enum MaterialNames
{
MTL_TERRAIN = 0,
MTL_BLUE_SHINY,
MTL_GOLD_METAL,
MTL_DULL_GREY,
MTL_BLACK_SHINY,
NUM_MATERIALS,
};
void CreateMaterials()
{
Framework::UniformBlockArray<MaterialBlock, NUM_MATERIALS> ubArray;
g_materialBlockOffset = ubArray.GetArrayOffset();
MaterialBlock mtl;
mtl.diffuseColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
mtl.specularColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
mtl.specularShininess = 0.6f;
ubArray[MTL_TERRAIN] = mtl;
mtl.diffuseColor = glm::vec4(0.1f, 0.1f, 0.8f, 1.0f);
mtl.specularColor = glm::vec4(0.8f, 0.8f, 0.8f, 1.0f);
mtl.specularShininess = 0.1f;
ubArray[MTL_BLUE_SHINY] = mtl;
mtl.diffuseColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f);
mtl.specularColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f) * 0.75;
mtl.specularShininess = 0.18f;
ubArray[MTL_GOLD_METAL] = mtl;
mtl.diffuseColor = glm::vec4(0.4f, 0.4f, 0.4f, 1.0f);
mtl.specularColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f);
mtl.specularShininess = 0.8f;
ubArray[MTL_DULL_GREY] = mtl;
mtl.diffuseColor = glm::vec4(0.05f, 0.05f, 0.05f, 1.0f);
mtl.specularColor = glm::vec4(0.95f, 0.95f, 0.95f, 1.0f);
mtl.specularShininess = 0.3f;
ubArray[MTL_BLACK_SHINY] = mtl;
g_materialUniformBuffer = ubArray.CreateBufferObject();
}
GLuint g_imposterVAO;
//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
void init()
{
InitializePrograms();
try
{
g_pPlaneMesh = new Framework::Mesh("LargePlane.xml"