new render target class while working on the exhibit UI
[laserbrain_demo] / src / rtarg.cc
diff --git a/src/rtarg.cc b/src/rtarg.cc
new file mode 100644 (file)
index 0000000..f90c1d3
--- /dev/null
@@ -0,0 +1,289 @@
+#include "rtarg.h"
+
+struct RTStackItem {
+       RenderTarget *rt;
+       int saved_vp[4];
+};
+
+static void attach_depth_texture(Texture *tex);
+
+#define MAX_STACK_SIZE 16
+static RTStackItem rstack[MAX_STACK_SIZE];
+static int rtop;
+
+void set_render_target(RenderTarget *rt)
+{
+       if(rt) {
+               if(!rstack[rtop].rt) {
+                       glGetIntegerv(GL_VIEWPORT, rstack[rtop].saved_vp);
+               }
+               rt->bind();
+
+       } else {
+               int *vp = rstack[rtop].saved_vp;
+               glViewport(vp[0], vp[1], vp[2], vp[3]);
+               glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       }
+
+       rstack[rtop].rt = rt;
+}
+
+RenderTarget *current_render_target()
+{
+       return rstack[rtop].rt;
+}
+
+bool push_render_target(RenderTarget *rt)
+{
+       if(!rt) {
+               error_log("push_render_target(0) is invalid\n");
+               return false;
+       }
+       if(rtop >= MAX_STACK_SIZE - 1) {
+               warning_log("push_render_target: overflow\n");
+               return false;
+       }
+       int *vp = rstack[rtop + 1].saved_vp;
+       RenderTarget *prev = current_render_target();
+
+       if(prev) {
+               vp[0] = vp[1] = 0;
+               vp[2] = prev->get_width();
+               vp[3] = prev->get_height();
+       } else {
+               memcpy(vp, rstack[rtop].saved_vp, 4 * sizeof(int));
+       }
+       rstack[++rtop].rt = rt;
+       return true;
+}
+
+bool pop_render_target()
+{
+       if(rtop <= 0) {
+               error_log("pop_render_target: undeflow\n");
+               return false;
+       }
+
+       int *vp = rstack[rtop].saved_vp;
+       glViewport(vp[0], vp[1], vp[2], vp[3]);
+
+       if(rstack[--rtop].rt) {
+               rstack[rtop].rt->bind();
+       } else {
+               glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       }
+       return true;
+}
+
+RenderTarget::RenderTarget()
+{
+       fbo = 0;
+       rbdepth = 0;
+       width = height = tex_width = tex_height = 0;
+       auto_vport = true;
+       rtcount = 0;
+
+       for(int i=0; i<4; i++) {
+               tex[i] = 0;
+               own_texture[i] = true;
+       }
+}
+
+RenderTarget::~RenderTarget()
+{
+       destroy();
+}
+
+bool RenderTarget::create(int xsz, int ysz, unsigned int fmt, unsigned int flags)
+{
+       return create_mrt(xsz, ysz, 1, fmt, flags);
+}
+
+bool RenderTarget::create_mrt(int xsz, int ysz, int num, unsigned int fmt, unsigned int flags)
+{
+       glGenFramebuffers(1, &fbo);
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+       width = xsz;
+       height = ysz;
+       tex_width = next_pow2(xsz);
+       tex_height = next_pow2(ysz);
+       rtcount = num;
+
+       texmat.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1);
+
+       if(flags & RT_COLOR) {
+               for(int i=0; i<num; i++) {
+                       tex[i] = new Texture;
+                       tex[i]->create(tex_width, tex_height, TEX_2D, fmt);
+                       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D,
+                                       tex[i]->get_id(), 0);
+                       own_texture[i] = true;
+               }
+       } else {
+               // in case set_texture was called before create
+               for(int i=0; i<num; i++) {
+                       if(tex[i]) {
+                               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D,
+                                               tex[i]->get_id(), 0);
+                       }
+               }
+       }
+
+       if(flags & (RT_DEPTH | RT_STENCIL)) {
+               unsigned int fmt, attachment;
+
+               glGenRenderbuffers(1, &rbdepth);
+               glBindRenderbuffer(GL_RENDERBUFFER, rbdepth);
+
+               switch(flags & (RT_DEPTH | RT_STENCIL)) {
+               case RT_DEPTH:
+                       fmt = GL_DEPTH_COMPONENT24;
+                       attachment = GL_DEPTH_ATTACHMENT;
+                       break;
+
+               case RT_STENCIL:
+                       fmt = GL_STENCIL_INDEX8;
+                       attachment = GL_STENCIL_ATTACHMENT;
+                       break;
+
+               case RT_DEPTH | RT_STENCIL:
+                       fmt = GL_DEPTH24_STENCIL8;
+                       attachment = GL_DEPTH_STENCIL_ATTACHMENT;
+                       break;
+               }
+
+               glRenderbufferStorage(GL_RENDERBUFFER, fmt, width, height);
+               glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbdepth);
+
+       } else if(depth) {
+               attach_depth_texture(depth);
+       }
+
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       return true;
+}
+
+void RenderTarget::destroy()
+{
+       for(int i=0; i<4; i++) {
+               if(tex[i] && own_texture[i]) {
+                       delete tex[i];
+                       tex[i] = 0;
+               }
+       }
+       if(rbdepth) {
+               glDeleteRenderbuffers(1, &rbdepth);
+               rbdepth = 0;
+       }
+       if(fbo) {
+               glDeleteFramebuffers(1, &fbo);
+               fbo = 0;
+       }
+}
+
+int RenderTarget::get_width() const
+{
+       return width;
+}
+
+int RenderTarget::get_height() const
+{
+       return height;
+}
+
+void RenderTarget::set_texture(Texture *tex, int idx)
+{
+       if(this->tex[idx] && own_texture[idx]) {
+               delete this->tex[idx];
+       }
+       this->tex[idx] = tex;
+       own_texture[idx] = false;
+
+       if(fbo) {
+               glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx, GL_TEXTURE_2D,
+                               tex->get_id(), 0);
+               glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       }
+}
+
+void RenderTarget::set_depth_texture(Texture *tex)
+{
+       depth = tex;
+
+       if(fbo) {
+               glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+               attach_depth_texture(tex);
+               glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       }
+}
+
+Texture *RenderTarget::texture(int idx) const
+{
+       return tex[idx];
+}
+
+Texture *RenderTarget::depth_texture() const
+{
+       return depth;
+}
+
+void RenderTarget::set_auto_viewport(bool enable)
+{
+       auto_vport = enable;
+}
+
+bool RenderTarget::auto_viewport() const
+{
+       return auto_vport;
+}
+
+void RenderTarget::bind() const
+{
+       glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+       if(auto_vport) {
+               glViewport(0, 0, width, height);
+       }
+}
+
+const Mat4 &RenderTarget::texture_matrix() const
+{
+       return texmat;
+}
+
+static void attach_depth_texture(Texture *tex)
+{
+       unsigned int fmt = tex->get_format();
+       unsigned int attachment = 0;
+
+       switch(fmt) {
+       case GL_DEPTH_COMPONENT:
+       case GL_DEPTH_COMPONENT16:
+       case GL_DEPTH_COMPONENT24:
+       case GL_DEPTH_COMPONENT32:
+               attachment = GL_DEPTH_ATTACHMENT;
+               break;
+
+       case GL_STENCIL_INDEX:
+       case GL_STENCIL_INDEX1:
+       case GL_STENCIL_INDEX4:
+       case GL_STENCIL_INDEX8:
+               attachment = GL_STENCIL_ATTACHMENT;
+               break;
+
+       case GL_DEPTH_STENCIL:
+       case GL_DEPTH24_STENCIL8:
+       case GL_DEPTH32F_STENCIL8:
+       case GL_UNSIGNED_INT_24_8:
+               attachment = GL_DEPTH_STENCIL_ATTACHMENT;
+               break;
+
+       default:
+               error_log("failed to attach depth/stencil texture: unexpected texture format: %x\n", fmt);
+               break;
+       }
+
+       glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex->get_id(), 0);
+}