Pārlūkot izejas kodu

Error handling improvement (5 of 7 - Graphics).
Minor error handling cleanup to AudioSource::create.

Work on issue #330.

Chris Culy 13 gadi atpakaļ
vecāks
revīzija
02926c60b1

+ 1 - 3
gameplay/src/AudioSource.cpp

@@ -32,16 +32,14 @@ AudioSource::~AudioSource()
 
 AudioSource* AudioSource::create(const char* url)
 {
-    GP_ASSERT(url);
-
     // Load from a .audio file.
     std::string pathStr = url;
     if (pathStr.find(".audio") != pathStr.npos)
     {
         Properties* properties = Properties::create(url);
-        GP_ASSERT(properties);
         if (properties == NULL)
         {
+            GP_ERROR("Failed to create audio source from .audio file.");
             return NULL;
         }
 

+ 10 - 7
gameplay/src/DepthStencilTarget.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 static std::vector<DepthStencilTarget*> __depthStencilTargets;
 
 DepthStencilTarget::DepthStencilTarget(const char* id, Format format)
-    : _id(id), _format(format), _depthTexture(NULL), _stencilBuffer(0)
+    : _id(id ? id : ""), _format(format), _depthTexture(NULL), _stencilBuffer(0)
 {
 }
 
@@ -38,29 +38,29 @@ DepthStencilTarget* DepthStencilTarget::create(const char* id, Format format, un
         return NULL;
     }
 
-    // Create stencil renderbuffer if format is DEPTH24_STENCIL8
+    // Create stencil renderbuffer if format is DEPTH24_STENCIL8.
     RenderBufferHandle stencilBuffer = 0;
     if (format == DEPTH24_STENCIL8)
     {
-        // Backup the existing render buffer
+        // Backup the existing render buffer.
         GLint currentRbo = 0;
         GL_ASSERT( glGetIntegerv(GL_RENDERBUFFER_BINDING, &currentRbo) );
 
-        // Create the new render buffer
+        // Create the new render buffer.
         GL_ASSERT( glGenRenderbuffers(1, &stencilBuffer) );
         GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer) );
         GL_ASSERT( glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height) );
 
-        // Restore the old render buffer
+        // Restore the old render buffer.
         GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, currentRbo) );
     }
 
-    // Create the depth stencil target
+    // Create the depth stencil target.
     DepthStencilTarget* depthStencilTarget = new DepthStencilTarget(id, format);
     depthStencilTarget->_depthTexture = depthTexture;
     depthStencilTarget->_stencilBuffer = stencilBuffer;
 
-    // Add it to the cache
+    // Add it to the cache.
     __depthStencilTargets.push_back(depthStencilTarget);
 
     return depthStencilTarget;
@@ -68,11 +68,14 @@ DepthStencilTarget* DepthStencilTarget::create(const char* id, Format format, un
 
 DepthStencilTarget* DepthStencilTarget::getDepthStencilTarget(const char* id)
 {
+    GP_ASSERT(id);
+
     // Search the vector for a matching ID.
     std::vector<DepthStencilTarget*>::const_iterator it;
     for (it = __depthStencilTargets.begin(); it < __depthStencilTargets.end(); it++)
     {
         DepthStencilTarget* dst = *it;
+        GP_ASSERT(dst);
         if (strcmp(id, dst->getID()) == 0)
         {
             return dst;

+ 31 - 10
gameplay/src/Effect.cpp

@@ -42,6 +42,9 @@ Effect::~Effect()
 
 Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const char* defines)
 {
+    GP_ASSERT(vshPath);
+    GP_ASSERT(fshPath);
+
     // Search the effect cache for an identical effect that is already loaded.
     std::string uniqueId = vshPath;
     uniqueId += ';';
@@ -55,6 +58,7 @@ Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const c
     if (itr != __effectCache.end())
     {
         // Found an exiting effect with this id, so increase its ref count and return it.
+        GP_ASSERT(itr->second);
         itr->second->addRef();
         return itr->second;
     }
@@ -63,11 +67,13 @@ Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const c
     char* vshSource = FileSystem::readAll(vshPath);
     if (vshSource == NULL)
     {
+        GP_ERROR("Failed to read vertex shader from file '%s'.", vshPath);
         return NULL;
     }
     char* fshSource = FileSystem::readAll(fshPath);
     if (fshSource == NULL)
     {
+        GP_ERROR("Failed to read fragment shader from file '%s'.", fshPath);
         SAFE_DELETE_ARRAY(vshSource);
         return NULL;
     }
@@ -79,7 +85,7 @@ Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const c
 
     if (effect == NULL)
     {
-        GP_ERROR("Failed to create effect from shaders: %s, %s", vshPath, fshPath);
+        GP_ERROR("Failed to create effect from shaders '%s', '%s'.", vshPath, fshPath);
     }
     else
     {
@@ -98,6 +104,9 @@ Effect* Effect::createFromSource(const char* vshSource, const char* fshSource, c
 
 Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, const char* fshPath, const char* fshSource, const char* defines)
 {
+    GP_ASSERT(vshSource);
+    GP_ASSERT(fshSource);
+
     const unsigned int SHADER_SOURCE_LENGTH = 3;
     const GLchar* shaderSource[SHADER_SOURCE_LENGTH];
     char* infoLog = NULL;
@@ -130,7 +139,7 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
             GL_ASSERT( glGetShaderInfoLog(vertexShader, length, NULL, infoLog) );
             infoLog[length-1] = '\0';
         }
-        GP_ERROR("Compile failed for vertex shader (%s): %s", vshPath == NULL ? "NULL" : vshPath, infoLog == NULL ? "" : infoLog);
+        GP_ERROR("Compile failed for vertex shader '%s' with error '%s'.", vshPath == NULL ? "NULL" : vshPath, infoLog == NULL ? "" : infoLog);
         SAFE_DELETE_ARRAY(infoLog);
 
         // Clean up.
@@ -140,14 +149,6 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
     }
 
     // Compile the fragment shader.
-    definesStr = (defines == NULL) ? "" : defines;
-#ifdef OPENGL_ES
-    if (defines && strlen(defines) != 0)
-        definesStr += "\n";
-    definesStr+= OPENGL_ES_DEFINE;
-#endif
-    shaderSource[0] = definesStr.c_str();
-    shaderSource[1] = "\n";
     shaderSource[2] = fshSource;
     GL_ASSERT( fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) );
     GL_ASSERT( glShaderSource(fragmentShader, SHADER_SOURCE_LENGTH, shaderSource, NULL) );
@@ -327,67 +328,87 @@ unsigned int Effect::getUniformCount() const
 
 void Effect::setValue(Uniform* uniform, float value)
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform1f(uniform->_location, value) );
 }
 
 void Effect::setValue(Uniform* uniform, const float* values, unsigned int count)
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniform1fv(uniform->_location, count, values) );
 }
 
 void Effect::setValue(Uniform* uniform, int value)
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform1i(uniform->_location, value) );
 }
 
 void Effect::setValue(Uniform* uniform, const int* values, unsigned int count)
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniform1iv(uniform->_location, count, values) );
 }
 
 void Effect::setValue(Uniform* uniform, const Matrix& value)
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniformMatrix4fv(uniform->_location, 1, GL_FALSE, value.m) );
 }
 
 void Effect::setValue(Uniform* uniform, const Matrix* values, unsigned int count)
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniformMatrix4fv(uniform->_location, count, GL_FALSE, (GLfloat*)values) );
 }
 
 void Effect::setValue(Uniform* uniform, const Vector2& value)
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform2f(uniform->_location, value.x, value.y) );
 }
 
 void Effect::setValue(Uniform* uniform, const Vector2* values, unsigned int count)
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniform2fv(uniform->_location, count, (GLfloat*)values) );
 }
 
 void Effect::setValue(Uniform* uniform, const Vector3& value)
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform3f(uniform->_location, value.x, value.y, value.z) );
 }
 
 void Effect::setValue(Uniform* uniform, const Vector3* values, unsigned int count)
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniform3fv(uniform->_location, count, (GLfloat*)values) );
 }
 
 void Effect::setValue(Uniform* uniform, const Vector4& value)
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform4f(uniform->_location, value.x, value.y, value.z, value.w) );
 }
 
 void Effect::setValue(Uniform* uniform, const Vector4* values, unsigned int count)
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniform4fv(uniform->_location, count, (GLfloat*)values) );
 }
 
 void Effect::setValue(Uniform* uniform, const Texture::Sampler* sampler)
 {
+    GP_ASSERT(uniform);
     GP_ASSERT(uniform->_type == GL_SAMPLER_2D);
+    GP_ASSERT(sampler);
 
     GL_ASSERT( glActiveTexture(GL_TEXTURE0 + uniform->_index) );
 

+ 68 - 4
gameplay/src/Font.cpp

@@ -66,10 +66,13 @@ Font::~Font()
 
 Font* Font::create(const char* path, const char* id)
 {
+    GP_ASSERT(path);
+
     // Search the font cache for a font with the given path and ID.
     for (unsigned int i = 0, count = __fontCache.size(); i < count; ++i)
     {
         Font* f = __fontCache[i];
+        GP_ASSERT(f);
         if (f->_path == path && (id == NULL || f->_id == id))
         {
             // Found a match.
@@ -82,17 +85,18 @@ Font* Font::create(const char* path, const char* id)
     Bundle* bundle = Bundle::create(path);
     if (bundle == NULL)
     {
+        GP_ERROR("Failed to load font bundle '%s'.", path);
         return NULL;
     }
 
     Font* font = NULL;
-
     if (id == NULL)
     {
-        // Get the ID of the first/only object in the bundle (assume it's a Font).
+        // Get the ID of the first object in the bundle (assume it's a Font).
         const char* id;
-        if (bundle->getObjectCount() != 1 || (id = bundle->getObjectID(0)) == NULL)
+        if ((id = bundle->getObjectID(0)) == NULL)
         {
+            GP_ERROR("Failed to load font without explicit id; the first object in the font bundle has a null id.");
             return NULL;
         }
 
@@ -118,6 +122,10 @@ Font* Font::create(const char* path, const char* id)
 
 Font* Font::create(const char* family, Style style, unsigned int size, Glyph* glyphs, int glyphCount, Texture* texture)
 {
+    GP_ASSERT(family);
+    GP_ASSERT(glyphs);
+    GP_ASSERT(texture);
+
     // Create the effect for the font's sprite batch.
     if (__fontEffect == NULL)
     {
@@ -171,14 +179,21 @@ unsigned int Font::getSize()
 
 void Font::begin()
 {
+    GP_ASSERT(_batch);
     _batch->begin();
 }
 
 Font::Text* Font::createText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify,
     bool wrap, bool rightToLeft, const Rectangle* clip)
 {
+    GP_ASSERT(text);
+    GP_ASSERT(clip);
+    GP_ASSERT(_glyphs);
+    GP_ASSERT(_batch);
+
     if (size == 0)
         size = _size;
+    GP_ASSERT(_size);
     float scale = (float)size / _size;
     const int length = strlen(text);
     int yPos = area.y;
@@ -189,6 +204,8 @@ Font::Text* Font::createText(const char* text, const Rectangle& area, const Vect
     getMeasurementInfo(text, area, size, justify, wrap, rightToLeft, &xPositions, &yPos, &lineLengths);
 
     Text* batch = new Text(text);
+    GP_ASSERT(batch->_vertices);
+    GP_ASSERT(batch->_indices);
 
     int xPos = area.x;
     std::vector<int>::const_iterator xPositionsIt = xPositions.begin();
@@ -400,6 +417,9 @@ Font::Text* Font::createText(const char* text, const Rectangle& area, const Vect
 
 void Font::drawText(Text* text)
 {
+    GP_ASSERT(_batch);
+    GP_ASSERT(text->_vertices);
+    GP_ASSERT(text->_indices);
     _batch->draw(text->_vertices, text->_vertexCount, text->_indices, text->_indexCount);
 }
 
@@ -407,6 +427,8 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
 {
     if (size == 0)
         size = _size;
+    GP_ASSERT(_size);
+    GP_ASSERT(text);
     float scale = (float)size / _size;
     const char* cursor = NULL;
 
@@ -469,6 +491,8 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
             iteration = 1;
         }
 
+        GP_ASSERT(_glyphs);
+        GP_ASSERT(_batch);
         for (int i = startIndex; i < length && i >= 0; i += iteration)
         {
             char c = 0;
@@ -521,8 +545,11 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
 
 void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify, bool wrap, bool rightToLeft, const Rectangle* clip)
 {
+    GP_ASSERT(text);
+
     if (size == 0)
         size = _size;
+    GP_ASSERT(_size);
     float scale = (float)size / _size;
     const int length = strlen(text);
     int yPos = area.y;
@@ -612,6 +639,8 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
             break;
         }
 
+        GP_ASSERT(_glyphs);
+        GP_ASSERT(_batch);
         for (int i = startIndex; i < (int)tokenLength && i >= 0; i += iteration)
         {
             char c = token[i];
@@ -712,11 +741,17 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
 
 void Font::end()
 {
+    GP_ASSERT(_batch);
     _batch->end();
 }
 
 void Font::measureText(const char* text, unsigned int size, unsigned int* width, unsigned int* height)
 {
+    GP_ASSERT(_size);
+    GP_ASSERT(text);
+    GP_ASSERT(width);
+    GP_ASSERT(height);
+
     float scale = (float)size / _size;
     const int length = strlen(text);
     const char* token = text;
@@ -746,6 +781,10 @@ void Font::measureText(const char* text, unsigned int size, unsigned int* width,
 
 void Font::measureText(const char* text, const Rectangle& clip, unsigned int size, Rectangle* out, Justify justify, bool wrap, bool ignoreClip)
 {
+    GP_ASSERT(_size);
+    GP_ASSERT(text);
+    GP_ASSERT(out);
+
     float scale = (float)size / _size;
     Justify vAlign = static_cast<Justify>(justify & 0xF0);
     if (vAlign == 0)
@@ -1068,6 +1107,10 @@ void Font::measureText(const char* text, const Rectangle& clip, unsigned int siz
 void Font::getMeasurementInfo(const char* text, const Rectangle& area, unsigned int size, Justify justify, bool wrap, bool rightToLeft,
         std::vector<int>* xPositions, int* yPosition, std::vector<unsigned int>* lineLengths)
 {
+    GP_ASSERT(_size);
+    GP_ASSERT(text);
+    GP_ASSERT(yPosition);
+
     float scale = (float)size / _size;
 
     Justify vAlign = static_cast<Justify>(justify & 0xF0);
@@ -1260,6 +1303,10 @@ void Font::getLocationAtIndex(const char* text, const Rectangle& clip, unsigned
 int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
                                       const int destIndex, Justify justify, bool wrap, bool rightToLeft)
 {
+    GP_ASSERT(_size);
+    GP_ASSERT(text);
+    GP_ASSERT(outLocation);
+
     unsigned int charIndex = 0;
 
     // Essentially need to measure text until we reach inLocation.
@@ -1371,6 +1418,7 @@ int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned i
             break;
         }
 
+        GP_ASSERT(_glyphs);
         for (int i = startIndex; i < (int)tokenLength && i >= 0; i += iteration)
         {
             char c = token[i];
@@ -1485,6 +1533,9 @@ int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned i
 
 unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale)
 {
+    GP_ASSERT(token);
+    GP_ASSERT(_glyphs);
+
     // Calculate width of word or line.
     unsigned int tokenWidth = 0;
     for (unsigned int i = 0; i < length; ++i)
@@ -1514,6 +1565,9 @@ unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigne
 
 unsigned int Font::getReversedTokenLength(const char* token, const char* bufStart)
 {
+    GP_ASSERT(token);
+    GP_ASSERT(bufStart);
+
     const char* cursor = token;
     char c = cursor[0];
     unsigned int length = 0;
@@ -1537,6 +1591,13 @@ int Font::handleDelimiters(const char** token, const unsigned int size, const in
                           std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd, unsigned int* charIndex,
                           const Vector2* stopAtPosition, const int currentIndex, const int destIndex)
 {
+    GP_ASSERT(token);
+    GP_ASSERT(*token);
+    GP_ASSERT(xPos);
+    GP_ASSERT(yPos);
+    GP_ASSERT(lineLength);
+    GP_ASSERT(xPositionsIt);
+
     char delimiter = *token[0];
     bool nextLine = true;
     while (delimiter == ' ' ||
@@ -1615,15 +1676,18 @@ void Font::addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Jus
     int hWhitespace = area.width - lineWidth;
     if (hAlign == ALIGN_HCENTER)
     {
+        GP_ASSERT(xPositions);
         (*xPositions).push_back(area.x + hWhitespace / 2);
     }
     else if (hAlign == ALIGN_RIGHT)
     {
+        GP_ASSERT(xPositions);
         (*xPositions).push_back(area.x + hWhitespace);
     }
 
     if (rightToLeft)
     {
+        GP_ASSERT(lineLengths);
         (*lineLengths).push_back(lineLength);
     }
 }
@@ -1709,7 +1773,7 @@ Font::Justify Font::getJustify(const char* justify)
     return Font::ALIGN_TOP_LEFT;
 }
 
-Font::Text::Text(const char* text) : _text(text), _vertexCount(0), _vertices(NULL), _indexCount(0), _indices(NULL)
+Font::Text::Text(const char* text) : _text(text ? text : ""), _vertexCount(0), _vertices(NULL), _indexCount(0), _indices(NULL)
 {
     const int length = strlen(text);
     _vertices = new SpriteBatch::SpriteVertex[length * 4];

+ 19 - 9
gameplay/src/FrameBuffer.cpp

@@ -55,7 +55,7 @@ FrameBuffer* FrameBuffer::create(const char* id)
     memset(renderTargets, 0, sizeof(RenderTarget*) * __maxRenderTargets);
 
     // Create the new frame buffer
-    FrameBuffer* frameBuffer = new FrameBuffer(id ? id : "");
+    FrameBuffer* frameBuffer = new FrameBuffer(id);
     frameBuffer->_handle = handle;
     frameBuffer->_renderTargets = renderTargets;
 
@@ -67,21 +67,23 @@ FrameBuffer* FrameBuffer::create(const char* id)
 
 FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned int height)
 {
-    // Create RenderTarget with same ID
+    // Create RenderTarget with same ID.
     RenderTarget* renderTarget = RenderTarget::create(id, width, height);
     if (renderTarget == NULL)
     {
+        GP_ERROR("Failed to create render target for frame buffer.");
         return NULL;
     }
 
-    // Create the frame buffer
+    // Create the frame buffer.
     FrameBuffer* frameBuffer = create(id);
     if (frameBuffer == NULL)
     {
+        GP_ERROR("Failed to create frame buffer.");
         return NULL;
     }
 
-    // Add the render target as the first color attachment
+    // Add the render target as the first color attachment.
     frameBuffer->setRenderTarget(renderTarget);
     SAFE_RELEASE(renderTarget);
 
@@ -90,11 +92,14 @@ FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned in
 
 FrameBuffer* FrameBuffer::getFrameBuffer(const char* id)
 {
+    GP_ASSERT(id);
+
     // Search the vector for a matching ID.
     std::vector<FrameBuffer*>::const_iterator it;
     for (it = __frameBuffers.begin(); it < __frameBuffers.end(); it++)
     {
         FrameBuffer* fb = *it;
+        GP_ASSERT(fb);
         if (strcmp(id, fb->getID()) == 0)
         {
             return fb;
@@ -128,10 +133,11 @@ unsigned int FrameBuffer::getMaxRenderTargets()
 void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
 {
     GP_ASSERT(index < __maxRenderTargets);
+    GP_ASSERT(_renderTargets);
 
     if (_renderTargets[index] == target)
     {
-        // No change
+        // No change.
         return;
     }
 
@@ -152,6 +158,7 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
         // Now set this target as the color attachment corresponding to index.
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
         GLenum attachment = GL_COLOR_ATTACHMENT0 + index;
+        GP_ASSERT(_renderTargets[index]->getTexture());
         GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, _renderTargets[index]->getTexture()->getHandle(), 0) );
 
         // Restore the FBO binding
@@ -161,6 +168,7 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
 
 RenderTarget* FrameBuffer::getRenderTarget(unsigned int index) const
 {
+    GP_ASSERT(_renderTargets);
     if (index < __maxRenderTargets)
     {
         return _renderTargets[index];
@@ -176,24 +184,26 @@ void FrameBuffer::setDepthStencilTarget(DepthStencilTarget* target)
         return; // No change
     }
 
-    // Release our existing depth stencil target
+    // Release our existing depth stencil target.
     SAFE_RELEASE(_depthStencilTarget);
 
     _depthStencilTarget = target;
 
     if (target)
     {
-        // The FrameBuffer now owns this DepthStencilTarget
+        // The FrameBuffer now owns this DepthStencilTarget.
         target->addRef();
 
-        // Store the current FBO binding so we can restore it
+        // Store the current FBO binding so we can restore it.
         GLint currentFbo;
         GL_ASSERT( glGetIntegerv(GL_FRAMEBUFFER_BINDING, &currentFbo) );
 
         // Now set this target as the color attachment corresponding to index.
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
 
-        // Bind the depth texture
+        // Bind the depth texture.
+        GP_ASSERT(_depthStencilTarget);
+        GP_ASSERT(_depthStencilTarget->getTexture());
         GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthStencilTarget->getTexture()->getHandle(), 0) );
 
         // If the taget has a stencil buffer, bind that as well

+ 32 - 8
gameplay/src/Image.cpp

@@ -7,10 +7,13 @@ namespace gameplay
 
 Image* Image::create(const char* path)
 {
+    GP_ASSERT(path);
+
     // Open the file.
     FILE* fp = FileSystem::openFile(path, "rb");
     if (fp == NULL)
     {
+        GP_ERROR("Failed to open image file '%s'.", path);
         return NULL;
     }
 
@@ -18,8 +21,11 @@ Image* Image::create(const char* path)
     unsigned char sig[8];
     if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0)
     {
-        GP_ERROR("Texture is not a valid PNG: %s", path);
-        fclose(fp);
+        GP_ERROR("Failed to load file '%s'; not a valid PNG.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close image file '%s'.", path);
+        }
         return NULL;
     }
 
@@ -27,7 +33,11 @@ Image* Image::create(const char* path)
     png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
     if (png == NULL)
     {
-        fclose(fp);
+        GP_ERROR("Failed to create PNG structure for reading PNG file '%s'.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close image file '%s'.", path);
+        }
         return NULL;
     }
 
@@ -35,7 +45,11 @@ Image* Image::create(const char* path)
     png_infop info = png_create_info_struct(png);
     if (info == NULL)
     {
-        fclose(fp);
+        GP_ERROR("Failed to create PNG info structure for PNG file '%s'.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close image file '%s'.", path);
+        }
         png_destroy_read_struct(&png, NULL, NULL);
         return NULL;
     }
@@ -43,7 +57,11 @@ Image* Image::create(const char* path)
     // Set up error handling (required without using custom error handlers above).
     if (setjmp(png_jmpbuf(png)))
     {
-        fclose(fp);
+        GP_ERROR("Failed to set up error handling for reading PNG file '%s'.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close image file '%s'.", path);
+        }
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
     }
@@ -73,8 +91,11 @@ Image* Image::create(const char* path)
         break;
 
     default:
-        GP_ERROR("Unsupported PNG color type (%d) for texture: %s", (int)colorType, path);
-        fclose(fp);
+        GP_ERROR("Unsupported PNG color type (%d) for image file '%s'.", (int)colorType, path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close image file '%s'.", path);
+        }
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
     }
@@ -93,7 +114,10 @@ Image* Image::create(const char* path)
 
     // Clean up.
     png_destroy_read_struct(&png, &info, NULL);
-    fclose(fp);
+    if (fclose(fp) != 0)
+    {
+        GP_ERROR("Failed to close image file '%s'.", path);
+    }
 
     return image;
 }

+ 32 - 4
gameplay/src/Light.cpp

@@ -36,6 +36,9 @@ Light::~Light()
     case SPOT:
         SAFE_DELETE(_spot);
         break;
+    default:
+        GP_ERROR("Invalid light type (%d).", _type);
+        break;
     }
 }
 
@@ -75,13 +78,16 @@ const Vector3& Light::getColor() const
     switch (_type)
     {
     case DIRECTIONAL:
+        GP_ASSERT(_directional);
         return _directional->color;
     case POINT:
+        GP_ASSERT(_point);
         return _point->color;
     case SPOT:
+        GP_ASSERT(_spot);
         return _spot->color;
     default:
-        GP_ASSERT(0);
+        GP_ERROR("Unsupported light type (%d).", _type);
         return Vector3::zero();
 
     }
@@ -92,14 +98,20 @@ void Light::setColor(const Vector3& color)
     switch (_type)
     {
     case DIRECTIONAL:
+        GP_ASSERT(_directional);
         _directional->color = color;
         break;
     case POINT:
+        GP_ASSERT(_point);
         _point->color = color;
         break;
     case SPOT:
+        GP_ASSERT(_spot);
         _spot->color = color;
         break;
+    default:
+        GP_ERROR("Unsupported light type (%d).", _type);
+        break;
     }
 }
 
@@ -110,11 +122,13 @@ float Light::getRange()  const
     switch (_type)
     {
     case POINT:
+        GP_ASSERT(_point);
         return _point->range;
     case SPOT:
+        GP_ASSERT(_spot);
         return _spot->range;
     default:
-        GP_ASSERT(0);
+        GP_ERROR("Unsupported light type (%d).", _type);
         return 0.0f;
     }
 }
@@ -126,13 +140,22 @@ void Light::setRange(float range)
     switch (_type)
     {
     case POINT:
+        GP_ASSERT(_point);
+        GP_ASSERT(range);
+
         _point->range = range;
         _point->rangeInverse = 1.0f / range;
         break;
     case SPOT:
+        GP_ASSERT(_spot);
+        GP_ASSERT(range);
+
         _spot->range = range;
         _spot->rangeInverse = 1.0f / range;
         break;
+    default:
+        GP_ERROR("Unsupported light type (%d).", _type);
+        break;
     }
 }
 
@@ -143,11 +166,13 @@ float Light::getRangeInverse() const
     switch (_type)
     {
     case POINT:
+        GP_ASSERT(_point);
         return _point->rangeInverse;
     case SPOT:
+        GP_ASSERT(_spot);
         return _spot->rangeInverse;
     default:
-        GP_ASSERT(0);
+        GP_ERROR("Unsupported light type (%d).", _type);
         return 0.0f;
     }
 }
@@ -211,7 +236,8 @@ Light* Light::clone(NodeCloneContext &context) const
         lightClone = createSpot(getColor(), getRange(), getInnerAngle(), getOuterAngle());
         break;
     default:
-        GP_ASSERT(false);
+        GP_ERROR("Unsupported light type (%d).", _type);
+        return NULL;
     }
     GP_ASSERT(lightClone);
 
@@ -230,12 +256,14 @@ Light::Directional::Directional(const Vector3& color)
 Light::Point::Point(const Vector3& color, float range)
     : color(color), range(range)
 {
+    GP_ASSERT(range);
     rangeInverse = 1.0f / range;
 }
 
 Light::Spot::Spot(const Vector3& color, float range, float innerAngle, float outerAngle)
     : color(color), range(range), innerAngle(innerAngle), outerAngle(outerAngle)
 {
+    GP_ASSERT(range);
     rangeInverse = 1.0f / range;
     innerAngleCos = cos(innerAngle);
     outerAngleCos = cos(outerAngle);

+ 66 - 26
gameplay/src/Material.cpp

@@ -34,13 +34,11 @@ Material::~Material()
 
 Material* Material::create(const char* url)
 {
-    GP_ASSERT(url);
-
-    // Load the material properties from file
+    // Load the material properties from file.
     Properties* properties = Properties::create(url);
-    GP_ASSERT(properties);
     if (properties == NULL)
     {
+        GP_ERROR("Failed to create material from file.");
         return NULL;
     }
 
@@ -53,9 +51,9 @@ Material* Material::create(const char* url)
 Material* Material::create(Properties* materialProperties)
 {
     // Check if the Properties is valid and has a valid namespace.
-    GP_ASSERT(materialProperties);
     if (!materialProperties || !(strcmp(materialProperties->getNamespace(), "material") == 0))
     {
+        GP_ERROR("Properties object must be non-null and have namespace equal to 'material'.");
         return NULL;
     }
 
@@ -70,16 +68,17 @@ Material* Material::create(Properties* materialProperties)
         {
             if (!loadTechnique(material, techniqueProperties))
             {
+                GP_ERROR("Failed to load technique for material.");
                 SAFE_RELEASE(material);
                 return NULL;
             }
         }
     }
 
-    // Load uniform value parameters for this material
+    // Load uniform value parameters for this material.
     loadRenderState(material, materialProperties);
 
-    // Set the current technique to the first found technique
+    // Set the current technique to the first found technique.
     if (material->getTechniqueCount() > 0)
     {
         material->setTechnique((unsigned int)0);
@@ -90,7 +89,9 @@ Material* Material::create(Properties* materialProperties)
 
 Material* Material::create(Effect* effect)
 {
-    // Create a new material with a single technique and pass for the given effect
+    GP_ASSERT(effect);
+
+    // Create a new material with a single technique and pass for the given effect.
     Material* material = new Material();
 
     Technique* technique = new Technique(NULL, material);
@@ -116,6 +117,7 @@ Material* Material::create(const char* vshPath, const char* fshPath, const char*
     Pass* pass = Pass::create(NULL, technique, vshPath, fshPath, defines);
     if (!pass)
     {
+        GP_ERROR("Failed to create pass for material.");
         SAFE_RELEASE(material);
         return NULL;
     }
@@ -134,6 +136,7 @@ Material* Material::clone(NodeCloneContext &context) const
     for (std::vector<Technique*>::const_iterator it = _techniques.begin(); it != _techniques.end(); ++it)
     {
         const Technique* technique = *it;
+        GP_ASSERT(technique);
         Technique* techniqueClone = technique->clone(material, context);
         material->_techniques.push_back(techniqueClone);
         if (_currentTechnique == technique)
@@ -152,15 +155,16 @@ unsigned int Material::getTechniqueCount() const
 Technique* Material::getTechnique(unsigned int index) const
 {
     GP_ASSERT(index < _techniques.size());
-
     return _techniques[index];
 }
 
 Technique* Material::getTechnique(const char* id) const
 {
+    GP_ASSERT(id);
     for (unsigned int i = 0, count = _techniques.size(); i < count; ++i)
     {
         Technique* t = _techniques[i];
+        GP_ASSERT(t);
         if (strcmp(t->getId(), id) == 0)
         {
             return t;
@@ -195,7 +199,10 @@ void Material::setTechnique(unsigned int index)
 
 bool Material::loadTechnique(Material* material, Properties* techniqueProperties)
 {
-    // Create a new technique
+    GP_ASSERT(material);
+    GP_ASSERT(techniqueProperties);
+
+    // Create a new technique.
     Technique* technique = new Technique(techniqueProperties->getId(), material);
 
     // Go through all the properties and create passes under this technique.
@@ -208,16 +215,17 @@ bool Material::loadTechnique(Material* material, Properties* techniqueProperties
             // Create and load passes.
             if (!loadPass(technique, passProperties))
             {
+                GP_ERROR("Failed to create pass for technique.");
                 SAFE_RELEASE(technique);
                 return false;
             }
         }
     }
 
-    // Load uniform value parameters for this technique
+    // Load uniform value parameters for this technique.
     loadRenderState(technique, techniqueProperties);
 
-    // Add the new technique to the material
+    // Add the new technique to the material.
     material->_techniques.push_back(technique);
 
     return true;
@@ -225,6 +233,9 @@ bool Material::loadTechnique(Material* material, Properties* techniqueProperties
 
 bool Material::loadPass(Technique* technique, Properties* passProperties)
 {
+    GP_ASSERT(passProperties);
+    GP_ASSERT(technique);
+
     // Fetch shader info required to create the effect of this technique.
     const char* vertexShaderPath = passProperties->getString("vertexShader");
     GP_ASSERT(vertexShaderPath);
@@ -244,17 +255,18 @@ bool Material::loadPass(Technique* technique, Properties* passProperties)
         define += "\n";
     }
 
-    // Create the pass
+    // Create the pass.
     Pass* pass = Pass::create(passProperties->getId(), technique, vertexShaderPath, fragmentShaderPath, define.c_str());
     if (!pass)
     {
+        GP_ERROR("Failed to create pass for technique.");
         return false;
     }
 
-    // Load render state
+    // Load render state.
     loadRenderState(pass, passProperties);
 
-    // Add the new pass to the technique
+    // Add the new pass to the technique.
     technique->_passes.push_back(pass);
 
     return true;
@@ -262,6 +274,8 @@ bool Material::loadPass(Technique* technique, Properties* passProperties)
 
 bool isMaterialKeyword(const char* str)
 {
+    GP_ASSERT(str);
+
     #define MATERIAL_KEYWORD_COUNT 3
     static const char* reservedKeywords[MATERIAL_KEYWORD_COUNT] =
     {
@@ -283,6 +297,7 @@ Texture::Filter parseTextureFilterMode(const char* str, Texture::Filter defaultV
 {
     if (str == NULL || strlen(str) == 0)
     {
+        GP_ERROR("Texture filter mode string must be non-null and non-empty.");
         return defaultValue;
     }
     else if (strcmp(str, "NEAREST") == 0)
@@ -309,13 +324,18 @@ Texture::Filter parseTextureFilterMode(const char* str, Texture::Filter defaultV
     {
         return Texture::LINEAR_MIPMAP_LINEAR;
     }
-    return defaultValue;
+    else
+    {
+        GP_ERROR("Unsupported texture filter mode string ('%s').", str);
+        return defaultValue;
+    }
 }
 
 Texture::Wrap parseTextureWrapMode(const char* str, Texture::Wrap defaultValue)
 {
     if (str == NULL || strlen(str) == 0)
     {
+        GP_ERROR("Texture wrap mode string must be non-null and non-empty.");
         return defaultValue;
     }
     else if (strcmp(str, "REPEAT") == 0)
@@ -326,12 +346,19 @@ Texture::Wrap parseTextureWrapMode(const char* str, Texture::Wrap defaultValue)
     {
         return Texture::CLAMP;
     }
-    return defaultValue;
+    else
+    {
+        GP_ERROR("Unsupported texture wrap mode string ('%s').", str);
+        return defaultValue;
+    }
 }
 
 void Material::loadRenderState(RenderState* renderState, Properties* properties)
 {
-    // Rewind the properties to start reading from the start
+    GP_ASSERT(renderState);
+    GP_ASSERT(properties);
+
+    // Rewind the properties to start reading from the start.
     properties->rewind();
 
     const char* name;
@@ -343,6 +370,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
         switch (properties->getType())
         {
         case Properties::NUMBER:
+            GP_ASSERT(renderState->getParameter(name));
             renderState->getParameter(name)->setValue(properties->getFloat());
             break;
         case Properties::VECTOR2:
@@ -350,6 +378,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
                 Vector2 vector2;
                 if (properties->getVector2(NULL, &vector2))
                 {
+                    GP_ASSERT(renderState->getParameter(name));
                     renderState->getParameter(name)->setValue(vector2);
                 }
             }
@@ -359,6 +388,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
                 Vector3 vector3;
                 if (properties->getVector3(NULL, &vector3))
                 {
+                    GP_ASSERT(renderState->getParameter(name));
                     renderState->getParameter(name)->setValue(vector3);
                 }
             }
@@ -368,6 +398,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
                 Vector4 vector4;
                 if (properties->getVector4(NULL, &vector4))
                 {
+                    GP_ASSERT(renderState->getParameter(name));
                     renderState->getParameter(name)->setValue(vector4);
                 }
             }
@@ -377,43 +408,51 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
                 Matrix matrix;
                 if (properties->getMatrix(NULL, &matrix))
                 {
+                    GP_ASSERT(renderState->getParameter(name));
                     renderState->getParameter(name)->setValue(matrix);
                 }
             }
             break;
         default:
             {
-                // Assume this is a parameter auto-binding
+                // Assume this is a parameter auto-binding.
                 renderState->setParameterAutoBinding(name, properties->getString());
             }
             break;
         }
     }
 
-    // Iterate through all child namespaces searching for samplers and render state blocks
+    // Iterate through all child namespaces searching for samplers and render state blocks.
     Properties* ns;
     while (ns = properties->getNextNamespace())
     {
         if (strcmp(ns->getNamespace(), "sampler") == 0)
         {
-            // Read the texture uniform name
+            // Read the texture uniform name.
             name = ns->getId();
             if (strlen(name) == 0)
-                continue; // missing texture uniform name
+            {
+                GP_ERROR("Texture sampler is missing required uniform name.");
+                continue;
+            }
 
-            // Get the texture path
+            // Get the texture path.
             const char* path = ns->getString("path");
             if (path == NULL || strlen(path) == 0)
-                continue; // missing texture path
+            {
+                GP_ERROR("Texture sampler '%s' is missing required image file path.", name);
+                continue;
+            }
 
-            // Read texture state (booleans default to 'false' if not present)
+            // Read texture state (booleans default to 'false' if not present).
             bool mipmap = ns->getBool("mipmap");
             Texture::Wrap wrapS = parseTextureWrapMode(ns->getString("wrapS"), Texture::REPEAT);
             Texture::Wrap wrapT = parseTextureWrapMode(ns->getString("wrapT"), Texture::REPEAT);
             Texture::Filter minFilter = parseTextureFilterMode(ns->getString("minFilter"), mipmap ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR);
             Texture::Filter magFilter = parseTextureFilterMode(ns->getString("magFilter"), Texture::LINEAR);
 
-            // Set the sampler parameter
+            // Set the sampler parameter.
+            GP_ASSERT(renderState->getParameter(name));
             Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path, mipmap);
             if (sampler)
             {
@@ -425,6 +464,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
         {
             while (name = ns->getNextProperty())
             {
+                GP_ASSERT(renderState->getStateBlock());
                 renderState->getStateBlock()->setState(name, ns->getString());
             }
         }

+ 52 - 11
gameplay/src/MaterialParameter.cpp

@@ -5,7 +5,7 @@ namespace gameplay
 {
 
 MaterialParameter::MaterialParameter(const char* name) :
-    _type(MaterialParameter::NONE), _count(1), _dynamic(false), _name(name), _uniform(NULL)
+    _type(MaterialParameter::NONE), _count(1), _dynamic(false), _name(name ? name : ""), _uniform(NULL)
 {
     clearValue();
 }
@@ -34,6 +34,9 @@ void MaterialParameter::clearValue()
         case MaterialParameter::METHOD:
             SAFE_RELEASE(_value.method);
             break;
+        default:
+            // Ignore all other cases.
+            break;
         }
 
         _dynamic = false;
@@ -49,6 +52,9 @@ void MaterialParameter::clearValue()
                 const_cast<Texture::Sampler*>(_value.samplerValue)->release();
             }
             break;
+        default:
+            // Ignore all other cases.
+            break;
         }
     }
 
@@ -111,6 +117,7 @@ void MaterialParameter::setValue(const Vector2& value)
 
 void MaterialParameter::setValue(const Vector2* values, unsigned int count)
 {
+    GP_ASSERT(values);
     clearValue();
 
     _value.floatPtrValue = const_cast<float*> (&values[0].x);
@@ -134,6 +141,7 @@ void MaterialParameter::setValue(const Vector3& value)
 
 void MaterialParameter::setValue(const Vector3* values, unsigned int count)
 {
+    GP_ASSERT(values);
     clearValue();
 
     _value.floatPtrValue = const_cast<float*> (&values[0].x);
@@ -157,6 +165,7 @@ void MaterialParameter::setValue(const Vector4& value)
 
 void MaterialParameter::setValue(const Vector4* values, unsigned int count)
 {
+    GP_ASSERT(values);
     clearValue();
 
     _value.floatPtrValue = const_cast<float*> (&values[0].x);
@@ -184,6 +193,7 @@ void MaterialParameter::setValue(const Matrix& value)
 
 void MaterialParameter::setValue(const Matrix* values, unsigned int count)
 {
+    GP_ASSERT(values);
     clearValue();
 
     _value.floatPtrValue = const_cast<Matrix&> (values[0]).m;
@@ -223,6 +233,8 @@ Texture::Sampler* MaterialParameter::setValue(const char* texturePath, bool gene
 
 void MaterialParameter::bind(Effect* effect)
 {
+    GP_ASSERT(effect);
+
     // If we had a Uniform cached that is not from the passed in effect,
     // we need to update our uniform to point to the new effect's uniform.
     if (!_uniform || _uniform->getEffect() != effect)
@@ -246,7 +258,6 @@ void MaterialParameter::bind(Effect* effect)
         }
         else
         {
-            GP_ASSERT(_value.floatPtrValue);
             effect->setValue(_uniform, _value.floatPtrValue, _count);
         }
         break;
@@ -257,34 +268,31 @@ void MaterialParameter::bind(Effect* effect)
         }
         else
         {
-            GP_ASSERT(_value.intPtrValue);
             effect->setValue(_uniform, _value.intPtrValue, _count);
         }
         break;
     case MaterialParameter::VECTOR2:
-        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Vector2*>(_value.floatPtrValue), _count);
         break;
     case MaterialParameter::VECTOR3:
-        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Vector3*>(_value.floatPtrValue), _count);
         break;
     case MaterialParameter::VECTOR4:
-        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Vector4*>(_value.floatPtrValue), _count);
         break;
     case MaterialParameter::MATRIX:
-        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Matrix*>(_value.floatPtrValue), _count);
         break;
     case MaterialParameter::SAMPLER:
-        GP_ASSERT(_value.samplerValue);
         effect->setValue(_uniform, _value.samplerValue);
         break;
     case MaterialParameter::METHOD:
         GP_ASSERT(_value.method);
         _value.method->setValue(effect);
         break;
+    default:
+        GP_ERROR("Unsupported material parameter type (%d).", _type);
+        break;
     }
 }
 
@@ -312,6 +320,7 @@ unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyI
                 case VECTOR4:
                     return 4 * _count;
                 default:
+                    GP_ERROR("Unsupported material parameter type (%d).", _type);
                     return 0;
             }
         }
@@ -323,6 +332,7 @@ unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyI
 
 void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 {
+    GP_ASSERT(value);
     switch (propertyId)
     {
         case ANIMATE_UNIFORM:
@@ -336,6 +346,7 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
                     }
                     else
                     {
+                        GP_ASSERT(_value.floatPtrValue);
                         for (unsigned int i = 0; i < _count; i++)
                         {
                             value->setFloat(i, _value.floatPtrValue[i]);
@@ -349,6 +360,7 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
                     }
                     else
                     {
+                        GP_ASSERT(_value.intPtrValue);
                         for (unsigned int i = 0; i < _count; i++)
                         {
                             value->setFloat(i, _value.intPtrValue[i]);
@@ -373,8 +385,15 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
                         value->setFloat(_value.floatPtrValue, i * 4, 4);
                     }
                     break;
-
-                // UNSUPPORTED: NONE, MATRIX, METHOD, SAMPLER 
+                case NONE:
+                case MATRIX:
+                case METHOD:
+                case SAMPLER:
+                    // Unsupported material parameter types for animation.
+                    break;
+                default:
+                    GP_ERROR("Unsupported material parameter type (%d).", _type);
+                    break;
             }
         }
         break;
@@ -383,6 +402,7 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
 
 void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
+    GP_ASSERT(value);
     GP_ASSERT(blendWeight >= 0.0f && blendWeight <= 1.0f);
 
     switch (propertyId)
@@ -407,6 +427,7 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
                     }
                     else
                     {
+                        GP_ASSERT(_value.intPtrValue);
                         for (unsigned int i = 0; i < _count; i++)
                             _value.intPtrValue[i] = Curve::lerp(blendWeight, _value.intPtrValue[i], value->getFloat(i));
                     }
@@ -427,7 +448,15 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
                     applyAnimationValue(value, blendWeight, 4);
                     break;
                 }
-                // UNSUPPORTED: NONE, MATRIX, METHOD, SAMPLER 
+                case NONE:
+                case MATRIX:
+                case METHOD:
+                case SAMPLER:
+                    // Unsupported material parameter types for animation.
+                    break;
+                default:
+                    GP_ERROR("Unsupported material parameter type (%d).", _type);
+                    break;
             }
         }
         break;
@@ -436,6 +465,9 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
 
 void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWeight, int components)
 {
+    GP_ASSERT(value);
+    GP_ASSERT(_value.floatPtrValue);
+
     unsigned int count = _count * components;
     for (unsigned int i = 0; i < count; i++)
         _value.floatPtrValue[i] = Curve::lerp(blendWeight, _value.floatPtrValue[i], value->getFloat(i));
@@ -443,6 +475,7 @@ void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWe
 
 void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
 {
+    GP_ASSERT(materialParameter);
     materialParameter->_type = _type;
     materialParameter->_count = _count;
     materialParameter->_dynamic = _dynamic;
@@ -462,6 +495,7 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
         Vector2* value = reinterpret_cast<Vector2*>(_value.floatPtrValue);
         if (_count == 1)
         {
+            GP_ASSERT(value);
             materialParameter->setValue(*value);
         }
         else
@@ -475,6 +509,7 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
         Vector3* value = reinterpret_cast<Vector3*>(_value.floatPtrValue);
         if (_count == 1)
         {
+            GP_ASSERT(value);
             materialParameter->setValue(*value);
         }
         else
@@ -488,6 +523,7 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
         Vector4* value = reinterpret_cast<Vector4*>(_value.floatPtrValue);
         if (_count == 1)
         {
+            GP_ASSERT(value);
             materialParameter->setValue(*value);
         }
         else
@@ -501,6 +537,7 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
         Matrix* value = reinterpret_cast<Matrix*>(_value.floatPtrValue);
         if (_count == 1)
         {
+            GP_ASSERT(value);
             materialParameter->setValue(*value);
         }
         else
@@ -514,8 +551,12 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
         break;
     case METHOD:
         materialParameter->_value.method = _value.method;
+        GP_ASSERT(materialParameter->_value.method);
         materialParameter->_value.method->addRef();
         break;
+    default:
+        GP_ERROR("Unsupported material parameter type(%d).", _type);
+        break;
     }
 }
 

+ 18 - 3
gameplay/src/Mesh.cpp

@@ -24,11 +24,14 @@ Mesh::Mesh(const Mesh& copy) :
 
 Mesh::~Mesh()
 {
-    for (unsigned int i = 0; i < _partCount; ++i)
+    if (_parts)
     {
-        SAFE_DELETE(_parts[i]);
+        for (unsigned int i = 0; i < _partCount; ++i)
+        {
+            SAFE_DELETE(_parts[i]);
+        }
+        SAFE_DELETE_ARRAY(_parts);
     }
-    SAFE_DELETE_ARRAY(_parts);
 
     if (_vertexBuffer)
     {
@@ -43,12 +46,14 @@ Mesh* Mesh::createMesh(const VertexFormat& vertexFormat, unsigned int vertexCoun
     GL_ASSERT( glGenBuffers(1, &vbo) );
     if (GL_LAST_ERROR())
     {
+        GP_ERROR("Failed to create VBO for mesh with OpenGL error %d.", GL_LAST_ERROR());
         return NULL;
     }
 
     GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, vbo) );
     if (GL_LAST_ERROR())
     {
+        GP_ERROR("Failed to bind VBO for mesh with OpenGL error %d.", GL_LAST_ERROR());
         glDeleteBuffers(1, &vbo);
         return NULL;
     }
@@ -56,6 +61,7 @@ Mesh* Mesh::createMesh(const VertexFormat& vertexFormat, unsigned int vertexCoun
     GL_CHECK( glBufferData(GL_ARRAY_BUFFER, vertexFormat.getVertexSize() * vertexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
     if (GL_LAST_ERROR())
     {
+        GP_ERROR("Failed to load VBO with vertex data with OpenGL error %d.", GL_LAST_ERROR());
         glBindBuffer(GL_ARRAY_BUFFER, 0);
         glDeleteBuffers(1, &vbo);
         return NULL;
@@ -92,6 +98,7 @@ Mesh* Mesh::createQuad(float x, float y, float width, float height)
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 3), 4, false);
     if (mesh == NULL)
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
     }
 
@@ -124,6 +131,7 @@ Mesh* Mesh::createQuadFullscreen()
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
     if (mesh == NULL)
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
     }
 
@@ -160,6 +168,7 @@ Mesh* Mesh::createQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3,
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 3), 4, false);
     if (mesh == NULL)
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
     }
 
@@ -171,6 +180,9 @@ Mesh* Mesh::createQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3,
 
 Mesh* Mesh::createLines(Vector3* points, unsigned int pointCount)
 {
+    GP_ASSERT(points);
+    GP_ASSERT(pointCount);
+
     float* vertices = new float[pointCount*3];
     memcpy(vertices, points, pointCount*3*sizeof(float));
 
@@ -181,6 +193,7 @@ Mesh* Mesh::createLines(Vector3* points, unsigned int pointCount)
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 1), pointCount, false);
     if (mesh == NULL)
     {
+        GP_ERROR("Failed to create mesh.");
         SAFE_DELETE_ARRAY(vertices);
         return NULL;
     }
@@ -226,6 +239,7 @@ Mesh* Mesh::createBoundingBox(const BoundingBox& box)
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 1), 18, false);
     if (mesh == NULL)
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
     }
 
@@ -325,6 +339,7 @@ unsigned int Mesh::getPartCount() const
 
 MeshPart* Mesh::getPart(unsigned int index)
 {
+    GP_ASSERT(_parts);
     return _parts[index];
 }
 

+ 25 - 9
gameplay/src/MeshBatch.cpp

@@ -28,7 +28,10 @@ MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveTy
 {
     Material* material = Material::create(materialPath);
     if (material == NULL)
+    {
+        GP_ERROR("Failed to create material for mesh batch from file '%s'.", materialPath);
         return NULL;
+    }
     MeshBatch* batch = create(vertexFormat, primitiveType, material, indexed, initialCapacity, growSize);
     SAFE_RELEASE(material); // batch now owns the material
     return batch;
@@ -47,13 +50,17 @@ MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveTy
 
 void MeshBatch::updateVertexAttributeBinding()
 {
-    // Update our vertex attribute bindings
+    GP_ASSERT(_material);
+
+    // Update our vertex attribute bindings.
     for (unsigned int i = 0, techniqueCount = _material->getTechniqueCount(); i < techniqueCount; ++i)
     {
         Technique* t = _material->getTechnique(i);
+        GP_ASSERT(t);
         for (unsigned int j = 0, passCount = t->getPassCount(); j < passCount; ++j)
         {
             Pass* p = t->getPass(j);
+            GP_ASSERT(p);
             VertexAttributeBinding* b = VertexAttributeBinding::create(_vertexFormat, _vertices, p->getEffect());
             p->setVertexAttributeBinding(b);
             SAFE_RELEASE(b);
@@ -73,14 +80,16 @@ void MeshBatch::setCapacity(unsigned int capacity)
 
 bool MeshBatch::resize(unsigned int capacity)
 {
-    GP_ASSERT(capacity > 0);
     if (capacity == 0)
+    {
+        GP_ERROR("Invalid resize capacity (0).");
         return false;
+    }
 
     if (capacity == _capacity)
         return true;
 
-    // Store old batch data
+    // Store old batch data.
     unsigned char* oldVertices = _vertices;
     unsigned short* oldIndices = _indices;
 
@@ -103,20 +112,21 @@ bool MeshBatch::resize(unsigned int capacity)
         vertexCapacity = capacity + 2;
         break;
     default:
-        GP_ASSERT(0); // unexpected
-        break;
+        GP_ERROR("Unsupported primitive type for mesh batch (%d).", _primitiveType);
+        return false;
     }
 
     // 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;
-
-    GP_ASSERT(indexCapacity <= USHRT_MAX);
     if (indexCapacity > USHRT_MAX)
+    {
+        GP_ERROR("Index capacity is greater than the maximum unsigned short value (%d > %d).", indexCapacity, USHRT_MAX);
         return false;
+    }
 
-    // Allocate new data and reset pointers
+    // Allocate new data and reset pointers.
     unsigned int voffset = _verticesPtr - _vertices;
     unsigned int vBytes = vertexCapacity * _vertexFormat.getVertexSize();
     _vertices = new unsigned char[vBytes];
@@ -173,12 +183,18 @@ void MeshBatch::draw()
     // ARRAY_BUFFER will be unbound automatically during pass->bind().
     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0 ) );
 
-    // Bind the material
+    GP_ASSERT(_material);
+    if (_indexed)
+        GP_ASSERT(_indices);
+
+    // Bind the material.
     Technique* technique = _material->getTechnique();
+    GP_ASSERT(technique);
     unsigned int passCount = technique->getPassCount();
     for (unsigned int i = 0; i < passCount; ++i)
     {
         Pass* pass = technique->getPass(i);
+        GP_ASSERT(pass);
         pass->bind();
 
         if (_indexed)

+ 10 - 5
gameplay/src/MeshBatch.inl

@@ -11,6 +11,7 @@ Material* MeshBatch::getMaterial() const
 template <class T>
 void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 {
+    GP_ASSERT(vertices);
     GP_ASSERT(sizeof(T) == _vertexFormat.getVertexSize());
     
     unsigned int newVertexCount = _vertexCount + vertexCount;
@@ -27,16 +28,20 @@ void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indic
             return; // failed to grow
     }
     
-    // Copy vertex data
+    // Copy vertex data.
+    GP_ASSERT(_verticesPtr);
     unsigned int vBytes = vertexCount * _vertexFormat.getVertexSize();
     memcpy(_verticesPtr, vertices, vBytes);
     
-    // Copy index data
+    // Copy index data.
     if (_indexed)
     {
+        GP_ASSERT(indices);
+        GP_ASSERT(_indicesPtr);
+
         if (_vertexCount == 0)
         {
-            // Simply copy values directly into the start of the index array
+            // Simply copy values directly into the start of the index array.
             memcpy(_indicesPtr, indices, indexCount * sizeof(unsigned short));
         }
         else
@@ -50,8 +55,8 @@ void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indic
                 _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
+            // Loop through all indices and insert them, with their values offset by
+            // 'vertexCount' so that they are relative to the first newly inserted vertex.
             for (unsigned int i = 0; i < indexCount; ++i)
             {
                 _indicesPtr[i] = indices[i] + _vertexCount;

+ 10 - 0
gameplay/src/MeshPart.cpp

@@ -29,12 +29,14 @@ MeshPart* MeshPart::create(Mesh* mesh, unsigned int meshIndex, Mesh::PrimitiveTy
     GL_ASSERT( glGenBuffers(1, &vbo) );
     if (GL_LAST_ERROR())
     {
+        GP_ERROR("Failed to create VBO for index buffer with OpenGL error %d.", GL_LAST_ERROR());
         return NULL;
     }
 
     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo) );
     if (GL_LAST_ERROR())
     {
+        GP_ERROR("Failed to bind VBO for index buffer with OpenGL error %d.", GL_LAST_ERROR());
         glDeleteBuffers(1, &vbo);
         return NULL;
     }
@@ -51,10 +53,15 @@ MeshPart* MeshPart::create(Mesh* mesh, unsigned int meshIndex, Mesh::PrimitiveTy
     case Mesh::INDEX32:
         indexSize = 4;
         break;
+    default:
+        GP_ERROR("Unsupported index format (%d).", indexFormat);
+        glDeleteBuffers(1, &vbo);
+        return NULL;
     }
     GL_CHECK( glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize * indexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
     if (GL_LAST_ERROR())
     {
+        GP_ERROR("Failed to load VBO with index data with OpenGL error %d.", GL_LAST_ERROR());
         glDeleteBuffers(1, &vbo);
         return NULL;
     }
@@ -117,6 +124,9 @@ void MeshPart::setIndexData(void* indexData, unsigned int indexStart, unsigned i
     case Mesh::INDEX32:
         indexSize = 4;
         break;
+    default:
+        GP_ERROR("Unsupported index format (%d).", _indexFormat);
+        return;
     }
 
     if (indexStart == 0 && indexCount == 0)

+ 6 - 2
gameplay/src/MeshSkin.cpp

@@ -85,6 +85,7 @@ MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
         for (unsigned int i = 0; i < jointCount; ++i)
         {
             Joint* oldJoint = getJoint(i);
+            GP_ASSERT(oldJoint);
             
             Joint* newJoint = static_cast<Joint*>(skin->_rootJoint->findNode(oldJoint->getId()));
             if (!newJoint)
@@ -101,10 +102,10 @@ MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
 
 void MeshSkin::setJointCount(unsigned int jointCount)
 {
-    // Erase the joints vector and release all joints
+    // Erase the joints vector and release all joints.
     clearJoints();
 
-    // Resize the joints vector and initialize to NULL
+    // Resize the joints vector and initialize to NULL.
     _joints.resize(jointCount);
     for (unsigned int i = 0; i < jointCount; i++)
     {
@@ -147,9 +148,12 @@ void MeshSkin::setJoint(Joint* joint, unsigned int index)
 
 Vector4* MeshSkin::getMatrixPalette() const
 {
+    GP_ASSERT(_matrixPalette);
+
     unsigned int count = _joints.size();
     for (unsigned int i = 0; i < count; i++)
     {
+        GP_ASSERT(_joints[i]);
         _joints[i]->updateJointMatrix(getBindShape(), &_matrixPalette[i * PALETTE_ROWS]);
     }
     return _matrixPalette;

+ 48 - 7
gameplay/src/Model.cpp

@@ -12,6 +12,7 @@ namespace gameplay
 Model::Model(Mesh* mesh) :
     _mesh(mesh), _material(NULL), _partCount(0), _partMaterials(NULL), _node(NULL), _skin(NULL)
 {
+    GP_ASSERT(mesh);
     _partCount = mesh->getPartCount();
 }
 
@@ -35,6 +36,7 @@ Model::~Model()
 
 Model* Model::create(Mesh* mesh)
 {
+    GP_ASSERT(mesh);
     mesh->addRef();
     return new Model(mesh);
 }
@@ -46,6 +48,7 @@ Mesh* Model::getMesh() const
 
 unsigned int Model::getMeshPartCount() const
 {
+    GP_ASSERT(_mesh);
     return _mesh->getPartCount();
 }
 
@@ -121,11 +124,13 @@ void Model::setMaterial(Material* material, int partIndex)
     // Release existing material and binding.
     if (oldMaterial)
     {
-        for (unsigned int i = 0, tCount = material->getTechniqueCount(); i < tCount; ++i)
+        for (unsigned int i = 0, tCount = oldMaterial->getTechniqueCount(); i < tCount; ++i)
         {
-            Technique* t = material->getTechnique(i);
+            Technique* t = oldMaterial->getTechnique(i);
+            GP_ASSERT(t);
             for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
             {
+                GP_ASSERT(t->getPass(j));
                 t->getPass(j)->setVertexAttributeBinding(NULL);
             }
         }
@@ -138,9 +143,11 @@ void Model::setMaterial(Material* material, int partIndex)
         for (unsigned int i = 0, tCount = material->getTechniqueCount(); i < tCount; ++i)
         {
             Technique* t = material->getTechnique(i);
+            GP_ASSERT(t);
             for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
             {
                 Pass* p = t->getPass(j);
+                GP_ASSERT(p);
                 VertexAttributeBinding* b = VertexAttributeBinding::create(_mesh, p->getEffect());
                 p->setVertexAttributeBinding(b);
                 SAFE_RELEASE(b);
@@ -161,6 +168,7 @@ Material* Model::setMaterial(const char* vshPath, const char* fshPath, const cha
     Material* material = Material::create(vshPath, fshPath, defines);
     if (material == NULL)
     {
+        GP_ERROR("Failed to create material for model.");
         return NULL;
     }
 
@@ -179,6 +187,7 @@ Material* Model::setMaterial(const char* materialPath, int partIndex)
     Material* material = Material::create(materialPath);
     if (material == NULL)
     {
+        GP_ERROR("Failed to create material for model.");
         return NULL;
     }
 
@@ -246,6 +255,8 @@ void Model::setNode(Node* node)
 
 void Model::draw(bool wireframe)
 {
+    GP_ASSERT(_mesh);
+
     unsigned int partCount = _mesh->getPartCount();
     if (partCount == 0)
     {
@@ -253,10 +264,12 @@ void Model::draw(bool wireframe)
         if (_material)
         {
             Technique* technique = _material->getTechnique();
+            GP_ASSERT(technique);
             unsigned int passCount = technique->getPassCount();
             for (unsigned int i = 0; i < passCount; ++i)
             {
                 Pass* pass = technique->getPass(i);
+                GP_ASSERT(pass);
                 pass->bind();
                 GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) );
                 if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
@@ -280,16 +293,19 @@ void Model::draw(bool wireframe)
         for (unsigned int i = 0; i < partCount; ++i)
         {
             MeshPart* part = _mesh->getPart(i);
+            GP_ASSERT(part);
 
             // Get the material for this mesh part.
             Material* material = getMaterial(i);
             if (material)
             {
                 Technique* technique = material->getTechnique();
+                GP_ASSERT(technique);
                 unsigned int passCount = technique->getPassCount();
                 for (unsigned int j = 0; j < passCount; ++j)
                 {
                     Pass* pass = technique->getPass(j);
+                    GP_ASSERT(pass);
                     pass->bind();
                     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, part->_indexBuffer) );
                     if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
@@ -307,6 +323,9 @@ void Model::draw(bool wireframe)
                         case Mesh::INDEX32:
                             indexSize = 4;
                             break;
+                        default:
+                            GP_ERROR("Unsupported index format (%d).", part->getIndexFormat());
+                            continue;
                         }
 
                         for (unsigned int k = 0; k < indexCount; k += 3)
@@ -327,6 +346,7 @@ void Model::draw(bool wireframe)
 
 void Model::validatePartCount()
 {
+    GP_ASSERT(_mesh);
     unsigned int partCount = _mesh->getPartCount();
 
     if (_partCount != partCount)
@@ -337,9 +357,12 @@ void Model::validatePartCount()
             Material** oldArray = _partMaterials;
             _partMaterials = new Material*[partCount];
             memset(_partMaterials, 0, sizeof(Material*) * partCount);
-            for (unsigned int i = 0; i < _partCount; ++i)
+            if (oldArray)
             {
-                _partMaterials[i] = oldArray[i];
+                for (unsigned int i = 0; i < _partCount; ++i)
+                {
+                    _partMaterials[i] = oldArray[i];
+                }
             }
             SAFE_DELETE_ARRAY(oldArray);
         }
@@ -352,18 +375,34 @@ void Model::validatePartCount()
 Model* Model::clone(NodeCloneContext &context)
 {
     Model* model = Model::create(getMesh());
+    if (!model)
+    {
+        GP_ERROR("Failed to clone model.");
+        return NULL;
+    }
+
     if (getSkin())
     {
         model->setSkin(getSkin()->clone(context));
     }
-    Material* materialClone = getMaterial()->clone(context);
-    model->setMaterial(materialClone); // TODO: Don't forget material parts
-    materialClone->release();
+    if (getMaterial())
+    {
+        Material* materialClone = getMaterial()->clone(context);
+        if (!materialClone)
+        {
+            GP_ERROR("Failed to clone material for model.");
+            return model;
+        }
+        model->setMaterial(materialClone); // TODO: Don't forget material parts
+        materialClone->release();
+    }
     return model;
 }
 
 void Model::setMaterialNodeBinding(Material *material)
 {
+    GP_ASSERT(material);
+
     if (_node)
     {
         material->setNodeBinding(_node);
@@ -372,6 +411,7 @@ void Model::setMaterialNodeBinding(Material *material)
         for (unsigned int i = 0; i < techniqueCount; ++i)
         {
             Technique* technique = material->getTechnique(i);
+            GP_ASSERT(technique);
             
             technique->setNodeBinding(_node);
 
@@ -379,6 +419,7 @@ void Model::setMaterialNodeBinding(Material *material)
             for (unsigned int j = 0; j < passCount; ++j)
             {
                 Pass* pass = technique->getPass(j);
+                GP_ASSERT(pass);
 
                 pass->setNodeBinding(_node);
             }

+ 62 - 29
gameplay/src/ParticleEmitter.cpp

@@ -30,8 +30,11 @@ ParticleEmitter::ParticleEmitter(SpriteBatch* batch, unsigned int particleCountM
     _node(NULL), _orbitPosition(false), _orbitVelocity(false), _orbitAcceleration(false),
     _timePerEmission(PARTICLE_EMISSION_RATE_TIME_INTERVAL), _timeLast(0L), _timeRunning(0L)
 {
+    GP_ASSERT(particleCountMax);
     _particles = new Particle[particleCountMax];
 
+    GP_ASSERT(_spriteBatch);
+    GP_ASSERT(_spriteBatch->getStateBlock());
     _spriteBatch->getStateBlock()->setDepthWrite(false);
     _spriteBatch->getStateBlock()->setDepthTest(true);
 }
@@ -45,16 +48,16 @@ ParticleEmitter::~ParticleEmitter()
 
 ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlending textureBlending, unsigned int particleCountMax)
 {
-    GP_ASSERT(textureFile);
-
     Texture* texture = NULL;
     texture = Texture::create(textureFile, false);
 
     if (!texture)
     {
-        GP_ERROR("Error creating ParticleEmitter: Could not read texture file: %s", textureFile);
+        GP_ERROR("Failed to create texture for particle emitter.");
         return NULL;
     }
+    GP_ASSERT(texture->getWidth());
+    GP_ASSERT(texture->getHeight());
 
     // Use default SpriteBatch material.
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
@@ -79,12 +82,10 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
 
 ParticleEmitter* ParticleEmitter::create(const char* url)
 {
-    GP_ASSERT(url);
-
     Properties* properties = Properties::create(url);
     if (!properties)
     {
-        GP_ERROR("Error loading ParticleEmitter: Could not load file: %s", url);
+        GP_ERROR("Failed to create particle emitter from file.");
         return NULL;
     }
 
@@ -98,23 +99,23 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 {
     if (!properties || strcmp(properties->getNamespace(), "particle") != 0)
     {
-        GP_ERROR("Error loading ParticleEmitter: No 'particle' namespace found");
+        GP_ERROR("Properties object must be non-null and have namespace equal to 'particle'.");
         return NULL;
     }
 
     Properties* sprite = properties->getNextNamespace();
     if (!sprite || strcmp(sprite->getNamespace(), "sprite") != 0)
     {
-        GP_ERROR("Error loading ParticleEmitter: No 'sprite' namespace found");
+        GP_ERROR("Failed to load particle emitter: required namespace 'sprite' is missing.");
         return NULL;
     }
 
     // Load sprite properties.
     // Path to image file is required.
     const char* texturePath = sprite->getString("path");
-    if (strlen(texturePath) == 0)
+    if (!texturePath || strlen(texturePath) == 0)
     {
-        GP_ERROR("Error loading ParticleEmitter: No texture path specified: %s", texturePath);
+        GP_ERROR("Failed to load particle emitter: required image file path ('path') is missing.");
         return NULL;
     }
 
@@ -143,7 +144,6 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
     }
 
     bool ellipsoid = properties->getBool("ellipsoid");
-
     float sizeStartMin = properties->getFloat("sizeStartMin");
     float sizeStartMax = properties->getFloat("sizeStartMax");
     float sizeEndMin = properties->getFloat("sizeEndMin");
@@ -186,6 +186,11 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 
     // Apply all properties to a newly created ParticleEmitter.
     ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
+    if (!emitter)
+    {
+        GP_ERROR("Failed to create particle emitter.");
+        return NULL;
+    }
     emitter->setEmissionRate(emissionRate);
     emitter->setEllipsoid(ellipsoid);
     emitter->setSize(sizeStartMin, sizeStartMax, sizeEndMin, sizeEndMax);
@@ -215,6 +220,7 @@ unsigned int ParticleEmitter::getEmissionRate() const
 
 void ParticleEmitter::setEmissionRate(unsigned int rate)
 {
+    GP_ASSERT(rate);
     _emissionRate = rate;
     _timePerEmission = 1000.0f / (float)_emissionRate;
 }
@@ -243,6 +249,7 @@ bool ParticleEmitter::isActive() const
     if (!_node)
         return false;
 
+    GP_ASSERT(_particles);
     bool active = false;
     for (unsigned int i = 0; i < _particleCount; i++)
     {
@@ -258,6 +265,9 @@ bool ParticleEmitter::isActive() const
 
 void ParticleEmitter::emit(unsigned int particleCount)
 {
+    GP_ASSERT(_node);
+    GP_ASSERT(_particles);
+
     // Limit particleCount so as not to go over _particleCountMax.
     if (particleCount + _particleCount > _particleCountMax)
     {
@@ -513,6 +523,9 @@ const Vector3& ParticleEmitter::getRotationAxisVariance() const
 
 void ParticleEmitter::setTextureBlending(TextureBlending textureBlending)
 {
+    GP_ASSERT(_spriteBatch);
+    GP_ASSERT(_spriteBatch->getStateBlock());
+
     switch (textureBlending)
     {
         case BLEND_OPAQUE:
@@ -533,6 +546,9 @@ void ParticleEmitter::setTextureBlending(TextureBlending textureBlending)
             _spriteBatch->getStateBlock()->setBlendSrc(RenderState::BLEND_ZERO);
             _spriteBatch->getStateBlock()->setBlendDst(RenderState::BLEND_SRC_COLOR);
             break;
+        default:
+            GP_ERROR("Unsupported texture blending mode (%d).", textureBlending);
+            break;
     }
 }
 
@@ -580,6 +596,9 @@ long ParticleEmitter::getSpriteFrameDuration() const
 
 void ParticleEmitter::setSpriteTexCoords(unsigned int frameCount, float* texCoords)
 {
+    GP_ASSERT(frameCount);
+    GP_ASSERT(texCoords);
+
     _spriteFrameCount = frameCount;
     _spritePercentPerFrame = 1.0f / (float)frameCount;
 
@@ -590,34 +609,30 @@ void ParticleEmitter::setSpriteTexCoords(unsigned int frameCount, float* texCoor
 
 void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, Rectangle* frameCoords)
 {
+    GP_ASSERT(frameCount);
+    GP_ASSERT(frameCoords);
+
     _spriteFrameCount = frameCount;
     _spritePercentPerFrame = 1.0f / (float)frameCount;
 
-    float* texCoords = new float[frameCount * 4];
+    SAFE_DELETE_ARRAY(_spriteTextureCoords);
+    _spriteTextureCoords = new float[frameCount * 4];
 
     // Pre-compute texture coordinates from rects.
     for (unsigned int i = 0; i < frameCount; i++)
     {
-        float u1 = _spriteTextureWidthRatio * frameCoords[i].x;
-        float v1 = 1.0f - _spriteTextureHeightRatio * frameCoords[i].y;
-        float u2 = u1 + _spriteTextureWidthRatio * frameCoords[i].width;
-        float v2 = v1 - _spriteTextureHeightRatio * frameCoords[i].height;
-
-        texCoords[i*4] = u1;
-        texCoords[i*4 + 1] = v1;
-        texCoords[i*4 + 2] = u2;
-        texCoords[i*4 + 3] = v2;
+        _spriteTextureCoords[i*4] = _spriteTextureWidthRatio * frameCoords[i].x;
+        _spriteTextureCoords[i*4 + 1] = 1.0f - _spriteTextureHeightRatio * frameCoords[i].y;
+        _spriteTextureCoords[i*4 + 2] = _spriteTextureCoords[i*4] + _spriteTextureWidthRatio * frameCoords[i].width;
+        _spriteTextureCoords[i*4 + 3] = _spriteTextureCoords[i*4 + 1] - _spriteTextureHeightRatio * frameCoords[i].height;
     }
-
-    SAFE_DELETE_ARRAY(_spriteTextureCoords);
-    _spriteTextureCoords = new float[frameCount * 4];
-    memcpy(_spriteTextureCoords, texCoords, frameCount * 4 * sizeof(float));
-
-    SAFE_DELETE_ARRAY(texCoords);
 }
 
 void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, int width, int height)
 {
+    GP_ASSERT(width);
+    GP_ASSERT(height);
+
     int x;
     int y;
     Rectangle* frameCoords = new Rectangle[frameCount];
@@ -691,6 +706,8 @@ float ParticleEmitter::generateScalar(float min, float max)
 
 void ParticleEmitter::generateVectorInRect(const Vector3& base, const Vector3& variance, Vector3* dst)
 {
+    GP_ASSERT(dst);
+
     // Scale each component of the variance vector by a random float
     // between -1 and 1, then add this to the corresponding base component.
     dst->x = base.x + variance.x * MATH_RANDOM_MINUS1_1();
@@ -700,6 +717,8 @@ void ParticleEmitter::generateVectorInRect(const Vector3& base, const Vector3& v
 
 void ParticleEmitter::generateVectorInEllipsoid(const Vector3& center, const Vector3& scale, Vector3* dst)
 {
+    GP_ASSERT(dst);
+
     // Generate a point within a unit cube, then reject if the point is not in a unit sphere.
     do
     {
@@ -731,6 +750,8 @@ void ParticleEmitter::generateVector(const Vector3& base, const Vector3& varianc
 
 void ParticleEmitter::generateColor(const Vector4& base, const Vector4& variance, Vector4* dst)
 {
+    GP_ASSERT(dst);
+
     // Scale each component of the variance color by a random float
     // between -1 and 1, then add this to the corresponding base component.
     dst->x = base.x + variance.x * MATH_RANDOM_MINUS1_1();
@@ -741,6 +762,8 @@ void ParticleEmitter::generateColor(const Vector4& base, const Vector4& variance
 
 ParticleEmitter::TextureBlending ParticleEmitter::getTextureBlendingFromString(const char* str)
 {
+    GP_ASSERT(str);
+
     if (strcmp(str, "BLEND_OPAQUE") == 0 || strcmp(str, "OPAQUE") == 0)
     {
         return BLEND_OPAQUE;
@@ -757,8 +780,10 @@ ParticleEmitter::TextureBlending ParticleEmitter::getTextureBlendingFromString(c
     {
         return BLEND_MULTIPLIED;
     }
-
-    return BLEND_TRANSPARENT;
+    else
+    {
+        return BLEND_TRANSPARENT;
+    }
 }
 
 
@@ -778,6 +803,7 @@ void ParticleEmitter::update(long elapsedTime)
         _timeRunning += elapsedTime;
 
         // How many particles should we emit this frame?
+        GP_ASSERT(_timePerEmission);
         unsigned int emitCount = _timeRunning / _timePerEmission;
             
         if (emitCount)
@@ -791,9 +817,11 @@ void ParticleEmitter::update(long elapsedTime)
         }
     }
 
+    GP_ASSERT(_node && _node->getScene() && _node->getScene()->getActiveCamera());
     const Frustum& frustum = _node->getScene()->getActiveCamera()->getFrustum();
 
     // Now update all currently living particles.
+    GP_ASSERT(_particles);
     for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex)
     {
         Particle* p = &_particles[particlesIndex];
@@ -893,6 +921,10 @@ void ParticleEmitter::draw()
 
     if (_particleCount > 0)
     {
+        GP_ASSERT(_spriteBatch);
+        GP_ASSERT(_particles);
+        GP_ASSERT(_spriteTextureCoords);
+
         // Set our node's view projection matrix to this emitter's effect.
         if (_node)
         {
@@ -906,6 +938,7 @@ void ParticleEmitter::draw()
         static const Vector2 pivot(0.5f, 0.5f);
 
         // 3D Rotation so that particles always face the camera.
+        GP_ASSERT(_node && _node->getScene() && _node->getScene()->getActiveCamera() && _node->getScene()->getActiveCamera()->getNode());
         const Matrix& cameraWorldMatrix = _node->getScene()->getActiveCamera()->getNode()->getWorldMatrix();
 
         Vector3 right;

+ 7 - 6
gameplay/src/Pass.cpp

@@ -10,8 +10,6 @@ namespace gameplay
 Pass::Pass(const char* id, Technique* technique, Effect* effect) :
     _id(id ? id : ""), _technique(technique), _effect(effect), _vaBinding(NULL)
 {
-    GP_ASSERT(technique);
-
     RenderState::_parent = _technique;
 }
 
@@ -23,15 +21,15 @@ Pass::~Pass()
 
 Pass* Pass::create(const char* id, Technique* technique, const char* vshPath, const char* fshPath, const char* defines)
 {
-    // Attempt to create/load the effect
+    // Attempt to create/load the effect.
     Effect* effect = Effect::createFromFile(vshPath, fshPath, defines);
-    GP_ASSERT(effect);
     if (effect == NULL)
     {
+        GP_ERROR("Failed to create effect for pass.");
         return NULL;
     }
 
-    // Return the new pass
+    // Return the new pass.
     return new Pass(id, technique, effect);
 }
 
@@ -58,7 +56,9 @@ void Pass::setVertexAttributeBinding(VertexAttributeBinding* binding)
 
 void Pass::bind()
 {
-    // Bind our effect
+    GP_ASSERT(_effect);
+
+    // Bind our effect.
     _effect->bind();
 
     // Bind our render state
@@ -83,6 +83,7 @@ void Pass::unbind()
 Pass* Pass::clone(Technique* technique, NodeCloneContext &context) const
 {
     Effect* effect = getEffect();
+    GP_ASSERT(effect);
     effect->addRef();
     Pass* pass = new Pass(getId(), technique, effect);
     RenderState::cloneInto(pass, context);

+ 99 - 53
gameplay/src/RenderState.cpp

@@ -29,11 +29,7 @@ RenderState::~RenderState()
     // Destroy all the material parameters
     for (unsigned int i = 0, count = _parameters.size(); i < count; ++i)
     {
-        MaterialParameter* parameter = _parameters[i];
-        if (parameter)
-        {
-            SAFE_RELEASE(parameter);
-        }
+        SAFE_RELEASE(_parameters[i]);
     }
 }
 
@@ -54,19 +50,19 @@ MaterialParameter* RenderState::getParameter(const char* name) const
 {
     GP_ASSERT(name);
 
+    // Search for an existing parameter with this name.
     MaterialParameter* param;
-
-    // Search for an existing parameter with this name
     for (unsigned int i = 0, count = _parameters.size(); i < count; ++i)
     {
         param = _parameters[i];
+        GP_ASSERT(param);
         if (strcmp(param->getName(), name) == 0)
         {
             return param;
         }
     }
 
-    // Create a new parameter and store it in our list
+    // Create a new parameter and store it in our list.
     param = new MaterialParameter(name);
     _parameters.push_back(param);
 
@@ -75,10 +71,12 @@ MaterialParameter* RenderState::getParameter(const char* name) const
 
 void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBinding)
 {
-    // Store the auto-binding
+    GP_ASSERT(name);
+
+    // Store the auto-binding.
     if (autoBinding == NONE)
     {
-        // Clear current auto binding
+        // Clear current auto binding.
         std::map<std::string, AutoBinding>::iterator itr = _autoBindings.find(name);
         if (itr != _autoBindings.end())
         {
@@ -87,11 +85,11 @@ void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBind
     }
     else
     {
-        // Set new auto binding
+        // Set new auto binding.
         _autoBindings[name] = autoBinding;
     }
 
-    // If we have a currently set node binding, apply the auto binding immediately
+    // If we have a currently set node binding, apply the auto binding immediately.
     if (_nodeBinding)
     {
         applyAutoBinding(name, autoBinding);
@@ -100,9 +98,10 @@ void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBind
 
 void RenderState::setParameterAutoBinding(const char* name, const char* autoBinding)
 {
+    GP_ASSERT(autoBinding);
     AutoBinding value = NONE;
 
-    // Parse the passed in autoBinding string
+    // Parse the passed in autoBinding string.
     if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
     {
         value = WORLD_MATRIX;
@@ -147,6 +146,10 @@ void RenderState::setParameterAutoBinding(const char* name, const char* autoBind
     {
         value = MATRIX_PALETTE;
     }
+    else
+    {
+        // Ignore all other cases (the value was previously set to the default of NONE).
+    }
 
     if (value != NONE)
     {
@@ -185,7 +188,7 @@ void RenderState::setNodeBinding(Node* node)
 
     if (_nodeBinding)
     {
-        // Apply all existing auto-bindings using this node
+        // Apply all existing auto-bindings using this node.
         std::map<std::string, AutoBinding>::const_iterator itr = _autoBindings.begin();
         while (itr != _autoBindings.end())
         {
@@ -197,46 +200,57 @@ void RenderState::setNodeBinding(Node* node)
 
 void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBinding)
 {
+    MaterialParameter* param = getParameter(uniformName);
     switch (autoBinding)
     {
     case WORLD_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getWorldMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getWorldMatrix);
         break;
 
     case VIEW_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getViewMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getViewMatrix);
         break;
 
     case PROJECTION_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getProjectionMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getProjectionMatrix);
         break;
 
     case WORLD_VIEW_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getWorldViewMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getWorldViewMatrix);
         break;
 
     case VIEW_PROJECTION_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getViewProjectionMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getViewProjectionMatrix);
         break;
 
     case WORLD_VIEW_PROJECTION_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
         break;
 
     case INVERSE_TRANSPOSE_WORLD_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
         break;
 
     case INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
         break;
 
     case CAMERA_WORLD_POSITION:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
         break;
 
     case CAMERA_VIEW_POSITION:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
         break;
 
     case MATRIX_PALETTE:
@@ -245,15 +259,22 @@ void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBind
             MeshSkin* skin = model ? model->getSkin() : NULL;
             if (skin)
             {
-                getParameter(uniformName)->bindValue(skin, &MeshSkin::getMatrixPalette, &MeshSkin::getMatrixPaletteSize);
+                GP_ASSERT(param);
+                param->bindValue(skin, &MeshSkin::getMatrixPalette, &MeshSkin::getMatrixPaletteSize);
             }
         }
         break;
+
+    default:
+        GP_ERROR("Unsupported auto binding type (%d).", autoBinding);
+        break;
     }
 }
 
 void RenderState::bind(Pass* pass)
 {
+    GP_ASSERT(pass);
+
     // Get the combined modified state bits for our RenderState hierarchy.
     long stateOverrideBits = _state ? _state->_bits : 0;
     RenderState* rs = _parent;
@@ -276,6 +297,7 @@ void RenderState::bind(Pass* pass)
     {
         for (unsigned int i = 0, count = rs->_parameters.size(); i < count; ++i)
         {
+            GP_ASSERT(rs->_parameters[i]);
             rs->_parameters[i]->bind(effect);
         }
 
@@ -291,7 +313,7 @@ RenderState* RenderState::getTopmost(RenderState* below)
     RenderState* rs = this;
     if (rs == below)
     {
-        // Nothing below ourself
+        // Nothing below ourself.
         return NULL;
     }
 
@@ -299,7 +321,7 @@ RenderState* RenderState::getTopmost(RenderState* below)
     {
         if (rs->_parent == below || rs->_parent == NULL)
         {
-            // Stop traversing up here
+            // Stop traversing up here.
             return rs;
         }
         rs = rs->_parent;
@@ -310,6 +332,8 @@ RenderState* RenderState::getTopmost(RenderState* below)
 
 void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context) const
 {
+    GP_ASSERT(renderState);
+
     for (std::map<std::string, AutoBinding>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
     {
         renderState->setParameterAutoBinding(it->first.c_str(), it->second);
@@ -317,6 +341,7 @@ void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context)
     for (std::vector<MaterialParameter*>::const_iterator it = _parameters.begin(); it != _parameters.end(); ++it)
     {
         const MaterialParameter* param = *it;
+        GP_ASSERT(param);
 
         MaterialParameter* paramCopy = new MaterialParameter(param->getName());
         param->cloneInto(paramCopy);
@@ -368,31 +393,42 @@ void RenderState::StateBlock::bind()
 
 void RenderState::StateBlock::bindNoRestore()
 {
+    GP_ASSERT(_defaultState);
+
     // Update any state that differs from _defaultState and flip _defaultState bits
     if ((_bits & RS_BLEND) && (_blendEnabled != _defaultState->_blendEnabled))
     {
-        _blendEnabled ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
+        if (_blendEnabled)
+            GL_ASSERT( glEnable(GL_BLEND) )
+        else
+            GL_ASSERT( glDisable(GL_BLEND) );
         _defaultState->_blendEnabled = _blendEnabled;
     }
     if ((_bits & RS_BLEND_FUNC) && (_srcBlend != _defaultState->_srcBlend || _dstBlend != _defaultState->_dstBlend))
     {
-        glBlendFunc((GLenum)_srcBlend, (GLenum)_dstBlend);
+        GL_ASSERT( glBlendFunc((GLenum)_srcBlend, (GLenum)_dstBlend) );
         _defaultState->_srcBlend = _srcBlend;
         _defaultState->_dstBlend = _dstBlend;
     }
     if ((_bits & RS_CULL_FACE) && (_cullFaceEnabled != _defaultState->_cullFaceEnabled))
     {
-        _cullFaceEnabled ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE);
+        if (_cullFaceEnabled)
+            GL_ASSERT( glEnable(GL_CULL_FACE) ) 
+        else
+            GL_ASSERT( glDisable(GL_CULL_FACE) );
         _defaultState->_cullFaceEnabled = _cullFaceEnabled;
     }
     if ((_bits & RS_DEPTH_TEST) && (_depthTestEnabled != _defaultState->_depthTestEnabled))
     {
-        _depthTestEnabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
+        if (_depthTestEnabled) 
+            GL_ASSERT( glEnable(GL_DEPTH_TEST) ) 
+        else 
+            GL_ASSERT( glDisable(GL_DEPTH_TEST) );
         _defaultState->_depthTestEnabled = _depthTestEnabled;
     }
     if ((_bits & RS_DEPTH_WRITE) && (_depthWriteEnabled != _defaultState->_depthWriteEnabled))
     {
-        glDepthMask(_depthWriteEnabled ? GL_TRUE : GL_FALSE);
+        GL_ASSERT( glDepthMask(_depthWriteEnabled ? GL_TRUE : GL_FALSE) );
         _defaultState->_depthWriteEnabled = _depthWriteEnabled;
     }
 
@@ -401,7 +437,9 @@ void RenderState::StateBlock::bindNoRestore()
 
 void RenderState::StateBlock::restore(long stateOverrideBits)
 {
-    // If there is no state to restore (i.e. no non-default state), do nothing
+    GP_ASSERT(_defaultState);
+
+    // If there is no state to restore (i.e. no non-default state), do nothing.
     if (_defaultState->_bits == 0)
     {
         return;
@@ -410,32 +448,32 @@ void RenderState::StateBlock::restore(long stateOverrideBits)
     // Restore any state that is not overridden and is not default
     if (!(stateOverrideBits & RS_BLEND) && (_defaultState->_bits & RS_BLEND))
     {
-        glDisable(GL_BLEND);
+        GL_ASSERT( glDisable(GL_BLEND) );
         _defaultState->_bits &= ~RS_BLEND;
         _defaultState->_blendEnabled = false;
     }
     if (!(stateOverrideBits & RS_BLEND_FUNC) && (_defaultState->_bits & RS_BLEND_FUNC))
     {
-        glBlendFunc(GL_ONE, GL_ONE);
+        GL_ASSERT( glBlendFunc(GL_ONE, GL_ONE) );
         _defaultState->_bits &= ~RS_BLEND_FUNC;
         _defaultState->_srcBlend = RenderState::BLEND_ONE;
         _defaultState->_dstBlend = RenderState::BLEND_ONE;
     }
     if (!(stateOverrideBits & RS_CULL_FACE) && (_defaultState->_bits & RS_CULL_FACE))
     {
-        glDisable(GL_CULL_FACE);
+        GL_ASSERT( glDisable(GL_CULL_FACE) );
         _defaultState->_bits &= ~RS_CULL_FACE;
         _defaultState->_cullFaceEnabled = false;
     }
     if (!(stateOverrideBits & RS_DEPTH_TEST) && (_defaultState->_bits & RS_DEPTH_TEST))
     {
-        glDisable(GL_DEPTH_TEST);
+        GL_ASSERT( glDisable(GL_DEPTH_TEST) );
         _defaultState->_bits &= ~RS_DEPTH_TEST;
         _defaultState->_depthTestEnabled = false;
     }
     if (!(stateOverrideBits & RS_DEPTH_WRITE) && (_defaultState->_bits & RS_DEPTH_WRITE))
     {
-        glDepthMask(GL_TRUE);
+        GL_ASSERT( glDepthMask(GL_TRUE) );
         _defaultState->_bits &= ~RS_DEPTH_WRITE;
         _defaultState->_depthWriteEnabled = true;
     }
@@ -443,12 +481,14 @@ void RenderState::StateBlock::restore(long stateOverrideBits)
 
 void RenderState::StateBlock::enableDepthWrite()
 {
+    GP_ASSERT(_defaultState);
+
     // 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);
+        GL_ASSERT( glDepthMask(GL_TRUE) );
         _defaultState->_bits &= ~RS_DEPTH_WRITE;
         _defaultState->_depthWriteEnabled = true;
     }
@@ -456,6 +496,8 @@ void RenderState::StateBlock::enableDepthWrite()
 
 bool parseBoolean(const char* value)
 {
+    GP_ASSERT(value);
+
     if (strlen(value) == 4)
     {
         return (
@@ -470,35 +512,39 @@ bool parseBoolean(const char* value)
 
 RenderState::Blend parseBlend(const char* value)
 {
-    // Conver the string to uppercase for comparison
+    GP_ASSERT(value);
+
+    // Convert the string to uppercase for comparison.
     std::string upper(value);
     std::transform(upper.begin(), upper.end(), upper.begin(), (int(*)(int))toupper);
     if (upper == "ZERO")
         return RenderState::BLEND_ZERO;
-    if (upper == "ONE")
+    else if (upper == "ONE")
         return RenderState::BLEND_ONE;
-    if (upper == "SRC_ALPHA")
+    else if (upper == "SRC_ALPHA")
         return RenderState::BLEND_SRC_ALPHA;
-    if (upper == "ONE_MINUS_SRC_ALPHA")
+    else if (upper == "ONE_MINUS_SRC_ALPHA")
         return RenderState::BLEND_ONE_MINUS_SRC_ALPHA;
-    if (upper == "DST_ALPHA")
+    else if (upper == "DST_ALPHA")
         return RenderState::BLEND_DST_ALPHA;
-    if (upper == "ONE_MINUS_DST_ALPHA")
+    else if (upper == "ONE_MINUS_DST_ALPHA")
         return RenderState::BLEND_ONE_MINUS_DST_ALPHA;
-    if (upper == "CONSTANT_ALPHA")
+    else if (upper == "CONSTANT_ALPHA")
         return RenderState::BLEND_CONSTANT_ALPHA;
-    if (upper == "ONE_MINUS_CONSTANT_ALPHA")
+    else if (upper == "ONE_MINUS_CONSTANT_ALPHA")
         return RenderState::BLEND_ONE_MINUS_CONSTANT_ALPHA;
-    if (upper == "SRC_ALPHA_SATURATE")
+    else if (upper == "SRC_ALPHA_SATURATE")
         return RenderState::BLEND_SRC_ALPHA_SATURATE;
-
-    GP_WARN("Warning: Unrecognized blend value (%s), defaulting to BLEND_ONE.", value);
-    return RenderState::BLEND_ONE;
+    else
+    {
+        GP_ERROR("Unsupported blend value (%s). (Will default to BLEND_ONE if errors are treated as warnings)", value);
+        return RenderState::BLEND_ONE;
+    }
 }
 
 void RenderState::StateBlock::setState(const char* name, const char* value)
 {
-    GP_ASSERT(name && value);
+    GP_ASSERT(name);
 
     if (strcmp(name, "blend") == 0)
     {
@@ -526,7 +572,7 @@ void RenderState::StateBlock::setState(const char* name, const char* value)
     }
     else
     {
-        GP_WARN("Warning: Invalid render state: %s", name);
+        GP_ERROR("Unsupported render state string '%s'.", name);
     }
 }
 

+ 7 - 3
gameplay/src/RenderTarget.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 static std::vector<RenderTarget*> __renderTargets;
 
 RenderTarget::RenderTarget(const char* id)
-    : _id(id), _texture(NULL)
+    : _id(id ? id : ""), _texture(NULL)
 {
 }
 
@@ -15,7 +15,7 @@ RenderTarget::~RenderTarget()
 {
     SAFE_RELEASE(_texture);
 
-    // Remove ourself from the cache
+    // Remove ourself from the cache.
     std::vector<RenderTarget*>::iterator it = std::find(__renderTargets.begin(), __renderTargets.end(), this);
     if (it != __renderTargets.end())
     {
@@ -25,10 +25,11 @@ RenderTarget::~RenderTarget()
 
 RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned int height)
 {
-    // Create a new texture with the given width
+    // Create a new texture with the given width.
     Texture* texture = Texture::create(Texture::RGBA, width, height, NULL, false);
     if (texture == NULL)
     {
+        GP_ERROR("Failed to create texture for render target.");
         return NULL;
     }
 
@@ -42,11 +43,14 @@ RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned
 
 RenderTarget* RenderTarget::getRenderTarget(const char* id)
 {
+    GP_ASSERT(id);
+
     // Search the vector for a matching ID.
     std::vector<RenderTarget*>::const_iterator it;
     for (it = __renderTargets.begin(); it < __renderTargets.end(); it++)
     {
         RenderTarget* dst = *it;
+        GP_ASSERT(dst);
         if (strcmp(id, dst->getID()) == 0)
         {
             return dst;

+ 36 - 13
gameplay/src/SpriteBatch.cpp

@@ -83,7 +83,9 @@ SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsign
 
 SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
 {
-    GP_ASSERT(texture != NULL);
+    GP_ASSERT(texture);
+    GP_ASSERT(texture->getWidth());
+    GP_ASSERT(texture->getHeight());
 
     bool customEffect = (effect != NULL);
     if (!customEffect)
@@ -94,7 +96,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
             __spriteEffect = Effect::createFromSource(SPRITE_VSH, SPRITE_FSH);
             if (__spriteEffect == NULL)
             {
-                GP_ERROR("Unable to load sprite effect.");
+                GP_ERROR("Unable to create default sprite effect.");
                 return NULL;
             }
 
@@ -120,25 +122,33 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     }
     if (!samplerUniform)
     {
-        GP_ERROR("No uniform of type GL_SAMPLER_2D found in sprite effect.");
+        GP_ERROR("Failed to create sprite batch; required uniform of type GL_SAMPLER_2D not found in sprite effect.");
         SAFE_RELEASE(effect);
         return NULL;
     }
 
-    // Wrap the effect in a material
+    // Wrap the effect in a material.
     Material* material = Material::create(effect); // +ref effect
+    if (!material)
+    {
+        GP_ERROR("Failed to create material for sprite batch.");
+        SAFE_RELEASE(effect);
+        return NULL;
+    }
 
-    // Set initial material state
+    // Set initial material state.
+    GP_ASSERT(material->getStateBlock());
     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
+    // Bind the texture to the material as a sampler.
     Texture::Sampler* sampler = Texture::Sampler::create(texture); // +ref texture
+    GP_ASSERT(material->getParameter(samplerUniform->getName()));
     material->getParameter(samplerUniform->getName())->setValue(sampler);
     SAFE_RELEASE(sampler);
 
-    // Define the vertex format for the batch
+    // Define the vertex format for the batch.
     VertexFormat::Element vertexElements[] =
     {
         VertexFormat::Element(VertexFormat::POSITION, 3),
@@ -147,11 +157,11 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     };
     VertexFormat vertexFormat(vertexElements, 3);
 
-    // Create the mesh batch
+    // 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
+    // Create the batch.
     SpriteBatch* batch = new SpriteBatch();
     batch->_customEffect = customEffect;
     batch->_batch = meshBatch;
@@ -159,6 +169,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
 
     // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
+    GP_ASSERT(material->getParameter("u_projectionMatrix"));
     material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getOrthoMatrix);
 
     return batch;
@@ -166,6 +177,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
 
 void SpriteBatch::begin()
 {
+    GP_ASSERT(_batch);
     _batch->begin();
 }
 
@@ -206,6 +218,8 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
 void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
                        const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
 {
+    GP_ASSERT(_batch);
+
     float x = dst.x;
     float y = dst.y;
 
@@ -251,6 +265,8 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
 void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height,
     float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
 {
+    GP_ASSERT(_batch);
+
     // Calculate the vertex positions.
     //static Vector3 p[4];
 
@@ -380,14 +396,14 @@ void SpriteBatch::addSprite(float x, float y, float width, float height, float u
 
 void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 {
-    GP_ASSERT(vertices);
-    GP_ASSERT(indices);
-
+    GP_ASSERT(_batch);
     _batch->add(vertices, vertexCount, indices, indexCount);
 }
 
 void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter)
 {
+    GP_ASSERT(_batch);
+
     // Treat the given position as the center if the user specified it as such.
     if (positionIsCenter)
     {
@@ -411,24 +427,28 @@ void SpriteBatch::draw(float x, float y, float z, float width, float height, flo
 
 void SpriteBatch::end()
 {
-    // Finish and draw the batch
+    // Finish and draw the batch.
+    GP_ASSERT(_batch);
     _batch->end();
     _batch->draw();
 }
 
 RenderState::StateBlock* SpriteBatch::getStateBlock() const
 {
+    GP_ASSERT(_batch && _batch->getMaterial());
     return _batch->getMaterial()->getStateBlock();
 }
 
 Material* SpriteBatch::getMaterial() const
 {
+    GP_ASSERT(_batch);
     return _batch->getMaterial();
 }
 
 void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 {
     // Bind the specified matrix to a parameter named 'u_projectionMatrix' (assumed to exist).
+    GP_ASSERT(_batch && _batch->getMaterial() && _batch->getMaterial()->getParameter("u_projectionMatrix"));
     _batch->getMaterial()->getParameter("u_projectionMatrix")->setValue(matrix);
 }
 
@@ -442,6 +462,9 @@ const Matrix& SpriteBatch::getOrthoMatrix() const
 
 bool SpriteBatch::clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2)
 {
+    GP_ASSERT(width);
+    GP_ASSERT(height);
+
     // Clip the rectangle given by { x, y, width, height } into clip.
     // We need to scale the uvs accordingly as we do this.
 

+ 5 - 4
gameplay/src/Technique.cpp

@@ -9,8 +9,6 @@ namespace gameplay
 Technique::Technique(const char* id, Material* material)
     : _id(id ? id : ""), _material(material)
 {
-    GP_ASSERT(material);
-
     RenderState::_parent = material;
 }
 
@@ -36,15 +34,17 @@ unsigned int Technique::getPassCount() const
 Pass* Technique::getPass(unsigned int index) const
 {
     GP_ASSERT(index < _passes.size());
-
     return _passes[index];
 }
 
 Pass* Technique::getPass(const char* id) const
 {
+    GP_ASSERT(id);
+
     for (unsigned int i = 0, count = _passes.size(); i < count; ++i)
     {
         Pass* pass = _passes[i];
+        GP_ASSERT(pass);
         if (strcmp(pass->getId(), id) == 0)
         {
             return pass;
@@ -57,11 +57,12 @@ Pass* Technique::getPass(const char* id) const
 Technique* Technique::clone(Material* material, NodeCloneContext &context) const
 {
     Technique* technique = new Technique(getId(), material);
-    technique->_material = material;
     for (std::vector<Pass*>::const_iterator it = _passes.begin(); it != _passes.end(); ++it)
     {
         Pass* pass = *it;
+        GP_ASSERT(pass);
         Pass* passCopy = pass->clone(technique, context);
+        GP_ASSERT(passCopy);
         technique->_passes.push_back(passCopy);
     }
     RenderState::cloneInto(technique, context);

+ 210 - 96
gameplay/src/Texture.cpp

@@ -56,7 +56,7 @@ Texture::~Texture()
 {
     if (_handle)
     {
-        glDeleteTextures(1, &_handle);
+        GL_ASSERT( glDeleteTextures(1, &_handle) );
         _handle = 0;
     }
 
@@ -73,10 +73,13 @@ Texture::~Texture()
 
 Texture* Texture::create(const char* path, bool generateMipmaps)
 {
+    GP_ASSERT(path);
+
     // Search texture cache first.
     for (unsigned int i = 0, count = __textureCache.size(); i < count; ++i)
     {
         Texture* t = __textureCache[i];
+        GP_ASSERT(t);
         if (t->_path == path)
         {
             // If 'generateMipmaps' is true, call Texture::generateMipamps() to force the 
@@ -111,7 +114,7 @@ Texture* Texture::create(const char* path, bool generateMipmaps)
             }
             else if (tolower(ext[1]) == 'p' && tolower(ext[2]) == 'v' && tolower(ext[3]) == 'r')
             {
-                // PowerVR Compressed Texture RGBA
+                // PowerVR Compressed Texture RGBA.
                 texture = createCompressedPVRTC(path);
             }
             else if (tolower(ext[1]) == 'd' && tolower(ext[2]) == 'd' && tolower(ext[3]) == 's')
@@ -134,21 +137,24 @@ Texture* Texture::create(const char* path, bool generateMipmaps)
         return texture;
     }
 
-    GP_ERROR("Failed to load texture: %s", path);
+    GP_ERROR("Failed to load texture from file '%s'.", path);
     return NULL;
 }
 
 Texture* Texture::create(Image* image, bool generateMipmaps)
 {
+    GP_ASSERT(image);
+
     switch (image->getFormat())
     {
     case Image::RGB:
         return create(Texture::RGB, image->getWidth(), image->getHeight(), image->getData(), generateMipmaps);
     case Image::RGBA:
         return create(Texture::RGBA, image->getWidth(), image->getHeight(), image->getData(), generateMipmaps);
+    default:
+        GP_ERROR("Unsupported image format (%d).", image->getFormat());
+        return NULL;
     }
-
-    return NULL;
 }
 
 Texture* Texture::create(Format format, unsigned int width, unsigned int height, unsigned char* data, bool generateMipmaps)
@@ -168,7 +174,7 @@ Texture* Texture::create(Format format, unsigned int width, unsigned int height,
         GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_BYTE, data) );
     }
 
-    // Set initial minification filter based on whether or not mipmaping was enabled
+    // Set initial minification filter based on whether or not mipmaping was enabled.
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generateMipmaps ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR) );
 
     Texture* texture = new Texture();
@@ -212,20 +218,38 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     FILE* file = FileSystem::openFile(path, "rb");
     if (file == NULL)
     {
-        GP_ERROR("Failed to load file: %s", path);
+        GP_ERROR("Failed to load file '%s'.", path);
         return NULL;
     }
 
-    // Read first 4 bytes to determine PVRTC format
+    // Read first 4 bytes to determine PVRTC format.
     unsigned int read;
     unsigned int version;
     read = fread(&version, sizeof(unsigned int), 1, file);
-    assert(read == 1);
+    if (read != 1)
+    {
+        GP_ERROR("Failed to read PVR version.");
+        if (fclose(file) != 0)
+        {
+            GP_ERROR("Failed to close PVR file '%s'.", path);
+            return NULL;
+        }
+        return NULL;
+    }
 
-    // Rewind to start of header
-    fseek(file, 0, SEEK_SET);
+    // Rewind to start of header.
+    if (fseek(file, 0, SEEK_SET) != 0)
+    {
+        GP_ERROR("Failed to seek backwards to beginning of file after reading PVR version.");
+        if (fclose(file) != 0)
+        {
+            GP_ERROR("Failed to close PVR file '%s'.", path);
+            return NULL;
+        }
+        return NULL;
+    }
 
-    // Read texture data
+    // Read texture data.
     GLsizei width, height;
     GLenum format;
     GLubyte* data = NULL;
@@ -233,60 +257,75 @@ Texture* Texture::createCompressedPVRTC(const char* path)
 
     if (version == 0x03525650)
     {
-    	// Modern PVR file format.
-    	data = readCompressedPVRTC(path, file, &width, &height, &format, &mipMapCount);
+        // Modern PVR file format.
+        data = readCompressedPVRTC(path, file, &width, &height, &format, &mipMapCount);
     }
     else
     {
-    	// Legacy PVR file format.
-    	data = readCompressedPVRTCLegacy(path, file, &width, &height, &format, &mipMapCount);
+        // Legacy PVR file format.
+        data = readCompressedPVRTCLegacy(path, file, &width, &height, &format, &mipMapCount);
     }
-
-    fclose(file);
-
     if (data == NULL)
     {
-    	GP_ERROR("Failed to read PVR file: %s", path);
-    	return NULL;
+        GP_ERROR("Failed to read texture data from PVR file '%s'.", path);
+        if (fclose(file) != 0)
+        {
+            GP_ERROR("Failed to close PVR file '%s'.", path);
+            return NULL;
+        }
+        return NULL;
+    }
+
+    if (fclose(file) != 0)
+    {
+        GP_ERROR("Failed to close PVR file '%s'.", path);
+        return NULL;
     }
 
     int bpp = (format == GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG || format == GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG) ? 2 : 4;
 
     // Generate our texture.
-	GLuint textureId;
-	GL_ASSERT( glGenTextures(1, &textureId) );
-	GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
-	GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipMapCount > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) );
-
-	Texture* texture = new Texture();
-	texture->_handle = textureId;
-	texture->_width = width;
-	texture->_height = height;
-	texture->_mipmapped = mipMapCount > 1;
-	texture->_compressed = true;
-
-	// Load the data for each level
-	GLubyte* ptr = data;
-	for (unsigned int level = 0; level < mipMapCount; ++level)
-	{
-		unsigned int dataSize = computePVRTCDataSize(width, height, bpp);
-
-		// Upload data to GL
-		GL_ASSERT( glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, dataSize, ptr) );
-
-		width = std::max(width >> 1, 1);
-		height = std::max(height >> 1, 1);
-		ptr += dataSize;
-	}
-
-	// Free data
-	SAFE_DELETE_ARRAY(data);
+    GLuint textureId;
+    GL_ASSERT( glGenTextures(1, &textureId) );
+    GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
+    GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipMapCount > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) );
+
+    Texture* texture = new Texture();
+    texture->_handle = textureId;
+    texture->_width = width;
+    texture->_height = height;
+    texture->_mipmapped = mipMapCount > 1;
+    texture->_compressed = true;
+
+    // Load the data for each level.
+    GLubyte* ptr = data;
+    for (unsigned int level = 0; level < mipMapCount; ++level)
+    {
+        unsigned int dataSize = computePVRTCDataSize(width, height, bpp);
+
+        // Upload data to GL.
+        GL_ASSERT( glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, dataSize, ptr) );
+
+        width = std::max(width >> 1, 1);
+        height = std::max(height >> 1, 1);
+        ptr += dataSize;
+    }
+
+    // Free data.
+    SAFE_DELETE_ARRAY(data);
 
     return texture;
 }
 
 GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
 {
+    GP_ASSERT(file);
+    GP_ASSERT(path);
+    GP_ASSERT(width);
+    GP_ASSERT(height);
+    GP_ASSERT(format);
+    GP_ASSERT(mipMapCount);
+
     struct pvrtc_file_header
     {
         unsigned int version;
@@ -305,15 +344,19 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
 
     unsigned int read;
 
-    // Read header data
+    // Read header data.
     pvrtc_file_header header;
     read = fread(&header, sizeof(pvrtc_file_header), 1, file);
-    assert(read == 1);
+    if (read != 1)
+    {
+        GP_ERROR("Failed to read PVR header data for file '%s'.", path);
+        return NULL;
+    }
 
     if (header.pixelFormat[1] != 0)
     {
-        // Unsupported pixel format
-        GP_ERROR("Unsupported pixel format in PVR file (MSB == %d != 0): %s", header.pixelFormat[1], path);
+        // Unsupported pixel format.
+        GP_ERROR("Unsupported pixel format in PVR file '%s'. (MSB == %d != 0)", path, header.pixelFormat[1]);
         return NULL;
     }
 
@@ -337,8 +380,8 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
         bpp = 4;
         break;
     default:
-        // Unsupported format
-        GP_ERROR("Unsupported pixel format value (%d) in PVR file: %s", header.pixelFormat[0], path);
+        // Unsupported format.
+        GP_ERROR("Unsupported pixel format value (%d) in PVR file '%s'.", header.pixelFormat[0], path);
         return NULL;
     }
 
@@ -346,8 +389,12 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
     *height = (GLsizei)header.height;
     *mipMapCount = header.mipMapCount;
 
-    // Skip meta-data
-    assert( fseek(file, header.metaDataSize, SEEK_CUR) == 0 );
+    // Skip meta-data.
+    if (fseek(file, header.metaDataSize, SEEK_CUR) != 0)
+    {
+        GP_ERROR("Failed to seek past header meta data in PVR file '%s'.", path);
+        return NULL;
+    }
 
     // Compute total size of data to be read.
     int w = *width;
@@ -356,14 +403,18 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
     for (unsigned int level = 0; level < header.mipMapCount; ++level)
     {
         dataSize += computePVRTCDataSize(w, h, bpp);
-    	w = std::max(w>>1, 1);
-    	h = std::max(h>>1, 1);
+        w = std::max(w>>1, 1);
+        h = std::max(h>>1, 1);
     }
 
-    // Read data
+    // Read data.
     GLubyte* data = new GLubyte[dataSize];
     read = fread(data, 1, dataSize, file);
-    assert(read == dataSize);
+    if (read != dataSize)
+    {
+        GP_ERROR("Failed to read texture data from PVR file '%s'.", path);
+        return NULL;
+    }
 
     return data;
 }
@@ -389,28 +440,35 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
         unsigned int surfaceCount;          // number of surfaces present in the pvrtc
     };
 
-    // Read the file header
+    // Read the file header.
     unsigned int size = sizeof(pvrtc_file_header_legacy);
     pvrtc_file_header_legacy header;
     unsigned int read = (int)fread(&header, 1, size, file);
-    GP_ASSERT(read == size);
     if (read != size)
     {
-        GP_ERROR("Read file header error for pvrtc file: %s (%d < %d)", path, (int)read, (int)size);
+        GP_ERROR("Failed to read file header for pvrtc file '%s'.", path);
+        if (fclose(file) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
         return NULL;
     }
 
-    // Proper file header identifier
+    // Proper file header identifier.
     if (PVRTCIdentifier[0] != (char)((header.pvrtcTag >>  0) & 0xff) ||
         PVRTCIdentifier[1] != (char)((header.pvrtcTag >>  8) & 0xff) ||
         PVRTCIdentifier[2] != (char)((header.pvrtcTag >> 16) & 0xff) ||
         PVRTCIdentifier[3] != (char)((header.pvrtcTag >> 24) & 0xff))
      {
-        GP_ERROR("Invalid PVRTC compressed texture file: %s", path);
+        GP_ERROR("Failed to load pvrtc file '%s': invalid header.", path);
+        if (fclose(file) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
         return NULL;
     }
 
-    // Format flags for GLenum format
+    // Format flags for GLenum format.
     if (header.bpp == 4)
     {
         *format = header.alphaBitMask ? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
@@ -421,7 +479,11 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
     }
     else
     {
-        GP_ERROR("Invalid PVRTC compressed texture format flags for file: %s", path);
+        GP_ERROR("Failed to load pvrtc file '%s': invalid pvrtc compressed texture format flags.", path);
+        if (fclose(file) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
         return NULL;
     }
 
@@ -431,10 +493,13 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
 
     GLubyte* data = new GLubyte[header.dataSize];
     read = (int)fread(data, 1, header.dataSize, file);
-    GP_ASSERT(read == header.dataSize);
     if (read != header.dataSize)
     {
-        GP_ERROR("Read file data error for pvrtc file: %s (%d < %d)", path, (int)read, (int)header.dataSize);
+        GP_ERROR("Failed to load texture data for pvrtc file '%s'.", path);
+        if (fclose(file) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
         SAFE_DELETE_ARRAY(data);
         return NULL;
     }
@@ -444,7 +509,9 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
 
 Texture* Texture::createCompressedDDS(const char* path)
 {
-    // DDS file structures
+    GP_ASSERT(path);
+
+    // DDS file structures.
     struct dds_pixel_format
     {
         unsigned int dwSize;
@@ -485,28 +552,45 @@ Texture* Texture::createCompressedDDS(const char* path)
 
     Texture* texture = NULL;
 
-    // Read DDS file
+    // Read DDS file.
     FILE* fp = FileSystem::openFile(path, "rb");
     if (fp == NULL)
+    {
+        GP_ERROR("Failed to open file '%s'.", path);
         return NULL;
+    }
 
-    // Validate DDS magic number
+    // Validate DDS magic number.
     char code[4];
     if (fread(code, 1, 4, fp) != 4 || strncmp(code, "DDS ", 4) != 0)
-        return NULL; // not a valid dds file
+    {
+        GP_ERROR("Failed to read DDS file '%s': invalid DDS magic number.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
+        return NULL;
+    }
 
-    // Read DDS header
+    // Read DDS header.
     dds_header header;
     if (fread(&header, sizeof(dds_header), 1, fp) != 1)
+    {
+        GP_ERROR("Failed to read header for DDS file '%s'.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
         return NULL;
+    }
 
     if ((header.dwFlags & 0x20000/*DDSD_MIPMAPCOUNT*/) == 0)
     {
-        // Mipmap count not specified (non-mipmapped texture)
+        // Mipmap count not specified (non-mipmapped texture).
         header.dwMipMapCount = 1;
     }
 
-    // Allocate mip level structures
+    // Allocate mip level structures.
     dds_mip_level* mipLevels = new dds_mip_level[header.dwMipMapCount];
     memset(mipLevels, 0, sizeof(dds_mip_level) * header.dwMipMapCount);
 
@@ -520,7 +604,7 @@ Texture* Texture::createCompressedDDS(const char* path)
     {
         compressed = true;
 
-        // Compressed
+        // Compressed.
         switch (header.ddspf.dwFourCC)
         {
         case ('D'|('X'<<8)|('T'<<16)|('1'<<24)):
@@ -548,7 +632,11 @@ Texture* Texture::createCompressedDDS(const char* path)
             bytesPerBlock = 16;
             break;
         default:
-            // Unsupported
+            GP_ERROR("Unsupported compressed texture format (%d) for DDS file '%s'.", header.ddspf.dwFourCC, path);
+            if (fclose(fp) != 0)
+            {
+                GP_ERROR("Failed to close file '%s'.", path);
+            }
             return NULL;
         }
 
@@ -562,7 +650,17 @@ Texture* Texture::createCompressedDDS(const char* path)
             if (fread(mipLevels[i].data, 1, mipLevels[i].size, fp) != (unsigned int)mipLevels[i].size)
             {
                 GP_ERROR("Failed to load dds compressed texture bytes for texture: %s", path);
-                goto cleanup;
+                
+                // Cleanup mip data.
+                for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
+                    SAFE_DELETE_ARRAY(mipLevels[i].data);
+                SAFE_DELETE_ARRAY(mipLevels);
+
+                if (fclose(fp) != 0)
+                {
+                    GP_ERROR("Failed to close file '%s'.", path);
+                }
+                return texture;
             }
 
             width  = std::max(1, width >> 1);
@@ -573,27 +671,48 @@ Texture* Texture::createCompressedDDS(const char* path)
     {
         // RGB (uncompressed)
         // Note: Use GL_BGR as internal format to flip bytes.
-        return NULL; // unsupported
+        GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGB format is not supported.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
+        return NULL;
     }
     else if (header.ddspf.dwFlags == 0x41/*DDPF_RGB|DDPF_ALPHAPIXELS*/)
     {
         // RGBA (uncompressed)
         // Note: Use GL_BGRA as internal format to flip bytes.
-        return NULL; // unsupported
+        GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGBA format is not supported.", path);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
+        return NULL;
     }
     else
     {
-        // Unsupported
+        // Unsupported.
+        GP_ERROR("Failed to create texture from DDS file '%s': unsupported flags (%d).", path, header.ddspf.dwFlags);
+        if (fclose(fp) != 0)
+        {
+            GP_ERROR("Failed to close file '%s'.", path);
+        }
         return NULL;
     }
+    
+    // Close file.
+    if (fclose(fp) != 0)
+    {
+        GP_ERROR("Failed to close file '%s'.", path);
+    }
 
-    // Generate GL texture
+    // Generate GL texture.
     GLuint textureId;
     GL_ASSERT( glGenTextures(1, &textureId) );
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, header.dwMipMapCount > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) );
 
-    // Create gameplay texture
+    // Create gameplay texture.
     texture = new Texture();
     texture->_handle = textureId;
     texture->_width = header.dwWidth;
@@ -601,7 +720,7 @@ Texture* Texture::createCompressedDDS(const char* path)
     texture->_compressed = compressed;
     texture->_mipmapped = header.dwMipMapCount > 1;
 
-    // Load texture data
+    // Load texture data.
     for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
     {
         if (compressed)
@@ -613,14 +732,7 @@ Texture* Texture::createCompressedDDS(const char* path)
             GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, mipLevels[i].width, mipLevels[i].height, 0, format, GL_UNSIGNED_INT, mipLevels[i].data) );
         }
     }
-
-cleanup:
-
-    // Cleanup mip data
-    for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
-        SAFE_DELETE_ARRAY(mipLevels[i].data);
-    SAFE_DELETE_ARRAY(mipLevels);
-
+    
     return texture;
 }
 
@@ -686,6 +798,7 @@ bool Texture::isCompressed() const
 Texture::Sampler::Sampler(Texture* texture)
     : _texture(texture), _wrapS(Texture::REPEAT), _wrapT(Texture::REPEAT), _magFilter(Texture::LINEAR)
 {
+    GP_ASSERT(texture);
     _minFilter = texture->isMipmapped() ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR;
 }
 
@@ -696,8 +809,7 @@ Texture::Sampler::~Sampler()
 
 Texture::Sampler* Texture::Sampler::create(Texture* texture)
 {
-    GP_ASSERT(texture != NULL);
-
+    GP_ASSERT(texture);
     texture->addRef();
     return new Sampler(texture);
 }
@@ -727,6 +839,8 @@ Texture* Texture::Sampler::getTexture() const
 
 void Texture::Sampler::bind()
 {
+    GP_ASSERT(_texture);
+
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _texture->_handle) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLenum)_wrapS) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLenum)_wrapT) );

+ 13 - 6
gameplay/src/VertexAttributeBinding.cpp

@@ -24,9 +24,7 @@ VertexAttributeBinding::~VertexAttributeBinding()
     }
 
     SAFE_RELEASE(_mesh);
-
     SAFE_RELEASE(_effect);
-
     SAFE_DELETE_ARRAY(_attributes);
 
     if (_handle)
@@ -38,11 +36,14 @@ VertexAttributeBinding::~VertexAttributeBinding()
 
 VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effect)
 {
+    GP_ASSERT(mesh);
+
     // 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];
+        GP_ASSERT(b);
         if (b->_mesh == mesh && b->_effect == effect)
         {
             // Found a match!
@@ -69,6 +70,8 @@ VertexAttributeBinding* VertexAttributeBinding::create(const VertexFormat& verte
 
 VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexFormat& vertexFormat, void* vertexPointer, Effect* effect)
 {
+    GP_ASSERT(effect);
+
     // One-time initialization.
     if (__maxVertexAttribs == 0)
     {
@@ -76,9 +79,9 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
         GL_ASSERT( glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &temp) );
 
         __maxVertexAttribs = temp;
-        GP_ASSERT(__maxVertexAttribs > 0);
         if (__maxVertexAttribs <= 0)
         {
+            GP_ERROR("The maximum number of vertex attributes supported by OpenGL on the current device is 0 or less.");
             return NULL;
         }
     }
@@ -97,6 +100,7 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
 
         if (b->_handle == 0)
         {
+            GP_ERROR("Failed to create VAO handle.");
             SAFE_DELETE(b);
             return NULL;
         }
@@ -140,7 +144,6 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
     for (unsigned int i = 0, count = vertexFormat.getElementCount(); i < count; ++i)
     {
         const VertexFormat::Element& e = vertexFormat.getElement(i);
-
         gameplay::VertexAttribute attrib;
 
         // Constructor vertex attribute name expected in shader.
@@ -189,6 +192,7 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
             attrib = effect->getVertexAttribute(name.c_str());
             break;
         default:
+            // This happens whenever vertex data contains extra information (not an error).
             attrib = -1;
             break;
         }
@@ -220,13 +224,14 @@ void VertexAttributeBinding::setVertexAttribPointer(GLuint indx, GLint size, GLe
 
     if (_handle)
     {
-        // Hardware mode
+        // Hardware mode.
         GL_ASSERT( glVertexAttribPointer(indx, size, type, normalize, stride, pointer) );
         GL_ASSERT( glEnableVertexAttribArray(indx) );
     }
     else
     {
-        // Software mode
+        // Software mode.
+        GP_ASSERT(_attributes);
         _attributes[indx].enabled = true;
         _attributes[indx].size = size;
         _attributes[indx].type = type;
@@ -255,6 +260,7 @@ void VertexAttributeBinding::bind()
             GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
         }
 
+        GP_ASSERT(_attributes);
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         {
             VertexAttribute& a = _attributes[i];
@@ -282,6 +288,7 @@ void VertexAttributeBinding::unbind()
             GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
         }
 
+        GP_ASSERT(_attributes);
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         {
             if (_attributes[i].enabled)

+ 2 - 1
gameplay/src/VertexFormat.cpp

@@ -7,6 +7,8 @@ namespace gameplay
 VertexFormat::VertexFormat(const Element* elements, unsigned int elementCount)
     : _vertexSize(0)
 {
+    GP_ASSERT(elements);
+
     // Copy elements and compute vertex size
     for (unsigned int i = 0; i < elementCount; ++i)
     {
@@ -26,7 +28,6 @@ VertexFormat::~VertexFormat()
 const VertexFormat::Element& VertexFormat::getElement(unsigned int index) const
 {
     GP_ASSERT(index < _elements.size());
-
     return _elements[index];
 }