// // fbo.h // J // // Created by Joshua Moerman on 7/01/11. // Copyright 2011 Vadovas. All rights reserved. // #ifndef FBO_H #define FBO_H #include #include #include #include "to_string.h" #include "basic.h" @class EAGLContext; @class CAEAGLLayer; // TODO: more error checking in debug build. // TODO: make texture class? for easier switching between linear/nearest interpolation for example // NOTE: stencil attachment is not supported namespace J { class fbo { public: typedef std::map RenderbuffersMap; int width, height; GLuint fbo_number; RenderbuffersMap renderbuffers; GLuint texture_id; public: // standard ctor, makes use of textures fbo(int width, int height) : width(width), height(height), fbo_number(0), renderbuffers(), texture_id(0) { glGenFramebuffers(1, &fbo_number); bind(); // generate texture glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_2D, texture_id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); // attach depth create_attach_renderbuffer(GL_DEPTH_COMPONENT16, GL_DEPTH_ATTACHMENT); check_error(); check_status(); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); unbind(); } // ctor from EAGLContext, not using texture, so you cannot use it as lookup, only as renderer. fbo(EAGLContext* context, CAEAGLLayer* layer); ~fbo() { end(); glDeleteFramebuffers(1, &fbo_number); for(RenderbuffersMap::const_iterator i = renderbuffers.begin(); i != renderbuffers.end(); ++i){ RenderbuffersMap::value_type const & it = *i; glDeleteRenderbuffers(1, &it.second); } glDeleteTextures(1, &texture_id); // NOTE: glDeleteTextures silently ignores 0's and names that do not correspond to existing textures. } void begin(){ bind(); // FIXME: this should be done, but also switching back should be done... //glPushAttrib(GL_VIEWPORT); glViewport(0, 0, width, height); // NOTE: commented out, because we don't want fbo's to nest. /*GLint savedFramebuffer = -1; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer); std::cout << "sfb: " << savedFramebuffer << ", fbo: " << fbo_number << std::endl;*/ } void end(){ unbind(); // FIXME: switch back to previous viewport. See begin() //glPopAttrib(); } void bind(){ glBindFramebuffer(GL_FRAMEBUFFER, fbo_number); check_error(); } void unbind(){ glBindFramebuffer(GL_FRAMEBUFFER, 0); } private: void create_attach_renderbuffer(GLenum format, GLenum attachment_point) { GLuint buffer; glGenRenderbuffers(1, &buffer); glBindRenderbuffer(GL_RENDERBUFFER, buffer); glRenderbufferStorage(GL_RENDERBUFFER, format, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment_point, GL_RENDERBUFFER, buffer); renderbuffers[format] = buffer; } void check_status() { #if defined(DEBUG) GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); switch(status) { case GL_FRAMEBUFFER_COMPLETE: std::cout << "FRAMEBUFFER_COMPLETE - OK" << std::endl; return; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: std::cout << "FRAMEBUFFER_INCOMPLETE_ATTACHMENT" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: std::cout << "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: std::cout << "FRAMEBUFFER_INCOMPLETE_DIMENSIONS" << std::endl; break; case GL_FRAMEBUFFER_UNSUPPORTED: std::cout << "FRAMEBUFFER_UNSUPPORTED" << std::endl; break; default: std::cout << "UNKNOWN FRAMEBUFFER ERROR" << std::endl; break; } throw std::runtime_error("I will not continu.."); #endif } }; } // namespace J #endif // FBO_H