231 lines
6.6 KiB
C++
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() {
|
|
|
|
} |