mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-11-25 17:53:47 +00:00
312 lines
6.8 KiB
C++
312 lines
6.8 KiB
C++
/*
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
This file is part of Aquaria.
|
|
|
|
Aquaria 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.
|
|
|
|
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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include "FrameBuffer.h"
|
|
#include "Core.h"
|
|
#include "RenderBase.h"
|
|
|
|
//WARNING: FrameBuffer objects have to have reloadDevice/unloadDevice called manually!
|
|
|
|
|
|
struct FBOStack
|
|
{
|
|
const FrameBuffer *fbo;
|
|
unsigned page;
|
|
};
|
|
|
|
static FBOStack s_fbostack[4]; // first entry is always unused
|
|
static size_t s_stackpos = 0;
|
|
static unsigned s_lastFBO = 0;
|
|
|
|
|
|
static bool isOnTop(const FrameBuffer *fbo)
|
|
{
|
|
return s_fbostack[s_stackpos].fbo == fbo;
|
|
}
|
|
|
|
static bool isInStack(const FrameBuffer *fbo, unsigned page)
|
|
{
|
|
for(size_t i = 1; i <= s_stackpos; ++i) // first entry is always NULL
|
|
if(s_fbostack[i].fbo == fbo && s_fbostack[i].page == page)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
FrameBuffer::FrameBuffer()
|
|
{
|
|
inited = false;
|
|
texw = 0;
|
|
texh = 0;
|
|
viewportW = 0;
|
|
viewportH = 0;
|
|
_curpage = 0;
|
|
_w = _h = 0;
|
|
_numpages = 0;
|
|
_numfbos = 0;
|
|
for(size_t i = 0; i < Countof(_texs); ++i)
|
|
{
|
|
_fbos[i] = 0;
|
|
_texs[i] = 0;
|
|
}
|
|
}
|
|
|
|
FrameBuffer::~FrameBuffer()
|
|
{
|
|
unloadDevice();
|
|
}
|
|
|
|
float FrameBuffer::getWidthP() const
|
|
{
|
|
return (float)core->getWindowWidth()/(float)texw;
|
|
}
|
|
|
|
float FrameBuffer::getHeightP() const
|
|
{
|
|
return (float)core->getWindowHeight()/(float)texh;
|
|
}
|
|
|
|
bool FrameBuffer::getCurrentPage() const
|
|
{
|
|
assert(_curpage);
|
|
return _curpage - 1;
|
|
}
|
|
|
|
bool FrameBuffer::init(int width, int height, unsigned pages)
|
|
{
|
|
assert(pages && pages < Countof(_texs));
|
|
_w = width;
|
|
_h = height;
|
|
_numpages = pages;
|
|
|
|
if (width == -1)
|
|
width = core->width;
|
|
|
|
if (height == -1)
|
|
height = core->height;
|
|
|
|
viewportW = width;
|
|
viewportH = height;
|
|
|
|
sizePowerOf2Texture(width);
|
|
sizePowerOf2Texture(height);
|
|
|
|
if (width == 0 || height == 0)
|
|
return false;
|
|
|
|
texw = width;
|
|
texh = height;
|
|
|
|
std::ostringstream os;
|
|
os << "Loading EXT_framebuffer_object (" << texw << ", " << texh << ")";
|
|
debugLog(os.str());
|
|
|
|
if( !glIsFramebufferEXT || !glBindFramebufferEXT || !glDeleteFramebuffersEXT ||
|
|
!glGenFramebuffersEXT || !glCheckFramebufferStatusEXT || !glFramebufferTexture2DEXT)
|
|
{
|
|
debugLog("One or more EXT_framebuffer_object functions were not found");
|
|
return false;
|
|
}
|
|
|
|
unloadDevice();
|
|
|
|
// If glDrawBuffersARB() is present, we can attach multiple textures per FBO
|
|
// and switch between them as render targets. More efficient than switching FBOs.
|
|
_numfbos = glDrawBuffersARB ? 1 : pages;
|
|
|
|
glGenFramebuffersEXT(_numfbos, &_fbos[0]);
|
|
for(unsigned i = 0; i < _numfbos; ++i)
|
|
if(!_fbos[i])
|
|
goto out;
|
|
|
|
if(_numfbos == 1)
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[0] );
|
|
|
|
glGenTextures( pages, &_texs[0] );
|
|
|
|
for(unsigned i = 0; i < pages; ++i)
|
|
{
|
|
unsigned attach = GL_COLOR_ATTACHMENT0_EXT;
|
|
if(_numfbos > 1)
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[i] );
|
|
else
|
|
attach += i;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, _texs[i]);
|
|
// GL_LINEAR
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
|
|
width, height,
|
|
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attach, GL_TEXTURE_2D, _texs[i], 0);
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
for(unsigned i = 0; i < _numfbos; ++i)
|
|
{
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _fbos[i] );
|
|
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
|
|
switch( status )
|
|
{
|
|
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
|
debugLog("GL_FRAMEBUFFER_UNSUPPORTED_EXT!");
|
|
default:
|
|
unloadDevice();
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
debugLog("Done");
|
|
inited = true;
|
|
|
|
out:
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
return inited;
|
|
|
|
}
|
|
|
|
void FrameBuffer::unloadDevice()
|
|
{
|
|
debugLog("frameBuffer::unloadDevice");
|
|
inited = false;
|
|
|
|
if (glDeleteFramebuffersEXT == NULL)
|
|
{
|
|
s_lastFBO = 0;
|
|
debugLog("Already shut down the GL, don't delete framebuffers");
|
|
return;
|
|
}
|
|
|
|
for(size_t i = 0; i < Countof(_texs); ++i)
|
|
if (_fbos[i])
|
|
{
|
|
if(s_lastFBO == _fbos[i])
|
|
s_lastFBO = 0;
|
|
debugLog("frameBuffer handle present, deleting");
|
|
glDeleteFramebuffersEXT(1, &_fbos[i]);
|
|
_fbos[i] = 0;
|
|
}
|
|
|
|
for(size_t i = 0; i < Countof(_texs); ++i)
|
|
if (_texs[i])
|
|
{
|
|
debugLog("delete framebuffer texture");
|
|
glDeleteTextures(1, &_texs[i]);
|
|
_texs[i] = 0;
|
|
}
|
|
|
|
|
|
debugLog("done");
|
|
}
|
|
|
|
void FrameBuffer::reloadDevice()
|
|
{
|
|
if(!_numpages)
|
|
return;
|
|
s_lastFBO = 0;
|
|
debugLog("frameBuffer::reloadDevice");
|
|
init(_w, _h, _numpages);
|
|
}
|
|
|
|
void FrameBuffer::_bind(unsigned page) const
|
|
{
|
|
assert(page < _numpages);
|
|
_curpage = page + 1;
|
|
unsigned fbo = _numfbos == 1 ? _fbos[0] : _fbos[page];
|
|
assert(fbo);
|
|
if(fbo != s_lastFBO)
|
|
{
|
|
s_lastFBO = fbo;
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
|
|
}
|
|
|
|
if(glDrawBuffersARB)
|
|
{
|
|
GLenum buf = GL_COLOR_ATTACHMENT0_EXT + page;
|
|
glDrawBuffersARB(1, &buf);
|
|
}
|
|
|
|
glViewport(0,0,viewportW,viewportH);
|
|
}
|
|
|
|
void FrameBuffer::pushCapture(unsigned page) const
|
|
{
|
|
assert(inited);
|
|
|
|
_bind(page);
|
|
|
|
size_t idx = ++s_stackpos;
|
|
assert(idx < Countof(s_fbostack));
|
|
s_fbostack[idx].fbo = this;
|
|
s_fbostack[idx].page = page;
|
|
}
|
|
|
|
unsigned FrameBuffer::popCapture() const
|
|
{
|
|
assert(inited && s_stackpos && isOnTop(this));
|
|
const unsigned page = s_fbostack[s_stackpos].page;
|
|
|
|
FBOStack prev = s_fbostack[--s_stackpos];
|
|
if(prev.fbo)
|
|
prev.fbo->_bind(prev.page);
|
|
else
|
|
{
|
|
s_lastFBO = 0;
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
if(glDrawBuffersARB)
|
|
glDrawBuffer(GL_BACK);
|
|
glViewport(0, 0, core->width, core->height);
|
|
}
|
|
return page;
|
|
}
|
|
|
|
void FrameBuffer::replaceCapture(unsigned page) const
|
|
{
|
|
assert(inited && s_stackpos);
|
|
|
|
_bind(page);
|
|
s_fbostack[s_stackpos].fbo = this;
|
|
s_fbostack[s_stackpos].page = page;
|
|
}
|
|
|
|
unsigned FrameBuffer::getTextureID(unsigned page) const
|
|
{
|
|
assert(inited && page < _numpages);
|
|
return _texs[page];
|
|
}
|
|
|
|
void FrameBuffer::bindTexture(unsigned page) const
|
|
{
|
|
// Technically it's enough that this texture isn't the one currently rendered to,
|
|
// but because we don't know when the topmost FBO is popped (and subsequently
|
|
// writing to the texture we're now about to read from) let's be extra safe
|
|
// and generalize this to: Textures part of the FBO stack are forbidden to bind.
|
|
assert(!isInStack(this, page));
|
|
unsigned tex = getTextureID(page);
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
}
|