X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=laserbrain_demo;a=blobdiff_plain;f=src%2Frtarg.cc;fp=src%2Frtarg.cc;h=f90c1d3cf31cf42e4f43dfb78e547cbfcc53fb88;hp=0000000000000000000000000000000000000000;hb=92e1b315a32da123b2f8d7bb633375033a10c66d;hpb=7ffa5c50d9254af9bd746fc5e6bc8e5db8eda675 diff --git a/src/rtarg.cc b/src/rtarg.cc new file mode 100644 index 0000000..f90c1d3 --- /dev/null +++ b/src/rtarg.cc @@ -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; icreate(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; iget_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); +}