|
- //
- // Lol Engine
- //
- // Copyright © 2010—2017 Sam Hocevar <sam@hocevar.net>
- //
- // Lol Engine is free software. It comes without any warranty, to
- // the extent permitted by applicable law. You can redistribute it
- // and/or modify it under the terms of the Do What the Fuck You Want
- // to Public License, Version 2, as published by the WTFPL Task Force.
- // See http://www.wtfpl.net/ for more details.
- //
-
- #include <lol/engine-internal.h>
-
- #include "lolgl.h"
-
- namespace lol
- {
-
- //
- // The FramebufferData class
- // -------------------------
- //
-
- class FramebufferData
- {
- friend class Framebuffer;
-
- ibox2 m_saved_viewport;
- ivec2 m_size;
- bool m_bound;
-
- GLuint m_fbo, m_texture, m_depth;
- };
-
- //
- // The FramebufferFormat struct
- // ----------------------
- //
-
- uint32_t FramebufferFormat::GetFormat()
- {
- switch (m_format)
- {
- #if defined HAVE_GLES_2X
- /* FIXME: incomplete */
- case RGBA_8:
- case RGBA_8_I:
- case RGBA_8_UI: return GL_RGBA;
- #elif defined __APPLE__ && defined __MACH__
- case R_8:
- case R_8_I:
- case R_8_UI:
- case R_8_F:
-
- case R_16:
- case R_16_I:
- case R_16_UI:
- case R_16_F:
-
- case R_32_I:
- case R_32:
- case R_32_UI:
- case R_32_F: return GL_RED;
-
- case RG_8:
- case RG_8_I:
- case RG_8_UI:
- case RG_8_F:
-
- case RG_16:
- case RG_16_I:
- case RG_16_UI:
- case RG_16_F:
-
- case RG_32:
- case RG_32_I:
- case RG_32_UI:
- case RG_32_F: return GL_RG;
-
- case RGB_8:
- case RGB_8_I:
- case RGB_8_UI:
- case RGB_8_F:
-
- case RGB_16:
- case RGB_16_I:
- case RGB_16_UI:
- case RGB_16_F:
-
- case RGB_32:
- case RGB_32_I:
- case RGB_32_UI:
- case RGB_32_F: return (m_invert_rgb)?(GL_BGR):(GL_RGB);
-
- case RGBA_8:
- case RGBA_8_I:
- case RGBA_8_UI:
- case RGBA_8_F:
-
- case RGBA_16:
- case RGBA_16_I:
- case RGBA_16_UI:
- case RGBA_16_F:
-
- case RGBA_32:
- case RGBA_32_I:
- case RGBA_32_UI:
- # if defined GL_BGRA
- case RGBA_32_F: return (m_invert_rgb)?(GL_BGRA):(GL_RGBA);
- # else
- case RGBA_32_F: return GL_RGBA;
- # endif
- #else
- case R_8: return GL_R8;
- case R_8_I: return GL_R8I;
- case R_8_UI: return GL_R8UI;
-
- case R_16: return GL_R16;
- case R_16_I: return GL_R16I;
- case R_16_UI: return GL_R16UI;
- case R_16_F: return GL_R16F;
-
- case R_32_I: return GL_R32I;
- case R_32_UI: return GL_R32UI;
- case R_32_F: return GL_R32F;
-
- case RG_8: return GL_RG8;
- case RG_8_I: return GL_RG8I;
- case RG_8_UI: return GL_RG8UI;
-
- case RG_16: return GL_RG16;
- case RG_16_I: return GL_RG16I;
- case RG_16_UI: return GL_RG16UI;
- case RG_16_F: return GL_RG16F;
-
- case RG_32_I: return GL_RG32I;
- case RG_32_UI: return GL_RG32UI;
- case RG_32_F: return GL_RG32F;
-
- case RGB_8: return GL_RGB8;
- case RGB_8_I: return GL_RGB8I;
- case RGB_8_UI: return GL_RGB8UI;
-
- case RGB_16: return GL_RGB16;
- case RGB_16_I: return GL_RGB16I;
- case RGB_16_UI: return GL_RGB16UI;
- case RGB_16_F: return GL_RGB16F;
-
- case RGB_32_I: return GL_RGB32I;
- case RGB_32_UI: return GL_RGB32UI;
- case RGB_32_F: return GL_RGB32F;
-
- case RGBA_8: return GL_RGBA8;
- case RGBA_8_I: return GL_RGBA8I;
- case RGBA_8_UI: return GL_RGBA8UI;
-
- case RGBA_16: return GL_RGBA16;
- case RGBA_16_I: return GL_RGBA16I;
- case RGBA_16_UI: return GL_RGBA16UI;
- case RGBA_16_F: return GL_RGBA16F;
-
- case RGBA_32_I: return GL_RGBA32I;
- case RGBA_32_UI: return GL_RGBA32UI;
- case RGBA_32_F: return GL_RGBA32F;
- #endif
- default:
- ASSERT(false, "unknown framebuffer format %d", m_format);
- return 0;
- }
- }
-
- uint32_t FramebufferFormat::GetFormatOrder()
- {
- switch (m_format)
- {
- #if defined HAVE_GLES_2X
- /* FIXME: incomplete */
- case R_8: case RG_8: case RGB_8: case RGBA_8:
- case R_8_I: case RG_8_I: case RGB_8_I: case RGBA_8_I:
- return GL_BYTE;
- case R_8_UI: case RG_8_UI: case RGB_8_UI: case RGBA_8_UI:
- return GL_UNSIGNED_BYTE;
- #elif defined __APPLE__ && defined __MACH__
- case R_8: case RG_8: case RGB_8: case RGBA_8:
- case R_8_I: case RG_8_I: case RGB_8_I: case RGBA_8_I:
- return GL_BYTE;
- case R_8_UI: case RG_8_UI: case RGB_8_UI: case RGBA_8_UI:
- return GL_UNSIGNED_BYTE;
-
- case R_16: case RG_16: case RGB_16: case RGBA_16:
- case R_16_I: case RG_16_I: case RGB_16_I: case RGBA_16_I:
- return GL_SHORT;
- case R_16_UI: case RG_16_UI: case RGB_16_UI: case RGBA_16_UI:
- return GL_UNSIGNED_SHORT;
-
- case R_16_F: case RG_16_F: case RGB_16_F: case RGBA_16_F:
- ASSERT(false, "unsupported framebuffer format order %d", m_format);
- return 0;
-
- case R_32_I: case RG_32_I: case RGB_32_I: case RGBA_32_I:
- return GL_INT;
- case R_32_UI: case RG_32_UI: case RGB_32_UI: case RGBA_32_UI:
- return GL_UNSIGNED_INT;
- case R_32_F: case RG_32_F: case RGB_32_F: case RGBA_32_F:
- return GL_FLOAT;
- #else
- case R_8: case R_8_I: case R_8_UI: case R_8_F:
- case R_16: case R_16_I: case R_16_UI: case R_16_F:
- case R_32: case R_32_I: case R_32_UI: case R_32_F:
- return GL_RED;
-
- case RG_8: case RG_8_I: case RG_8_UI: case RG_8_F:
- case RG_16: case RG_16_I: case RG_16_UI: case RG_16_F:
- case RG_32: case RG_32_I: case RG_32_UI: case RG_32_F:
- return GL_RG;
-
- case RGB_8: case RGB_8_I: case RGB_8_UI: case RGB_8_F:
- case RGB_16: case RGB_16_I: case RGB_16_UI: case RGB_16_F:
- case RGB_32: case RGB_32_I: case RGB_32_UI: case RGB_32_F:
- return m_invert_rgb ? GL_BGR : GL_RGB;
-
- case RGBA_8: case RGBA_8_I: case RGBA_8_UI: case RGBA_8_F:
- case RGBA_16: case RGBA_16_I: case RGBA_16_UI: case RGBA_16_F:
- case RGBA_32: case RGBA_32_I: case RGBA_32_UI: case RGBA_32_F:
- # if defined GL_BGRA
- return (m_invert_rgb)?(GL_BGRA):(GL_RGBA);
- # else
- return GL_RGBA;
- # endif
- #endif
- default:
- ASSERT(false, "unknown framebuffer format order %d", m_format);
- return 0;
- }
- }
-
- //
- // The Framebuffer class
- // ----------------------
- //
-
- Framebuffer::Framebuffer(ivec2 size, FramebufferFormat fbo_format)
- : m_data(new FramebufferData)
- {
- m_data->m_size = size;
- m_data->m_bound = false;
- #if GL_VERSION_1_1
- GLenum internal_format = fbo_format.GetFormat();
- GLenum format = fbo_format.GetFormatOrder();
- GLenum depth = GL_DEPTH_COMPONENT;
- #elif GL_ES_VERSION_2_0
- /* In OpenGL ES, internal format and format must match. */
- GLenum internal_format = fbo_format.GetFormat();
- GLenum format = fbo_format.GetFormat();
- GLenum depth = GL_DEPTH_COMPONENT16; /* for WebGL */
- #else
- /* In OpenGL ES, internal format and format must match. */
- GLenum internal_format = fbo_format.GetFormat();
- GLenum format = fbo_format.GetFormat();
- #endif
- GLenum wrapmode = GL_CLAMP_TO_EDGE;
- GLenum filtering = GL_LINEAR;
-
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- glGenFramebuffers(1, &m_data->m_fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, m_data->m_fbo);
- #else
- glGenFramebuffersOES(1, &m_data->m_fbo);
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_data->m_fbo);
- #endif
-
- glGenTextures(1, &m_data->m_texture);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapmode);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapmode);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0,
- format, GL_UNSIGNED_BYTE, nullptr);
-
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, m_data->m_texture, 0);
- #else
- glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_2D, m_data->m_texture, 0);
- #endif
-
- m_data->m_depth = GL_INVALID_ENUM;
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- if (depth != GL_INVALID_ENUM)
- {
- /* XXX: might not work on GL ES, see
- * http://stackoverflow.com/q/4041682/111461
- * See also http://qt-project.org/forums/viewthread/11734 */
- glGenRenderbuffers(1, &m_data->m_depth);
- glBindRenderbuffer(GL_RENDERBUFFER, m_data->m_depth);
- glRenderbufferStorage(GL_RENDERBUFFER, depth, size.x, size.y);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, m_data->m_depth);
- }
- #endif
-
- glBindTexture(GL_TEXTURE_2D, 0);
-
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- ASSERT(status == GL_FRAMEBUFFER_COMPLETE,
- "invalid framebuffer status 0x%x", status);
- #endif
-
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- #else
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- #endif
- }
-
- Framebuffer::~Framebuffer()
- {
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- glDeleteFramebuffers(1, &m_data->m_fbo);
- #else
- glDeleteFramebuffersOES(1, &m_data->m_fbo);
- #endif
- glDeleteTextures(1, &m_data->m_texture);
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- if (m_data->m_depth != GL_INVALID_ENUM)
- glDeleteRenderbuffers(1, &m_data->m_depth);
- #endif
- delete m_data;
- }
-
- TextureUniform Framebuffer::GetTextureUniform() const
- {
- TextureUniform ret;
- ret.m_flags = m_data->m_texture;
- return ret;
- }
-
- ivec2 Framebuffer::GetSize() const
- {
- return m_data->m_size;
- }
-
- image Framebuffer::GetImage() const
- {
- image ret(m_data->m_size);
-
- u8vec4 *buffer = ret.lock<PixelFormat::RGBA_8>();
- glReadPixels(0, 0, m_data->m_size.x, m_data->m_size.y,
- GL_RGBA, GL_UNSIGNED_BYTE, buffer);
- ret.unlock(buffer);
-
- return ret;
- }
-
- void Framebuffer::Bind()
- {
- ASSERT(!m_data->m_bound, "trying to bind an already bound framebuffer");
-
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- glBindFramebuffer(GL_FRAMEBUFFER, m_data->m_fbo);
- #else
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_data->m_fbo);
- #endif
-
- /* FIXME: this should be done in the RenderContext object
- * instead, maybe by getting rid of Framebuffer::Bind() and
- * creating RenderContext::SetFramebuffer() instead. */
- m_data->m_saved_viewport = Renderer::Get()->GetViewport();
- Renderer::Get()->SetViewport(ibox2(ivec2::zero, m_data->m_size));
- m_data->m_bound = true;
- }
-
- void Framebuffer::Unbind()
- {
- ASSERT(m_data->m_bound, "trying to unbind an unbound framebuffer");
-
- #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- #else
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- #endif
-
- Renderer::Get()->SetViewport(m_data->m_saved_viewport);
- m_data->m_bound = false;
- }
-
- } /* namespace lol */
|