Files
CDAG/Research/Renderer.cpp

1455 lines
48 KiB
C++

#define GLM_FORCE_RADIANS
#include "Renderer.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/stat.h>
#endif
#include "inc/gl/glew.h"
#include "inc/gl/glfw3.h"
#include "inc/glm/glm.hpp"
#include <fstream>
#include <algorithm>
#include <time.h>
#include <regex>
#include <thread>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
#include "inc/glm/gtc/matrix_transform.hpp"
#include "inc/lodepng/lodepng.h"
#include "core/PathHelper.h"
#include "core/Defines.h"
#include "core/Camera.h"
#include "core/Controls.h"
#include "core/OctreeBuilder/OctreeLoader.h"
#include "core/OctreeBuilder/TreeTypeParser.h"
#include "core/Serializer.h"
#include "core/MathHelper.h"
#include "scene/DirectionalLight.h"
#include "scene/PNG.h"
#include "scene/ObjLoader.h"
#include "shaders/ShaderLoader.h"
#include "PropertyLoader.h"
Renderer* Renderer::mInstance = NULL;
const unsigned32 GetTextureUnit(unsigned32 index)
{
switch (index)
{
case 0: return GL_TEXTURE0;
case 1: return GL_TEXTURE1;
case 2: return GL_TEXTURE2;
case 3: return GL_TEXTURE3;
case 4: return GL_TEXTURE4;
case 5: return GL_TEXTURE5;
case 6: return GL_TEXTURE6;
case 7: return GL_TEXTURE7;
case 8: return GL_TEXTURE8;
case 9: return GL_TEXTURE9;
case 10: return GL_TEXTURE10;
case 11: return GL_TEXTURE11;
case 12: return GL_TEXTURE12;
case 13: return GL_TEXTURE13;
case 14: return GL_TEXTURE14;
case 15: return GL_TEXTURE15;
case 16: return GL_TEXTURE16;
case 17: return GL_TEXTURE17;
case 18: return GL_TEXTURE18;
case 19: return GL_TEXTURE19;
case 20: return GL_TEXTURE20;
case 21: return GL_TEXTURE21;
case 22: return GL_TEXTURE22;
case 23: return GL_TEXTURE23;
case 24: return GL_TEXTURE24;
case 25: return GL_TEXTURE25;
case 26: return GL_TEXTURE26;
case 27: return GL_TEXTURE27;
case 28: return GL_TEXTURE28;
case 29: return GL_TEXTURE29;
case 30: return GL_TEXTURE30;
case 31: return GL_TEXTURE31;
}
throw std::out_of_range("Texture index out of range!");
}
const float Renderer::QUAD_VERTEX_BUFFER_DATA[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
void PrintOpenGLError()
{
GLenum error = glGetError();
if (error != GL_NO_ERROR)
printf("OpenGL error: 0x%x\n", error);
}
void Renderer::Create() {
if (mInstance == NULL)
mInstance = new Renderer();
}
void Renderer::Destroy() {
if (mInstance != NULL)
delete mInstance;
mInstance = NULL;
}
Renderer* Renderer::Instance() {
return mInstance;
}
bool Renderer::Initialize(bool fullscreen) {
if (!InitializeGLFW(fullscreen))
return false;
PrintOpenGLError();
//GLint64 bufferTextureSize;
//glGetInteger64v(GL_MAX_TEXTURE_BUFFER_SIZE, &bufferTextureSize);
//printf("Buffer texture size: %I64d\n", bufferTextureSize);
mLightState.active = true;
mLightState.lightType = LightState::LightType::Directional;
mLightState.lightProps = glm::vec3(0.25, -0.6, -1);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
return false;
}
PrintOpenGLError();
mScene = new Scene();
mObjLoader->Load("../Research/data/EpicCitadel_Skybox.obj", *mScene);
for (Mesh mesh : mScene->meshes)
{
Load2DTexture(mesh.texture);
}
LoadOctree();
glEnable(GL_DEPTH_TEST);
// Generate and bind vertex array object
glGenVertexArrays(1, &mVertexArrayID);
glBindVertexArray(mVertexArrayID);
// Generate vertex buffer
glm::vec3 zeroVec(0);
glGenBuffers(1, &mVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
if ( !mScene->vertices.empty() )
glBufferData(GL_ARRAY_BUFFER, mScene->vertices.size() * sizeof(glm::vec3), &mScene->vertices[0], GL_STATIC_DRAW);
else
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3), &zeroVec, GL_STATIC_DRAW);
// Generate texture coordinate buffer
glGenBuffers(1, &mTextureBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mTextureBuffer);
if ( !mScene->uvs.empty() )
glBufferData(GL_ARRAY_BUFFER, mScene->uvs.size() * sizeof(glm::vec2), &mScene->uvs[0], GL_STATIC_DRAW);
else
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2), &zeroVec, GL_STATIC_DRAW);
// Generate normal buffer
glGenBuffers(1, &mNormalBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mNormalBuffer);
if ( !mScene->normals.empty() )
glBufferData(GL_ARRAY_BUFFER, mScene->normals.size() * sizeof(glm::vec3), &mScene->normals[0], GL_STATIC_DRAW);
else
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3), &zeroVec, GL_STATIC_DRAW);
// Generate element buffer
glGenBuffers(1, &mElementBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementBuffer);
if ( !mScene->indices.empty() )
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mScene->indices.size() * sizeof(unsigned), &mScene->indices[0], GL_STATIC_DRAW);
// Create element buffer for a fullscreen quad
glGenBuffers(1, &mQuadVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(QUAD_VERTEX_BUFFER_DATA), QUAD_VERTEX_BUFFER_DATA, GL_STATIC_DRAW);
InitFramebuffer();
GenerateRandomTexture();
// Initialize and use shader program
LoadShaders();
LoadRecording(mPropertyLoader->GetProperty("last_recording_filename"));
LoadLightPathRecording(mPropertyLoader->GetProperty("last_lightpath_recording_filename"));
strcpy( mText, "Waiting for first frame..." );
//system("pause");
return true;
}
void Renderer::InitFramebuffer()
{
// Create framebuffer texture
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &mFramebufferTexture);
glBindTexture(GL_TEXTURE_2D, mFramebufferTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Create direct light texture (contains direct lighting)
glGenTextures(1, &mDirectLightTexture);
glBindTexture(GL_TEXTURE_2D, mDirectLightTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mWidth, mHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Create GI texture (contains indirect, ray-traced, lighting)
glGenTextures(1, &mIndirectLightTexture);
glBindTexture(GL_TEXTURE_2D, mIndirectLightTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mWidth, mHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
PrintOpenGLError();
glGenTextures(1, &mVoxelPositionsTexture);
glBindTexture(GL_TEXTURE_2D, mVoxelPositionsTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, mWidth, mHeight, 0, GL_RGB, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
PrintOpenGLError();
// Create Depthbuffer texture
glGenRenderbuffers(1, &mDepthTexture);
glGenTextures(1, &mDepthTexture);
glBindTexture(GL_TEXTURE_2D, mDepthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
/* Framebuffer to link everything together */
glGenFramebuffers(1, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFramebufferTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mDirectLightTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, mIndirectLightTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, mVoxelPositionsTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mDepthTexture, 0);
GLenum status;
if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) {
printf("Error when building framebuffer, glCheckFramebufferStatus: error 0x%x\n", status);
}
const unsigned DRAW_BUFFERS_SIZE = 4;
GLenum DrawBuffers[DRAW_BUFFERS_SIZE] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
glDrawBuffers(DRAW_BUFFERS_SIZE, DrawBuffers);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Renderer::DeleteFramebuffer()
{
glDeleteRenderbuffers(1, &mDepthTexture);
glDeleteTextures(1, &mFramebufferTexture);
glDeleteFramebuffers(1, &mFramebuffer);
}
void Renderer::LoadFragmentShaderUniforms()
{
mTextureSampler = glGetUniformLocation(mDefaultProgramID, "textureSampler");
mOctreeSamplers = glGetUniformLocation(mDefaultProgramID, "octreeSamplers");
mRandomTextureSampler = glGetUniformLocation(mDefaultProgramID, "randomSampler");
mMaterialOctreeSampler = glGetUniformLocation(mDefaultProgramID, "materialOctreeSampler");
mMaterialTextureSampler = glGetUniformLocation(mDefaultProgramID, "materialTextureSampler");
mBlockPointerSampler = glGetUniformLocation(mDefaultProgramID, "blockPointerSampler");
mBlockSamplers = glGetUniformLocation(mDefaultProgramID, "blockSamplers");
mRenderDepthUniform = glGetUniformLocation(mDefaultProgramID, "renderDepth");
mPostprocessDepthTextureUniform = glGetUniformLocation(mPostProcessProgramID, "depthTexture");
mPostprocessRenderTextureUniform = glGetUniformLocation(mPostProcessProgramID, "renderTexture");
mPostprocessVoxelPositionsTextureUniform = glGetUniformLocation(mPostProcessProgramID, "voxelPositionsTexture");
mGiPostProcessRenderTextureUniform = glGetUniformLocation(mGiPostProcessProgramID, "shadeless");
mGiPostProcessIndirectLightTextureUniform = glGetUniformLocation(mGiPostProcessProgramID, "indirect");
mGiPostProcessDirectLightTextureUniform = glGetUniformLocation(mGiPostProcessProgramID, "direct");
}
GLenum GetFormat(unsigned8 pixelsPerChannel)
{
switch (pixelsPerChannel)
{
case 1: return GL_RED_INTEGER;
case 2: return GL_RG_INTEGER;
case 3: return GL_RGB_INTEGER;
case 4: default: return GL_RGBA_INTEGER;
}
}
GLint GetInternalFormat(unsigned8 pixelsPerChannel)
{
switch (pixelsPerChannel)
{
case 1: return GL_R8UI;
case 2: return GL_RG8UI;
case 3: return GL_RGB8UI;
case 4: default: return GL_RGBA8UI;
}
}
void Renderer::LoadOctree()
{
GLint max3DTextureSize;
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize);
printf("Max 3D texture size: %i\n", max3DTextureSize);
// Get the NodePool 3D texture and material texture if available
GLuint poolSize = 0;
std::vector<unsigned8> nodePool;
GLuint materialTextureSize = 0;
std::vector<unsigned8> materialTexture;
GLuint materialNodePoolTextureSize = 0;
std::vector<unsigned8> materialNodePool;
std::vector<unsigned8> blockPointerPool;
std::vector<unsigned8> blockPool;
mAdditionalProperties.clear();
bool succes = OctreeLoader::GetPool(nodePool, poolSize, materialTexture, materialTextureSize, materialNodePool, materialNodePoolTextureSize,
blockPointerPool, blockPool, mAdditionalProperties);
if (!succes) printf("Octree loading failed!\n");
// Create the 3D texture containing the octree
unsigned32 octreeSamplerCount = MathHelper::DivideRoundUp(poolSize, TEXTURE_BLOCK_SIZE);
mOctreeTextures.resize(octreeSamplerCount);
for (unsigned32 i = 0; i < octreeSamplerCount; i++)
{
glGenTextures(1, &mOctreeTextures[i]);
glBindTexture(GL_TEXTURE_3D, mOctreeTextures[i]);
unsigned32 texSizeZ = std::min(poolSize - i * TEXTURE_BLOCK_SIZE, TEXTURE_BLOCK_SIZE);
glTexImage3D(GL_TEXTURE_3D, 0, GL_R8UI, TEXTURE_SIZE, TEXTURE_SIZE, texSizeZ, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &nodePool[i * TEXTURE_SIZE * TEXTURE_SIZE * TEXTURE_BLOCK_SIZE]);
PrintOpenGLError();
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
PrintOpenGLError();
}
mAdditionalProperties.insert(std::make_pair("OctreeSamplerCount", std::to_string(octreeSamplerCount)));
// If the material texture is available, load it to the GPU
if (materialTextureSize != 0)
{
unsigned8 cpp = (unsigned8)(materialTexture.size() / (materialTextureSize * materialTextureSize));
// Write the material texture to a PNG (for debug purposes)
std::string textureFileName = OctreeLoader::GetFullFilename() + "_texture.png";
//Encode the image
std::vector<unsigned8> rgbMaterialTexture;
if (cpp < 3)
{
rgbMaterialTexture = std::vector<unsigned8>(materialTextureSize * materialTextureSize * 3);
for (size_t i = 0; i < materialTextureSize * materialTextureSize; i++)
for (size_t j = 0; j < cpp; j++)
rgbMaterialTexture[i * 3 + j] = materialTexture[i * cpp + j];
}
else
{
rgbMaterialTexture = materialTexture;
}
unsigned error = lodepng::encode(textureFileName.c_str(), rgbMaterialTexture, materialTextureSize, materialTextureSize, LodePNGColorType::LCT_RGB);
if (error) printf("encoder error %u: %s", error, lodepng_error_text(error));
// Put the material texture in GPU memory
glGenTextures(1, &mMaterialTexture);
glBindTexture(GL_TEXTURE_2D, mMaterialTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(cpp), materialTextureSize, materialTextureSize, 0, GetFormat(cpp), GL_UNSIGNED_BYTE, &materialTexture[0]);
PrintOpenGLError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
PrintOpenGLError();
}
// If the material nodepool is available, load it to the GPU
if (materialNodePoolTextureSize != 0)
{
glGenTextures(1, &mMaterialOctreeTexture);
glBindTexture(GL_TEXTURE_3D, mMaterialOctreeTexture);
// Read the material octree nodepool
glTexImage3D(GL_TEXTURE_3D, 0, GL_R8UI, TEXTURE_SIZE, TEXTURE_SIZE, materialNodePoolTextureSize, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &materialNodePool[0]);
PrintOpenGLError();
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
PrintOpenGLError();
}
if (blockPointerPool.size() > 0)
{
glGenTextures(1, &mBlockPointerTexture);
glBindTexture(GL_TEXTURE_3D, mBlockPointerTexture);
blockPointerPool.resize(MathHelper::DivideRoundUp(blockPointerPool.size(), (size_t)TEXTURE_SIZE * TEXTURE_SIZE) * TEXTURE_SIZE * TEXTURE_SIZE);
// Read the material octree nodepool
glTexImage3D(GL_TEXTURE_3D, 0, GL_R8UI, TEXTURE_SIZE, TEXTURE_SIZE, blockPointerPool.size() / (TEXTURE_SIZE * TEXTURE_SIZE), 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &blockPointerPool[0]);
if (mAdditionalProperties.find("blockCount") != mAdditionalProperties.end())
printf("LastBlockIndex = %u\n", blockPointerPool[(std::stoul(mAdditionalProperties["blockCount"]) - 1) * 3]);
PrintOpenGLError();
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
PrintOpenGLError();
}
// If the material pointer nodepool is available, load it to the GPU
if (blockPool.size() > 0)
{
blockPool.resize(MathHelper::DivideRoundUp(blockPool.size(), (size_t)(1024 * 1024)) * 1024 * 1024);
unsigned32 blockPoolDepth = (unsigned32)(blockPool.size() / (size_t)(1024 * 1024));
unsigned32 blockSamplerCount = MathHelper::DivideRoundUp(blockPoolDepth, TEXTURE_BLOCK_SIZE);
mBlockTextures.resize(blockSamplerCount);
for (unsigned32 i = 0; i < blockSamplerCount; i++)
{
glGenTextures(1, &mBlockTextures[i]);
glBindTexture(GL_TEXTURE_3D, mBlockTextures[i]);
// Read the material octree nodepool
unsigned texSizeZ = std::min(TEXTURE_BLOCK_SIZE, blockPoolDepth - i * TEXTURE_BLOCK_SIZE);
glTexImage3D(GL_TEXTURE_3D, 0, GL_R8UI, TEXTURE_SIZE, TEXTURE_SIZE, texSizeZ, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &blockPool[i * TEXTURE_SIZE * TEXTURE_SIZE * TEXTURE_BLOCK_SIZE]);
PrintOpenGLError();
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
PrintOpenGLError();
}
mAdditionalProperties.insert(std::make_pair("BlockSamplerCount", std::to_string(blockSamplerCount)));
}
mAdditionalProperties.insert(std::make_pair("TextureBlockSize", std::to_string(TEXTURE_BLOCK_SIZE)));
glBindTexture(GL_TEXTURE_3D, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Renderer::GenerateRandomTexture()
{
size_t size = 256;
std::vector<float> randomTexture(size * size * 3);
srand((unsigned)time(NULL));
for (size_t i = 0; i < randomTexture.size(); i++)
randomTexture[i] = (float)rand() / (float)(RAND_MAX);
//for (size_t i = 0; i < size * size; i++)
//{
// glm::vec3 res(0);
// for (glm::length_t x = 0; x < 3; x++) res[x] = (float)rand() / (float)(RAND_MAX);
// res = glm::normalize(res);
// for (glm::length_t x = 0; x < 3; x++) randomTexture[i * 3 + x] = res[x];
//}
glGenTextures(1, &mRandomTexture);
glBindTexture(GL_TEXTURE_2D, mRandomTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, size, size, 0, GL_RGB, GL_FLOAT, &randomTexture[0]);
PrintOpenGLError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
PrintOpenGLError();
}
void Renderer::Render() {
do {
mCurrentTime = glfwGetTime();
mFrames++;
// Print frame rate every second
if (mCurrentTime - mLastTime >= 1) {
sprintf( mText, "%.2f ms (%u FPS)", 1000.0/double(mFrames), mFrames );
for (unsigned i = 0; i < 100; ++i)
printf("\b");
printf("%s", mText);
mFrames = 0;
mLastTime += 1;
}
// Let controller listen to input
mController->Run();
glBindFramebuffer(GL_FRAMEBUFFER, mAOMode == AOMode::SSAO || mAOMode == AOMode::REAL ? mFramebuffer : 0);
glUseProgram(mDefaultProgramID);
// Enable vertex attributes array (positions)
glEnableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexPosition")));
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
glVertexAttribPointer(
stoi(mPropertyLoader->GetProperty("shader_vertexPosition")), // Attribute positions
3, // Size
GL_FLOAT, // Type
GL_FALSE, // Normalized
0, // Stride
(void*) 0 // Array buffer offset
);
// Enable vertex attributes array (texture coordinates)
glEnableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexUV")));
glBindBuffer(GL_ARRAY_BUFFER, mTextureBuffer);
glVertexAttribPointer(
stoi(mPropertyLoader->GetProperty("shader_vertexUV")), // Attribute texture coordinates
2, // Size
GL_FLOAT, // Type
GL_FALSE, // Normalized
0, // Stride
(void*) 0 // Array buffer offset
);
// Enable vertex attributes array (normals)
glEnableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexNormal")));
glBindBuffer(GL_ARRAY_BUFFER, mNormalBuffer);
glVertexAttribPointer(
stoi(mPropertyLoader->GetProperty("shader_vertexNormal")), // Attribute normals
3, // Size
GL_FLOAT, // Type
GL_FALSE, // Normalized
0, // Stride
(void*)0 // Array buffer offset
);
// Get the camera state from the recording if we're replaying a recording
if (mIsPlayingFlythrough)
{
if (!mIsStoringRecording)
mFrameTimes.push_back(mCurrentTime);
float time = ((float)mCameraPathFrame) * mCameraPathFrameTime;
if (time > mCameraPath.GetLength())
{
if (mIsStoringRecording)
StopStoringRecording();
else
StopBenchmark();
}
else
{
mCurrentCameraState = mCameraPath.GetStateAtTime(time);
/*mCurrentCameraState.position /= 1.0 / 0.98;
mCurrentCameraState.target /= 1.0 / 0.98;*/
if (!mLightPath.Empty())
mLightState = mLightPath.GetStateAtTime(time);
}
mCameraPathFrame++;
}
// Get the camera state from user input (mCamera) otherwise
else
{
mCurrentCameraState.position = mCamera->GetPosition();
mCurrentCameraState.direction = mCamera->GetDirection();
mCurrentCameraState.target = mCamera->GetTarget();
mCurrentCameraState.FOV = mCamera->GetFOV();
mCurrentCameraState.nearPlane = mCamera->GetNear();
mCurrentCameraState.farPlane = mCamera->GetFar();
glm::vec3 lightProps = (mLightState.lightType == LightState::LightType::Directional) ? mDirectionalLight.GetDirection() : mPointLight.GetPosition();
mLightState.lightProps = lightProps;
}
Transform();
// If we're recording, put the current camera state in the recording
if (mIsRecording)
mCameraPath.SetStateAtTime(mRecordingTimer.GetTime(), mCurrentCameraState);
if (!mStaticUniformsSet[mDefaultProgramID]) SetStaticUniforms(mDefaultProgramID);
SetDynamicUniforms(mDefaultProgramID);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
unsigned32 tex = 0;
// Render meshes
for (Mesh mesh : mScene->meshes) {
// Bind texture to texture unit 0
glActiveTexture(GetTextureUnit(tex));
glBindTexture(GL_TEXTURE_2D, mTextures[mesh.texture]);
// Set texture sampler to texture unit 0
glUniform1i(mTextureSampler, tex);
tex++;
for (unsigned32 i = 0; i < mOctreeTextures.size(); i++)
{
glActiveTexture(GetTextureUnit(tex));
glBindTexture(GL_TEXTURE_3D, mOctreeTextures[i]);
glUniform1i(mOctreeSamplers + i, tex);
tex++;
}
TreeType globalType = TreeTypeParser::GetGlobalType(mTreeType);
if (globalType == ONLYMATERIAL)
{
glActiveTexture(GetTextureUnit(tex));
glBindTexture(GL_TEXTURE_3D, mMaterialOctreeTexture);
glUniform1i(mMaterialOctreeSampler, tex);
tex++;
}
if (globalType == HIERARCHICAL || globalType == ONLYMATERIAL || globalType == UNIQUEINDEX || globalType == BITTREES) {
glActiveTexture(GetTextureUnit(tex));
glBindTexture(GL_TEXTURE_2D, mMaterialTexture);
glUniform1i(mMaterialTextureSampler, tex);
tex++;
}
if (globalType == UNIQUEINDEX)
{
glActiveTexture(GetTextureUnit(tex));
glBindTexture(GL_TEXTURE_3D, mBlockPointerTexture);
glUniform1i(mBlockPointerSampler, tex);
tex++;
for (unsigned32 i = 0; i < mBlockTextures.size(); i++)
{
glActiveTexture(GetTextureUnit(tex));
glBindTexture(GL_TEXTURE_3D, mBlockTextures[i]);
glUniform1i(mBlockSamplers + i, tex);
tex++;
}
}
// Bind the random texture
glActiveTexture(GetTextureUnit(tex));
glBindTexture(GL_TEXTURE_2D, mRandomTexture);
glUniform1i(mRandomTextureSampler, tex);
tex++;
// Draw the element arrays
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementBuffer);
glDrawElements(
GL_TRIANGLES, // Drawing mode
mesh.size, // Element count
GL_UNSIGNED_INT, // Type
(void*)(mesh.offset * sizeof(unsigned)) // Element array buffer offset
);
}
// Disable vertex attributes arrays
glDisableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexPosition")));
glDisableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexUV")));
glDisableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexNormal")));
// Swap buffers
if (mAOMode == AOMode::SSAO)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(mPostProcessProgramID);
if (!mStaticUniformsSet[mPostProcessProgramID]) SetStaticUniforms(mPostProcessProgramID);
SetDynamicUniforms(mPostProcessProgramID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mDepthTexture);
glUniform1i(mPostprocessDepthTextureUniform, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mFramebufferTexture);
glUniform1i(mPostprocessRenderTextureUniform, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, mVoxelPositionsTexture);
glUniform1i(mPostprocessVoxelPositionsTextureUniform, 2);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangles !
glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles
glDisableVertexAttribArray(0);
}
else if (mAOMode == AOMode::REAL)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(mGiPostProcessProgramID);
if (!mStaticUniformsSet[mGiPostProcessProgramID]) SetStaticUniforms(mGiPostProcessProgramID);
SetDynamicUniforms(mGiPostProcessProgramID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mFramebufferTexture);
glUniform1i(mGiPostProcessRenderTextureUniform, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mDirectLightTexture);
glUniform1i(mGiPostProcessDirectLightTextureUniform, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, mIndirectLightTexture);
glUniform1i(mGiPostProcessIndirectLightTextureUniform, 2);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangles !
glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles
glDisableVertexAttribArray(0);
}
glfwSwapBuffers(mWindow);
glfwPollEvents();
// If we're recording, store the current screenshot
if (mIsStoringRecording)
{
std::string filename = mRecordingDirectory + "/" + std::to_string(mCameraPathFrame) + ".png";
TakeScreenshot(filename);
}
}
while( mController->GetState() != Controls::State::EXIT && glfwWindowShouldClose(mWindow) == 0 );
}
void Renderer::Transform() {
const CameraState& viewpoint = mCurrentCameraState;
glm::mat4 mViewMatrix = glm::lookAt(viewpoint.position, viewpoint.target, glm::vec3(0, 1, 0));
mProjectionMatrix = glm::perspective(viewpoint.FOV, float(glm::max(1, mWidth)) / float(glm::max(1, mHeight)), viewpoint.nearPlane, viewpoint.farPlane);
mMVP = mProjectionMatrix * mViewMatrix;
mInvMVP = glm::inverse(mMVP);
}
void Renderer::SetCameraState(CameraState state)
{
if (!mCamera->IsFree()) mCamera->ToggleMode();
mCamera->SetPosition(state.position);
mCamera->SetDirection(state.direction);
mCamera->SetTarget(state.target);
mCamera->SetFOV(state.FOV);
mCamera->SetNear(state.nearPlane);
mCamera->SetFar(state.farPlane);
}
//************************************
// Returns the index of the texture to access it in the texture vector
//************************************
size_t Renderer::Load2DTexture(std::string filename) {
if (Renderer::Instance()->textureLoaded(filename))
return Renderer::Instance()->mTextures[filename];
unsigned textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
PNG* png = new PNG(filename.c_str());
if ( png->Initialized() )
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, png->W(), png->H(), 0, GL_RGBA, GL_UNSIGNED_BYTE, png->Data() );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
delete png;
glBindTexture(GL_TEXTURE_2D, 0);
Renderer::Instance()->mTextures.insert(std::pair<std::string, unsigned>(filename, textureID));
return Renderer::Instance()->mTextures.size() - 1;
}
bool Renderer::textureLoaded(std::string filename) {
return mTextures.find(filename) != mTextures.end();
}
void Renderer::ToggleFullscreen(bool fullscreen) {
if (fullscreen) {
mWidthOld = mWidth;
mHeightOld = mHeight;
}
Reinitialize(fullscreen);
}
void Renderer::UpdateLightDirection()
{
mLightState.active = true;
if (mLightState.lightType == LightState::LightType::Directional)
mDirectionalLight.SetDirection(mCamera->GetDirection());
else if (mLightState.lightType == LightState::LightType::Point)
mPointLight.SetPosition(mCamera->GetPosition());
}
void Renderer::ToggleLight()
{
if (!mLightState.active)
{
mLightState.active = true;
mLightState.lightType = LightState::LightType::Directional;
}
else if (mLightState.lightType == LightState::LightType::Directional)
{
mLightState.lightType = LightState::LightType::Point;
}
else
mLightState.active = false;
}
void Renderer::ToggleAOMode() {
mAOMode = static_cast<AOMode>((mAOMode + 1) % AOMode::NUM_VALUES);
ReloadShaders();
}
void Renderer::ToggleReflections() { mReflections = !mReflections; }
void Renderer::IncreaseOctreeRenderLevel()
{
if (mRenderDepth < mMaxLevel)
mRenderDepth++;
printf("Octree rendering depth increased to %u\n", mRenderDepth);
}
void Renderer::DecreaseOctreeRenderLevel()
{
if (mRenderDepth > 0)
mRenderDepth--;
printf("Octree rendering depth decreased to %u\n", mRenderDepth);
}
// Remove directory if present.
// Do this before extension removal incase directory has a period character.
std::string ExtractFileName(std::string fullPath)
{
PathHelper::GetFilename(fullPath);
const size_t last_slash_idx = fullPath.find_last_of("\\/");
if (std::string::npos != last_slash_idx)
{
fullPath.erase(0, last_slash_idx + 1);
}
return fullPath;
}
std::string GetCurrentTimeString()
{
time_t t = time(0);
struct tm * now = localtime(&t);
char timeString[16];
strftime(timeString, sizeof(timeString), "%Y%m%d_%H%M%S", now);
return std::string(timeString);
}
void Renderer::TakeScreenshot()
{
// Build the filename for the screenshot
std::string filename = mScreenshotDirectory
// Use the scene name of the octree file as the first part of the screenshot name
+ ExtractFileName(OctreeLoader::GetFullFilename())
// Append the current datetime to the filename to make it unique
+ "_" + GetCurrentTimeString();
// Read the pixels
TakeScreenshot(filename + ".png");
SaveViewpoint(filename + "_viewpoint.bin");
}
void Renderer::TakeScreenshot(std::string filename)
{
std::vector<unsigned char> screenshot(mWidth * mHeight * 3);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glReadPixels(0, 0, mWidth, mHeight, GL_RGB, GL_UNSIGNED_BYTE, &screenshot[0]);
// Start a threat that encodes and saves the screenshot
std::thread t([](unsigned width, unsigned height, std::vector<unsigned char> screenshot, std::string filename)
{
unsigned bufferSize = width * 3;
std::vector<unsigned char> buffer(bufferSize);
// Since OpenGL likes stuff being upside down, flip the image
unsigned halfHeight = (unsigned)height / 2;
for (unsigned y = 0; y < halfHeight; y++)
{
unsigned row1 = y * bufferSize;
unsigned row2 = (height - y - 1) * bufferSize;
std::move(screenshot.begin() + row1, screenshot.begin() + row1 + bufferSize, buffer.begin());
std::move(screenshot.begin() + row2, screenshot.begin() + row2 + bufferSize, screenshot.begin() + row1);
std::move(buffer.begin(), buffer.end(), screenshot.begin() + row2);
}
//Encode the image
unsigned error = lodepng::encode(filename, screenshot, width, height, LodePNGColorType::LCT_RGB);
//if there's an error, display it
if (error) printf("encoder error %u: %s", error, lodepng_error_text(error));
}, mWidth, mHeight, screenshot, filename);
// Detach the threat so it doesn't go out of scope when we leave this method.
t.detach();
}
glm::vec3 ReadVec3(std::string source)
{
std::regex numberRegex("[-+]?[0-9]+[.,]?[0-9]*");
glm::vec3 res(0);
std::sregex_iterator end;
int j = 0;
for (std::sregex_iterator i(source.cbegin(), source.cend(), numberRegex); i != end; ++i)
{
auto value = *i;
res[j++] = std::stof(value.str());
}
return res;
}
void Renderer::SetPresetLocation(unsigned8 presetID)
{
std::string positionProperty = "renderer_position" + std::to_string(presetID);
std::string directionProperty = "renderer_direction" + std::to_string(presetID);
std::string positionStr = mPropertyLoader->GetProperty(positionProperty);
std::string directionStr = mPropertyLoader->GetProperty(directionProperty);
glm::vec3 position = ReadVec3(positionStr);
glm::vec3 direction = ReadVec3(directionStr);
if (!mCamera->IsFree()) mCamera->ToggleMode();
mCamera->SetPosition(position);
mCamera->SetDirection(direction);
}
void Renderer::ToggleRecording()
{
// Don't start recording when a recording is currently being stored
if (mIsPlayingFlythrough) return;
if (!mIsRecording)
// Clear the current recording
mCameraPath.Clear();
if (mIsRecording)
{
mCameraPath.Normalize();
// Store the current recording as the last recording
SaveRecording(mPropertyLoader->GetProperty("last_recording_filename"));
}
// Toggle
mIsRecording = !mIsRecording;
if (mIsRecording)
printf("Started recording...\n");
else
printf("Recording finished...\n");
}
void Renderer::ClearLightPath()
{
printf("Clearing light path...\n");
mLightPath.Clear();
}
void Renderer::AddLightPathNode()
{
if (mIsPlayingFlythrough || mIsRecording) return;
printf("Adding light path node...\n");
if (mLightPath.Empty())
{
mLightPath.SetStateAtTime(0, mLightState);
}
else
{
float curLength = mLightPath.GetLength();
mLightPath.SetStateAtTime(curLength + mLightPathNodeTime, mLightState);
}
}
void Renderer::ClearCameraPath()
{
printf("Clearing camera path...\n");
mCameraPath.Clear();
}
void Renderer::AddCameraPathNode()
{
if (mIsPlayingFlythrough || mIsRecording) return;
printf("Adding camera path node...\n");
if (mCameraPath.Empty())
{
mCameraPath.SetStateAtTime(0, mCurrentCameraState);
}
else
{
float curLength = mCameraPath.GetLength();
mCameraPath.SetStateAtTime(curLength + mCameraPathNodeTime, mCurrentCameraState);
}
}
void Renderer::RemoveCameraPathNode()
{
if (mIsPlayingFlythrough || mIsRecording) return;
if (mCameraPath.Empty())
{
printf("Removing last camera path node failed, camera path is empty...\n");
}
else
{
printf("Removing last stored camera path node...\n");
mCameraPath.RemoveLast();
}
}
void Renderer::StoreRecording()
{
// Don't start storing if we're running a benchmark
if ((mIsPlayingFlythrough && !mIsStoringRecording) || mIsRecording)
return;
if (mIsStoringRecording)
{
// If a video is beind recorded, stop the recording
StopStoringRecording();
return;
}
if (mCameraPath.Empty())
{
printf("No recording to store.\n");
return;
}
StartStoringRecording();
}
void Renderer::RunBenchmark()
{
// Don't run a benchmark while recording
if (mIsStoringRecording || mIsRecording)
return;
if (mIsPlayingFlythrough)
{
StopBenchmark();
return;
}
StartBenchmark();
}
void Renderer::StartFlythrough()
{
mIsPlayingFlythrough = true;
mIsTrackballReconding = false;
mCameraPath.Normalize();
mLightPath.Normalize();
mCameraPathFrame = 0;
float length = (float)mCameraPath.GetLength();
printf("Started flythrough of %f seconds (%u frames at %f FPS)...\n", length, (unsigned)(length / (double)mCameraPathFrameTime), mRecordingFPS);
}
void Renderer::StopFlythrough()
{
mIsPlayingFlythrough = false;
mCameraPathFrame = 0;
mCameraPath.Normalize();
}
void Renderer::StartStoringRecording()
{
StartFlythrough();
mIsStoringRecording = true;
mIsTrackballReconding = false;
// Store camera- and light path
InitRecordingDirectory();
SaveRecording(mRecordingDirectory + "/recording.bin");
SaveLightPathRecording(mRecordingDirectory + "/lightpath.bin");
// Also store the most recent camera and light path
SaveRecording(mPropertyLoader->GetProperty("last_recording_filename"));
SaveLightPathRecording(mPropertyLoader->GetProperty("last_lightpath_recording_filename"));
}
void Renderer::StopStoringRecording()
{
StopFlythrough();
mIsStoringRecording = false;
mRecordingDirectory = "";
}
void Renderer::SetRotationRecording()
{
double altitude = mCamera->GetAltitude();
glm::vec3 pivot = mCamera->GetPivot();
double r = glm::length(mCamera->GetPosition() - pivot);
ClearCameraPath();
printf("Setting camera path to rotation around pivot...\n");
int frame = 0;
double frameD = frame;
double time;
while ((time = frameD * mCameraPathFrameTime) < mCameraPathNodeTime) {
double azimuth = time * 2.0 * mPi;
double x = r * cos(altitude) * sin(azimuth);
double y = r * sin(altitude);
double z = r * cos(altitude) * cos(azimuth);
glm::vec3 position = pivot + glm::vec3(x, y, z);
CameraState camState = mCurrentCameraState;
camState.position = position;
camState.target = pivot;
camState.direction = position - pivot;
mCameraPath.SetStateAtTime(time, camState);
frame++;
frameD = frame;
}
}
void Renderer::StartBenchmark()
{
StartFlythrough();
mFrameTimes.clear();
mFrameTimes.push_back(glfwGetTime());
// Store camera- and light path
InitRecordingDirectory();
SaveRecording(mRecordingDirectory + "/recording.bin");
SaveLightPathRecording(mRecordingDirectory + "/lightpath.bin");
// Also store them as the most recent camera- and light path.
SaveRecording(mPropertyLoader->GetProperty("last_recording_filename"));
SaveLightPathRecording(mPropertyLoader->GetProperty("last_lightpath_recording_filename"));
}
void Renderer::StopBenchmark()
{
StopFlythrough();
// Write the frametimes:
std::ofstream file;
file.open(mRecordingDirectory + "/frametimes.txt");
for (double time : mFrameTimes)
file << time << "\n";
file.flush();
file.close();
printf("Recording stored...\n");
}
void Renderer::InitRecordingDirectory()
{
// Build the filename for the screenshot
mRecordingDirectory = mScreenshotDirectory
// Use the scene name of the octree file as the first part of the screenshot name
+ ExtractFileName(OctreeLoader::GetFullFilename())
// Append the current datetime to the filename to make it unique
+ "_" + GetCurrentTimeString();
// Make sure the recording directory exists
#ifdef _WIN32
CreateDirectory(mRecordingDirectory.c_str(), NULL);
#else
struct stat st = { 0 };
if (stat(mRecordingDirectory.c_str(), &st) == -1) {
mkdir(mRecordingDirectory.c_str(), 0700);
}
#endif
}
void Renderer::SaveLastViewpoint()
{
SaveViewpoint(mPropertyLoader->GetProperty("last_viewpoint_filename"));
}
void Renderer::LoadLastViewpoint()
{
LoadViewpoint(mPropertyLoader->GetProperty("last_viewpoint_filename"));
}
void Renderer::LoadRecording(std::string filename)
{
mCameraPath.ReadFromFile(filename);
}
void Renderer::SaveRecording(std::string filename)
{
mCameraPath.WriteToFile(filename);
}
void Renderer::LoadLightPathRecording(std::string filename)
{
mLightPath.ReadFromFile(filename);
}
void Renderer::SaveLightPathRecording(std::string filename)
{
mLightPath.WriteToFile(filename);
}
void Renderer::LoadViewpoint(std::string filename)
{
std::ifstream viewPointFile(filename, std::ios::binary);
Serializer<CameraState>::Deserialize(mCurrentCameraState, viewPointFile);
SetCameraState(mCurrentCameraState);
}
void Renderer::SaveViewpoint(std::string filename)
{
std::ofstream viewPointFile(filename, std::ios::binary);
Serializer<CameraState>::Serialize(mCurrentCameraState, viewPointFile);
}
void Renderer::LoadShaders()
{
mPropertyLoader->Update();
std::string vertexShader = mPropertyLoader->GetProperty("vertex_shader");
std::string fragmentShader = mPropertyLoader->GetProperty("fragment_shader");
std::string postProcessFragmentShader = mPropertyLoader->GetProperty("postprocess_shader").c_str();
std::string giPostProcessFragmentShader = mPropertyLoader->GetProperty("gi_postprocess_shader").c_str();
mAdditionalProperties["AOEnabled"] = mAOMode == AOMode::REAL ? "1" : "0";
mDefaultProgramID = mShaderLoader->LoadShader(vertexShader.c_str(), fragmentShader.c_str(), mAdditionalProperties);
mPostProcessProgramID = mShaderLoader->LoadShader("RenderTexture.vert", postProcessFragmentShader.c_str(), mAdditionalProperties);
mGiPostProcessProgramID = mShaderLoader->LoadShader("RenderTexture.vert", giPostProcessFragmentShader.c_str(), mAdditionalProperties);
mStaticUniformsSet.insert(std::make_pair(mDefaultProgramID, false));
mStaticUniformsSet.insert(std::make_pair(mPostProcessProgramID, false));
mStaticUniformsSet.insert(std::make_pair(mGiPostProcessProgramID, false));
LoadFragmentShaderUniforms();
}
void Renderer::ReloadShaders() {
LoadShaders();
SetStaticUniforms(mDefaultProgramID);
}
Renderer::Renderer() {
Camera::Create();
mCamera = Camera::Instance();
ObjLoader::Create();
mObjLoader = ObjLoader::Instance();
ShaderLoader::Create();
mShaderLoader = ShaderLoader::Instance();
PropertyLoader::Create();
mPropertyLoader = PropertyLoader::Instance();
mWidth = std::stoi(mPropertyLoader->GetProperty("width"));
mHeight = std::stoi(mPropertyLoader->GetProperty("height"));
mWidthOld = mWidth;
mHeightOld = mHeight;
mUseCache = mPropertyLoader->GetProperty("renderer_usecache") != "0";
mMaxLevel = std::stoi(mPropertyLoader->GetProperty("shader_max_level"));
mRenderDepth = mMaxLevel;
std::string treeType = mPropertyLoader->GetProperty("octree_type");
mScreenshotDirectory = mPropertyLoader->GetProperty("screenshot_dir");
mTreeType = treeType;
mAOMode = AOMode::NONE;
mReflections = false;
mIsPlayingFlythrough = false;
mIsStoringRecording = false;
mDirectionalLight.SetDirection(glm::vec3(0.25, -0.6, -1));
mDirectionalLight.SetActive(true);
mPointLight.SetPosition(0.0);
mLightState.active = false;
ToggleLight();
mRecordingFPS = mPropertyLoader->GetFloatProperty("recording_framerate");
mCameraPathFrameTime = 1.f / mRecordingFPS;
mCameraPathNodeTime = mPropertyLoader->GetFloatProperty("recording_node_time");
mLightPathNodeTime = mPropertyLoader->GetFloatProperty("recording_lightpath_node_time");
mWindow = NULL;
}
Renderer::~Renderer() {
glDeleteBuffers(1, &mVertexBuffer);
glDeleteBuffers(1, &mTextureBuffer);
glDeleteBuffers(1, &mNormalBuffer);
glDeleteBuffers(1, &mElementBuffer);
glDeleteBuffers(1, &mQuadVertexBuffer);
glDeleteProgram(mDefaultProgramID);
for (auto elem : mTextures)
glDeleteTextures(1, &elem.second);
mTextures.clear();
glDeleteTextures(1, &mTextureSampler);
glDeleteTextures((GLsizei)mOctreeTextures.size(), &mOctreeTextures[0]);
glDeleteVertexArrays(1, &mVertexArrayID);
DeleteFramebuffer();
glfwTerminate();
delete mScene;
Camera::Destroy();
Controls::Destroy();
ObjLoader::Destroy();
ShaderLoader::Destroy();
PropertyLoader::Destroy();
}
bool Renderer::Reinitialize(bool fullscreen) {
glDeleteBuffers(1, &mVertexBuffer);
glDeleteBuffers(1, &mTextureBuffer);
glDeleteBuffers(1, &mNormalBuffer);
glDeleteBuffers(1, &mElementBuffer);
glDeleteBuffers(1, &mQuadVertexBuffer);
glDeleteProgram(mDefaultProgramID);
for (auto elem : mTextures)
glDeleteTextures(1, &elem.second);
mTextures.clear();
glDeleteTextures(1, &mTextureSampler);
glDeleteTextures((GLsizei)mOctreeTextures.size(), &mOctreeTextures[0]);
glDeleteTextures(1, &mMaterialTexture);
glDeleteTextures(1, &mMaterialOctreeTexture);
glDeleteTextures(1, &mMaterialOctreeTexture);
glDeleteVertexArrays(1, &mVertexArrayID);
DeleteFramebuffer();
glfwTerminate();
delete mScene;
Controls::Destroy();
return Initialize(fullscreen);
}
bool Renderer::InitializeGLFW(bool fullscreen) {
// Initialize GLFW
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW\n");
return false;
}
glfwWindowHint(GLFW_SAMPLES, std::stoi(mPropertyLoader->GetProperty("anti_aliasing")));
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, std::stoi(mPropertyLoader->GetProperty("opengl_version_major")));
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, std::stoi(mPropertyLoader->GetProperty("opengl_version_minor")));
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
// Open a window and create OpenGL context
int monitorCount;
GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
if (fullscreen) { // In fullscreen mode, get window size from monitor resolution
const GLFWvidmode* mode = glfwGetVideoMode(monitors[monitorCount - 1]);
mWidth = mode->width;
mHeight = mode->height;
}
else {
mWidth = mWidthOld;
mHeight = mHeightOld;
}
mWindow = glfwCreateWindow(mWidth, mHeight, mPropertyLoader->GetProperty("window_name").c_str(), fullscreen ? monitors[monitorCount - 1] : NULL, NULL);
if (mWindow == NULL) {
fprintf(stderr, "Failed to open GLFW window.\n");
glfwTerminate();
return false;
}
glfwMakeContextCurrent(mWindow);
glfwSetWindowSizeCallback(mWindow, this->Resize);
glfwSetInputMode(mWindow, GLFW_STICKY_KEYS, GL_TRUE);
Controls::Create(mWindow, fullscreen);
mController = Controls::Instance();
mLastTime = glfwGetTime();
mFrames = 0;
return true;
}
// Set uniforms
void Renderer::SetStaticUniforms(unsigned program) {
glProgramUniform1i(program, stoi(mPropertyLoader->GetProperty("shader_width")), mWidth);
glProgramUniform1i(program, stoi(mPropertyLoader->GetProperty("shader_height")), mHeight);
glProgramUniform1f(program, stoi(mPropertyLoader->GetProperty("shader_aspect")), float(mWidth) / float(mHeight));
if (TreeTypeParser::GetGlobalType(mTreeType) == MULTIROOT)
{
TreeTypeDescriptor descr = TreeTypeParser::GetTreeTypeDescriptor(mTreeType);
glProgramUniform1ui(program, stoi(mPropertyLoader->GetProperty("shader_bitsPerChannel")), TreeTypeParser::GetBitsPerChannel(descr));
glProgramUniform1ui(program, stoi(mPropertyLoader->GetProperty("shader_bitsPerTree")), TreeTypeParser::GetBitsPerTree(descr));
}
mStaticUniformsSet[program] = true;
}
void Renderer::SetDynamicUniforms(unsigned program) {
glProgramUniformMatrix4fv(program, mPropertyLoader->GetIntProperty("shader_MVP"), 1, GL_FALSE, &mMVP[0][0]);
glProgramUniformMatrix4fv(program, mPropertyLoader->GetIntProperty("shader_invMVP"), 1, GL_FALSE, &mInvMVP[0][0]);
glProgramUniform1f(program, mPropertyLoader->GetIntProperty("shader_angle"), tan(.5f * mCurrentCameraState.FOV));
glProgramUniform3fv(program, mPropertyLoader->GetIntProperty("shader_lightDirection"), 1, &mLightState.lightProps[0]);
glProgramUniform1i(program, mPropertyLoader->GetIntProperty("shader_lightType"), mLightState.active ? ((mLightState.lightType == LightState::LightType::Directional) ? 1 : 2) : 0);
glProgramUniform3fv(program, mPropertyLoader->GetIntProperty("shader_cameraPosition"), 1, &mCurrentCameraState.position[0]);
glProgramUniform1i(program, mPropertyLoader->GetIntProperty("shader_reflections"), mReflections ? 1 : 0);
glProgramUniform1ui(program, mRenderDepthUniform, mRenderDepth);
}
void Renderer::Resize(int width, int height)
{
mWidth = width;
mHeight = height;
DeleteFramebuffer();
InitFramebuffer();
SetStaticUniforms(mDefaultProgramID);
SetStaticUniforms(mPostProcessProgramID);
}
// Resize callback
void Renderer::Resize(GLFWwindow* window, int width, int height) {
Renderer::Instance()->Resize(width, height);
glViewport(0,0,width,height);
};