gamma correction in post when there's no sRGB framebuffer and simple shader compositi...
[vrfileman] / src / rtarg.cc
diff --git a/src/rtarg.cc b/src/rtarg.cc
new file mode 100644 (file)
index 0000000..84a9142
--- /dev/null
@@ -0,0 +1,262 @@
+#include "rtarg.h"
+#include "texture.h"
+#include "app.h"
+
+unsigned int RenderTarget::default_fbo = 0;
+
+RenderTarget::RenderTarget()
+{
+       width = height = 0;
+       fbo = 0;
+       rbuf_zstencil = 0;
+       color_tex = 0;
+       tex_face = 0;
+       tex_targ = 0;
+}
+
+RenderTarget::~RenderTarget()
+{
+       cleanup();
+}
+
+bool RenderTarget::create(unsigned int fmt)
+{
+       return create(win_width, win_height, fmt);
+}
+
+bool RenderTarget::create(int width, int height, unsigned int fmt)
+{
+       fprintf(stderr, "RenderTarget::create(%d, %d)\n", width, height);
+       cleanup();
+
+       tex_targ = GL_TEXTURE_2D;
+       this->width = width;
+       this->height = height;
+       int tex_width = next_pow2(width);
+       int tex_height = next_pow2(height);
+
+       tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0);
+
+       color_tex = new Texture2D;
+       color_tex->create(tex_width, tex_height, fmt);
+       tex_face = 0;
+
+       glGenFramebuffers(1, &fbo);
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+       glBindTexture(GL_TEXTURE_2D, color_tex->get_id());
+       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0);
+       glBindTexture(GL_TEXTURE_2D, 0);
+
+       glGenRenderbuffers(1, &rbuf_zstencil);
+       glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
+#ifdef GL_ES_VERSION_2_0
+       // XXX really? 16bit zbuffer attachment is the best we can do on ES2?
+       glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, tex_width, tex_height);
+#else
+       glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height);
+#endif
+       glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+#ifndef GL_ES_VERSION_2_0
+       glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+#endif
+
+       glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+       return true;
+}
+
+bool RenderTarget::create(Texture *tex, int face)
+{
+       fprintf(stderr, "RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0),
+                       tex->get_size(1), face);
+
+       tex_targ = GL_TEXTURE_2D;
+       if(dynamic_cast<TextureCube*>(tex)) {
+               if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
+                       tex_targ = face;
+               } else if(face >= 0 && face < 6) {
+                       tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+               } else {
+                       fprintf(stderr, "invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face);
+                       return false;
+               }
+       }
+
+       cleanup();
+
+       width = tex->get_size(0);
+       height = tex->get_size(1);
+
+       tex_matrix = Mat4::identity;
+
+       color_tex = tex;
+
+       glGenFramebuffers(1, &fbo);
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+       glBindTexture(tex_targ, color_tex->get_id());
+       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
+       glBindTexture(tex_targ, 0);
+
+       glGenRenderbuffers(1, &rbuf_zstencil);
+       glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
+       glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
+       glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+       glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
+
+       glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+       return true;
+}
+
+void RenderTarget::cleanup()
+{
+       delete color_tex;
+       color_tex = 0;
+
+       if(fbo) {
+               glDeleteFramebuffers(1, &fbo);
+       }
+       if(rbuf_zstencil) {
+               glDeleteRenderbuffers(1, &rbuf_zstencil);
+       }
+
+       fbo = rbuf_zstencil = 0;
+       width = height = 0;
+       tex_face = 0;
+       tex_targ = 0;
+}
+
+bool RenderTarget::resize(int width, int height)
+{
+       if(width == this->width && height == this->height) {
+               return true;
+       }
+
+       int tex_width = next_pow2(width);
+       int tex_height = next_pow2(height);
+       this->width = width;
+       this->height = height;
+
+       if(tex_width != color_tex->get_size(0) || tex_height != color_tex->get_size(1)) {
+               printf("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height);
+
+               glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+               color_tex->create(tex_width, tex_height, color_tex->get_format());
+               color_tex->bind();
+               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
+               glBindTexture(tex_targ, 0);
+
+               glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
+
+               unsigned int depth_format;
+#ifndef GL_ES_VERSION_2_0
+               // desktop OpenGL, use typical depth24-stencil8 format
+               depth_format = GL_DEPTH24_STENCIL8;
+#else  // OpenGL ES
+               // OpenGL ES provides combined depth/stencil formats as an extension
+#ifdef GL_OES_packed_depth_stencil
+               if(have_ext_packed_depth_stencil) {
+                       depth_format = GL_DEPTH24_STENCIL8_OES;
+               } else
+#endif // GL_OES_packed_depth_stencil
+               {
+                       depth_format = GL_DEPTH_COMPONENT16;    // no compile-time or runtime support for packed d/s
+               }
+#endif // GL_ES_VERSION_2_0
+               glRenderbufferStorage(GL_RENDERBUFFER, depth_format, tex_width, tex_height);
+
+               glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+       }
+
+       tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0);
+       return true;
+}
+
+int RenderTarget::get_width() const
+{
+       return width;
+}
+
+int RenderTarget::get_height() const
+{
+       return height;
+}
+
+Texture *RenderTarget::get_texture() const
+{
+       return color_tex;
+}
+
+const Mat4 &RenderTarget::get_texture_matrix() const
+{
+       return tex_matrix;
+}
+
+
+static const char *fbstname[] = {
+       "GL_FRAMEBUFFER_COMPLETE",
+       "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
+       "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
+       "no such fbo error",
+       "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
+       "GL_FRAMEBUFFER_INCOMPLETE_FORMATS",
+       "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",
+       "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER",
+       "GL_FRAMEBUFFER_UNSUPPORTED"
+};
+
+bool RenderTarget::check() const
+{
+       bool res = true;
+
+#ifndef GL_ES_VERSION_2_0
+       int prev_fb = 0;
+       glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb);
+#endif
+
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+       int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+       if(status != GL_FRAMEBUFFER_COMPLETE) {
+               fprintf(stderr, "RenderTarget::check: incomplete FBO %u: %s\n", fbo,
+                               fbstname[status - GL_FRAMEBUFFER_COMPLETE]);
+               res = false;
+               goto end;
+       }
+
+end:
+#ifndef GL_ES_VERSION_2_0
+       glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
+#endif
+       return res;
+}
+
+void RenderTarget::bind() const
+{
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+       glViewport(0, 0, width, height);
+}
+
+void set_render_target(const RenderTarget *rtarg)
+{
+       if(rtarg) {
+               rtarg->bind();
+       } else {
+               glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
+               glViewport(0, 0, win_width, win_height);
+       }
+}
+
+int next_pow2(int x)
+{
+       x--;
+       x = (x >> 1) | x;
+       x = (x >> 2) | x;
+       x = (x >> 4) | x;
+       x = (x >> 8) | x;
+       x = (x >> 16) | x;
+       return x + 1;
+}
+