// 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() { // FT_Face face; // if(FT_New_Face(ft, "C:\\Windows\\Fonts\\Arial.ttf", 0, &face)) { // std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; // assertUnreachable(); // } // // TESTING FONT // // glGenTextures(1, &texture); // // glBindTexture(GL_TEXTURE_2D, texture); // // glTexImage2D( // // GL_TEXTURE_2D, // // 0, // // GL_RED, // // face->glyph->bitmap.width, // // face->glyph->bitmap.rows, // // 0, // // GL_RED, // // GL_UNSIGNED_BYTE, // // face->glyph->bitmap.buffer // // ); // // // set texture options // // 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_MIN_FILTER, GL_LINEAR); // // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Mesh glyphMesh; // FT_Set_Pixel_Sizes(face, 0, 36); // FT_UInt glyph_index = FT_Get_Char_Index(face, 'B'); // auto error = FT_Load_Glyph( // face, /* handle to face object */ // glyph_index, /* glyph index */ // FT_LOAD_DEFAULT ); /* load flags, see below */ // if(error) { // std::cout << "Error loading glyph" << std::endl; // assertUnreachable(); // } // /* convert to an anti-aliased bitmap */ // error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL ); // if(error) { // std::cout << "Error rendering glyph" << std::endl; // assertUnreachable(); // } // // Shader // auto shdr = &this->renderManager->simpleTexturedShader->program; // shdr->bind(); // shdr->setMatrix(shdr->paramProjection, camera->getProjection()); // shdr->setMatrix(shdr->paramView, camera->transform->getWorldTransform()); // shdr->setMatrix(shdr->paramModel, glm::mat4(1.0f)); // shdr->setColor(shdr->paramColor, COLOR_WHITE); // // Texture // Texture texture; // texture.setSize( // face->glyph->bitmap.width, // face->glyph->bitmap.rows, // TEXTURE_FORMAT_R // ); // texture.wrapModeX = TEXTURE_WRAP_MODE_CLAMP_TO_EDGE; // texture.wrapModeY = TEXTURE_WRAP_MODE_CLAMP_TO_EDGE; // texture.buffer((uint8_t*)face->glyph->bitmap.buffer); // shdr->setBoolean(shdr->paramHasTexture, true); // shdr->setTexture(shdr->paramTexture, 0); // texture.bind(0); // this->renderManager->setRenderFlags(RENDER_MANAGER_RENDER_FLAG_DEPTH_TEST | RENDER_MANAGER_RENDER_FLAG_BLEND); // auto faceCloneForDebugging = face; // QuadMesh::initQuadMesh(&glyphMesh, // glm::vec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), glm::vec2(1, 0), // glm::vec2(0, 0), glm::vec2(0, 1), // 0.0f // ); // glyphMesh.draw(MESH_DRAW_MODE_TRIANGLES, 0, -1); this->shaderBuffer.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 == nullptr) { ++it; continue; } else 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); // Update shader parameter buffers with current knowledge struct RenderPipelineShaderBufferData shaderBufferData; shaderBufferData.projection = camera->getProjection(); shaderBufferData.view = camera->transform->getWorldTransform(); this->shaderBuffer.buffer(&shaderBufferData); this->camera = camera; // Get the list of things to render first. std::vector shaderPassItems; // Renderables auto renderables = scene->findComponents(); auto itRenderables = renderables.begin(); while(itRenderables != renderables.end()) { vectorAppend(&shaderPassItems, (*itRenderables)->getRenderPasses()); ++itRenderables; } // 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. Shader *boundShader = nullptr; std::map boundTextures; std::map boundBuffers; shaderbufferslot_t slot; // TODO: This will be editable! renderTarget->bind(); renderTarget->clear( RENDER_TARGET_CLEAR_FLAG_DEPTH | RENDER_TARGET_CLEAR_FLAG_COLOR ); // Shader items itPassItem = shaderPassItems.begin(); while(itPassItem != shaderPassItems.end()) { auto item = *itPassItem; // Bind the program. if(boundShader != item.shader) { boundShader = item.shader; boundShader->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; } // Bind the buffers to their slots slot = 0; auto itBufferSlot = item.parameterBuffers.begin(); while(itBufferSlot != item.parameterBuffers.end()) { auto location = itBufferSlot->first; auto buff = itBufferSlot->second; boundBuffers[itBufferSlot->first] = slot; buff->bind(slot); slot++; ++itBufferSlot; } // Now set each of the parameters. Nothing exciting here. auto itColors = item.colorValues.begin(); while(itColors != item.colorValues.end()) { item.shader->setColor(itColors->first, itColors->second); ++itColors; } auto itBool = item.boolValues.begin(); while(itBool != item.boolValues.end()) { item.shader->setBoolean(itBool->first, itBool->second); ++itBool; } auto itMat = item.matrixValues.begin(); while(itMat != item.matrixValues.end()) { item.shader->setMatrix(itMat->first, itMat->second); ++itMat; } auto itVec3 = item.vec3Values.begin(); while(itVec3 != item.vec3Values.end()) { item.shader->setVector3(itVec3->first, itVec3->second); ++itVec3; } auto itText = item.textureValues.begin(); while(itText != item.textureValues.end()) { item.shader->setTexture(itText->first, itText->second); ++itText; } auto itBuffer = item.parameterBuffers.begin(); while(itBuffer != item.parameterBuffers.end()) { item.shader->setParameterBuffer(itBuffer->first, boundBuffers[itBuffer->first]); ++itBuffer; } auto itFloat = item.floatValues.begin(); while(itFloat != item.floatValues.end()) { item.shader->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() { }