|
|
@@ -1,8 +1,9 @@
|
|
|
+/**
|
|
|
+ * FrameBuffer.cpp
|
|
|
+ */
|
|
|
+
|
|
|
#include "Base.h"
|
|
|
#include "FrameBuffer.h"
|
|
|
-#include "RenderTarget.h"
|
|
|
-#include "DepthStencilTarget.h"
|
|
|
-#include "Texture.h"
|
|
|
|
|
|
namespace gameplay
|
|
|
{
|
|
|
@@ -10,28 +11,80 @@ namespace gameplay
|
|
|
static unsigned int __maxRenderTargets = 0;
|
|
|
static std::vector<FrameBuffer*> __frameBuffers;
|
|
|
|
|
|
+FrameBuffer::FrameBuffer(const char* id) :
|
|
|
+ _id(id ? id : ""), _handle(0), _renderTargets(NULL), _depthStencilTarget(NULL)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+FrameBuffer::~FrameBuffer()
|
|
|
+{
|
|
|
+ if (_renderTargets)
|
|
|
+ {
|
|
|
+ for (unsigned int i = 0; i < __maxRenderTargets; ++i)
|
|
|
+ {
|
|
|
+ SAFE_RELEASE(_renderTargets[i]);
|
|
|
+ }
|
|
|
+ SAFE_DELETE_ARRAY(_renderTargets);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Release GL resource.
|
|
|
+ if (_handle)
|
|
|
+ {
|
|
|
+ GL_ASSERT( glDeleteFramebuffers(1, &_handle) );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Remove self from vector.
|
|
|
+ std::vector<FrameBuffer*>::iterator it = std::find(__frameBuffers.begin(), __frameBuffers.end(), this);
|
|
|
+ if (it != __frameBuffers.end())
|
|
|
+ {
|
|
|
+ __frameBuffers.erase(it);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
FrameBuffer* FrameBuffer::create(const char* id)
|
|
|
{
|
|
|
- FrameBuffer* frameBuffer = new FrameBuffer(id);
|
|
|
+ // Create GL FBO resource.
|
|
|
+ GLuint handle = 0;
|
|
|
+ GL_ASSERT( glGenFramebuffers(1, &handle) );
|
|
|
+
|
|
|
+ // Call getMaxRenderTargets() to force __maxRenderTargets to be set
|
|
|
+ getMaxRenderTargets();
|
|
|
+
|
|
|
+ // Create the render target array for the new frame buffer
|
|
|
+ RenderTarget** renderTargets = new RenderTarget*[__maxRenderTargets];
|
|
|
+ memset(renderTargets, 0, sizeof(RenderTarget*) * __maxRenderTargets);
|
|
|
+
|
|
|
+ // Create the new frame buffer
|
|
|
+ FrameBuffer* frameBuffer = new FrameBuffer(id ? id : "");
|
|
|
+ frameBuffer->_handle = handle;
|
|
|
+ frameBuffer->_renderTargets = renderTargets;
|
|
|
|
|
|
+ // Add to the global list of managed frame buffers
|
|
|
__frameBuffers.push_back(frameBuffer);
|
|
|
- frameBuffer->_index = __frameBuffers.size() - 1;
|
|
|
|
|
|
return frameBuffer;
|
|
|
}
|
|
|
|
|
|
FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned int height)
|
|
|
{
|
|
|
- FrameBuffer* frameBuffer = new FrameBuffer(id);
|
|
|
-
|
|
|
- // Create RenderTarget with same ID and set as first color attachment.
|
|
|
+ // Create RenderTarget with same ID
|
|
|
RenderTarget* renderTarget = RenderTarget::create(id, width, height);
|
|
|
+ if (renderTarget == NULL)
|
|
|
+ {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create the frame buffer
|
|
|
+ FrameBuffer* frameBuffer = create(id);
|
|
|
+ if (frameBuffer == NULL)
|
|
|
+ {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add the render target as the first color attachment
|
|
|
frameBuffer->setRenderTarget(renderTarget);
|
|
|
SAFE_RELEASE(renderTarget);
|
|
|
|
|
|
- __frameBuffers.push_back(frameBuffer);
|
|
|
- frameBuffer->_index = __frameBuffers.size() - 1;
|
|
|
-
|
|
|
return frameBuffer;
|
|
|
}
|
|
|
|
|
|
@@ -39,7 +92,7 @@ FrameBuffer* FrameBuffer::getFrameBuffer(const char* id)
|
|
|
{
|
|
|
// Search the vector for a matching ID.
|
|
|
std::vector<FrameBuffer*>::const_iterator it;
|
|
|
- for (it = __frameBuffers.begin(); it < __frameBuffers.end(); ++it)
|
|
|
+ for (it = __frameBuffers.begin(); it < __frameBuffers.end(); it++)
|
|
|
{
|
|
|
FrameBuffer* fb = *it;
|
|
|
if (strcmp(id, fb->getID()) == 0)
|
|
|
@@ -51,25 +104,6 @@ FrameBuffer* FrameBuffer::getFrameBuffer(const char* id)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-FrameBuffer::~FrameBuffer()
|
|
|
-{
|
|
|
- if (__renderTargets)
|
|
|
- {
|
|
|
- for (unsigned int i = 0; i < __maxRenderTargets; ++i)
|
|
|
- {
|
|
|
- SAFE_RELEASE(__renderTargets[i]);
|
|
|
- }
|
|
|
- SAFE_DELETE_ARRAY(__renderTargets);
|
|
|
- }
|
|
|
-
|
|
|
- // Release GL resource.
|
|
|
- glDeleteFramebuffers(1, &_handle);
|
|
|
-
|
|
|
- // Remove self from vector.
|
|
|
- std::vector<FrameBuffer*>::const_iterator it = __frameBuffers.begin() + _index;
|
|
|
- __frameBuffers.erase(it);
|
|
|
-}
|
|
|
-
|
|
|
const char* FrameBuffer::getID() const
|
|
|
{
|
|
|
return _id.c_str();
|
|
|
@@ -79,9 +113,13 @@ unsigned int FrameBuffer::getMaxRenderTargets()
|
|
|
{
|
|
|
if (__maxRenderTargets == 0)
|
|
|
{
|
|
|
+#ifdef GL_MAX_COLOR_ATTACHMENTS
|
|
|
GLint val;
|
|
|
- glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &val);
|
|
|
+ GL_ASSERT( glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &val) );
|
|
|
__maxRenderTargets = (unsigned int) val;
|
|
|
+#else
|
|
|
+ __maxRenderTargets = 1;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
return __maxRenderTargets;
|
|
|
@@ -91,82 +129,41 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
|
|
|
{
|
|
|
assert(index < __maxRenderTargets);
|
|
|
|
|
|
- if (__renderTargets[index] == target)
|
|
|
+ if (_renderTargets[index] == target)
|
|
|
{
|
|
|
+ // No change
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Release our reference to the current RenderTarget at this index.
|
|
|
- SAFE_RELEASE(__renderTargets[index]);
|
|
|
+ SAFE_RELEASE(_renderTargets[index]);
|
|
|
|
|
|
- __renderTargets[index] = target;
|
|
|
-
|
|
|
- // This FrameBuffer now references the RenderTarget.
|
|
|
- target->addRef();
|
|
|
+ _renderTargets[index] = target;
|
|
|
|
|
|
- // Now set this target as the color attachment corresponding to index.
|
|
|
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _handle);
|
|
|
- GLenum attachment;
|
|
|
- switch (index)
|
|
|
+ if (target)
|
|
|
{
|
|
|
- case 0:
|
|
|
- attachment = GL_COLOR_ATTACHMENT0;
|
|
|
- break;
|
|
|
- case 1:
|
|
|
- attachment = GL_COLOR_ATTACHMENT1;
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- attachment = GL_COLOR_ATTACHMENT2;
|
|
|
- break;
|
|
|
- case 3:
|
|
|
- attachment = GL_COLOR_ATTACHMENT3;
|
|
|
- break;
|
|
|
- case 4:
|
|
|
- attachment = GL_COLOR_ATTACHMENT4;
|
|
|
- break;
|
|
|
- case 5:
|
|
|
- attachment = GL_COLOR_ATTACHMENT5;
|
|
|
- break;
|
|
|
- case 6:
|
|
|
- attachment = GL_COLOR_ATTACHMENT6;
|
|
|
- break;
|
|
|
- case 7:
|
|
|
- attachment = GL_COLOR_ATTACHMENT7;
|
|
|
- break;
|
|
|
- case 8:
|
|
|
- attachment = GL_COLOR_ATTACHMENT8;
|
|
|
- break;
|
|
|
- case 9:
|
|
|
- attachment = GL_COLOR_ATTACHMENT9;
|
|
|
- break;
|
|
|
- case 10:
|
|
|
- attachment = GL_COLOR_ATTACHMENT10;
|
|
|
- break;
|
|
|
- case 11:
|
|
|
- attachment = GL_COLOR_ATTACHMENT11;
|
|
|
- break;
|
|
|
- case 12:
|
|
|
- attachment = GL_COLOR_ATTACHMENT12;
|
|
|
- break;
|
|
|
- case 13:
|
|
|
- attachment = GL_COLOR_ATTACHMENT13;
|
|
|
- break;
|
|
|
- case 14:
|
|
|
- attachment = GL_COLOR_ATTACHMENT14;
|
|
|
- break;
|
|
|
- case 15:
|
|
|
- attachment = GL_COLOR_ATTACHMENT15;
|
|
|
- break;
|
|
|
+ // This FrameBuffer now references the RenderTarget.
|
|
|
+ target->addRef();
|
|
|
+
|
|
|
+ // Store the current FBO binding so we can restore it
|
|
|
+ GLint currentFbo;
|
|
|
+ GL_ASSERT( glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFbo) );
|
|
|
+
|
|
|
+ // Now set this target as the color attachment corresponding to index.
|
|
|
+ GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
|
|
|
+ GLenum attachment = GL_COLOR_ATTACHMENT0 + index;
|
|
|
+ GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, _renderTargets[index]->getTexture()->getHandle(), 0) );
|
|
|
+
|
|
|
+ // Restore the FBO binding
|
|
|
+ GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, currentFbo) );
|
|
|
}
|
|
|
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, __renderTargets[index]->getTexture()->getHandle(), 0);
|
|
|
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
}
|
|
|
|
|
|
RenderTarget* FrameBuffer::getRenderTarget(unsigned int index) const
|
|
|
{
|
|
|
if (index < __maxRenderTargets)
|
|
|
{
|
|
|
- return __renderTargets[index];
|
|
|
+ return _renderTargets[index];
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
@@ -174,9 +171,40 @@ RenderTarget* FrameBuffer::getRenderTarget(unsigned int index) const
|
|
|
|
|
|
void FrameBuffer::setDepthStencilTarget(DepthStencilTarget* target)
|
|
|
{
|
|
|
+ if (_depthStencilTarget == target)
|
|
|
+ {
|
|
|
+ return; // No change
|
|
|
+ }
|
|
|
+
|
|
|
+ // Release our existing depth stencil target
|
|
|
SAFE_RELEASE(_depthStencilTarget);
|
|
|
+
|
|
|
_depthStencilTarget = target;
|
|
|
- target->addRef();
|
|
|
+
|
|
|
+ if (target)
|
|
|
+ {
|
|
|
+ // The FrameBuffer now owns this DepthStencilTarget
|
|
|
+ target->addRef();
|
|
|
+
|
|
|
+ // Store the current FBO binding so we can restore it
|
|
|
+ GLint currentFbo;
|
|
|
+ GL_ASSERT( glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFbo) );
|
|
|
+
|
|
|
+ // Now set this target as the color attachment corresponding to index.
|
|
|
+ GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
|
|
|
+
|
|
|
+ // Bind the depth texture
|
|
|
+ GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthStencilTarget->getTexture()->getHandle(), 0) );
|
|
|
+
|
|
|
+ // If the taget has a stencil buffer, bind that as well
|
|
|
+ if (target->getFormat() == DepthStencilTarget::DEPTH24_STENCIL8)
|
|
|
+ {
|
|
|
+ GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthStencilTarget->_stencilBuffer) );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Restore the FBO binding
|
|
|
+ GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, currentFbo) );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
DepthStencilTarget* FrameBuffer::getDepthStencilTarget() const
|
|
|
@@ -186,56 +214,13 @@ DepthStencilTarget* FrameBuffer::getDepthStencilTarget() const
|
|
|
|
|
|
void FrameBuffer::bind()
|
|
|
{
|
|
|
- // Check framebuffer completeness.
|
|
|
- GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
|
|
|
- switch(status)
|
|
|
- {
|
|
|
- case GL_FRAMEBUFFER_COMPLETE:
|
|
|
- // Success.
|
|
|
- break;
|
|
|
-
|
|
|
- case GL_FRAMEBUFFER_UNSUPPORTED:
|
|
|
- // Configuration error.
|
|
|
- LOG_ERROR("FrameBuffer unsupported.");
|
|
|
- return;
|
|
|
-
|
|
|
- default:
|
|
|
- // Programming error; will fail on all hardware.
|
|
|
- LOG_ERROR("Unknown error checking FrameBuffer status.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
// Bind this FrameBuffer for rendering.
|
|
|
- glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _handle);
|
|
|
+ GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
|
|
|
}
|
|
|
|
|
|
void FrameBuffer::bindDefault()
|
|
|
{
|
|
|
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
+ GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, 0) );
|
|
|
}
|
|
|
|
|
|
-FrameBuffer::FrameBuffer(const char* id) :
|
|
|
- _handle(0), _depthStencilTarget(NULL), _index(0)
|
|
|
-{
|
|
|
- if (id)
|
|
|
- {
|
|
|
- _id = id;
|
|
|
- }
|
|
|
-
|
|
|
- // Create GL FBO resource.
|
|
|
- glGenFramebuffers(1, &_handle);
|
|
|
-
|
|
|
- // Query MAX_COLOR_ATTACHMENTS the first time a FrameBuffer is created.
|
|
|
- if (__maxRenderTargets == 0)
|
|
|
- {
|
|
|
- GLint val;
|
|
|
- glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &val);
|
|
|
- __maxRenderTargets = (unsigned int) val;
|
|
|
- }
|
|
|
-
|
|
|
- __renderTargets = new RenderTarget*[__maxRenderTargets];
|
|
|
- memset(__renderTargets, 0, sizeof(RenderTarget*) * __maxRenderTargets);
|
|
|
}
|
|
|
-
|
|
|
-}
|