#include "PostProcessing.hpp" #include "Mesh.hpp" #include "Shader.hpp" #include "Texture.hpp" #include "Framebuffer.hpp" #include "DrawContext.hpp" #include "PostEffect.hpp" #include "assets/Assets.hpp" #include PostProcessing::PostProcessing(size_t effectSlotsCount) : effectSlots(effectSlotsCount) { // Fullscreen quad mesh bulding PostProcessingVertex meshData[]{ {{-1.0f, -1.0f}}, {{-1.0f, 1.0f}}, {{1.0f, 1.0f}}, {{-1.0f, -1.0f}}, {{1.0f, 1.0f}}, {{1.0f, -1.0f}}, }; quadMesh = std::make_unique>(meshData, 6, PostProcessingVertex::ATTRIBUTES); } PostProcessing::~PostProcessing() = default; void PostProcessing::use(DrawContext& context) { const auto& vp = context.getViewport(); if (fbo) { fbo->resize(vp.x, vp.y); fboSecond->resize(vp.x, vp.y); } else { fbo = std::make_unique(vp.x, vp.y); fboSecond = std::make_unique(vp.x, vp.y); } context.setFramebuffer(fbo.get()); } void PostProcessing::render( const DrawContext& context, const Assets& assets, float timer ) { if (fbo == nullptr) { throw std::runtime_error("'use(...)' was never called"); } int totalPasses = 0; for (const auto& effect : effectSlots) { totalPasses += (effect != nullptr && effect->isActive()); } if (totalPasses == 0) { auto& effect = assets.require("default"); effect.use(); fbo->getTexture()->bind(); quadMesh->draw(); return; } int currentPass = 1; for (const auto& effect : effectSlots) { if (effect == nullptr || !effect->isActive()) { continue; } auto& shader = effect->use(); const auto& viewport = context.getViewport(); shader.uniform1i("u_screen", 0); shader.uniform2i("u_screenSize", viewport); shader.uniform1f("u_timer", timer); fbo->getTexture()->bind(); if (currentPass < totalPasses) { fboSecond->bind(); } quadMesh->draw(); if (currentPass < totalPasses) { fboSecond->unbind(); std::swap(fbo, fboSecond); } currentPass++; } } void PostProcessing::setEffect(size_t slot, std::shared_ptr effect) { effectSlots.at(slot) = std::move(effect); } PostEffect* PostProcessing::getEffect(size_t slot) { return effectSlots.at(slot).get(); } std::unique_ptr PostProcessing::toImage() { return fbo->getTexture()->readData(); } Framebuffer* PostProcessing::getFramebuffer() const { return fbo.get(); }