Bläddra i källkod

Added MeshBatch class to allow batching of any primitive mesh data.
Refactored SpriteBatch to use MeshBatch internally.
Updated VertexFormat class to no longer inherit Ref and to no longer be cached.
Implemented support for client arrays in VertexAttributeBinding.
Fixed an issue where depth writing was not always enabled before clearing the depth buffer, causing many obscure random artifacts.

Steve Grenier 14 år sedan
förälder
incheckning
dfc153a212

+ 3 - 0
gameplay/gameplay.vcxproj

@@ -41,6 +41,7 @@
     <ClCompile Include="src\Joint.cpp" />
     <ClCompile Include="src\Light.cpp" />
     <ClCompile Include="src\Material.cpp" />
+    <ClCompile Include="src\MeshBatch.cpp" />
     <ClCompile Include="src\Pass.cpp" />
     <ClCompile Include="src\MaterialParameter.cpp" />
     <ClCompile Include="src\Matrix.cpp" />
@@ -109,6 +110,7 @@
     <ClInclude Include="src\Joint.h" />
     <ClInclude Include="src\Light.h" />
     <ClInclude Include="src\Material.h" />
+    <ClInclude Include="src\MeshBatch.h" />
     <ClInclude Include="src\Pass.h" />
     <ClInclude Include="src\MaterialParameter.h" />
     <ClInclude Include="src\Matrix.h" />
@@ -177,6 +179,7 @@
     <None Include="src\gameplay-main-macos.mm" />
     <None Include="src\Image.inl" />
     <None Include="src\Matrix.inl" />
+    <None Include="src\MeshBatch.inl" />
     <None Include="src\Plane.inl" />
     <None Include="src\PlatformMacOS.mm" />
     <None Include="src\Quaternion.inl" />

+ 9 - 0
gameplay/gameplay.vcxproj.filters

@@ -213,6 +213,9 @@
     <ClCompile Include="src\Image.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\MeshBatch.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -410,6 +413,9 @@
     <ClInclude Include="src\Image.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\MeshBatch.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -487,6 +493,9 @@
     <None Include="src\Image.inl">
       <Filter>src</Filter>
     </None>
+    <None Include="src\MeshBatch.inl">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">

+ 0 - 1
gameplay/src/FileSystem.cpp

@@ -84,5 +84,4 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     return buffer;
 }
 
-
 }

+ 5 - 5
gameplay/src/Font.cpp

@@ -8,14 +8,14 @@
 #define FONT_VSH \
     "uniform mat4 u_projectionMatrix;\n" \
     "attribute vec3 a_position;\n" \
-    "attribute vec2 a_texcoord;\n" \
+    "attribute vec2 a_texCoord;\n" \
     "attribute vec4 a_color;\n" \
-    "varying vec2 v_texcoord;\n" \
+    "varying vec2 v_texCoord;\n" \
     "varying vec4 v_color;\n" \
     "void main()\n" \
     "{\n" \
         "gl_Position = u_projectionMatrix * vec4(a_position, 1);\n" \
-        "v_texcoord = a_texcoord;\n" \
+        "v_texCoord = a_texCoord;\n" \
         "v_color = a_color;\n" \
     "}\n"
 
@@ -24,13 +24,13 @@
     "#ifdef OPENGL_ES\n" \
     "precision highp float;\n" \
     "#endif\n" \
-    "varying vec2 v_texcoord;\n" \
+    "varying vec2 v_texCoord;\n" \
     "varying vec4 v_color;\n" \
     "uniform sampler2D u_texture;\n" \
     "void main()\n" \
     "{\n" \
         "gl_FragColor = v_color;\n" \
-        "gl_FragColor.a = texture2D(u_texture, v_texcoord).a;\n" \
+        "gl_FragColor.a = texture2D(u_texture, v_texCoord).a;\n" \
     "}"
 
 namespace gameplay

+ 5 - 0
gameplay/src/Game.cpp

@@ -217,6 +217,11 @@ void Game::clear(ClearFlags flags, const Vector4& clearColor, float clearDepth,
             _clearDepth = clearDepth;
         }
         bits |= GL_DEPTH_BUFFER_BIT;
+
+        // We need to explicitly call the static enableDepthWrite() method on StateBlock
+        // to ensure depth writing is enabled before clearing the depth buffer (and to 
+        // update the global StateBlock render state to reflect this).
+        RenderState::StateBlock::enableDepthWrite();
     }
 
     if (flags & CLEAR_STENCIL)

+ 0 - 14
gameplay/src/Material.cpp

@@ -174,20 +174,6 @@ void Material::setTechnique(unsigned int index)
     }
 }
 
-void Material::setMeshBinding(Mesh* mesh)
-{
-    // Call setMeshBinding() on all passes in all techniques
-    for (unsigned int i = 0, tCount = _techniques.size(); i < tCount; ++i)
-    {
-        Technique* t = _techniques[i];
-        for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
-        {
-            Pass* p = t->getPass(j);
-            p->setMeshBinding(mesh);
-        }
-    }
-}
-
 bool Material::loadTechnique(Material* material, Properties* techniqueProperties)
 {
     // Create a new technique

+ 0 - 12
gameplay/src/Material.h

@@ -4,7 +4,6 @@
 #include "RenderState.h"
 #include "Technique.h"
 #include "Properties.h"
-#include "Mesh.h"
 
 namespace gameplay
 {
@@ -115,17 +114,6 @@ public:
      */
     void setTechnique(const char* id);
 
-    /**
-     * Stores a binding for the specified mesh onto all techniques and passes in this material.
-     *
-     * This method creates and stores a VertexAttributeBinding for all techniques and passes
-     * onto the  specified Mesh. When a mesh binding is set, the VertexAttributeBinding
-     * will be automatically bound when the bind() method is called for a pass.
-     *
-     * @param mesh The Mesh to create and store a VertexAttributeBinding for (or NULL to remove an existing mesh bindings).
-     */
-    void setMeshBinding(Mesh* mesh);
-
 private:
 
     /**

+ 18 - 30
gameplay/src/Mesh.cpp

@@ -8,13 +8,16 @@
 namespace gameplay
 {
 
-Mesh::Mesh() 
-    : _vertexFormat(NULL), _vertexCount(0), _vertexBuffer(0), _primitiveType(TRIANGLES), 
+Mesh::Mesh(const VertexFormat& vertexFormat) 
+    : _vertexFormat(vertexFormat), _vertexCount(0), _vertexBuffer(0), _primitiveType(TRIANGLES), 
       _partCount(0), _parts(NULL), _dynamic(false)
 {
 }
 
-Mesh::Mesh(const Mesh& copy)
+Mesh::Mesh(const Mesh& copy) :
+    _vertexFormat(copy._vertexFormat), _vertexCount(copy._vertexCount), _vertexBuffer(copy._vertexBuffer),
+    _primitiveType(copy._primitiveType), _partCount(copy._partCount), _parts(copy._parts), _dynamic(copy._dynamic),
+    _boundingBox(copy._boundingBox), _boundingSphere(copy._boundingSphere)
 {
     // hidden
 }
@@ -32,11 +35,9 @@ Mesh::~Mesh()
         glDeleteBuffers(1, &_vertexBuffer);
         _vertexBuffer = 0;
     }
-
-    SAFE_RELEASE(_vertexFormat);
 }
 
-Mesh* Mesh::createMesh(VertexFormat* vertexFormat, unsigned int vertexCount, bool dynamic)
+Mesh* Mesh::createMesh(const VertexFormat& vertexFormat, unsigned int vertexCount, bool dynamic)
 {
     GLuint vbo;
     GL_ASSERT( glGenBuffers(1, &vbo) );
@@ -52,7 +53,7 @@ Mesh* Mesh::createMesh(VertexFormat* vertexFormat, unsigned int vertexCount, boo
         return NULL;
     }
 
-    GL_CHECK( glBufferData(GL_ARRAY_BUFFER, vertexFormat->getVertexSize() * vertexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
+    GL_CHECK( glBufferData(GL_ARRAY_BUFFER, vertexFormat.getVertexSize() * vertexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
     if (GL_LAST_ERROR())
     {
         glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -60,14 +61,11 @@ Mesh* Mesh::createMesh(VertexFormat* vertexFormat, unsigned int vertexCount, boo
         return NULL;
     }
 
-    Mesh* mesh = new Mesh();
-    mesh->_vertexFormat = vertexFormat;
+    Mesh* mesh = new Mesh(vertexFormat);
     mesh->_vertexCount = vertexCount;
     mesh->_vertexBuffer = vbo;
     mesh->_dynamic = dynamic;
 
-    vertexFormat->addRef();
-
     return mesh;
 }
 
@@ -91,9 +89,7 @@ Mesh* Mesh::createQuad(float x, float y, float width, float height)
         VertexFormat::Element(VertexFormat::NORMAL, 3),
         VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
     };
-    VertexFormat* format = VertexFormat::create(elements, 3);
-    Mesh* mesh = Mesh::createMesh(format, 4, false);
-    SAFE_RELEASE(format);
+    Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 3), 4, false);
     if (mesh == NULL)
     {
         return NULL;
@@ -125,9 +121,7 @@ Mesh* Mesh::createQuadFullscreen()
         VertexFormat::Element(VertexFormat::POSITION, 2),
         VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
     };
-    VertexFormat* format = VertexFormat::create(elements, 2);
-    Mesh* mesh = Mesh::createMesh(format, 4, false);
-    SAFE_RELEASE(format);
+    Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
     if (mesh == NULL)
     {
         return NULL;
@@ -163,9 +157,7 @@ Mesh* Mesh::createQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3,
         VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
     };
 
-    VertexFormat* format = VertexFormat::create(elements, 3);
-    Mesh* mesh = Mesh::createMesh(format, 4, false);
-    SAFE_RELEASE(format);
+    Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 3), 4, false);
     if (mesh == NULL)
     {
         return NULL;
@@ -186,9 +178,7 @@ Mesh* Mesh::createLines(Vector3* points, unsigned int pointCount)
     {
         VertexFormat::Element(VertexFormat::POSITION, 3)
     };
-    VertexFormat* format = VertexFormat::create(elements, 1);
-    Mesh* mesh = Mesh::createMesh(format, pointCount, false);
-    SAFE_RELEASE(format);
+    Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 1), pointCount, false);
     if (mesh == NULL)
     {
         SAFE_DELETE_ARRAY(vertices);
@@ -233,9 +223,7 @@ Mesh* Mesh::createBoundingBox(const BoundingBox& box)
     {
         VertexFormat::Element(VertexFormat::POSITION, 3)
     };
-    VertexFormat* format = VertexFormat::create(elements, 1);
-    Mesh* mesh = Mesh::createMesh(format, 18, false);
-    SAFE_RELEASE(format);
+    Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 1), 18, false);
     if (mesh == NULL)
     {
         return NULL;
@@ -247,7 +235,7 @@ Mesh* Mesh::createBoundingBox(const BoundingBox& box)
     return mesh;
 }
 
-const VertexFormat* Mesh::getVertexFormat() const
+const VertexFormat& Mesh::getVertexFormat() const
 {
     return _vertexFormat;
 }
@@ -259,7 +247,7 @@ unsigned int Mesh::getVertexCount() const
 
 unsigned int Mesh::getVertexSize() const
 {
-    return _vertexFormat->getVertexSize();
+    return _vertexFormat.getVertexSize();
 }
 
 VertexBufferHandle Mesh::getVertexBuffer() const
@@ -289,7 +277,7 @@ void Mesh::setVertexData(void* vertexData, unsigned int vertexStart, unsigned in
 
     if (vertexStart == 0 && vertexCount == 0)
     {
-        GL_ASSERT( glBufferData(GL_ARRAY_BUFFER, _vertexFormat->getVertexSize() * _vertexCount, vertexData, _dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
+        GL_ASSERT( glBufferData(GL_ARRAY_BUFFER, _vertexFormat.getVertexSize() * _vertexCount, vertexData, _dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
     }
     else
     {
@@ -298,7 +286,7 @@ void Mesh::setVertexData(void* vertexData, unsigned int vertexStart, unsigned in
             vertexCount = _vertexCount - vertexStart;
         }
 
-        GL_ASSERT( glBufferSubData(GL_ARRAY_BUFFER, vertexStart * _vertexFormat->getVertexSize(), vertexCount * _vertexFormat->getVertexSize(), vertexData) );
+        GL_ASSERT( glBufferSubData(GL_ARRAY_BUFFER, vertexStart * _vertexFormat.getVertexSize(), vertexCount * _vertexFormat.getVertexSize(), vertexData) );
     }
 }
 

+ 6 - 5
gameplay/src/Mesh.h

@@ -1,6 +1,7 @@
 #ifndef MESH_H_
 #define MESH_H_
 
+#include "Ref.h"
 #include "VertexFormat.h"
 #include "Vector3.h"
 #include "BoundingBox.h"
@@ -47,7 +48,7 @@ public:
     };
 
     /**
-     * Constructs a basic static mesh with the specified vertex format.
+     * Constructs a new mesh with the specified vertex format.
      *
      * @param vertexFormat The vertex format.
      * @param vertexCount The number of vertices.
@@ -55,7 +56,7 @@ public:
      * 
      * @return The created mesh.
      */
-    static Mesh* createMesh(VertexFormat* vertexFormat, unsigned int vertexCount, bool dynamic = false);
+    static Mesh* createMesh(const VertexFormat& vertexFormat, unsigned int vertexCount, bool dynamic = false);
 
     /**
      * Creates a new textured 3D quad.
@@ -127,7 +128,7 @@ public:
      *
      * @return The vertex format.
      */
-    const VertexFormat* getVertexFormat() const;
+    const VertexFormat& getVertexFormat() const;
 
     /**
      * Gets the number of vertices in the mesh.
@@ -285,7 +286,7 @@ private:
     /**
      * Constructor.
      */
-    Mesh();
+    Mesh(const VertexFormat& vertexFormat);
 
     /**
      * Constructor.
@@ -294,7 +295,7 @@ private:
      */
     Mesh(const Mesh& copy);
 
-    VertexFormat* _vertexFormat;
+    const VertexFormat _vertexFormat;
     unsigned int _vertexCount;
     VertexBufferHandle _vertexBuffer;
     PrimitiveType _primitiveType;

+ 197 - 0
gameplay/src/MeshBatch.cpp

@@ -0,0 +1,197 @@
+#include "Base.h"
+#include "MeshBatch.h"
+
+namespace gameplay
+{
+
+MeshBatch::MeshBatch(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, Material* material, bool indexed, unsigned int initialCapacity, unsigned int growSize)
+    : _vertexFormat(vertexFormat), _primitiveType(primitiveType), _material(material), _indexed(indexed), _capacity(0), _growSize(growSize),
+      _vertexCapacity(0), _indexCapacity(0), _vertexCount(0), _indexCount(0), _vertices(NULL), _verticesPtr(NULL), _indices(NULL), _indicesPtr(NULL)
+{
+    resize(initialCapacity);
+}
+
+MeshBatch::MeshBatch(const MeshBatch& copy)
+    : _vertexFormat(copy._vertexFormat)
+{
+    // hidden
+}
+
+MeshBatch::~MeshBatch()
+{
+    SAFE_RELEASE(_material);
+    SAFE_DELETE_ARRAY(_vertices);
+    SAFE_DELETE_ARRAY(_indices);
+}
+
+MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, const char* materialPath, bool indexed, unsigned int initialCapacity, unsigned int growSize)
+{
+    Material* material = Material::create(materialPath);
+    if (material == NULL)
+        return NULL;
+    MeshBatch* batch = create(vertexFormat, primitiveType, material, indexed, initialCapacity, growSize);
+    SAFE_RELEASE(material); // batch now owns the material
+    return batch;
+}
+
+MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, Material* material, bool indexed, unsigned int initialCapacity, unsigned int growSize)
+{
+    assert(material);
+
+    MeshBatch* batch = new MeshBatch(vertexFormat, primitiveType, material, indexed, initialCapacity, growSize);
+
+    material->addRef();
+
+    return batch;
+}
+
+void MeshBatch::updateVertexAttributeBinding()
+{
+    // Update our vertex attribute bindings
+    for (unsigned int i = 0, techniqueCount = _material->getTechniqueCount(); i < techniqueCount; ++i)
+    {
+        Technique* t = _material->getTechnique(i);
+        for (unsigned int j = 0, passCount = t->getPassCount(); j < passCount; ++j)
+        {
+            Pass* p = t->getPass(j);
+            VertexAttributeBinding* b = VertexAttributeBinding::create(_vertexFormat, _vertices, p->getEffect());
+            p->setVertexAttributeBinding(b);
+            SAFE_RELEASE(b);
+        }
+    }
+}
+
+unsigned int MeshBatch::getCapacity() const
+{
+    return _capacity;
+}
+
+void MeshBatch::setCapacity(unsigned int capacity)
+{
+    resize(capacity);
+}
+
+bool MeshBatch::resize(unsigned int capacity)
+{
+    assert(capacity > 0);
+    if (capacity == 0)
+        return false;
+
+    if (capacity == _capacity)
+        return true;
+
+    // Store old batch data
+    unsigned char* oldVertices = _vertices;
+    unsigned short* oldIndices = _indices;
+
+    unsigned int vertexCapacity = 0;
+    switch (_primitiveType)
+    {
+    case Mesh::LINES:
+        vertexCapacity = capacity * 2;
+        break;
+    case Mesh::LINE_STRIP:
+        vertexCapacity = capacity + 1;
+        break;
+    case Mesh::POINTS:
+        vertexCapacity = capacity;
+        break;
+    case Mesh::TRIANGLES:
+        vertexCapacity = capacity * 3;
+        break;
+    case Mesh::TRIANGLE_STRIP:
+        vertexCapacity = capacity + 2;
+        break;
+    default:
+        assert(0); // unexpected
+        break;
+    }
+
+    // We have no way of knowing how many vertices will be stored in the batch
+    // (we only know how many indices will be stored). Assume the worst case
+    // for now, which is the same number of vertices as indices.
+    unsigned int indexCapacity = vertexCapacity;
+
+    assert(indexCapacity <= USHRT_MAX);
+    if (indexCapacity > USHRT_MAX)
+        return false;
+
+    // Allocate new data and reset pointers
+    unsigned int voffset = _verticesPtr - _vertices;
+    unsigned int vBytes = vertexCapacity * _vertexFormat.getVertexSize();
+    _vertices = new unsigned char[vBytes];
+    if (voffset >= vBytes)
+        voffset = vBytes - 1;
+    _verticesPtr = _vertices + voffset;
+
+    if (_indexed)
+    {
+        unsigned int ioffset = _indicesPtr - _indices;
+        _indices = new unsigned short[indexCapacity];
+        if (ioffset >= indexCapacity)
+            ioffset = indexCapacity - 1;
+        _indicesPtr = _indices + ioffset;
+    }
+
+    // Copy old data back in
+    if (oldVertices)
+        memcpy(_vertices, oldVertices, std::min(_vertexCapacity, vertexCapacity) * _vertexFormat.getVertexSize());
+    SAFE_DELETE_ARRAY(oldVertices);
+    if (oldIndices)
+        memcpy(_indices, oldIndices, std::min(_indexCapacity, indexCapacity) * sizeof(unsigned short));
+    SAFE_DELETE_ARRAY(oldIndices);
+
+    // Assign new capacities
+    _capacity = capacity;
+    _vertexCapacity = vertexCapacity;
+    _indexCapacity = indexCapacity;
+
+    // Update our vertex attribute bindings now that our client array pointers have changed
+    updateVertexAttributeBinding();
+
+    return true;
+}
+
+void MeshBatch::begin()
+{
+    _vertexCount = 0;
+    _indexCount = 0;
+    _verticesPtr = _vertices;
+    _indicesPtr = _indices;
+}
+
+void MeshBatch::end()
+{
+}
+
+void MeshBatch::draw()
+{
+    if (_vertexCount == 0 || (_indexed && _indexCount == 0))
+        return; // nothing to draw
+
+    // Not using VBOs, so unbind the element array buffer.
+    // ARRAY_BUFFER will be unbound automatically during pass->bind().
+    GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0 ) );
+
+    // Bind the material
+    Technique* technique = _material->getTechnique();
+    unsigned int passCount = technique->getPassCount();
+    for (unsigned int i = 0; i < passCount; ++i)
+    {
+        Pass* pass = technique->getPass(i);
+        pass->bind();
+
+        if (_indexed)
+        {
+            GL_ASSERT( glDrawElements(_primitiveType, _indexCount, GL_UNSIGNED_SHORT, (GLvoid*)_indices) );
+        }
+        else
+        {
+            GL_ASSERT( glDrawArrays(_primitiveType, 0, _vertexCount) );
+        }
+
+        pass->unbind();
+    }
+}
+
+}

+ 143 - 0
gameplay/src/MeshBatch.h

@@ -0,0 +1,143 @@
+#ifndef MESHBATCH_H_
+#define MESHBATCH_H_
+
+#include "Mesh.h"
+#include "Material.h"
+
+namespace gameplay
+{
+
+class MeshBatch
+{
+public:
+
+    /**
+     * Creates a new mesh batch.
+     *
+     * @param vertexFormat The format of vertices in the new batch.
+     * @param primitiveType The type of primitives that will be added to the batch.
+     * @param materialPath Path to a material file to be used for drawing the batch.
+     * @param indexed True if the batched primivites will contain index data, false otherwise.
+     * @param initialCapacity The initial capacity of the batch, in triangles.
+     * @param growSize Amount to grow the batch by when it overflows (a value of zero prevents batch growing).
+     *
+     * @return A new mesh batch.
+     */
+    static MeshBatch* create(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, const char* materialPath, bool indexed, unsigned int initialCapacity = 1024, unsigned int growSize = 1024);
+
+    /**
+     * Creates a new mesh batch.
+     *
+     * @param vertexFormat The format of vertices in the new batch.
+     * @param primitiveType The type of primitives that will be added to the batch.
+     * @param material Material to be used for drawing the batch.
+     * @param indexed True if the batched primivites will contain index data, false otherwise.
+     * @param initialCapacity The initial capacity of the batch, in triangles.
+     * @param growSize Amount to grow the batch by when it overflows (a value of zero prevents batch growing).
+     *
+     * @return A new mesh batch.
+     */
+    static MeshBatch* create(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, Material* material, bool indexed, unsigned int initialCapacity = 1024, unsigned int growSize = 1024);
+
+    /**
+     * Destructor.
+     */
+    ~MeshBatch();
+
+    /**
+     * Returs the current capacity of the batch.
+     *
+     * @return The batch capacity.
+     */
+    unsigned int getCapacity() const;
+
+    /**
+     * Explicitly sets a new capacity for the batch.
+     *
+     * @param The new batch capacity.
+     */
+    void setCapacity(unsigned int capacity);
+
+    /**
+     * Returns the material for this mesh batch.
+     *
+     * @return The material used to draw the batch.
+     */
+    inline Material* getMaterial() const;
+
+    /**
+     * Adds a group of primitives to the batch.
+     *
+     * The vertex list passed in should be a pointer of structs, where the struct T represents
+     * the format of a single vertex (e.g. {x,y,z,u,v}).
+     *
+     * If the batch was created with 'indexed' set to true, then valid index data should be
+     * passed in this method. However, if 'indxed' was set to false, the indices and indexCount
+     * parameters can be omitted since only vertex data will be used.
+     *
+     * If the batch created to draw triangle strips, this method assumes that separate calls to
+     * add specify seprate triangle strips. In this case, this method will automatically stitch
+     * seperate triangle strips together using degenerate (zero-area) triangles.
+     *
+     * @param vertices Array of vertices.
+     * @param vertexCount Number of vertices.
+     * @param indices Array of indices into the vertex array (should be NULL for non-indexed batches).
+     * @param indexCount Number of indices (should be zero for non-indexed batches).
+     */
+    template <class T>
+    void add(T* vertices, unsigned int vertexCount, unsigned short* indices = NULL, unsigned int indexCount = 0);
+
+    /**
+     * Begins batching.
+     *
+     * This method should be called before calling add() to add primitives to the batch.
+     * After all primitives have been added to the batch, call the end() method to
+     * complete the batch.
+     *
+     * Calling this method will clear any primitives currently in the batch and set the
+     * position of the batch back to the beginning.
+     */
+    void begin();
+
+    /**
+     * Indicates that batching is complete and prepares the batch for drawing.
+     */
+    void end();
+
+    /**
+     * Draws the primitives currently in batch.
+     */
+    void draw();
+
+private:
+
+    MeshBatch(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, Material* material, bool indexed, unsigned int initialCapacity, unsigned int growSize);
+
+    MeshBatch(const MeshBatch& copy);
+
+    void updateVertexAttributeBinding();
+
+    bool resize(unsigned int capacity);
+
+    const VertexFormat _vertexFormat;
+    Mesh::PrimitiveType _primitiveType;
+    Material* _material;
+    bool _indexed;
+    unsigned int _capacity;
+    unsigned int _growSize;
+    unsigned int _vertexCapacity;
+    unsigned int _indexCapacity;
+    unsigned int _vertexCount;
+    unsigned int _indexCount;
+    unsigned char* _vertices;
+    unsigned char* _verticesPtr;
+    unsigned short* _indices;
+    unsigned short* _indicesPtr;
+
+};
+
+}
+
+#include "MeshBatch.inl"
+
+#endif

+ 68 - 0
gameplay/src/MeshBatch.inl

@@ -0,0 +1,68 @@
+#include "MeshBatch.h"
+
+namespace gameplay
+{
+
+Material* MeshBatch::getMaterial() const
+{
+    return _material;
+}
+
+template <class T>
+void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
+{
+    assert(sizeof(T) == _vertexFormat.getVertexSize());
+
+    unsigned int newVertexCount = _vertexCount + vertexCount;
+    unsigned int newIndexCount = _indexCount + indexCount;
+    if (_primitiveType == Mesh::TRIANGLE_STRIP && _vertexCount > 0)
+        newIndexCount += 2; // need an extra 2 indices for connecting strips with degenerate triangles
+
+    // Do we need to grow the batch?
+    while (newVertexCount > _vertexCapacity || (_indexed && newIndexCount > _indexCapacity))
+    {
+        if (_growSize == 0)
+            return; // growing disabled, just clip batch
+        if (!resize(_capacity + _growSize))
+            return; // failed to grow
+    }
+
+    // Copy vertex data
+    unsigned int vBytes = vertexCount * _vertexFormat.getVertexSize();
+    memcpy(_verticesPtr, vertices, vBytes);
+
+    // Copy index data
+    if (_indexed)
+    {
+        if (_vertexCount == 0)
+        {
+            // Simply copy values directly into the start of the index array
+            memcpy(_indicesPtr, indices, indexCount * sizeof(unsigned short));
+        }
+        else
+        {
+            if (_primitiveType == Mesh::TRIANGLE_STRIP)
+            {
+                // Create a degenerate triangle to connect separate triangle strips
+                // by duplicating the previous and next vertices.
+                _indicesPtr[0] = *(_indicesPtr-1);
+                _indicesPtr[1] = _vertexCount;
+                _indicesPtr += 2;
+            }
+
+            // Loop through all indices and insert them, their their value offset by
+            // 'vertexCount' so that they are relative to the first newly insertted vertex
+            for (unsigned int i = 0; i < indexCount; ++i)
+            {
+                _indicesPtr[i] = indices[i] + _vertexCount;
+            }
+        }
+        _indicesPtr += indexCount;
+        _indexCount = newIndexCount;
+    }
+
+    _verticesPtr += vBytes;
+    _vertexCount = newVertexCount;
+}
+
+}

+ 45 - 23
gameplay/src/Model.cpp

@@ -76,21 +76,17 @@ void Model::setMaterial(Material* material, int partIndex)
 {
     assert(partIndex == -1 || (partIndex >= 0 && partIndex < (int)getMeshPartCount()));
 
+    Material* oldMaterial = NULL;
+
     if (partIndex == -1)
     {
-        // Release existing shared material and binding.
-        if (_material)
-        {
-            _material->setMeshBinding(NULL);
-            SAFE_RELEASE(_material);
-        }
+        oldMaterial = _material;
 
         // Set new shared material.
         if (material)
         {
             _material = material;
             _material->addRef();
-            _material->setMeshBinding(_mesh);
         }
     }
     else if (partIndex >= 0 && partIndex < (int)getMeshPartCount())
@@ -101,11 +97,7 @@ void Model::setMaterial(Material* material, int partIndex)
         // Release existing part material and part binding.
         if (_partMaterials)
         {
-            if (_partMaterials[partIndex])
-            {
-                _partMaterials[partIndex]->setMeshBinding(NULL);
-                SAFE_RELEASE(_partMaterials[partIndex]);
-            }
+            oldMaterial = _partMaterials[partIndex];
         }
         else
         {
@@ -122,14 +114,43 @@ void Model::setMaterial(Material* material, int partIndex)
         {
             _partMaterials[partIndex] = material;
             material->addRef();
-            material->setMeshBinding(_mesh);
         }
     }
 
-    // Apply node binding for the new material.
-    if (material && _node)
+    // Release existing material and binding.
+    if (oldMaterial)
+    {
+        for (unsigned int i = 0, tCount = material->getTechniqueCount(); i < tCount; ++i)
+        {
+            Technique* t = material->getTechnique(i);
+            for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
+            {
+                t->getPass(j)->setVertexAttributeBinding(NULL);
+            }
+        }
+        SAFE_RELEASE(oldMaterial);
+    }
+
+    if (material)
     {
-        setMaterialNodeBinding(material);
+        // Hookup vertex attribute bindings for all passes in the new material.
+        for (unsigned int i = 0, tCount = material->getTechniqueCount(); i < tCount; ++i)
+        {
+            Technique* t = material->getTechnique(i);
+            for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
+            {
+                Pass* p = t->getPass(j);
+                VertexAttributeBinding* b = VertexAttributeBinding::create(_mesh, p->getEffect());
+                p->setVertexAttributeBinding(b);
+                SAFE_RELEASE(b);
+            }
+        }
+
+        // Apply node binding for the new material.
+        if (_node)
+        {
+            setMaterialNodeBinding(material);
+        }
     }
 }
 
@@ -218,20 +239,21 @@ void Model::setNode(Node* node)
 
 void Model::draw(bool wireframe)
 {
-    wireframe &= (_mesh->getPrimitiveType() == Mesh::TRIANGLES) | (_mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP);
     unsigned int partCount = _mesh->getPartCount();
     if (partCount == 0)
     {
         // No mesh parts (index buffers).
         if (_material)
         {
+            GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) );
+
             Technique* technique = _material->getTechnique();
-            unsigned int techniqueCount = technique->getPassCount();
-            for (unsigned int i = 0; i < techniqueCount; ++i)
+            unsigned int passCount = technique->getPassCount();
+            for (unsigned int i = 0; i < passCount; ++i)
             {
                 Pass* pass = technique->getPass(i);
                 pass->bind();
-                if (wireframe)
+                if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
                 {
                     unsigned int vertexCount = _mesh->getVertexCount();
                     for (unsigned int j = 0; j < vertexCount; j += 3)
@@ -267,13 +289,13 @@ void Model::draw(bool wireframe)
             if (material)
             {
                 Technique* technique = material->getTechnique();
-                unsigned int techniqueCount = technique->getPassCount();
-                for (unsigned int j = 0; j < techniqueCount; ++j)
+                unsigned int passCount = technique->getPassCount();
+                for (unsigned int j = 0; j < passCount; ++j)
                 {
                     Pass* pass = technique->getPass(j);
                     pass->bind();
                     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, part->_indexBuffer) );
-                    if (wireframe)
+                    if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
                     {
                         unsigned int indexCount = part->getIndexCount();
                         unsigned int indexSize = 0;

+ 2 - 11
gameplay/src/Package.cpp

@@ -1034,25 +1034,19 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
     }
 
     // Create VertexFormat
-    VertexFormat* vertexFormat = VertexFormat::create(vertexElements, vertexElementCount);
+    VertexFormat vertexFormat(vertexElements, vertexElementCount);
     SAFE_DELETE_ARRAY(vertexElements);
-    if (vertexFormat == NULL)
-    {
-        return NULL;
-    }
 
     // Read vertex data
     unsigned int vertexByteCount;
     if (fread(&vertexByteCount, 4, 1, _file) != 1 || vertexByteCount == 0)
     {
-        SAFE_RELEASE(vertexFormat);
         return NULL;
     }
     unsigned char* vertexData = new unsigned char[vertexByteCount];
     if (fread(vertexData, 1, vertexByteCount, _file) != vertexByteCount)
     {
         LOG_ERROR_VARG("Failed to read %d vertex data bytes for mesh: %s", vertexByteCount, id);
-        SAFE_RELEASE(vertexFormat);
         return NULL;
     }
 
@@ -1062,20 +1056,17 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
     if (fread(&boundsMin.x, 4, 3, _file) != 3 || fread(&boundsMax.x, 4, 3, _file) != 3)
     {
         LOG_ERROR_VARG("Failed to read bounding box for mesh: %s", id);
-        SAFE_RELEASE(vertexFormat);
         return NULL;
     }
     if (fread(&boundsCenter.x, 4, 3, _file) != 3 || fread(&boundsRadius, 4, 1, _file) != 1)
     {
         LOG_ERROR_VARG("Failed to read bounding sphere for mesh: %s", id);
-        SAFE_RELEASE(vertexFormat);
         return NULL;
     }
 
     // Create Mesh
-    int vertexCount = vertexByteCount / vertexFormat->getVertexSize();
+    int vertexCount = vertexByteCount / vertexFormat.getVertexSize();
     Mesh* mesh = Mesh::createMesh(vertexFormat, vertexCount, false);
-    SAFE_RELEASE(vertexFormat);
     if (mesh == NULL)
     {
         LOG_ERROR_VARG("Failed to create mesh: %s", id);

+ 4 - 5
gameplay/src/Pass.cpp

@@ -48,16 +48,15 @@ Effect* Pass::getEffect() const
     return _effect;
 }
 
-VertexAttributeBinding* Pass::setMeshBinding(Mesh* mesh)
+void Pass::setVertexAttributeBinding(VertexAttributeBinding* binding)
 {
     SAFE_RELEASE(_vaBinding);
 
-    if (mesh)
+    if (binding)
     {
-        _vaBinding = VertexAttributeBinding::create(mesh, _effect);
+        _vaBinding = binding;
+        binding->addRef();
     }
-
-    return _vaBinding;
 }
 
 void Pass::bind()

+ 5 - 8
gameplay/src/Pass.h

@@ -40,17 +40,14 @@ public:
     const std::vector<std::string>* getAutoBindProperties() const;
 
     /**
-     * Stores a binding for this pass onto the specified mesh.
+     * Stores a vertex attribute binding for this pass.
      *
-     * This method creates and stores a VertexAttributeBinding for this pass onto the 
-     * specified Mesh. When a mesh binding is set, the VertexAttributeBinding
-     * will be automatically bound when the bind() method is called for the pass.
+     * When a mesh binding is set, the VertexAttributeBinding will be automatically
+     * bound when the bind() method is called for the pass.
      *
-     * @param mesh The Mesh to create and store a VertexAttributeBinding for (or NULL to remove an existing mesh binding).
-     *
-     * @return The newly created VertexAttributeBinding between this pass and the specified mesh.
+     * @param binding The VertexAttributeBinding to set (or NULL to remove an existing binding).
      */
-    VertexAttributeBinding* setMeshBinding(Mesh* mesh);
+    void setVertexAttributeBinding(VertexAttributeBinding* binding);
 
     /**
      * Binds the render state for this pass.

+ 1 - 1
gameplay/src/PhysicsController.cpp

@@ -347,7 +347,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
     unsigned int vertexCount = data->mesh->getVertexCount();
     body->_vertexData = new float[vertexCount * 3];
     Vector3 v;
-    int vertexStride = data->mesh->getVertexFormat()->getVertexSize();
+    int vertexStride = data->mesh->getVertexFormat().getVertexSize();
     for (unsigned int i = 0; i < vertexCount; i++)
     {
         v.set(*((float*)&data->vertexData[i * vertexStride + 0 * sizeof(float)]),

+ 14 - 1
gameplay/src/RenderState.cpp

@@ -353,7 +353,7 @@ void RenderState::StateBlock::bindNoRestore()
     }
     if ((_bits & RS_DEPTH_WRITE) && (_depthWriteEnabled != _defaultState->_depthWriteEnabled))
     {
-        glDepthMask(_depthWriteEnabled);
+        glDepthMask(_depthWriteEnabled ? GL_TRUE : GL_FALSE);
         _defaultState->_depthWriteEnabled = _depthWriteEnabled;
     }
 
@@ -402,6 +402,19 @@ void RenderState::StateBlock::restore(long stateOverrideBits)
     }
 }
 
+void RenderState::StateBlock::enableDepthWrite()
+{
+    // Internal method used by Game::clear() to restore depth writing before a
+    // clear operation. This is neccessary if the last code to draw before the
+    // next frame leaves depth writing disabled.
+    if (!_defaultState->_depthWriteEnabled)
+    {
+        glDepthMask(GL_TRUE);
+        _defaultState->_bits &= ~RS_DEPTH_WRITE;
+        _defaultState->_depthWriteEnabled = true;
+    }
+}
+
 bool parseBoolean(const char* value)
 {
     if (strlen(value) == 4)

+ 3 - 0
gameplay/src/RenderState.h

@@ -100,6 +100,7 @@ public:
     class StateBlock : public Ref
     {
         friend class RenderState;
+        friend class Game;
 
     public:
 
@@ -195,6 +196,8 @@ public:
 
         static void restore(long stateOverrideBits);
 
+        static void enableDepthWrite();
+
         // States
         bool _blendEnabled;
         bool _cullFaceEnabled;

+ 86 - 246
gameplay/src/SpriteBatch.cpp

@@ -8,23 +8,32 @@
 // Factor to grow a sprite batch by when its size is exceeded
 #define SPRITE_BATCH_GROW_FACTOR 2.0f
 
-// Macro to add a sprite vertex
-#define ADD_SPRITE_VERTEX(ptr, x, y, z, u, v, r, g, b, a) \
-    ptr[0] = x; ptr[1] = y; ptr[2] = z; ptr[3] = u; ptr[4] = v; \
-    ptr[5] = r; ptr[6] = g; ptr[7] = b; ptr[8] = a
+// Macro for adding a sprite to the batch
+#define ADD_SPRITE_VERTEX(vtx, vx, vy, vz, vu, vv, vr, vg, vb, va) \
+    vtx.x = vx; vtx.y = vy; vtx.z = vz; \
+    vtx.u = vu; vtx.v = vv; \
+    vtx.r = vr; vtx.g = vg; vtx.b = vb; vtx.a = va
+
+// Sprite vertex structured used for batching
+struct SpriteVertex
+{
+    float x, y, z;
+    float u, v;
+    float r, g, b, a;
+};
 
 // Default sprite vertex shader
 #define SPRITE_VSH \
     "uniform mat4 u_projectionMatrix;\n" \
     "attribute vec3 a_position;\n" \
-    "attribute vec2 a_texcoord;\n" \
+    "attribute vec2 a_texCoord;\n" \
     "attribute vec4 a_color;\n" \
-    "varying vec2 v_texcoord;\n" \
+    "varying vec2 v_texCoord;\n" \
     "varying vec4 v_color;\n" \
     "void main()\n" \
     "{\n" \
         "gl_Position = u_projectionMatrix * vec4(a_position, 1);\n" \
-        "v_texcoord = a_texcoord;\n" \
+        "v_texCoord = a_texCoord;\n" \
         "v_color = a_color;\n" \
     "}\n"
 
@@ -33,12 +42,12 @@
     "#ifdef OPENGL_ES\n" \
     "precision highp float;\n" \
     "#endif\n" \
-    "varying vec2 v_texcoord;\n" \
+    "varying vec2 v_texCoord;\n" \
     "varying vec4 v_color;\n" \
     "uniform sampler2D u_texture;\n" \
     "void main()\n" \
     "{\n" \
-        "gl_FragColor = v_color * texture2D(u_texture, v_texcoord);\n" \
+        "gl_FragColor = v_color * texture2D(u_texture, v_texCoord);\n" \
     "}\n"
 
 namespace gameplay
@@ -47,16 +56,9 @@ namespace gameplay
 // Shared sprite effects
 static Effect* __spriteEffect = NULL;
 
-SpriteBatch::SpriteBatch() :
-    _texture(NULL), _effect(NULL), _stateBlock(NULL), _sampler(NULL), _samplerUniform(NULL), _projectionUniform(NULL), _vaPosition(-1), _vaTexCoord(-1), _vaColor(-1),
-    _textureWidthRatio(0.0f), _textureHeightRatio(0.0f), _capacity(0), _count(0),
-    _vertices(NULL), _verticesPtr(NULL), _indices(NULL), _indicesPtr(NULL), _index(0),
-    _drawing(false), _projectionMatrix(NULL), _customProjectionMatrix(false)
+SpriteBatch::SpriteBatch()
+    : _batch(NULL), _textureWidthRatio(0.0f), _textureHeightRatio(0.0f)
 {
-    _stateBlock = RenderState::StateBlock::create();
-    _stateBlock->setBlend(true);
-    _stateBlock->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
-    _stateBlock->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
 }
 
 SpriteBatch::SpriteBatch(const SpriteBatch& copy)
@@ -66,20 +68,14 @@ SpriteBatch::SpriteBatch(const SpriteBatch& copy)
 
 SpriteBatch::~SpriteBatch()
 {
-    SAFE_RELEASE(_stateBlock);
-    SAFE_DELETE_ARRAY(_vertices);
-    SAFE_DELETE_ARRAY(_indices);
-    SAFE_DELETE(_projectionMatrix);
-    SAFE_RELEASE(_sampler);
-    SAFE_RELEASE(_effect);
-    SAFE_RELEASE(_texture);
+    SAFE_DELETE(_batch);
 }
 
 SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsigned int initialCapacity)
 {
     Texture* texture = Texture::create(texturePath);
     SpriteBatch* batch = SpriteBatch::create(texture);
-    batch->_texture = texture;
+    SAFE_RELEASE(texture);
     return batch;
 }
 
@@ -104,25 +100,8 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
         else
         {
             effect = __spriteEffect;
-            effect->addRef();
         }
     }
-    else
-    {
-        // Add a reference to the effect.
-        effect->addRef();
-    }
-
-    // Look up vertex attributes.
-    VertexAttribute vaPosition = effect->getVertexAttribute("a_position");
-    VertexAttribute vaTexCoord = effect->getVertexAttribute("a_texcoord");
-    VertexAttribute vaColor = effect->getVertexAttribute("a_color");
-    if (vaPosition == -1 || vaTexCoord == -1 || vaColor == -1)
-    {
-        LOG_ERROR("Failed to load vertex attributes for sprite effect.");
-        SAFE_RELEASE(effect);
-        return NULL;
-    }
 
     // Search for the first sampler uniform in the effect.
     Uniform* samplerUniform = NULL;
@@ -142,38 +121,48 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
         return NULL;
     }
 
-    // Create the batch.
+    // Wrap the effect in a material
+    Material* material = Material::create(effect); // +ref effect
+
+    // Set initial material state
+    material->getStateBlock()->setBlend(true);
+    material->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
+    material->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
+
+    // Bind the texture to the material as a sampler
+    Texture::Sampler* sampler = Texture::Sampler::create(texture); // +ref texture
+    material->getParameter(samplerUniform->getName())->setValue(sampler);
+    SAFE_RELEASE(sampler);
+
+    // Define the vertex format for the batch
+    VertexFormat::Element vertexElements[] =
+    {
+        VertexFormat::Element(VertexFormat::POSITION, 3),
+        VertexFormat::Element(VertexFormat::TEXCOORD0, 2),
+        VertexFormat::Element(VertexFormat::COLOR, 4),
+        
+    };
+    VertexFormat vertexFormat(vertexElements, 3);
+
+    // Create the mesh batch
+    MeshBatch* meshBatch = MeshBatch::create(vertexFormat, Mesh::TRIANGLE_STRIP, material, true, initialCapacity > 0 ? initialCapacity : SPRITE_BATCH_DEFAULT_SIZE);
+    material->release(); // don't call SAFE_RELEASE since material is used below
+
+    // Create the batch
     SpriteBatch* batch = new SpriteBatch();
-    batch->_effect = effect;
-    batch->_sampler = Texture::Sampler::create(texture);
-    batch->_samplerUniform = samplerUniform;
-    batch->_vaPosition = vaPosition;
-    batch->_vaTexCoord = vaTexCoord;
-    batch->_vaColor = vaColor;
+    batch->_batch = meshBatch;
     batch->_textureWidthRatio = 1.0f / (float)texture->getWidth();
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
-    batch->resizeBatch(initialCapacity > 0 ? initialCapacity : SPRITE_BATCH_DEFAULT_SIZE);
 
-    // If there is a uniform named 'u_projectionMatrix', store it so that we can set our projection matrix to it
-    batch->_projectionUniform = effect->getUniform("u_projectionMatrix");
-    if (batch->_projectionUniform)
-    {
-        batch->_projectionMatrix = new Matrix();
-    }
+    // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
+    material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getOrthoMatrix);
 
     return batch;
 }
 
 void SpriteBatch::begin()
 {
-    assert(!_drawing);
-
-    // Simply clear our sprite count to start writing to the beginning of the batch.
-    _count = 0;
-    _index = 0;
-    _verticesPtr = _vertices;
-    _indicesPtr = _indices;
-    _drawing = true;
+    _batch->begin();
 }
 
 void SpriteBatch::draw(const Rectangle& dst, const Rectangle& src, const Vector4& color)
@@ -201,13 +190,6 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
 void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2& scale, const Vector4& color,
                        const Vector2& rotationPoint, float rotationAngle)
 {
-    assert(_drawing);
-
-    if (_count >= _capacity)
-    {
-        growBatch();
-    }
-
     // Calculate uvs.
     float u1 = _textureWidthRatio * src.x;
     float v1 = 1.0f - _textureHeightRatio * src.y;
@@ -241,29 +223,15 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
     downRight.rotate(pivotPoint, rotationAngle);
     
     // Write sprite vertex data.
-    ADD_SPRITE_VERTEX(_verticesPtr, upLeft.x, upLeft.y, dst.z, u1, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX((_verticesPtr + 9), upRight.x, upRight.y, dst.z, u1, v2, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX((_verticesPtr + 18), downLeft.x, downLeft.y, dst.z, u2, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX((_verticesPtr + 27), downRight.x, downRight.y, dst.z, u2, v2, color.x, color.y, color.z, color.w);
-    _verticesPtr += 36; // 4 vertices per sprite, 9 elements per vertex (4*9)
-
-    // Write sprite index data.
-    if (_count > 0)
-    {
-        // Create a degenerate triangle to connect two triangle strips
-        // by duplicating the previous and next vertices.
-        _indicesPtr[0] = *(_indicesPtr-1);
-        _indicesPtr[1] = _index;
-        _indicesPtr += 2;
-    }
-    _indicesPtr[0] = _index;
-    _indicesPtr[1] = _index + 1;
-    _indicesPtr[2] = _index + 2;
-    _indicesPtr[3] = _index + 3;
-    _indicesPtr += 4;
-    _index += 4;
-
-    ++_count;
+    static SpriteVertex v[4];
+    ADD_SPRITE_VERTEX(v[0], upLeft.x, upLeft.y, dst.z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], upRight.x, upRight.y, dst.z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], downLeft.x, downLeft.y, dst.z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], downRight.x, downRight.y, dst.z, u2, v2, color.x, color.y, color.z, color.w);
+    
+    static unsigned short indices[4] = { 0, 1, 2, 3 };
+
+    _batch->add(v, 4, indices, 4);
 }
 
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
@@ -273,177 +241,49 @@ void SpriteBatch::draw(float x, float y, float width, float height, float u1, fl
 
 void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
 {
-    assert(_drawing);
-
-    if (_count >= _capacity)
-    {
-        growBatch();
-    }
-
     // Write sprite vertex data.
     float x2 = x + width;
     float y2 = y + height;
-    ADD_SPRITE_VERTEX(_verticesPtr, x, y, z, u1, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX((_verticesPtr + 9), x, y2, z, u1, v2, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX((_verticesPtr + 18), x2, y, z, u2, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX((_verticesPtr + 27), x2, y2, z, u2, v2, color.x, color.y, color.z, color.w);
-    _verticesPtr += 36; // 4 vertices per sprite, 9 elements per vertex (4*9)
-
-    // Write sprite index data.
-    if (_count > 0)
-    {
-        // Create a degenerate triangle to connect two triangle strips
-        // by duplicating the previous and next vertices.
-        _indicesPtr[0] = *(_indicesPtr-1);
-        _indicesPtr[1] = _index;
-        _indicesPtr += 2;
-    }
-    _indicesPtr[0] = _index;
-    _indicesPtr[1] = _index + 1;
-    _indicesPtr[2] = _index + 2;
-    _indicesPtr[3] = _index + 3;
-    _indicesPtr += 4;
-    _index += 4;
-
-    ++_count;
-}
-
-void SpriteBatch::end()
-{
-    assert(_drawing);
-
-    if (_count > 0)
-    {
-        // Flush the batch.
-        if (_projectionMatrix && !_customProjectionMatrix)
-        {
-            // Update projection matrix with ortho projection.
-            Game* game = Game::getInstance();
-            Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, _projectionMatrix);
-        }
-
-        // Apply render state
-        _stateBlock->bind();
-
-        // Bind our effect and any required parameters
-        _effect->bind();
-        if (_samplerUniform && _sampler)
-        {
-            _effect->setValue(_samplerUniform, _sampler);
-        }
-        if (_projectionMatrix)
-        {
-            _effect->setValue(_projectionUniform, _projectionMatrix);
-        }
-
-        // Unbind any currently bound VBOs so we can use client arrays.
-        GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0 ) );
-        GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0 ) );
-
-        GL_ASSERT( glEnableVertexAttribArray(_vaPosition) );
-        GL_ASSERT( glVertexAttribPointer(_vaPosition, 3, GL_FLOAT, GL_FALSE, 36, (GLvoid*)_vertices) );
+    static SpriteVertex v[4];
+    ADD_SPRITE_VERTEX(v[0], x, y, z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], x, y2, z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], x2, y, z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], x2, y2, z, u2, v2, color.x, color.y, color.z, color.w);
 
-        GL_ASSERT( glEnableVertexAttribArray(_vaTexCoord) );
-        GL_ASSERT( glVertexAttribPointer(_vaTexCoord, 2, GL_FLOAT, GL_FALSE, 36, (GLvoid*)(_vertices + 3)) );
+    static unsigned short indices[4] = { 0, 1, 2, 3 };
 
-        GL_ASSERT( glEnableVertexAttribArray(_vaColor) );
-        GL_ASSERT( glVertexAttribPointer(_vaColor, 4, GL_FLOAT, GL_FALSE, 36, (GLvoid*)(_vertices + 5)) );
-
-        GLsizei indexCount = _count * 4 + ((_count - 1) * 2);
-        GL_ASSERT( glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_SHORT, (GLvoid*)_indices) );
-
-        glDisableVertexAttribArray(_vaPosition);
-        glDisableVertexAttribArray(_vaTexCoord);
-        glDisableVertexAttribArray(_vaColor);
-    }
-
-    _drawing = false;
+    _batch->add(v, 4, indices, 4);
 }
 
-RenderState::StateBlock* SpriteBatch::getStateBlock() const
+void SpriteBatch::end()
 {
-    return _stateBlock;
+    // Finish and draw the batch
+    _batch->end();
+    _batch->draw();
 }
 
-void SpriteBatch::growBatch()
+RenderState::StateBlock* SpriteBatch::getStateBlock() const
 {
-    resizeBatch(_capacity == 0 ? SPRITE_BATCH_DEFAULT_SIZE : (int)((float)_capacity * SPRITE_BATCH_GROW_FACTOR));
+    return _batch->getMaterial()->getStateBlock();
 }
 
-void SpriteBatch::resizeBatch(unsigned int capacity)
+Material* SpriteBatch::getMaterial()
 {
-    // 4 verts per sprite
-    unsigned int newVertexCapacity = capacity * 4;
-
-    // 2 extra indices per sprite for degenerate vertices
-    unsigned int newIndexCapacity = capacity * 4 + ((capacity - 1) * 2);
-
-    // 9 elements per vertex (x,y,z,u,v,r,g,b,a)
-    float* newVertices = new float[newVertexCapacity * 9];
-    unsigned short* newIndices = new unsigned short[newIndexCapacity];
-
-    // Copy and destroy old arrays.
-    if (_vertices)
-    {
-        if (_count > 0)
-        {
-            unsigned int vertexCount = _count * 4;
-            if (vertexCount > newVertexCapacity)
-            {
-                vertexCount = newVertexCapacity;
-            }
-            memcpy(newVertices, _vertices, vertexCount * 9 * sizeof(float));
-        }
-        SAFE_DELETE_ARRAY(_vertices);
-    }
-    if (_indices)
-    {
-        if (_count > 0)
-        {
-            unsigned int indexCount = _count * 4 + ((_count - 1) * 2);
-            if (indexCount > newIndexCapacity)
-            {
-                indexCount = newIndexCapacity;
-            }
-            memcpy(newIndices, _indices, indexCount * sizeof(unsigned short));
-        }
-        SAFE_DELETE_ARRAY(_indices);
-    }
-
-    // Store new arrays.
-    _vertices = newVertices;
-    _indices = newIndices;
-    _capacity = capacity;
-    if (_count > _capacity)
-    {
-        _count = capacity;
-    }
-
-    // Update current pointers.
-    if (_count > 0)
-    {
-        _verticesPtr = _vertices + (_count * 36);
-        _indicesPtr = _indices + (_count * 4) + ((_count - 1) * 2);
-    }
-    else
-    {
-        _verticesPtr = _vertices;
-        _indicesPtr = _indices;
-    }
+    return _batch->getMaterial();
 }
 
-Effect* SpriteBatch::getEffect()
+void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 {
-    return _effect;
+    // Bind the specified matrix to a parameter named 'u_projectionMatrix' (assumed to exist).
+    _batch->getMaterial()->getParameter("u_projectionMatrix")->setValue(matrix);
 }
 
-void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
+const Matrix& SpriteBatch::getOrthoMatrix() const
 {
-    if (_projectionMatrix)
-    {
-        _projectionMatrix->set(matrix);
-        _customProjectionMatrix = true;
-    }
+    // Update matrix with ortho projection and return it.
+    Game* game = Game::getInstance();
+    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &_projectionMatrix);
+    return _projectionMatrix;
 }
 
 }

+ 7 - 25
gameplay/src/SpriteBatch.h

@@ -7,6 +7,7 @@
 #include "Rectangle.h"
 #include "Matrix.h"
 #include "RenderState.h"
+#include "MeshBatch.h"
 
 namespace gameplay
 {
@@ -194,11 +195,11 @@ public:
     RenderState::StateBlock* getStateBlock() const;
 
     /**
-     * Gets the effect used by this batch.
+     * Gets the material used by this batch.
      * 
-     * @return The effect.
+     * @return The material.
      */
-    Effect* getEffect();
+    Material* getMaterial();
 
     /**
      * Sets a custom projection matrix to use with the sprite batch.
@@ -226,32 +227,13 @@ private:
      */
     SpriteBatch(const SpriteBatch& copy);
 
-    void growBatch();
+    const Matrix& getOrthoMatrix() const;
 
-    void resizeBatch(unsigned int capacity);
-
-    Texture* _texture;
-    Effect* _effect;
-    RenderState::StateBlock* _stateBlock;
-    Texture::Sampler* _sampler;
-    Uniform* _samplerUniform;
-    Uniform* _projectionUniform;
+    MeshBatch* _batch;
     bool _customEffect;
-    VertexAttribute _vaPosition;
-    VertexAttribute _vaTexCoord;
-    VertexAttribute _vaColor;
     float _textureWidthRatio;
     float _textureHeightRatio;
-    unsigned int _capacity;
-    unsigned int _count;
-    float* _vertices;
-    float* _verticesPtr;
-    unsigned short* _indices;
-    unsigned short* _indicesPtr;
-    unsigned short _index;
-    bool _drawing;
-    Matrix* _projectionMatrix;
-    bool _customProjectionMatrix;
+    mutable Matrix _projectionMatrix;
 };
 
 }

+ 64 - 36
gameplay/src/VertexAttributeBinding.cpp

@@ -37,6 +37,37 @@ VertexAttributeBinding::~VertexAttributeBinding()
 }
 
 VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effect)
+{
+    // Search for an existing vertex attribute binding that can be used.
+    VertexAttributeBinding* b;
+    for (unsigned int i = 0, count = __vertexAttributeBindingCache.size(); i < count; ++i)
+    {
+        b = __vertexAttributeBindingCache[i];
+        if (b->_mesh == mesh && b->_effect == effect)
+        {
+            // Found a match!
+            b->addRef();
+            return b;
+        }
+    }
+
+    b = create(mesh, mesh->getVertexFormat(), 0, effect);
+
+    // Add the new vertex attribute binding to the cache.
+    if (b)
+    {
+        __vertexAttributeBindingCache.push_back(b);
+    }
+
+    return b;
+}
+
+VertexAttributeBinding* VertexAttributeBinding::create(const VertexFormat& vertexFormat, void* vertexPointer, Effect* effect)
+{
+    return create(NULL, vertexFormat, vertexPointer, effect);
+}
+
+VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexFormat& vertexFormat, void* vertexPointer, Effect* effect)
 {
     // One-time initialization.
     if (__maxVertexAttribs == 0)
@@ -52,25 +83,11 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effec
         }
     }
 
-    VertexAttributeBinding* b;
-
-    // Search for an existing vertex attribute binding that can be used.
-    for (unsigned int i = 0, count = __vertexAttributeBindingCache.size(); i < count; ++i)
-    {
-        b = __vertexAttributeBindingCache[i];
-        if (b->_mesh == mesh && b->_effect == effect)
-        {
-            // Found a match!
-            b->addRef();
-            return b;
-        }
-    }
-
     // Create a new VertexAttributeBinding.
-    b = new VertexAttributeBinding();
+    VertexAttributeBinding* b = new VertexAttributeBinding();
 
 #ifdef USE_GL_VAOS
-    if (glGenVertexArrays)
+    if (mesh && glGenVertexArrays)
     {
         GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
         GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0) );
@@ -97,29 +114,32 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effec
         VertexAttribute* attribs = new VertexAttribute[__maxVertexAttribs];
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         {
+            // Set GL defaults
             attribs[i].enabled = GL_FALSE;
             attribs[i].size = 4;
             attribs[i].stride = 0;
             attribs[i].type = GL_FLOAT;
             attribs[i].normalized = GL_FALSE;
-            attribs[i].offset = 0;
+            attribs[i].pointer = 0;
         }
         b->_attributes = attribs;
     }
 
-    b->_mesh = mesh;
-    mesh->addRef();
-
+    if (mesh)
+    {
+        b->_mesh = mesh;
+        mesh->addRef();
+    }
+    
     b->_effect = effect;
     effect->addRef();
 
-    // Call setVertexAttribPointer for each vertex element in our mesh vertex format.
+    // Call setVertexAttribPointer for each vertex element.
     std::string name;
     unsigned int offset = 0;
-    const VertexFormat* vertexFormat = mesh->getVertexFormat();
-    for (unsigned int i = 0, count = vertexFormat->getElementCount(); i < count; ++i)
+    for (unsigned int i = 0, count = vertexFormat.getElementCount(); i < count; ++i)
     {
-        const VertexFormat::Element& e = vertexFormat->getElement(i);
+        const VertexFormat::Element& e = vertexFormat.getElement(i);
 
         gameplay::VertexAttribute attrib;
 
@@ -156,7 +176,7 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effec
                 name += "0";
                 attrib = effect->getVertexAttribute(name.c_str());
             }
-            break;
+            break; 
         case VertexFormat::TEXCOORD1:
         case VertexFormat::TEXCOORD2:
         case VertexFormat::TEXCOORD3:
@@ -179,24 +199,22 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effec
         }
         else
         {
-            b->setVertexAttribPointer(attrib, (GLint)e.size, GL_FLOAT, GL_FALSE, (GLsizei)vertexFormat->getVertexSize(), offset);
+            void* pointer = vertexPointer ? (void*)(((unsigned char*)vertexPointer) + offset) : (void*)offset;
+            b->setVertexAttribPointer(attrib, (GLint)e.size, GL_FLOAT, GL_FALSE, (GLsizei)vertexFormat.getVertexSize(), pointer);
         }
 
         offset += e.size * sizeof(float);
     }
 
-    if (glBindVertexArray)
+    if (b->_handle)
     {
         GL_ASSERT( glBindVertexArray(0) );
     }
 
-    // Add the new vertex attribute binding to the cache
-    __vertexAttributeBindingCache.push_back(b);
-
     return b;
 }
 
-void VertexAttributeBinding::setVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalize, GLsizei stride, unsigned int offset)
+void VertexAttributeBinding::setVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalize, GLsizei stride, void* pointer)
 {
     assert(indx < (GLuint)__maxVertexAttribs);
 
@@ -204,7 +222,7 @@ void VertexAttributeBinding::setVertexAttribPointer(GLuint indx, GLint size, GLe
     {
         // Hardware mode
         GL_ASSERT( glEnableVertexAttribArray(indx) );
-        GL_ASSERT( glVertexAttribPointer(indx, size, type, normalize, stride, (const GLvoid*)offset) );
+        GL_ASSERT( glVertexAttribPointer(indx, size, type, normalize, stride, pointer) );
     }
     else
     {
@@ -214,7 +232,7 @@ void VertexAttributeBinding::setVertexAttribPointer(GLuint indx, GLint size, GLe
         _attributes[indx].type = type;
         _attributes[indx].normalized = normalize;
         _attributes[indx].stride = stride;
-        _attributes[indx].offset = offset;
+        _attributes[indx].pointer = pointer;
     }
 }
 
@@ -228,7 +246,14 @@ void VertexAttributeBinding::bind()
     else
     {
         // Software mode
-        GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, _mesh->getVertexBuffer()) );
+        if (_mesh)
+        {
+            GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, _mesh->getVertexBuffer()) );
+        }
+        else
+        {
+            GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
+        }
 
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         {
@@ -236,7 +261,7 @@ void VertexAttributeBinding::bind()
             if (a.enabled)
             {
                 GL_ASSERT( glEnableVertexAttribArray(i) );
-                GL_ASSERT( glVertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (const GLvoid*)a.offset) );
+                GL_ASSERT( glVertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, a.pointer) );
             }
         }
     }
@@ -252,7 +277,10 @@ void VertexAttributeBinding::unbind()
     else
     {
         // Software mode
-        GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
+        if (_mesh)
+        {
+            GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
+        }
 
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         {

+ 38 - 4
gameplay/src/VertexAttributeBinding.h

@@ -2,6 +2,7 @@
 #define VERTEXATTRIBUTEBINDING_H_
 
 #include "Ref.h"
+#include "VertexFormat.h"
 
 namespace gameplay
 {
@@ -13,14 +14,20 @@ class Effect;
  * Represents a binding between the vertex layout of a Mesh and the vertex
  * input attributes of a vertex shader (Effect).
  *
- * In a perfect world, this class would be a binding directly between
+ * In a perfect world, this class would always be a binding directly between
  * a unique VertexFormat and an Effect, where the VertexFormat is simply the
  * definition of the layout of any annoymous vertex buffer. However, the OpenGL
  * mechanism for setting up these bindings is Vertex Array Objects (VAOs).
  * OpenGL requires a separate VAO per vertex buffer object (VBO), rather than per
  * vertex layout definition. Therefore, although we would like to define this
  * binding between a VertexFormat and Effect, we are specifying the binding
- * between a Mesh and Effect to satisfy the OpenGL require of one VAO per VBO.
+ * between a Mesh and Effect to satisfy the OpenGL requirement of one VAO per VBO.
+ *
+ * Note that this class still does proivide a binding between a VertexFormat
+ * and an Effect, however this binding is actually a client-side binding and 
+ * should only be used when writing custom code that use client-side vertex
+ * arrays, since it is slower than the server-side VAOs used by OpenGL
+ * (when creating a VertexAttributeBinding between a Mesh and Effect).
  */
 class VertexAttributeBinding : public Ref
 {
@@ -29,11 +36,36 @@ public:
     /**
      * Creates a new VertexAttributeBinding between the given Mesh and Effect.
      *
+     * If a VertexAttributeBinding matching the specified Mesh and Effect already
+     * exists, it will be returned. Otherwise, a new VertexAttributeBinding will
+     * be returned. If OpenGL VAOs are enabled, the a new VAO will be created and
+     * stored in the returned VertexAttributeBinding, otherwise a client-side
+     * array of vertex attribute bindings will be stored.
+     *
      * @param mesh The mesh.
      * @param effect The effect.
+     * 
+     * @return A VertexAttributeBinding for the requested parameters.
      */
     static VertexAttributeBinding* create(Mesh* mesh, Effect* effect);
 
+    /**
+     * Creates a client-side vertex attribute binding.
+     *
+     * This method creates a client-side vertex attribute binding, which is simply a cached
+     * set of parameters that need to be passed to the renderer to setup vertex attribute
+     * bindings between a vertex buffer and a vertex shader. The specified vertexPointer is
+     * a client-side block of memory that contains the vertices to be send to the renderer,
+     * formatted as indiicated in the specified vertexFormat parameter.
+     *
+     * @param vertexFormat The vertex format.
+     * @param vertexPointer Pointer to beginning of client-side vertex array.
+     * @param effect The effect.
+     * 
+     * @return A VertexAttributeBinding for the requested parameters.
+     */
+    static VertexAttributeBinding* create(const VertexFormat& vertexFormat, void* vertexPointer, Effect* effect);
+
     /**
      * Binds this vertex array object.
      */
@@ -54,7 +86,7 @@ private:
         GLenum type;
         bool normalized;
         unsigned int stride;
-        unsigned int offset;
+        void* pointer;
     };
 
     /**
@@ -67,7 +99,9 @@ private:
      */
     ~VertexAttributeBinding();
 
-    void setVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalize, GLsizei stride, unsigned int offset);
+    static VertexAttributeBinding* create(Mesh* mesh, const VertexFormat& vertexFormat, void* vertexPointer, Effect* effect);
+
+    void setVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalize, GLsizei stride, void* pointer);
 
     GLuint _handle;
     VertexAttribute* _attributes;

+ 43 - 110
gameplay/src/VertexFormat.cpp

@@ -4,132 +4,35 @@
 namespace gameplay
 {
 
-static std::vector<VertexFormat*> __vertexFormatCache;
-
-VertexFormat::VertexFormat() :
-    _elements(NULL), _elementCount(0), _vertexSize(0)
-{
-}
-
-VertexFormat::~VertexFormat()
+VertexFormat::VertexFormat(const Element* elements, unsigned int elementCount)
+    : _vertexSize(0)
 {
-    // Remove from the vertex format cache.
-    std::vector<VertexFormat*>::iterator itr = find(__vertexFormatCache.begin(), __vertexFormatCache.end(), this);
-    if (itr != __vertexFormatCache.end())
-    {
-        __vertexFormatCache.erase(itr);
-    }
-
-    SAFE_DELETE_ARRAY(_elements);
-}
-
-VertexFormat* VertexFormat::create(const Element* elements, unsigned int elementCount)
-{
-    VertexFormat* format;
-
-    // Search for an existing VertexFormat with matching elements.
-    for (unsigned int i = 0, count = __vertexFormatCache.size(); i < count; ++i)
-    {
-        format = __vertexFormatCache[i];
-        if (format->_elementCount == elementCount)
-        {
-            bool match = true;
-            for (unsigned int j = 0; j < format->_elementCount; ++j)
-            {
-                const Element& e1 = format->_elements[j];
-                const Element& e2 = elements[j];
-
-                // Compare elements properties.
-                if (e1.size != e2.size || e1.usage != e2.usage)
-                {
-                    // Not a match.
-                    match = false;
-                    break;
-                }
-            }
-            if (match)
-            {
-                // Found a match, return it.
-                format->addRef();
-                return format;
-            }
-        }
-    }
-
-    // Create a new vertex format.
-    format = new VertexFormat();
-    format->_elementCount = elementCount;
-    format->_elements = new Element[elementCount];
-    format->_vertexSize = 0;
-    bool hasPosition = false;
+    // Copy elements and compute vertex size
     for (unsigned int i = 0; i < elementCount; ++i)
     {
-        // Validate size (must be between 1-4).
-        assert(elements[i].size >= 1 && elements[i].size <= 4);
-        if (elements[i].size < 1 || elements[i].size > 4)
-        {
-            SAFE_DELETE(format);
-            return NULL;
-        }
-
-        // Validate usage.
-        switch (elements[i].usage)
-        {
-        case POSITION:
-            hasPosition = true;
-            break;
-        case NORMAL:
-        case COLOR:
-        case TANGENT:
-        case BINORMAL:
-        case BLENDWEIGHTS:
-        case BLENDINDICES:
-        case TEXCOORD0:
-        case TEXCOORD1:
-        case TEXCOORD2:
-        case TEXCOORD3:
-        case TEXCOORD4:
-        case TEXCOORD5:
-        case TEXCOORD6:
-        case TEXCOORD7:
-            break;
-        default:
-            assert(0); // invalid usage
-            SAFE_DELETE(format);
-            return NULL;
-        }
-
-        // Copy element.
-        memcpy(&format->_elements[i], &elements[i], sizeof(Element));
-        format->_vertexSize += elements[i].size * sizeof(float);
-
-        if (elements[i].usage == POSITION)
-        {
-            hasPosition = true;
-        }
-    }
+        // Copy element
+        Element element;
+        memcpy(&element, &elements[i], sizeof(Element));
+        _elements.push_back(element);
 
-    // All formats must include 1 POSITION.
-    assert(hasPosition);
-    if (!hasPosition)
-    {
-        SAFE_DELETE(format);
-        return NULL;
+        _vertexSize += element.size * sizeof(float);
     }
+}
 
-    return format;
+VertexFormat::~VertexFormat()
+{
 }
 
 const VertexFormat::Element& VertexFormat::getElement(unsigned int index) const
 {
-    assert(index < _elementCount);
+    assert(index < _elements.size());
 
     return _elements[index];
 }
 
 unsigned int VertexFormat::getElementCount() const
 {
-    return _elementCount;
+    return _elements.size();
 }
 
 unsigned int VertexFormat::getVertexSize() const
@@ -137,6 +40,26 @@ unsigned int VertexFormat::getVertexSize() const
     return _vertexSize;
 }
 
+bool VertexFormat::operator == (const VertexFormat& f) const
+{
+    if (_elements.size() != f._elements.size())
+        return false;
+
+    for (unsigned int i = 0, count = _elements.size(); i < count; ++i)
+    {
+        if (_elements[i] != f._elements[i])
+            return false;
+    }
+
+    return true;
+}
+
+
+bool VertexFormat::operator != (const VertexFormat& f) const
+{
+    return !(*this == f);
+}
+
 VertexFormat::Element::Element() :
     usage(POSITION), size(0)
 {
@@ -147,4 +70,14 @@ VertexFormat::Element::Element(Usage usage, unsigned int size) :
 {
 }
 
+bool VertexFormat::Element::operator == (const VertexFormat::Element& e) const
+{
+    return (size == e.size && usage == e.usage);
+}
+
+bool VertexFormat::Element::operator != (const VertexFormat::Element& e) const
+{
+    return !(*this == e);
+}
+
 }

+ 43 - 15
gameplay/src/VertexFormat.h

@@ -1,8 +1,6 @@
 #ifndef VERTEXFORMAT_H_
 #define VERTEXFORMAT_H_
 
-#include "Ref.h"
-
 namespace gameplay
 {
 
@@ -12,7 +10,7 @@ namespace gameplay
  * A VertexFormat is immutable and cannot be changed
  * once created.
  */
-class VertexFormat : public Ref
+class VertexFormat
 {
 public:
 
@@ -71,17 +69,40 @@ public:
          * @param size The number of float values in the vertex element.
          */
         Element(Usage usage, unsigned int size);
+
+        /**
+         * Compares two vertex elements for equality.
+         *
+         * @param e The vertex element to compare.
+         *
+         * @return true if this element matches the specified one, false otherwise.
+         */
+        bool operator == (const Element& e) const;
+
+        /**
+         * Compares to vertex elements for inequality.
+         *
+         * @param e The vertex element to compare.
+         *
+         * @return true if this element does not matche the specified one, false otherwise.
+         */
+        bool operator != (const Element& e) const;
     };
 
     /**
-     * Returns a unique VertexFormat for the request vertex element layout.
+     * Constructs a new vertex format for the requested vertex element layout.
+     *
+     * The passed in element array is copied into the new VertexFormat.
      *
      * @param elements The list of vertex elements defining the vertex format.
      * @param elementCount The number of items in the elements array.
-     * 
-     * @return A unique VertexFormat object.
      */
-    static VertexFormat* create(const Element* elements, unsigned int elementCount);
+    VertexFormat(const Element* elements, unsigned int elementCount);
+
+    /**
+     * Destructor.
+     */
+    ~VertexFormat();
 
     /**
      * Returns the vertex element at the specified index.
@@ -102,20 +123,27 @@ public:
      */
     unsigned int getVertexSize() const;
 
-private:
-
     /**
-     * Constructor.
+     * Compares two vertex formats for equality.
+     *
+     * @param f The vertex format to compare.
+     *
+     * @return true if the elements in this VertexFormat matches the specified one, false otherwise.
      */
-    VertexFormat();
+    bool operator == (const VertexFormat& f) const;
 
     /**
-     * Destructor.
+     * Compares to vertex formats for inequality.
+     *
+     * @param f The vertex format to compare.
+     *
+     * @return true if the elements in this VertexFormat are not equal to the specified one, false otherwise.
      */
-    ~VertexFormat();
+    bool operator != (const VertexFormat& f) const;
+
+private:
 
-    Element* _elements;
-    unsigned int _elementCount;
+    std::vector<Element> _elements;
     unsigned int _vertexSize;
 };