910 lines
36 KiB
C++
910 lines
36 KiB
C++
#include "Debug.h"
|
|
#include "RenderSystem.h"
|
|
#include "common.h"
|
|
#include "VBO.h"
|
|
#include "VAO.h"
|
|
#include "EBO.h"
|
|
#include "FBO.h"
|
|
#include "shaderClass.h"
|
|
#include "Object.h"
|
|
#include "Camera.h"
|
|
#include "Cubemap.h"
|
|
#include "QuadVertices.h"
|
|
#include "Lighting.h"
|
|
#include "Animator.h"
|
|
|
|
static const float hitboxLineVertices[] = {
|
|
-0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f,
|
|
0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f,
|
|
0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f,
|
|
-0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
|
|
-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f,
|
|
0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f,
|
|
0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f,
|
|
-0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f,
|
|
-0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f,
|
|
0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f,
|
|
0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
|
|
-0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f,
|
|
};
|
|
|
|
static void ConfigurePBR(Shader* shader, Lighting* lighting)
|
|
{
|
|
shader->Set3F("dirLight.direction", lighting->sun.direction);
|
|
shader->Set3F("dirLight.color", lighting->sun.color);
|
|
shader->Set1F("dirLight.intensity", lighting->sun.intensity);
|
|
shader->Set1F("envIntensity", 0.8f);
|
|
shader->SetInt("environmentMap", 2);
|
|
|
|
const int directionalCount = glm::min((int)lighting->directionalLights.size(), MAX_DIRECTIONAL_LIGHTS);
|
|
shader->SetInt("directionalLightCount", directionalCount);
|
|
for (int i = 0; i < directionalCount; i++) {
|
|
const DirectionalLight* light = lighting->directionalLights[i];
|
|
const std::string name = "directionalLights[" + std::to_string(i) + "].";
|
|
shader->Set3F((name + "direction").c_str(), light->direction);
|
|
shader->Set3F((name + "color").c_str(), light->color);
|
|
shader->Set1F((name + "intensity").c_str(), light->intensity);
|
|
}
|
|
|
|
const int pointCount = glm::min((int)lighting->pointLights.size(), MAX_POINT_LIGHTS);
|
|
shader->SetInt("pointLightCount", pointCount);
|
|
for (int i = 0; i < pointCount; i++) {
|
|
const PointLight* light = lighting->pointLights[i];
|
|
const std::string name = "pointLights[" + std::to_string(i) + "].";
|
|
shader->Set3F((name + "position").c_str(), light->position);
|
|
shader->Set3F((name + "color").c_str(), light->color);
|
|
shader->Set1F((name + "intensity").c_str(), light->intensity);
|
|
shader->Set1F((name + "radius").c_str(), light->radius);
|
|
shader->SetInt((name + "shadowTile").c_str(), light->shadowTile);
|
|
}
|
|
|
|
shader->SetInt("localShadowMap", 10);
|
|
if (!lighting->shadowTileMatrices.empty()) {
|
|
shader->SetMat4Array("shadowTileMatrices", lighting->shadowTileMatrices.data(),
|
|
(int)lighting->shadowTileMatrices.size());
|
|
}
|
|
|
|
const int spotCount = glm::min((int)lighting->spotLights.size(), MAX_SPOT_LIGHTS);
|
|
shader->SetInt("spotLightCount", spotCount);
|
|
for (int i = 0; i < spotCount; i++) {
|
|
const SpotLight* light = lighting->spotLights[i];
|
|
const std::string name = "spotLights[" + std::to_string(i) + "].";
|
|
shader->Set3F((name + "position").c_str(), light->position);
|
|
shader->Set3F((name + "direction").c_str(), light->direction);
|
|
shader->Set3F((name + "color").c_str(), light->color);
|
|
shader->Set1F((name + "intensity").c_str(), light->intensity);
|
|
shader->Set1F((name + "radius").c_str(), light->radius);
|
|
shader->Set1F((name + "innerCos").c_str(), std::cos(glm::radians(light->innerAngle)));
|
|
shader->Set1F((name + "outerCos").c_str(), std::cos(glm::radians(light->outerAngle)));
|
|
shader->SetInt((name + "shadowTile").c_str(), light->shadowTile);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Initialize
|
|
// ============================================================================
|
|
void RenderSystem::Initialize()
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
glViewport(0, 0, window->width, window->height);
|
|
glEnable(GL_MULTISAMPLE);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_CULL_FACE);
|
|
glCullFace(GL_BACK);
|
|
glFrontFace(GL_CCW);
|
|
|
|
// Screen quad
|
|
screenfbo = new FBO();
|
|
skyboxVAO = new VAO();
|
|
skyboxVBO = new VBO();
|
|
quadVAO = new VAO();
|
|
quadVBO = new VBO();
|
|
|
|
skyboxVAO->GenerateID();
|
|
skyboxVBO->GenerateID();
|
|
skyboxVAO->Bind();
|
|
skyboxVBO->Bind();
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
|
|
|
quadVAO->GenerateID();
|
|
quadVBO->GenerateID();
|
|
quadVAO->Bind();
|
|
quadVBO->Bind();
|
|
quadVBO->BufferData(quadVertices, sizeof(quadVertices));
|
|
quadVAO->LinkVBO(*quadVBO, 0, 2, GL_FLOAT, 4 * sizeof(float), (void*)0);
|
|
quadVAO->LinkVBO(*quadVBO, 1, 2, GL_FLOAT, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
|
|
|
glGenVertexArrays(1, &hitboxVAO);
|
|
glGenBuffers(1, &hitboxVBO);
|
|
glBindVertexArray(hitboxVAO);
|
|
glBindBuffer(GL_ARRAY_BUFFER, hitboxVBO);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(hitboxLineVertices), hitboxLineVertices, GL_STATIC_DRAW);
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
|
glBindVertexArray(0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
std::vector<std::string> faces = {
|
|
"assets/miramar_ft.tga", "assets/miramar_bk.tga",
|
|
"assets/miramar_up.tga", "assets/miramar_dn.tga",
|
|
"assets/miramar_rt.tga", "assets/miramar_lf.tga"
|
|
};
|
|
Cubemap* skyboxcm = new Cubemap();
|
|
skyboxcm->FillCubemap(faces);
|
|
skybox = skyboxcm;
|
|
|
|
// Screen framebuffer
|
|
screenfbo->Bind();
|
|
glGenTextures(1, &textureColorbuffer);
|
|
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, window->width, window->height, 0, GL_RGBA, GL_FLOAT, NULL);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
|
|
|
|
glGenRenderbuffers(1, &rbo);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, window->width, window->height);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
|
|
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!\n";
|
|
screenfbo->Unbind();
|
|
|
|
// G-buffer
|
|
gBuffer = new FBO();
|
|
gBuffer->Bind();
|
|
unsigned int* gTextures[] = { &gPosition, &gNormal, &gAlbedo, &gMaterial, &gDepth };
|
|
for (unsigned int* texture : gTextures) {
|
|
glGenTextures(1, texture);
|
|
glBindTexture(GL_TEXTURE_2D, *texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
|
|
}
|
|
AllocateGBuffer(window->width, window->height);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedo, 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, gMaterial, 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, gDepth, 0);
|
|
const GLenum gDrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
|
|
glDrawBuffers(4, gDrawBuffers);
|
|
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
std::cout << "ERROR::FRAMEBUFFER:: G-buffer is not complete!\n";
|
|
gBuffer->Unbind();
|
|
|
|
// Bloom framebuffers, the blur bounces between these two
|
|
for (int i = 0; i < 2; i++) {
|
|
bloomFBO[i] = new FBO();
|
|
glGenTextures(1, &bloomTextures[i]);
|
|
glBindTexture(GL_TEXTURE_2D, bloomTextures[i]);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
AllocateBloom(window->width, window->height);
|
|
for (int i = 0; i < 2; i++) {
|
|
bloomFBO[i]->Bind();
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, bloomTextures[i], 0);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
std::cout << "ERROR::FRAMEBUFFER:: Bloom framebuffer is not complete!\n";
|
|
bloomFBO[i]->Unbind();
|
|
}
|
|
|
|
// Shadow framebuffer
|
|
shadowdepthmapfbo = new FBO();
|
|
SHADOW_RESOLUTION = 4096;
|
|
glGenTextures(1, &depthMaptexture);
|
|
glBindTexture(GL_TEXTURE_2D, depthMaptexture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
|
SHADOW_RESOLUTION, SHADOW_RESOLUTION, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
|
|
shadowdepthmapfbo->Bind();
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMaptexture, 0);
|
|
glDrawBuffer(GL_NONE);
|
|
glReadBuffer(GL_NONE);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
// Spot and point light shadow atlas. Unit 10 stays reserved for it, so it's only bound here.
|
|
const int shadowAtlasSize = SHADOW_ATLAS_COLUMNS * SHADOW_ATLAS_TILE_SIZE;
|
|
localShadowFBO = new FBO();
|
|
glGenTextures(1, &localShadowAtlas);
|
|
glActiveTexture(GL_TEXTURE10);
|
|
glBindTexture(GL_TEXTURE_2D, localShadowAtlas);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, shadowAtlasSize, shadowAtlasSize, 0,
|
|
GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
localShadowFBO->Bind();
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, localShadowAtlas, 0);
|
|
glDrawBuffer(GL_NONE);
|
|
glReadBuffer(GL_NONE);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
std::cout << "ERROR::FRAMEBUFFER:: Shadow atlas is not complete!\n";
|
|
localShadowFBO->Unbind();
|
|
|
|
// The shadow map uses compare mode, which can't be read as plain depth without this
|
|
glGenSamplers(1, &shadowDebugSampler);
|
|
glSamplerParameteri(shadowDebugSampler, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
glSamplerParameteri(shadowDebugSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glSamplerParameteri(shadowDebugSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
lighting = new Lighting();
|
|
}
|
|
|
|
void RenderSystem::AllocateGBuffer(int width, int height)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, gPosition);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
|
|
glBindTexture(GL_TEXTURE_2D, gNormal);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
|
|
glBindTexture(GL_TEXTURE_2D, gAlbedo);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
|
|
glBindTexture(GL_TEXTURE_2D, gMaterial);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
// Same format as the screen rbo, the depth blit between them requires it
|
|
glBindTexture(GL_TEXTURE_2D, gDepth);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
// Quarter resolution, the blur reaches much further for the same number of samples
|
|
void RenderSystem::AllocateBloom(int width, int height)
|
|
{
|
|
bloomWidth = glm::max(width / 4, 1);
|
|
bloomHeight = glm::max(height / 4, 1);
|
|
for (unsigned int texture : bloomTextures) {
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, bloomWidth, bloomHeight, 0, GL_RGBA, GL_FLOAT, NULL);
|
|
}
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
void RenderSystem::CycleDebugView()
|
|
{
|
|
static const char* const viewNames[] = { "FINAL", "POSITION", "NORMAL", "ALBEDO", "MATERIAL", "DEPTH", "SHADOW MAP", "BLOOM", "SHADOW ATLAS" };
|
|
debugView = (debugView + 1) % 9;
|
|
std::cout << "DEBUG VIEW: " << viewNames[debugView] << "\n";
|
|
}
|
|
|
|
void RenderSystem::Resize(int width, int height)
|
|
{
|
|
if (!screenfbo || !gBuffer || !bloomFBO[0] || textureColorbuffer == 0 || rbo == 0 || width <= 0 || height <= 0)
|
|
return;
|
|
|
|
AllocateBloom(width, height);
|
|
AllocateGBuffer(width, height);
|
|
gBuffer->Bind();
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
std::cout << "ERROR::FRAMEBUFFER:: G-buffer is not complete after resize!\n";
|
|
gBuffer->Unbind();
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
screenfbo->Bind();
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete after resize!\n";
|
|
screenfbo->Unbind();
|
|
|
|
glViewport(0, 0, width, height);
|
|
}
|
|
|
|
// ============================================================================
|
|
// RenderTranslucent
|
|
// Collects all non-opaque 3D renderables (mesh + particle), sorts back-to-front,
|
|
// then renders each with the correct shader. Blending stays on throughout.
|
|
// ============================================================================
|
|
void RenderSystem::RenderTranslucent(Camera& camera, const glm::mat4& lightSpaceMatrix,
|
|
const glm::vec3& camPos)
|
|
{
|
|
// Collect translucent 3D renderables
|
|
std::vector<std::pair<float, Renderable*>> sorted;
|
|
for (Renderable* r : renderables) {
|
|
if (r->opaque) continue;
|
|
if (r->shadertype != Renderable::MeshShader &&
|
|
r->shadertype != Renderable::ParticleShader &&
|
|
r->shadertype != Renderable::RigShader) continue;
|
|
|
|
glm::vec3 pos = r->GetWorldPosition();
|
|
float dist = glm::length(pos - camPos);
|
|
sorted.push_back({ dist, r });
|
|
}
|
|
|
|
// Sort back-to-front
|
|
std::sort(sorted.begin(), sorted.end(),
|
|
[](const auto& a, const auto& b) { return a.first > b.first; });
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glDepthMask(GL_FALSE); // write to color but not depth � prevents z-fighting between translucents
|
|
|
|
Renderable::ShaderType lastShader = Renderable::ShaderType::MeshShader;
|
|
bool shaderSet = false;
|
|
|
|
for (auto& [dist, r] : sorted) {
|
|
// Only re-bind shader when it changes
|
|
if (!shaderSet || r->shadertype != lastShader) {
|
|
if (r->shadertype == Renderable::MeshShader) {
|
|
PrepareMeshShader(camera);
|
|
MeshShader->SetMat4("lightSpaceMatrix", lightSpaceMatrix);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, depthMaptexture);
|
|
}
|
|
else if (r->shadertype == Renderable::ParticleShader) {
|
|
PrepareParticleShader(camera);
|
|
}
|
|
else if (r->shadertype == Renderable::RigShader) {
|
|
PrepareRigShader(camera);
|
|
RigShader->SetMat4("lightSpaceMatrix", lightSpaceMatrix);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, depthMaptexture);
|
|
}
|
|
lastShader = r->shadertype;
|
|
shaderSet = true;
|
|
}
|
|
|
|
Shader* sh = (r->shadertype == Renderable::MeshShader) ? MeshShader
|
|
: (r->shadertype == Renderable::ParticleShader) ? ParticleShader
|
|
: RigShader;
|
|
r->Render(*sh);
|
|
}
|
|
|
|
glDepthMask(GL_TRUE); // restore depth writing for subsequent passes
|
|
}
|
|
|
|
// ============================================================================
|
|
// Render
|
|
// ============================================================================
|
|
void RenderSystem::Render(Camera& camera)
|
|
{
|
|
lighting->sun.direction = glm::normalize(glm::quat(glm::vec3(glm::radians(0.05f), 0.0f, 0.0f)) * lighting->sun.direction);
|
|
|
|
const glm::mat4 proj = camera.GetProjectionMatrix(0.05f, 2000.0f);
|
|
const glm::mat4 view = camera.GetViewMatrix();
|
|
const glm::vec3 camPos = camera.t.GetTranslation();
|
|
|
|
const float near_plane = 0.1f, far_plane = 100.0f;
|
|
const glm::mat4 lightSpaceMatrix =
|
|
glm::ortho(-32.0f, 32.0f, -32.0f, 32.0f, near_plane, far_plane)
|
|
* glm::lookAt(camPos - lighting->sun.direction * 10.0f,
|
|
camPos, camera.t.GetUpVector());
|
|
|
|
// ---- Shadow pass ----
|
|
glCullFace(GL_FRONT);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glViewport(0, 0, SHADOW_RESOLUTION, SHADOW_RESOLUTION);
|
|
shadowdepthmapfbo->Bind();
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
if (graphics.shadows)
|
|
RenderShadowCasters(lightSpaceMatrix); // with nothing drawn, the cleared map leaves everything lit
|
|
RenderLocalShadows();
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
// ---- Geometry pass ----
|
|
RenderGeometryPass(proj, view, camPos);
|
|
|
|
// ---- Main scene ----
|
|
// Translucents and hitboxes are still forward, they depth test against the G-buffer's depth
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer->ID);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, screenfbo->ID);
|
|
glBlitFramebuffer(0, 0, window->width, window->height, 0, 0, window->width, window->height,
|
|
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
|
screenfbo->Bind();
|
|
glClearColor(212.f / 255.f, 223.f / 255.f, 232.f / 255.f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
// Skybox, depth tested so nothing covered by geometry gets shaded
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
glDepthFunc(GL_LEQUAL);
|
|
glDepthMask(GL_FALSE);
|
|
SkyboxShader->Activate();
|
|
SkyboxShader->SetMat4("proj", proj);
|
|
SkyboxShader->SetMat4("view", glm::mat4(glm::mat3(view)));
|
|
skyboxVAO->Bind();
|
|
glActiveTexture(GL_TEXTURE0);
|
|
skybox->Bind();
|
|
glDrawArrays(GL_TRIANGLES, 0, 36);
|
|
glBindVertexArray(0);
|
|
glDepthMask(GL_TRUE);
|
|
glDepthFunc(GL_LESS);
|
|
|
|
// ---- Lighting pass ----
|
|
glDisable(GL_DEPTH_TEST);
|
|
RenderLightingPass(camPos, lightSpaceMatrix);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
// ---- Translucent pass (meshes + particles + rigs, sorted back-to-front) ----
|
|
// Disable face culling so thin translucent quads render both sides
|
|
glDisable(GL_CULL_FACE);
|
|
RenderTranslucent(camera, lightSpaceMatrix, camPos);
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
RenderHitboxDebug(camera);
|
|
|
|
// ---- Bloom ----
|
|
glDisable(GL_DEPTH_TEST);
|
|
if (graphics.bloom)
|
|
RenderBloom();
|
|
else
|
|
screenfbo->Unbind();
|
|
|
|
// ---- Framebuffer blit to screen ----
|
|
glViewport(0, 0, window->width, window->height);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
if (debugView == 0) {
|
|
ScreenShader->Activate();
|
|
ScreenShader->Set1F("exposure", 1.0f);
|
|
ScreenShader->Set1F("bloomStrength", graphics.bloom ? 0.6f : 0.0f);
|
|
ScreenShader->SetInt("screenTexture", 0);
|
|
ScreenShader->SetInt("bloomTexture", 1);
|
|
quadVAO->Bind();
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D, bloomTextures[0]);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
}
|
|
else {
|
|
RenderDebugView();
|
|
}
|
|
|
|
// ---- GUI (always on top, no depth) ----
|
|
glDisable(GL_CULL_FACE);
|
|
PrepareBoxShader(camera);
|
|
for (Renderable* r : renderables) {
|
|
if (r->shadertype == Renderable::BoxShader)
|
|
r->Render(*BoxShader);
|
|
}
|
|
|
|
PrepareImageBoxShader(camera);
|
|
for (Renderable* r : renderables) {
|
|
if (r->shadertype == Renderable::ImageBoxShader)
|
|
r->Render(*ImageBoxShader);
|
|
}
|
|
|
|
PrepareTextShader(camera);
|
|
for (Renderable* r : renderables) {
|
|
if (r->shadertype == Renderable::TextShader)
|
|
r->Render(*Text2DShader);
|
|
}
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glfwSwapBuffers(window->handle);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Deferred passes
|
|
// ============================================================================
|
|
void RenderSystem::RenderGeometryPass(const glm::mat4& proj, const glm::mat4& view, const glm::vec3& camPos)
|
|
{
|
|
gBuffer->Bind();
|
|
glViewport(0, 0, window->width, window->height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_BLEND); // the attachments store data in alpha, blending would corrupt them
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// Frustum planes as (normal, distance), pulled straight out of the view projection matrix
|
|
const glm::mat4 viewProjection = proj * view;
|
|
glm::vec4 frustum[6];
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
const glm::vec4 row(viewProjection[0][axis], viewProjection[1][axis], viewProjection[2][axis], viewProjection[3][axis]);
|
|
const glm::vec4 last(viewProjection[0][3], viewProjection[1][3], viewProjection[2][3], viewProjection[3][3]);
|
|
frustum[axis * 2] = last + row;
|
|
frustum[axis * 2 + 1] = last - row;
|
|
}
|
|
occlusionCandidates.clear();
|
|
|
|
GeometryShader->Activate();
|
|
GeometryShader->SetMat4("proj", proj);
|
|
GeometryShader->SetMat4("view", view);
|
|
for (Renderable* r : renderables) {
|
|
if (r->shadertype == Renderable::MeshShader && r->opaque && IsVisible(r, frustum, camPos))
|
|
r->Render(*GeometryShader);
|
|
}
|
|
|
|
GeometryRigShader->Activate();
|
|
GeometryRigShader->SetMat4("proj", proj);
|
|
GeometryRigShader->SetMat4("view", view);
|
|
for (Renderable* r : renderables) {
|
|
if (r->shadertype == Renderable::RigShader && r->opaque && IsVisible(r, frustum, camPos))
|
|
r->Render(*GeometryRigShader);
|
|
}
|
|
|
|
IssueOcclusionQueries(proj, view);
|
|
glEnable(GL_BLEND);
|
|
}
|
|
|
|
// Bounds padding, so a box sitting exactly on its object's surface can't fail its own depth test
|
|
static constexpr float OCCLUSION_PADDING = 0.05f;
|
|
|
|
bool RenderSystem::IsVisible(Renderable* r, const glm::vec4 (&frustum)[6], const glm::vec3& camPos)
|
|
{
|
|
BoundingBox bounds;
|
|
if (!graphics.culling || !r->GetWorldBounds(bounds)) {
|
|
r->occluded = false;
|
|
return true;
|
|
}
|
|
|
|
// Frustum: outside if the corner furthest along a plane's normal is still behind it
|
|
for (const glm::vec4& plane : frustum) {
|
|
const glm::vec3 normal(plane);
|
|
const glm::vec3 furthest(
|
|
normal.x >= 0.0f ? bounds.max.x : bounds.min.x,
|
|
normal.y >= 0.0f ? bounds.max.y : bounds.min.y,
|
|
normal.z >= 0.0f ? bounds.max.z : bounds.min.z);
|
|
if (glm::dot(normal, furthest) + plane.w < 0.0f) {
|
|
r->occluded = false; // so it draws straight away when it comes back into view
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Only use an answer the GPU already has, waiting for one would stall the frame
|
|
if (r->occlusionPending) {
|
|
GLuint available = 0;
|
|
glGetQueryObjectuiv(r->occlusionQuery, GL_QUERY_RESULT_AVAILABLE, &available);
|
|
if (available) {
|
|
GLuint samplesPassed = 0;
|
|
glGetQueryObjectuiv(r->occlusionQuery, GL_QUERY_RESULT, &samplesPassed);
|
|
r->occluded = samplesPassed == 0;
|
|
r->occlusionPending = false;
|
|
}
|
|
}
|
|
|
|
// The query box can't be seen from inside it, so never trust it then
|
|
const glm::vec3 padding(OCCLUSION_PADDING * 2.0f);
|
|
if (glm::all(glm::greaterThan(camPos, bounds.min - padding)) && glm::all(glm::lessThan(camPos, bounds.max + padding)))
|
|
r->occluded = false;
|
|
|
|
occlusionCandidates.push_back(r);
|
|
return !r->occluded;
|
|
}
|
|
|
|
// Draws each candidate's box against this frame's depth, colour and depth writes off.
|
|
// The results get used next frame.
|
|
void RenderSystem::IssueOcclusionQueries(const glm::mat4& proj, const glm::mat4& view)
|
|
{
|
|
if (occlusionCandidates.empty())
|
|
return;
|
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
glDepthMask(GL_FALSE);
|
|
glDisable(GL_CULL_FACE); // the skybox cube faces inward
|
|
|
|
HitboxShader->Activate();
|
|
HitboxShader->SetMat4("proj", proj);
|
|
HitboxShader->SetMat4("view", view);
|
|
skyboxVAO->Bind();
|
|
|
|
for (Renderable* r : occlusionCandidates) {
|
|
if (r->occlusionPending)
|
|
continue;
|
|
|
|
BoundingBox bounds;
|
|
r->GetWorldBounds(bounds);
|
|
const glm::vec3 center = (bounds.min + bounds.max) * 0.5f;
|
|
const glm::vec3 halfSize = (bounds.max - bounds.min) * 0.5f + glm::vec3(OCCLUSION_PADDING);
|
|
|
|
if (r->occlusionQuery == 0)
|
|
glGenQueries(1, &r->occlusionQuery);
|
|
|
|
// The skybox cube runs from -1 to 1, so half size is the scale
|
|
HitboxShader->SetMat4("modl", glm::scale(glm::translate(glm::mat4(1.0f), center), halfSize));
|
|
glBeginQuery(GL_ANY_SAMPLES_PASSED, r->occlusionQuery);
|
|
glDrawArrays(GL_TRIANGLES, 0, 36);
|
|
glEndQuery(GL_ANY_SAMPLES_PASSED);
|
|
r->occlusionPending = true;
|
|
}
|
|
|
|
glBindVertexArray(0);
|
|
glEnable(GL_CULL_FACE);
|
|
glDepthMask(GL_TRUE);
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
}
|
|
|
|
void RenderSystem::RenderShadowCasters(const glm::mat4& lightSpaceMatrix)
|
|
{
|
|
MeshShadowShader->Activate();
|
|
MeshShadowShader->SetMat4("lightSpaceMatrix", lightSpaceMatrix);
|
|
for (Renderable* r : renderables) {
|
|
if (!r->castshadow || r->shadertype != Renderable::MeshShader) continue;
|
|
r->Render(*MeshShadowShader);
|
|
}
|
|
|
|
RigShadowShader->Activate();
|
|
RigShadowShader->SetMat4("lightSpaceMatrix", lightSpaceMatrix);
|
|
for (Renderable* r : renderables) {
|
|
if (!r->castshadow || r->shadertype != Renderable::RigShader) continue;
|
|
r->Render(*RigShadowShader);
|
|
}
|
|
}
|
|
|
|
// Draws the casters from each spot and point light into its tiles of the atlas
|
|
void RenderSystem::RenderLocalShadows()
|
|
{
|
|
static constexpr float nearPlane = 0.2f;
|
|
|
|
std::vector<glm::mat4>& tiles = lighting->shadowTileMatrices;
|
|
tiles.clear();
|
|
|
|
if (!graphics.lightShadows) {
|
|
for (SpotLight* light : lighting->spotLights)
|
|
light->shadowTile = -1;
|
|
for (PointLight* light : lighting->pointLights)
|
|
light->shadowTile = -1;
|
|
return;
|
|
}
|
|
|
|
localShadowFBO->Bind();
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
auto RenderTile = [&](const glm::mat4& matrix) {
|
|
const int index = (int)tiles.size();
|
|
const int x = (index % SHADOW_ATLAS_COLUMNS) * SHADOW_ATLAS_TILE_SIZE;
|
|
const int y = (index / SHADOW_ATLAS_COLUMNS) * SHADOW_ATLAS_TILE_SIZE;
|
|
glViewport(x, y, SHADOW_ATLAS_TILE_SIZE, SHADOW_ATLAS_TILE_SIZE);
|
|
glScissor(x, y, SHADOW_ATLAS_TILE_SIZE, SHADOW_ATLAS_TILE_SIZE);
|
|
RenderShadowCasters(matrix);
|
|
tiles.push_back(matrix);
|
|
};
|
|
|
|
for (SpotLight* light : lighting->spotLights) {
|
|
light->shadowTile = -1;
|
|
if (!light->castShadow || (int)tiles.size() >= SHADOW_ATLAS_TILES)
|
|
continue;
|
|
|
|
light->shadowTile = (int)tiles.size();
|
|
const glm::vec3 up = glm::abs(light->direction.y) > 0.99f ? glm::vec3(0.0f, 0.0f, 1.0f) : glm::vec3(0.0f, 1.0f, 0.0f);
|
|
RenderTile(glm::perspective(glm::radians(light->outerAngle * 2.0f + 2.0f), 1.0f, nearPlane, light->radius)
|
|
* glm::lookAt(light->position, light->position + light->direction, up));
|
|
}
|
|
|
|
// Order has to match PointShadowFace in the shaders. Slightly over 90 degrees so the faces overlap at the seams.
|
|
static const glm::vec3 faceDirections[6] = {
|
|
{ 1.0f, 0.0f, 0.0f }, { -1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f },
|
|
{ 0.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, -1.0f }
|
|
};
|
|
static const glm::vec3 faceUps[6] = {
|
|
{ 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f },
|
|
{ 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }
|
|
};
|
|
|
|
for (PointLight* light : lighting->pointLights) {
|
|
light->shadowTile = -1;
|
|
if (!light->castShadow || (int)tiles.size() + 6 > SHADOW_ATLAS_TILES)
|
|
continue;
|
|
|
|
light->shadowTile = (int)tiles.size();
|
|
const glm::mat4 projection = glm::perspective(glm::radians(92.0f), 1.0f, nearPlane, light->radius);
|
|
for (int face = 0; face < 6; face++)
|
|
RenderTile(projection * glm::lookAt(light->position, light->position + faceDirections[face], faceUps[face]));
|
|
}
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
localShadowFBO->Unbind();
|
|
}
|
|
|
|
// Leaves the finished glow in bloomTextures[0] and the default framebuffer bound
|
|
void RenderSystem::RenderBloom()
|
|
{
|
|
static constexpr int blurPasses = 8; // has to stay even, or the result ends up in bloomTextures[1]
|
|
|
|
glDisable(GL_BLEND);
|
|
glViewport(0, 0, bloomWidth, bloomHeight);
|
|
quadVAO->Bind();
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
bloomFBO[0]->Bind();
|
|
BloomExtractShader->Activate();
|
|
BloomExtractShader->SetInt("screenTexture", 0);
|
|
BloomExtractShader->Set1F("threshold", 1.8f);
|
|
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
BloomBlurShader->Activate();
|
|
BloomBlurShader->SetInt("image", 0);
|
|
for (int pass = 0; pass < blurPasses; pass++) {
|
|
const int source = pass % 2;
|
|
bloomFBO[1 - source]->Bind();
|
|
BloomBlurShader->SetInt("horizontal", source == 0 ? 1 : 0);
|
|
glBindTexture(GL_TEXTURE_2D, bloomTextures[source]);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
}
|
|
|
|
bloomFBO[0]->Unbind();
|
|
glEnable(GL_BLEND);
|
|
}
|
|
|
|
// Units 0 and 2 stay the shadow map and environment map, same as the forward shaders
|
|
void RenderSystem::BindGBufferTextures(Shader* shader)
|
|
{
|
|
shader->SetInt("gPosition", 3);
|
|
shader->SetInt("gNormal", 4);
|
|
shader->SetInt("gAlbedo", 5);
|
|
shader->SetInt("gMaterial", 6);
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glBindTexture(GL_TEXTURE_2D, gPosition);
|
|
glActiveTexture(GL_TEXTURE4);
|
|
glBindTexture(GL_TEXTURE_2D, gNormal);
|
|
glActiveTexture(GL_TEXTURE5);
|
|
glBindTexture(GL_TEXTURE_2D, gAlbedo);
|
|
glActiveTexture(GL_TEXTURE6);
|
|
glBindTexture(GL_TEXTURE_2D, gMaterial);
|
|
}
|
|
|
|
// Expects depth testing off, the fullscreen quad shouldn't touch the depth buffer
|
|
void RenderSystem::RenderLightingPass(const glm::vec3& camPos, const glm::mat4& lightSpaceMatrix)
|
|
{
|
|
DeferredLightingShader->Activate();
|
|
DeferredLightingShader->Set3F("viewPos", camPos);
|
|
DeferredLightingShader->SetMat4("lightSpaceMatrix", lightSpaceMatrix);
|
|
DeferredLightingShader->SetInt("shadowMap", 0);
|
|
ConfigurePBR(DeferredLightingShader, lighting);
|
|
BindGBufferTextures(DeferredLightingShader);
|
|
glActiveTexture(GL_TEXTURE2);
|
|
skybox->Bind();
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, depthMaptexture);
|
|
quadVAO->Bind();
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
void RenderSystem::RenderDebugView()
|
|
{
|
|
GBufferDebugShader->Activate();
|
|
const bool showAtlas = debugView == 8; // reuses the shadow map view
|
|
GBufferDebugShader->SetInt("debugMode", showAtlas ? 6 : debugView);
|
|
GBufferDebugShader->Set1F("nearPlane", 0.05f);
|
|
GBufferDebugShader->Set1F("farPlane", 2000.0f);
|
|
BindGBufferTextures(GBufferDebugShader);
|
|
GBufferDebugShader->SetInt("depthTex", 7);
|
|
GBufferDebugShader->SetInt("shadowTex", 8);
|
|
glActiveTexture(GL_TEXTURE7);
|
|
glBindTexture(GL_TEXTURE_2D, gDepth);
|
|
glActiveTexture(GL_TEXTURE8);
|
|
glBindTexture(GL_TEXTURE_2D, showAtlas ? localShadowAtlas : depthMaptexture);
|
|
glBindSampler(8, shadowDebugSampler);
|
|
GBufferDebugShader->SetInt("bloomTex", 9);
|
|
glActiveTexture(GL_TEXTURE9);
|
|
glBindTexture(GL_TEXTURE_2D, bloomTextures[0]);
|
|
quadVAO->Bind();
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glBindSampler(8, 0);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Shader preparation helpers
|
|
// ============================================================================
|
|
void RenderSystem::RenderRenderable(Renderable* renderable, Camera& camera)
|
|
{
|
|
Shader* shader = (renderable->shadertype == Renderable::MeshShader) ? MeshShader
|
|
: (renderable->shadertype == Renderable::ParticleShader) ? ParticleShader
|
|
: RigShader;
|
|
shader->Activate();
|
|
renderable->Render(*shader);
|
|
}
|
|
|
|
void RenderSystem::RenderHitboxDebug(Camera& camera)
|
|
{
|
|
if (hitboxDebugLines.empty() || !HitboxShader || hitboxVAO == 0)
|
|
return;
|
|
|
|
HitboxShader->Activate();
|
|
HitboxShader->SetMat4("proj", camera.GetProjectionMatrix(0.05f, 2000.0f));
|
|
HitboxShader->SetMat4("view", camera.GetViewMatrix());
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_CULL_FACE);
|
|
glLineWidth(2.0f);
|
|
glBindVertexArray(hitboxVAO);
|
|
for (const HitboxDebugLine& line : hitboxDebugLines) {
|
|
HitboxShader->SetMat4("modl", line.transform);
|
|
HitboxShader->Set3F("hitboxColor", line.color);
|
|
glDrawArrays(GL_LINES, 0, 24);
|
|
}
|
|
glBindVertexArray(0);
|
|
glLineWidth(1.0f);
|
|
glEnable(GL_CULL_FACE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
void RenderSystem::PrepareMeshShader(Camera& camera)
|
|
{
|
|
MeshShader->Activate();
|
|
MeshShader->SetMat4("proj", camera.GetProjectionMatrix(0.05f, 2000.0f));
|
|
MeshShader->SetMat4("view", camera.GetViewMatrix());
|
|
MeshShader->Set3F("viewPos", camera.t.GetTranslation());
|
|
ConfigurePBR(MeshShader, lighting);
|
|
glActiveTexture(GL_TEXTURE2);
|
|
skybox->Bind();
|
|
glActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
void RenderSystem::PrepareRigShader(Camera& camera)
|
|
{
|
|
RigShader->Activate();
|
|
RigShader->SetMat4("proj", camera.GetProjectionMatrix(0.05f, 2000.0f));
|
|
RigShader->SetMat4("view", camera.GetViewMatrix());
|
|
RigShader->Set3F("viewPos", camera.t.GetTranslation());
|
|
ConfigurePBR(RigShader, lighting);
|
|
glActiveTexture(GL_TEXTURE2);
|
|
skybox->Bind();
|
|
glActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
void RenderSystem::PrepareParticleShader(Camera& camera)
|
|
{
|
|
ParticleShader->Activate();
|
|
ParticleShader->SetMat4("proj", camera.GetProjectionMatrix(0.05f, 2000.0f));
|
|
ParticleShader->SetMat4("view", camera.GetViewMatrix());
|
|
}
|
|
|
|
void RenderSystem::PrepareBoxShader(Camera& camera)
|
|
{
|
|
BoxShader->Activate();
|
|
BoxShader->Set2F("screenSize", { (float)window->width, (float)window->height });
|
|
}
|
|
|
|
void RenderSystem::PrepareImageBoxShader(Camera& camera)
|
|
{
|
|
ImageBoxShader->Activate();
|
|
ImageBoxShader->Set2F("screenSize", { (float)window->width, (float)window->height });
|
|
}
|
|
|
|
void RenderSystem::PrepareTextShader(Camera& camera)
|
|
{
|
|
Text2DShader->Activate();
|
|
Text2DShader->Set2F("screenSize", { (float)window->width, (float)window->height });
|
|
}
|
|
|
|
// ============================================================================
|
|
// Renderable base
|
|
// ============================================================================
|
|
Renderable::Renderable() {}
|
|
|
|
Renderable::~Renderable()
|
|
{
|
|
if (occlusionQuery != 0)
|
|
glDeleteQueries(1, &occlusionQuery);
|
|
engine->rendersystem->RemoveRenderable(this);
|
|
}
|
|
|
|
void Renderable::BindToRenderSystem()
|
|
{
|
|
engine->rendersystem->AddRenderable(this);
|
|
} |