Browse Source

Added method for creating an empty FrameBuffer object.
Added method for creating a gameplay Texture object from a native OpenGL texture handle, to allow unrestricted access to OpenGL texture formats and extensions which cannot be represented by the Texture::Format enumeration.
Added method for creating a RenderTarget object from a Texture object.
Added the ability to register material parameter auto binding resolvers for extending or overriding the set of built-in material AutoBinding support.
Some code cleanup in FrameBuffer, RenderTarget and DepthStencilTarget classes.
Removed all (non-debug-mode) glGet calls throughout the code to ensure there are no unnecessary GL pipeline stalls.
Removed the GL_CHECK macro since it promoted use of glGetError at runtime (use GL_ASSERT instead).

Steve Grenier 13 years ago
parent
commit
4ee5b127e4

+ 0 - 25
gameplay/src/Base.h

@@ -295,35 +295,10 @@ typedef GLuint RenderBufferHandle;
     } while(0)
 #endif
 
-/**
- * Executes the specified GL code and checks the GL error afterwards
- * to ensure it succeeded.
- *
- * This macro should be used instead of GL_ASSERT for code that must
- * be checked in both debug and release builds. The GL_LAST_ERROR
- * macro can be used afterwards to check whether a GL error was
- * encountered executing the specified code.
- */
-#define GL_CHECK( gl_code ) do \
-    { \
-        while (glGetError() != GL_NO_ERROR) ; \
-        gl_code; \
-        __gl_error_code = glGetError(); \
-        if (__gl_error_code != GL_NO_ERROR) \
-        { \
-            GP_ERROR(#gl_code ": %d", (int)__gl_error_code); \
-        } \
-    } while(0)
-
 /** Global variable to hold GL errors
  * @script{ignore} */
 extern GLenum __gl_error_code;
 
-/**
- * Accesses the most recently set global GL error.
- */
-#define GL_LAST_ERROR() __gl_error_code
-
 /**
  * Executes the specified AL code and checks the AL error afterwards
  * to ensure it succeeded.

+ 18 - 3
gameplay/src/DepthStencilTarget.cpp

@@ -6,8 +6,8 @@ namespace gameplay
 
 static std::vector<DepthStencilTarget*> __depthStencilTargets;
 
-DepthStencilTarget::DepthStencilTarget(const char* id, Format format)
-    : _id(id ? id : ""), _format(format), _renderBuffer(0)
+DepthStencilTarget::DepthStencilTarget(const char* id, Format format, unsigned int width, unsigned int height)
+    : _id(id ? id : ""), _format(format), _renderBuffer(0), _width(width), _height(height)
 {
 }
 
@@ -28,7 +28,12 @@ DepthStencilTarget::~DepthStencilTarget()
 DepthStencilTarget* DepthStencilTarget::create(const char* id, Format format, unsigned int width, unsigned int height)
 {
     // Create the depth stencil target.
-    DepthStencilTarget* depthStencilTarget = new DepthStencilTarget(id, format);
+    DepthStencilTarget* depthStencilTarget = new DepthStencilTarget(id, format, width, height);
+
+    // Create a render buffer for this new depth stencil target
+    GL_ASSERT( glGenRenderbuffers(1, &depthStencilTarget->_renderBuffer) );
+    GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, depthStencilTarget->_renderBuffer) );
+    GL_ASSERT( glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height) );
 
     // Add it to the cache.
     __depthStencilTargets.push_back(depthStencilTarget);
@@ -64,4 +69,14 @@ DepthStencilTarget::Format DepthStencilTarget::getFormat() const
     return _format;
 }
 
+unsigned int DepthStencilTarget::getWidth() const
+{
+    return _width;
+}
+
+unsigned int DepthStencilTarget::getHeight() const
+{
+    return _height;
+}
+
 }

+ 18 - 6
gameplay/src/DepthStencilTarget.h

@@ -8,11 +8,7 @@ namespace gameplay
 {
 
 /**
- * Defines a contain for depth and stencil targets in a frame buffer object.
- *
- * This class assumes that the target hardware supports depth textures, since
- * creation of a DepthStencilTarget always attempts to create an underlying
- * depth texture.
+ * Defines a container for depth and stencil targets in a frame buffer object.
  */
 class DepthStencilTarget : public Ref
 {
@@ -72,12 +68,26 @@ public:
      */
     Format getFormat() const;
 
+    /**
+     * Returns the width of the DepthStencilTarget.
+     *
+     * @return The width.
+     */
+    unsigned int getWidth() const;
+
+    /**
+     * Returns the height of the DepthStencilTarget.
+     *
+     * @return The height.
+     */
+    unsigned int getHeight() const;
+
 private:
 
     /**
      * Constructor.
      */
-    DepthStencilTarget(const char* id, Format format);
+    DepthStencilTarget(const char* id, Format format, unsigned int width, unsigned int height);
 
     /**
      * Destructor.
@@ -92,6 +102,8 @@ private:
     std::string _id;
     Format _format;
     RenderBufferHandle _renderBuffer;
+    unsigned int _width;
+    unsigned int _height;
 };
 
 }

+ 2 - 2
gameplay/src/Effect.cpp

@@ -563,8 +563,8 @@ void Effect::setValue(Uniform* uniform, const Texture::Sampler* sampler)
 
 void Effect::bind()
 {
-   glUseProgram(_program) ;
-   GLenum test = glGetError();
+   GL_ASSERT( glUseProgram(_program) );
+
     __currentEffect = this;
 }
 

+ 57 - 56
gameplay/src/FrameBuffer.cpp

@@ -11,6 +11,7 @@ namespace gameplay
 static unsigned int __maxRenderTargets = 0;
 static std::vector<FrameBuffer*> __frameBuffers;
 static FrameBufferHandle __defaultHandle = 0;
+static FrameBufferHandle __currentHandle = 0;
 
 FrameBuffer::FrameBuffer(const char* id, unsigned int width, unsigned int height) :
     _id(id ? id : ""), _width(width), _height(height), _handle(0), 
@@ -52,29 +53,50 @@ FrameBuffer::~FrameBuffer()
 
 void FrameBuffer::initialize()
 {
+    // Query the current/initial FBO handle and store is as out 'default' frame buffer.
+    // On many platforms this will simply be the zero (0) handle, but this is not always the case.
     GLint fbo;
     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
     __defaultHandle = (FrameBufferHandle)fbo;
+    __currentHandle = __defaultHandle;
+
+    // Query the max supported color attachments. This glGet operation is not supported
+    // on GL ES 2.x, so if the define does not exist, assume a value of 1.
+#ifdef GL_MAX_COLOR_ATTACHMENTS
+        GLint val;
+        GL_ASSERT( glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &val) );
+        __maxRenderTargets = (unsigned int)std::max(1, val);
+#else
+        __maxRenderTargets = 1;
+#endif
 }
 
-FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned int height)
+FrameBuffer* FrameBuffer::create(const char* id)
 {
-    if (!isPowerOfTwo(width) | !isPowerOfTwo(height))
-    {
-        GP_ERROR("Failed to create render target for frame buffer. Width and Height must be a power of 2.");
-        return NULL;
-    }
+    return create(id, 0, 0);
+}
 
+FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned int height)
+{
     // Call getMaxRenderTargets() to force __maxRenderTargets to be set
     getMaxRenderTargets();
 
-    // Create RenderTarget with same ID.
     RenderTarget* renderTarget = NULL;
-    renderTarget = RenderTarget::create(id, width, height);
-    if (renderTarget == NULL)
+    if (width > 0 && height > 0)
     {
-        GP_ERROR("Failed to create render target for frame buffer.");
-        return NULL;
+        if (!isPowerOfTwo(width) | !isPowerOfTwo(height))
+        {
+            GP_ERROR("Failed to create render target for frame buffer. Width and Height must be a power of 2.");
+            return NULL;
+        }
+
+        // Create a default RenderTarget with same ID.
+        renderTarget = RenderTarget::create(id, width, height);
+        if (renderTarget == NULL)
+        {
+            GP_ERROR("Failed to create render target for frame buffer.");
+            return NULL;
+        }
     }
 
     // Create the frame buffer
@@ -84,12 +106,14 @@ FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned in
     frameBuffer->_handle = handle;
     
     // Create the render target array for the new frame buffer
-    RenderTarget** renderTargets = new RenderTarget*[__maxRenderTargets];
-    memset(renderTargets, 0, sizeof(RenderTarget*) * __maxRenderTargets);
-    frameBuffer->_renderTargets = renderTargets;
+    frameBuffer->_renderTargets = new RenderTarget*[__maxRenderTargets];
+    memset(frameBuffer->_renderTargets, 0, sizeof(RenderTarget*) * __maxRenderTargets);
 
-    frameBuffer->setRenderTarget(renderTarget, 0);
-    SAFE_RELEASE(renderTarget);
+    if (renderTarget)
+    {
+        frameBuffer->setRenderTarget(renderTarget, 0);
+        SAFE_RELEASE(renderTarget);
+    }
 
     __frameBuffers.push_back(frameBuffer);
 
@@ -131,16 +155,6 @@ unsigned int FrameBuffer::getHeight() const
 
 unsigned int FrameBuffer::getMaxRenderTargets()
 {
-    if (__maxRenderTargets == 0)
-    {
-#ifdef GL_MAX_COLOR_ATTACHMENTS
-        GLint val;
-        GL_ASSERT( glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &val) );
-        __maxRenderTargets = (unsigned int) val;
-#else
-        __maxRenderTargets = 1;
-#endif
-    }
     return __maxRenderTargets;
 }
 
@@ -162,31 +176,23 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
 
     if (target)
     {
+        GP_ASSERT( _renderTargets[index]->getTexture() );
+
         // This FrameBuffer now references the RenderTarget.
         target->addRef();
 
-        // Store the current FBO binding so we can restore it
-        GLint currentFbo;
-        GL_ASSERT( glGetIntegerv(GL_FRAMEBUFFER_BINDING, &currentFbo) );
-
         // Now set this target as the color attachment corresponding to index.
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
-        GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _renderTargets[index]->getTexture()->getHandle()) );
-        GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) );
-        GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) );
-        GL_ASSERT( glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
-        GL_ASSERT( glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) );
-        GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL) );
         GLenum attachment = GL_COLOR_ATTACHMENT0 + index;
-        GP_ASSERT( _renderTargets[index]->getTexture() );
         GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, _renderTargets[index]->getTexture()->getHandle(), 0) );
         GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
         if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
         {
-            GP_ERROR("Framebuffer status incompleted: 0x%x", fboStatus);
+            GP_ERROR("Framebuffer status incomplete: 0x%x", fboStatus);
         }
+
         // Restore the FBO binding
-        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, currentFbo) );
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, __currentHandle) );
     }
 }
 
@@ -216,36 +222,25 @@ void FrameBuffer::setDepthStencilTarget(DepthStencilTarget* target)
         // The FrameBuffer now owns this DepthStencilTarget.
         target->addRef();
 
-        // Store the current FBO binding so we can restore it.
-        GLint currentFbo;
-        GL_ASSERT( glGetIntegerv(GL_FRAMEBUFFER_BINDING, &currentFbo) );
-
         // Now set this target as the color attachment corresponding to index.
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
-        
-        // Create a render buffer 
-        RenderBufferHandle renderBuffer = 0;
-        GL_ASSERT( glGenRenderbuffers(1, &renderBuffer) );
-        GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer) );
-        GL_ASSERT( glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height) );
-        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer) );
-        // Attach the 
+
+        // Attach the render buffer to the framebuffer
+        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencilTarget->_renderBuffer) );
         if (target->getFormat() == DepthStencilTarget::DEPTH_STENCIL)
         {
-            GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer) );
+            GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthStencilTarget->_renderBuffer) );
         }
 
         // Check the framebuffer is good to go.
         GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
         if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
         {
-            GP_ERROR("Framebuffer status incompleted: 0x%x", fboStatus);
+            GP_ERROR("Framebuffer status incomplete: 0x%x", fboStatus);
         }
-        _depthStencilTarget->_renderBuffer = renderBuffer;
-
 
         // Restore the FBO binding
-        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, currentFbo) );
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, __currentHandle) );
     }
 }
 
@@ -258,11 +253,17 @@ void FrameBuffer::bind()
 {
     // Bind this FrameBuffer for rendering.
     GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
+
+    // Update the current FBO handle
+    __currentHandle = _handle;
 }
 
 void FrameBuffer::bindDefault()
 {
     GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, __defaultHandle) );
+
+    // Update the current FBO handle
+    __currentHandle = __defaultHandle;
 }
 
 bool FrameBuffer::isPowerOfTwo(unsigned int value)

+ 20 - 1
gameplay/src/FrameBuffer.h

@@ -23,12 +23,31 @@ class FrameBuffer : public Ref
 
 public:
 
+    /**
+     * Creates a new, empty FrameBuffer object.
+     *
+     * The new FrameBuffer does not have any render targets or a depth/stencil target and these
+     * must be added before it can be used. The FrameBuffer is added to the list of available
+     * FrameBuffers.
+     *
+     * @param id The ID of the new FrameBuffer. Uniqueness is recommended but not enforced.
+     *
+     * @return A newly created FrameBuffer.
+     * @script{create}
+     */
+    static FrameBuffer* create(const char* id);
+
     /**
      * Creates a new FrameBuffer with a single RenderTarget of the specified width and height,
      * and adds the FrameBuffer to the list of available FrameBuffers.
+     *
+     * If width and height are non-zero a default RenderTarget of type RGBA will be created
+     * and added to the FrameBuffer, with the same ID. The ID of the render target can be
+     * changed later via RenderTarget::setId(const char*).
+     *
      * You can additionally add a DepthStencilTarget using FrameBuffer::setDepthStencilTarget.
      *
-     * @param id The ID of the new FrameBuffer.  Uniqueness is recommended but not enforced.
+     * @param id The ID of the new FrameBuffer. Uniqueness is recommended but not enforced.
      * @param width The width of the RenderTarget to be created and attached.
      * @param height The height of the RenderTarget to be created and attached.
      *

+ 1 - 21
gameplay/src/Mesh.cpp

@@ -36,28 +36,8 @@ Mesh* Mesh::createMesh(const VertexFormat& vertexFormat, unsigned int vertexCoun
 {
     GLuint vbo;
     GL_ASSERT( glGenBuffers(1, &vbo) );
-    if (GL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to create VBO for mesh with OpenGL error %d.", GL_LAST_ERROR());
-        return NULL;
-    }
-
     GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, vbo) );
-    if (GL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to bind VBO for mesh with OpenGL error %d.", GL_LAST_ERROR());
-        glDeleteBuffers(1, &vbo);
-        return NULL;
-    }
-
-    GL_CHECK( glBufferData(GL_ARRAY_BUFFER, vertexFormat.getVertexSize() * vertexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
-    if (GL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to load VBO with vertex data with OpenGL error %d.", GL_LAST_ERROR());
-        glBindBuffer(GL_ARRAY_BUFFER, 0);
-        glDeleteBuffers(1, &vbo);
-        return NULL;
-    }
+    GL_ASSERT( glBufferData(GL_ARRAY_BUFFER, vertexFormat.getVertexSize() * vertexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
 
     Mesh* mesh = new Mesh(vertexFormat);
     mesh->_vertexCount = vertexCount;

+ 2 - 19
gameplay/src/MeshPart.cpp

@@ -23,19 +23,7 @@ MeshPart* MeshPart::create(Mesh* mesh, unsigned int meshIndex, Mesh::PrimitiveTy
     // Create a VBO for our index buffer.
     GLuint vbo;
     GL_ASSERT( glGenBuffers(1, &vbo) );
-    if (GL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to create VBO for index buffer with OpenGL error %d.", GL_LAST_ERROR());
-        return NULL;
-    }
-
     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo) );
-    if (GL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to bind VBO for index buffer with OpenGL error %d.", GL_LAST_ERROR());
-        glDeleteBuffers(1, &vbo);
-        return NULL;
-    }
 
     unsigned int indexSize = 0;
     switch (indexFormat)
@@ -54,13 +42,8 @@ MeshPart* MeshPart::create(Mesh* mesh, unsigned int meshIndex, Mesh::PrimitiveTy
         glDeleteBuffers(1, &vbo);
         return NULL;
     }
-    GL_CHECK( glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize * indexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
-    if (GL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to load VBO with index data with OpenGL error %d.", GL_LAST_ERROR());
-        glDeleteBuffers(1, &vbo);
-        return NULL;
-    }
+
+    GL_ASSERT( glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize * indexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
 
     MeshPart* part = new MeshPart();
     part->_mesh = mesh;

+ 124 - 134
gameplay/src/RenderState.cpp

@@ -16,6 +16,7 @@ namespace gameplay
 {
 
 RenderState::StateBlock* RenderState::StateBlock::_defaultState = NULL;
+std::vector<RenderState::ResolveAutoBindingCallback> RenderState::_customAutoBindingResolvers;
 
 RenderState::RenderState()
     : _nodeBinding(NULL), _state(NULL), _parent(NULL)
@@ -46,6 +47,11 @@ void RenderState::finalize()
     SAFE_RELEASE(StateBlock::_defaultState);
 }
 
+void RenderState::registerAutoBindingResolver(ResolveAutoBindingCallback callback)
+{
+    _customAutoBindingResolvers.push_back(callback);
+}
+
 MaterialParameter* RenderState::getParameter(const char* name) const
 {
     GP_ASSERT(name);
@@ -69,91 +75,73 @@ MaterialParameter* RenderState::getParameter(const char* name) const
     return param;
 }
 
-void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBinding)
+const char* autoBindingToString(RenderState::AutoBinding autoBinding)
 {
-    GP_ASSERT(name);
-
-    // Store the auto-binding.
-    if (autoBinding == NONE)
-    {
-        // Clear current auto binding.
-        std::map<std::string, AutoBinding>::iterator itr = _autoBindings.find(name);
-        if (itr != _autoBindings.end())
-        {
-            _autoBindings.erase(itr);
-        }
-    }
-    else
+    // NOTE: As new AutoBinding values are added, this switch statement must be updatd.
+    switch (autoBinding)
     {
-        // Set new auto binding.
-        _autoBindings[name] = autoBinding;
-    }
+    case RenderState::NONE:
+        return NULL;
 
-    // If we have a currently set node binding, apply the auto binding immediately.
-    if (_nodeBinding)
-    {
-        applyAutoBinding(name, autoBinding);
+    case RenderState::VIEW_MATRIX:
+        return "VIEW_MATRIX";
+
+    case RenderState::PROJECTION_MATRIX:
+        return "PROJECTION_MATRIX";
+
+    case RenderState::WORLD_VIEW_MATRIX:
+        return "WORLD_VIEW_MATRIX";
+
+    case RenderState::VIEW_PROJECTION_MATRIX:
+        return "VIEW_PROJECTION_MATRIX";
+
+    case RenderState::WORLD_VIEW_PROJECTION_MATRIX:
+        return "WORLD_VIEW_PROJECTION_MATRIX";
+
+    case RenderState::INVERSE_TRANSPOSE_WORLD_MATRIX:
+        return "INVERSE_TRANSPOSE_WORLD_MATRIX";
+
+    case RenderState::INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX:
+        return "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX";
+
+    case RenderState::CAMERA_WORLD_POSITION:
+        return "CAMERA_WORLD_POSITION";
+
+    case RenderState::CAMERA_VIEW_POSITION:
+        return "CAMERA_VIEW_POSITION";
+
+    case RenderState::MATRIX_PALETTE:
+        return "MATRIX_PALETTE";
     }
 }
 
+void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBinding)
+{
+    setParameterAutoBinding(name, autoBindingToString(autoBinding));
+}
+
 void RenderState::setParameterAutoBinding(const char* name, const char* autoBinding)
 {
+    GP_ASSERT(name);
     GP_ASSERT(autoBinding);
-    AutoBinding value = NONE;
 
-    // Parse the passed in autoBinding string.
-    if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
+    if (autoBinding == NULL)
     {
-        value = WORLD_MATRIX;
-    }
-    else if (strcmp(autoBinding, "VIEW_MATRIX") == 0)
-    {
-        value = VIEW_MATRIX;
-    }
-    else if (strcmp(autoBinding, "PROJECTION_MATRIX") == 0)
-    {
-        value = PROJECTION_MATRIX;
-    }
-    else if (strcmp(autoBinding, "WORLD_VIEW_MATRIX") == 0)
-    {
-        value = WORLD_VIEW_MATRIX;
-    }
-    else if (strcmp(autoBinding, "VIEW_PROJECTION_MATRIX") == 0)
-    {
-        value = VIEW_PROJECTION_MATRIX;
-    }
-    else if (strcmp(autoBinding, "WORLD_VIEW_PROJECTION_MATRIX") == 0)
-    {
-        value = WORLD_VIEW_PROJECTION_MATRIX;
-    }
-    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_MATRIX") == 0)
-    {
-        value = INVERSE_TRANSPOSE_WORLD_MATRIX;
-    }
-    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX") == 0)
-    {
-        value = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX;
-    }
-    else if (strcmp(autoBinding, "CAMERA_WORLD_POSITION") == 0)
-    {
-        value = CAMERA_WORLD_POSITION;
-    }
-    else if (strcmp(autoBinding, "CAMERA_VIEW_POSITION") == 0)
-    {
-        value = CAMERA_VIEW_POSITION;
-    }
-    else if (strcmp(autoBinding, "MATRIX_PALETTE") == 0)
-    {
-        value = MATRIX_PALETTE;
+        // Remove an existing auto-binding
+        std::map<std::string, std::string>::iterator itr = _autoBindings.find(name);
+        if (itr != _autoBindings.end())
+            _autoBindings.erase(itr);
     }
     else
     {
-        // Ignore all other cases (the value was previously set to the default of NONE).
+        // Add/update an auto-binding
+        _autoBindings[name] = autoBinding;
     }
 
-    if (value != NONE)
+    // If we already have a node binding set, pass it to our handler now
+    if (_nodeBinding)
     {
-        setParameterAutoBinding(name, value);
+        applyAutoBinding(name, autoBinding);
     }
 }
 
@@ -184,90 +172,92 @@ RenderState::StateBlock* RenderState::getStateBlock() const
 
 void RenderState::setNodeBinding(Node* node)
 {
-    _nodeBinding = node;
-
-    if (_nodeBinding)
+    if (_nodeBinding != node)
     {
-        // Apply all existing auto-bindings using this node.
-        std::map<std::string, AutoBinding>::const_iterator itr = _autoBindings.begin();
-        while (itr != _autoBindings.end())
+        _nodeBinding = node;
+
+        if (_nodeBinding)
         {
-            applyAutoBinding(itr->first.c_str(), itr->second);
-            ++itr;
+            // Apply all existing auto-bindings using this node.
+            std::map<std::string, std::string>::const_iterator itr = _autoBindings.begin();
+            while (itr != _autoBindings.end())
+            {
+                applyAutoBinding(itr->first.c_str(), itr->second.c_str());
+                ++itr;
+            }
         }
     }
 }
 
-void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBinding)
+void RenderState::applyAutoBinding(const char* uniformName, const char* autoBinding)
 {
     MaterialParameter* param = getParameter(uniformName);
-    switch (autoBinding)
+    GP_ASSERT(param);
+
+    // First attempt to resolve the binding using custom registered resolvers.
+    if (_customAutoBindingResolvers.size() > 0)
     {
-    case WORLD_MATRIX:
-        GP_ASSERT(param);
-        param->bindValue(_nodeBinding, &Node::getWorldMatrix);
-        break;
+        for (size_t i = 0, count = _customAutoBindingResolvers.size(); i < count; ++i)
+        {
+            if (_customAutoBindingResolvers[i](autoBinding, _nodeBinding, param))
+                return; // handled by custom resolver
+        }
+    }
 
-    case VIEW_MATRIX:
-        GP_ASSERT(param);
+    // Perform built-in resolution
+    if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
+    {
+        param->bindValue(_nodeBinding, &Node::getWorldMatrix);
+    }
+    else if (strcmp(autoBinding, "VIEW_MATRIX") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getViewMatrix);
-        break;
-
-    case PROJECTION_MATRIX:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "PROJECTION_MATRIX") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getProjectionMatrix);
-        break;
-
-    case WORLD_VIEW_MATRIX:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "WORLD_VIEW_MATRIX") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getWorldViewMatrix);
-        break;
-
-    case VIEW_PROJECTION_MATRIX:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "VIEW_PROJECTION_MATRIX") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getViewProjectionMatrix);
-        break;
-
-    case WORLD_VIEW_PROJECTION_MATRIX:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "WORLD_VIEW_PROJECTION_MATRIX") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
-        break;
-
-    case INVERSE_TRANSPOSE_WORLD_MATRIX:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_MATRIX") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
-        break;
-
-    case INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
-        break;
-
-    case CAMERA_WORLD_POSITION:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "CAMERA_WORLD_POSITION") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
-        break;
-
-    case CAMERA_VIEW_POSITION:
-        GP_ASSERT(param);
+    }
+    else if (strcmp(autoBinding, "CAMERA_VIEW_POSITION") == 0)
+    {
         param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
-        break;
-
-    case MATRIX_PALETTE:
+    }
+    else if (strcmp(autoBinding, "MATRIX_PALETTE") == 0)
+    {
+        Model* model = _nodeBinding->getModel();
+        MeshSkin* skin = model ? model->getSkin() : NULL;
+        if (skin)
         {
-            Model* model = _nodeBinding->getModel();
-            MeshSkin* skin = model ? model->getSkin() : NULL;
-            if (skin)
-            {
-                GP_ASSERT(param);
-                param->bindValue(skin, &MeshSkin::getMatrixPalette, &MeshSkin::getMatrixPaletteSize);
-            }
+            GP_ASSERT(param);
+            param->bindValue(skin, &MeshSkin::getMatrixPalette, &MeshSkin::getMatrixPaletteSize);
         }
-        break;
-
-    default:
-        GP_ERROR("Unsupported auto binding type (%d).", autoBinding);
-        break;
+    }
+    else
+    {
+        GP_WARN("Unsupported auto binding type (%d).", autoBinding);
     }
 }
 
@@ -326,7 +316,7 @@ RenderState* RenderState::getTopmost(RenderState* below)
         }
         rs = rs->_parent;
     }
-
+    
     return NULL;
 }
 
@@ -334,9 +324,9 @@ void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context)
 {
     GP_ASSERT(renderState);
 
-    for (std::map<std::string, AutoBinding>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
+    for (std::map<std::string, std::string>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
     {
-        renderState->setParameterAutoBinding(it->first.c_str(), it->second);
+        renderState->setParameterAutoBinding(it->first.c_str(), it->second.c_str());
     }
     for (std::vector<MaterialParameter*>::const_iterator it = _parameters.begin(); it != _parameters.end(); ++it)
     {

+ 64 - 6
gameplay/src/RenderState.h

@@ -87,6 +87,27 @@ public:
         MATRIX_PALETTE
     };
 
+    /**
+     * Callback function prototype for resolving material parameter auto bindings.
+     *
+     * Functions matching this callback signature can be registered via the 
+     * RenderState::registerAutoBindingResolver method to extend or override the set
+     * of built-in material paramter auto bindings.
+     *
+     * @param autoBinding Name of the auto binding to resolve.
+     * @param node Node that is bound to the material of the specified parameter.
+     * @param parameter Material parameter to set the binding on.
+     *
+     * @return True ONLY if the implementations explicitly handles the auto binding, false otherwise.
+     *      Returning true here will prevent any further code (including built-in resolving code) from
+     *      handling the auto binding.
+     *
+     * @see RenderState::registerAutoBindingResolver(const char*, RenderState::AutoBindingResolver)
+     *
+     * @script{ignore}
+     */
+    typedef bool (*ResolveAutoBindingCallback) (const char* autoBinding, Node* node, MaterialParameter* parameter);
+
     /**
      * Defines blend constants supported by the blend function.
      */
@@ -286,6 +307,35 @@ public:
      */
     StateBlock* getStateBlock() const;
 
+    /**
+     * Registers a custom auto binding resolver.
+     *
+     * Implementing a custom auto binding reolver allows the set of built-in parameter auto
+     * bindings to be extended or overridden. Any parameter auto binding that is set on a
+     * material will be forwarded to any custom auto binding resolvers, in the order in which
+     * they are registered. If a registered resolver returns true (specifying that it handles
+     * the specified autoBinding), no further code will be exeucted for that autoBinding.
+     * This allows auto binding resolvers to not only implement new/custom binding strings,
+     * but it also lets them override existing/built-in ones. For this reason, you should
+     * ensure that you ONLY return true if you explicitly handle a custom auto binding; return
+     * false otherwise.
+     *
+     * Note that the custom resolver is called only once for a RenderState object when its
+     * node binding is initially set. This occurs when a material is initially bound to a
+     * Model that belongs to a Node. The resolver is NOT called each frame or each time
+     * the RenderState is bound. Therefore, when implementing custom auto bindings for values
+     * that change over time, the you should bind a method pointer onto the passed in
+     * MaterialParaemter using the MaterialParameter::bindValue mehtod. This way, the bound 
+     * method will be called each frame to set an updated value into the MaterialParameter.
+     *
+     * If no registered resolvers explicitly handle an auto binding, the binding will attempt
+     * to be resolved using the internal/built-in resolver, which is able to handle any
+     * auto bindings found in the RenderState::AutoBinding enumeration.
+     *
+     * @param callback Callback function for resolving parameter auto bindings.
+     */
+    static void registerAutoBindingResolver(ResolveAutoBindingCallback callback);
+
 protected:
 
     /**
@@ -320,9 +370,12 @@ protected:
     void setNodeBinding(Node* node);
 
     /**
-     * Applies the specified auto-binding.
+     * Applies the specified custom auto-binding.
+     *
+     * @param uniformName Name of the shader uniform.
+     * @param autoBinding Name of the auto binding.s
      */
-    void applyAutoBinding(const char* uniformName, AutoBinding binding);
+    void applyAutoBinding(const char* uniformName, const char* autoBinding);
 
     /**
      * Binds the render state for this RenderState and any of its parents, top-down, 
@@ -358,14 +411,14 @@ private:
 protected:
 
     /**
-     * Collection of MaterialParameter's to be applied to the gamplay::Effect.
+     * Collection of MaterialParameter's to be applied to the gameplay::Effect.
      */
     mutable std::vector<MaterialParameter*> _parameters;
-    
+
     /**
-     * Map of IDs to AutoBindings.
+     * Map of parameter names to auto binding strings.
      */
-    std::map<std::string, AutoBinding> _autoBindings;
+    std::map<std::string, std::string> _autoBindings;
 
     /**
      * The Node bound to the RenderState.
@@ -381,6 +434,11 @@ protected:
      * The RenderState's parent.
      */
     RenderState* _parent;
+
+    /**
+     * Map of custom auto binding resolverss.
+     */
+    static std::vector<ResolveAutoBindingCallback> _customAutoBindingResolvers;
 };
 
 }

+ 20 - 1
gameplay/src/RenderTarget.cpp

@@ -33,8 +33,17 @@ RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned
         return NULL;
     }
 
+    RenderTarget* rt = create(id, texture);
+    texture->release();
+
+    return rt;
+}
+
+RenderTarget* RenderTarget::create(const char* id, Texture* texture)
+{
     RenderTarget* renderTarget = new RenderTarget(id);
     renderTarget->_texture = texture;
+    renderTarget->_texture->addRef();
 
     __renderTargets.push_back(renderTarget);
 
@@ -64,10 +73,20 @@ const char* RenderTarget::getId() const
 {
     return _id.c_str();
 }
-     
+
 Texture* RenderTarget::getTexture() const
 {
     return _texture;
 }
 
+unsigned int RenderTarget::getWidth() const
+{
+    return _texture->getWidth();
+}
+
+unsigned int RenderTarget::getHeight() const
+{
+    return _texture->getHeight();
+}
+
 }

+ 30 - 0
gameplay/src/RenderTarget.h

@@ -13,6 +13,8 @@ namespace gameplay
  */
 class RenderTarget : public Ref
 {
+    friend class FrameBuffer;
+
 public:
  
     /**
@@ -29,6 +31,20 @@ public:
      */
     static RenderTarget* create(const char* id, unsigned int width, unsigned int height);
 
+    /**
+     * Create a RenderTarget from the given Texture and add it to the list of
+     * available RenderTargets.
+     *
+     * Note that different hardware and OpenGL versions have different capabilities
+     * and restrictions on what texture formats are supported as render targets.
+     *
+     * @param id The ID of the new RenderTarget.
+     *
+     * @return A newly created RenderTarget.
+     * @script{create}
+     */
+    static RenderTarget* create(const char* id, Texture* texture);
+
     /**
      * Get a named RenderTarget from its ID.
      *
@@ -51,6 +67,20 @@ public:
      * @return The backing texture of this RenderTarget.
      */
     Texture* getTexture() const;
+
+    /**
+     * Returns the width of the RenderTarget.
+     *
+     * @return The width.
+     */
+    unsigned int getWidth() const;
+
+    /**
+     * Returns the height of the RenderTarget.
+     *
+     * @return The height.
+     */
+    unsigned int getHeight() const;
  
 private:
 

+ 18 - 12
gameplay/src/Texture.cpp

@@ -43,8 +43,9 @@ namespace gameplay
 {
 
 static std::vector<Texture*> __textureCache;
+static TextureHandle __currentTextureId;
 
-Texture::Texture() : _handle(0), _format(RGBA), _width(0), _height(0), _mipmapped(false), _cached(false), _compressed(false)
+Texture::Texture() : _handle(0), _format(UNKNOWN), _width(0), _height(0), _mipmapped(false), _cached(false), _compressed(false)
 {
 }
 
@@ -162,7 +163,6 @@ Texture* Texture::create(Format format, unsigned int width, unsigned int height,
     GL_ASSERT( glPixelStorei(GL_UNPACK_ALIGNMENT, 1) );
     GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_BYTE, data) );
 
-
     // Set initial minification filter based on whether or not mipmaping was enabled.
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generateMipmaps ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR) );
 
@@ -174,9 +174,24 @@ Texture* Texture::create(Format format, unsigned int width, unsigned int height,
     if (generateMipmaps)
     {
         texture->generateMipmaps();
-        texture->_mipmapped = true;
     }
 
+    // Restore the texture id
+    GL_ASSERT( glBindTexture(GL_TEXTURE_2D, __currentTextureId) );
+
+    return texture;
+}
+
+Texture* Texture::create(TextureHandle handle, int width, int height, Format format)
+{
+    GP_ASSERT(handle);
+
+    Texture* texture = new Texture();
+    texture->_handle = handle;
+    texture->_format = format;
+    texture->_width = width;
+    texture->_height = height;
+
     return texture;
 }
 
@@ -760,33 +775,24 @@ TextureHandle Texture::getHandle() const
 
 void Texture::setWrapMode(Wrap wrapS, Wrap wrapT)
 {
-    GLint currentTextureId;
-    GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLenum)wrapS) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLenum)wrapT) );
-    GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
 }
 
 void Texture::setFilterMode(Filter minificationFilter, Filter magnificationFilter)
 {
-    GLint currentTextureId;
-    GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLenum)minificationFilter) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLenum)magnificationFilter) );
-    GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
 }
 
 void Texture::generateMipmaps()
 {
     if (!_mipmapped)
     {
-        GLint currentTextureId;
-        GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
         GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
         GL_ASSERT( glGenerateMipmap(GL_TEXTURE_2D) );
-        GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
 
         _mipmapped = true;
     }

+ 21 - 0
gameplay/src/Texture.h

@@ -22,6 +22,7 @@ public:
      */
     enum Format
     {
+        UNKNOWN = 0,
         RGB     = GL_RGB,
         RGBA    = GL_RGBA,
         ALPHA   = GL_ALPHA
@@ -177,6 +178,26 @@ public:
      */
     static Texture* create(Format format, unsigned int width, unsigned int height, unsigned char* data, bool generateMipmaps = false);
 
+    /**
+     * Creates a texture object to wrap the specified pre-created native texture handle.
+     *
+     * The specified TextureHandle must represent a valid texture that has been created
+     * on the underlying renderer and it should not be referenced by any other Texture
+     * object. When the returned Texture object is destroyed, the passed in TextureHandle
+     * will also be destroyed.
+     *
+     * @param handle Native texture handle.
+     * @param width The width of the texture represented by 'handle'.
+     * @param height The height of the texture represented by 'handle'.
+     * @param format Optionally, the format of the texture represented by 'handle'.
+     *      If the format cannot be represented by any of the Texture::Format values,
+     *      use a value of UNKNOWN.
+     *
+     * @return The new texture.
+     * @script{create}
+     */
+    static Texture* create(TextureHandle handle, int width, int height, Format format = UNKNOWN);
+
     /**
      * Gets the format of the texture.
      *