/* * 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 "blt/std/assert.h" namespace blt::gfx { void render_buffer_t::create() { glGenRenderbuffers(1, &rboID); } void render_buffer_t::bind() const { glBindRenderbuffer(GL_RENDERBUFFER, rboID); } void render_buffer_t::unbind() { glBindRenderbuffer(GL_RENDERBUFFER, 0); } void render_buffer_t::destroy() { glDeleteRenderbuffers(1, &rboID); } void render_buffer_t::updateStorage(blt::i32 width, blt::i32 height) { width_ = width; height_ = height; samples = 0; glRenderbufferStorage(GL_RENDERBUFFER, storage_type, width, height); } void render_buffer_t::updateStorageMultiSampled(blt::i32 width, blt::i32 height, blt::i32 s) { width_ = width; height_ = height; samples = s; glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage_type, width, height); } void render_buffer_t::setStorage(GLuint s_type, blt::i32 width, blt::i32 height) { storage_type = s_type; updateStorage(width, height); } void render_buffer_t::setStorageMultiSampled(GLuint s_type, blt::i32 width, blt::i32 height, blt::i32 s) { storage_type = s_type; updateStorageMultiSampled(width, height, s); } void render_buffer_t::resize(blt::i32 width, blt::i32 height) { if (samples > 0) updateStorageMultiSampled(width, height, samples); else updateStorage(width, height); } render_buffer_t render_buffer_t::make_render_buffer(GLuint storage_type, blt::i32 width, blt::i32 height) { render_buffer_t rbo{}; rbo.create(); rbo.bind(); rbo.setStorage(storage_type, width, height); render_buffer_t::unbind(); return rbo; } render_buffer_t render_buffer_t::make_render_buffer(GLuint storage_type, blt::i32 width, blt::i32 height, blt::i32 samples) { render_buffer_t rbo{}; rbo.create(); rbo.bind(); rbo.setStorageMultiSampled(storage_type, width, height, samples); render_buffer_t::unbind(); return rbo; } void frame_buffer_t::create(frame_buffer_t::draw_t type) { generic_bind_type = static_cast(type); glGenFramebuffers(1, &fboID); } void frame_buffer_t::bind(draw_t type) const { glBindFramebuffer(static_cast(type), fboID); } void frame_buffer_t::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } void frame_buffer_t::destroy() { glDeleteFramebuffers(1, &fboID); for (auto& v : render_buffers) v.destroy(); for (auto* texture : texture_buffers) delete texture; } bool frame_buffer_t::validate() { return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; } void frame_buffer_t::attachTexture(blt::gfx::texture_gl* texture, attachment_t attachment, int level) { if (width_ == -1) { width_ = texture->getWidth(); height_ = texture->getHeight(); } else BLT_ASSERT_MSG(width_ == texture->getWidth() && height_ == texture->getHeight(), "Please ensure attached textures are the same size!"); texture_buffers.push_back(texture); glFramebufferTexture2D(generic_bind_type, static_cast(attachment), texture->getBindType(), texture->getTextureID(), level); } void frame_buffer_t::attachRenderBuffer(render_buffer_t rbo, frame_buffer_t::attachment_t attachment) { if (width_ == -1) { width_ = rbo.width_; height_ = rbo.height_; } else BLT_ASSERT_MSG(width_ == rbo.width_ && height_ == rbo.height_, "Please ensure attached renderbuffer is the same size!"); rbo.bind(); render_buffers.push_back(rbo); glFramebufferRenderbuffer(generic_bind_type, static_cast(attachment), GL_RENDERBUFFER, rbo.rboID); } void frame_buffer_t::updateBuffersStorage(blt::i32 width, blt::i32 height) { if (width == width_ && height == height_) return; if (fboID == 0) return; width_ = width; height_ = height; for (auto& rbo : render_buffers) { rbo.bind(); rbo.resize(width, height); } for (auto& texture : texture_buffers) texture->updateSize(width, height); if (!frame_buffer_t::validate()) BLT_ERROR("Failed to update framebuffer storage size!"); } void frame_buffer_t::blitTexture(const frame_buffer_t& draw, blt::i32 srcX, blt::i32 srcY, blt::i32 destX, blt::i32 destY, GLuint filter, attachment_t attachment_read, attachment_t attachment_write) const { glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.fboID); glReadBuffer(static_cast(attachment_read)); GLenum buffers[] {static_cast(attachment_write)}; glDrawBuffers(1, buffers); glBlitFramebuffer(srcX, srcY, width_, height_, destX, destY, draw.width_, draw.height_, GL_COLOR_BUFFER_BIT, filter); } void frame_buffer_t::blitDepth(const frame_buffer_t& draw, blt::i32 srcX, blt::i32 srcY, blt::i32 destX, blt::i32 destY, GLuint filter) const { glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.fboID); glReadBuffer(static_cast(attachment_t::DEPTH)); GLenum buffers[] {static_cast(attachment_t::DEPTH)}; glDrawBuffers(1, buffers); glBlitFramebuffer(srcX, srcY, width_, height_, destX, destY, draw.width_, draw.height_, GL_DEPTH_BUFFER_BIT, filter); } void frame_buffer_t::blitStencil(const frame_buffer_t& draw, blt::i32 srcX, blt::i32 srcY, blt::i32 destX, blt::i32 destY, GLuint filter) const { glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.fboID); glReadBuffer(static_cast(attachment_t::STENCIL)); GLenum buffers[] {static_cast(attachment_t::STENCIL)}; glDrawBuffers(1, buffers); glBlitFramebuffer(srcX, srcY, width_, height_, destX, destY, draw.width_, draw.height_, GL_STENCIL_BUFFER_BIT, filter); } void frame_buffer_t::blitToScreen(blt::i32 width, blt::i32 height) const { glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); //glReadBuffer(GL_COLOR_ATTACHMENT0); //glDrawBuffer(GL_COLOR_ATTACHMENT0); glReadBuffer(static_cast(attachment_t::COLOR0)); GLenum buffers[] {GL_BACK}; glDrawBuffers(1, buffers); glBlitFramebuffer(0, 0, width_, height_, 0, 0, width, height, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); } frame_buffer_t frame_buffer_t::make_render_texture(blt::i32 width, blt::i32 height) { frame_buffer_t fbo; fbo.create(); fbo.bind(); auto* texture = new texture_gl2D(width, height); fbo.attachTexture(texture, attachment_t::COLOR0); render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, width, height); fbo.attachRenderBuffer(depth_rbo, attachment_t::DEPTH_STENCIL); if (!frame_buffer_t::validate()) BLT_ERROR("Failed to create render texture framebuffer!"); frame_buffer_t::unbind(); return fbo; } frame_buffer_t frame_buffer_t::make_multisample_render_texture(blt::i32 width, blt::i32 height, blt::i32 samples) { frame_buffer_t fbo; fbo.create(); fbo.bind(); auto* texture = new texture_gl2D_multisample(width, height, samples); fbo.attachTexture(texture, attachment_t::COLOR0); render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, width, height, samples); fbo.attachRenderBuffer(depth_rbo, attachment_t::DEPTH_STENCIL); if (!frame_buffer_t::validate()) BLT_ERROR("Failed to create render texture framebuffer!"); frame_buffer_t::unbind(); return fbo; } frame_buffer_t frame_buffer_t::make_multisample_render_target(blt::i32 width, blt::i32 height, blt::i32 samples) { frame_buffer_t fbo; fbo.create(); fbo.bind(); render_buffer_t color_rbo = render_buffer_t::make_render_buffer(GL_RGBA8, width, height, samples); render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, width, height, samples); fbo.attachRenderBuffer(color_rbo, attachment_t::COLOR0); fbo.attachRenderBuffer(depth_rbo, attachment_t::DEPTH_STENCIL); if (!frame_buffer_t::validate()) BLT_ERROR("Failed to create multi-sampled render texture framebuffer!"); frame_buffer_t::unbind(); return fbo; } frame_buffer_t frame_buffer_t::make_render_target(blt::i32 width, blt::i32 height) { frame_buffer_t fbo; fbo.create(); fbo.bind(); render_buffer_t color_rbo = render_buffer_t::make_render_buffer(GL_RGBA8, width, height); render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, width, height); fbo.attachRenderBuffer(color_rbo, attachment_t::COLOR0); fbo.attachRenderBuffer(depth_rbo, attachment_t::DEPTH_STENCIL); if (!frame_buffer_t::validate()) BLT_ERROR("Failed to create render texture framebuffer!"); frame_buffer_t::unbind(); return fbo; } void frame_buffer_t::bindScreen(frame_buffer_t::draw_t type) { glBindFramebuffer(static_cast(type), 0); } }