228 lines
5.7 KiB
C++
228 lines
5.7 KiB
C++
// frambuffer.cpp
|
|
//
|
|
// Copyright (C) 2010-2020, the Celestia Development Team
|
|
// Original version by Chris Laurel <claurel@gmail.com>
|
|
//
|
|
// 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 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include "framebuffer.h"
|
|
|
|
FramebufferObject::FramebufferObject(GLuint width, GLuint height, unsigned int attachments) :
|
|
m_width(width),
|
|
m_height(height),
|
|
m_colorTexId(0),
|
|
m_depthTexId(0),
|
|
m_fboId(0),
|
|
m_status(GL_FRAMEBUFFER_UNSUPPORTED)
|
|
{
|
|
if (attachments != 0)
|
|
{
|
|
generateFbo(attachments);
|
|
}
|
|
}
|
|
|
|
FramebufferObject::FramebufferObject(FramebufferObject &&other) :
|
|
m_width(other.m_width),
|
|
m_height(other.m_height),
|
|
m_colorTexId(other.m_colorTexId),
|
|
m_depthTexId(other.m_depthTexId),
|
|
m_fboId(other.m_fboId),
|
|
m_status(other.m_status)
|
|
{
|
|
other.m_fboId = 0;
|
|
other.m_status = GL_FRAMEBUFFER_UNSUPPORTED;
|
|
}
|
|
|
|
FramebufferObject& FramebufferObject::operator=(FramebufferObject &&other)
|
|
{
|
|
m_width = other.m_width;
|
|
m_height = other.m_height;
|
|
m_colorTexId = other.m_colorTexId;
|
|
m_depthTexId = other.m_depthTexId;
|
|
m_fboId = other.m_fboId;
|
|
m_status = other.m_status;
|
|
|
|
other.m_fboId = 0;
|
|
other.m_status = GL_FRAMEBUFFER_UNSUPPORTED;
|
|
return *this;
|
|
}
|
|
|
|
FramebufferObject::~FramebufferObject()
|
|
{
|
|
cleanup();
|
|
}
|
|
|
|
bool
|
|
FramebufferObject::isValid() const
|
|
{
|
|
return m_status == GL_FRAMEBUFFER_COMPLETE;
|
|
}
|
|
|
|
GLuint
|
|
FramebufferObject::colorTexture() const
|
|
{
|
|
return m_colorTexId;
|
|
}
|
|
|
|
GLuint
|
|
FramebufferObject::depthTexture() const
|
|
{
|
|
return m_depthTexId;
|
|
}
|
|
|
|
void
|
|
FramebufferObject::generateColorTexture()
|
|
{
|
|
// Create and bind the texture
|
|
glGenTextures(1, &m_colorTexId);
|
|
glBindTexture(GL_TEXTURE_2D, m_colorTexId);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
// Clamp to edge
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
// Set the texture dimensions
|
|
// Do we need to set GL_DEPTH_COMPONENT24 here?
|
|
#ifdef GL_ES
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
#else
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m_width, m_height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
|
|
#endif
|
|
|
|
// Unbind the texture
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
#ifdef GL_ES
|
|
#define CEL_DEPTH_FORMAT GL_UNSIGNED_INT
|
|
#else
|
|
#define CEL_DEPTH_FORMAT GL_UNSIGNED_BYTE
|
|
#endif
|
|
|
|
void
|
|
FramebufferObject::generateDepthTexture()
|
|
{
|
|
// Create and bind the texture
|
|
glGenTextures(1, &m_depthTexId);
|
|
glBindTexture(GL_TEXTURE_2D, m_depthTexId);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
|
|
// Only nearest sampling is appropriate for depth textures
|
|
// But we can use linear to decrease aliasing
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
// Clamp to edge
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
// Set the texture dimensions
|
|
// Do we need to set GL_DEPTH_COMPONENT24 here?
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_width, m_height, 0, GL_DEPTH_COMPONENT, CEL_DEPTH_FORMAT, nullptr);
|
|
|
|
// Unbind the texture
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
void
|
|
FramebufferObject::generateFbo(unsigned int attachments)
|
|
{
|
|
// Create the FBO
|
|
glGenFramebuffers(1, &m_fboId);
|
|
GLint oldFboId;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFboId);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_fboId);
|
|
|
|
#ifndef GL_ES
|
|
glReadBuffer(GL_NONE);
|
|
#endif
|
|
|
|
if ((attachments & ColorAttachment) != 0)
|
|
{
|
|
generateColorTexture();
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTexId, 0);
|
|
m_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
if (m_status != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, oldFboId);
|
|
cleanup();
|
|
return;
|
|
}
|
|
}
|
|
#ifndef GL_ES
|
|
else
|
|
{
|
|
// Depth-only rendering; no color buffer.
|
|
glDrawBuffer(GL_NONE);
|
|
}
|
|
#endif
|
|
|
|
if ((attachments & DepthAttachment) != 0)
|
|
{
|
|
generateDepthTexture();
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexId, 0);
|
|
m_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
if (m_status != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, oldFboId);
|
|
cleanup();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
|
}
|
|
|
|
// Restore default frame buffer
|
|
glBindFramebuffer(GL_FRAMEBUFFER, oldFboId);
|
|
}
|
|
|
|
// Delete all GL objects associated with this framebuffer object
|
|
void
|
|
FramebufferObject::cleanup()
|
|
{
|
|
if (m_fboId != 0)
|
|
{
|
|
glDeleteFramebuffers(1, &m_fboId);
|
|
}
|
|
|
|
if (m_colorTexId != 0)
|
|
{
|
|
glDeleteTextures(1, &m_colorTexId);
|
|
}
|
|
|
|
if (m_depthTexId != 0)
|
|
{
|
|
glDeleteTextures(1, &m_depthTexId);
|
|
}
|
|
}
|
|
|
|
bool
|
|
FramebufferObject::bind()
|
|
{
|
|
if (isValid())
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_fboId);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
FramebufferObject::unbind(GLint oldfboId)
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, oldfboId);
|
|
return true;
|
|
}
|