#ifndef FBO_H #define FBO_H #include #include #include #include "to_string.h" #import #import // TODO: more error checking in debug build. // TODO: make texture class? for easier switching between linear/nearest interpolation for example namespace J { class fbo { public: typedef std::map RenderbuffersMap; int width, height; GLuint fbo_number; RenderbuffersMap renderbuffers; GLuint texture_id; public: 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); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture_id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_BYTE, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); // attach depth create_attach_renderbuffer(GL_DEPTH_COMPONENT, GL_DEPTH_ATTACHMENT); // attach stencil //create_attach_renderbuffer(GL_STENCIL_INDEX, GL_STENCIL_ATTACHMENT); check_status(); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); unbind(); } ~fbo() { end(); glDeleteFramebuffers(1, &fbo_number); for(RenderbuffersMap::const_iterator i = renderbuffers.begin(); i != renderbuffers.end(); ++i){ RenderbuffersMap::value_type const & it = *i; glDeleteFramebuffers(1, &it.second); } glDeleteTextures(1, &texture_id); } 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. //glPopAttrib(); } private: void bind(){ glBindFramebuffer(GL_FRAMEBUFFER, fbo_number); GLenum status = glGetError(); if(status != GL_NO_ERROR) throw std::runtime_error("i has error" + std::to_string(status) + "with fbo: " + std::to_string(fbo_number)); } void unbind(){ glBindFramebuffer(GL_FRAMEBUFFER, 0); } 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() { 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.."); } void check_error(){ GLenum status = glGetError(); if(status != GL_NO_ERROR){ throw std::runtime_error("GL Error: " + std::to_string(status)); } } }; } // namespace J #endif // FBO_H