/* * * Copyright (C) 2024 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #define __EMSCRIPTEN__ float full_screen_vertices[20] = { 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f }; const unsigned int full_screen_indices[6] = { 0, 1, 3, 1, 2, 3 }; template bool once() { static bool ran = true; return std::exchange(ran, false); } namespace blt::gfx { void pp_engine_t::create() { { vertex_buffer_t vertices_vbo; element_buffer_t indices_vbo; vertices_vbo.create(); indices_vbo.create(); vertices_vbo.allocate(sizeof(full_screen_vertices), full_screen_vertices); indices_vbo.allocate(sizeof(full_screen_indices), full_screen_indices); screen_vao = std::make_unique(); const auto tb = screen_vao->bindVBO(vertices_vbo, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); screen_vao->bindVBO(tb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); screen_vao->bindElement(indices_vbo); } #ifdef __EMSCRIPTEN__ addStep(std::make_unique()); #endif for (auto& step : steps) step->create(); } pp_step_t& pp_engine_t::find_last_frame_buffer(size_t index) { for (i64 i = static_cast(index); i >= 0; i--) { if (steps[i]->requests(pp_request_t::BIND_BUFFER)) return *steps[i]; } BLT_FATAL("We reached the end trying to find a previous frame buffer, unable to do so! Please ensure this in-place step has a framebuffer!"); std::exit(1); } void pp_engine_t::render() { screen_vao->bind(); for (const auto& [index, step] : blt::enumerate(steps)) { if (index == 0) continue; auto& previous = find_last_frame_buffer(index - 1); if (step->requests(pp_request_t::BIND_BUFFER)) step->getBuffer().bind(); else previous.getBuffer().bind(); if (step->requests(pp_request_t::CLEAR_BUFFER)) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); step->draw(previous.getBuffer()); if (step->hasShader()) step->getShader().bind(); render_quad(); step->post_draw(previous.getBuffer()); } glBindTexture(GL_TEXTURE_2D, 0); frame_buffer_t::unbind(); #ifndef __EMSCRIPTEN__ steps.back()->getBuffer().blitToScreen(getWindowWidth(), getWindowHeight()); #endif } void pp_engine_t::cleanup() { screen_vao = nullptr; for (auto& v : steps) v = nullptr; steps.clear(); } std::unique_ptr pp_engine_t::createShader(std::string_view fragment) { auto shader = std::make_unique(shader_postprocess_vert, std::string(fragment)); shader->bindAttribute(0, "vertex"); shader->bindAttribute(1, "uv_in"); return shader; } void pp_engine_t::bind() { steps.front()->getBuffer().bind(); steps.front()->draw(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } void pp_engine_t::render_quad() { glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } void pp_in_place_t::create() { shader_pass = pp_engine_t::createShader(shader_pp_screen_frag); draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight()); } void pp_in_place_t::post_draw(frame_buffer_t& previous) { draw_buffer.blitTexture(previous, 0, 0, 0, 0, GL_NEAREST, from, to); // previous.bind(); // GLenum buf[32]; // auto buff_val = static_cast(to); // auto size = buff_val - GL_COLOR_ATTACHMENT0; // for (GLenum i = 0; i < size; i++) // buf[i] = GL_NONE; // buf[size] = buff_val; // glDrawBuffers(static_cast(size) + 1, buf); // glClear(GL_COLOR_BUFFER_BIT); // shader_pass->bind(); // glActiveTexture(GL_TEXTURE0); // draw_buffer.getTexture(frame_buffer_t::attachment_t::COLOR0).bind(); // pp_engine_t::render_quad(); } void pp_in_place_t::draw(frame_buffer_t& previous) { draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight()); glActiveTexture(GL_TEXTURE0); previous.getTexture(from).bind(); draw_buffer.bind(); GLenum buf[]{static_cast(frame_buffer_t::attachment_t::COLOR0)}; glDrawBuffers(1, buf); glClear(GL_COLOR_BUFFER_BIT); } void pp_to_screen_step_t::create() { shader = pp_engine_t::createShader(shader_pp_screen_frag); } void pp_to_screen_step_t::draw(frame_buffer_t& previous) { glActiveTexture(GL_TEXTURE0); previous.getTexture(frame_buffer_t::attachment_t::COLOR0).bind(); GLenum buf[]{GL_BACK}; glDrawBuffers(1, buf); } void pp_render_target_t::create() { draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight()); } void pp_render_target_t::draw() { draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight()); GLenum buf[]{static_cast(frame_buffer_t::attachment_t::COLOR0)}; glDrawBuffers(1, buf); } void pp_outline_target_t::create() { draw_buffer.create(); draw_buffer.bind(); auto* texture = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8); draw_buffer.attachTexture(texture, frame_buffer_t::attachment_t::COLOR0); auto* mask = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8); draw_buffer.attachTexture(mask, frame_buffer_t::attachment_t::COLOR1); render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, getWindowWidth(), getWindowHeight()); draw_buffer.attachRenderBuffer(depth_rbo, frame_buffer_t::attachment_t::DEPTH_STENCIL); if (!frame_buffer_t::validate()) BLT_ERROR("Failed to create render texture framebuffer!"); frame_buffer_t::unbind(); } void pp_outline_target_t::draw() { draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight()); const GLenum buffers[]{static_cast(frame_buffer_t::attachment_t::COLOR0), static_cast(frame_buffer_t::attachment_t::COLOR1)}; glDrawBuffers(2, buffers); } void pp_outline_step_t::create() { draw_buffer = frame_buffer_t::make_render_texture(getWindowHeight(), getWindowHeight()); shader = pp_engine_t::createShader(shader_pp_outline_step_frag); shader->setInt("albedo", 0); shader->setInt("mask", 1); } void pp_outline_step_t::draw(frame_buffer_t& previous) { draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight()); glActiveTexture(GL_TEXTURE0); previous.getTexture(frame_buffer_t::attachment_t::COLOR0).bind(); glActiveTexture(GL_TEXTURE1); previous.getTexture(frame_buffer_t::attachment_t::COLOR1).bind(); shader->bind(); //auto v = vec2(getWindowWidth(), getWindowHeight()) * (1.0 / blt::make_vec2(manager.getScale2D())); //BLT_TRACE_STREAM << v << '\n'; //shader->setVec2("viewportSize", static_cast(v.x()), static_cast(v.y())); GLenum buf[]{static_cast(frame_buffer_t::attachment_t::COLOR0)}; glDrawBuffers(1, buf); } void pp_blur_step_inplace_t::create() { pp_in_place_t::create(); shader = pp_engine_t::createShader(shader_gaussian_blur_frag); } void pp_blur_step_inplace_t::draw(frame_buffer_t& previous) { pp_in_place_t::draw(previous); shader->bind(); //auto v = vec2(getWindowWidth(), getWindowHeight()) * (1.0 / blt::make_vec2(manager.getScale2D())); auto v = vec2(getWindowWidth(), getWindowHeight()); shader->setVec4i("size", {static_cast(v.x()), static_cast(v.y()), x_blur, y_blur}); } void pp_expansion_step_inplace_t::create() { pp_in_place_t::create(); shader = pp_engine_t::createShader(shader_multiplier_frag); } void pp_expansion_step_inplace_t::draw(frame_buffer_t& previous) { pp_in_place_t::draw(previous); shader->bind(); shader->setVec4("multiplier", multiplier); } }