2
0
Эх сурвалжийг харах

Added FrameBuffer support.
Fixed shader compiler error for bumped.fsh.
Removed extra define for animation property on MaterialParameter.cpp
Added color support on Vector3 for rgb and Vector4 for rgba and on Properties.
Fixed bug in Curve for supporting multiple quaterion offets.

Sean Paul Taylor 14 жил өмнө
parent
commit
4e90a68a9e

+ 1 - 1
gameplay-resources/res/shaders/bumped.fsh

@@ -60,7 +60,7 @@ void applyLight()
     // Fetch normals from the normal map.
     vec3 normalVector = normalize(texture2D(u_normalMapTexture, v_texCoord).rgb * 2.0 - 1.0);
     vec3 spotLightDirection =normalize(v_spotLightDirection);
-    vec3 vertexToSpotLightDirection = normalize(v_spotLightDirectionCurrent);
+    vec3 vertexToSpotLightDirection = normalize(v_vertexToSpotLightDirection);
     
     // "-lightDirection" because light direction points in opposite direction to
     // to spot direction.

+ 2 - 0
gameplay/src/Base.h

@@ -155,6 +155,8 @@ typedef GLint VertexAttribute;
 typedef GLuint VertexBuffer;
 typedef GLuint IndexBuffer;
 typedef GLuint TextureHandle;
+typedef GLuint FrameBufferHandle;
+typedef GLuint RenderBufferHandle;
 }
 
 /**

+ 65 - 131
gameplay/src/Curve.cpp

@@ -225,38 +225,25 @@ void Curve::interpolateBezier(float s, Point* from, Point* to, float* dst) const
         unsigned int quaternionOffsetIndex = 0;
         unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
         unsigned int i = 0;
-        for (i = 0; i < quaternionOffset; i++)
-        {
-            dst[i] = from->value[i] * eq1 + from->outValue[i] * eq2 + to->inValue[i] * eq3 + to->value[i] * eq4;
-        }
-
-        // Handle quaternion component.
-        float interpTime = from->outValue[quaternionOffset] * eq2 + to->inValue[quaternionOffset] * eq3 + 1.0f * eq4;
-        interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
-        quaternionOffsetIndex++;
-
-        // Handles additional quaternion components.
-        while (quaternionOffsetIndex < _quaternionOffsetsCount)
-        {
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-            // Loop through values until you hit the next quaternion offset.
+        
+        do {
             while (i < quaternionOffset)
             {
-                // Interpolate as scalar.
                 dst[i] = from->value[i] * eq1 + from->outValue[i] * eq2 + to->inValue[i] * eq3 + to->value[i] * eq4;
                 i++;
             }
-            // We've hit a quaternion component, so handle it. increase the component counter by 4, and increase quaternionOffsetIndex
-            float interpTime = from->outValue[quaternionOffset] * eq2 + to->inValue[quaternionOffset] * eq3 + 1.0f * eq4;
-            interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
+            // Handle quaternion component.
+            float interpTime = from->time * eq1 + from->outValue[i] * eq2 + to->inValue[i] * eq3 + to->time * eq4;
+            interpolateQuaternion(s, (from->value + i), (to->value + i), (dst + i));
             i += 4;
             quaternionOffsetIndex++;
-        }
+            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
 
-        // Handle remaining scalar values.
-        for (i = quaternionOffset + 4; i < _componentCount; i++)
+        while (i < _componentCount)
         {
             dst[i] = from->value[i] * eq1 + from->outValue[i] * eq2 + to->inValue[i] * eq3 + to->value[i] * eq4;
+            i++;
         }
     }
 }
@@ -283,43 +270,32 @@ void Curve::interpolateBSpline(float s, Point* c0, Point* c1, Point* c2, Point*
         unsigned int quaternionOffsetIndex = 0;
         unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
         unsigned int i = 0;
-        for (i = 0; i < quaternionOffset; i++)
-        {
-            dst[i] = c0->value[i] * eq0 + c1->value[i] * eq1 + c2->value[i] * eq2 + c3->value[i] * eq3;
-        }
-
-        // Handle quaternion component.
-        float interpTime;
-        if (c0->time == c1->time)
-            interpTime = -c0->time * eq0 + c1->time * eq1 + c2->time * eq2 + c3->time * eq3;
-        else if (c2->time == c3->time)
-            interpTime = c0->time * eq0 + c1->time * eq1 + c2->time * eq2  - c3->time * eq3;
-        else
-            interpTime = c0->time * eq0 + c1->time * eq1 + c2->time * eq2 + c3->time * eq3;
-        interpolateQuaternion(s, (c1->value + quaternionOffset) , (c2->value + quaternionOffset), (dst + quaternionOffset));
-        quaternionOffsetIndex++;
-
-        // Handles additional quaternion components.
-        while (quaternionOffsetIndex < _quaternionOffsetsCount)
-        {
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-            // Loop through values until you hit the next quaternion offset.
+        do {
             while (i < quaternionOffset)
             {
-                // Interpolate as scalar.
-                dst[i] = dst[i] = c0->value[i] * eq0 + c1->value[i] * eq1 + c2->value[i] * eq2 + c3->value[i] * eq3;
+                dst[i] = c0->value[i] * eq0 + c1->value[i] * eq1 + c2->value[i] * eq2 + c3->value[i] * eq3;
                 i++;
             }
-            // We've hit a quaternion component, so handle it. increase the component counter by 4, and increase quaternionOffsetIndex
+            // Handle quaternion component.
+            float interpTime;
+            if (c0->time == c1->time)
+                interpTime = -c0->time * eq0 + c1->time * eq1 + c2->time * eq2 + c3->time * eq3;
+            else if (c2->time == c3->time)
+                interpTime = c0->time * eq0 + c1->time * eq1 + c2->time * eq2  - c3->time * eq3;
+            else
+                interpTime = c0->time * eq0 + c1->time * eq1 + c2->time * eq2 + c3->time * eq3;
+            
             interpolateQuaternion(s, (c1->value + quaternionOffset) , (c2->value + quaternionOffset), (dst + quaternionOffset));
             i += 4;
             quaternionOffsetIndex++;
-        }
-
+            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
+        
         // Handle remaining scalar values.
-        for (i = quaternionOffset + 4; i < _componentCount; i++)
+        while (i < _componentCount)
         {
             dst[i] = c0->value[i] * eq0 + c1->value[i] * eq1 + c2->value[i] * eq2 + c3->value[i] * eq3;
+            i++;
         }
     }
 }
@@ -347,38 +323,27 @@ void Curve::interpolateHermite(float s, Point* from, Point* to, float* dst) cons
         unsigned int quaternionOffsetIndex = 0;
         unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
         unsigned int i = 0;
-        for (i = 0; i < quaternionOffset; i++)
-        {
-            dst[i] = h00 * from->value[i] + h01 * to->value[i] + h10 * from->outValue[i] + h11 * to->inValue[i];
-        }
-
-        // Handle quaternion component.
-        float interpTime = h01 * 1.0f + h10 * from->outValue[quaternionOffset] + h11 * to->inValue[quaternionOffset];
-        interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
-        quaternionOffsetIndex++;
 
-        // Handles additional quaternion components.
-        while (quaternionOffsetIndex < _quaternionOffsetsCount)
-        {
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-            // Loop through values until you hit the next quaternion offset.
+        do {
             while (i < quaternionOffset)
             {
-                // Interpolate as scalar.
                 dst[i] = h00 * from->value[i] + h01 * to->value[i] + h10 * from->outValue[i] + h11 * to->inValue[i];
                 i++;
             }
-            // We've hit a quaternion component, so handle it. increase the component counter by 4, and increase quaternionOffsetIndex
+            // Handle quaternion component.
             float interpTime = h01 * 1.0f + h10 * from->outValue[quaternionOffset] + h11 * to->inValue[quaternionOffset];
             interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
             i += 4;
             quaternionOffsetIndex++;
-        }
-
+            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+            
+        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
+       
         // Handle remaining scalar values.
-        for (i = quaternionOffset + 4; i < _componentCount; i++)
+        while (i < _componentCount)
         {
             dst[i] = h00 * from->value[i] + h01 * to->value[i] + h10 * from->outValue[i] + h11 * to->inValue[i];
+            i++;
         }
     }
 }
@@ -404,37 +369,25 @@ void Curve::interpolateHermiteFlat(float s, Point* from, Point* to, float* dst)
         unsigned int quaternionOffsetIndex = 0;
         unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
         unsigned int i = 0;
-        for (i = 0; i < quaternionOffset; i++)
-        {
-            dst[i] = h00 * from->value[i] + h01 * to->value[i];
-        }
-
-        // Handle quaternion component.
-        float interpTime = h01 * 1.0f; // Can drop all other terms because they will compute to 0.
-        interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
-        quaternionOffsetIndex++;
-
-        // Handles additional quaternion components.
-        while (quaternionOffsetIndex < _quaternionOffsetsCount)
-        {
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-            // Loop through values until you hit the next quaternion offset.
+        float interpTime = h01 * 1.0f; // Can drop all other terms because they will compute to 0. Only need to compute once.
+        do {
             while (i < quaternionOffset)
             {
-                // Interpolate as scalar.
-                dst[i] = dst[i] = h00 * from->value[i] + h01 * to->value[i];
+                dst[i] = h00 * from->value[i] + h01 * to->value[i];
                 i++;
             }
             // We've hit a quaternion component, so handle it. increase the component counter by 4, and increase quaternionOffsetIndex
             interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
             i += 4;
             quaternionOffsetIndex++;
-        }
+            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
 
         // Handle remaining scalar values.
-        for (i = quaternionOffset + 4; i < _componentCount; i++)
+        while (i < _componentCount)
         {
             dst[i] = h00 * from->value[i] + h01 * to->value[i];
+            i++;
         }
     }
 }
@@ -479,16 +432,8 @@ void Curve::interpolateHermiteSmooth(float s, unsigned int index, Point* from, P
     }
     else
     {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        unsigned int i = 0;
-        for (i = 0; i < quaternionOffset; i++)
-        {
-            dst[i] = h00 * from->value[i] + h01 * to->value[i];
-        }
-
-        // Handle quaternion component.
+        // Calculates in/out values for interpolating the time for the quaternion component.
+        // Only need to calculate this once.
         if (index == 0)
         {
             outValue = to->time - from->time;
@@ -506,16 +451,13 @@ void Curve::interpolateHermiteSmooth(float s, unsigned int index, Point* from, P
         {
             inValue = ((to + 1)->time - from->time) * ((to->time - from->time) / ((to + 1)->time - from->time));
         }
+        // Interpolate values as scalars up to first quaternion offset.
+        unsigned int quaternionOffsetIndex = 0;
+        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        unsigned int i = 0;
 
-        float interpTime = h01 * 1.0f + h10 * outValue + h11 * inValue;
-        interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
-        quaternionOffsetIndex++;
-
-        // Handles additional quaternion components.
-        while (quaternionOffsetIndex < _quaternionOffsetsCount)
-        {
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-            // Loop through values until you hit the next quaternion offset.
+        do {
+            // Handle scalar values up to the quaternion offset.
             while (i < quaternionOffset)
             {
                 // Interpolate as scalar.
@@ -541,13 +483,16 @@ void Curve::interpolateHermiteSmooth(float s, unsigned int index, Point* from, P
                 i++;
             }
             
+            float interpTime = h01 * 1.0f + h10 * outValue + h11 * inValue;
             interpolateQuaternion(interpTime, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
-            i += 4;
+            i+=4;
             quaternionOffsetIndex++;
-        }
+            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
+        
 
         // Handle remaining scalar values.
-        for (i = quaternionOffset + 4; i < _componentCount; i++)
+        while (i < _componentCount)
         {
             // Interpolate as scalar.
             if (index == 0)
@@ -569,6 +514,7 @@ void Curve::interpolateHermiteSmooth(float s, unsigned int index, Point* from, P
             }
 
             dst[i] = h00 * from->value[i] + h01 * to->value[i] + h10 * outValue + h11 * inValue;
+            i++;
         }
     }
 }
@@ -588,37 +534,25 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
         unsigned int quaternionOffsetIndex = 0;
         unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
         unsigned int i = 0;
-        for (i = 0; i < quaternionOffset; i++)
-        {
-            dst[i] = from->value[i] + (to->value[i] - from->value[i]) * s; 
-        }
-
-        // Handle quaternion component.
-        interpolateQuaternion(s, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
-        
-        quaternionOffsetIndex++;
-
-        // Handles additional quaternion components.
-        while (quaternionOffsetIndex < _quaternionOffsetsCount)
-        {
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        do {
             // Loop through values until you hit the next quaternion offset.
             while (i < quaternionOffset)
             {
-                // Interpolate as scalar.
-                dst[i] = from->value[i] + (to->value[i] - from->value[i]) * s;
+                dst[i] = from->value[i] + (to->value[i] - from->value[i]) * s; 
                 i++;
             }
-            // We've hit a quaternion component, so handle it. increase the component counter by 4, and increase quaternionOffsetIndex
-            interpolateQuaternion(s, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));
+            // Handle quaternion component.
+            interpolateQuaternion(s, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));        
             i += 4;
             quaternionOffsetIndex++;
-        }
+            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
 
-        // Handle remaining scalar values.
-        for (i = quaternionOffset + 4; i < _componentCount; i++)
+        // Loop through the last remaining values, if any.
+        while (i < _componentCount)
         {
-            dst[i] = from->value[i] + (to->value[i] - from->value[i]) * s;
+            dst[i] = from->value[i] + (to->value[i] - from->value[i]) * s; 
+            i++;
         }
     }
 }

+ 60 - 0
gameplay/src/DepthStencilTarget.cpp

@@ -0,0 +1,60 @@
+#include "DepthStencilTarget.h"
+
+namespace gameplay
+{
+
+static std::vector<DepthStencilTarget*> __depthStencilTargets;
+
+DepthStencilTarget* DepthStencilTarget::create(const char* id, unsigned int width, unsigned int height)
+{
+    DepthStencilTarget* depthStencilTarget = new DepthStencilTarget(id, width, height);
+
+    __depthStencilTargets.push_back(depthStencilTarget);
+    depthStencilTarget->_index = __depthStencilTargets.size() - 1;
+
+    return depthStencilTarget;
+}
+
+DepthStencilTarget* DepthStencilTarget::getDepthStencilTarget(const char* id)
+{
+    // Search the vector for a matching ID.
+    std::vector<DepthStencilTarget*>::const_iterator it;
+    for (it = __depthStencilTargets.begin(); it < __depthStencilTargets.end(); ++it)
+    {
+        DepthStencilTarget* dst = *it;
+        if (strcmp(id, dst->getID()) == 0)
+        {
+            return dst;
+        }
+    }
+
+    return NULL;
+}
+
+DepthStencilTarget::~DepthStencilTarget()
+{
+    // Destroy GL resources.
+    glDeleteTextures(1, &_handle);
+
+    // Remove from vector.
+    std::vector<DepthStencilTarget*>::const_iterator it = __depthStencilTargets.begin() + _index;
+    __depthStencilTargets.erase(it);
+}
+
+const char* DepthStencilTarget::getID() const
+{
+    return _id.c_str();
+}
+
+DepthStencilTarget::DepthStencilTarget(const char* id, unsigned int width, unsigned int height)
+{
+    // Create a backing texture / renderbuffer.
+    // Need to experiment to determine whether GL_DEPTH_STENCIL textures are supported.
+    // If not, need to use dual renderbuffers instead.
+    glGenTextures(1, &_handle);
+    glBindTexture(GL_TEXTURE_2D, _handle);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
+}
+
+}

+ 55 - 0
gameplay/src/DepthStencilTarget.h

@@ -0,0 +1,55 @@
+#ifndef DEPTHSTENCILTARGET_H_
+#define DEPTHSTENCILTARGET_H_
+
+#include "Base.h"
+#include "Ref.h"
+
+namespace gameplay
+{
+
+class DepthStencilTarget : public Ref
+{
+public:
+ 
+    /**
+     * Create a DepthStencilTarget and add it to the list of available DepthStencilTargets.
+     *
+     * @param id The ID of the new DepthStencilTarget.  Uniqueness is recommended but not enforced.
+     *
+     * @return A newly created DepthStencilTarget.
+     */
+    static DepthStencilTarget* create(const char* id, unsigned int width, unsigned int height);
+
+    /**
+     * Get a named DepthStencilTarget from its ID.
+     *
+     * @param id The ID of the DepthStencilTarget to search for.
+     *
+     * @return The DepthStencilTarget with the specified ID, or NULL if one was not found.
+     */
+    static DepthStencilTarget* getDepthStencilTarget(const char* id);
+
+    /**
+     * Destructor.  Removes this DepthStencilTarget from the list of available DepthStencilTargets.
+     */
+    ~DepthStencilTarget();
+
+    /**
+     * Get the ID of this DepthStencilTarget.
+     *
+     * @return The ID of this DepthStencilTarget.
+     */
+    const char* getID() const;
+ 
+private:
+ 
+    DepthStencilTarget(const char* id, unsigned int width, unsigned int height);
+
+    std::string _id;
+    RenderBufferHandle _handle;
+    unsigned int _index;
+};
+
+}
+
+#endif

+ 241 - 0
gameplay/src/FrameBuffer.cpp

@@ -0,0 +1,241 @@
+#include "Base.h"
+#include "FrameBuffer.h"
+#include "RenderTarget.h"
+#include "DepthStencilTarget.h"
+#include "Texture.h"
+
+namespace gameplay
+{
+
+static unsigned int __maxRenderTargets = 0;
+static std::vector<FrameBuffer*> __frameBuffers;
+
+FrameBuffer* FrameBuffer::create(const char* id)
+{
+    FrameBuffer* frameBuffer = new FrameBuffer(id);
+
+    __frameBuffers.push_back(frameBuffer);
+    frameBuffer->_index = __frameBuffers.size() - 1;
+
+    return frameBuffer;
+}
+
+FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned int height)
+{
+    FrameBuffer* frameBuffer = new FrameBuffer(id);
+
+    // Create RenderTarget with same ID and set as first color attachment.
+    RenderTarget* renderTarget = RenderTarget::create(id, width, height);
+    frameBuffer->setRenderTarget(renderTarget);
+    SAFE_RELEASE(renderTarget);
+
+    __frameBuffers.push_back(frameBuffer);
+    frameBuffer->_index = __frameBuffers.size() - 1;
+
+    return frameBuffer;
+}
+
+FrameBuffer* FrameBuffer::getFrameBuffer(const char* id)
+{
+    // Search the vector for a matching ID.
+    std::vector<FrameBuffer*>::const_iterator it;
+    for (it = __frameBuffers.begin(); it < __frameBuffers.end(); ++it)
+    {
+        FrameBuffer* fb = *it;
+        if (strcmp(id, fb->getID()) == 0)
+        {
+            return fb;
+        }
+    }
+
+    return NULL;
+}
+
+FrameBuffer::~FrameBuffer()
+{
+    if (__renderTargets)
+    {
+        for (unsigned int i = 0; i < __maxRenderTargets; ++i)
+        {
+            SAFE_RELEASE(__renderTargets[i]);
+        }
+        SAFE_DELETE_ARRAY(__renderTargets);
+    }
+
+    // Release GL resource.
+    glDeleteFramebuffers(1, &_handle);
+
+    // Remove self from vector.
+    std::vector<FrameBuffer*>::const_iterator it = __frameBuffers.begin() + _index;
+    __frameBuffers.erase(it);
+}
+
+const char* FrameBuffer::getID() const
+{
+    return _id.c_str();
+}
+
+unsigned int FrameBuffer::getMaxRenderTargets()
+{
+    if (__maxRenderTargets == 0)
+    {
+        GLint val;
+        glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &val);
+        __maxRenderTargets = (unsigned int) val;
+    }
+
+    return __maxRenderTargets;
+}
+
+void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
+{
+    assert(index < __maxRenderTargets);
+
+    if (__renderTargets[index] == target)
+    {
+        return;
+    }
+
+    // Release our reference to the current RenderTarget at this index.
+    SAFE_RELEASE(__renderTargets[index]);
+
+    __renderTargets[index] = target;
+        
+    // This FrameBuffer now references the RenderTarget.
+    target->addRef();
+
+    // Now set this target as the color attachment corresponding to index.
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _handle);
+    GLenum attachment;
+    switch (index)
+    {
+        case 0:
+            attachment = GL_COLOR_ATTACHMENT0;
+            break;
+        case 1:
+            attachment = GL_COLOR_ATTACHMENT1;
+            break;
+        case 2:
+            attachment = GL_COLOR_ATTACHMENT2;
+            break;
+        case 3:
+            attachment = GL_COLOR_ATTACHMENT3;
+            break;
+        case 4:
+            attachment = GL_COLOR_ATTACHMENT4;
+            break;
+        case 5:
+            attachment = GL_COLOR_ATTACHMENT5;
+            break;
+        case 6:
+            attachment = GL_COLOR_ATTACHMENT6;
+            break;
+        case 7:
+            attachment = GL_COLOR_ATTACHMENT7;
+            break;
+        case 8:
+            attachment = GL_COLOR_ATTACHMENT8;
+            break;
+        case 9:
+            attachment = GL_COLOR_ATTACHMENT9;
+            break;
+        case 10:
+            attachment = GL_COLOR_ATTACHMENT10;
+            break;
+        case 11:
+            attachment = GL_COLOR_ATTACHMENT11;
+            break;
+        case 12:
+            attachment = GL_COLOR_ATTACHMENT12;
+            break;
+        case 13:
+            attachment = GL_COLOR_ATTACHMENT13;
+            break;
+        case 14:
+            attachment = GL_COLOR_ATTACHMENT14;
+            break;
+        case 15:
+            attachment = GL_COLOR_ATTACHMENT15;
+            break;
+    }
+    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, __renderTargets[index]->getTexture()->getHandle(), 0);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+}
+
+RenderTarget* FrameBuffer::getRenderTarget(unsigned int index) const
+{
+    if (index < __maxRenderTargets)
+    {
+        return __renderTargets[index];
+    }
+
+    return NULL;
+}
+
+void FrameBuffer::setDepthStencilTarget(DepthStencilTarget* target)
+{
+    SAFE_RELEASE(_depthStencilTarget);
+    _depthStencilTarget = target;
+    target->addRef();
+}
+
+DepthStencilTarget* FrameBuffer::getDepthStencilTarget() const
+{
+    return _depthStencilTarget;
+}
+
+void FrameBuffer::bind()
+{
+    // Check framebuffer completeness.
+    GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+    switch(status)
+    {
+        case GL_FRAMEBUFFER_COMPLETE:
+            // Success.
+            break;
+
+        case GL_FRAMEBUFFER_UNSUPPORTED:
+            // Configuration error.
+            LOG_ERROR("FrameBuffer unsupported.");
+            return;
+
+        default:
+            // Programming error; will fail on all hardware.
+            LOG_ERROR("Unknown error checking FrameBuffer status.");
+            return;
+    }
+
+    // Bind this FrameBuffer for rendering.
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _handle);
+}
+
+void FrameBuffer::bindDefault()
+{
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+}
+
+FrameBuffer::FrameBuffer(const char* id) :
+    _handle(0), _depthStencilTarget(NULL), _index(0)
+{
+    if (id)
+    {
+        _id = id;
+    }
+
+    // Create GL FBO resource.
+    glGenFramebuffers(1, &_handle);
+
+    // Query MAX_COLOR_ATTACHMENTS the first time a FrameBuffer is created.
+    if (__maxRenderTargets == 0)
+    {
+        GLint val;
+        glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &val);
+        __maxRenderTargets = (unsigned int) val;
+    }
+
+    __renderTargets = new RenderTarget*[__maxRenderTargets];
+    memset(__renderTargets, 0, sizeof(RenderTarget*) * __maxRenderTargets);
+}
+
+}

+ 120 - 0
gameplay/src/FrameBuffer.h

@@ -0,0 +1,120 @@
+#ifndef FRAMEBUFFER_H_
+#define FRAMEBUFFER_H_
+
+#include "Base.h"
+#include "Ref.h"
+
+namespace gameplay
+{
+
+class RenderTarget;
+class DepthStencilTarget;
+
+class FrameBuffer : public Ref
+{
+public:
+
+    /**
+     * Create an empty FrameBuffer and add it 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.
+     */
+    static FrameBuffer* create(const char* id);
+
+    /**
+     * Create an empty FrameBuffer with a RenderTarget of the specified width and height,
+     * and add the FrameBuffer to the list of available FrameBuffers.
+     *
+     * @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.
+     *
+     * @return A newly created FrameBuffer.
+     */
+    static FrameBuffer* create(const char* id, unsigned int width, unsigned int height);
+
+    /**
+     * Get a named FrameBuffer from its ID.
+     *
+     * @param id The ID of the FrameBuffer to search for.
+     *
+     * @return The FrameBuffer with the specified ID, or NULL if one was not found.
+     */
+    static FrameBuffer* getFrameBuffer(const char* id);
+     
+    /**
+     * Destructor.  Removes this FrameBuffer from the list of available FrameBuffers.
+     */
+    ~FrameBuffer();
+
+    /**
+     * Get the ID of this FrameBuffer.
+     *
+     * @return The ID of this FrameBuffer.
+     */
+    const char* getID() const;
+
+    /**
+     * Get the number of color attachments available on the current hardware.
+     *
+     * @return The number of color attachments available on the current hardware.
+     */
+    static unsigned int getMaxRenderTargets();
+ 
+    /**
+     * Set a RenderTarget on this FrameBuffer's color attachment at the specified index.
+     *
+     * @param target The RenderTarget to set.
+     * @param index The index of the color attachment to set.
+     */
+    void setRenderTarget(RenderTarget* target, unsigned int index = 0);
+ 
+    /**
+     * Get the RenderTarget attached to the FrameBuffer's color attachment at the specified index.
+     *
+     * @param index The index of the color attachment to retrieve a RenderTarget from.
+     *
+     * @return The RenderTarget attached at the specified index.
+     */
+    RenderTarget* getRenderTarget(unsigned int index = 0) const;
+ 
+    /**
+     * Set this FrameBuffer's DepthStencilTarget.
+     *
+     * @param target The DepthStencilTarget to set on this FrameBuffer.
+     */
+    void setDepthStencilTarget(DepthStencilTarget* target);
+  
+    /**
+     * Get this FrameBuffer's DepthStencilTarget.
+     *
+     * @return This FrameBuffer's DepthStencilTarget.
+     */
+    DepthStencilTarget* getDepthStencilTarget() const;
+ 
+    /**
+     * Binds this FrameBuffer for off-screen rendering.
+     */
+    void bind();
+
+    /**
+     * Binds the default FrameBuffer for rendering to the display.
+     */
+    static void bindDefault(); 
+     
+private:
+ 
+    FrameBuffer(const char* id);
+
+    std::string _id;
+    FrameBufferHandle _handle;
+    RenderTarget** __renderTargets;
+    DepthStencilTarget* _depthStencilTarget;
+    unsigned int _index;
+};
+
+}
+
+#endif

+ 17 - 11
gameplay/src/MaterialParameter.cpp

@@ -294,16 +294,22 @@ void MaterialParameter::bind(Effect* effect)
 
 unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyId) const
 {
-    switch (MATERIALPARAMETER_ANIMATE_UNIFORM)
+    switch (propertyId)
     {
-        // These types don't support animation.
-        case NONE:
-        case MATRIX:
-        case SAMPLER:
-        case METHOD:
-            return 0;
-        default:
-            return _count;
+        case ANIMATE_UNIFORM:
+        {
+            switch(_type)
+            {
+                // These types don't support animation.
+                case NONE:
+                case MATRIX:
+                case SAMPLER:
+                case METHOD:
+                    return 0;
+                default:
+                    return _count;
+            }
+        }
     }
 
     return 0;
@@ -313,7 +319,7 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
 {
     switch (propertyId)
     {
-        case MATERIALPARAMETER_ANIMATE_UNIFORM:
+        case ANIMATE_UNIFORM:
         {
             switch (_type)
             {
@@ -342,7 +348,7 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
 {
     switch (propertyId)
     {
-        case MATERIALPARAMETER_ANIMATE_UNIFORM:
+        case ANIMATE_UNIFORM:
         {
             switch (_type)
             {

+ 0 - 2
gameplay/src/MaterialParameter.h

@@ -13,8 +13,6 @@
 #include "Texture.h"
 #include "Effect.h"
 
-#define MATERIALPARAMETER_ANIMATE_UNIFORM           1
-
 namespace gameplay
 {
 

+ 57 - 0
gameplay/src/Properties.cpp

@@ -5,6 +5,7 @@
 #include "Properties.h"
 #include "FileSystem.h"
 #include <xtree>
+#include <sstream>
 
 namespace gameplay
 {
@@ -581,4 +582,60 @@ bool Properties::getVector4(const char* name, Vector4* out) const
     return false;
 }
 
+bool Properties::getColor(const char* name, Vector3* out) const
+{
+    assert(out);
+
+    const char* valueString = getString(name);
+    if (valueString)
+    {
+        if (strlen(valueString) != 7 ||
+            valueString[0] != '#')
+        {
+            // Not a color string.
+            LOG_ERROR_VARG("Error parsing property: %s", name);
+            out->set(0.0f, 0.0f, 0.0f);
+            return false;
+        }
+
+        // Read the string into an int as hex.
+        unsigned int color;
+        sscanf_s(valueString+1, "%x", &color);
+
+        out->set(Vector3::fromColor(color));
+        return true;
+    }
+
+    out->set(0.0f, 0.0f, 0.0f);
+    return false;
+}
+
+bool Properties::getColor(const char* name, Vector4* out) const
+{
+    assert(out);
+
+    const char* valueString = getString(name);
+    if (valueString)
+    {
+        if (strlen(valueString) != 9 ||
+            valueString[0] != '#')
+        {
+            // Not a color string.
+            LOG_ERROR_VARG("Error parsing property: %s", name);
+            out->set(0.0f, 0.0f, 0.0f, 0.0f);
+            return false;
+        }
+
+        // Read the string into an int as hex.
+        unsigned int color;
+        sscanf_s(valueString+1, "%x", &color);
+
+        out->set(Vector4::fromColor(color));
+        return true;
+    }
+
+    out->set(0.0f, 0.0f, 0.0f, 0.0f);
+    return false;
+}
+
 }

+ 31 - 3
gameplay/src/Properties.h

@@ -291,7 +291,7 @@ public:
      * to Vector2(0.0f, 0.0f).
      *
      * @param name The name of the property to interpret, or NULL to return the current property's value.
-     * @param out The matrix to set to this property's interpreted value.
+     * @param out The vector to set to this property's interpreted value.
      * 
      * @return True on success, false if the property does not exist or could not be scanned.
      */
@@ -304,7 +304,7 @@ public:
      * to Vector3(0.0f, 0.0f, 0.0f).
      *
      * @param name The name of the property to interpret, or NULL to return the current property's value.
-     * @param out The matrix to set to this property's interpreted value.
+     * @param out The vector to set to this property's interpreted value.
      * 
      * @return True on success, false if the property does not exist or could not be scanned.
      */
@@ -317,12 +317,40 @@ public:
      * to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
      *
      * @param name The name of the property to interpret, or NULL to return the current property's value.
-     * @param out The matrix to set to this property's interpreted value.
+     * @param out The vector to set to this property's interpreted value.
      * 
      * @return True on success, false if the property does not exist or could not be scanned.
      */
     bool getVector4(const char* name, Vector4* out) const;
 
+    /**
+     * Interpret the value of the given property as an RGB color in hex and write this color to a Vector3.
+     * E.g. 0xff0000 represents red and sets the vector to (1, 0, 0).
+     * If the property does not exist, out will be set to Vector3(0.0f, 0.0f, 0.0f).
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to Vector3(0.0f, 0.0f, 0.0f).
+     *
+     * @param name The name of the property to interpret, or NULL to return the current property's value.
+     * @param out The vector to set to this property's interpreted value.
+     * 
+     * @return True on success, false if the property does not exist or could not be scanned.
+     */
+    bool getColor(const char* name, Vector3* out) const;
+
+    /**
+     * Interpret the value of the given property as an RGBA color in hex and write this color to a Vector4.
+     * E.g. 0xff0000ff represents opaque red and sets the vector to (1, 0, 0, 1).
+     * If the property does not exist, out will be set to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
+     *
+     * @param name The name of the property to interpret, or NULL to return the current property's value.
+     * @param out The vector to set to this property's interpreted value.
+     * 
+     * @return True on success, false if the property does not exist or could not be scanned.
+     */
+    bool getColor(const char* name, Vector4* out) const;
+
 
 private:
     

+ 65 - 0
gameplay/src/RenderTarget.cpp

@@ -0,0 +1,65 @@
+#include "Base.h"
+#include "RenderTarget.h"
+#include "Texture.h"
+
+namespace gameplay
+{
+
+static std::vector<RenderTarget*> __renderTargets;
+
+RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned int height)
+{
+    RenderTarget* renderTarget = new RenderTarget(id, width, height);
+
+    __renderTargets.push_back(renderTarget);
+    renderTarget->_index = __renderTargets.size() - 1;
+
+    return renderTarget;
+}
+
+RenderTarget* RenderTarget::getRenderTarget(const char* id)
+{
+    // Search the vector for a matching ID.
+    std::vector<RenderTarget*>::const_iterator it;
+    for (it = __renderTargets.begin(); it < __renderTargets.end(); ++it)
+    {
+        RenderTarget* rt = *it;
+        if (strcmp(id, rt->getID()) == 0)
+        {
+            return rt;
+        }
+    }
+
+    return NULL;
+}
+ 
+RenderTarget::~RenderTarget()
+{
+    SAFE_RELEASE(_texture);
+
+    // Erase this RenderTarget from the vector.
+    std::vector<RenderTarget*>::const_iterator it = __renderTargets.begin() + _index;
+    __renderTargets.erase(it);
+}
+
+const char* RenderTarget::getID() const
+{
+    return _id.c_str();
+}
+     
+Texture* RenderTarget::getTexture() const
+{
+    return _texture;
+}
+ 
+RenderTarget::RenderTarget(const char* id, unsigned int width, unsigned int height)
+{
+    if (id)
+    {
+        _id = id;
+    }
+
+    _texture = Texture::create(Texture::RGBA8888, width, height, NULL, true);
+}
+
+}

+ 67 - 0
gameplay/src/RenderTarget.h

@@ -0,0 +1,67 @@
+#ifndef RENDERTARGET_H_
+#define RENDERTARGET_H_
+
+#include "Base.h"
+#include "Ref.h"
+
+namespace gameplay
+{
+
+class Texture;
+
+class RenderTarget : public Ref
+{
+public:
+ 
+    /**
+     * Create a RenderTarget and add it to the list of available RenderTargets.
+     *
+     * @param id The ID of the new RenderTarget.  Uniqueness is recommended but not enforced.
+     * @param width The width of the new RenderTarget.
+     * @param height The height of the new RenderTarget.
+     *
+     * @return A newly created RenderTarget.
+     */
+    static RenderTarget* create(const char* id, unsigned int width, unsigned int height);
+
+    /**
+     * Get a named RenderTarget from its ID.
+     *
+     * @param id The ID of the RenderTarget to search for.
+     *
+     * @return The RenderTarget with the specified ID, or NULL if one was not found.
+     */
+    static RenderTarget* getRenderTarget(const char* id);
+ 
+    /**
+     * Destructor.  Removes this RenderTarget from the list of available RenderTargets.
+     */
+    ~RenderTarget();
+
+    /**
+     * Get the ID of this RenderTarget.
+     *
+     * @return The ID of this RenderTarget.
+     */
+    const char* getID() const;
+
+    /**
+     * Get the backing texture of this RenderTarget.
+     *
+     * @return The backing texture of this RenderTarget.
+     */
+    Texture* getTexture() const;
+ 
+private:
+ 
+    RenderTarget(const char* id, unsigned int width, unsigned int height);
+    RenderTarget();
+
+    std::string _id;
+    Texture* _texture;
+    unsigned int _index; // Index into vector.
+};
+
+}
+
+#endif

+ 15 - 0
gameplay/src/Vector3.cpp

@@ -33,6 +33,21 @@ Vector3::Vector3(const Vector3& copy)
     set(copy);
 }
 
+Vector3 Vector3::fromColor(unsigned int color)
+{
+    float components[3];
+    int componentIndex = 0;
+    for (int i = 2; i >= 0; --i)
+    {
+        int component = (color >> i*8) & 0x0000ff;
+
+        components[componentIndex++] = static_cast<float>(component) / 255.0f;
+    }
+
+    Vector3 value(components);
+    return value;
+}
+
 Vector3::~Vector3()
 {
 }

+ 10 - 0
gameplay/src/Vector3.h

@@ -75,6 +75,16 @@ public:
      */
     Vector3(const Vector3& copy);
 
+    /**
+     * Creates a new vector from an integer interpreted as an RGB value.
+     * E.g. 0xff0000 represents red or the vector (1, 0, 0).
+     *
+     * @param color The integer to interpret as an RGB value.
+     *
+     * @return A vector corresponding to the interpreted RGB color.
+     */
+    static Vector3 fromColor(unsigned int color);
+
     /**
      * Destructor.
      */

+ 15 - 0
gameplay/src/Vector4.cpp

@@ -33,6 +33,21 @@ Vector4::Vector4(const Vector4& copy)
     set(copy);
 }
 
+Vector4 Vector4::fromColor(unsigned int color)
+{
+    float components[4];
+    int componentIndex = 0;
+    for (int i = 3; i >= 0; --i)
+    {
+        int component = (color >> i*8) & 0x000000ff;
+
+        components[componentIndex++] = static_cast<float>(component) / 255.0f;
+    }
+
+    Vector4 value(components);
+    return value;
+}
+
 Vector4::~Vector4()
 {
 }

+ 10 - 0
gameplay/src/Vector4.h

@@ -76,6 +76,16 @@ public:
      */
     Vector4(const Vector4& copy);
 
+    /**
+     * Creates a new vector from an integer interpreted as an RGBA value.
+     * E.g. 0xff0000ff represents opaque red or the vector (1, 0, 0, 1).
+     *
+     * @param color The integer to interpret as an RGBA value.
+     *
+     * @return A vector corresponding to the interpreted RGBA color.
+     */
+    static Vector4 fromColor(unsigned int color);
+
     /**
      * Destructor.
      */

+ 3 - 0
gameplay/src/gameplay.h

@@ -45,6 +45,9 @@
 #include "Font.h"
 #include "SpriteBatch.h"
 #include "ParticleEmitter.h"
+#include "FrameBuffer.h"
+#include "RenderTarget.h"
+#include "DepthStencilTarget.h"
 
 // Audio
 #include "AudioController.h"