// 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(); 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 *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::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 shaderPassItems; // Meshes auto meshes = scene->findComponents(); 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(); 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(); 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(); 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 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() { }