text: /****************************************************************************
  * VCGLib                                                            o o     *
  * Visual and Computer Graphics Library                            o     o   *
  *                                                                _   O  _   *
  * Copyright(C) 2004-2016                                           \/)\/    *
  * Visual Computing Lab                                            /\/|      *
  * ISTI - Italian National Research Council                           |      *
  *                                                                    \      *
  * All rights reserved.                                                      *
  *                                                                           *
  * This program is free software; you can redistribute it and/or modify      *   
  * it under the terms of the GNU General Public License as published by      *
  * the Free Software Foundation; either version 2 of the License, or         *
  * (at your option) any later version.                                       *
  *                                                                           *
  * This program is distributed in the hope that it will be useful,           *
  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
  * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
  * for more details.                                                         *
  *                                                                           *
  ****************************************************************************/
  
  /****************************************************************************
    History
  
  $Log: not supported by cvs2svn $
  Revision 1.1  2007/07/26 16:22:47  m_di_benedetto
  First Commit.
  
  
  
  ****************************************************************************/
  
  #ifndef VCGLIB_GL_SURFACE_H
  #define VCGLIB_GL_SURFACE_H
  
  #include <vector>
  #include <GL/glew.h>
  
  namespace vcg
  {
  
  /****************************************************************************
  The gl_surface class simplify the render-to-texture OpenGL functionality.
  
  It provides a framebuffer composed of single or multiple color buffers and
  a depth buffer; color-only and depth-only framebuffers can also be created.
  
  Sample usage:
  
  ****************************************************************
  // *** declaration
  
  gl_surface my_srf;
  
  
  ***********************
  // *** initialization: single color render target, with depth buffer
  {
  	std::vector<GLenum> color_formats;
  	color_formats.push_back(GL_RGBA8);
  
  	my_srf.set(width, height, color_formats, GL_DEPTH_COMPONENT);
  }
  ***********************
  
  
  ***********************
  //  *** initialization: two color render targets, without depth buffer
  // NOTE: the maximum number of color targets is implementation dependent.
  // NOTE: DX10 class hardware allows different formats for each color target.
  {
  	std::vector<GLenum> color_formats;
  	color_formats.push_back(GL_RGBA8);
  	color_formats.push_back(GL_RGBA8);
  
  	my_srf.set(width, height, color_formats, GL_NONE);
  }
  ***********************
  
  
  ***********************
  // *** usage: render-to-targets
  {
  	my_srf.begin_write();
  		application_draw_code();
  	my_srf.end_write();
  }
  ***********************
  
  
  ***********************
  // *** usage: using rendered textures
  {
  	// 2 buffers
  
  	// bind the second
  	glActiveTexture(GL_TEXTURE1);
  	my_srf.begin_read_color(1); // actually does a glBindTexture();
  
  	// bind the first
  	glActiveTexture(GL_TEXTURE0);
  	my_srf.begin_read_color(0);
  
  		application_draw_code();
  
  	// unbind the second
  	glActiveTexture(GL_TEXTURE1);
  	my_srf.end_read_color(1);
  
  	// unbind first
  	glActiveTexture(GL_TEXTURE0);
  	my_srf.end_read_color(0);
  }
  
  ***********************
  // *** usage: use depth map
  {
  	my_srf.begin_read_depth();
  
  		// use depth map here
  		application_draw_code();
  
  	my_srf.end_read_depth();
  }
  
  ***********************
  // *** usage: cleanup
  {
  	my_srf.clear();
  	// clear() is also safely called in the destructor.
  }
  ***********************
  
  Other commodity methods for getting/setting pixels are also provided.
  
  ****************************************************************************/
  
  class gl_surface
  {
  	public:
  
  		typedef gl_surface this_type;
  
  		gl_surface(void) : width(0), height(0), depth_tex(0), fb(0)
  		{
  			;
  		}
  
  		~gl_surface(void)
  		{
  			this->clear();
  		}
  
  		bool set(int width, int height, const std::vector<GLenum> & color_formats, GLenum depth_format)
  		{
  			this->clear();
  
  			this->width  = width;
  			this->height = height;
  
  			this->color_formats = color_formats;
  
  			this->color_texs.resize(color_formats.size());
  
  			for (size_t i=0; i<this->color_texs.size(); ++i)
  			{
  				glGenTextures   (1, &(this->color_texs[i]));
  				glBindTexture   (GL_TEXTURE_2D, this->color_texs[i]);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_CLAMP_TO_EDGE);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_CLAMP_TO_EDGE);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  				glTexImage2D    (GL_TEXTURE_2D, 0, this->color_formats[i], this->width, this->height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
  			}
  
  			this->depth_format = depth_format;
  			if (this->depth_format != GL_NONE)
  			{
  				glGenTextures   (1, &(this->depth_tex));
  				glBindTexture   (GL_TEXTURE_2D, this->depth_tex);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,           GL_CLAMP_TO_EDGE);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,           GL_CLAMP_TO_EDGE);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,       GL_NEAREST);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,       GL_NEAREST);
  				glTexParameteri (GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB,   GL_LUMINANCE);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
  				glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
  				glTexImage2D    (GL_TEXTURE_2D, 0, this->depth_format, this->width, this->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
  			}
  
  			glGenFramebuffersEXT(1, &(this->fb));
  			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, this->fb);
  
  			std::vector<GLenum> sites(this->color_texs.size());
  			for (size_t i=0; i<this->color_texs.size(); ++i)
  			{
  				sites[i] = GL_COLOR_ATTACHMENT0_EXT + ((GLenum)i);
  				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, sites[i], GL_TEXTURE_2D, this->color_texs[i], 0);
  			}
  
  			if (this->depth_format != GL_NONE)
  			{
  				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, this->depth_tex, 0);
  			}
  
  			if (!sites.empty())
  			{
  				glDrawBuffers((GLsizei)(sites.size()), &(sites[0]));
  			}
  
  			const GLenum s = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  			const bool res = (s == GL_FRAMEBUFFER_COMPLETE_EXT);
  
  			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
  
  			if (!res)
  			{
  				glDeleteFramebuffersEXT(1, &(this->fb));
  				this->fb = 0;
  
  				for (size_t i=0; i<this->color_texs.size(); ++i)
  				{
  					glDeleteTextures(1, &(this->color_texs[i]));
  				}
  				this->color_texs.clear();
  
  				this->color_formats.clear();
  
  				if (this->depth_tex != 0)
  				{
  					glDeleteTextures(1, &(this->depth_tex));
  					this->depth_tex = 0;
  				}
  
  				this->width  = 0;
  				this->height = 0;
  
  				return false;
  			}
  
  			return true;
  		}
  
  		bool set_simple(int width, int height)
  		{
  			std::vector<GLenum> c_formats;
  			c_formats.push_back(GL_RGBA8);
  
  			return this->set(width, height, c_formats, GL_DEPTH_COMPONENT);
  		}
  
  		bool set_color_only(int width, int height, GLenum color_format)
  		{
  			std::vector<GLenum> c_formats;
  			c_formats.push_back(color_format);
  
  			return this->set(width, height, c_formats, GL_NONE);
  		}
  
  		bool set_color_only(int width, int height, const std::vector<GLenum> & color_formats)
  		{
  			return this->set(width, height, color_formats, GL_NONE);
  		}
  
  		bool set_depth_only(int width, int height, GLenum depth_format)
  		{
  			std::vector<GLenum> c_formats;
  
  			return this->set(width, height, c_formats, depth_format);
  		}
  
  		bool clear(void)
  		{
  			if (!this->is_valid()) return false;
  
  			glDeleteFramebuffersEXT(1, &(this->fb));
  			this->fb = 0;
  
  			for (size_t i=0; i<this->color_texs.size(); ++i)
  			{
  				glDeleteTextures(1, &(this->color_texs[i]));
  			}
  			this->color_texs.clear();
  
  			this->color_formats.clear();
  
  			if (this->depth_tex != 0)
  			{
  				glDeleteTextures(1, &(this->depth_tex));
  				this->depth_tex = 0;
  			}
  
  			this->width  = 0;
  			this->height = 0;
  
  			return true;
  		}
  
  		bool is_valid(void) const
  		{
  			return (this->fb != 0);
  		}
  
  		int get_width(void) const
  		{
  			return this->width;
  		}
  
  		int get_height(void) const
  		{
  			return this->height;
  		}
  
  		int color_attachments_count(void) const
  		{
  			return ((int)(this->color_texs.size()));
  		}
  
  		GLenum get_color_attachment_format(int attachment) const
  		{
  			if (!this->is_valid()) return GL_NONE;
  			if ((attachment < 0) || (attachment >= this->color_attachments_count())) return GL_NONE;
  
  			return this->color_formats[attachment];
  		}
  
  		bool has_depth_attachment(void) const
  		{
  			return (this->depth_tex != 0);
  		}
  
  		GLenum get_depth_attachment_format(void) const
  		{
  			if (!this->is_valid()) return GL_NONE;
  			if (!this->has_depth_attachment()) return GL_NONE;
  
  			return this->depth_format;
  		}
  
  		bool set_color_pixels(int attachment, GLenum format, GLenum type, const void * pixels)
  		{
  			if (!this->begin_read_color(attachment)) return false;
  
  			glTexImage2D(GL_TEXTURE_2D, 0, this->color_formats[attachment], this->width, this->height, 0, format, type, pixels);
  
  			this->end_read_color(attachment);
  
  			return true;
  		}
  
  		bool get_color_pixels(int attachment, GLenum format, GLenum type, void * pixels)
  		{
  			if (!this->begin_read_color(attachment)) return false;
  
  			glGetTexImage(GL_TEXTURE_2D, 0, format, type, pixels);
  
  			this->end_read_color(attachment);
  
  			return true;
  		}
  
  		bool set_depth_pixels(GLenum format, GLenum type, const void * pixels)
  		{
  			if (!this->is_valid()) return false;
  			if (!this->has_depth_attachment()) return false;
  
  			glTexImage2D(GL_TEXTURE_2D, 0, this->depth_format, this->width, this->height, 0, format, type, pixels);
  
  			return true;
  		}
  
  		bool get_depth_pixels(GLenum format, GLenum type, void * pixels)
  		{
  			if (!this->begin_read_depth()) return false;
  
  			glGetTexImage(GL_TEXTURE_2D, 0, format, type, pixels);
  
  			this->end_read_depth();
  
  			return true;
  		}
  
  		bool begin_read_color(int attachment)
  		{
  			if (!this->is_valid()) return false;
  			if ((attachment < 0) || (attachment >= this->color_attachments_count())) return false;
  
  			glBindTexture(GL_TEXTURE_2D, this->color_texs[attachment]);
  
  			return true;
  		}
  
  		bool end_read_color(int attachment)
  		{
  			if (!this->is_valid()) return false;
  			if ((attachment < 0) || (attachment >= this->color_attachments_count())) return false;
  
  			glBindTexture(GL_TEXTURE_2D, 0);
  
  			return true;
  		}
  
  		bool begin_read_depth(void)
  		{
  			if (!this->is_valid()) return false;
  			if (!this->has_depth_attachment()) return false;
  
  			glBindTexture(GL_TEXTURE_2D, this->depth_tex);
  
  			return true;
  		}
  
  		bool end_read_depth(void)
  		{
  			if (!this->is_valid()) return false;
  			if (!this->has_depth_attachment()) return false;
  
  			glBindTexture(GL_TEXTURE_2D, 0);
  
  			return true;
  		}
  
  		bool begin_write(void)
  		{
  			if (!this->is_valid()) return false;
  
  			glPushAttrib(GL_VIEWPORT_BIT);
  
  			glViewport(0, 0, this->width, this->height);
  
  			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, this->fb);
  
  			return true;
  		}
  
  		bool end_write(void)
  		{
  			if (!this->is_valid()) return false;
  
  			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
  
  			glPopAttrib();
  
  			return true;
  		}
  
  		bool draw_color_attachment(int x, int y, int width, int height, int attachment)
  		{
  			if (!this->is_valid()) return false;
  			if ((attachment < 0) || (attachment >= this->color_attachments_count())) return false;
  
  			glPushAttrib(GL_ALL_ATTRIB_BITS);
  
  			glViewport(x, y, width, height);
  
  			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  
  			glMatrixMode(GL_PROJECTION);
  			glPushMatrix();
  			glLoadIdentity();
  
  			glMatrixMode(GL_MODELVIEW);
  			glPushMatrix();
  			glLoadIdentity();
  
  			this->begin_read_color(attachment);
  				glBegin(GL_QUADS);
  					glTexCoord2f(0.0f, 0.0f);    glVertex2f(-1.0f, -1.0f);
  					glTexCoord2f(1.0f, 0.0f);    glVertex2f( 1.0f, -1.0f);
  					glTexCoord2f(1.0f, 1.0f);    glVertex2f( 1.0f,  1.0f);
  					glTexCoord2f(0.0f, 1.0f);    glVertex2f(-1.0f,  1.0f);
  				glEnd();
  			this->end_read_color(attachment);
  
  			glMatrixMode(GL_PROJECTION);
  			glPopMatrix();
  
  			glMatrixMode(GL_MODELVIEW);
  			glPopMatrix();
  
  			glPopAttrib();
        return true;
  		}
  
  		bool draw_depth_attachment(int x, int y, int width, int height)
  		{
  			if (!this->is_valid()) return false;
  			if (!this->has_depth_attachment()) return false;
  
  			glPushAttrib(GL_ALL_ATTRIB_BITS);
  
  			glViewport(x, y, width, height);
  
  			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  
  			glMatrixMode(GL_PROJECTION);
  			glPushMatrix();
  			glLoadIdentity();
  
  			glMatrixMode(GL_MODELVIEW);
  			glPushMatrix();
  			glLoadIdentity();
  
  			this->begin_read_depth();
  				glBegin(GL_QUADS);
  					glTexCoord2f(0.0f, 0.0f);    glVertex2f(-1.0f, -1.0f);
  					glTexCoord2f(1.0f, 0.0f);    glVertex2f( 1.0f, -1.0f);
  					glTexCoord2f(1.0f, 1.0f);    glVertex2f( 1.0f,  1.0f);
  					glTexCoord2f(0.0f, 1.0f);    glVertex2f(-1.0f,  1.0f);
  				glEnd();
  			this->end_read_depth();
  
  			glMatrixMode(GL_PROJECTION);
  			glPopMatrix();
  
  			glMatrixMode(GL_MODELVIEW);
  			glPopMatrix();
  
  			glPopAttrib();
        return true;
  		}
  
  	protected:
  
  		int width;
  		int height;
  		std::vector<GLenum> color_formats;
  		std::vector<GLuint> color_texs;
  		GLenum depth_format;
  		GLuint depth_tex;
  		GLuint fb;
  };
  
  } // end namespace vcg
  
  #endif // VCGLIB_GL_SURFACE_H
  