1455 lines
48 KiB
C++
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);
|
|
}; |