Dawn/src/dawn/display/RenderPipeline.cpp

264 lines
7.7 KiB
C++

// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "RenderPipeline.hpp"
#include "game/DawnGame.hpp"
#if DAWN_DEBUG_BUILD
#include "scene/debug/SceneDebugLine.hpp"
#endif
using namespace Dawn;
RenderPipeline::RenderPipeline(RenderManager *renderManager) {
assertNotNull(renderManager);
this->renderManager = renderManager;
}
void RenderPipeline::init() {
}
void RenderPipeline::render() {
if(this->renderManager->game->scene != nullptr) {
this->renderScene(this->renderManager->game->scene);
}
}
void RenderPipeline::renderScene(Scene *scene) {
assertNotNull(scene);
// Render subscenes first.
auto subSceneControllers = scene->findComponents<SubSceneController>();
auto itSubScene = subSceneControllers.begin();
while(itSubScene != subSceneControllers.end()) {
Scene *subScene = (Scene *)((*itSubScene)->subScene);
if(subScene == nullptr) {
++itSubScene;
continue;
}
if((*itSubScene)->onlyUpdateUnpaused && scene->game->timeManager.isPaused) {
++itSubScene;
continue;
}
this->renderScene(subScene);
++itSubScene;
}
// Now render backbuffers.
auto backBuffer = this->renderManager->getBackBuffer();
auto cameras = scene->findComponents<Camera>();
Camera *backBufferCamera = nullptr;
// First, render all non-backbuffer cameras.
auto it = cameras.begin();
while(it != cameras.end()) {
RenderTarget *cameraTarget = (*it)->getRenderTarget();
// Leave the backbuffer camera(s) to last, so we skip them. we do this so
// that children framebuffers contain the CURRENT frame, not LAST frame.
if(cameraTarget == backBuffer) {
backBufferCamera = *it;
} else {
this->renderSceneCamera(scene, *it);
}
++it;
}
// Now render the backbuffer camera.
if(backBufferCamera == nullptr) return;
this->renderSceneCamera(scene, backBufferCamera);
}
void RenderPipeline::renderSceneCamera(Scene *scene, Camera *camera) {
std::vector<struct ShaderPassItem>::iterator itPassItem;
assertNotNull(scene);
assertNotNull(camera);
// Create a new render ID. Long story short this is a really dirty way of
// not sending parameters to shaders more than we need.
this->renderId--;
// Get the render target.
auto renderTarget = camera->getRenderTarget();
assertNotNull(renderTarget);
// Get the list of things to render first.
std::vector<struct ShaderPassItem> shaderPassItems;
// Meshes
auto meshes = scene->findComponents<MeshRenderer>();
auto itMesh = meshes.begin();
while(itMesh != meshes.end()) {
// Get Mesh
auto mesh = *itMesh;
assertNotNull(mesh);
assertNotNull(mesh->mesh);
// Make sure this mesh has a material
auto mat = mesh->item->getComponent<Material>();
assertNotNull(mat);
auto shader = mat->getShader();
assertNotNull(shader);
// Now get and validate the pass items for this material/shader
auto materialPassItems = shader->getPassItems(mesh->mesh, mat, camera);
itPassItem = materialPassItems.begin();
while(itPassItem != materialPassItems.end()) {
auto item = *itPassItem;
// Validate the pass
assertNotNull(item.mesh);
assertTrue(item.start >= 0);
assertTrue(item.count > 0 || item.count == -1);
assertNotNull(item.shaderProgram);
// Queue
shaderPassItems.push_back(item);
++itPassItem;
}
++itMesh;
}
// UI Elements
auto canvases = scene->findComponents<UICanvas>();
auto itCanvas = canvases.begin();
while(itCanvas != canvases.end()) {
auto canvas = *itCanvas;
glm::mat4 projection;
glm::mat4 view;
canvas->getProjectionAndView(&projection, &view);
auto renderables = canvas->item->findChildrenDeep<UIComponentRenderable>();
auto itChild = renderables.begin();
while(itChild != renderables.end()) {
vectorAppend(&shaderPassItems,(*itChild)->getPassItems(projection, view));
++itChild;
}
++itCanvas;
}
// Debug Lines
#if DAWN_DEBUG_BUILD
Mesh lineMesh;
if(scene->debugLines.size() > 0) {
int32_t lineIndex = 0;
lineMesh.createBuffers(
scene->debugLines.size() * SCENE_DEBUG_LINE_VERTICE_COUNT,
scene->debugLines.size() * SCENE_DEBUG_LINE_INDICE_COUNT
);
auto itDebugLine = scene->debugLines.begin();
while(itDebugLine != scene->debugLines.end()) {
auto item = itDebugLine->createShaderItem(
&lineMesh,
&lineIndex,
camera,
this->renderManager->simpleTexturedShader
);
shaderPassItems.push_back(item);
itDebugLine = scene->debugLines.erase(itDebugLine);
lineIndex++;
}
}
#endif
// Now we've queued everything, let's sort the rendering queue by the priority
std::sort(
shaderPassItems.begin(),
shaderPassItems.end(),
[](struct ShaderPassItem &a, struct ShaderPassItem &b) {
if(a.priority == b.priority) {
return a.w < b.w;
}
return a.priority < b.priority;
}
);
// Now we've sorted everything! Let's actually start rendering.
ShaderProgram *boundProgram = nullptr;
std::map<textureslot_t, Texture*> boundTextures;
// TODO: This will be editable!
renderTarget->bind();
renderTarget->clear(
RENDER_TARGET_CLEAR_FLAG_DEPTH |
RENDER_TARGET_CLEAR_FLAG_COLOR
);
itPassItem = shaderPassItems.begin();
while(itPassItem != shaderPassItems.end()) {
auto item = *itPassItem;
// Bind the program.
if(boundProgram != item.shaderProgram) {
boundProgram = item.shaderProgram;
boundProgram->bind();
}
// Bind the textures to the slots
auto itTextureSlot = item.textureSlots.begin();
while(itTextureSlot != item.textureSlots.end()) {
// Assert texture isn't null, just don't include it.
assertNotNull(itTextureSlot->second);
if(boundTextures[itTextureSlot->first] != itTextureSlot->second) {
itTextureSlot->second->bind(itTextureSlot->first);
boundTextures[itTextureSlot->first] = itTextureSlot->second;
}
++itTextureSlot;
}
// Now set each of the parameters. Nothing exciting here.
auto itColors = item.colorValues.begin();
while(itColors != item.colorValues.end()) {
item.shaderProgram->setColor(itColors->first, itColors->second);
++itColors;
}
auto itBool = item.boolValues.begin();
while(itBool != item.boolValues.end()) {
item.shaderProgram->setBoolean(itBool->first, itBool->second);
++itBool;
}
auto itMat = item.matrixValues.begin();
while(itMat != item.matrixValues.end()) {
item.shaderProgram->setMatrix(itMat->first, itMat->second);
++itMat;
}
auto itVec3 = item.vec3Values.begin();
while(itVec3 != item.vec3Values.end()) {
item.shaderProgram->setVector3(itVec3->first, itVec3->second);
++itVec3;
}
auto itText = item.textureValues.begin();
while(itText != item.textureValues.end()) {
item.shaderProgram->setTexture(itText->first, itText->second);
++itText;
}
auto itFloat = item.floatValues.begin();
while(itFloat != item.floatValues.end()) {
item.shaderProgram->setFloat(itFloat->first, itFloat->second);
++itFloat;
}
// Set Render flags
this->renderManager->setRenderFlags(item.renderFlags);
// Thank god that's done, now just draw the damn mesh.
item.mesh->draw(item.drawMode, item.start, item.count);
++itPassItem;
}
}
RenderPipeline::~RenderPipeline() {
}