Przeglądaj źródła

Merge branch 'next' of https://github.com/blackberry-gaming/GamePlay into next-ablake

Conflicts:
	gameplay/src/SpriteBatch.cpp
Adam Blake 13 lat temu
rodzic
commit
68f241012e
63 zmienionych plików z 1890 dodań i 589 usunięć
  1. BIN
      gameplay-api/gameplay.png
  2. 20 0
      gameplay-api/header.html
  3. 8 7
      gameplay.doxyfile
  4. 6 2
      gameplay/.cproject
  5. 1 1
      gameplay/android/jni/Android.mk
  6. 1 3
      gameplay/src/AudioSource.cpp
  7. 34 12
      gameplay/src/Base.h
  8. 10 7
      gameplay/src/DepthStencilTarget.cpp
  9. 31 10
      gameplay/src/Effect.cpp
  10. 86 35
      gameplay/src/FileSystem.cpp
  11. 8 0
      gameplay/src/FileSystem.h
  12. 68 4
      gameplay/src/Font.cpp
  13. 19 9
      gameplay/src/FrameBuffer.cpp
  14. 30 16
      gameplay/src/Game.cpp
  15. 1 0
      gameplay/src/Game.inl
  16. 32 8
      gameplay/src/Image.cpp
  17. 32 4
      gameplay/src/Light.cpp
  18. 66 26
      gameplay/src/Material.cpp
  19. 52 11
      gameplay/src/MaterialParameter.cpp
  20. 203 4
      gameplay/src/Matrix.cpp
  21. 18 3
      gameplay/src/Mesh.cpp
  22. 25 9
      gameplay/src/MeshBatch.cpp
  23. 10 5
      gameplay/src/MeshBatch.inl
  24. 10 0
      gameplay/src/MeshPart.cpp
  25. 6 2
      gameplay/src/MeshSkin.cpp
  26. 48 7
      gameplay/src/Model.cpp
  27. 3 0
      gameplay/src/Node.cpp
  28. 2 1
      gameplay/src/Node.h
  29. 62 29
      gameplay/src/ParticleEmitter.cpp
  30. 7 6
      gameplay/src/Pass.cpp
  31. 70 34
      gameplay/src/PhysicsCharacter.cpp
  32. 37 4
      gameplay/src/PhysicsCollisionObject.cpp
  33. 23 4
      gameplay/src/PhysicsCollisionObject.h
  34. 33 13
      gameplay/src/PhysicsCollisionShape.cpp
  35. 13 1
      gameplay/src/PhysicsConstraint.cpp
  36. 4 0
      gameplay/src/PhysicsConstraint.inl
  37. 129 44
      gameplay/src/PhysicsController.cpp
  38. 7 0
      gameplay/src/PhysicsGenericConstraint.cpp
  39. 12 0
      gameplay/src/PhysicsGenericConstraint.inl
  40. 10 3
      gameplay/src/PhysicsGhostObject.cpp
  41. 5 0
      gameplay/src/PhysicsHingeConstraint.cpp
  42. 5 0
      gameplay/src/PhysicsMotionState.cpp
  43. 34 8
      gameplay/src/PhysicsRigidBody.cpp
  44. 16 0
      gameplay/src/PhysicsRigidBody.inl
  45. 6 0
      gameplay/src/PhysicsSocketConstraint.cpp
  46. 8 0
      gameplay/src/PhysicsSpringConstraint.cpp
  47. 33 14
      gameplay/src/PlatformAndroid.cpp
  48. 110 33
      gameplay/src/PlatformMacOSX.mm
  49. 14 6
      gameplay/src/PlatformQNX.cpp
  50. 67 33
      gameplay/src/PlatformWin32.cpp
  51. 3 1
      gameplay/src/PlatformiOS.mm
  52. 11 2
      gameplay/src/Ref.cpp
  53. 99 53
      gameplay/src/RenderState.cpp
  54. 7 3
      gameplay/src/RenderTarget.cpp
  55. 5 4
      gameplay/src/Technique.cpp
  56. 210 96
      gameplay/src/Texture.cpp
  57. 13 6
      gameplay/src/VertexAttributeBinding.cpp
  58. 2 1
      gameplay/src/VertexFormat.cpp
  59. 1 1
      gameplay/src/gameplay-main-android.cpp
  60. 1 1
      gameplay/src/gameplay-main-ios.mm
  61. 1 1
      gameplay/src/gameplay-main-macosx.mm
  62. 1 1
      gameplay/src/gameplay-main-qnx.cpp
  63. 1 1
      gameplay/src/gameplay-main-win32.cpp

BIN
gameplay-api/gameplay.png


+ 20 - 0
gameplay-api/header.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<title>gameplay: Main Page</title>
+
+<link href="tabs.css" rel="stylesheet" type="text/css"/>
+<link href="doxygen.css" rel="stylesheet" type="text/css" />
+
+<link href="search/search.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="search/search.js"></script>
+<script type="text/javascript">
+  $(document).ready(function() { searchBox.OnSelectItem(0); });
+</script>
+
+</head>
+<body>
+<div id="top"><!-- do not remove this div! -->

+ 8 - 7
gameplay.doxyfile

@@ -45,7 +45,7 @@ PROJECT_BRIEF          =
 # exceed 55 pixels and the maximum width should not exceed 200 pixels. 
 # Doxygen will copy the logo to the output directory.
 
-PROJECT_LOGO           = ./gameplay-api/gameplay.png
+PROJECT_LOGO           = 
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
 # base path where the generated documentation will be put. 
@@ -562,7 +562,7 @@ MAX_INITIALIZER_LINES  = 30
 # at the bottom of the documentation of classes and structs. If set to YES the 
 # list will mention the files that were used to generate the documentation.
 
-SHOW_USED_FILES        = YES
+SHOW_USED_FILES        = NO
 
 # If the sources in your project are distributed over multiple directories 
 # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
@@ -574,7 +574,7 @@ SHOW_DIRECTORIES       = NO
 # This will remove the Files entry from the Quick Index and from the 
 # Folder Tree View (if specified). The default is YES.
 
-SHOW_FILES             = YES
+SHOW_FILES             = NO
 
 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
 # Namespaces page.  This will remove the Namespaces entry from the Quick Index 
@@ -689,7 +689,8 @@ INPUT_ENCODING         = UTF-8
 # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py 
 # *.f90 *.f *.for *.vhd *.vhdl
 
-FILE_PATTERNS          = *.h
+FILE_PATTERNS          = *.h \
+                         *.dox
 
 # The RECURSIVE tag can be used to turn specify whether or not subdirectories 
 # should be searched for input files as well. Possible values are YES and NO. 
@@ -839,7 +840,7 @@ USE_HTAGS              = NO
 # will generate a verbatim copy of the header file for each class for 
 # which an include is specified. Set to NO to disable this.
 
-VERBATIM_HEADERS       = YES
+VERBATIM_HEADERS       = NO
 
 #---------------------------------------------------------------------------
 # configuration options related to the alphabetical class index
@@ -896,7 +897,7 @@ HTML_FILE_EXTENSION    = .html
 # have to redo this when upgrading to a newer version of doxygen or when 
 # changing the value of configuration settings such as GENERATE_TREEVIEW!
 
-HTML_HEADER            = 
+HTML_HEADER            = gameplay-api/header.html
 
 # The HTML_FOOTER tag can be used to specify a personal HTML footer for 
 # each generated HTML page. If it is left blank doxygen will generate a 
@@ -1136,7 +1137,7 @@ DISABLE_INDEX          = NO
 # Since the tree basically has the same information as the tab index you 
 # could consider to set DISABLE_INDEX to NO when enabling this option.
 
-GENERATE_TREEVIEW      = YES
+GENERATE_TREEVIEW      = NO
 
 # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 
 # (range [0,1..20]) that doxygen will group on one line in the generated HTML 

+ 6 - 2
gameplay/.cproject

@@ -29,6 +29,10 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.qccoptions.1968057343" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList"/>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1078137668" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+									<listOptionValue builtIn="false" value="-mfpu=neon"/>
+								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.997142816" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.1988140188" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -197,7 +201,7 @@
 							</tool>
 							<tool id="com.qnx.qcc.tool.linker.1976564730" name="QCC Linker" superClass="com.qnx.qcc.tool.linker">
 								<option id="com.qnx.qcc.option.linker.debug.483005272" name="Debug (-g)" superClass="com.qnx.qcc.option.linker.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.coverage.1325683096" name="Build for Code Coverage (-ftest-coverage -fprofile-arcs -p)" superClass="com.qnx.qcc.option.linker.coverage" value="true" valueType="boolean"/>
+								<option id="com.qnx.qcc.option.linker.coverage.1325683096" name="Build for Code Coverage (-ftest-coverage -fprofile-arcs)" superClass="com.qnx.qcc.option.linker.coverage" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.linker.langcpp.1336725462" name="C++ (-lang-c++)" superClass="com.qnx.qcc.option.linker.langcpp" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.linker.security.261244208" name="Enhanced Security (-Wl,-z,relro -Wl,-z,now)" superClass="com.qnx.qcc.option.linker.security" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.linker.libraryPaths.1333876349" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
@@ -357,7 +361,7 @@
 							</tool>
 							<tool id="com.qnx.qcc.tool.linker.499344619" name="QCC Linker" superClass="com.qnx.qcc.tool.linker">
 								<option id="com.qnx.qcc.option.linker.debug.1036858603" name="Debug (-g)" superClass="com.qnx.qcc.option.linker.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.coverage.120064975" name="Build for Code Coverage (-ftest-coverage -fprofile-arcs -p)" superClass="com.qnx.qcc.option.linker.coverage" value="true" valueType="boolean"/>
+								<option id="com.qnx.qcc.option.linker.coverage.120064975" name="Build for Code Coverage (-ftest-coverage -fprofile-arcs)" superClass="com.qnx.qcc.option.linker.coverage" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.linker.langcpp.732448976" name="C++ (-lang-c++)" superClass="com.qnx.qcc.option.linker.langcpp" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.linker.security.2060919956" name="Enhanced Security (-Wl,-z,relro -Wl,-z,now)" superClass="com.qnx.qcc.option.linker.security" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.linker.libraryPaths.2023922042" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">

+ 1 - 1
gameplay/android/jni/Android.mk

@@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir)/../../src
 
 include $(CLEAR_VARS)
 LOCAL_MODULE    := libgameplay
-LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp FlowLayout.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
+LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp FlowLayout.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp ScrollLayout.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
 LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include" -I"../../external-deps/oggvorbis/include" -I"../../external-deps/openal/include"
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 

+ 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;
         }
 

+ 34 - 12
gameplay/src/Base.h

@@ -51,6 +51,13 @@ namespace gameplay
 extern void printError(const char* format, ...);
 }
 
+// Current function macro.
+#ifdef WIN32
+#define __current__func__ __FUNCTION__
+#else
+#define __current__func__ __func__
+#endif
+
 // Assert macros.
 #ifdef _DEBUG
 #ifdef WIN32
@@ -58,11 +65,10 @@ extern void printError(const char* format, ...);
 #else
 #define GP_FORCE_ASSERTION_FAILURE do { assert(0); } while (0)
 #endif
-
 #define GP_ASSERT(expression) do { \
     if (!(expression)) \
     { \
-        printError("%s -- Assertion '" #expression "' failed.\n", __FUNCTION__); \
+        printError("%s -- Assertion '" #expression "' failed.\n", __current__func__); \
         GP_FORCE_ASSERTION_FAILURE; \
     } } while (0)
 #else
@@ -71,19 +77,23 @@ extern void printError(const char* format, ...);
 #endif
 
 // Error macro.
+#ifdef GP_ERRORS_AS_WARNINGS
+#define GP_ERROR GP_WARN
+#else
 #define GP_ERROR(...) do \
     { \
-        printError("%s -- ", __FUNCTION__); \
+        printError("%s -- ", __current__func__); \
         printError(__VA_ARGS__); \
         printError("\n"); \
         GP_FORCE_ASSERTION_FAILURE; \
         std::exit(-1); \
     } while (0)
+#endif
 
 // Warning macro.
 #define GP_WARN(...) do \
     { \
-        printError("%s -- ", __FUNCTION__); \
+        printError("%s -- ", __current__func__); \
         printError(__VA_ARGS__); \
         printError("\n"); \
     } while (0)
@@ -170,8 +180,20 @@ extern void printError(const char* format, ...);
 #define WINDOW_VSYNC        1
 
 // Graphics (OpenGL)
-#if defined (__QNX__) || defined(__ANDROID__)
-    #include <EGL/egl.h>
+#ifdef __QNX__
+#include <EGL/egl.h>
+    #include <GLES2/gl2.h>
+    #include <GLES2/gl2ext.h>
+    extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray;
+    extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays;
+    extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays;
+    extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
+    #define glClearDepth glClearDepthf
+    #define OPENGL_ES
+    #define USE_PVRTC
+	#define USE_NEON
+#elif __ANDROID__
+	#include <EGL/egl.h>
     #include <GLES2/gl2.h>
     #include <GLES2/gl2ext.h>
     extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray;
@@ -241,12 +263,12 @@ typedef GLuint RenderBufferHandle;
 #ifdef NDEBUG
 #define GL_ASSERT( gl_code ) gl_code
 #else
-#define GL_ASSERT( gl_code ) \
+#define GL_ASSERT( gl_code ) do \
     { \
         gl_code; \
         __gl_error_code = glGetError(); \
         GP_ASSERT(__gl_error_code == GL_NO_ERROR); \
-    }
+    } while(0)
 #endif
 
 /**
@@ -258,7 +280,7 @@ typedef GLuint RenderBufferHandle;
  * macro can be used afterwards to check whether a GL error was
  * encountered executing the specified code.
  */
-#define GL_CHECK( gl_code ) \
+#define GL_CHECK( gl_code ) do \
     { \
         while (glGetError() != GL_NO_ERROR) ; \
         gl_code; \
@@ -267,7 +289,7 @@ typedef GLuint RenderBufferHandle;
         { \
             GP_ERROR(#gl_code ": %d", (int)__gl_error_code); \
         } \
-    }
+    } while(0)
 
 // Global variable to hold GL errors
 extern GLenum __gl_error_code;
@@ -284,7 +306,7 @@ extern GLenum __gl_error_code;
  * The AL_LAST_ERROR macro can be used afterwards to check whether a AL error was
  * encountered executing the specified code.
  */
-#define AL_CHECK( al_code ) \
+#define AL_CHECK( al_code ) do \
     { \
         while (alGetError() != AL_NO_ERROR) ; \
         al_code; \
@@ -293,7 +315,7 @@ extern GLenum __gl_error_code;
         { \
             GP_ERROR(#al_code ": %d", (int)__al_error_code); \
         } \
-    }
+    } while(0)
 
 // Global variable to hold AL errors
 extern ALenum __al_error_code;

+ 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) );
 

+ 86 - 35
gameplay/src/FileSystem.cpp

@@ -2,13 +2,19 @@
 #include "FileSystem.h"
 #include "Properties.h"
 
+#include <sys/types.h>
+#include <sys/stat.h>
+
 #ifdef WIN32
     #include <windows.h>
     #include <tchar.h>
     #include <stdio.h>
+    #define gp_stat _stat
+    #define gp_stat_struct struct stat
 #else
     #include <dirent.h>
-    #include <sys/stat.h>
+    #define gp_stat stat
+    #define gp_stat_struct struct stat
 #endif
 
 #ifdef __ANDROID__
@@ -19,9 +25,10 @@ extern AAssetManager* __assetManager;
 namespace gameplay
 {
 
+// Creates a file on the file system from the specified asset (Android-specific).
+static void createFileFromAsset(const char* path);
+
 #ifdef __ANDROID__
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h>
 
 void makepath(std::string path, int mode)
@@ -109,6 +116,8 @@ void FileSystem::loadResourceAliases(Properties* properties)
 
 const char* FileSystem::resolvePath(const char* path)
 {
+    GP_ASSERT(path);
+
     size_t len = strlen(path);
     if (len > 1 && path[0] == '@')
     {
@@ -191,49 +200,40 @@ bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
 #endif
 }
 
-FILE* FileSystem::openFile(const char* path, const char* mode)
+bool FileSystem::fileExists(const char* path)
 {
     GP_ASSERT(path);
 
     std::string fullPath(__resourcePath);
     fullPath += resolvePath(path);
 
-#ifdef __ANDROID__
-    std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
-    struct stat s;
-    if (stat(directoryPath.c_str(), &s) != 0)
-        makepath(directoryPath.c_str(), 0777);
+    createFileFromAsset(path);
 
+    gp_stat_struct s;
+// Win32 doesn't support an asset or bundle definitions.
+#ifdef WIN32
     if (stat(fullPath.c_str(), &s) != 0)
     {
-        AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
-        if (asset)
-        {
-            const void* data = AAsset_getBuffer(asset);
-            int length = AAsset_getLength(asset);
-            FILE* file = fopen(fullPath.c_str(), "wb");
-            if (file != NULL)
-            {
-                int ret = fwrite(data, sizeof(unsigned char), length, file);
-                if (fclose(file) != 0)
-                {
-                    GP_ERROR("Failed to close file on file system created from APK asset.");
-                    return NULL;
-                }
-                if (ret != length)
-                {
-                    GP_ERROR("Failed to write all data from APK asset to file on file system.");
-                    return NULL;
-                }
-            }
-            else
-            {
-                GP_ERROR("Failed to create file on file system from APK asset.");
-                return NULL;
-            }
-        }
+        fullPath = __resourcePath;
+        fullPath += "../../gameplay/";
+        fullPath += path;
+        
+        return stat(fullPath.c_str(), &s) == 0;
     }
+    return true;
+#else
+    return stat(fullPath.c_str(), &s) == 0;
 #endif
+}
+
+FILE* FileSystem::openFile(const char* path, const char* mode)
+{
+    GP_ASSERT(path);
+
+    std::string fullPath(__resourcePath);
+    fullPath += resolvePath(path);
+
+    createFileFromAsset(path);
     
     FILE* fp = fopen(fullPath.c_str(), mode);
     
@@ -301,4 +301,55 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     return buffer;
 }
 
+void createFileFromAsset(const char* path)
+{
+#ifdef __ANDROID__
+    static std::set<std::string> upToDateAssets;
+
+    GP_ASSERT(path);
+    std::string fullPath(__resourcePath);
+    fullPath += FileSystem::resolvePath(path);
+
+    std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
+    struct stat s;
+    if (stat(directoryPath.c_str(), &s) != 0)
+        makepath(directoryPath.c_str(), 0777);
+
+    // To ensure that the files on the file system corresponding to the assets in the APK bundle
+    // are always up to date (and in sync), we copy them from the APK to the file system once
+    // for each time the process (game) runs.
+    if (upToDateAssets.find(fullPath) == upToDateAssets.end())
+    {
+        AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
+        if (asset)
+        {
+            const void* data = AAsset_getBuffer(asset);
+            int length = AAsset_getLength(asset);
+            FILE* file = fopen(fullPath.c_str(), "wb");
+            if (file != NULL)
+            {
+                int ret = fwrite(data, sizeof(unsigned char), length, file);
+                if (fclose(file) != 0)
+                {
+                    GP_ERROR("Failed to close file on file system created from APK asset '%s'.", path);
+                    return;
+                }
+                if (ret != length)
+                {
+                    GP_ERROR("Failed to write all data from APK asset '%s' to file on file system.", path);
+                    return;
+                }
+            }
+            else
+            {
+                GP_ERROR("Failed to create file on file system from APK asset '%s'.", path);
+                return;
+            }
+
+            upToDateAssets.insert(fullPath);
+        }
+    }
+#endif
+}
+
 }

+ 8 - 0
gameplay/src/FileSystem.h

@@ -92,6 +92,14 @@ public:
      */
     static bool listFiles(const char* dirPath, std::vector<std::string>& files);
 
+    /**
+     * Checks if the file at the given path exists.
+     * 
+     * @param path The path to the file.
+     * @return <code>true</code> if the file exists; <code>false</code> otherwise.
+     */
+    static bool fileExists(const char* path);
+
     /**
      * Opens the specified file.
      *

+ 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

+ 30 - 16
gameplay/src/Game.cpp

@@ -34,7 +34,7 @@ Game::~Game()
 {
     // Do not call any virtual functions from the destructor.
     // Finalization is done from outside this class.
-    delete _timeEvents;
+    SAFE_DELETE(_timeEvents);
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION
     Ref::printLeaks();
     printMemoryLeaks();
@@ -114,6 +114,10 @@ void Game::shutdown()
     // Call user finalization.
     if (_state != UNINITIALIZED)
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         Platform::signalShutdown();
         finalize();
 
@@ -140,6 +144,10 @@ void Game::pause()
 {
     if (_state == RUNNING)
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         _state = PAUSED;
         _pausedTimeLast = Platform::getAbsoluteTime();
         _animationController->pause();
@@ -152,6 +160,10 @@ void Game::resume()
 {
     if (_state == PAUSED)
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         _state = RUNNING;
         _pausedTimeTotal += Platform::getAbsoluteTime() - _pausedTimeLast;
         _animationController->resume();
@@ -175,6 +187,10 @@ void Game::frame()
 
     if (_state == Game::RUNNING)
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         // Update Time.
         static long lastFrameTime = Game::getGameTime();
         long frameTime = Game::getGameTime();
@@ -288,7 +304,7 @@ void Game::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactI
 
 void Game::schedule(long timeOffset, TimeListener* timeListener, void* cookie)
 {
-    GP_ASSERT(timeListener);
+    GP_ASSERT(_timeEvents);
     TimeEvent timeEvent(getGameTime() + timeOffset, timeListener, cookie);
     _timeEvents->push(timeEvent);
 }
@@ -300,6 +316,10 @@ bool Game::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 
 void Game::updateOnce()
 {
+    GP_ASSERT(_animationController);
+    GP_ASSERT(_audioController);
+    GP_ASSERT(_physicsController);
+
     // Update Time.
     static long lastFrameTime = Game::getGameTime();
     long frameTime = Game::getGameTime();
@@ -325,22 +345,15 @@ void Game::loadConfig()
     if (_properties == NULL)
     {
         // Try to load custom config from file.
-        // Check if file exists.
-        FILE* file = FileSystem::openFile("game.config", "rb");
-        if (file)
+        if (FileSystem::fileExists("game.config"))
         {
-            fclose(file);
             _properties = Properties::create("game.config");
+            
+            // Load filesystem aliases.
+            Properties* aliases = _properties->getNamespace("aliases", true);
+            if (aliases)
+                FileSystem::loadResourceAliases(aliases);
         }
-        else
-        {
-            _properties = new Properties();
-        }
-
-        // Load filesystem aliases
-        Properties* aliases = _properties->getNamespace("aliases", true);
-        if (aliases)
-            FileSystem::loadResourceAliases(aliases);
     }
 }
 
@@ -353,7 +366,8 @@ void Game::fireTimeEvents(long frameTime)
         {
             break;
         }
-        timeEvent->listener->timeEvent(frameTime - timeEvent->time, timeEvent->cookie);
+        if (timeEvent->listener)
+            timeEvent->listener->timeEvent(frameTime - timeEvent->time, timeEvent->cookie);
         _timeEvents->pop();
     }
 }

+ 1 - 0
gameplay/src/Game.inl

@@ -47,6 +47,7 @@ inline PhysicsController* Game::getPhysicsController() const
 template <class T>
 void Game::renderOnce(T* instance, void (T::*method)(void*), void* cookie)
 {
+    GP_ASSERT(instance);
     (instance->*method)(cookie);
     Platform::swapBuffers();
 }

+ 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;
     }
 }
 

+ 203 - 4
gameplay/src/Matrix.cpp

@@ -116,7 +116,7 @@ void Matrix::createPerspective(float fieldOfView, float aspectRatio,
 
     float f_n = 1.0f / (zFarPlane - zNearPlane);
     float theta = MATH_DEG_TO_RAD(fieldOfView) * 0.5f;
-    if (abs(fmod(theta, MATH_PIOVER2)) < MATH_EPSILON)
+    if (fabs(fmod(theta, MATH_PIOVER2)) < MATH_EPSILON)
     {
         GP_ERROR("Invalid field of view value (%d) causes attempted calculation tan(%d), which is undefined.", fieldOfView, theta);
         return;
@@ -388,6 +388,26 @@ void Matrix::add(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
     GP_ASSERT(dst);
 
+#ifdef USE_NEON
+
+    asm volatile(
+    	"vld1.32 	{q0, q1}, 	[%1]! 	\n\t"
+		"vld1.32 	{q2, q3}, 	[%1]! 	\n\t"
+    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t"
+		"vld1.32 	{q10, q11}, [%2]! 	\n\t"
+		"vadd.f32   q12, q0, q8 		\n\t"
+    	"vadd.f32   q13, q1, q9			\n\t"
+    	"vadd.f32   q14, q2, q10		\n\t"
+    	"vadd.f32   q15, q3, q11		\n\t"
+    	"vst1.32    {q12, q13}, [%0]!   \n\t"
+		"vst1.32    {q14, q15}, [%0]!   \n\t"
+		:
+        : "r"(dst->m), "r"(m1.m), "r"(m2.m)
+        : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
+    );
+
+#else
+
     dst->m[0]  = m1.m[0]  + m2.m[0];
     dst->m[1]  = m1.m[1]  + m2.m[1];
     dst->m[2]  = m1.m[2]  + m2.m[2];
@@ -404,6 +424,8 @@ void Matrix::add(const Matrix& m1, const Matrix& m2, Matrix* dst)
     dst->m[13] = m1.m[13] + m2.m[13];
     dst->m[14] = m1.m[14] + m2.m[14];
     dst->m[15] = m1.m[15] + m2.m[15];
+
+#endif
 }
 
 bool Matrix::decompose(Vector3* scale, Quaternion* rotation, Vector3* translation) const
@@ -675,6 +697,27 @@ void Matrix::multiply(const Matrix& m, float scalar, Matrix* dst)
 {
     GP_ASSERT(dst);
 
+#ifdef USE_NEON
+
+    asm volatile(
+    	"vld1.32 	{d0[0]},	 	[%0]     	\n\t"
+    	"vld1.32	{q4-q5},  		[%1]!    	\n\t"
+		"vld1.32	{q6-q7},  		[%1]!		\n\t"
+
+    	"vmul.f32 	q8, q4, d0[0]    			\n\t"
+    	"vmul.f32 	q9, q5, d0[0]    			\n\t"
+		"vmul.f32 	q10, q6, d0[0]    			\n\t"
+		"vmul.f32 	q11, q7, d0[0]   		 	\n\t"
+
+    	"vst1.32 	{q8-q9},   		[%2]! 		\n\t"
+		"vst1.32 	{q10-q11}, 		[%2]!		\n\t"
+		:
+		: "r"(&scalar), "r"(m.m), "r"(dst->m)
+		: "q0", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "memory"
+	);
+
+#else
+
     dst->m[0]  = m.m[0]  * scalar;
     dst->m[1]  = m.m[1]  * scalar;
     dst->m[2]  = m.m[2]  * scalar;
@@ -691,6 +734,8 @@ void Matrix::multiply(const Matrix& m, float scalar, Matrix* dst)
     dst->m[13] = m.m[13] * scalar;
     dst->m[14] = m.m[14] * scalar;
     dst->m[15] = m.m[15] * scalar;
+
+#endif
 }
 
 void Matrix::multiply(const Matrix& m)
@@ -700,7 +745,46 @@ void Matrix::multiply(const Matrix& m)
 
 void Matrix::multiply(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
-    GP_ASSERT(dst);
+	GP_ASSERT(dst);
+
+#ifdef USE_NEON // if set, neon unit is present.
+
+    asm volatile
+    (
+        "vld1.32	 {d16 - d19}, [%1]!	\n\t"         // load first eight elements of matrix 0
+		"vld1.32     {d20 - d23}, [%1]!   \n\t"         // load second eight elements of matrix 0
+		"vld1.32     {d0 - d3}, [%2]!     \n\t"         // load first eight elements of matrix 1
+		"vld1.32     {d4 - d7}, [%2]!     \n\t"         // load second eight elements of matrix 1
+
+		"vmul.f32    q12, q8, d0[0]     \n\t"         // rslt col0  = (mat0 col0) * (mat1 col0 elt0)
+		"vmul.f32    q13, q8, d2[0]     \n\t"         // rslt col1  = (mat0 col0) * (mat1 col1 elt0)
+		"vmul.f32    q14, q8, d4[0]     \n\t"         // rslt col2  = (mat0 col0) * (mat1 col2 elt0)
+		"vmul.f32    q15, q8, d6[0]     \n\t"         // rslt col3  = (mat0 col0) * (mat1 col3 elt0)
+
+		"vmla.f32    q12, q9, d0[1]     \n\t"         // rslt col0 += (mat0 col1) * (mat1 col0 elt1)
+		"vmla.f32    q13, q9, d2[1]     \n\t"         // rslt col1 += (mat0 col1) * (mat1 col1 elt1)
+		"vmla.f32    q14, q9, d4[1]     \n\t"         // rslt col2 += (mat0 col1) * (mat1 col2 elt1)
+		"vmla.f32    q15, q9, d6[1]     \n\t"         // rslt col3 += (mat0 col1) * (mat1 col3 elt1)
+
+		"vmla.f32    q12, q10, d1[0]    \n\t"         // rslt col0 += (mat0 col2) * (mat1 col0 elt2)
+		"vmla.f32    q13, q10, d3[0]    \n\t"         // rslt col1 += (mat0 col2) * (mat1 col1 elt2)
+		"vmla.f32    q14, q10, d5[0]    \n\t"         // rslt col2 += (mat0 col2) * (mat1 col2 elt2)
+		"vmla.f32    q15, q10, d7[0]    \n\t"         // rslt col3 += (mat0 col2) * (mat1 col2 elt2)
+
+		"vmla.f32    q12, q11, d1[1]    \n\t"         // rslt col0 += (mat0 col3) * (mat1 col0 elt3)
+		"vmla.f32    q13, q11, d3[1]    \n\t"         // rslt col1 += (mat0 col3) * (mat1 col1 elt3)
+		"vmla.f32    q14, q11, d5[1]    \n\t"         // rslt col2 += (mat0 col3) * (mat1 col2 elt3)
+		"vmla.f32    q15, q11, d7[1]    \n\t"         // rslt col3 += (mat0 col3) * (mat1 col3 elt3)
+
+		"vst1.32    {d24 - d27}, [%0]!    \n\t"         // store first eight elements of result
+		"vst1.32    {d28 - d31}, [%0]!    \n\t"         // store second eight elements of result
+        
+        : // output
+        : "r"(dst->m), "r"(m1.m), "r"(m2.m) // input - note *value* of pointer doesn't change.
+        : "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
+	);
+
+#else
 
     // Support the case where m1 or m2 is the same array as dst.
     float product[16];
@@ -726,6 +810,8 @@ void Matrix::multiply(const Matrix& m1, const Matrix& m2, Matrix* dst)
     product[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
 
     memcpy(dst->m, product, MATRIX_SIZE);
+
+#endif
 }
 
 void Matrix::negate()
@@ -737,6 +823,26 @@ void Matrix::negate(Matrix* dst) const
 {
     GP_ASSERT(dst);
 
+#ifdef USE_NEON
+
+    asm volatile(
+    	"vld1.32 	{q0-q1},  [%1]! 	\n\t" // load m0-m7
+    	"vld1.32 	{q2-q3},  [%1]! 	\n\t" // load m8-m15
+
+    	"vneg.f32 	q4, q0 				\n\t" // negate m0-m3
+    	"vneg.f32 	q5, q1 				\n\t" // negate m4-m7
+		"vneg.f32 	q6, q2 				\n\t" // negate m8-m15
+		"vneg.f32 	q7, q3 				\n\t" // negate m8-m15
+
+    	"vst1.32 	{q4-q5},  [%0]!		\n\t" // store m0-m7
+    	"vst1.32 	{q6-q7},  [%0]!		\n\t" // store m8-m15
+    	:
+    	: "r"(dst->m), "r"(m)
+    	: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory"
+    );
+
+#else
+
     dst->m[0]  = -m[0];
     dst->m[1]  = -m[1];
     dst->m[2]  = -m[2];
@@ -753,6 +859,8 @@ void Matrix::negate(Matrix* dst) const
     dst->m[13] = -m[13];
     dst->m[14] = -m[14];
     dst->m[15] = -m[15];
+
+#endif
 }
 
 void Matrix::rotate(const Quaternion& q)
@@ -897,6 +1005,27 @@ void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
     GP_ASSERT(dst);
 
+#ifdef USE_NEON
+
+    asm volatile(
+    	"vld1.32 	{q0, q1}, 	[%1]! 	\n\t"
+		"vld1.32 	{q2, q3}, 	[%1]! 	\n\t"
+    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t"
+		"vld1.32 	{q10, q11}, [%2]! 	\n\t"
+		"vsub.f32   q12, q0, q8 		\n\t"
+    	"vsub.f32   q13, q1, q9			\n\t"
+    	"vsub.f32   q14, q2, q10		\n\t"
+    	"vsub.f32   q15, q3, q11		\n\t"
+    	"vst1.32    {q12, q13}, [%0]!   \n\t"
+		"vst1.32    {q14, q15}, [%0]!   \n\t"
+		:
+        : "r"(dst->m), "r"(m1.m), "r"(m2.m)
+        : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
+    );
+
+
+#else
+
     dst->m[0]  = m1.m[0]  - m2.m[0];
     dst->m[1]  = m1.m[1]  - m2.m[1];
     dst->m[2]  = m1.m[2]  - m2.m[2];
@@ -913,6 +1042,8 @@ void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
     dst->m[13] = m1.m[13] - m2.m[13];
     dst->m[14] = m1.m[14] - m2.m[14];
     dst->m[15] = m1.m[15] - m2.m[15];
+
+#endif
 }
 
 void Matrix::transformPoint(Vector3* point) const
@@ -940,11 +1071,38 @@ void Matrix::transformVector(const Vector3& vector, Vector3* dst) const
 void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) const
 {
     GP_ASSERT(dst);
-    
+
+#ifdef USE_NEON
+
+    asm volatile(
+    	"vld1.32	{d0[0]},		[%0]	\n\t"	// load x
+		"vld1.32	{d0[1]},    	[%1]	\n\t"	// load y
+		"vld1.32	{d1[0]},		[%2]	\n\t"	// load z
+		"vld1.32	{d1[1]},		[%3]	\n\t"	// load w
+		"vld1.32	{d18 - d21},	[%4]!	\n\t"	// load first 8 elements of matrix m0-m7
+		"vld1.32	{d22 - d25},	[%4]!	\n\t"	// load second 8 elements of matrix m8-m15
+
+    	"vmul.f32 q13,  q9, d0[0]			\n\t"	// Q5 =  (m0-m3)*x
+    	"vmla.f32 q13, q10, d0[1]      		\n\t"	// Q5 +=  (m4-m7)*y
+    	"vmla.f32 q13, q11, d1[0]      		\n\t"	// Q5 +=  (m8-m11)*z
+		"vmla.f32 q13, q12, d1[1]      		\n\t"	// Q5 +=  (m12-m15)*w
+
+    	"vst1.32 {d26[0]}, [%5]!        	\n\t"	// store dst->x
+		"vst1.32 {d26[1]}, [%5]!        	\n\t"	// store dst->y
+		"vst1.32 {d27[0]}, [%5]!        	\n\t"	// store dst->z
+		:
+    	: "r"(&x), "r"(&y), "r"(&z), "r"(&w), "r"(m), "r"(dst)
+        : "q0", "q9", "q10","q11", "q12", "q13", "memory"
+    );
+
+#else
+
     dst->set(
         x * m[0] + y * m[4] + z * m[8] + w * m[12],
         x * m[1] + y * m[5] + z * m[9] + w * m[13],
         x * m[2] + y * m[6] + z * m[10] + w * m[14]);
+
+#endif
 }
 
 void Matrix::transformVector(Vector4* vector) const
@@ -957,11 +1115,33 @@ void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
 {
     GP_ASSERT(dst);
 
+#ifdef USE_NEON
+
+    asm volatile
+    (
+    		"vld1.32	{d0, d1}, [%1]		\n\t"   //Q0 = v (x, y, z, w)
+    		"vld1.32    {d18 - d21}, [%0]!  \n\t"   //Q1 = M (m0-m7)
+    		"vld1.32    {d22 - d25}, [%0]!  \n\t"   //Q2 = M (m8-m15)
+
+    		"vmul.f32   q13, q9, d0[0]      \n\t"   //Q5 =  Q0*Q0[0]
+    		"vmla.f32   q13, q10, d0[1]     \n\t"   //Q5 += Q1*Q0[1]
+    		"vmla.f32   q13, q11, d1[0]     \n\t"   //Q5 += Q2*Q0[2]
+    		"vmla.f32   q13, q12, d1[1]     \n\t"   //Q5 += Q3*Q0[3]
+    		"vst1.32    {d26, d27}, [%2]    \n\t"   //Q4 = m+12
+    		:
+    		: "r"(m), "r"(&vector), "r"(dst)
+    		: "q0", "q9", "q10","q11", "q12", "q13", "memory"
+    );
+
+#else
+
     dst->set(
         vector.x * m[0] + vector.y * m[4] + vector.z * m[8] + vector.w * m[12],
         vector.x * m[1] + vector.y * m[5] + vector.z * m[9] + vector.w * m[13],
-        vector.x * m[2] + vector.y * m[6] + vector.z * m[10] + vector.w * m[14],
+        vector.x * m[2] + vector.y * m[6] + vector.z * m[10] + vector.w * m[14],
         vector.x * m[3] + vector.y * m[7] + vector.z * m[11] + vector.w * m[15]);
+
+#endif
 }
 
 void Matrix::translate(float x, float y, float z)
@@ -994,7 +1174,24 @@ void Matrix::transpose()
 void Matrix::transpose(Matrix* dst) const
 {
     GP_ASSERT(dst);
+
+#ifdef USE_NEON
     
+    asm volatile(
+    	"vld4.32 {d0[0], d2[0], d4[0], d6[0]}, [%0]! \n\t"
+		"vld4.32 {d0[1], d2[1], d4[1], d6[1]}, [%0]! \n\t"
+		"vld4.32 {d1[0], d3[0], d5[0], d7[0]}, [%0]! \n\t"
+		"vld4.32 {d1[1], d3[1], d5[1], d7[1]}, [%0]! \n\t"
+
+		"vst1.32 {q0-q1}, [%1]! \n\t"
+		"vst1.32 {q2-q3}, [%1]! \n\t"
+    	:
+    	: "r"(this->m), "r"(dst->m)
+    	: "q0", "q1", "q2", "q3", "memory"
+    );
+
+#else
+
     float t[16] = {
         m[0], m[4], m[8], m[12],
         m[1], m[5], m[9], m[13],
@@ -1002,6 +1199,8 @@ void Matrix::transpose(Matrix* dst) const
         m[3], m[7], m[11], m[15]
     };
     memcpy(dst->m, t, MATRIX_SIZE);
+
+#endif
 }
 
 }

+ 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);
             }

+ 3 - 0
gameplay/src/Node.cpp

@@ -1013,6 +1013,9 @@ PhysicsCollisionObject* Node::setCollisionObject(PhysicsCollisionObject::Type ty
             _collisionObject = new PhysicsCharacter(this, shape, rigidBodyParameters ? rigidBodyParameters->mass : 1.0f);
         }
         break;
+
+    case PhysicsCollisionObject::NONE:
+        break;  // Already deleted, Just don't add a new collision object back.
     }
 
     return _collisionObject;

+ 2 - 1
gameplay/src/Node.h

@@ -517,7 +517,8 @@ public:
      *        must point to a valid rigid body parameters object containing information
      *        about the rigid body; otherwise, this parmater may be NULL.
      */
-    PhysicsCollisionObject* setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape, PhysicsRigidBody::Parameters* rigidBodyParameters = NULL);
+    PhysicsCollisionObject* setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape = PhysicsCollisionShape::box(), 
+                                               PhysicsRigidBody::Parameters* rigidBodyParameters = NULL);
 
     /**
      * Sets the physics collision object for this node using the data from the Properties object defined at the specified URL, 

+ 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);

+ 70 - 34
gameplay/src/PhysicsCharacter.cpp

@@ -31,7 +31,8 @@ public:
     btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
     {
         PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
-
+        
+        GP_ASSERT(object);
         if (object == _me || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
             return 1.0f;
 
@@ -71,26 +72,28 @@ PhysicsCharacter::PhysicsCharacter(Node* node, const PhysicsCollisionShape::Defi
 {
     setMaxSlopeAngle(45.0f);
 
-    // Set the collision flags on the ghost object to indicate it's a character
+    // Set the collision flags on the ghost object to indicate it's a character.
+    GP_ASSERT(_ghostObject);
     _ghostObject->setCollisionFlags(_ghostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE);
 
-    // Register ourselves as an action on the physics world so we are called back during physics ticks
+    // Register ourselves as an action on the physics world so we are called back during physics ticks.
+    GP_ASSERT(Game::getInstance()->getPhysicsController() && Game::getInstance()->getPhysicsController()->_world);
     Game::getInstance()->getPhysicsController()->_world->addAction(this);
 }
 
 PhysicsCharacter::~PhysicsCharacter()
 {
-    // Unregister ourselves as action from world
+    // Unregister ourselves as action from world.
+    GP_ASSERT(Game::getInstance()->getPhysicsController() && Game::getInstance()->getPhysicsController()->_world);
     Game::getInstance()->getPhysicsController()->_world->removeAction(this);
 }
 
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "character") == 0))
     {
-        GP_WARN("Failed to load physics character from properties object: must be non-null object and have namespace equal to 'character'.");
+        GP_ERROR("Failed to load physics character from properties object: must be non-null object and have namespace equal to 'character'.");
         return NULL;
     }
 
@@ -98,7 +101,7 @@ PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     {
-        GP_WARN("Failed to create collision shape during physics character creation.");
+        GP_ERROR("Failed to create collision shape during physics character creation.");
         return NULL;
     }
 
@@ -122,6 +125,10 @@ PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
         {
             maxSlopeAngle = properties->getFloat();
         }
+        else
+        {
+            // Ignore this case (the attributes for the character's collision shape would end up here).
+        }
     }
 
     // Create the physics character.
@@ -181,21 +188,25 @@ void PhysicsCharacter::setVelocity(const Vector3& velocity)
 
 void PhysicsCharacter::rotate(const Vector3& axis, float angle)
 {
+    GP_ASSERT(_node);
     _node->rotate(axis, angle);
 }
 
 void PhysicsCharacter::rotate(const Quaternion& rotation)
 {
+    GP_ASSERT(_node);
     _node->rotate(rotation);
 }
 
 void PhysicsCharacter::setRotation(const Vector3& axis, float angle)
 {
+    GP_ASSERT(_node);
     _node->setRotation(axis, angle);
 }
 
 void PhysicsCharacter::setRotation(const Quaternion& rotation)
 {
+    GP_ASSERT(_node);
     _node->setRotation(rotation);
 }
 
@@ -228,6 +239,7 @@ void PhysicsCharacter::jump(float height)
     //  v0 == initial velocity (zero for jumping)
     //  a == acceleration (inverse gravity)
     //  s == linear displacement (height)
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Vector3 jumpVelocity = -Game::getInstance()->getPhysicsController()->getGravity() * height * 2.0f;
     jumpVelocity.set(
         jumpVelocity.x == 0 ? 0 : std::sqrt(jumpVelocity.x),
@@ -238,20 +250,22 @@ void PhysicsCharacter::jump(float height)
 
 void PhysicsCharacter::updateCurrentVelocity()
 {
+    GP_ASSERT(_node);
+    
     Vector3 temp;
     btScalar velocity2 = 0;
 
-    // Reset velocity vector
+    // Reset velocity vector.
     _normalizedVelocity.setValue(0, 0, 0);
 
-    // Add movement velocity contribution
+    // Add movement velocity contribution.
     if (!_moveVelocity.isZero())
     {
         _normalizedVelocity = _moveVelocity;
         velocity2 = _moveVelocity.length2();
     }
 
-    // Add forward velocity contribution
+    // Add forward velocity contribution.
     if (_forwardVelocity != 0)
     {
         _node->getWorldMatrix().getForwardVector(&temp);
@@ -261,7 +275,7 @@ void PhysicsCharacter::updateCurrentVelocity()
         velocity2 = std::max(std::abs(velocity2), std::abs(_forwardVelocity*_forwardVelocity));
     }
 
-    // Add right velocity contribution
+    // Add right velocity contribution.
     if (_rightVelocity != 0)
     {
         _node->getWorldMatrix().getRightVector(&temp);
@@ -285,6 +299,9 @@ void PhysicsCharacter::updateCurrentVelocity()
 
 void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep)
 {
+    GP_ASSERT(_ghostObject);
+    GP_ASSERT(_node);
+
     // First check for existing collisions and attempt to respond/fix them.
     // Basically we are trying to move the character so that it does not penetrate
     // any other collision objects in the scene. We need to do this to ensure that
@@ -301,28 +318,28 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
 
             if (++stepCount > 4)
             {
-                // Most likely we are wedged between a number of different collision objects
+                // Most likely we are wedged between a number of different collision objects.
                 break;
             }
         }
     }
 
-    // Update current and target world positions
+    // Update current and target world positions.
     btVector3 startPosition = _ghostObject->getWorldTransform().getOrigin();
     _currentPosition = startPosition;
 
-    // Process movement in the up direction
+    // Process movement in the up direction.
     if (_physicsEnabled)
         stepUp(collisionWorld, deltaTimeStep);
-
-    // Process horizontal movement
+    
+    // Process horizontal movement.
     stepForwardAndStrafe(collisionWorld, deltaTimeStep);
 
-    // Process movement in the down direction
+    // Process movement in the down direction.
     if (_physicsEnabled)
         stepDown(collisionWorld, deltaTimeStep);
 
-    // Set new position
+    // Set new position.
     btVector3 translation = _currentPosition - startPosition;
     _node->translate(translation.x(), translation.y(), translation.z());
 }
@@ -383,6 +400,10 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
     int maxIter = 10;
 
+    GP_ASSERT(_ghostObject && _ghostObject->getBroadphaseHandle());
+    GP_ASSERT(_collisionShape);
+    GP_ASSERT(collisionWorld);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     while (fraction > btScalar(0.01) && maxIter-- > 0)
     {
         start.setOrigin(_currentPosition);
@@ -402,9 +423,11 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
         {
             Vector3 normal(callback.m_hitNormalWorld.x(), callback.m_hitNormalWorld.y(), callback.m_hitNormalWorld.z());
             PhysicsCollisionObject* o = Game::getInstance()->getPhysicsController()->getCollisionObject(callback.m_hitCollisionObject);
+            GP_ASSERT(o);
             if (o->getType() == PhysicsCollisionObject::RIGID_BODY && o->isDynamic())
             {
                 PhysicsRigidBody* rb = static_cast<PhysicsRigidBody*>(o);
+                GP_ASSERT(rb);
                 normal.normalize();
                 rb->applyImpulse(_mass * -normal * velocity.length());
             }
@@ -436,6 +459,11 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
 void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController() && Game::getInstance()->getPhysicsController()->_world);
+    GP_ASSERT(_ghostObject && _ghostObject->getBroadphaseHandle());
+    GP_ASSERT(_collisionShape);
+    GP_ASSERT(collisionWorld);
+
     // Contribute gravity to vertical velocity.
     btVector3 gravity = Game::getInstance()->getPhysicsController()->_world->getGravity();
     _verticalVelocity += (gravity * time);
@@ -444,7 +472,7 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
     btVector3 targetPosition = _currentPosition + (_verticalVelocity * time);
     targetPosition -= btVector3(0, _stepHeight, 0);
 
-    // Perform a convex sweep test between current and target position
+    // Perform a convex sweep test between current and target position.
     btTransform start;
     btTransform end;
     start.setIdentity();
@@ -485,9 +513,11 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
             else
             {
                 PhysicsCollisionObject* o = Game::getInstance()->getPhysicsController()->getCollisionObject(callback.m_hitCollisionObject);
+                GP_ASSERT(o);
                 if (o->getType() == PhysicsCollisionObject::RIGID_BODY && o->isDynamic())
                 {
                     PhysicsRigidBody* rb = static_cast<PhysicsRigidBody*>(o);
+                    GP_ASSERT(rb);
                     normal.normalize();
                     rb->applyImpulse(_mass * -normal * sqrt(BV(normal).dot(_verticalVelocity)));
                 }
@@ -508,14 +538,14 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
     // want to keep increasing the vertical velocity until the character 
     // randomly drops through the floor when it can finally move due to its
     // vertical velocity having such a great magnitude.
-    if (!_verticalVelocity.isZero())
+    if (!_verticalVelocity.isZero() && time > 0.0f)
         _verticalVelocity = ((targetPosition + btVector3(0.0, _stepHeight, 0.0)) - _currentPosition) / time;
 
     _currentPosition = targetPosition;
 }
 
 /*
- * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
+ * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'.
  */
 btVector3 computeReflectionDirection(const btVector3& direction, const btVector3& normal)
 {
@@ -523,7 +553,7 @@ btVector3 computeReflectionDirection(const btVector3& direction, const btVector3
 }
 
 /*
- * Returns the portion of 'direction' that is parallel to 'normal'
+ * Returns the portion of 'direction' that is parallel to 'normal'.
  */
 btVector3 parallelComponent(const btVector3& direction, const btVector3& normal)
 {
@@ -532,7 +562,7 @@ btVector3 parallelComponent(const btVector3& direction, const btVector3& normal)
 }
 
 /*
- * Returns the portion of 'direction' that is perpindicular to 'normal'
+ * Returns the portion of 'direction' that is perpindicular to 'normal'.
  */
 btVector3 perpindicularComponent(const btVector3& direction, const btVector3& normal)
 {
@@ -566,25 +596,30 @@ void PhysicsCharacter::updateTargetPositionFromCollision(btVector3& targetPositi
 
 bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
 {
-    bool collision = false;
+    GP_ASSERT(_node);
+    GP_ASSERT(_ghostObject);
+    GP_ASSERT(world && world->getDispatcher());
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
 
+    bool collision = false;
     btOverlappingPairCache* pairCache = _ghostObject->getOverlappingPairCache();
+    GP_ASSERT(pairCache);
 
-    // Tell the world to dispatch collision events for our ghost object
+    // Tell the world to dispatch collision events for our ghost object.
     world->getDispatcher()->dispatchAllCollisionPairs(pairCache, world->getDispatchInfo(), world->getDispatcher());
 
-    // Store our current world position
+    // Store our current world position.
     Vector3 startPosition;
     _node->getWorldMatrix().getTranslation(&startPosition);
     btVector3 currentPosition = BV(startPosition);
 
-    // Handle all collisions/overlappign pairs
+    // Handle all collisions/overlappign pairs.
     btScalar maxPenetration = btScalar(0.0);
     for (int i = 0, count = pairCache->getNumOverlappingPairs(); i < count; ++i)
     {
         _manifoldArray.resize(0);
 
-        // Query contacts between this overlapping pair (store in _manifoldArray)
+        // Query contacts between this overlapping pair (store in _manifoldArray).
         btBroadphasePair* collisionPair = &pairCache->getOverlappingPairArray()[i];
         if (collisionPair->m_algorithm)
         {
@@ -594,11 +629,12 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
         for (int j = 0, manifoldCount = _manifoldArray.size(); j < manifoldCount; ++j)
         {
             btPersistentManifold* manifold = _manifoldArray[j];
+            GP_ASSERT(manifold);
 
             // Get the direction of the contact points (used to scale normal vector in the correct direction).
             btScalar directionSign = manifold->getBody0() == _ghostObject ? -1.0f : 1.0f;
 
-            // Skip ghost objects
+            // Skip ghost objects.
             PhysicsCollisionObject* object = Game::getInstance()->getPhysicsController()->getCollisionObject(
                 (btCollisionObject*)(manifold->getBody0() == _ghostObject ? manifold->getBody1() : manifold->getBody0()));
             if (!object || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
@@ -608,20 +644,20 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
             {
                 const btManifoldPoint& pt = manifold->getContactPoint(p);
 
-                // Get penetration distance for this contact point
+                // Get penetration distance for this contact point.
                 btScalar dist = pt.getDistance();
 
                 if (dist < 0.0)
                 {
-                    // A negative distance means the objects are overlapping
+                    // A negative distance means the objects are overlapping.
                     if (dist < maxPenetration)
                     {
-                        // Store collision normal for this point
+                        // Store collision normal for this point.
                         maxPenetration = dist;
                         _collisionNormal = pt.m_normalWorldOnB * directionSign;
                     }
 
-                    // Calculate new position for object, which is translated back along the collision normal
+                    // Calculate new position for object, which is translated back along the collision normal.
                     currentPosition += pt.m_normalWorldOnB * directionSign * dist * 0.2f;
                     collision = true;
                 }
@@ -629,7 +665,7 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
         }
     }
 
-    // Set the new world transformation to apply to fix the collision
+    // Set the new world transformation to apply to fix the collision.
     _node->translate(Vector3(currentPosition.x(), currentPosition.y(), currentPosition.z()) - startPosition);
 
     return collision;

+ 37 - 4
gameplay/src/PhysicsCollisionObject.cpp

@@ -14,9 +14,7 @@ struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
     /**
      * Called with each contact. Needed to implement collidesWith(PhysicsCollisionObject*).
      */
-    btScalar addSingleResult(btManifoldPoint& cp, 
-        const btCollisionObject* a, int partIdA, int indexA, 
-        const btCollisionObject* b, int partIdB, int indexB)
+    btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB)
     {
         result = true;
         return 0.0f;
@@ -29,7 +27,7 @@ struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
 };
 
 PhysicsCollisionObject::PhysicsCollisionObject(Node* node)
-    : _node(node), _motionState(NULL), _collisionShape(NULL)
+    : _node(node), _motionState(NULL), _collisionShape(NULL), _enabled(true)
 {
 }
 
@@ -37,11 +35,13 @@ PhysicsCollisionObject::~PhysicsCollisionObject()
 {
     SAFE_DELETE(_motionState);
 
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->destroyShape(_collisionShape);
 }
 
 PhysicsCollisionShape::Type PhysicsCollisionObject::getShapeType() const
 {
+    GP_ASSERT(getCollisionShape());
     return getCollisionShape()->getType();
 }
 
@@ -68,27 +68,60 @@ bool PhysicsCollisionObject::isKinematic() const
     case CHARACTER:
         return true;
     default:
+        GP_ASSERT(getCollisionObject());
         return getCollisionObject()->isKinematicObject();
     }
 }
 
 bool PhysicsCollisionObject::isDynamic() const
 {
+    GP_ASSERT(getCollisionObject());
     return !getCollisionObject()->isStaticOrKinematicObject();
 }
 
+bool PhysicsCollisionObject::isEnabled() const
+{
+    return _enabled;
+}
+
+void PhysicsCollisionObject::setEnabled(bool enable)
+{
+    if (enable)
+    {  
+        if (!_enabled)
+        {
+            Game::getInstance()->getPhysicsController()->addCollisionObject(this);
+            _enabled = true;
+        }
+    }
+    else
+    {
+        if (_enabled)
+        {
+            Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
+            _enabled = false;
+        }
+    }
+}
+
 void PhysicsCollisionObject::addCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, object);
 }
 
 void PhysicsCollisionObject::removeCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->removeCollisionListener(listener, this, object);
 }
 
 bool PhysicsCollisionObject::collidesWith(PhysicsCollisionObject* object) const
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController() && Game::getInstance()->getPhysicsController()->_world);
+    GP_ASSERT(object && object->getCollisionObject());
+    GP_ASSERT(getCollisionObject());
+
     static CollidesWithCallback callback;
 
     callback.result = false;

+ 23 - 4
gameplay/src/PhysicsCollisionObject.h

@@ -155,7 +155,7 @@ public:
      * A kinematic collision object is an object that is not simulated by
      * the physics system and instead has its transform driven manually.
      *
-     * @return Whether the collision object is kinematic.
+     * @return true if the collision object is kinematic.
      */
     bool isKinematic() const;
 
@@ -165,10 +165,24 @@ public:
      * A dynamic collision object is simulated entirely by the physics system,
      * such as with dynamic rigid bodies. 
      *
-     * @return Whether the collision object is dynamic.
+     * @return true if the collision object is dynamic.
      */
     bool isDynamic() const;
 
+    /**
+     * Check if th collision object is enabled.
+     *
+     * @return true if the collision object is enabled.
+     */
+    bool isEnabled() const;
+
+    /**
+     * Sets the collision object be enabled or disabled.
+     *
+     * @param enable true enables the collision object, false diables it.
+     */
+    void setEnabled(bool enable);
+
     /**
      * Adds a collision listener for this collision object.
      * 
@@ -190,10 +204,11 @@ public:
      * 
      * @param object The collision object to test for collision with.
      * 
-     * @return True if this object collides with the specified one; false otherwise.
+     * @return true if this object collides with the specified one; false otherwise.
      */
     bool collidesWith(PhysicsCollisionObject* object) const;
 
+
 protected:
 
     /**
@@ -215,7 +230,6 @@ protected:
      */
     PhysicsMotionState* getMotionState() const;
 
-    // Common member variables
     /**
      * Pointer to Node contained by this collision object.
      */ 
@@ -231,6 +245,11 @@ protected:
      */
     PhysicsCollisionShape* _collisionShape;
 
+    /**
+     * If the collision object is enabled or not.
+     */
+    bool _enabled;
+
 };
 
 }

+ 33 - 13
gameplay/src/PhysicsCollisionShape.cpp

@@ -21,7 +21,7 @@ PhysicsCollisionShape::~PhysicsCollisionShape()
 {
     if (_shape)
     {
-        // Cleanup shape-specific cached data
+        // Cleanup shape-specific cached data.
         switch (_type)
         {
         case SHAPE_MESH:
@@ -44,7 +44,7 @@ PhysicsCollisionShape::~PhysicsCollisionShape()
             break;
         }
 
-        // Free the bullet shape
+        // Free the bullet shape.
         SAFE_DELETE(_shape);
     }
 }
@@ -69,10 +69,12 @@ PhysicsCollisionShape::Definition::Definition(const Definition& definition)
     switch (type)
     {
     case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+        GP_ASSERT(data.heightfield);
         data.heightfield->addRef();
         break;
 
     case PhysicsCollisionShape::SHAPE_MESH:
+        GP_ASSERT(data.mesh);
         data.mesh->addRef();
         break;
     }
@@ -103,10 +105,12 @@ PhysicsCollisionShape::Definition& PhysicsCollisionShape::Definition::operator=(
         switch (type)
         {
         case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+            GP_ASSERT(data.heightfield);
             data.heightfield->addRef();
             break;
 
         case PhysicsCollisionShape::SHAPE_MESH:
+            GP_ASSERT(data.mesh);
             data.mesh->addRef();
             break;
         }
@@ -117,14 +121,15 @@ PhysicsCollisionShape::Definition& PhysicsCollisionShape::Definition::operator=(
 
 PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Node* node, Properties* properties)
 {
+    GP_ASSERT(node);
+
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || 
         !(strcmp(properties->getNamespace(), "character") == 0 || 
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "rigidBody") == 0))
     {
-        GP_WARN("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to 'character', 'ghostObject', or 'rigidBody'.");
+        GP_ERROR("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to 'character', 'ghostObject', or 'rigidBody'.");
         return NULL;
     }
 
@@ -158,7 +163,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                 type = SHAPE_CAPSULE;
             else
             {
-                GP_WARN("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", typeStr.c_str());
+                GP_ERROR("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", typeStr.c_str());
                 return NULL;
             }
 
@@ -190,11 +195,15 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
         {
             centerIsAbsolute = properties->getBool();
         }
+        else
+        {
+            // Ignore this case (these are the properties for the rigid body, character, or ghost object that this collision shape is for).
+        }
     }
 
     if (!typeSpecified)
     {
-        GP_WARN("Missing 'type' specifier for collision shape definition.");
+        GP_ERROR("Missing 'type' specifier for collision shape definition.");
         return NULL;
     }
 
@@ -255,11 +264,11 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
             break;
         case SHAPE_MESH:
         {
-            // Mesh is required on node
+            // Mesh is required on node.
             Mesh* nodeMesh = node->getModel() ? node->getModel()->getMesh() : NULL;
             if (nodeMesh == NULL)
             {
-                GP_WARN("Cannot create mesh rigid body for node without mode/mesh.");
+                GP_ERROR("Cannot create mesh collision object for node without model/mesh.");
                 return NULL;
             }
 
@@ -275,7 +284,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                 case Mesh::LINE_STRIP:
                 case Mesh::POINTS:
                 case Mesh::TRIANGLE_STRIP:
-                    GP_WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
+                    GP_ERROR("Mesh collision objects are currently only supported on meshes with primitive type equal to TRIANGLES.");
                     SAFE_DELETE(shape);
                     break;
             }
@@ -285,14 +294,20 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
         case SHAPE_HEIGHTFIELD:
             if (imagePath == NULL)
             {
-                GP_WARN("Heightfield rigid body requires an image path.");
+                GP_ERROR("Heightfield collision objects require an image path.");
+                SAFE_DELETE(shape);
+                return NULL;
             }
             else
             {
                 // Load the image data from the given file path.
                 Image* image = Image::create(imagePath);
                 if (!image)
+                {
+                    GP_ERROR("Failed create image for heightfield collision object from file '%s'.", imagePath);
+                    SAFE_DELETE(shape);
                     return NULL;
+                }
 
                 // Ensure that the image's pixel format is supported.
                 switch (image->getFormat())
@@ -301,7 +316,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                     case Image::RGBA:
                         break;
                     default:
-                        GP_WARN("Heightmap: pixel format is not supported: %d", image->getFormat());
+                        GP_ERROR("Heightmap: pixel format is not supported: %d.", image->getFormat());
+                        SAFE_RELEASE(image);
+                        SAFE_DELETE(shape);
                         return NULL;
                 }
 
@@ -310,8 +327,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
             }
             break;
         default:
-            GP_WARN("Unsupported value for physics collision shape type.");
-            break;
+            GP_ERROR("Unsupported physics collision shape type (%d).", type);
+            SAFE_DELETE(shape);
+            return NULL;
     }
 
     SAFE_DELETE(extents);
@@ -383,6 +401,7 @@ PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule(float radius, f
 
 PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* image)
 {
+    GP_ASSERT(image);
     image->addRef();
 
     Definition d;
@@ -395,6 +414,7 @@ PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* imag
 
 PhysicsCollisionShape::Definition PhysicsCollisionShape::mesh(Mesh* mesh)
 {
+    GP_ASSERT(mesh);
     mesh->addRef();
 
     Definition d;

+ 13 - 1
gameplay/src/PhysicsConstraint.cpp

@@ -22,12 +22,16 @@ PhysicsConstraint::~PhysicsConstraint()
         _b->removeConstraint(this);
 
     // Remove the constraint from the physics world and delete the Bullet object.
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->removeConstraint(this);
     SAFE_DELETE(_constraint);
 }
 
 Vector3 PhysicsConstraint::centerOfMassMidpoint(const Node* a, const Node* b)
 {
+    GP_ASSERT(a);
+    GP_ASSERT(b);
+
     Vector3 tA, tB;
     a->getWorldMatrix().getTranslation(&tA);
     b->getWorldMatrix().getTranslation(&tB);
@@ -45,6 +49,8 @@ Vector3 PhysicsConstraint::centerOfMassMidpoint(const Node* a, const Node* b)
 
 Quaternion PhysicsConstraint::getRotationOffset(const Node* node, const Vector3& point)
 {
+    GP_ASSERT(node);
+
     // Create a translation matrix that translates to the given origin.
     Matrix m;
     Matrix::createTranslation(point, &m);
@@ -64,6 +70,8 @@ Quaternion PhysicsConstraint::getRotationOffset(const Node* node, const Vector3&
 
 Vector3 PhysicsConstraint::getTranslationOffset(const Node* node, const Vector3& point)
 {
+    GP_ASSERT(node);
+
     // Create a translation matrix that translates to the given origin.
     Matrix m;
     Matrix::createTranslation(point, &m);
@@ -92,6 +100,8 @@ Vector3 PhysicsConstraint::getTranslationOffset(const Node* node, const Vector3&
 
 btTransform PhysicsConstraint::getTransformOffset(const Node* node, const Vector3& origin)
 {
+    GP_ASSERT(node);
+
     // Create a translation matrix that translates to the given origin.
     Matrix m;
     Matrix::createTranslation(origin, &m);
@@ -123,8 +133,9 @@ btTransform PhysicsConstraint::getTransformOffset(const Node* node, const Vector
 
 Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 {
-    Vector3 center;
+    GP_ASSERT(model && model->getMesh() && model->getNode());
 
+    Vector3 center;
     const BoundingBox& box = model->getMesh()->getBoundingBox();
     if (!(box.min.isZero() && box.max.isZero()))
     {
@@ -155,6 +166,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 
 Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 {
+    GP_ASSERT(node && node->getCollisionObject() && node->getCollisionObject()->getMotionState());
     btVector3 centerOfMassOffset = (node->getCollisionObject()->getMotionState())->_centerOfMassOffset.getOrigin();
     return Vector3(v.x + centerOfMassOffset.x(), v.y + centerOfMassOffset.y(), v.z + centerOfMassOffset.z());
 }

+ 4 - 0
gameplay/src/PhysicsConstraint.inl

@@ -5,21 +5,25 @@ namespace gameplay
 
 inline float PhysicsConstraint::getBreakingImpulse() const
 {
+    GP_ASSERT(_constraint);
     return _constraint->getBreakingImpulseThreshold();
 }
 
 inline void PhysicsConstraint::setBreakingImpulse(float impulse)
 {
+    GP_ASSERT(_constraint);
     _constraint->setBreakingImpulseThreshold(impulse);
 }
 
 inline bool PhysicsConstraint::isEnabled() const
 {
+    GP_ASSERT(_constraint);
     return _constraint->isEnabled();
 }
 
 inline void PhysicsConstraint::setEnabled(bool enabled)
 {
+    GP_ASSERT(_constraint);
     _constraint->setEnabled(enabled);
 }
 

+ 129 - 44
gameplay/src/PhysicsController.cpp

@@ -38,6 +38,7 @@ PhysicsController::~PhysicsController()
 
 void PhysicsController::addStatusListener(Listener* listener)
 {
+    GP_ASSERT(listener);
     if (!_listeners)
         _listeners = new std::vector<Listener*>();
 
@@ -129,6 +130,9 @@ void PhysicsController::setGravity(const Vector3& gravity)
 
 void PhysicsController::drawDebug(const Matrix& viewProjection)
 {
+    GP_ASSERT(_debugDrawer);
+    GP_ASSERT(_world);
+
     _debugDrawer->begin(viewProjection);
     _world->debugDrawWorld();
     _debugDrawer->end();
@@ -136,6 +140,8 @@ void PhysicsController::drawDebug(const Matrix& viewProjection)
 
 bool PhysicsController::rayTest(const Ray& ray, float distance, PhysicsController::HitResult* result)
 {
+    GP_ASSERT(_world);
+
     btCollisionWorld::ClosestRayResultCallback callback(BV(ray.getOrigin()), BV(distance * ray.getDirection()));
     _world->rayTest(BV(ray.getOrigin()), BV(distance * ray.getDirection()), callback);
     if (callback.hasHit())
@@ -167,6 +173,7 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
 
         btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
         {
+            GP_ASSERT(convexResult.m_hitCollisionObject);
             PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
 
             if (object == me)
@@ -178,12 +185,13 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
         PhysicsCollisionObject* me;
     };
 
+    GP_ASSERT(object && object->getCollisionShape());
     PhysicsCollisionShape* shape = object->getCollisionShape();
     PhysicsCollisionShape::Type type = shape->getType();
     if (type != PhysicsCollisionShape::SHAPE_BOX && type != PhysicsCollisionShape::SHAPE_SPHERE && type != PhysicsCollisionShape::SHAPE_CAPSULE)
         return false; // unsupported type
 
-    // Define the start transform
+    // Define the start transform.
     btTransform start;
     start.setIdentity();
     if (object->getNode())
@@ -199,14 +207,11 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
         start.setRotation(BQ(rotation));
     }
 
-    // Define the end transform
+    // Define the end transform.
     btTransform end(start);
     end.setOrigin(BV(endPosition));
 
-    float d1 = object->getNode()->getTranslationWorld().distance(endPosition);
-    float d2 = start.getOrigin().distance(end.getOrigin());
-
-    // Perform bullet convex sweep test
+    // Perform bullet convex sweep test.
     SweepTestCallback callback(object);
 
     // If the object is represented by a ghost object, use the ghost object's convex sweep test
@@ -224,9 +229,10 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
         break;
     }*/
 
+    GP_ASSERT(_world);
     _world->convexSweepTest(static_cast<btConvexShape*>(shape->getShape()), start, end, callback, _world->getDispatchInfo().m_allowedCcdPenetration);
 
-    // Check for hits and store results
+    // Check for hits and store results.
     if (callback.hasHit())
     {
         if (result)
@@ -246,15 +252,17 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
 btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 
     const btCollisionObject* b, int partIdB, int indexB)
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+
     // Get pointers to the PhysicsCollisionObject objects.
-    PhysicsCollisionObject* rbA = Game::getInstance()->getPhysicsController()->getCollisionObject(a);
-    PhysicsCollisionObject* rbB = Game::getInstance()->getPhysicsController()->getCollisionObject(b);
+    PhysicsCollisionObject* objectA = Game::getInstance()->getPhysicsController()->getCollisionObject(a);
+    PhysicsCollisionObject* objectB = Game::getInstance()->getPhysicsController()->getCollisionObject(b);
 
-    // If the given rigid body pair has collided in the past, then
+    // If the given collision object pair has collided in the past, then
     // we notify the listeners only if the pair was not colliding
     // during the previous frame. Otherwise, it's a new pair, so add a
     // new entry to the cache with the appropriate listeners and notify them.
-    PhysicsCollisionObject::CollisionPair pair(rbA, rbB);
+    PhysicsCollisionObject::CollisionPair pair(objectA, objectB);
 
     CollisionInfo* collisionInfo;
     if (_collisionStatus.count(pair) > 0)
@@ -263,7 +271,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
     }
     else
     {
-        // Add a new collision pair for these objects
+        // Add a new collision pair for these objects.
         collisionInfo = &_collisionStatus[pair];
 
         // Add the appropriate listeners.
@@ -274,6 +282,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
             std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = ci._listeners.begin();
             for (; iter != ci._listeners.end(); iter++)
             {
+                GP_ASSERT(*iter);
                 collisionInfo->_listeners.push_back(*iter);
             }
         }
@@ -284,17 +293,19 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
             std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = ci._listeners.begin();
             for (; iter != ci._listeners.end(); iter++)
             {
+                GP_ASSERT(*iter);
                 collisionInfo->_listeners.push_back(*iter);
             }
         }
     }
 
-    // Fire collision event
+    // Fire collision event.
     if ((collisionInfo->_status & COLLISION) == 0)
     {
         std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = collisionInfo->_listeners.begin();
         for (; iter != collisionInfo->_listeners.end(); iter++)
         {
+            GP_ASSERT(*iter);
             if ((collisionInfo->_status & REMOVE) == 0)
             {
                 (*iter)->collisionEvent(PhysicsCollisionObject::CollisionListener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
@@ -323,9 +334,9 @@ void PhysicsController::initialize()
     _world->setGravity(BV(_gravity));
 
     // Register ghost pair callback so bullet detects collisions with ghost objects (used for character collisions).
+    GP_ASSERT(_world->getPairCache());
     _ghostPairCallback = bullet_new<btGhostPairCallback>();
     _world->getPairCache()->setInternalGhostPairCallback(_ghostPairCallback);
-
     _world->getDispatchInfo().m_allowedCcdPenetration = 0.0001f;
 
     // Set up debug drawing.
@@ -356,6 +367,8 @@ void PhysicsController::resume()
 
 void PhysicsController::update(long elapsedTime)
 {
+    GP_ASSERT(_world);
+
     // Update the physics simulation, with a maximum
     // of 10 simulation steps being performed in a given frame.
     //
@@ -372,6 +385,7 @@ void PhysicsController::update(long elapsedTime)
         {
             for (int i = 0; i < _world->getNumCollisionObjects(); i++)
             {
+                GP_ASSERT(_world->getCollisionObjectArray()[i]);
                 if (_world->getCollisionObjectArray()[i]->isActive())
                 {
                     _status = Listener::ACTIVATED;
@@ -384,6 +398,7 @@ void PhysicsController::update(long elapsedTime)
             bool allInactive = true;
             for (int i = 0; i < _world->getNumCollisionObjects(); i++)
             {
+                GP_ASSERT(_world->getCollisionObjectArray()[i]);
                 if (_world->getCollisionObjectArray()[i]->isActive())
                 {
                     allInactive = false;
@@ -400,6 +415,7 @@ void PhysicsController::update(long elapsedTime)
         {
             for (unsigned int k = 0; k < _listeners->size(); k++)
             {
+                GP_ASSERT((*_listeners)[k]);
                 (*_listeners)[k]->statusEvent(_status);
             }
         }
@@ -467,6 +483,10 @@ void PhysicsController::update(long elapsedTime)
 
 void PhysicsController::addCollisionListener(PhysicsCollisionObject::CollisionListener* listener, PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB)
 {
+    GP_ASSERT(listener);
+    
+    // One of the collision objects in the pair must be non-null.
+    GP_ASSERT(objectA || objectB);
     PhysicsCollisionObject::CollisionPair pair(objectA, objectB);
 
     // Add the listener and ensure the status includes that this collision pair is registered.
@@ -477,8 +497,11 @@ void PhysicsController::addCollisionListener(PhysicsCollisionObject::CollisionLi
 
 void PhysicsController::removeCollisionListener(PhysicsCollisionObject::CollisionListener* listener, PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB)
 {
-    // Mark the collision pair for these objects for removal
+    // One of the collision objects in the pair must be non-null.
+    GP_ASSERT(objectA || objectB);
     PhysicsCollisionObject::CollisionPair pair(objectA, objectB);
+
+    // Mark the collision pair for these objects for removal.
     if (_collisionStatus.count(pair) > 0)
     {
         _collisionStatus[pair]._status |= REMOVE;
@@ -487,11 +510,14 @@ void PhysicsController::removeCollisionListener(PhysicsCollisionObject::Collisio
 
 void PhysicsController::addCollisionObject(PhysicsCollisionObject* object)
 {
+    GP_ASSERT(object && object->getCollisionObject());
+    GP_ASSERT(_world);
+
     // Assign user pointer for the bullet collision object to allow efficient
     // lookups of bullet objects -> gameplay objects.
     object->getCollisionObject()->setUserPointer(object);
 
-    // Add the object to the physics world
+    // Add the object to the physics world.
     switch (object->getType())
     {
     case PhysicsCollisionObject::RIGID_BODY:
@@ -507,14 +533,17 @@ void PhysicsController::addCollisionObject(PhysicsCollisionObject* object)
         break;
 
     default:
-        GP_ASSERT(0); // unexpected (new type?)
+        GP_ERROR("Unsupported collision object type (%d).", object->getType());
         break;
     }
 }
 
 void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
 {
-    // Remove the collision object from the world
+    GP_ASSERT(object);
+    GP_ASSERT(_world);
+
+    // Remove the collision object from the world.
     if (object->getCollisionObject())
     {
         switch (object->getType())
@@ -529,7 +558,7 @@ void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
             break;
 
         default:
-            GP_ASSERT(0); // unexpected (new type?)
+            GP_ERROR("Unsupported collision object type (%d).", object->getType());
             break;
         }
     }
@@ -545,14 +574,20 @@ void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
 
 PhysicsCollisionObject* PhysicsController::getCollisionObject(const btCollisionObject* collisionObject) const
 {
-    // Gameplay rigid bodies are stored in the userPointer data of bullet collision objects
+    // Gameplay collision objects are stored in the userPointer data of Bullet collision objects.
+    GP_ASSERT(collisionObject);
     return reinterpret_cast<PhysicsCollisionObject*>(collisionObject->getUserPointer());
 }
 
 void getBoundingBox(Node* node, BoundingBox* out, bool merge = false)
 {
+    GP_ASSERT(node);
+    GP_ASSERT(out);
+
     if (node->getModel())
     {
+        GP_ASSERT(node->getModel()->getMesh());
+
         if (merge)
             out->merge(node->getModel()->getMesh()->getBoundingBox());
         else
@@ -572,8 +607,13 @@ void getBoundingBox(Node* node, BoundingBox* out, bool merge = false)
 
 void getBoundingSphere(Node* node, BoundingSphere* out, bool merge = false)
 {
+    GP_ASSERT(node);
+    GP_ASSERT(out);
+
     if (node->getModel())
     {
+        GP_ASSERT(node->getModel()->getMesh());
+
         if (merge)
             out->merge(node->getModel()->getMesh()->getBoundingSphere());
         else
@@ -593,7 +633,9 @@ void getBoundingSphere(Node* node, BoundingSphere* out, bool merge = false)
 
 void computeCenterOfMass(const Vector3& center, const Vector3& scale, Vector3* centerOfMassOffset)
 {
-    // Update center of mass offset
+    GP_ASSERT(centerOfMassOffset);
+
+    // Update center of mass offset.
     *centerOfMassOffset = center;
     centerOfMassOffset->x *= scale.x;
     centerOfMassOffset->y *= scale.y;
@@ -603,6 +645,8 @@ void computeCenterOfMass(const Vector3& center, const Vector3& scale, Vector3* c
 
 PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsCollisionShape::Definition& shape, Vector3* centerOfMassOffset)
 {
+    GP_ASSERT(node);
+
     PhysicsCollisionShape* collisionShape = NULL;
 
     // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
@@ -615,7 +659,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
         {
             if (shape.isExplicit)
             {
-                // Use the passed in box information
+                // Use the passed in box information.
                 collisionShape = createBox(Vector3(shape.data.box.extents), Vector3::one());
 
                 if (shape.centerAbsolute)
@@ -631,7 +675,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
             }
             else
             {
-                // Automatically compute bounding box from mesh's bounding box
+                // Automatically compute bounding box from mesh's bounding box.
                 BoundingBox box;
                 getBoundingBox(node, &box);
                 collisionShape = createBox(Vector3(std::abs(box.max.x - box.min.x), std::abs(box.max.y - box.min.y), std::abs(box.max.z - box.min.z)), scale);
@@ -645,7 +689,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
         {
             if (shape.isExplicit)
             {
-                // Use the passed in sphere information
+                // Use the passed in sphere information.
                 collisionShape = createSphere(shape.data.sphere.radius, Vector3::one());
 
                 if (shape.centerAbsolute)
@@ -661,7 +705,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
             }
             else
             {
-                // Automatically compute bounding sphere from mesh's bounding sphere
+                // Automatically compute bounding sphere from mesh's bounding sphere.
                 BoundingSphere sphere;
                 getBoundingSphere(node, &sphere);
                 collisionShape = createSphere(sphere.radius, scale);
@@ -675,7 +719,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
         {
             if (shape.isExplicit)
             {
-                // Use the passed in capsule information
+                // Use the passed in capsule information.
                 collisionShape = createCapsule(shape.data.capsule.radius, shape.data.capsule.height, Vector3::one());
 
                 if (shape.centerAbsolute)
@@ -691,7 +735,7 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
             }
             else
             {
-                // Compute a capsule shape that roughly matches the bounding box of the mesh
+                // Compute a capsule shape that roughly matches the bounding box of the mesh.
                 BoundingBox box;
                 getBoundingBox(node, &box);
                 float radius = std::max((box.max.x - box.min.x) * 0.5f, (box.max.z - box.min.z) * 0.5f);
@@ -705,17 +749,20 @@ PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsC
 
     case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
         {
-            // Build heightfield rigid body from the passed in shape
+            // Build heightfield rigid body from the passed in shape.
             collisionShape = createHeightfield(node, shape.data.heightfield, centerOfMassOffset);
         }
         break;
 
     case PhysicsCollisionShape::SHAPE_MESH:
         {
-            // Build mesh from passed in shape
+            // Build mesh from passed in shape.
             collisionShape = createMesh(shape.data.mesh, scale);
         }
         break;
+    default:
+        GP_ERROR("Unsupported collision shape type (%d).", shape.type);
+        break;
     }
 
     return collisionShape;
@@ -731,10 +778,11 @@ PhysicsCollisionShape* PhysicsController::createBox(const Vector3& extents, cons
     for (unsigned int i = 0; i < _shapes.size(); ++i)
     {
         shape = _shapes[i];
+        GP_ASSERT(shape);
         if (shape->getType() == PhysicsCollisionShape::SHAPE_BOX)
         {
             btBoxShape* box = static_cast<btBoxShape*>(shape->_shape);
-            if (box->getHalfExtentsWithMargin() == halfExtents)
+            if (box && box->getHalfExtentsWithMargin() == halfExtents)
             {
                 shape->addRef();
                 return shape;
@@ -767,10 +815,11 @@ PhysicsCollisionShape* PhysicsController::createSphere(float radius, const Vecto
     for (unsigned int i = 0; i < _shapes.size(); ++i)
     {
         shape = _shapes[i];
+        GP_ASSERT(shape);
         if (shape->getType() == PhysicsCollisionShape::SHAPE_SPHERE)
         {
             btSphereShape* sphere = static_cast<btSphereShape*>(shape->_shape);
-            if (sphere->getRadius() == scaledRadius)
+            if (sphere && sphere->getRadius() == scaledRadius)
             {
                 shape->addRef();
                 return shape;
@@ -799,10 +848,11 @@ PhysicsCollisionShape* PhysicsController::createCapsule(float radius, float heig
     for (unsigned int i = 0; i < _shapes.size(); i++)
     {
         shape = _shapes[i];
+        GP_ASSERT(shape);
         if (shape->getType() == PhysicsCollisionShape::SHAPE_CAPSULE)
         {
             btCapsuleShape* capsule = static_cast<btCapsuleShape*>(shape->_shape);
-            if (capsule->getRadius() == scaledRadius && capsule->getHalfHeight() == 0.5f * scaledHeight)
+            if (capsule && capsule->getRadius() == scaledRadius && capsule->getHalfHeight() == 0.5f * scaledHeight)
             {
                 shape->addRef();
                 return shape;
@@ -819,6 +869,10 @@ PhysicsCollisionShape* PhysicsController::createCapsule(float radius, float heig
 
 PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* image, Vector3* centerOfMassOffset)
 {
+    GP_ASSERT(node);
+    GP_ASSERT(image);
+    GP_ASSERT(centerOfMassOffset);
+
     // Get the dimensions of the heightfield.
     // If the node has a mesh defined, use the dimensions of the bounding box for the mesh.
     // Otherwise simply use the image dimensions (with a max height of 255).
@@ -851,7 +905,7 @@ PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* i
             pixelSize = 4;
             break;
         default:
-            GP_ERROR("Unsupported pixel format for heightmap image.");
+            GP_ERROR("Unsupported pixel format for heightmap image (%d).", image->getFormat());
             return NULL;
     }
 
@@ -872,9 +926,12 @@ PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* i
     heightfieldData->heightData = NULL;
     heightfieldData->inverseIsDirty = true;
 
-    // Generate the heightmap data needed for physics (one height per world unit).
     unsigned int sizeWidth = width;
     unsigned int sizeHeight = length;
+    GP_ASSERT(sizeWidth);
+    GP_ASSERT(sizeHeight);
+    
+    // Generate the heightmap data needed for physics (one height per world unit).
     heightfieldData->width = sizeWidth + 1;
     heightfieldData->height = sizeHeight + 1;
     heightfieldData->heightData = new float[heightfieldData->width * heightfieldData->height];
@@ -897,13 +954,14 @@ PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* i
     // of its heightfield collision shape; see documentation for the btHeightfieldTerrainShape for more info.
     Vector3 s;
     node->getWorldMatrix().getScale(&s);
+    GP_ASSERT(s.y);
     centerOfMassOffset->set(0.0f, -(maxHeight - (0.5f * (maxHeight - minHeight))) / s.y, 0.0f);
 
-    // Create the bullet terrain shape
+    // Create the bullet terrain shape.
     btHeightfieldTerrainShape* terrainShape = bullet_new<btHeightfieldTerrainShape>(
         heightfieldData->width, heightfieldData->height, heightfieldData->heightData, 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);
 
-    // Create our collision shape object and store heightfieldData in it
+    // Create our collision shape object and store heightfieldData in it.
     PhysicsCollisionShape* shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_HEIGHTFIELD, terrainShape);
     shape->_shapeData.heightfieldData = heightfieldData;
 
@@ -916,7 +974,7 @@ PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3&
 {
     GP_ASSERT(mesh);
 
-    // Only support meshes with triangle list primitive types
+    // Only support meshes with triangle list primitive types.
     bool triMesh = true;
     if (mesh->getPartCount() > 0)
     {
@@ -951,10 +1009,11 @@ PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3&
     Bundle::MeshData* data = Bundle::readMeshData(mesh->getUrl());
     if (data == NULL)
     {
+        GP_ERROR("Failed to load mesh data from url '%s'.", mesh->getUrl());
         return NULL;
     }
 
-    // Create mesh data to be populated and store in returned collision shape
+    // Create mesh data to be populated and store in returned collision shape.
     PhysicsCollisionShape::MeshData* shapeMeshData = new PhysicsCollisionShape::MeshData();
     shapeMeshData->vertexData = NULL;
 
@@ -985,6 +1044,7 @@ PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3&
         for (unsigned int i = 0; i < partCount; i++)
         {
             meshPart = data->parts[i];
+            GP_ASSERT(meshPart);
 
             switch (meshPart->indexFormat)
             {
@@ -1000,6 +1060,13 @@ PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3&
                 indexType = PHY_INTEGER;
                 indexStride = 4;
                 break;
+            default:
+                GP_ERROR("Unsupported index format (%d).", meshPart->indexFormat);
+                SAFE_DELETE(meshInterface);
+                SAFE_DELETE_ARRAY(shapeMeshData->vertexData);
+                SAFE_DELETE(shapeMeshData);
+                SAFE_DELETE(data);
+                return NULL;
             }
 
             // Move the index data into the rigid body's local buffer.
@@ -1047,13 +1114,13 @@ PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3&
         meshInterface->addIndexedMesh(indexedMesh, indexedMesh.m_indexType);
     }
 
-    // Create our collision shape object and store shapeMeshData in it
+    // Create our collision shape object and store shapeMeshData in it.
     PhysicsCollisionShape* shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_MESH, bullet_new<btBvhTriangleMeshShape>(meshInterface, true));
     shape->_shapeData.meshData = shapeMeshData;
 
     _shapes.push_back(shape);
 
-    // Free the temporary mesh data now that it's stored in physics system
+    // Free the temporary mesh data now that it's stored in physics system.
     SAFE_DELETE(data);
 
     return shape;
@@ -1065,19 +1132,21 @@ void PhysicsController::destroyShape(PhysicsCollisionShape* shape)
     {
         if (shape->getRefCount() == 1)
         {
-            // Remove shape from shape cache
+            // Remove shape from shape cache.
             std::vector<PhysicsCollisionShape*>::iterator shapeItr = std::find(_shapes.begin(), _shapes.end(), shape);
             if (shapeItr != _shapes.end())
                 _shapes.erase(shapeItr);
         }
 
-        // Release the shape
+        // Release the shape.
         shape->release();
     }
 }
 
 float PhysicsController::calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
 {
+    GP_ASSERT(data);
+
     unsigned int x1 = x;
     unsigned int y1 = y;
     unsigned int x2 = x1 + 1;
@@ -1109,6 +1178,10 @@ float PhysicsController::calculateHeight(float* data, unsigned int width, unsign
 
 void PhysicsController::addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
 {
+    GP_ASSERT(a);
+    GP_ASSERT(constraint);
+    GP_ASSERT(_world);
+
     a->addConstraint(constraint);
     if (b)
     {
@@ -1120,15 +1193,19 @@ void PhysicsController::addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b,
 
 bool PhysicsController::checkConstraintRigidBodies(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
+    GP_ASSERT(a);
+
     if (!a->supportsConstraints())
     {
-        GP_WARN("Rigid body '%s' does not support constraints; unexpected behavior may occur.", a->_node->getId());
+        GP_ASSERT(a->_node);
+        GP_ERROR("Rigid body '%s' does not support constraints; unexpected behavior may occur.", a->_node->getId());
         return false;
     }
     
     if (b && !b->supportsConstraints())
     {
-        GP_WARN("Rigid body '%s' does not support constraints; unexpected behavior may occur.", b->_node->getId());
+        GP_ASSERT(b->_node);
+        GP_ERROR("Rigid body '%s' does not support constraints; unexpected behavior may occur.", b->_node->getId());
         return false;
     }
 
@@ -1137,6 +1214,9 @@ bool PhysicsController::checkConstraintRigidBodies(PhysicsRigidBody* a, PhysicsR
 
 void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
 {
+    GP_ASSERT(constraint);
+    GP_ASSERT(_world);
+
     // Find the constraint and remove it from the physics world.
     for (int i = _world->getNumConstraints() - 1; i >= 0; i--)
     {
@@ -1180,6 +1260,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
 
     Effect* effect = Effect::createFromSource(vs_str, fs_str);
     Material* material = Material::create(effect);
+    GP_ASSERT(material && material->getStateBlock());
     material->getStateBlock()->setDepthTest(true);
 
     VertexFormat::Element elements[] =
@@ -1200,12 +1281,14 @@ PhysicsController::DebugDrawer::~DebugDrawer()
 
 void PhysicsController::DebugDrawer::begin(const Matrix& viewProjection)
 {
+    GP_ASSERT(_meshBatch);
     _viewProjection = &viewProjection;
     _meshBatch->begin();
 }
 
 void PhysicsController::DebugDrawer::end()
 {
+    GP_ASSERT(_meshBatch && _meshBatch->getMaterial() && _meshBatch->getMaterial()->getParameter("u_viewProjectionMatrix"));
     _meshBatch->end();
     _meshBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_viewProjection);
     _meshBatch->draw();
@@ -1213,6 +1296,8 @@ void PhysicsController::DebugDrawer::end()
 
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
 {
+    GP_ASSERT(_meshBatch);
+
     static DebugDrawer::DebugVertex fromVertex, toVertex;
 
     fromVertex.x = from.getX();

+ 7 - 0
gameplay/src/PhysicsGenericConstraint.cpp

@@ -18,8 +18,11 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, PhysicsR
     : PhysicsConstraint(a, b), _rotationOffsetA(NULL), _rotationOffsetB(NULL),
     _translationOffsetA(NULL), _translationOffsetB(NULL)
 {
+    GP_ASSERT(a && a->_body && a->getNode());
+
     if (b)
     {
+        GP_ASSERT(b->_body && b->getNode());
         Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
         _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
     }
@@ -33,6 +36,8 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, const Qu
     PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
     : PhysicsConstraint(a, b), _rotationOffsetA(NULL), _rotationOffsetB(NULL), _translationOffsetA(NULL), _translationOffsetB(NULL)
 {
+    GP_ASSERT(a && a->_body && a->getNode());
+
     // Take scale into account for the first node's translation offset.
     Vector3 sA;
     a->getNode()->getWorldMatrix().getScale(&sA);
@@ -40,6 +45,8 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, const Qu
 
     if (b)
     {
+        GP_ASSERT(b->_body && b->getNode());
+
         // Take scale into account for the second node's translation offset.
         Vector3 sB;
         b->getNode()->getWorldMatrix().getScale(&sB);

+ 12 - 0
gameplay/src/PhysicsGenericConstraint.inl

@@ -8,6 +8,7 @@ inline const Quaternion& PhysicsGenericConstraint::getRotationOffsetA() const
     if (!_rotationOffsetA)
         _rotationOffsetA = new Quaternion();
 
+    GP_ASSERT(_constraint);
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getRotation();
     _rotationOffsetA->set(ro.x(), ro.y(), ro.z(), ro.w());
     return *_rotationOffsetA;
@@ -18,6 +19,7 @@ inline const Quaternion& PhysicsGenericConstraint::getRotationOffsetB() const
     if (!_rotationOffsetB)
         _rotationOffsetB = new Quaternion();
 
+    GP_ASSERT(_constraint);
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getRotation();
     _rotationOffsetB->set(ro.x(), ro.y(), ro.z(), ro.w());
     return *_rotationOffsetB;
@@ -28,6 +30,7 @@ inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetA() const
     if (!_translationOffsetA)
         _translationOffsetA = new Vector3();
 
+    GP_ASSERT(_constraint);
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getOrigin();
     _translationOffsetA->set(to.x(), to.y(), to.z());
     return *_translationOffsetA;
@@ -38,6 +41,7 @@ inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetB() const
     if (!_translationOffsetB)
         _translationOffsetB = new Vector3();
 
+    GP_ASSERT(_constraint);
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getOrigin();
     _translationOffsetB->set(to.x(), to.y(), to.z());
     return *_translationOffsetB;
@@ -45,41 +49,49 @@ inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetB() const
 
 inline void PhysicsGenericConstraint::setAngularLowerLimit(const Vector3& limits)
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setAngularLowerLimit(BV(limits));
 }
 
 inline void PhysicsGenericConstraint::setAngularUpperLimit(const Vector3& limits)
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setAngularUpperLimit(BV(limits));
 }
 
 inline void PhysicsGenericConstraint::setLinearLowerLimit(const Vector3& limits)
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setLinearLowerLimit(BV(limits));
 }
     
 inline void PhysicsGenericConstraint::setLinearUpperLimit(const Vector3& limits)
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setLinearUpperLimit(BV(limits));
 }
 
 inline void PhysicsGenericConstraint::setRotationOffsetA(const Quaternion& rotationOffset)
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setRotation(BQ(rotationOffset));
 }
 
 inline void PhysicsGenericConstraint::setRotationOffsetB(const Quaternion& rotationOffset)
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setRotation(BQ(rotationOffset));
 }
 
 inline void PhysicsGenericConstraint::setTranslationOffsetA(const Vector3& translationOffset)
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setOrigin(BV(translationOffset));
 }
 
 inline void PhysicsGenericConstraint::setTranslationOffsetB(const Vector3& translationOffset)
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setOrigin(BV(translationOffset));
 }
 

+ 10 - 3
gameplay/src/PhysicsGhostObject.cpp

@@ -11,9 +11,11 @@ PhysicsGhostObject::PhysicsGhostObject(Node* node, const PhysicsCollisionShape::
 {
     Vector3 centerOfMassOffset;
     PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
+    GP_ASSERT(physicsController);
 
     // Create and set the collision shape for the ghost object.
     _collisionShape = physicsController->createShape(node, shape, &centerOfMassOffset);
+    GP_ASSERT(_collisionShape);
 
     // Create the ghost object.
     _ghostObject = bullet_new<btPairCachingGhostObject>();
@@ -27,13 +29,16 @@ PhysicsGhostObject::PhysicsGhostObject(Node* node, const PhysicsCollisionShape::
     // Add the ghost object to the physics world.
     physicsController->addCollisionObject(this);
 
+    GP_ASSERT(_node);
     _node->addListener(this);
 }
 
 PhysicsGhostObject::~PhysicsGhostObject()
 {
+    GP_ASSERT(_node);
     _node->removeListener(this);
 
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
     SAFE_DELETE(_ghostObject);
@@ -42,10 +47,9 @@ PhysicsGhostObject::~PhysicsGhostObject()
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "ghostObject") == 0))
     {
-        GP_WARN("Failed to load ghost object from properties object: must be non-null object and have namespace equal to 'ghost'.");
+        GP_ERROR("Failed to load ghost object from properties object: must be non-null object and have namespace equal to 'ghost'.");
         return NULL;
     }
 
@@ -53,7 +57,7 @@ PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* propertie
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     {
-        GP_WARN("Failed to create collision shape during ghost object creation.");
+        GP_ERROR("Failed to create collision shape during ghost object creation.");
         return NULL;
     }
 
@@ -76,6 +80,9 @@ btCollisionObject* PhysicsGhostObject::getCollisionObject() const
 
 void PhysicsGhostObject::transformChanged(Transform* transform, long cookie)
 {
+    GP_ASSERT(_motionState);
+    GP_ASSERT(_ghostObject);
+
     // Update the motion state with the transform from the node.
     _motionState->updateTransformFromNode();
 

+ 5 - 0
gameplay/src/PhysicsHingeConstraint.cpp

@@ -8,6 +8,7 @@ namespace gameplay
 void PhysicsHingeConstraint::setLimits(float minAngle, float maxAngle, float bounciness)
 {
     // Use the defaults for softness (0.9) and biasFactor (0.3).
+    GP_ASSERT(_constraint);
     ((btHingeConstraint*)_constraint)->setLimit(minAngle, maxAngle, 0.9f, 0.3f, bounciness);
 }
 
@@ -15,6 +16,8 @@ PhysicsHingeConstraint::PhysicsHingeConstraint(PhysicsRigidBody* a, const Quater
     PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
     : PhysicsConstraint(a, b)
 {
+    GP_ASSERT(a && a->_body && a->getNode());
+
     // Take scale into account for the first node's translation offset.
     Vector3 sA;
     a->getNode()->getWorldMatrix().getScale(&sA);
@@ -22,6 +25,8 @@ PhysicsHingeConstraint::PhysicsHingeConstraint(PhysicsRigidBody* a, const Quater
 
     if (b)
     {
+        GP_ASSERT(b->_body && b->getNode());
+
         // Take scale into account for the second node's translation offset.
         Vector3 sB;
         b->getNode()->getWorldMatrix().getScale(&sB);

+ 5 - 0
gameplay/src/PhysicsMotionState.cpp

@@ -23,6 +23,7 @@ PhysicsMotionState::~PhysicsMotionState()
 
 void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 {
+    GP_ASSERT(_node);
     if (_node->getCollisionObject() && _node->getCollisionObject()->isKinematic())
         updateTransformFromNode();
 
@@ -31,6 +32,8 @@ void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 
 void PhysicsMotionState::setWorldTransform(const btTransform &transform)
 {
+    GP_ASSERT(_node);
+
     _worldTransform = transform * _centerOfMassOffset;
         
     const btQuaternion& rot = _worldTransform.getRotation();
@@ -42,6 +45,8 @@ void PhysicsMotionState::setWorldTransform(const btTransform &transform)
 
 void PhysicsMotionState::updateTransformFromNode() const
 {
+    GP_ASSERT(_node);
+
     // Store the initial world transform (minus the scale) for use by Bullet later on.
     Quaternion rotation;
     const Matrix& m = _node->getWorldMatrix();

+ 34 - 8
gameplay/src/PhysicsRigidBody.cpp

@@ -13,11 +13,15 @@ namespace gameplay
 PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters)
         : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL)
 {
-    // Create our collision shape
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+    GP_ASSERT(_node);
+
+    // Create our collision shape.
     Vector3 centerOfMassOffset;
     _collisionShape = Game::getInstance()->getPhysicsController()->createShape(node, shape, &centerOfMassOffset);
+    GP_ASSERT(_collisionShape && _collisionShape->getShape());
 
-    // Create motion state object
+    // Create motion state object.
     _motionState = new PhysicsMotionState(node, (centerOfMassOffset.lengthSquared() > MATH_EPSILON) ? &centerOfMassOffset : NULL);
 
     // If the mass is non-zero, then the object is dynamic so we calculate the local 
@@ -55,6 +59,10 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Defi
 
 PhysicsRigidBody::~PhysicsRigidBody()
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+    GP_ASSERT(_collisionShape);
+    GP_ASSERT(_node);
+
     // Clean up all constraints linked to this rigid body.
     if (_constraints)
     {
@@ -94,6 +102,7 @@ void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativeP
     // to make sure that it isn't sleeping and apply the force.
     if (force.lengthSquared() > MATH_EPSILON)
     {
+        GP_ASSERT(_body);
         _body->activate();
         if (relativePosition)
             _body->applyForce(BV(force), BV(*relativePosition));
@@ -108,8 +117,8 @@ void PhysicsRigidBody::applyImpulse(const Vector3& impulse, const Vector3* relat
     // to make sure that it isn't sleeping and apply the impulse.
     if (impulse.lengthSquared() > MATH_EPSILON)
     {
+        GP_ASSERT(_body);
         _body->activate();
-
         if (relativePosition)
         {
             _body->applyImpulse(BV(impulse), BV(*relativePosition));
@@ -125,6 +134,7 @@ void PhysicsRigidBody::applyTorque(const Vector3& torque)
     // to make sure that it isn't sleeping and apply the torque.
     if (torque.lengthSquared() > MATH_EPSILON)
     {
+        GP_ASSERT(_body);
         _body->activate();
         _body->applyTorque(BV(torque));
     }
@@ -136,6 +146,7 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
     // to make sure that it isn't sleeping and apply the torque impulse.
     if (torque.lengthSquared() > MATH_EPSILON)
     {
+        GP_ASSERT(_body);
         _body->activate();
         _body->applyTorqueImpulse(BV(torque));
     }
@@ -144,10 +155,9 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "rigidBody") == 0))
     {
-        GP_WARN("Failed to load rigid body from properties object: must be non-null object and have namespace equal to 'rigidBody'.");
+        GP_ERROR("Failed to load rigid body from properties object: must be non-null object and have namespace equal to 'rigidBody'.");
         return NULL;
     }
 
@@ -155,7 +165,7 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     {
-        GP_WARN("Failed to create collision shape during rigid body creation.");
+        GP_ERROR("Failed to create collision shape during rigid body creation.");
         return NULL;
     }
 
@@ -199,6 +209,10 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         {
             properties->getVector3(NULL, &parameters.gravity);
         }
+        else
+        {
+            // Ignore this case (the attributes for the rigid body's collision shape would end up here).
+        }
     }
 
     // Create the rigid body.
@@ -210,6 +224,8 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 
 void PhysicsRigidBody::setKinematic(bool kinematic)
 {
+    GP_ASSERT(_body);
+
     if (kinematic)
     {
         _body->setCollisionFlags(_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
@@ -224,13 +240,18 @@ void PhysicsRigidBody::setKinematic(bool kinematic)
 
 float PhysicsRigidBody::getHeight(float x, float y) const
 {
+    GP_ASSERT(_collisionShape);
+
     // This function is only supported for heightfield rigid bodies.
     if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
-        GP_WARN("Attempting to get the height of a non-heightfield rigid body.");
+        GP_ERROR("Attempting to get the height of a non-heightfield rigid body.");
         return 0.0f;
     }
 
+    GP_ASSERT(_collisionShape->_shapeData.heightfieldData);
+    GP_ASSERT(_node);
+
     // Calculate the correct x, y position relative to the heightfield data.
     if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
     {
@@ -241,6 +262,9 @@ float PhysicsRigidBody::getHeight(float x, float y) const
     float w = _collisionShape->_shapeData.heightfieldData->width;
     float h = _collisionShape->_shapeData.heightfieldData->height;
 
+    GP_ASSERT(w - 1);
+    GP_ASSERT(h - 1);
+
     Vector3 v = _collisionShape->_shapeData.heightfieldData->inverse * Vector3(x, 0.0f, y);
     x = (v.x + (0.5f * (w - 1))) * w / (w - 1);
     y = (v.z + (0.5f * (h - 1))) * h / (h - 1);
@@ -248,7 +272,7 @@ float PhysicsRigidBody::getHeight(float x, float y) const
     // Check that the x, y position is within the bounds.
     if (x < 0.0f || x > w || y < 0.0f || y > h)
     {
-        GP_WARN("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, w, h);
+        GP_ERROR("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, w, h);
         return 0.0f;
     }
 
@@ -257,6 +281,7 @@ float PhysicsRigidBody::getHeight(float x, float y) const
 
 void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 {
+    GP_ASSERT(constraint);
     if (_constraints == NULL)
         _constraints = new std::vector<PhysicsConstraint*>();
 
@@ -287,6 +312,7 @@ void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
 {
     if (getShapeType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
+        GP_ASSERT(_collisionShape && _collisionShape->_shapeData.heightfieldData);
         _collisionShape->_shapeData.heightfieldData->inverseIsDirty = true;
     }
 }

+ 16 - 0
gameplay/src/PhysicsRigidBody.inl

@@ -11,85 +11,101 @@ inline float PhysicsRigidBody::getMass() const
 
 inline float PhysicsRigidBody::getFriction() const
 {
+    GP_ASSERT(_body);
     return _body->getFriction();
 }
 
 inline void PhysicsRigidBody::setFriction(float friction)
 {
+    GP_ASSERT(_body);
     _body->setFriction(friction);
 }
 
 inline float PhysicsRigidBody::getRestitution() const
 {
+    GP_ASSERT(_body);
     return _body->getRestitution();
 }
 
 inline void PhysicsRigidBody::setRestitution(float restitution)
 {
+    GP_ASSERT(_body);
     _body->setRestitution(restitution);
 }
 
 inline float PhysicsRigidBody::getLinearDamping() const
 {
+    GP_ASSERT(_body);
     return _body->getLinearDamping();
 }
 
 inline float PhysicsRigidBody::getAngularDamping() const
 {
+    GP_ASSERT(_body);
     return _body->getAngularDamping();
 }
 
 inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
 {
+    GP_ASSERT(_body);
     _body->setDamping(linearDamping, angularDamping);
 }
 
 inline Vector3 PhysicsRigidBody::getLinearVelocity() const
 {
+    GP_ASSERT(_body);
     const btVector3& v = _body->getLinearVelocity();
     return Vector3(v.x(), v.y(), v.z());
 }
 
 inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
 {
+    GP_ASSERT(_body);
     _body->setLinearVelocity(BV(velocity));
 }
 
 inline Vector3 PhysicsRigidBody::getAngularVelocity() const
 {
+    GP_ASSERT(_body);
     const btVector3& v = _body->getAngularVelocity();
     return Vector3(v.x(), v.y(), v.z());
 }
 
 inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
 {
+    GP_ASSERT(_body);
     _body->setAngularVelocity(BV(velocity));
 }
 
 inline Vector3 PhysicsRigidBody::getAnisotropicFriction() const
 {
+    GP_ASSERT(_body);
     const btVector3& af = _body->getAnisotropicFriction();
     return Vector3(af.x(), af.y(), af.z());
 }
 
 inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
 {
+    GP_ASSERT(_body);
     _body->setAnisotropicFriction(BV(friction));
 }
 
 inline Vector3 PhysicsRigidBody::getGravity() const
 {
+    GP_ASSERT(_body);
     const btVector3& g = _body->getGravity();
     return Vector3(g.x(), g.y(), g.z());
 }
 
 inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
 {
+    GP_ASSERT(_body);
     _body->setGravity(BV(gravity));
 }
 
 inline bool PhysicsRigidBody::isStatic() const
 {
+    GP_ASSERT(_body);
     return _body->isStaticObject();
 }
 

+ 6 - 0
gameplay/src/PhysicsSocketConstraint.cpp

@@ -8,8 +8,10 @@ namespace gameplay
 PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
     : PhysicsConstraint(a, b)
 {
+    GP_ASSERT(a && a->_body && a->getNode());
     if (b)
     {
+        GP_ASSERT(b->_body && b->getNode());
         Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
         btTransform frameInA = getTransformOffset(a->getNode(), origin);
         btTransform frameInB = getTransformOffset(b->getNode(), origin);
@@ -26,6 +28,8 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, const Vect
                                                  PhysicsRigidBody* b, const Vector3& translationOffsetB)
     : PhysicsConstraint(a, b)
 {
+    GP_ASSERT(a && a->_body && a->getNode());
+
     // Take scale into account for the first node's translation offset.
     Vector3 sA;
     a->getNode()->getWorldMatrix().getScale(&sA);
@@ -33,6 +37,8 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, const Vect
 
     if (b)
     {
+        GP_ASSERT(b->_body && b->getNode());
+
         // Take scale into account for the second node's translation offset.
         Vector3 sB;
         b->getNode()->getWorldMatrix().getScale(&sB);

+ 8 - 0
gameplay/src/PhysicsSpringConstraint.cpp

@@ -8,6 +8,9 @@ namespace gameplay
 
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
+    GP_ASSERT(a && a->_body);
+    GP_ASSERT(b && b->_body);
+
     // Initialize the physics rigid body references since we don't call the PhysicsConstraint constructor that does it properly automatically.
     _a = a;
     _b = b;
@@ -19,6 +22,9 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, PhysicsRig
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
                                                  PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
 {
+    GP_ASSERT(a && a->_body && a->getNode());
+    GP_ASSERT(b && b->_body && b->getNode());
+
     // Initialize the physics rigid body references since we don't call the PhysicsConstraint constructor that does it properly automatically.
     _a = a;
     _b = b;
@@ -44,6 +50,7 @@ PhysicsSpringConstraint::~PhysicsSpringConstraint()
 
 void PhysicsSpringConstraint::setStrength(SpringProperty property, float strength)
 {
+    GP_ASSERT(_constraint);
     if (strength < MATH_EPSILON)
         ((btGeneric6DofSpringConstraint*)_constraint)->enableSpring(property, false);
     else
@@ -56,6 +63,7 @@ void PhysicsSpringConstraint::setStrength(SpringProperty property, float strengt
 
 void PhysicsSpringConstraint::setDamping(SpringProperty property, float damping)
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofSpringConstraint*)_constraint)->setDamping(property, damping);
     ((btGeneric6DofSpringConstraint*)_constraint)->setEquilibriumPoint(property);
 }

+ 33 - 14
gameplay/src/PlatformAndroid.cpp

@@ -11,7 +11,6 @@
 #include <android_native_app_glue.h>
 
 #include <android/log.h>
-#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
 
 // Externally referenced global variables.
 struct android_app* __state;
@@ -50,19 +49,22 @@ namespace gameplay
 
 static long timespec2millis(struct timespec *a)
 {
+    GP_ASSERT(a);
     return a->tv_sec*1000 + a->tv_nsec/1000000;
 }
 
 extern void printError(const char* format, ...)
 {
+    GP_ASSERT(format);
     va_list argptr;
     va_start(argptr, format);
-    LOGI(format, argptr);
+    __android_log_vprint(ANDROID_LOG_INFO, "gameplay-native-activity", format, argptr);
     va_end(argptr);
 }
 
 static EGLenum checkErrorEGL(const char* msg)
 {
+    GP_ASSERT(msg);
     static const char* errmsg[] =
     {
         "EGL function succeeded",
@@ -82,7 +84,7 @@ static EGLenum checkErrorEGL(const char* msg)
         "EGL power management event has occurred",
     };
     EGLenum error = eglGetError();
-    LOGI("%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
+    printError("%s: %s.", msg, errmsg[error - EGL_SUCCESS]);
     return error;
 }
 
@@ -232,17 +234,21 @@ static void displayKeyboard(android_app* state, bool show)
     // ANativeActivity_showSoftInput(state->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
     // ANativeActivity_hideSoftInput(state->activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY);
     
+    GP_ASSERT(state && state->activity && state->activity->vm);
+
     // Show or hide the keyboard by calling the appropriate Java method through JNI instead.
-    jint result;
     jint flags = 0;
     JavaVM* jvm = state->activity->vm;
-    JNIEnv* env;
+    JNIEnv* env = NULL;
     jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jvm->AttachCurrentThread(&env, NULL);
+    jint result = jvm->AttachCurrentThread(&env, NULL);
     if (result == JNI_ERR)
-    { 
+    {
+        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
         return; 
-    } 
+    }
+    GP_ASSERT(env);
+
     // Retrieves NativeActivity. 
     jobject lNativeActivity = state->activity->clazz;
     jclass ClassNativeActivity = env->GetObjectClass(lNativeActivity);
@@ -564,7 +570,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
         switch (action & AMOTION_EVENT_ACTION_MASK)
         {
             case AMOTION_EVENT_ACTION_DOWN:
-                // Primary pointer down
+                // Primary pointer down.
                 pointerId = AMotionEvent_getPointerId(event, 0);
                 gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0), pointerId);
                 __primaryTouchId = pointerId;
@@ -578,7 +584,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
                 __primaryTouchId = -1;
                 break;
             case AMOTION_EVENT_ACTION_POINTER_DOWN:
-                // Non-primary pointer down
+                // Non-primary pointer down.
                 if (__multiTouch)
                 {
                     pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
@@ -718,15 +724,23 @@ Platform* Platform::create(Game* game)
 
 int Platform::enterMessagePump()
 {
+    GP_ASSERT(__state && __state->activity && __state->activity->vm);
+
     __initialized = false;
     __suspended = false;
 
     // Get the android application's activity.
     ANativeActivity* activity = __state->activity;
     JavaVM* jvm = __state->activity->vm;
-    JNIEnv* env;
+    JNIEnv* env = NULL;
     jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jvm->AttachCurrentThread(&env, NULL);
+    jint res = jvm->AttachCurrentThread(&env, NULL);
+    if (res == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
+        return -1; 
+    }
+    GP_ASSERT(env);
 
     // Get the package name for this app from Java.
     jclass clazz = env->GetObjectClass(activity->clazz);
@@ -745,7 +759,7 @@ int Platform::enterMessagePump()
     FileSystem::setResourcePath(assetsPath.c_str());    
         
     // Get the asset manager to get the resources from the .apk file.
-    __assetManager = __state->activity->assetManager; 
+    __assetManager = activity->assetManager; 
     
     // Set the event call back functions.
     __state->onAppCmd = engine_handle_cmd;
@@ -893,16 +907,21 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
     }
     else
     {
-        // 0
         tx = __sensorEvent.acceleration.x;
         ty = __sensorEvent.acceleration.y;
     }
     tz = __sensorEvent.acceleration.z;
 
     if (pitch != NULL)
+    {
+        GP_ASSERT(tx * tx + tz * tz);
         *pitch = -atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
+    }
     if (roll != NULL)
+    {
+        GP_ASSERT(ty * ty + tz * tz);
         *roll = -atan(tx / sqrt(ty * ty + tz * tz)) * 180.0f * M_1_PI;
+    }
 }
 
 void Platform::swapBuffers()

+ 110 - 33
gameplay/src/PlatformMacOSX.mm

@@ -15,11 +15,11 @@ using namespace std;
 using namespace gameplay;
 
 // Default to 720p
-#define WINDOW_WIDTH    1280
-#define WINDOW_HEIGHT   720
+static int __width = 1280;
+static int __height = 720;
 
-static const float ACCELEROMETER_FACTOR_X = 90.0f / WINDOW_WIDTH;
-static const float ACCELEROMETER_FACTOR_Y = 90.0f / WINDOW_HEIGHT;
+static float ACCELEROMETER_FACTOR_X = 90.0f / __width;
+static float ACCELEROMETER_FACTOR_Y = 90.0f / __height;
 
 static long __timeStart;
 static long __timeAbsolute;
@@ -33,6 +33,8 @@ static bool __leftMouseDown = false;
 static bool __rightMouseDown = false;
 static bool __otherMouseDown = false;
 static bool __shiftDown = false;
+static char* __title = NULL;
+static bool __fullscreen = false;
 
 
 long getMachTimeInMilliseconds()
@@ -44,6 +46,7 @@ long getMachTimeInMilliseconds()
         (void) mach_timebase_info(&s_timebase_info);
     
     // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
+    GP_ASSERT(kOneMillion * s_timebase_info.denom);
     return (long)((mach_absolute_time() * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
 }
 
@@ -107,7 +110,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 
 - (id) initWithFrame: (NSRect) frame
 {    
-    NSOpenGLPixelFormatAttribute attrs[] = 
+    NSOpenGLPixelFormatAttribute windowedAttrs[] = 
     {
         NSOpenGLPFAAccelerated,
         NSOpenGLPFADoubleBuffer,
@@ -117,6 +120,18 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
         NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
         0
     };
+    NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
+    {
+        NSOpenGLPFADoubleBuffer,
+        NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
+        NSOpenGLPFAFullScreen,
+        NSOpenGLPFAColorSize, 32,
+        NSOpenGLPFADepthSize, 24,
+        NSOpenGLPFAAlphaSize, 8,
+        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
+        0
+    };
+    NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
     
     NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
     if (!pf)
@@ -135,14 +150,14 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 {
     [super prepareOpenGL];
     
-    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
-    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
-    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
     _game->run();
     
-    [[self window] setLevel: NSFloatingWindowLevel];
+    if (__fullscreen)
+        [[self window] setLevel: NSMainMenuWindowLevel+1];
+    else
+        [[self window] setLevel: NSFloatingWindowLevel];
     [[self window] makeKeyAndOrderFront: self];
-    [[self window] setTitle: [NSString stringWithUTF8String: ""]];
+    [[self window] setTitle: [NSString stringWithUTF8String: __title ? __title : ""]];
     
     // Make all the OpenGL calls to setup rendering and build the necessary rendering objects
     [[self openGLContext] makeCurrentContext];
@@ -160,6 +175,10 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
     CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
     
+    GLint dim[2] = {__width, __height};
+    CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim);
+    CGLEnable(cglContext, kCGLCESurfaceBackingSize);
+    
     // Activate the display link
     CVDisplayLinkStart(displayLink);
 }
@@ -204,20 +223,20 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __leftMouseDown = true;
-    [self mouse: Mouse::MOUSE_PRESS_LEFT_BUTTON orTouchEvent: Touch::TOUCH_PRESS x: point.x y: WINDOW_HEIGHT - point.y s: 0];
+    [self mouse: Mouse::MOUSE_PRESS_LEFT_BUTTON orTouchEvent: Touch::TOUCH_PRESS x: point.x y: __height - point.y s: 0];
 }
 
 - (void) mouseUp: (NSEvent*) event
 {
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __leftMouseDown = false;
-    [self mouse: Mouse::MOUSE_RELEASE_LEFT_BUTTON orTouchEvent: Touch::TOUCH_RELEASE x: point.x y: WINDOW_HEIGHT - point.y s: 0];
+    [self mouse: Mouse::MOUSE_RELEASE_LEFT_BUTTON orTouchEvent: Touch::TOUCH_RELEASE x: point.x y: __height - point.y s: 0];
 }
 
 - (void)mouseMoved:(NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, point.x, WINDOW_HEIGHT - point.y, 0);
+    Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void) mouseDragged: (NSEvent*) event
@@ -225,7 +244,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     if (__leftMouseDown)
     {
-        [self mouse: Mouse::MOUSE_MOVE orTouchEvent: Touch::TOUCH_MOVE x: point.x y: WINDOW_HEIGHT - point.y s: 0];
+        [self mouse: Mouse::MOUSE_MOVE orTouchEvent: Touch::TOUCH_MOVE x: point.x y: __height - point.y s: 0];
     }
 }
 
@@ -234,15 +253,15 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     __rightMouseDown = true;
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __lx = point.x;
-    __ly = WINDOW_HEIGHT - point.y;    
-    _game->mouseEvent(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, WINDOW_HEIGHT - point.y, 0);
+    __ly = __height - point.y;    
+    _game->mouseEvent(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void) rightMouseUp: (NSEvent*) event
 {
    __rightMouseDown = false;
     NSPoint point = [event locationInWindow];
-    _game->mouseEvent(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, WINDOW_HEIGHT - point.y, 0);
+    _game->mouseEvent(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void) rightMouseDragged: (NSEvent*) event
@@ -252,7 +271,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     {
         // Update the pitch and roll by adding the scaled deltas.
         __roll += (float)(point.x - __lx) * ACCELEROMETER_FACTOR_X;
-        __pitch -= -(float)(point.y - (WINDOW_HEIGHT - __ly)) * ACCELEROMETER_FACTOR_Y;
+        __pitch -= -(float)(point.y - (__height - __ly)) * ACCELEROMETER_FACTOR_Y;
     
         // Clamp the values to the valid range.
         __roll = max(min(__roll, 90.0f), -90.0f);
@@ -260,32 +279,32 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     
         // Update the last X/Y values.
         __lx = point.x;
-        __ly = (WINDOW_HEIGHT - point.y);
+        __ly = (__height - point.y);
     }
     
     // In right-mouse case, whether __rightMouseDown is true or false
     // this should not matter, mouse move is still occuring
-    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, WINDOW_HEIGHT - point.y, 0);
+    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseDown: (NSEvent *) event 
 {
     __otherMouseDown = true;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, WINDOW_HEIGHT - point.y, 0);
+    _game->mouseEvent(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseUp: (NSEvent *) event 
 {
     __otherMouseDown = false;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, WINDOW_HEIGHT - point.y, 0);
+    _game->mouseEvent(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseDragged: (NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, WINDOW_HEIGHT - point.y, 0);
+    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void) mouseEntered: (NSEvent*)event
@@ -296,7 +315,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 - (void)scrollWheel: (NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, point.x, WINDOW_HEIGHT - point.y, (int)([event deltaY] * 10.0f));
+    Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
 }
 
 - (void) mouseExited: (NSEvent*)event
@@ -557,12 +576,25 @@ int getKey(unsigned short keyCode, unsigned int modifierFlags)
 
 @end
 
+@interface FullscreenWindow : NSWindow
+{ 
+}
+@end
+
+@implementation FullscreenWindow
+- (BOOL)canBecomeKeyWindow
+{
+    return YES;
+}
+@end
+
 
 namespace gameplay
 {
 
 extern void printError(const char* format, ...)
 {
+    GP_ASSERT(format);
     va_list argptr;
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
@@ -591,10 +623,40 @@ Platform* Platform::create(Game* game)
 
 int Platform::enterMessagePump()
 {
+    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
+    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
+    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
+    
+    // Read window settings from config.
+    if (_game->getConfig())
+    {
+        Properties* config = _game->getConfig()->getNamespace("window", true);
+        if (config)
+        {
+            // Read window title.
+            __title = const_cast<char *>(config->getString("title"));
+
+            // Read window size.
+            int width = config->getInt("width");
+            if (width != 0)
+                __width = width;
+            int height = config->getInt("height");
+            if (height != 0)
+                __height = height;
+
+            // Read fullscreen state.
+            __fullscreen = config->getBool("fullscreen");
+        }
+    }
+
+    // Set the scale factors for the mouse movement used to simulate the accelerometer.
+    ACCELEROMETER_FACTOR_X = 90.0f / __width;
+    ACCELEROMETER_FACTOR_Y = 90.0f / __height;
+
     NSAutoreleasePool* pool = [NSAutoreleasePool new];
     NSApplication* app = [NSApplication sharedApplication];
     NSRect screenBounds = [[NSScreen mainScreen] frame];
-    NSRect viewBounds = NSMakeRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
+    NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
     
     __view = [[View alloc] initWithFrame:viewBounds];
     
@@ -603,11 +665,23 @@ int Platform::enterMessagePump()
                                  viewBounds.size.width, 
                                  viewBounds.size.height);
     
-    NSWindow* window = [[NSWindow alloc]
-                        initWithContentRect:centered
-                        styleMask:NSTitledWindowMask | NSClosableWindowMask
-                        backing:NSBackingStoreBuffered
-                        defer:NO];
+    NSWindow* window = NULL;
+    if (__fullscreen)
+    {
+        window = [[FullscreenWindow alloc]
+                   initWithContentRect:screenBounds
+                   styleMask:NSBorderlessWindowMask
+                   backing:NSBackingStoreBuffered
+                   defer:NO];
+    }
+    else
+    {
+        window = [[NSWindow alloc]
+                   initWithContentRect:centered
+                   styleMask:NSTitledWindowMask | NSClosableWindowMask
+                   backing:NSBackingStoreBuffered
+                   defer:NO];
+    }
     
     [window setAcceptsMouseMovedEvents:YES];
     [window setContentView:__view];
@@ -632,12 +706,12 @@ void Platform::signalShutdown()
     
 unsigned int Platform::getDisplayWidth()
 {
-    return WINDOW_WIDTH;
+    return __width;
 }
 
 unsigned int Platform::getDisplayHeight()
 {
-    return WINDOW_HEIGHT;
+    return __height;
 }
 
 long Platform::getAbsoluteTime()
@@ -672,6 +746,9 @@ bool Platform::isMultiTouch()
     
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
     *pitch = __pitch;
     *roll = __roll;
 }
@@ -697,7 +774,7 @@ void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned
     
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(evt, key);
 }
     

+ 14 - 6
gameplay/src/PlatformQNX.cpp

@@ -401,6 +401,7 @@ static int getUnicode(int qnxKeyCode)
 
 extern void printError(const char* format, ...)
 {
+    GP_ASSERT(format);
     va_list argptr;
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
@@ -409,6 +410,7 @@ extern void printError(const char* format, ...)
 
 EGLenum checkErrorEGL(const char* msg)
 {
+    GP_ASSERT(msg);
     static const char* errmsg[] =
     {
         "EGL function succeeded",
@@ -751,6 +753,7 @@ error:
  */
 long timespec2millis(struct timespec *a)
 {
+    GP_ASSERT(a);
     return a->tv_sec*1000 + a->tv_nsec/1000000;
 }
 
@@ -773,6 +776,8 @@ void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEven
 
 int Platform::enterMessagePump()
 {
+    GP_ASSERT(_game);
+
     int rc;
     int eventType;
     int flags;
@@ -849,7 +854,7 @@ int Platform::enterMessagePump()
                         // A move event will be fired unless a button state changed.
                         bool move = true;
                         bool left_move = false;
-                        //This is a mouse move event, it is applicable to a device with a usb mouse or simulator
+                        // This is a mouse move event, it is applicable to a device with a usb mouse or simulator.
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
@@ -875,7 +880,7 @@ int Platform::enterMessagePump()
                             mouseOrTouchEvent(Mouse::MOUSE_RELEASE_LEFT_BUTTON, Touch::TOUCH_RELEASE, position[0], position[1]);
                         }
 
-                        // Handle right mouse
+                        // Handle right mouse.
                         if (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
                         {
                             if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0)
@@ -892,7 +897,7 @@ int Platform::enterMessagePump()
                             Game::getInstance()->mouseEvent(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
                         }
 
-                        // Handle middle mouse
+                        // Handle middle mouse.
                         if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
                         {
                             if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0)
@@ -919,7 +924,7 @@ int Platform::enterMessagePump()
                             Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, position[0], position[1], 0);
                         }
 
-                        // Handle mouse wheel events
+                        // Handle mouse wheel events.
                         if (wheel)
                         {
                             Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
@@ -932,7 +937,7 @@ int Platform::enterMessagePump()
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
                         gameplay::Keyboard::KeyEvent evt = (flags & KEY_DOWN) ? gameplay::Keyboard::KEY_PRESS :  gameplay::Keyboard::KEY_RELEASE;
-                        // Suppress key repeats
+                        // Suppress key repeats.
                         if ((flags & KEY_REPEAT) == 0)
                         {
                             keyEventInternal(evt, getKey(value));
@@ -1076,6 +1081,9 @@ bool Platform::isMultiTouch()
 
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
     switch(__orientationAngle)
     {
     // Landscape based device adjusting for landscape game mode
@@ -1136,7 +1144,7 @@ void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(evt, key);
 }
 

+ 67 - 33
gameplay/src/PlatformWin32.cpp

@@ -8,6 +8,8 @@
 #include <GL/wglew.h>
 #include <windowsx.h>
 
+using gameplay::printError;
+
 // Default to 720p
 static int __width = 1280;
 static int __height = 720;
@@ -257,11 +259,13 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     if (hwnd != __hwnd)
     {
-        // Ignore messages that are not for our game window
-        return DefWindowProc(hwnd, msg, wParam, lParam); 
+        // Ignore messages that are not for our game window.
+        return DefWindowProc(hwnd, msg, wParam, lParam);
     }
 
     // Scale factors for the mouse movement used to simulate the accelerometer.
+    GP_ASSERT(__width);
+    GP_ASSERT(__height);
     static const float ACCELEROMETER_X_FACTOR = 90.0f / __width;
     static const float ACCELEROMETER_Y_FACTOR = 90.0f / __height;
 
@@ -374,7 +378,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         if (wParam == VK_CAPITAL)
             capsOn = !capsOn;
 
-        // Suppress key repeats
+        // Suppress key repeats.
         if ((lParam & 0x40000000) == 0)
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown ^ capsOn));
         break;
@@ -387,13 +391,13 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         break;
 
     case WM_CHAR:
-        // Suppress key repeats
+        // Suppress key repeats.
         if ((lParam & 0x40000000) == 0)
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
         break;
 
     case WM_UNICHAR:
-        // Suppress key repeats
+        // Suppress key repeats.
         if ((lParam & 0x40000000) == 0)
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
         break;
@@ -449,6 +453,8 @@ Platform::~Platform()
 
 Platform* Platform::create(Game* game)
 {
+    GP_ASSERT(game);
+
     FileSystem::setResourcePath("./");
 
     Platform* platform = new Platform(game);
@@ -460,31 +466,34 @@ Platform* Platform::create(Game* game)
     std::wstring windowName;
     bool fullscreen = false;
     
-    // Read window settings from config
-    Properties* config = game->getConfig()->getNamespace("window", true);
-    if (config)
+    // Read window settings from config.
+    if (game->getConfig())
     {
-        // Read window title
-        const char* title = config->getString("title");
-        if (title)
+        Properties* config = game->getConfig()->getNamespace("window", true);
+        if (config)
         {
-            int len = MultiByteToWideChar(CP_ACP, 0, title, -1, NULL, 0);
-            wchar_t* wtitle = new wchar_t[len];
-            MultiByteToWideChar(CP_ACP, 0, title, -1, wtitle, len);
-            windowName = wtitle;
-            SAFE_DELETE_ARRAY(wtitle);
-        }
+            // Read window title.
+            const char* title = config->getString("title");
+            if (title)
+            {
+                int len = MultiByteToWideChar(CP_ACP, 0, title, -1, NULL, 0);
+                wchar_t* wtitle = new wchar_t[len];
+                MultiByteToWideChar(CP_ACP, 0, title, -1, wtitle, len);
+                windowName = wtitle;
+                SAFE_DELETE_ARRAY(wtitle);
+            }
 
-        // Read window size
-        int width = config->getInt("width");
-        if (width != 0)
-            __width = width;
-        int height = config->getInt("height");
-        if (height != 0)
-            __height = height;
+            // Read window size.
+            int width = config->getInt("width");
+            if (width != 0)
+                __width = width;
+            int height = config->getInt("height");
+            if (height != 0)
+                __height = height;
 
-        // Read fullscreen state
-        fullscreen = config->getBool("fullscreen");
+            // Read fullscreen state.
+            fullscreen = config->getBool("fullscreen");
+        }
     }
 
     RECT rect = { 0, 0, __width, __height };
@@ -505,7 +514,10 @@ Platform* Platform::create(Game* game)
     wc.lpszClassName  = windowClass;
 
     if (!::RegisterClassEx(&wc))
+    {
+        GP_ERROR("Failed to register window class.");
         goto error;
+    }
 
     if (fullscreen)
     {
@@ -517,11 +529,12 @@ Platform* Platform::create(Game* game)
         dm.dmBitsPerPel	= 32;
         dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
 
-        // Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
+        // Try to set selected mode and get results. NOTE: CDS_FULLSCREEN gets rid of start bar.
         if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
         {
             fullscreen = false;
-            GP_ERROR("Failed to start fullscreen with resolution %dx%d.", __width, __height);
+            GP_ERROR("Failed to start game in full-screen mode with resolution %dx%d.", __width, __height);
+            goto error;
         }
     }
 
@@ -557,14 +570,25 @@ Platform* Platform::create(Game* game)
 
     int pixelFormat = ChoosePixelFormat(__hdc, &pfd);
 
-    if (pixelFormat == 0 || !SetPixelFormat (__hdc, pixelFormat, &pfd))
+    if (pixelFormat == 0)
+    {
+        GP_ERROR("Failed to choose a pixel format.");
+        goto error;
+    }
+    if (!SetPixelFormat (__hdc, pixelFormat, &pfd))
+    {
+        GP_ERROR("Failed to set the pixel format.");
         goto error;
+    }
 
     HGLRC tempContext = wglCreateContext(__hdc);
     wglMakeCurrent(__hdc, tempContext);
 
     if (GLEW_OK != glewInit())
+    {
+        GP_ERROR("Failed to initialize GLEW.");
         goto error;
+    }
 
     int attribs[] =
     {
@@ -576,17 +600,21 @@ Platform* Platform::create(Game* game)
     if (!(__hrc = wglCreateContextAttribsARB(__hdc, 0, attribs) ) )
     {
         wglDeleteContext(tempContext);
+        GP_ERROR("Failed to create OpenGL context.");
         goto error;
     }
     wglDeleteContext(tempContext);
 
     if (!wglMakeCurrent(__hdc, __hrc) || !__hrc)
+    {
+        GP_ERROR("Failed to make the window current.");
         goto error;
+    }
 
     // Vertical sync.
     wglSwapIntervalEXT(__vsync ? 1 : 0);
 
-    // Show the window
+    // Show the window.
     ShowWindow(__hwnd, SW_SHOW);
 
     return platform;
@@ -598,7 +626,8 @@ error:
 }
 
 int Platform::enterMessagePump()
-{  
+{
+    GP_ASSERT(_game);
     int rc = 0;
 
     // Get the initial time.
@@ -607,6 +636,7 @@ int Platform::enterMessagePump()
     __timeTicksPerMillis = (long)(tps.QuadPart / 1000L);
     LARGE_INTEGER queryTime;
     QueryPerformanceCounter(&queryTime);
+    GP_ASSERT(__timeTicksPerMillis);
     __timeStart = queryTime.QuadPart / __timeTicksPerMillis;
 
     // Set the initial pitch and roll values.
@@ -659,8 +689,9 @@ unsigned int Platform::getDisplayHeight()
     
 long Platform::getAbsoluteTime()
 {
-       LARGE_INTEGER queryTime;
+    LARGE_INTEGER queryTime;
     QueryPerformanceCounter(&queryTime);
+    GP_ASSERT(__timeTicksPerMillis);
     __timeAbsolute = queryTime.QuadPart / __timeTicksPerMillis;
 
     return __timeAbsolute;
@@ -693,6 +724,9 @@ bool Platform::isMultiTouch()
 
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
     *pitch = __pitch;
     *roll = __roll;
 }
@@ -718,7 +752,7 @@ void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(evt, key);
 }
 

+ 3 - 1
gameplay/src/PlatformiOS.mm

@@ -549,6 +549,7 @@ long getMachTimeInMilliseconds()
         (void) mach_timebase_info(&s_timebase_info);
     
     // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
+    GP_ASSERT(kOneMillion * s_timebase_info.denom);
     return (long)((mach_absolute_time() * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
 }
 
@@ -773,6 +774,7 @@ namespace gameplay
     
 extern void printError(const char* format, ...)
 {
+    GP_ASSERT(format);
     va_list argptr;
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
@@ -891,7 +893,7 @@ void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(evt, key);
 }
     

+ 11 - 2
gameplay/src/Ref.cpp

@@ -76,6 +76,7 @@ void Ref::printLeaks()
         for (RefAllocationRecord* rec = __refAllocations; rec != NULL; rec = rec->next)
         {
             Ref* ref = rec->ref;
+            GP_ASSERT(ref);
             const char* type = typeid(*ref).name();
             printError("[memory] LEAK: Ref object '%s' still active with reference count %d.\n", (type ? type : ""), ref->getRefCount());
         }
@@ -84,7 +85,9 @@ void Ref::printLeaks()
 
 void* trackRef(Ref* ref)
 {
-    // Create memory allocation record
+    GP_ASSERT(ref);
+
+    // Create memory allocation record.
     RefAllocationRecord* rec = (RefAllocationRecord*)malloc(sizeof(RefAllocationRecord));
     rec->ref = ref;
     rec->next = __refAllocations;
@@ -100,6 +103,12 @@ void* trackRef(Ref* ref)
 
 void untrackRef(Ref* ref, void* record)
 {
+    if (!record)
+    {
+        printError("[memory] ERROR: Attempting to free null ref tracking record.\n");
+        return;
+    }
+
     RefAllocationRecord* rec = (RefAllocationRecord*)record;
     if (rec->ref != ref)
     {
@@ -107,7 +116,7 @@ void untrackRef(Ref* ref, void* record)
         return;
     }
 
-    // Link this item out
+    // Link this item out.
     if (__refAllocations == rec)
         __refAllocations = rec->next;
     if (rec->prev)

+ 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;

+ 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];
 }
 

+ 1 - 1
gameplay/src/gameplay-main-android.cpp

@@ -18,8 +18,8 @@ void android_main(struct android_app* state)
     
     __state = state;
     Game* game = Game::getInstance();
-    GP_ASSERT(game != NULL);
     Platform* platform = Platform::create(game);
+    GP_ASSERT(platform);
     platform->enterMessagePump();
     delete platform;
     

+ 1 - 1
gameplay/src/gameplay-main-ios.mm

@@ -10,8 +10,8 @@ using namespace gameplay;
 int main(int argc, char** argv)
 {    
     Game* game = Game::getInstance();
-    GP_ASSERT(game != NULL);
     Platform* platform = Platform::create(game);
+    GP_ASSERT(platform);
     int result = platform->enterMessagePump();
 	delete platform;
     return result;

+ 1 - 1
gameplay/src/gameplay-main-macosx.mm

@@ -10,8 +10,8 @@ using namespace gameplay;
 int main(int argc, char** argv)
 {
     Game* game = Game::getInstance();
-    GP_ASSERT(game != NULL);
     Platform* platform = Platform::create(game);
+    GP_ASSERT(platform);
     int result = platform->enterMessagePump();
 	delete platform;
     return result;

+ 1 - 1
gameplay/src/gameplay-main-qnx.cpp

@@ -10,8 +10,8 @@ using namespace gameplay;
 int main(int argc, char** argv)
 {
     Game* game = Game::getInstance();
-    GP_ASSERT(game != NULL);
     Platform* platform = Platform::create(game);
+    GP_ASSERT(platform);
     int result = platform->enterMessagePump();
     delete platform;
     return result;

+ 1 - 1
gameplay/src/gameplay-main-win32.cpp

@@ -15,8 +15,8 @@ using namespace gameplay;
 extern "C" int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
 {
     Game* game = Game::getInstance();
-    GP_ASSERT(game != NULL);
     Platform* platform = Platform::create(game);
+    GP_ASSERT(platform);
     int result = platform->enterMessagePump();
     delete platform;
     return result;