framebuffer.cpp

Go to the documentation of this file.
00001 /*
00002  * framebuffer.cpp
00003  *
00004  * Copyright (C) 2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of simple framebuffer object for offscreen drawing.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "framebuffer.h"                // always include our own header first!
00036 
00037 #include "perf/perf.h"
00038 
00039 
00040 namespace glut {
00041 
00042 
00043 // interface destructors
00044 Framebuffer::~Framebuffer(void) throw() { }
00045 
00046 
00047 ////////////////////////////////////////////////////////////////////////////////
00048 //
00049 //      static helper methods
00050 //
00051 ////////////////////////////////////////////////////////////////////////////////
00052 
00053 
00054 
00055 ////////////////////////////////////////////////////////////////////////////////
00056 //
00057 //      FBuffer -- class that implements the glut::Framebuffer interface
00058 //
00059 ////////////////////////////////////////////////////////////////////////////////
00060 
00061 class FBuffer : public Framebuffer {
00062 public:
00063         FBuffer(void) throw();
00064         ~FBuffer(void) throw();
00065 
00066         // public class methods ------------------------------------------------
00067         void initialize(IN int width, IN int height);
00068 
00069         // glut::Framebuffer class interface methods ---------------------------
00070         int getWidth(void) const throw() { return m_width; }
00071         int getHeight(void) const throw() { return m_height; }
00072         void setupForRendering(IN int textureId,
00073                                 IN const render_context_t& rc);
00074         void endRendering(void) throw();
00075 
00076 private:
00077         // private member data -------------------------------------------------
00078         int             m_width;
00079         int             m_height;
00080         GLuint          m_fbufId;       // framebuffer ID
00081         GLuint          m_rbufId;       // renderbuffer ID
00082 };
00083 
00084 
00085 
00086 FBuffer::FBuffer
00087 (
00088 void
00089 )
00090 throw()
00091 {
00092         m_width = m_height = 0;
00093         m_fbufId = 0;
00094         m_rbufId = 0;
00095 }
00096 
00097 
00098 
00099 FBuffer::~FBuffer
00100 (
00101 void
00102 )
00103 throw()
00104 {
00105         if (m_rbufId) {
00106                 glDeleteRenderbuffersEXT(1, &m_rbufId);
00107         }
00108         if (m_fbufId) {
00109                 glDeleteFramebuffersEXT(1, &m_fbufId);
00110         }
00111 }
00112 
00113 
00114 
00115 void
00116 FBuffer::initialize
00117 (
00118 IN int width,
00119 IN int height
00120 )
00121 {
00122         ASSERT(width > 1, "bad width: %d", width);
00123         ASSERT(height > 1, "bad height: %d", height);
00124 
00125         // save width and height
00126         m_width = width;
00127         m_height = height;
00128 
00129         // create raw OpenGL framebuffer
00130         ASSERT(!m_fbufId, "already have framebuffer?");
00131         glGenFramebuffersEXT(1, &m_fbufId);
00132         ASSERT(m_fbufId, "failed to create OpenGL framebuffer");
00133         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbufId);
00134 
00135         // create renderbuffer for depth buffer, attach to framebuffer
00136         ASSERT(!m_rbufId, "already have a renderbuffer?");
00137         glGenRenderbuffersEXT(1, &m_rbufId);
00138         ASSERT(m_rbufId, "failed to create OpenGL renderbuffer");
00139         glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_rbufId);
00140         glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
00141             m_width, m_height);
00142         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
00143             GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_rbufId);
00144 
00145         // now that we have finished construction, unbind framebuffer
00146         const int nullFramebuffer = 0;
00147         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, nullFramebuffer);
00148 }
00149 
00150 
00151 
00152 void
00153 FBuffer::setupForRendering
00154 (
00155 IN int textureId,
00156 IN const render_context_t& rc
00157 )
00158 {
00159         ASSERT(textureId, "null");
00160 
00161         // bind and validate incoming texture
00162         // NOTE: I've heard these glGet() calls can be expensive (can interrupt
00163         //      GPU processing).  But I'm not sure if I want to require another
00164         //      struct besides the texture ID.
00165         glBindTexture(GL_TEXTURE_2D, textureId);
00166         const int levelOfDetail = 0;
00167         int width, height;
00168         glGetTexLevelParameteriv(GL_TEXTURE_2D, levelOfDetail, GL_TEXTURE_WIDTH,
00169             &width);
00170         glGetTexLevelParameteriv(GL_TEXTURE_2D, levelOfDetail, GL_TEXTURE_HEIGHT,
00171             &height);
00172 
00173         ASSERT_THROW(width == m_width && height == m_height,
00174             "Incoming texture size does not match framebuffer.  Texture size: "
00175             << width << " x " << height << ", framebuffer size: " << m_width
00176             << " x " << m_height);
00177 
00178         // okay, bind our framebuffer in preparation for drawing
00179         ASSERT(m_fbufId, "should have valid framebuffer");
00180         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbufId);
00181 
00182         // bind texture to framebuffer
00183         const int mipmapLevel = 0;
00184         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
00185             GL_TEXTURE_2D, textureId, mipmapLevel);
00186 
00187         // framebuffer should be complete now
00188         GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
00189         ASSERT_THROW(GL_FRAMEBUFFER_COMPLETE_EXT == status,
00190             "Framebuffer is not complete, status = " << status);
00191 
00192         // save current opengl state
00193         glMatrixMode(GL_PROJECTION);
00194         glPushMatrix();
00195         glPushAttrib(GL_VIEWPORT_BIT);
00196 
00197         // set up new projection + modelview matrices
00198         camera_t camera = rc.camera;
00199         camera.setAspect(m_width, m_height);
00200         setOpenGLProjection(camera);
00201         glViewport(0, 0, m_width, m_height);
00202 }
00203 
00204 
00205 
00206 void
00207 FBuffer::endRendering
00208 (
00209 void
00210 )
00211 throw()
00212 {
00213         // unbind framebuffer
00214         const int nullFramebuffer = 0;
00215         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, nullFramebuffer);
00216 
00217         // unbind texture to framebuffer
00218         const int mipmapLevel = 0;
00219         const int nullTexture = 0;
00220         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
00221             GL_TEXTURE_2D, nullTexture, mipmapLevel);
00222 
00223         // restore original transformation matrices
00224         glPopAttrib();
00225         glMatrixMode(GL_PROJECTION);
00226         glPopMatrix();
00227 }
00228 
00229 
00230 
00231 ////////////////////////////////////////////////////////////////////////////////
00232 //
00233 //      public API
00234 //
00235 ////////////////////////////////////////////////////////////////////////////////
00236 
00237 smart_ptr<Framebuffer>
00238 Framebuffer::create
00239 (
00240 IN int width,
00241 IN int height
00242 )
00243 {
00244         ASSERT(width > 1, "Bad width: %d", width);
00245         ASSERT(height > 1, "Bad height: %d", height);
00246 
00247         smart_ptr<FBuffer> local = new FBuffer;
00248         ASSERT(local, "out of memory");
00249 
00250         local->initialize(width, height);
00251 
00252         return local;
00253 }
00254 
00255 
00256 
00257 };      // glut namespace
00258