ソースを参照

Merge pull request #96 from blackberry-gaming/next-sgrenier

Next sgrenier
Steve Grenier 14 年 前
コミット
e13d615c31

+ 3 - 0
gameplay/gameplay.vcxproj

@@ -42,6 +42,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" />
@@ -113,6 +114,7 @@
     <ClInclude Include="src\Keyboard.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" />
@@ -184,6 +186,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

@@ -219,6 +219,9 @@
     <ClCompile Include="src\DepthStencilTarget.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\MeshBatch.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -428,6 +431,9 @@
     <ClInclude Include="src\Touch.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\MeshBatch.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -508,6 +514,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

@@ -219,6 +219,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;
 };