nuclear@0: #include nuclear@0: #include "rtarg.h" nuclear@0: #include "texture.h" nuclear@0: #include "logger.h" nuclear@0: nuclear@0: RenderTarget::RenderTarget() nuclear@0: { nuclear@0: width = height = 0; nuclear@0: fbo = 0; nuclear@0: rbuf_zstencil = 0; nuclear@0: color_tex = 0; nuclear@0: tex_face = 0; nuclear@0: tex_targ = 0; nuclear@0: } nuclear@0: nuclear@0: RenderTarget::~RenderTarget() nuclear@0: { nuclear@0: cleanup(); nuclear@0: } nuclear@0: nuclear@0: bool RenderTarget::create(unsigned int fmt) nuclear@0: { nuclear@0: int vp[4]; nuclear@0: glGetIntegerv(GL_VIEWPORT, vp); nuclear@0: nuclear@0: return create(vp[2] - vp[0], vp[3] - vp[1], fmt); nuclear@0: } nuclear@0: nuclear@0: bool RenderTarget::create(int width, int height, unsigned int fmt) nuclear@0: { nuclear@0: debug_log("RenderTarget::create(%d, %d)\n", width, height); nuclear@0: cleanup(); nuclear@0: nuclear@0: tex_targ = GL_TEXTURE_2D; nuclear@0: this->width = width; nuclear@0: this->height = height; nuclear@0: int tex_width = next_pow2(width); nuclear@0: int tex_height = next_pow2(height); nuclear@0: nuclear@0: CHECKGLERR; nuclear@0: color_tex = new Texture2D; nuclear@0: color_tex->create(tex_width, tex_height, fmt); nuclear@0: CHECKGLERR; nuclear@0: tex_face = 0; nuclear@0: nuclear@0: glGenFramebuffers(1, &fbo); nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, fbo); nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, color_tex->get_id()); nuclear@0: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0); nuclear@0: glBindTexture(GL_TEXTURE_2D, 0); nuclear@0: nuclear@0: glGenRenderbuffers(1, &rbuf_zstencil); nuclear@0: glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); nuclear@0: glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height); nuclear@0: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); nuclear@0: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); nuclear@0: nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, 0); nuclear@0: CHECKGLERR; nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: bool RenderTarget::create(Texture *tex, int face) nuclear@0: { nuclear@0: debug_log("RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0), nuclear@0: tex->get_size(1), face); nuclear@0: nuclear@0: tex_targ = GL_TEXTURE_2D; nuclear@0: if(dynamic_cast(tex)) { nuclear@0: if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) { nuclear@0: tex_targ = face; nuclear@0: } else if(face >= 0 && face < 6) { nuclear@0: tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; nuclear@0: } else { nuclear@0: error_log("invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face); nuclear@0: return false; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: cleanup(); nuclear@0: nuclear@0: width = tex->get_size(0); nuclear@0: height = tex->get_size(1); nuclear@0: nuclear@0: color_tex = tex; nuclear@0: nuclear@0: glGenFramebuffers(1, &fbo); nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, fbo); nuclear@0: nuclear@0: glBindTexture(tex_targ, color_tex->get_id()); nuclear@0: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0); nuclear@0: glBindTexture(tex_targ, 0); nuclear@0: nuclear@0: glGenRenderbuffers(1, &rbuf_zstencil); nuclear@0: glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); nuclear@0: glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); nuclear@0: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); nuclear@0: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); nuclear@0: nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, 0); nuclear@0: CHECKGLERR; nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: void RenderTarget::cleanup() nuclear@0: { nuclear@0: delete color_tex; nuclear@0: color_tex = 0; nuclear@0: nuclear@0: if(fbo) { nuclear@0: glDeleteFramebuffers(1, &fbo); nuclear@0: } nuclear@0: if(rbuf_zstencil) { nuclear@0: glDeleteRenderbuffers(1, &rbuf_zstencil); nuclear@0: } nuclear@0: nuclear@0: fbo = rbuf_zstencil = 0; nuclear@0: width = height = 0; nuclear@0: tex_face = 0; nuclear@0: tex_targ = 0; nuclear@0: } nuclear@0: nuclear@0: bool RenderTarget::resize(int width, int height) nuclear@0: { nuclear@0: this->width = width; nuclear@0: this->height = height; nuclear@0: int tex_width = next_pow2(width); nuclear@0: int tex_height = next_pow2(height); nuclear@0: nuclear@0: debug_log("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height); nuclear@0: nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, fbo); nuclear@0: nuclear@0: color_tex->create(tex_width, tex_height, color_tex->get_format()); nuclear@0: color_tex->bind(); nuclear@0: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0); nuclear@0: glBindTexture(tex_targ, 0); nuclear@0: nuclear@0: glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); nuclear@0: glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height); nuclear@0: nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, 0); nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: int RenderTarget::get_width() const nuclear@0: { nuclear@0: return width; nuclear@0: } nuclear@0: nuclear@0: int RenderTarget::get_height() const nuclear@0: { nuclear@0: return height; nuclear@0: } nuclear@0: nuclear@0: Texture *RenderTarget::get_texture() const nuclear@0: { nuclear@0: return color_tex; nuclear@0: } nuclear@0: nuclear@0: Matrix4x4 RenderTarget::get_texture_matrix() const nuclear@0: { nuclear@0: float sx = (float)width / (float)color_tex->get_size(0); nuclear@0: float sy = (float)height / (float)color_tex->get_size(1); nuclear@0: nuclear@0: // counting on RVO to optimize this away nuclear@0: return Matrix4x4(sx, 0, 0, 0, nuclear@0: 0, sy, 0, 0, nuclear@0: 0, 0, 1, 0, nuclear@0: 0, 0, 0, 1); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: static const char *fbstname[] = { nuclear@0: "GL_FRAMEBUFFER_COMPLETE", nuclear@0: "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT", nuclear@0: "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", nuclear@0: "no such fbo error", nuclear@0: "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS", nuclear@0: "GL_FRAMEBUFFER_INCOMPLETE_FORMATS", nuclear@0: "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER", nuclear@0: "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER", nuclear@0: "GL_FRAMEBUFFER_UNSUPPORTED" nuclear@0: }; nuclear@0: nuclear@0: bool RenderTarget::check() const nuclear@0: { nuclear@0: bool res = true; nuclear@0: nuclear@0: #ifndef GL_ES_VERSION_2_0 nuclear@0: int prev_fb = 0; nuclear@0: glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb); nuclear@0: #endif nuclear@0: nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, fbo); nuclear@0: nuclear@0: int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); nuclear@0: if(status != GL_FRAMEBUFFER_COMPLETE) { nuclear@0: error_log("RenderTarget::check: incomplete FBO %u: %s\n", fbo, nuclear@0: fbstname[status - GL_FRAMEBUFFER_COMPLETE]); nuclear@0: res = false; nuclear@0: goto end; nuclear@0: } nuclear@0: nuclear@0: end: nuclear@0: #ifndef GL_ES_VERSION_2_0 nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, prev_fb); nuclear@0: #endif nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: void RenderTarget::bind() const nuclear@0: { nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, fbo); nuclear@0: nuclear@0: glViewport(0, 0, width, height); nuclear@0: } nuclear@0: nuclear@0: struct Viewport { int vp[4]; }; nuclear@0: static std::stack vpstack; nuclear@0: nuclear@0: void set_render_target(const RenderTarget *rtarg) nuclear@0: { nuclear@0: Viewport vp; nuclear@0: nuclear@0: if(rtarg) { nuclear@0: glGetIntegerv(GL_VIEWPORT, vp.vp); nuclear@0: vpstack.push(vp); nuclear@0: nuclear@0: rtarg->bind(); nuclear@0: } else { nuclear@0: #ifdef GL_ES_VERSION_2_0 nuclear@0: extern unsigned int default_fbo; nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, default_fbo); nuclear@0: #else nuclear@0: glBindFramebuffer(GL_FRAMEBUFFER, 0); nuclear@0: #endif nuclear@0: nuclear@0: if(vpstack.empty()) { nuclear@0: return; nuclear@0: } nuclear@0: vp = vpstack.top(); nuclear@0: vpstack.pop(); nuclear@0: glViewport(vp.vp[0], vp.vp[1], vp.vp[2], vp.vp[3]); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: int next_pow2(int x) nuclear@0: { nuclear@0: x--; nuclear@0: x = (x >> 1) | x; nuclear@0: x = (x >> 2) | x; nuclear@0: x = (x >> 4) | x; nuclear@0: x = (x >> 8) | x; nuclear@0: x = (x >> 16) | x; nuclear@0: return x + 1; nuclear@0: } nuclear@0: