|
|
|
//
|
|
|
|
// fbo.h
|
|
|
|
// J
|
|
|
|
//
|
|
|
|
// Created by Joshua Moerman on 7/01/11.
|
|
|
|
// Copyright 2011 Vadovas. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
/*
|
|
|
|
Like the shader object, all work is done in the constructor and destructor. So copying a fbo object is impossible, use smart pointers instead. The standard constructor only takes a width and height. Texture settings and the presence of a depth buffer are preset and cannot be changed (since one probably wants those things, and there is not a lot of flexibility in ES2).
|
|
|
|
|
|
|
|
The other constructor (wich is defined in fbo.mm) is to make EAGLView more compact, it makes a fbo in a given context and with a given layer (no texture will be generated, so you cannot use this fbo for texture lookup).
|
|
|
|
|
|
|
|
Just like the shader, this class has begin() and end() functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef FBO_H
|
|
|
|
#define FBO_H
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <iostream>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include "to_string.h"
|
|
|
|
|
|
|
|
#include "basic.h"
|
|
|
|
#include "texture.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. (also affects shader::set_texture).
|
|
|
|
// NOTE: stencil attachment is not supported
|
|
|
|
|
|
|
|
namespace J {
|
|
|
|
|
|
|
|
class fbo {
|
|
|
|
public:
|
|
|
|
typedef std::map<GLenum, GLuint> RenderbuffersMap;
|
|
|
|
|
|
|
|
int width, height;
|
|
|
|
GLuint fbo_number;
|
|
|
|
RenderbuffersMap renderbuffers;
|
|
|
|
GLuint texture_id;
|
|
|
|
|
|
|
|
public:
|
|
|
|
// standard ctor, makes use of textures
|
|
|
|
template <typename Texture = no_texture>
|
|
|
|
fbo(int width, int height, Texture const & texture = no_texture()) : 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);
|
|
|
|
bool should_clear = !make_tex(texture);
|
|
|
|
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();
|
|
|
|
|
|
|
|
if(should_clear){
|
|
|
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
unbind();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ctor from EAGLContext, not using textures, so you cannot use it as lookup, only as renderer.
|
|
|
|
// this one is defined in fbo.mm, because it uses Quartz and objC / iOS stuff.
|
|
|
|
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();
|
|
|
|
glViewport(0, 0, width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
void end(){
|
|
|
|
unbind();
|
|
|
|
// NOTE: we don't set a viewport, because it will be always set (the 'main' fbo is also an J::fbo)
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename Texture>
|
|
|
|
bool make_tex(Texture const & texture){
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, Texture::gl_format, Texture::gl_type, texture.data());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool make_tex(no_texture const &){
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bind(){
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo_number);
|
|
|
|
check_error();
|
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
|
|
|
#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
|