Dawn/src/dawn/display/RenderPipeline.cpp

231 lines
6.6 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"
#include "display/RenderManager.hpp"
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()) {
auto subScene = (*itSubScene)->getSubScene();
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) {
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 list of things to render first.
std::vector<struct RenderPipelineItem> pipelineItems;
// Meshes
auto meshes = scene->findComponents<MeshRenderer>();
auto itMesh = meshes.begin();
while(itMesh != meshes.end()) {
// Get Mesh
auto mesh = *itMesh;
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 do each pass.
auto passes = shader->getItemPasses(mesh, mat);
auto itPass = passes.begin();
while(itPass != passes.end()) {
struct RenderPipelineItem item;
item.mesh = mesh;
item.pass = *itPass;
// Do we need to get the W Vector?
if(item.pass.needsW) {
assertUnreachable();// TODO: Add distance from camera for W vector.
} else {
item.w = 0;
}
// Queue
pipelineItems.push_back(item);
++itPass;
}
// Now, for optimization, we bind the global parameters here, once for each
// shader.
if(shader->renderId != this->renderId) {
shader->setGlobalParameters(
camera->projection,
camera->transform->getWorldTransform()
);
shader->renderId = this->renderId;
}
++itMesh;
}
// TODO: Get UI stuff here.
// Now we've queued everything, let's sort the rendering queue by the priority
std::sort(
pipelineItems.begin(),
pipelineItems.end(),
[](struct RenderPipelineItem &a, struct RenderPipelineItem &b){
if(a.pass.orderShader == b.pass.orderShader) {
return a.w < b.w;
}
return a.pass.orderShader < b.pass.orderShader;
}
);
// Now we've sorted everything! Let's actually start rendering.
ShaderProgram *boundProgram = nullptr;
std::map<textureslot_t, Texture*> boundTextures;
auto renderTarget = camera->getRenderTarget();
assertNotNull(renderTarget);
// TODO: This will be editable!
renderTarget->bind();
renderTarget->clear(
RENDER_TARGET_CLEAR_FLAG_DEPTH |
RENDER_TARGET_CLEAR_FLAG_COLOR
);
this->renderManager->setRenderFlags(
RENDER_MANAGER_RENDER_FLAG_DEPTH_TEST |
RENDER_MANAGER_RENDER_FLAG_BLEND
);
auto itItems = pipelineItems.begin();
while(itItems != pipelineItems.end()) {
auto item = *itItems;
// Bind the program.
if(boundProgram != item.pass.shaderProgram) {
boundProgram->bind();
boundProgram = item.pass.shaderProgram;
}
// Bind the textures to the slots
auto itTextureSlot = item.pass.textureSlots.begin();
while(itTextureSlot != item.pass.textureSlots.end()) {
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.pass.colorValues.begin();
while(itColors != item.pass.colorValues.end()) {
item.pass.shaderProgram->setColor(itColors->first, itColors->second);
++itColors;
}
auto itBool = item.pass.boolValues.begin();
while(itBool != item.pass.boolValues.end()) {
item.pass.shaderProgram->setBoolean(itBool->first, itBool->second);
++itBool;
}
auto itMat = item.pass.matrixValues.begin();
while(itMat != item.pass.matrixValues.end()) {
item.pass.shaderProgram->setMatrix(itMat->first, itMat->second);
++itMat;
}
auto itVec3 = item.pass.vec3Values.begin();
while(itVec3 != item.pass.vec3Values.end()) {
item.pass.shaderProgram->setVector3(itVec3->first, itVec3->second);
++itVec3;
}
auto itText = item.pass.textureValues.begin();
while(itText != item.pass.textureValues.end()) {
item.pass.shaderProgram->setTexture(itText->first, itText->second);
++itText;
}
auto itFloat = item.pass.floatValues.begin();
while(itFloat != item.pass.floatValues.end()) {
item.pass.shaderProgram->setFloat(itFloat->first, itFloat->second);
++itFloat;
}
// Thank god that's done, now just draw the damn mesh.
item.mesh->mesh->draw(MESH_DRAW_MODE_TRIANGLES, 0, -1);
++itItems;
}
}
RenderPipeline::~RenderPipeline() {
}