You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

808 lines
32 KiB

#include "RenderingWindow.h"
#include "ShaderProgram.h"
#include "Inputs.h"
#include "STLMesh.h"
#include "OFFMesh.h"
#include "Material.h"
#include <iostream>
#include <set>
#include <string>
#include <chrono>
#include "ExternalLib/glad.h"
#include "ExternalLib/SOIL/inc/SOIL/SOIL.h"
#include "GLFW/glfw3.h"
#include "ExternalLib/glm/gtc/matrix_transform.hpp"
#include "ExternalLib/glm/gtc/type_ptr.hpp"
#include "ExternalLib/glm/glm.hpp"
#include "ExternalLib/glm/gtx/transform.hpp"
#include "ExternalLib/glm/gtx/euler_angles.hpp"
#include "Config.h"
#define DEBUG_RENDERING_WINDOW 1 // Allow things like shaders hot swaping, reloading
#define glInfo(a) std::cout << #a << ": " << glGetString(a) << std::endl
// BEGINNING OF MESS ZONE, here lies some global variables that should disapear over time as features get implemented
#include "Object.h"
GLint modelLoc; // model shader's variable / uniforms key
Object buddha, bunny, max, monkey, triceratops, floorSurface, mirror, mirrorFrame; // TODO : store those in a GraphScene
Material floorMat;
Mesh mirrorMesh, mirrorFrameMesh, floorMesh;
GLuint skyboxVAO, skyboxVBO;
GLuint cubemapTexture;
// This function is called on any openGL API error
void debug(GLenum, // source
GLenum, // type
GLuint, // id
GLenum, // severity
GLsizei, // length
const GLchar *message,
const void *) // userParam
{
std::set<std::string> debugGLMessages;
if(debugGLMessages.count(std::string(message)) == 0)
{
debugGLMessages.insert(std::string(message));
std::cout << "DEBUG : " << message << std::endl;
}
}
// To Be Moved with GraphScene
unsigned int loadCubemap(std::vector<std::string> faces)
{
/**
* \brief Load from file and in GPU memory skybox's textures
* \param faces <=> skybox's textures
*/
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++)
{
unsigned char *data = SOIL_load_image( faces[i].c_str(), &width, &height, &nrChannels, SOIL_LOAD_RGB);
if (data)
glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
);
else
std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
SOIL_free_image_data(data);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return textureID;
}
// END OF THE MESS ZONE
/******************
* Constructors *
******************/
RenderingWindow::RenderingWindow(const int width_, const int height_)
:width(width_), height(height_)
{
/**
* \brief RenderingWindow constructor, call glfwCreateWindow() for window and OpenGL context creation,
* set OpenGL options, read resources directories
* \param width <=> window's width
* height <=> window's height
*/
/*
* Loading Config File
*/
Config::LoadConfigFile();
/*
* Initialize glfw
*/
if (!glfwInit())
{
std::cerr << "Could not init glfw" << std::endl;
exit(1);
}
if (DEBUG_RENDERING_WINDOW)
{ // Enable opengl debug context => slow
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
}
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(width, height, "Embryo", nullptr, nullptr);
if (!window)
{
std::cerr << "Could not init window" << std::endl;
glfwTerminate();
exit(2);
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
if(!gladLoadGL())
{
std::cerr << "Something went wrong loading glad!" << std::endl;
exit(3);
}
/* set user-defined pointer to access non global structures and objects
* during callback functions
*/
glfwSetWindowUserPointer(window, this);
if (DEBUG_RENDERING_WINDOW)
{ // Now that the context is initialised, print some informations, and enable more DEBUG options
glInfo(GL_VENDOR);
glInfo(GL_RENDERER);
glInfo(GL_VERSION);
glInfo(GL_SHADING_LANGUAGE_VERSION);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // SYNCHRONOUS Debug => even slower but immediate
glDebugMessageCallback(debug, nullptr); // link debug function to be called at every
// TODO : Investigate why it's causing OpenGL ERROR on some setups
}
/*
* Enable Opengl Tests and global settings
*/
glEnable(GL_DEPTH_TEST);
/*
* Scan MAIN_SHADERS_BASE_DIRECTORY looking for every list of Shader folder
*/
tinydir_open(&shaderBaseDirectory, std::string(static_cast<std::string>(Config::GetValue("shaders")) + "MainShaders/").c_str());
while (shaderBaseDirectory.has_next)
{
tinydir_file shaderFolderFile;
tinydir_readfile(&shaderBaseDirectory, &shaderFolderFile);
// if sharedFolder is a folder (apart from "." and "..")
if ( shaderFolderFile.is_dir && strcmp(shaderFolderFile.name, ".") && strcmp(shaderFolderFile.name, "..") )
// push shader folder path on the list
shaderFolderPathList.push_back(static_cast<std::string>(Config::GetValue("shaders")) + "MainShaders/" + std::string(shaderFolderFile.name) + "/");
tinydir_next(&shaderBaseDirectory);
}
tinydir_close(&shaderBaseDirectory);
if (shaderFolderPathList.size() == 0)
{
std::cerr << "No shader could be found, check the \"shaders\" folder next to the .exe" << std::endl;
exit(5);
}
// set the currentShaderFolderPath iterator to the first Shader of the list
currentShaderFolderPath = shaderFolderPathList.begin();
}
/******************
* Listeners *
******************/
void key_callback(GLFWwindow* window, int key, int /*scancode*/, int action, int /*mods*/)
{
/**
* \brief glfw method used to bind inputs to actions
* \param window <=> glfw's window instance
* key <=> input, key pressed
* action <=> type of input (press, hold, release)
*/
RenderingWindow *thisRW = static_cast<RenderingWindow *>(glfwGetWindowUserPointer(window));
/**********************
* SHADERS CONTROLS *
* hotswap, reload *
* FOR DEBUG ONLY *
**********************/
if (DEBUG_RENDERING_WINDOW)
{
if (key == GLFW_KEY_N && action == GLFW_PRESS)
{ // load next batch of shaders
// Set currentShaderFolderPath / iterator to the next value in the list
thisRW->currentShaderFolderPath++;
// verify end-of-vector situation / loop iterator
if (thisRW->currentShaderFolderPath == thisRW->shaderFolderPathList.end() )
thisRW->currentShaderFolderPath = thisRW->shaderFolderPathList.begin(); // get first ShaderFolder
// Load new Shaders
thisRW->mainShaderProgram.attachShaders(*(thisRW->currentShaderFolderPath));
thisRW->mainShaderProgram.linkProgram();
}
if (key == GLFW_KEY_P && action == GLFW_PRESS)
{ // load previous batch of shaders
// Set currentShaderFolderPath / iterator to the next value in the list
thisRW->currentShaderFolderPath--;
// verify end-of-vector situation / loop iterator
if (thisRW->currentShaderFolderPath == thisRW->shaderFolderPathList.end() )
thisRW->currentShaderFolderPath = --(thisRW->shaderFolderPathList.end()); // get last ShaderFolder
// Load new Shaders
thisRW->mainShaderProgram.attachShaders(*(thisRW->currentShaderFolderPath));
thisRW->mainShaderProgram.linkProgram();
}
if (key == GLFW_KEY_R && action == GLFW_PRESS)
{
// reload shaders from source
thisRW->mainShaderProgram.reload();
}
}
/*
* CAMERA POSITION CONTROLS
*/
if ( action == GLFW_REPEAT )
return;
switch (key)
{
/* handle the table of inputs based on GLFW_PRESS and GLFW_RELEASE
* TO THINK : possible states should be : released, just pressed, held down (, just released ?)
* DO NOT USE GLFW_REPEAT as it is OS governed by the OS
* ( => thus creating text editor feeling in windows with delay between "just pressed" and "held down" event
*/
case GLFW_KEY_W : {Inputs::bForward = action; break;}
case GLFW_KEY_S : {Inputs::bBackward = action; break;}
case GLFW_KEY_A : {Inputs::bLeft = action; break;}
case GLFW_KEY_D : {Inputs::bRight = action; break;}
case GLFW_KEY_Q : {Inputs::bYawLeft = action; break;}
case GLFW_KEY_E : {Inputs::bYawRight = action; break;}
}
/*
* EXIT
*/
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
exit(0);
}
void RenderingWindow::handleKeyInputs()
{
/**
* \brief Update mainCamera / player's Camera position according to inputs
* TO THINK : where to put this method as it's using glfwPollEvents()
* RenderingWindow isn't really the correct place to get a input method
*/
glfwPollEvents(); // treat in between frames pending inputs
GLfloat cameraNormalizedStep = mainCamera.cameraStep * deltaTime;
GLfloat cameraNormalizedRollStep = mainCamera.cameraRollStep * deltaTime;
if (Inputs::bForward)
{
mainCamera.cameraPos += glm::normalize(mainCamera.cameraForward) * cameraNormalizedStep;
mainCamera.actualizeViewMatrix();
}
if (Inputs::bBackward)
{
mainCamera.cameraPos -= glm::normalize(mainCamera.cameraForward)* cameraNormalizedStep;
mainCamera.actualizeViewMatrix();
}
if (Inputs::bLeft)
{
mainCamera.cameraPos -= glm::normalize(glm::cross(mainCamera.cameraForward, mainCamera.cameraUp)) * cameraNormalizedStep;
mainCamera.actualizeViewMatrix();
}
if (Inputs::bRight)
{
mainCamera.cameraPos += glm::normalize(glm::cross(mainCamera.cameraForward, mainCamera.cameraUp)) * cameraNormalizedStep;
mainCamera.actualizeViewMatrix();
}
if (Inputs::bYawLeft)
{
mainCamera.cameraRoll += cameraNormalizedRollStep; // TODO loop values around 2pi
mainCamera.actualizeViewMatrix();
}
if (Inputs::bYawRight)
{
mainCamera.cameraRoll -= cameraNormalizedRollStep;
mainCamera.actualizeViewMatrix();
}
}
static void cursor_position_callback(GLFWwindow* window, double xPos, double yPos)
{
/**
* \brief glfw's mouseListener
* \param window <=> glfw's window instance
* xPos <=> mouse x posiition
* yPos <=> mouse y position
* TO THINK : where to put it as it's using glfwPollEvents() and it's handling inputs ...
* RenderingWindow doesn't sound like a place to search for inputs
*/
RenderingWindow *thisRW = static_cast<RenderingWindow *>(glfwGetWindowUserPointer(window));
float xMovePitch = (thisRW->mainCamera.invertXAxis) ? (2.0f*glm::pi<float>()/100.0f * thisRW->mainCamera.xAxisSensitivity * static_cast<float>(xPos))
: -(2.0f*glm::pi<float>()/100.0f * thisRW->mainCamera.xAxisSensitivity * static_cast<float>(xPos));
float yMoveYaw = (thisRW->mainCamera.invertYAxis) ? -(glm::pi<float>()/100.0f * thisRW->mainCamera.yAxisSensitivity * static_cast<float>(yPos))
: (glm::pi<float>()/100.0f * thisRW->mainCamera.yAxisSensitivity * static_cast<float>(yPos));
thisRW->mainCamera.cameraPitch = (thisRW->mainCamera.cameraPitch+xMovePitch);
if ( !(thisRW->mainCamera.cameraYaw+yMoveYaw > glm::pi<float>() || thisRW->mainCamera.cameraYaw+yMoveYaw < 0)) // prevent Y flip
thisRW->mainCamera.cameraYaw = (thisRW->mainCamera.cameraYaw+yMoveYaw);
thisRW->mainCamera.actualizeViewMatrix();
glfwSetCursorPos(window, 0, 0); // reset postion values => avoid overflow
}
void scroll_callback(GLFWwindow* window, double /*xoffset*/, double yoffset)
{
/**
* \brief Update mainCamera / player's Camera FOV according to mouse scroll inputs
* \param window <=> glfw's window instance
* yoffset <=> mousescroll input
* TO THINK : where to put it as it's using glfwPollEvents() yadi yada, not a correct place to handle inputs
*/
RenderingWindow *thisRW = static_cast<RenderingWindow *>(glfwGetWindowUserPointer(window));
float scrollInput = static_cast<float>(yoffset) * thisRW->mainCamera.FOVScrollStep;
// modifying perspective for a zoom effect
if( thisRW->mainCamera.FOV-scrollInput >= 30.0f
&& thisRW->mainCamera.FOV-scrollInput <= 120.0f)
thisRW->mainCamera.FOV -= scrollInput;
thisRW->mainCamera.actualizeProjectionMatrix();
}
void window_size_callback(GLFWwindow* window, int width_, int height_)
{
/**
* \brief glfw method, Update mainCamera / player's Camera projection matrix according to window's resize events
* \param width <=> window's new width
* height <=> window's new height
*/
RenderingWindow *thisRW = static_cast<RenderingWindow *>(glfwGetWindowUserPointer(window));
thisRW->width = width_;
thisRW->height = height_;
thisRW->mainCamera.actualizeProjectionMatrix();
}
/******************
* Methods *
******************/
void RenderingWindow::init()
{
/**
* \brief Handle the initialization of everything non OpenGL related
* for now it mainly handles the scene initialization;
* loading objects, cameras, Shaders, Skybox
* Setting listeners, etc
* ! OpenGL Context already initialized by Constructor !
*/
/****************************************************************
* *
* SCENE CONSTRUCTION *
* (here for now) *
* *
* SIDENOTE : For now the transformation order is as follow *
* Translation -> Rotation -> Scale *
****************************************************************/
/*******************
* Loading Objects *
*******************/
// LOADING External mesh files
buddha = Object(glm::vec3(glm::vec3(-6.0f, 1.3f, 1.0f)), // Translation
glm::vec3(90.0f, 90.0f, 0.0f), // Rotation
glm::vec3(glm::vec3(2.0f, 2.0f, 2.0f)), // Scale
OFFMesh(static_cast<std::string>(Config::GetValue("models")) + "buddha.off"),
ObjectType::SimpleMesh);
bunny = Object(glm::vec3(-3.0f, 1.3f, 1.0f),
glm::vec3(90.0f, 90.0f, 0.0f),
glm::vec3(1.5f, 1.5f, 1.5f),
OFFMesh(static_cast<std::string>(Config::GetValue("models")) + "bunny.off"),
ObjectType::SimpleMesh);
max = Object(glm::vec3(-0.0f, 1.3f, 1.0f),
glm::vec3(90.0f, 90.0f, 0.0f),
glm::vec3(1.0f, 1.0f, 1.0f),
OFFMesh(static_cast<std::string>(Config::GetValue("models")) + "max.off"),
ObjectType::SimpleMesh);
monkey = Object(glm::vec3(3.0f, 1.3f, 1.0f),
glm::vec3(90.0f, 90.0f, 0.0f),
glm::vec3(0.5f, 0.5f, 0.5f),
STLMesh(static_cast<std::string>(Config::GetValue("models")) + "monkey.stl"),
ObjectType::SimpleMesh);
triceratops = Object(glm::vec3(6.0f, 1.3f, 1.0f),
glm::vec3(90.0f, 90.0f, 0.0f),
glm::vec3(1.5f, 1.5f, 1.5f),
OFFMesh(static_cast<std::string>(Config::GetValue("models")) + "triceratops.off"),
ObjectType::SimpleMesh);
// END OF External mesh files
// BEGINNING OF FLOOR
// Triangle 1
floorMesh.vertices.push_back({ {-5.0f, 0.0f, 5.0f}, {0.0f, 1.0f, 0.0f} });
floorMesh.vertices.push_back({ { 5.0f, 0.0f, 5.0f}, {0.0f, 1.0f, 0.0f} });
floorMesh.vertices.push_back({ { 5.0f, 0.0f, -5.0f}, {0.0f, 1.0f, 0.0f} });
// Triangle 2
floorMesh.vertices.push_back({ {-5.0f, 0.0f, 5.0f}, {0.0f, 1.0f, 0.0f} });
floorMesh.vertices.push_back({ { 5.0f, 0.0f, -5.0f}, {0.0f, 1.0f, 0.0f} });
floorMesh.vertices.push_back({ {-5.0f, 0.0f, -5.0f}, {0.0f, 1.0f, 0.0f} });
floorSurface = Object(glm::vec3(0.0f, -1.0f, 6.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(1.2f, 1.0f, 1.0f),
floorMesh,
ObjectType::SimpleMesh);
// Floor Material // TODO : reimplement correctly Material and Object in order to add a BufferedObject to floor
// in order to stuff in the texCoords
// float floorMatTexCoords [] =
// {
// -1.0f, 1.0f,
// 1.0f, 1.0f,
// 1.0f, -1.0f,
// -1.0f, 1.0f,
// 1.0f, -1.0f,
// -1.0f, -1.0f
// };
// glBindVertexArray(floorSurface.getVAO());
// // texture coord attribute
// glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
// glEnableVertexAttribArray(2);
// glBindVertexArray(0);
// floorMat = Material(MaterialType::Texture, (RESOURCES_DIRECTORY+"container.jpg").c_str(), "containerTexture" );
// END OF FLOOR
// BEGINNING OF MIRROR
// Triangle 1
mirrorMesh.vertices.push_back({ { -0.9f, 0.9f, 0.0f },{ 0.0f, 0.0f, 1.0f } });
mirrorMesh.vertices.push_back({ { 0.9f, 0.9f, 0.0f },{ 0.0f, 0.0f, 1.0f } });
mirrorMesh.vertices.push_back({ { 0.9f, -0.9f, 0.0f },{ 0.0f, 0.0f, 1.0f } });
// Triangle 2
mirrorMesh.vertices.push_back({ {-0.9f, 0.9f, 0.0f}, {0.0f, 0.0f, 1.0f} });
mirrorMesh.vertices.push_back({ {0.9f, -0.9f, 0.0f}, {0.0f, 0.0f, 1.0f} });
mirrorMesh.vertices.push_back({ {-0.9f, -0.9f, 0.0f}, {0.0f, 0.0f, 1.0f} });
mirror = Object(glm::vec3(0.0f, 0.0f, -0.0f), // ignored for now (using mirrorMesh informations)
glm::vec3(0.0f, 0.0f, -0.0f), // ignored for now (using mirrorMesh informations)
glm::vec3(0.0f, 0.0f, -0.0f), // ignored for now (using mirrorMesh informations)
mirrorMesh,
ObjectType::Mirror);
// END OF MIRROR
// MIRROR FRAME (AND ITS BACK)
mirrorFrameMesh.vertices.push_back({ {-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} });
mirrorFrameMesh.vertices.push_back({ { 1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} });
mirrorFrameMesh.vertices.push_back({ { 1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} });
mirrorFrameMesh.vertices.push_back({ {-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} });
mirrorFrameMesh.vertices.push_back({ {-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} });
mirrorFrameMesh.vertices.push_back({ { 1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} });
mirrorFrame = Object(glm::vec3(0.0f, 0.0f, -0.0001f),
glm::vec3(135.0f, 0.0f, 0.0f),
glm::vec3(8.0f, 1.0f, 1.0f),
mirrorFrameMesh,
ObjectType::SimpleMesh);
// END OF MIRROR FRAME
/**************************************************************************
* Loading Shaders, for now mainShaderProgram declare its own glProgram *
**************************************************************************/
mainShaderProgram.initGL();
mainShaderProgram.attachShaders(*currentShaderFolderPath);
// load every shader in the first subFolder of MAIN_SHADERS_BASE_DIRECTORY
mainShaderProgram.linkProgram();
mainShaderProgram.enable();
skyboxShaderProgram.initGL();
skyboxShaderProgram.attachShaders(static_cast<std::string>(Config::GetValue("shaders")) + "SkyboxShaders/basicSkybox/");
skyboxShaderProgram.linkProgram();
skyboxShaderProgram.enable();
floorShaderProgram.initGL();
floorShaderProgram.attachShaders(static_cast<std::string>(Config::GetValue("shaders")) + "FloorShaders/basicFloor/");
floorShaderProgram.linkProgram();
floorShaderProgram.enable();
/*********************
* Loading Cameras *
*********************/
mainCamera = Camera(&width, &height, 0.1f, 100.0f);
mainCamera.setPosition(glm::vec3(0.0f, 0.0f, 5.0f));
/*********************
* Loading Skybox *
*********************/
// Setting skybox VAO,VBO
float skyboxVertices[] =
{
// positions
-1.f, 1.f, -1.f,
-1.f, -1.f, -1.f,
1.f, -1.f, -1.f,
1.f, -1.f, -1.f,
1.f, 1.f, -1.f,
-1.f, 1.f, -1.f,
-1.f, -1.f, 1.f,
-1.f, -1.f, -1.f,
-1.f, 1.f, -1.f,
-1.f, 1.f, -1.f,
-1.f, 1.f, 1.f,
-1.f, -1.f, 1.f,
1.f, -1.f, -1.f,
1.f, -1.f, 1.f,
1.f, 1.f, 1.f,
1.f, 1.f, 1.f,
1.f, 1.f, -1.f,
1.f, -1.f, -1.f,
-1.f, -1.f, 1.f,
-1.f, 1.f, 1.f,
1.f, 1.f, 1.f,
1.f, 1.f, 1.f,
1.f, -1.f, 1.f,
-1.f, -1.f, 1.f,
-1.f, 1.f, -1.f,
1.f, 1.f, -1.f,
1.f, 1.f, 1.f,
1.f, 1.f, 1.f,
-1.f, 1.f, 1.f,
-1.f, 1.f, -1.f,
-1.f, -1.f, -1.f,
-1.f, -1.f, 1.f,
1.f, -1.f, -1.f,
1.f, -1.f, -1.f,
-1.f, -1.f, 1.f,
1.f, -1.f, 1.f
};
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices ), &skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
// Loading textures
std::vector<std::string> faces
{
std::string(static_cast<std::string>(Config::GetValue("resources")) + "/ame_desert/desertsky_rt.tga"),
std::string(static_cast<std::string>(Config::GetValue("resources")) + "/ame_desert/desertsky_lf.tga"),
std::string(static_cast<std::string>(Config::GetValue("resources")) + "/ame_desert/desertsky_up.tga"),
std::string(static_cast<std::string>(Config::GetValue("resources")) + "/ame_desert/desertsky_dn.tga"),
std::string(static_cast<std::string>(Config::GetValue("resources")) + "/ame_desert/desertsky_bk.tga"),
std::string(static_cast<std::string>(Config::GetValue("resources")) + "/ame_desert/desertsky_ft.tga")
};
cubemapTexture = loadCubemap(faces);
/******************************************
* Setting glfw keys and mouse listeners *
******************************************/
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Lock MouseCursor
glfwSetCursorPos (window, 0, 0);
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetWindowSizeCallback(window, window_size_callback);
/*****************
* Inializeing lastTime & deltaTime
*****************/
lastTime = static_cast<float>(glfwGetTime());
deltaTime = 0.0f;
}
void RenderingWindow::show()
{
/**
* \brief here's where the magic happens, everything is initialized (except OpenGL context)
* Thus we start the OpenGL loop
*/
// Initialize openGL variables, construct scenes and such
init();
// Handle glfw part of rendering's loop
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
int width, height;
glfwGetFramebufferSize(window, &width, &height);
render();
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events (like key inputs) */
glfwPollEvents();
}
glfwTerminate();
}
void RenderingWindow::render()
{
/**
* \brief part of the OpenGL rendering loop, called to render every frame
* handles input, time and uniform management
* also calls the scene rendering method
*/
/***********************************
* Hnadle current & pending inputs *
***********************************/
handleKeyInputs();
/*****************************
* Managing Time & deltaTime *
*****************************/
executionTime = static_cast<float>(glfwGetTime()); // Time since the beginning
deltaTime = executionTime - lastTime; // Time since last frame rendering
lastTime = executionTime; // Actualize last time for the computation of next deltaTime
// Modifiy/Actualize width, height and viewport according to current window side
glfwGetWindowSize(window, &width, &height);
glViewport(0, 0, width, height);
// Setting purple as "default" color
glClearColor(184/256.f, 48/256.f, 179/256.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT /*| GL_STENCIL_BUFFER_BIT*/);
/************************************************************************
* SENDING OVER SHADERS GLOBAL VALUES *
* !!! GLUT / TODO FIX IT !!! *
* For now due to the possibility of hotswaping shaders/ShaderPrograms *
* We need to send every uniform any shader/ShaderProgram can ever need *
* .... Pretty stup**, when got time, add Uniforms to ShaderProgram *
* and only send Uniform on hotswap *
************************************************************************/
// TODO : when got time find a way to get Uniform value saved across multiple shaders
// breaking case example (1) : 1/ init light in the init() for the first shader
// 2/ Load other shader
// 3/ Cry in despair wondering why lightPosition isn't actualized / is in wrong space / etc
// example (2) : 1/ send a uniform time
// 2/ change shader
// 3/ no more time in shader
// TODO : use NVAPI to always use graphic card instead of integrated chipset
// that will spit out random OpenGL errors because of its openGL implementation
// TODO : investigate on why sometime NVIDIA drivers loose my uniform into oblivion
glProgramUniform1f(mainShaderProgram.getId(), 4, executionTime);
glProgramUniform3f(mainShaderProgram.getId(), 5,
std::fabs(std::cos(executionTime)),
std::fabs(std::cos(executionTime)+0.3f),
std::fabs(std::cos(executionTime)+0.6f)
);
// Reminder : max position <=> middle model position : (-0.0f, 1.3f, 1.0f)
glProgramUniform3f(mainShaderProgram.getId(), 6, 0.0f, 30.0f, -1.0f); // Light position ( Causing OpenGL error on non lighted shader, duh )
/**************************************
* DRAW SCENE *
**************************************/
drawScene(mainCamera, 1);
}
void RenderingWindow::drawScene(Camera& camera, int nbRecursions)
{
/**
* \brief To be replaced method... There lies what should be in a GraphScene.draw() method
* binding camera, every object's VAO and shaderProgram then drawing said object
* (also handle reflection loop draw() loop)
*/
/**********************
* RENDERING SKYBOX *
**********************/
glUseProgram(skyboxShaderProgram.getId());
camera.bindToShaderProgram(skyboxShaderProgram); // Send Camera's view and projection on GC
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glClear(GL_DEPTH_BUFFER_BIT); // clear depth, to make sure no fragment will be hidden behind the skybox
/****************************
* RENDERING MAIN OBJECTS *
****************************/
glUseProgram(floorShaderProgram.getId());
camera.bindToShaderProgram(floorShaderProgram); // Send Camera's view and projection on GC
floorSurface.Draw(floorShaderProgram);
glUseProgram(mainShaderProgram.getId());
camera.bindToShaderProgram(mainShaderProgram); // Send Camera's view and projection on GC
// BEGIN Buddha
buddha.setRotation(glm::vec3(90.0f, 90.0f, 0.0f)*executionTime*1.2f); // Set dynamic rotation
buddha.Draw(mainShaderProgram);
// END Buddha
// BEGIN Bunny
bunny.setRotation(glm::vec3(90.0f, 90.0f, 0.0f)*executionTime*1.2f); // Set dynamic rotation
bunny.Draw(mainShaderProgram);
// END Bunny
// BEGIN Max
max.setRotation(glm::vec3(90.0f, 90.0f, 0.0f)*executionTime*1.2f); // Set dynamic rotation
max.Draw(mainShaderProgram);
// END Max
// BEGIN Monkey
monkey.setRotation(glm::vec3(90.0f, 90.0f, 0.0f)*executionTime*1.2f); // Set dynamic rotation
monkey.Draw(mainShaderProgram);
// END Monkey
// BEGIN Triceratops
triceratops.setRotation(glm::vec3(90.0f, 90.0f, 0.0f)*executionTime*1.2f); // Set dynamic rotation
triceratops.Draw(mainShaderProgram);
// END Triceratops
/*******************************************************
* RENDERING MIRRORS, PORTALS, ANYTHING ANOTHER PASS *
*******************************************************/
if ( nbRecursions )
{
// BEGIN Mirror frame
mirrorFrame.Draw(mainShaderProgram);
// END Mirror frame
// BEGIN Mirror (and reflection)
// mirror.Draw(mainShaderProgram);
// Camera mirroredCamera = camera.mirrorCamera(mirror.getModel(), glm::vec4(mirror.getRotation().x, mirror.getRotation().y, -mirror.getRotation().z, 0.0f), glm::vec4(0,0,0,0));
// drawScene(mirroredCamera, nbRecursions-1);
// Can't work like that because of stencil being turned off by mirror.Draw() => need to wait for graphScene in order to correctly make a generic draw method
// // END Mirror
// BEGIN Mirror (and reflection)
glBindVertexArray(mirror.getVAO());
/*** Draw the mirror ***/
glEnable(GL_STENCIL_TEST); // Just to be safe let's enable and disable stencil only for mirror
glStencilFunc(GL_ALWAYS, 1, 0xFF); // Set stencil to a full 1 grid
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP);
glStencilMask(0xFF); // Allow full write
glDepthMask(GL_FALSE); // Turn off depthTesting
glClear(GL_STENCIL_BUFFER_BIT); // Clear stencil
/* Model Matrix NODE SPECIFIC */
/* Allow conversion from LocalSpace To WorldSpace coordinates */
glm::mat4 modelMirror = glm::translate(mirrorFrame.getModel(), glm::vec3(0.0f, 0.0f, -0.001f)); // Shift it a little to avoid Z fighting
glProgramUniformMatrix4fv(mainShaderProgram.getId(), modelLoc, 1, GL_FALSE, glm::value_ptr(modelMirror));
glDrawArrays(GL_TRIANGLES, 0, static_cast<int>(mirrorMesh.vertices.size())); // Draw it
/*** Draw the reflection ***/
glStencilFunc(GL_EQUAL, 1, 0xFF); // Set stencil to draw only if 1 <=> where mirror has been drew before
glStencilMask(0x00); // Disable stencil writing
glDepthMask(GL_TRUE); // Turn back on depthTesting
glClear(GL_DEPTH_BUFFER_BIT); // Clear Depth buffer
Camera mirroredCamera = camera.mirrorCamera(modelMirror, glm::vec4(mirror.getRotation().x, mirror.getRotation().y, -mirror.getRotation().z, 0.0f), glm::vec4(0,0,0,0));
drawScene(mirroredCamera, nbRecursions - 1);
glDisable(GL_STENCIL_TEST);
glBindVertexArray(0);
// END Mirror reflection
}
glUseProgram(0);
}