+#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;
+}
+