💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

393 lines
13 KiB

/*
* Copyright (C) 2017-2019 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lighting-render-pass.hpp"
#include "resources/resource-manager.hpp"
#include "shadow-map-render-pass.hpp"
const std::uint64_t MATERIAL_FLAG_RIGGED = 0x0000000001;
const std::uint64_t MATERIAL_FLAG_TRANSLUCENT = 0x0000000002;
const std::uint64_t MATERIAL_FLAG_DISABLE_SHADOW_CASTING = 0x0000000004;
LightingRenderPass::LightingRenderPass(ResourceManager* resourceManager):
resourceManager(resourceManager),
shader(nullptr)
{}
bool LightingRenderPass::load(const RenderContext* renderContext)
{
// Setup constants
maxBoneCount = 64;
frustumSplitCount = 4;
wireframeLineWidth = 0.0f;
// Create bias matrix
biasMatrix = glm::translate(Vector3(0.5f)) * glm::scale(Vector3(0.5f));
// Setup permutation values
unskinnedPermutation = 0;
skinnedPermutation = 1;
// Load shader
shader = resourceManager->load<Shader>("lighting.glsl");
// Generate unskinned and skinned permutations
if (!shader->generatePermutation(unskinnedPermutation) || !shader->generatePermutation(skinnedPermutation))
{
std::cerr << std::string("LightingRenderPass: failed to generate shader permutation.") << std::endl;
return false;
}
// Allocate array shader parameters
matrixPaletteParam = new ShaderMatrix4(maxBoneCount);
lightViewProjectionMatricesParam = new ShaderMatrix4(frustumSplitCount);
// Connect shader parameters
modelMatrixParam.connect(shader->getInput("modelMatrix"));
modelViewMatrixParam.connect(shader->getInput("modelViewMatrix"));
modelViewProjectionMatrixParam.connect(shader->getInput("modelViewProjectionMatrix"));
normalModelMatrixParam.connect(shader->getInput("normalModelMatrix"));
normalModelViewMatrixParam.connect(shader->getInput("normalModelViewMatrix"));
matrixPaletteParam->connect(shader->getInput("matrixPalette"));
lightViewProjectionMatricesParam->connect(shader->getInput("lightViewProjectionMatrices"));
splitDistancesParam.connect(shader->getInput("splitDistances"));
shadowMapParam.connect(shader->getInput("shadowMap"));
cameraPositionParam.connect(shader->getInput("cameraPosition"));
timeParam.connect(shader->getInput("time"));
directionalLightCountParam.connect(shader->getInput("directionalLightCount"));
directionalLightColorsParam.connect(shader->getInput("directionalLightColors"));
directionalLightDirectionsParam.connect(shader->getInput("directionalLightDirections"));
spotlightCountParam.connect(shader->getInput("spotlightCount"));
spotlightColorsParam.connect(shader->getInput("spotlightColors"));
spotlightPositionsParam.connect(shader->getInput("spotlightPositions"));
spotlightAttenuationsParam.connect(shader->getInput("spotlightAttenuations"));
spotlightDirectionsParam.connect(shader->getInput("spotlightDirections"));
spotlightCutoffsParam.connect(shader->getInput("spotlightCutoffs"));
spotlightExponentsParam.connect(shader->getInput("spotlightExponents"));
#if defined(DEBUG)
wireframeLineWidthParam.connect(shader->getInput("wireframeLineWidth"));
#endif
// Load tree shadow
time = 0.0f;
return true;
}
void LightingRenderPass::unload()
{
}
void LightingRenderPass::render(RenderContext* renderContext)
{
// Get camera parameters
const Camera& camera = *(renderContext->camera);
const Matrix4& view = camera.getViewTween()->getSubstate();
const Matrix4& projection = camera.getProjectionTween()->getSubstate();
const Vector3& cameraPosition = camera.getTransformTween()->getSubstate().translation;
// Gather lights
const std::list<SceneObject*>* lights = renderContext->scene->getObjects(SceneObjectType::LIGHT);
// Gather ambient cubes
AmbientCube* ambientCube = nullptr;
for (SceneObject* object: *lights)
{
Light* light = reinterpret_cast<Light*>(object);
if (light->getLightType() == LightType::AMBIENT_CUBE)
{
ambientCube = reinterpret_cast<AmbientCube*>(light);
break;
}
}
// Gather directional lights
const int MAX_DIRECTIONAL_LIGHT_COUNT = 3;
int directionalLightCount = 0;
Vector3 directionalLightColors[MAX_DIRECTIONAL_LIGHT_COUNT];
Vector3 directionalLightDirections[MAX_DIRECTIONAL_LIGHT_COUNT];
if (lights != nullptr)
{
for (auto object: *lights)
{
const Light* light = static_cast<const Light*>(object);
LightType lightType = light->getLightType();
if (lightType == LightType::DIRECTIONAL && light->isActive())
{
const DirectionalLight* directionalLight = static_cast<const DirectionalLight*>(light);
directionalLightColors[directionalLightCount] = directionalLight->getColorTween()->getSubstate() * directionalLight->getIntensityTween()->getSubstate();
directionalLightDirections[directionalLightCount] = glm::normalize(Vector3(view * Vector4(-directionalLight->getDirectionTween()->getSubstate(), 0.0f)));
++directionalLightCount;
}
}
}
directionalLightCountParam.setValue(directionalLightCount);
directionalLightColorsParam.setValues(0, directionalLightColors, directionalLightCount);
directionalLightDirectionsParam.setValues(0, directionalLightDirections, directionalLightCount);
// Gather spotlights
const int MAX_SPOTLIGHT_COUNT = 3;
int spotlightCount = 0;
Vector3 spotlightColors[MAX_SPOTLIGHT_COUNT];
Vector3 spotlightPositions[MAX_SPOTLIGHT_COUNT];
Vector3 spotlightAttenuations[MAX_SPOTLIGHT_COUNT];
Vector3 spotlightDirections[MAX_SPOTLIGHT_COUNT];
float spotlightCutoffs[MAX_SPOTLIGHT_COUNT];
float spotlightExponents[MAX_SPOTLIGHT_COUNT];
if (lights != nullptr)
{
for (auto object: *lights)
{
const Light* light = static_cast<const Light*>(object);
LightType lightType = light->getLightType();
if (lightType == LightType::SPOTLIGHT && light->isActive())
{
const Spotlight* spotlight = static_cast<const Spotlight*>(light);
spotlightColors[spotlightCount] = spotlight->getColorTween()->getSubstate() * spotlight->getIntensityTween()->getSubstate();
spotlightPositions[spotlightCount] = Vector3(view * Vector4(spotlight->getTransformTween()->getSubstate().translation, 1.0f));
spotlightAttenuations[spotlightCount] = spotlight->getAttenuationTween()->getSubstate();
spotlightDirections[spotlightCount] = glm::normalize(Vector3(view * Vector4(-spotlight->getDirectionTween()->getSubstate(), 0.0f)));
spotlightCutoffs[spotlightCount] = spotlight->getCutoffTween()->getSubstate();
spotlightExponents[spotlightCount] = spotlight->getExponentTween()->getSubstate();
++spotlightCount;
}
}
}
spotlightCountParam.setValue(spotlightCount);
spotlightColorsParam.setValues(0, spotlightColors, spotlightCount);
spotlightPositionsParam.setValues(0, spotlightPositions, spotlightCount);
spotlightAttenuationsParam.setValues(0, spotlightAttenuations, spotlightCount);
spotlightDirectionsParam.setValues(0, spotlightDirections, spotlightCount);
spotlightCutoffsParam.setValues(0, spotlightCutoffs, spotlightCount);
spotlightExponentsParam.setValues(0, spotlightExponents, spotlightCount);
#if defined(DEBUG)
wireframeLineWidthParam.setValue(wireframeLineWidth);
#endif
std::list<RenderOperation>* operations = renderContext->queue->getOperations();
// Bind framebuffer and setup viewport
glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer);
glViewport(0, 0, renderTarget->width, renderTarget->height);
// Enable depth testing
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
// Enable backface culling
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// Disable alpha blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
// Load shadow map pass parameters
Vector4 splitDistances;
for (int i = 0; i < 4; ++i)
{
splitDistances[i] = shadowMapPass->getSplitViewFrustum().getSplitDistance(i + 1);
}
// Calculate the (light-space) view-projection matrices
Matrix4 lightViewProjectionMatrices[4];
for (int i = 0; i < 4; ++i)
{
lightViewProjectionMatrices[i] = shadowMapPass->getTileMatrix(i) * biasMatrix * shadowMapPass->getCropMatrix(i) * shadowMapPass->getLightCamera()->getViewProjection();
}
// Set shader parameter values
lightViewProjectionMatricesParam->setValues(0, &lightViewProjectionMatrices[0], 4);
splitDistancesParam.setValue(splitDistances);
cameraPositionParam.setValue(cameraPosition);
shadowMapParam.setValue(shadowMap);
timeParam.setValue(time);
std::uint32_t permutation = 0xDEADBEEF;
bool blending = false;
GLuint boundVAO = 0;
// Sort operations
operations->sort(RenderOpCompare());
// Render operations
for (const RenderOperation& operation: *operations)
{
// Skip operations without materials
if (!operation.material)
{
continue;
}
bool hasTranslucency = operation.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT;
if (hasTranslucency && !blending)
{
glEnable(GL_BLEND);
blending = true;
}
// Select permutation
std::uint32_t targetPermutation = (operation.pose != nullptr) ? skinnedPermutation : unskinnedPermutation;
if (permutation != targetPermutation)
{
permutation = targetPermutation;
shader->activate(permutation);
// Pass static params
lightViewProjectionMatricesParam->upload();
splitDistancesParam.upload();
cameraPositionParam.upload();
shadowMapParam.upload();
timeParam.upload();
directionalLightCountParam.upload();
if (directionalLightCount > 0)
{
directionalLightColorsParam.upload();
directionalLightDirectionsParam.upload();
}
spotlightCountParam.upload();
if (spotlightCount > 0)
{
spotlightColorsParam.upload();
spotlightPositionsParam.upload();
spotlightAttenuationsParam.upload();
spotlightDirectionsParam.upload();
spotlightCutoffsParam.upload();
spotlightExponentsParam.upload();
}
#if defined(DEBUG)
wireframeLineWidthParam.upload();
#endif
}
const Matrix4& modelMatrix = operation.transform;
Matrix4 modelViewMatrix = view * modelMatrix;
Matrix4 modelViewProjectionMatrix = projection * modelViewMatrix;
Matrix3 normalModelViewMatrix = glm::transpose(glm::inverse(Matrix3(modelViewMatrix)));
Matrix3 normalModelMatrix = glm::transpose(glm::inverse(Matrix3(modelMatrix)));
modelMatrixParam.setValue(modelMatrix);
modelViewMatrixParam.setValue(modelViewMatrix);
modelViewProjectionMatrixParam.setValue(modelViewProjectionMatrix);
normalModelViewMatrixParam.setValue(normalModelViewMatrix);
normalModelMatrixParam.setValue(normalModelMatrix);
// Upload matrix parameters
modelMatrixParam.upload();
modelViewMatrixParam.upload();
modelViewProjectionMatrixParam.upload();
normalModelViewMatrixParam.upload();
normalModelMatrixParam.upload();
// Upload pose matrix palette
if (operation.pose != nullptr && matrixPaletteParam->isConnected())
{
matrixPaletteParam->getConnectedInput()->upload(0, operation.pose->getMatrixPalette(), operation.pose->getSkeleton()->getBoneCount());
}
// Upload material parameters
if (operation.material != nullptr)
{
if (operation.material->getShader() != shader)
{
((Material*)operation.material)->setShader(shader);
}
operation.material->upload();
}
if (boundVAO != operation.vao)
{
glBindVertexArray(operation.vao);
boundVAO = operation.vao;
}
glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset);
}
}
bool LightingRenderPass::RenderOpCompare::operator()(const RenderOperation& opA, const RenderOperation& opB) const
{
if (!opA.material)
return false;
else if (!opB.material)
return true;
// Determine transparency
bool transparentA = opA.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT;
bool transparentB = opB.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT;
if (transparentA)
{
if (transparentB)
{
// A and B are both transparent, render back to front
return (opA.depth >= opB.depth);
}
else
{
// A is transparent, B is opaque. Render B first
return false;
}
}
else
{
if (transparentB)
{
// A is opaque, B is transparent. Render A first
return true;
}
else
{
// A and B are both opaque
if (opA.material->getShader() == opB.material->getShader())
{
// A and B have the same shader
if (opA.vao == opB.vao)
{
// A and B have the same VAO, sort by depth
return (opA.depth < opB.depth);
}
else
{
// Sort by VAO
return (opA.vao < opB.vao);
}
}
}
}
// A and B are both opaque and have different shaders, sort by shader
return (opA.material->getShader() < opB.material->getShader());
}