Skip to content

Instantly share code, notes, and snippets.

@ofTheo
Created May 16, 2012 16:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ofTheo/2711815 to your computer and use it in GitHub Desktop.
Save ofTheo/2711815 to your computer and use it in GitHub Desktop.
#include "ofConstants.h"
#include "ofFbo.h"
#include "ofAppRunner.h"
#include "ofUtils.h"
#include "ofGraphics.h"
#include "ofGLRenderer.h"
#include <map>
//#ifndef TARGET_OPENGLES
/*
See
http://www.gandogames.com/2010/07/tutorial-using-anti-aliasing-msaa-in-the-iphone/
and
http://stackoverflow.com/questions/3340189/how-do-you-activate-multisampling-in-opengl-es-on-the-iphone
for multisampling on iphone
*/
// mapping to allow simple opengl EXT and opengl ES OES
// commented out ones are already defined
#ifndef TARGET_OPENGLES
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT
/*#define glGenFramebuffers glGenFramebuffersEXT
#define glGenRenderbuffers glGenRenderbuffersEXT
#define glDeleteFramebuffers glDeleteFramebuffersEXT
#define glBindFramebuffer glBindFramebufferEXT
#define glBindRenderbuffer glBindRenderbufferEXT
#define glRenderbufferStorage glRenderbufferStorageEXT
#define glFramebufferRenderbuffer glFramebufferRenderbufferEXT
#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleEXT
#define glFramebufferTexture2D glFramebufferTexture2DEXT
#define glCheckFramebufferStatus glCheckFramebufferStatusEXT
#define GL_WRITE_FRAMEBUFFER GL_WRITE_FRAMEBUFFER_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT
#ifdef TARGET_WIN32
#define glBlitFramebuffer glBlitFramebufferEXT
#define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
#define GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_EXT
#define GL_DRAW_FRAMEBUFFER GL_DRAW_FRAMEBUFFER_EXT
#define GL_FRAMEBUFFER GL_FRAMEBUFFER_EXT
#define GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_EXT
#define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT
#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT
#define GL_FRAMEBUFFER_UNSUPPORTED GL_FRAMEBUFFER_UNSUPPORTED_EXT
#define GL_MAX_COLOR_ATTACHMENTS GL_MAX_COLOR_ATTACHMENTS_EXT
#define GL_MAX_SAMPLES GL_MAX_SAMPLES_EXT
#define GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT_EXT
#define GL_READ_FRAMEBUFFER GL_READ_FRAMEBUFFER_EXT
#define GL_RENDERBUFFER GL_RENDERBUFFER_EXT
#endif //TARGET_WIN32
*/
#else
#define glGenFramebuffers glGenFramebuffersOES
#define glGenRenderbuffers glGenRenderbuffersOES
#define glDeleteFramebuffers glDeleteFramebuffersOES
#define glDeleteRenderbuffers glDeleteRenderbuffersOES
#define glBindFramebuffer glBindFramebufferOES
#define glBindRenderbuffer glBindRenderbufferOES
#define glRenderbufferStorage glRenderbufferStorageOES
#define glFramebufferRenderbuffer glFramebufferRenderbufferOES
#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleOES
#define glFramebufferTexture2D glFramebufferTexture2DOES
#define glCheckFramebufferStatus glCheckFramebufferStatusOES
#define GL_FRAMEBUFFER GL_FRAMEBUFFER_OES
#define GL_RENDERBUFFER GL_RENDERBUFFER_OES
#define GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_OES
#define GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT_OES
//#define GL_DEPTH_STENCIL_ATTACHMENT GL_DEPTH_STENCIL_ATTACHMENT_OES
#define GL_DEPTH_STENCIL GL_DEPTH24_STENCIL8_OES
#define GL_DEPTH_COMPONENT GL_DEPTH_COMPONENT16_OES
#define GL_STENCIL_INDEX GL_STENCIL_INDEX8_OES
#define GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_OES
#define GL_MAX_COLOR_ATTACHMENTS GL_MAX_COLOR_ATTACHMENTS_OES
#define GL_MAX_SAMPLES GL_MAX_SAMPLES_OES
#define GL_READ_FRAMEBUFFER GL_READ_FRAMEBUFFER_OES
#define GL_DRAW_FRAMEBUFFER GL_DRAW_FRAMEBUFFER_OES
#define GL_WRITE_FRAMEBUFFER GL_WRITE_FRAMEBUFFER_OES
#define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES
#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES
#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES
#define GL_FRAMEBUFFER_UNSUPPORTED GL_FRAMEBUFFER_UNSUPPORTED_OES
#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_OES
#define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_OES
#endif
//-------------------------------------------------------------------------------------
ofFbo::Settings::Settings() {
width = 0;
height = 0;
numColorbuffers = 1;
useDepth = false;
useStencil = false;
depthAsTexture = false;
#ifndef TARGET_OPENGLES
textureTarget = GL_TEXTURE_RECTANGLE_ARB;
#else
textureTarget = GL_TEXTURE_2D;
#endif
internalformat = GL_RGBA;
dethInternalFormat = GL_DEPTH_COMPONENT;
wrapModeHorizontal = GL_CLAMP_TO_EDGE;
wrapModeVertical = GL_CLAMP_TO_EDGE;
minFilter = GL_LINEAR;
maxFilter = GL_LINEAR;
numSamples = 0;
}
static map<GLuint,int> & getIdsFB(){
static map<GLuint,int> * idsFB = new map<GLuint,int>;
return *idsFB;
}
//--------------------------------------------------------------
static void retainFB(GLuint id){
if(id==0) return;
if(getIdsFB().find(id)!=getIdsFB().end()){
getIdsFB()[id]++;
}else{
getIdsFB()[id]=1;
}
}
//--------------------------------------------------------------
static void releaseFB(GLuint id){
if(getIdsFB().find(id)!=getIdsFB().end()){
getIdsFB()[id]--;
if(getIdsFB()[id]==0){
glDeleteFramebuffers(1, &id);
}
}else{
ofLog(OF_LOG_WARNING,"ofFbo: releasing id not found, this shouldn't be happening releasing anyway");
glDeleteFramebuffers(1, &id);
}
}
static map<GLuint,int> & getIdsRB(){
static map<GLuint,int> * idsRB = new map<GLuint,int>;
return *idsRB;
}
//--------------------------------------------------------------
static void retainRB(GLuint id){
if(id==0) return;
if(getIdsRB().find(id)!=getIdsRB().end()){
getIdsRB()[id]++;
}else{
getIdsRB()[id]=1;
}
}
//--------------------------------------------------------------
static void releaseRB(GLuint id){
if(getIdsRB().find(id)!=getIdsRB().end()){
getIdsRB()[id]--;
if(getIdsRB()[id]==0){
glDeleteRenderbuffers(1, &id);
}
}else{
ofLog(OF_LOG_WARNING,"ofFbo: releasing id not found, this shouldn't be happening releasing anyway");
glDeleteRenderbuffers(1, &id);
}
}
//-------------------------------------------------------------------------------------
int ofFbo::_maxColorAttachments = -1;
int ofFbo::_maxDrawBuffers = -1;
int ofFbo::_maxSamples = -1;
ofFbo::ofFbo():
isBound(0),
fbo(0),
fboTextures(0),
depthBuffer(0),
stencilBuffer(0),
savedFramebuffer(0),
defaultTextureIndex(0),
bIsAllocated(false)
{
}
ofFbo::ofFbo(const ofFbo & mom){
settings = mom.settings;
isBound = mom.isBound;
bIsAllocated = mom.bIsAllocated;
fbo = mom.fbo;
retainFB(fbo);
fboTextures = mom.fboTextures;
if(settings.numSamples){
retainFB(fboTextures);
}
if(mom.settings.depthAsTexture){
depthBufferTex = mom.depthBufferTex;
}else{
depthBuffer = mom.depthBuffer;
retainRB(depthBuffer);
}
stencilBuffer = mom.stencilBuffer;
retainRB(stencilBuffer);
savedFramebuffer = mom.savedFramebuffer;
colorBuffers = mom.colorBuffers;
for(int i=0;i<(int)colorBuffers.size();i++){
retainRB(colorBuffers[i]);
}
textures = mom.textures;
}
ofFbo & ofFbo::operator=(const ofFbo & mom){
if(&mom==this) return *this;
settings = mom.settings;
isBound = mom.isBound;
bIsAllocated = mom.bIsAllocated;
fbo = mom.fbo;
retainFB(fbo);
fboTextures = mom.fboTextures;
if(settings.numSamples){
retainFB(fboTextures);
}
if(mom.settings.depthAsTexture){
depthBufferTex = mom.depthBufferTex;
}else{
depthBuffer = mom.depthBuffer;
retainRB(depthBuffer);
}
stencilBuffer = mom.stencilBuffer;
retainRB(stencilBuffer);
savedFramebuffer = mom.savedFramebuffer;
colorBuffers = mom.colorBuffers;
for(int i=0;i<(int)colorBuffers.size();i++){
retainRB(colorBuffers[i]);
}
textures = mom.textures;
return *this;
}
ofFbo::~ofFbo(){
destroy();
}
int ofFbo::maxColorAttachments() {
if(_maxColorAttachments<0) checkGLSupport();
return _maxColorAttachments;
}
int ofFbo::maxDrawBuffers() {
if(_maxDrawBuffers<0) checkGLSupport();
return _maxDrawBuffers;
}
int ofFbo::maxSamples() {
if(_maxSamples<0) checkGLSupport();
return _maxSamples;
}
void ofFbo::destroy() {
if(fbo){
releaseFB(fbo);
fbo=0;
}
if(depthBuffer){
releaseRB(depthBuffer);
depthBuffer = 0;
}
if(depthBufferTex.isAllocated()){
depthBufferTex.clear();
}
if(stencilBuffer){
releaseRB(stencilBuffer);
stencilBuffer = 0;
}
if(settings.numSamples && fboTextures){
releaseFB(fboTextures);
fboTextures = 0;
}
textures.clear();
for(int i=0; i<(int)colorBuffers.size(); i++) releaseRB(colorBuffers[i]);
colorBuffers.clear();
isBound = 0;
bIsAllocated = false;
}
static GLboolean CheckExtension( const char *extName ){
/*
** Search for extName in the extensions string. Use of strstr()
** is not sufficient because extension names can be prefixes of
** other extension names. Could use strtok() but the constant
** string returned by glGetString can be in read-only memory.
*/
char *p = (char *) glGetString(GL_EXTENSIONS);
char *end;
int extNameLen;
extNameLen = strlen(extName);
end = p + strlen(p);
while (p < end) {
int n = strcspn(p, " ");
if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
return GL_TRUE;
}
p += (n + 1);
}
return GL_FALSE;
}
bool ofFbo::checkGLSupport() {
#ifndef TARGET_OPENGLES
if(CheckExtension("GL_EXT_framebuffer_object")){
ofLog(OF_LOG_VERBOSE,"FBO supported");
}else{
ofLog(OF_LOG_ERROR, "FBO not supported by this graphics card");
return false;
}
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &_maxColorAttachments);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &_maxDrawBuffers);
glGetIntegerv(GL_MAX_SAMPLES, &_maxSamples);
ofLog(OF_LOG_NOTICE, string("ofFbo::checkGLSupport()\n") +
"maxColorAttachments: " + ofToString(_maxColorAttachments) + "\n" +
"maxDrawBuffers: " + ofToString(_maxDrawBuffers) + "\n" +
"maxSamples: " + ofToString(_maxSamples)
);
#else
if(CheckExtension("GL_OES_framebuffer_object")){
ofLog(OF_LOG_VERBOSE,"FBO supported");
}else{
ofLog(OF_LOG_ERROR, "FBO not supported by this graphics card");
return false;
}
string extensions = (char*)glGetString(GL_EXTENSIONS);
ofLog(OF_LOG_VERBOSE,extensions);
#endif
return true;
}
void ofFbo::allocate(int width, int height, int internalformat, int numSamples) {
settings.width = width;
settings.height = height;
settings.internalformat = internalformat;
settings.numSamples = numSamples;
#ifdef TARGET_OS_IPHONE
settings.useDepth = false;
settings.useStencil = false;
#else
settings.useDepth = true;
settings.useStencil = true;
#endif
allocate(settings);
}
void ofFbo::allocate(Settings _settings) {
if(!checkGLSupport()) return;
destroy();
if(_settings.width == 0) _settings.width = ofGetWidth();
if(_settings.height == 0) _settings.height = ofGetHeight();
settings = _settings;
// create main fbo
// this is the main one we bind for drawing into
// all the renderbuffers are attached to this (whether MSAA is enabled or not)
glGenFramebuffers(1, &fbo);
retainFB(fbo);
bind();
if(settings.depthAsTexture && settings.numSamples){
ofLogWarning() << "multisampling not supported with depth as texture, setting 0 samples";
settings.numSamples = 0;
}
// If we want both a depth AND a stencil buffer tehn combine them into a single buffer
#ifndef TARGET_OPENGLES
if( settings.useDepth && settings.useStencil )
{
if(!settings.depthAsTexture){
stencilBuffer = depthBuffer = createAndAttachRenderbuffer(GL_DEPTH_STENCIL, GL_DEPTH_STENCIL_ATTACHMENT);
retainRB(depthBuffer);
retainRB(stencilBuffer);
}else{
glGenTextures(1, &depthBufferTex.texData.textureID);
//retainRB(depthBuffer);
glBindTexture(GL_TEXTURE_2D, depthBufferTex.texData.textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#ifndef TARGET_OPENGLES
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
#else
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
#endif
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8_EXT, settings.width, settings.height, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 0);
glBindTexture( GL_TEXTURE_2D, 0 );
// allocate depthBufferTex as depth buffer;
depthBufferTex.texData.glTypeInternal = GL_DEPTH24_STENCIL8_EXT;
depthBufferTex.texData.glType = GL_DEPTH_STENCIL_EXT;
depthBufferTex.texData.pixelType = GL_UNSIGNED_INT_24_8_EXT;
depthBufferTex.texData.textureTarget = GL_TEXTURE_2D;
depthBufferTex.texData.bFlipTexture = false;
depthBufferTex.texData.tex_w = settings.width;
depthBufferTex.texData.tex_h = settings.height;
depthBufferTex.texData.tex_t = 1.0f;
depthBufferTex.texData.tex_u = 1.0f;
depthBufferTex.texData.width = settings.width;
depthBufferTex.texData.height = settings.height;
depthBufferTex.texData.bAllocated = true;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,GL_TEXTURE_2D, depthBufferTex.texData.textureID, 0);
}
}else
#endif
{
// if we want a depth buffer, create it, and attach to our main fbo
if(settings.useDepth){
GLint depthPixelType = GL_UNSIGNED_BYTE;
if(settings.dethInternalFormat==GL_DEPTH_COMPONENT){
depthPixelType = GL_UNSIGNED_BYTE;
}
#ifndef TARGET_OPENGLES
else if(settings.dethInternalFormat==GL_DEPTH_COMPONENT16){
depthPixelType = GL_UNSIGNED_SHORT;
}else if(settings.dethInternalFormat==GL_DEPTH_COMPONENT24){
depthPixelType = GL_UNSIGNED_INT;
}else if(settings.dethInternalFormat==GL_DEPTH_COMPONENT32){
depthPixelType = GL_UNSIGNED_INT;
}
#endif
if(!settings.depthAsTexture){
depthBuffer = createAndAttachRenderbuffer(settings.dethInternalFormat, GL_DEPTH_ATTACHMENT);
retainRB(depthBuffer);
}else{
glGenTextures(1, &depthBufferTex.texData.textureID);
//retainRB(depthBuffer);
glBindTexture(GL_TEXTURE_2D, depthBufferTex.texData.textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#ifndef TARGET_OPENGLES
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
#else
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
#endif
glTexImage2D( GL_TEXTURE_2D, 0, settings.dethInternalFormat, settings.width, settings.height, 0, GL_DEPTH_COMPONENT, depthPixelType, 0 );
glBindTexture( GL_TEXTURE_2D, 0 );
// allocate depthBufferTex as depth buffer;
depthBufferTex.texData.glTypeInternal = settings.dethInternalFormat;
depthBufferTex.texData.glType = GL_DEPTH_COMPONENT;
depthBufferTex.texData.pixelType = depthPixelType;
depthBufferTex.texData.textureTarget = GL_TEXTURE_2D;
depthBufferTex.texData.bFlipTexture = false;
depthBufferTex.texData.tex_w = settings.width;
depthBufferTex.texData.tex_h = settings.height;
depthBufferTex.texData.tex_t = 1.0f;
depthBufferTex.texData.tex_u = 1.0f;
depthBufferTex.texData.width = settings.width;
depthBufferTex.texData.height = settings.height;
depthBufferTex.texData.bAllocated = true;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D, depthBufferTex.texData.textureID, 0);
}
}
// if we want a stencil buffer, create it, and attach to our main fbo
if(settings.useStencil){
stencilBuffer = createAndAttachRenderbuffer(GL_STENCIL_INDEX, GL_STENCIL_ATTACHMENT);
retainRB(stencilBuffer);
}
}
// if we want MSAA, create a new fbo for textures
#ifndef TARGET_OPENGLES
if(settings.numSamples){
glGenFramebuffers(1, &fboTextures);
retainFB(fboTextures);
}else{
fboTextures = fbo;
}
#else
fboTextures = fbo;
if(settings.numSamples){
ofLog(OF_LOG_WARNING,"ofFbo: multisampling not supported in opengles");
}
#endif
// now create all textures and color buffers
for(int i=0; i<settings.numColorbuffers; i++) createAndAttachTexture(i);
// if textures are attached to a different fbo (e.g. if using MSAA) check it's status
if(fbo != fboTextures) {
glBindFramebuffer(GL_FRAMEBUFFER, fboTextures);
}
// check everything is ok with this fbo
checkStatus();
// unbind it
unbind();
bIsAllocated = true;
}
bool ofFbo::isAllocated(){
return bIsAllocated;
}
/* removed by now, was crashing on draw
*
void ofFbo::allocateForShadow( int width, int height )
{
//#ifndef TARGET_OPENGLES
int old;
glGetIntegerv( GL_FRAMEBUFFER_BINDING, &old );
settings.width = width;
settings.height = height;
glGenTextures(1, &depthBuffer);
retainRB(depthBuffer);
glBindTexture(GL_TEXTURE_2D, depthBuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#ifndef TARGET_OPENGLES
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
#else
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
#endif
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, settings.width, settings.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0 );
glBindTexture( GL_TEXTURE_2D, 0 );
glGenFramebuffers( 1, &fbo );
retainFB(fbo);
glBindFramebuffer( GL_FRAMEBUFFER, fbo );
#ifndef TARGET_OPENGLES
glDrawBuffer( GL_NONE );
glReadBuffer( GL_NONE );
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D, depthBuffer, 0);
if( glCheckFramebufferStatus( GL_FRAMEBUFFER ) != GL_FRAMEBUFFER_COMPLETE )
printf("Can't use FBOs !\n");
glBindFramebuffer( GL_FRAMEBUFFER, old );
}*/
GLuint ofFbo::createAndAttachRenderbuffer(GLenum internalFormat, GLenum attachmentPoint) {
GLuint buffer;
glGenRenderbuffers(1, &buffer);
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
#ifndef TARGET_OPENGLES
if(settings.numSamples==0) glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, settings.width, settings.height);
else glRenderbufferStorageMultisample(GL_RENDERBUFFER, settings.numSamples, internalFormat, settings.width, settings.height);
#else
glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, settings.width, settings.height);
#endif
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentPoint, GL_RENDERBUFFER, buffer);
return buffer;
}
void ofFbo::createAndAttachTexture(GLenum attachmentPoint) {
// bind fbo for textures (if using MSAA this is the newly created fbo, otherwise its the same fbo as before)
glBindFramebuffer(GL_FRAMEBUFFER, fboTextures);
ofTexture tex;
tex.allocate(settings.width, settings.height, settings.internalformat, settings.textureTarget == GL_TEXTURE_2D ? false : true);
//tex.texData.bFlipTexture = true;
tex.setTextureWrap(settings.wrapModeHorizontal, settings.wrapModeVertical);
tex.setTextureMinMagFilter(settings.minFilter, settings.maxFilter);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachmentPoint, tex.texData.textureTarget, tex.texData.textureID, 0);
textures.push_back(tex);
// if MSAA, bind main fbo and attach renderbuffer
if(settings.numSamples) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLuint colorBuffer = createAndAttachRenderbuffer(settings.internalformat, GL_COLOR_ATTACHMENT0 + attachmentPoint);
colorBuffers.push_back(colorBuffer);
retainRB(colorBuffer);
}
}
void ofFbo::begin(bool setupScreen) {
ofPushView();
if(ofGetGLRenderer()){
ofGetGLRenderer()->setCurrentFBO(this);
}
ofViewport(0, 0, getWidth(), getHeight(), false);
if(setupScreen){
ofOrientation orient = ofGetOrientation();
#ifdef TARGET_OF_IPHONE
orient = OF_ORIENTATION_DEFAULT;
#endif
ofSetupScreenPerspective(getWidth(), getHeight(), orient, false);
}
bind();
}
//void ofViewport(float x = 0, float y = 0, float width = 0, float height = 0, bool invertY = true);
//void ofSetupScreenPerspective(float width = 0, float height = 0, int orientation = 0, bool vFlip = true, float fov = 60, float nearDist = 0, float farDist = 0);
void ofFbo::end() {
unbind();
if(ofGetGLRenderer()){
ofGetGLRenderer()->setCurrentFBO(NULL);
}
ofPopView();
}
void ofFbo::bind() {
if(isBound == 0) {
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
isBound++;
}
void ofFbo::unbind() {
if(isBound) {
glBindFramebuffer(GL_FRAMEBUFFER, savedFramebuffer);
isBound = 0;
dirty = true;
}
}
int ofFbo::getNumTextures() {
return textures.size();
}
//TODO: Should we also check against card's max attachments or can we assume that's taken care of in texture setup? Still need to figure out MSAA in conjunction with MRT
void ofFbo::setActiveDrawBuffer(int i){
#ifndef TARGET_OPENGLES
if (i < getNumTextures()){
GLenum e = GL_COLOR_ATTACHMENT0 + i;
glDrawBuffer(e);
}else{
ofLog(OF_LOG_WARNING,"trying to activate texture "+ofToString(i) + " for drawing that is out of the range (0->" + ofToString(getNumTextures()) + ") of allocated textures for this fbo.");
}
#endif
}
void ofFbo::setActiveDrawBuffers(const vector<int>& ids){
#ifndef TARGET_OPENGLES
vector<GLenum> attachments;
for(int i=0; i < ids.size(); i++){
int id = ids[i];
if (id < getNumTextures()){
GLenum e = GL_COLOR_ATTACHMENT0 + id;
attachments.push_back(e);
}else{
ofLog(OF_LOG_WARNING,"trying to activate texture "+ofToString(id) + " for drawing that is out of the range (0->" + ofToString(getNumTextures()) + ") of allocated textures for this fbo.");
}
}
glDrawBuffers(attachments.size(),&attachments[0]);
#endif
}
void ofFbo::activateAllDrawBuffers(){
#ifndef TARGET_OPENGLES
vector<GLenum> attachments;
for(int i=0; i < getNumTextures(); i++){
if (i < getNumTextures()){
GLenum e = GL_COLOR_ATTACHMENT0 + i;
attachments.push_back(e);
}else{
ofLog(OF_LOG_WARNING,"trying to activate texture "+ofToString(i) + " for drawing that is out of the range (0->" + ofToString(getNumTextures()) + ") of allocated textures for this fbo.");
}
}
glDrawBuffers(attachments.size(),&attachments[0]);
#endif
}
void ofFbo::setDefaultTextureIndex(int defaultTexture)
{
defaultTextureIndex = defaultTexture;
}
int ofFbo::getDefaultTextureIndex()
{
return defaultTextureIndex;
}
ofTexture& ofFbo::getTextureReference(){
return getTextureReference(defaultTextureIndex);
}
ofTexture& ofFbo::getTextureReference(int attachmentPoint) {
updateTexture(attachmentPoint);
ofTexture & ref = textures[attachmentPoint];
if( ref.texData.textureTarget == GL_TEXTURE_2D ){
ref.texData.tex_t = ofMap(ref.getWidth(), 0, ofNextPow2(ref.getWidth()), 0, 1, true);
ref.texData.tex_u = ofMap(ref.getHeight(), 0, ofNextPow2(ref.getHeight()), 0, 1, true);
}
return ref;
}
void ofFbo::setAnchorPercent(float xPct, float yPct){
getTextureReference().setAnchorPercent(xPct, yPct);
}
void ofFbo::setAnchorPoint(float x, float y){
getTextureReference().setAnchorPoint(x, y);
}
void ofFbo::resetAnchor(){
getTextureReference().resetAnchor();
}
void ofFbo::readToPixels(ofPixels & pixels, int attachmentPoint){
#ifndef TARGET_OPENGLES
getTextureReference(attachmentPoint).readToPixels(pixels);
#else
bind();
int format,type;
ofGetGlFormatAndType(settings.internalformat,format,type);
glReadPixels(0,0,settings.width, settings.height, format, GL_UNSIGNED_BYTE, pixels.getPixels());
unbind();
#endif
}
void ofFbo::readToPixels(ofShortPixels & pixels, int attachmentPoint){
#ifndef TARGET_OPENGLES
getTextureReference(attachmentPoint).readToPixels(pixels);
#else
bind();
int format,type;
ofGetGlFormatAndType(settings.internalformat,format,type);
glReadPixels(0,0,settings.width, settings.height, format, GL_UNSIGNED_SHORT, pixels.getPixels());
unbind();
#endif
}
void ofFbo::readToPixels(ofFloatPixels & pixels, int attachmentPoint){
#ifndef TARGET_OPENGLES
getTextureReference(attachmentPoint).readToPixels(pixels);
#else
bind();
int format,type;
ofGetGlFormatAndType(settings.internalformat,format,type);
glReadPixels(0,0,settings.width, settings.height, format, GL_FLOAT, pixels.getPixels());
unbind();
#endif
}
void ofFbo::updateTexture(int attachmentPoint) {
// TODO: flag to see if this is dirty or not
#ifndef TARGET_OPENGLES
if(fbo != fboTextures && dirty) {
glGetIntegerv( GL_FRAMEBUFFER_BINDING, &savedFramebuffer );
// save current drawbuffer
glPushAttrib(GL_COLOR_BUFFER_BIT);
// save current readbuffer
GLint readBuffer;
glGetIntegerv(GL_READ_BUFFER, &readBuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboTextures);
glDrawBuffer(GL_COLOR_ATTACHMENT0 + attachmentPoint);
glReadBuffer(GL_COLOR_ATTACHMENT0 + attachmentPoint);
glBlitFramebuffer(0, 0, settings.width, settings.height, 0, 0, settings.width, settings.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, savedFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, savedFramebuffer);
glBindFramebuffer( GL_FRAMEBUFFER, savedFramebuffer );
// restore readbuffer
glReadBuffer(readBuffer);
// restore drawbuffer
glPopAttrib();
dirty = false;
}
#endif
}
void ofFbo::draw(float x, float y) {
draw(x, y, settings.width, settings.height);
}
void ofFbo::draw(float x, float y, float width, float height) {
getTextureReference().draw(x, y, width, height);
}
GLuint ofFbo::getFbo() {
return fbo;
}
float ofFbo::getWidth() {
return settings.width;
}
float ofFbo::getHeight() {
return settings.height;
}
bool ofFbo::checkStatus() {
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch(status) {
case GL_FRAMEBUFFER_COMPLETE:
ofLog(OF_LOG_NOTICE, "FRAMEBUFFER_COMPLETE - OK");
return true;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
ofLog(OF_LOG_ERROR, "FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
ofLog(OF_LOG_ERROR, "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
ofLog(OF_LOG_ERROR, "FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
ofLog(OF_LOG_ERROR, "FRAMEBUFFER_INCOMPLETE_FORMATS");
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
ofLog(OF_LOG_ERROR, "FRAMEBUFFER_UNSUPPORTED");
break;
#ifndef TARGET_OPENGLES
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
ofLog(OF_LOG_ERROR, "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
ofLog(OF_LOG_ERROR, "FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
ofLog(OF_LOG_ERROR, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE");
break;
#endif
default:
ofLog(OF_LOG_ERROR, "UNKNOWN ERROR");
break;
}
return false;
}
ofTexture & ofFbo::getDepthTexture(){
if(!settings.depthAsTexture){
ofLogError() << "fbo not allocated with depthAsTexture";
}
if(!settings.useDepth){
ofLogError() << "fbo not allocated with useDepth";
}
return depthBufferTex;
}
//#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment