Browse Source

Merge pull request #112 from blackberry-gaming/next

Next
Sean Paul Taylor 13 năm trước cách đây
mục cha
commit
ac58818d6e
85 tập tin đã thay đổi với 2955 bổ sung946 xóa
  1. 1 1
      gameplay/android/jni/Android.mk
  2. 5 5
      gameplay/src/AnimationTarget.cpp
  3. 2 4
      gameplay/src/AudioSource.cpp
  4. 20 13
      gameplay/src/Base.h
  5. 6 4
      gameplay/src/BoundingBox.cpp
  6. 8 0
      gameplay/src/BoundingSphere.cpp
  7. 225 107
      gameplay/src/Bundle.cpp
  8. 10 17
      gameplay/src/Camera.cpp
  9. 2 2
      gameplay/src/Container.cpp
  10. 3 3
      gameplay/src/Control.cpp
  11. 10 10
      gameplay/src/DebugNew.cpp
  12. 10 7
      gameplay/src/DepthStencilTarget.cpp
  13. 31 10
      gameplay/src/Effect.cpp
  14. 148 27
      gameplay/src/FileSystem.cpp
  15. 57 0
      gameplay/src/FileSystem.h
  16. 72 8
      gameplay/src/Font.cpp
  17. 123 3
      gameplay/src/Form.cpp
  18. 19 9
      gameplay/src/FrameBuffer.cpp
  19. 1 0
      gameplay/src/Frustum.cpp
  20. 59 15
      gameplay/src/Game.cpp
  21. 17 4
      gameplay/src/Game.h
  22. 1 0
      gameplay/src/Game.inl
  23. 32 8
      gameplay/src/Image.cpp
  24. 2 0
      gameplay/src/Joint.cpp
  25. 1 0
      gameplay/src/Layout.h
  26. 32 4
      gameplay/src/Light.cpp
  27. 66 26
      gameplay/src/Material.cpp
  28. 52 11
      gameplay/src/MaterialParameter.cpp
  29. 44 27
      gameplay/src/Matrix.cpp
  30. 18 3
      gameplay/src/Mesh.cpp
  31. 25 9
      gameplay/src/MeshBatch.cpp
  32. 10 5
      gameplay/src/MeshBatch.inl
  33. 10 0
      gameplay/src/MeshPart.cpp
  34. 6 2
      gameplay/src/MeshSkin.cpp
  35. 48 7
      gameplay/src/Model.cpp
  36. 24 20
      gameplay/src/Node.cpp
  37. 62 29
      gameplay/src/ParticleEmitter.cpp
  38. 7 6
      gameplay/src/Pass.cpp
  39. 70 34
      gameplay/src/PhysicsCharacter.cpp
  40. 10 0
      gameplay/src/PhysicsCollisionObject.cpp
  41. 33 13
      gameplay/src/PhysicsCollisionShape.cpp
  42. 14 2
      gameplay/src/PhysicsConstraint.cpp
  43. 4 0
      gameplay/src/PhysicsConstraint.inl
  44. 129 44
      gameplay/src/PhysicsController.cpp
  45. 7 0
      gameplay/src/PhysicsGenericConstraint.cpp
  46. 12 0
      gameplay/src/PhysicsGenericConstraint.inl
  47. 10 3
      gameplay/src/PhysicsGhostObject.cpp
  48. 5 0
      gameplay/src/PhysicsHingeConstraint.cpp
  49. 5 0
      gameplay/src/PhysicsMotionState.cpp
  50. 34 8
      gameplay/src/PhysicsRigidBody.cpp
  51. 16 0
      gameplay/src/PhysicsRigidBody.inl
  52. 6 0
      gameplay/src/PhysicsSocketConstraint.cpp
  53. 8 0
      gameplay/src/PhysicsSpringConstraint.cpp
  54. 5 1
      gameplay/src/Plane.cpp
  55. 34 15
      gameplay/src/PlatformAndroid.cpp
  56. 111 34
      gameplay/src/PlatformMacOSX.mm
  57. 15 7
      gameplay/src/PlatformQNX.cpp
  58. 109 28
      gameplay/src/PlatformWin32.cpp
  59. 4 2
      gameplay/src/PlatformiOS.mm
  60. 57 28
      gameplay/src/Properties.cpp
  61. 13 6
      gameplay/src/Properties.h
  62. 10 4
      gameplay/src/Quaternion.cpp
  63. 12 10
      gameplay/src/Ray.cpp
  64. 2 0
      gameplay/src/Rectangle.cpp
  65. 15 6
      gameplay/src/Ref.cpp
  66. 99 53
      gameplay/src/RenderState.cpp
  67. 7 3
      gameplay/src/RenderTarget.cpp
  68. 16 9
      gameplay/src/Scene.cpp
  69. 125 59
      gameplay/src/SceneLoader.cpp
  70. 36 13
      gameplay/src/SpriteBatch.cpp
  71. 5 4
      gameplay/src/Technique.cpp
  72. 544 82
      gameplay/src/Texture.cpp
  73. 15 10
      gameplay/src/Texture.h
  74. 11 0
      gameplay/src/Theme.cpp
  75. 16 8
      gameplay/src/Transform.cpp
  76. 10 10
      gameplay/src/Vector2.cpp
  77. 14 14
      gameplay/src/Vector3.cpp
  78. 18 18
      gameplay/src/Vector4.cpp
  79. 13 6
      gameplay/src/VertexAttributeBinding.cpp
  80. 2 1
      gameplay/src/VertexFormat.cpp
  81. 1 1
      gameplay/src/gameplay-main-android.cpp
  82. 1 1
      gameplay/src/gameplay-main-ios.mm
  83. 1 1
      gameplay/src/gameplay-main-macosx.mm
  84. 1 1
      gameplay/src/gameplay-main-qnx.cpp
  85. 1 1
      gameplay/src/gameplay-main-win32.cpp

+ 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
 

+ 5 - 5
gameplay/src/AnimationTarget.cpp

@@ -123,7 +123,7 @@ Animation* AnimationTarget::createAnimation(const char* id, Properties* animatio
     const char* propertyIdStr = animationProperties->getString("property");
     if (propertyIdStr == NULL)
     {
-        GP_ERROR("Attribute \'property\' must be specified for an animation.");
+        GP_ERROR("Attribute 'property' must be specified for an animation.");
         return NULL;
     }
     
@@ -138,28 +138,28 @@ Animation* AnimationTarget::createAnimation(const char* id, Properties* animatio
     unsigned int keyCount = animationProperties->getInt("keyCount");
     if (keyCount == 0)
     {
-        GP_ERROR("Attribute \'keyCount\' must be specified for an animation.");
+        GP_ERROR("Attribute 'keyCount' must be specified for an animation.");
         return NULL;
     }
 
     const char* keyTimesStr = animationProperties->getString("keyTimes");
     if (keyTimesStr == NULL)
     {
-        GP_ERROR("Attribute \'keyTimes\' must be specified for an animation.");
+        GP_ERROR("Attribute 'keyTimes' must be specified for an animation.");
         return NULL;
     }
     
     const char* keyValuesStr = animationProperties->getString("keyValues");
     if (keyValuesStr == NULL)
     {
-        GP_ERROR("Attribute \'keyValues\' must be specified for an animation.");
+        GP_ERROR("Attribute 'keyValues' must be specified for an animation.");
         return NULL;
     }
     
     const char* curveStr = animationProperties->getString("curve");
     if (curveStr == NULL)
     {
-        GP_ERROR("Attribute \'curve\' must be specified for an animation.");
+        GP_ERROR("Attribute 'curve' must be specified for an animation.");
         return NULL;
     }
     

+ 2 - 4
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;
         }
 
@@ -75,7 +73,7 @@ AudioSource* AudioSource::create(Properties* properties)
     GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "audio") == 0))
     {
-        GP_ERROR("Failed to load audio source from properties object: must be non-null object and have namespace equal to \'audio\'.");
+        GP_ERROR("Failed to load audio source from properties object: must be non-null object and have namespace equal to 'audio'.");
         return NULL;
     }
 

+ 20 - 13
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)
@@ -168,7 +178,6 @@ extern void printError(const char* format, ...);
 #include <png.h>
 
 #define WINDOW_VSYNC        1
-#define WINDOW_FULLSCREEN   0
 
 // Graphics (OpenGL)
 #if defined (__QNX__) || defined(__ANDROID__)
@@ -181,7 +190,6 @@ extern void printError(const char* format, ...);
     extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
     #define glClearDepth glClearDepthf
     #define OPENGL_ES
-    #define USE_PVRTC
 #elif WIN32
     #define WIN32_LEAN_AND_MEAN
     #include <GL/glew.h>
@@ -197,7 +205,6 @@ extern void printError(const char* format, ...);
         #define glIsVertexArray glIsVertexArrayOES
         #define glClearDepth glClearDepthf
         #define OPENGL_ES
-        #define USE_PVRTC
         #define USE_VAO
     #elif TARGET_OS_MAC
         #include <OpenGL/gl.h>
@@ -244,12 +251,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
 
 /**
@@ -261,7 +268,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; \
@@ -270,7 +277,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;
@@ -287,7 +294,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; \
@@ -296,7 +303,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;

+ 6 - 4
gameplay/src/BoundingBox.cpp

@@ -64,6 +64,8 @@ Vector3 BoundingBox::getCenter() const
 
 void BoundingBox::getCenter(Vector3* dst) const
 {
+    GP_ASSERT(dst);
+
     dst->set(min, max);
     dst->scale(0.5f);
     dst->add(min);
@@ -247,6 +249,10 @@ void BoundingBox::set(const Vector3& min, const Vector3& max)
 
 void updateMinMax(Vector3* point, Vector3* min, Vector3* max)
 {
+    GP_ASSERT(point);
+    GP_ASSERT(min);
+    GP_ASSERT(max);
+
     // Leftmost point.
     if (point->x < min->x)
     {
@@ -292,10 +298,6 @@ void BoundingBox::set(const BoundingBox& box)
 
 void BoundingBox::set(const BoundingSphere& sphere)
 {
-    std::vector<int> v;
-    v.push_back(0);
-    std::vector<int> v2 = v;
-
     const Vector3& center = sphere.center;
     float radius = sphere.radius;
 

+ 8 - 0
gameplay/src/BoundingSphere.cpp

@@ -158,6 +158,9 @@ bool BoundingSphere::isEmpty() const
 
 void BoundingSphere::merge(const BoundingSphere& sphere)
 {
+    if (sphere.isEmpty())
+        return;
+
     // Calculate the distance between the two centers.
     float vx = center.x - sphere.center.x;
     float vy = center.y - sphere.center.y;
@@ -177,6 +180,7 @@ void BoundingSphere::merge(const BoundingSphere& sphere)
     }
 
     // Calculate the unit vector between the two centers.
+    GP_ASSERT(d != 0.0f);
     float dI = 1.0f / d;
     vx *= dI;
     vy *= dI;
@@ -200,6 +204,9 @@ void BoundingSphere::merge(const BoundingSphere& sphere)
 
 void BoundingSphere::merge(const BoundingBox& box)
 {
+    if (box.isEmpty())
+        return;
+
     const Vector3& min = box.min;
     const Vector3& max = box.max;
 
@@ -240,6 +247,7 @@ void BoundingSphere::merge(const BoundingBox& box)
     }
 
     // Calculate the unit vector between the center and the farthest point.
+    GP_ASSERT(distance != 0.0f);
     float dI = 1.0f / distance;
     v1x *= dI;
     v1y *= dI;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 225 - 107
gameplay/src/Bundle.cpp


+ 10 - 17
gameplay/src/Camera.cpp

@@ -245,23 +245,21 @@ const Frustum& Camera::getFrustum() const
 
 void Camera::project(const Rectangle& viewport, const Vector3& position, float* x, float* y, float* depth)
 {
-    // Determine viewport coords to use.
-    float vpx = viewport.x;
-    float vpy = viewport.y;
-    float vpw = viewport.width;
-    float vph = viewport.height;
+    GP_ASSERT(x);
+    GP_ASSERT(y);
 
     // Transform the point to clip-space.
     Vector4 clipPos;
     getViewProjectionMatrix().transformVector(Vector4(position.x, position.y, position.z, 1.0f), &clipPos);
 
     // Compute normalized device coordinates.
+    GP_ASSERT(clipPos.w != 0.0f);
     float ndcX = clipPos.x / clipPos.w;
     float ndcY = clipPos.y / clipPos.w;
 
     // Compute screen coordinates by applying our viewport transformation.
-    *x = vpx + (ndcX + 1.0f) * 0.5f * vpw;
-    *y = vpy + (1.0f - (ndcY + 1.0f) * 0.5f) * vph;
+    *x = viewport.x + (ndcX + 1.0f) * 0.5f * viewport.width;
+    *y = viewport.y + (1.0f - (ndcY + 1.0f) * 0.5f) * viewport.height;
     if (depth)
     {
         float ndcZ = clipPos.z / clipPos.w;
@@ -271,18 +269,11 @@ void Camera::project(const Rectangle& viewport, const Vector3& position, float*
 
 void Camera::unproject(const Rectangle& viewport, float x, float y, float depth, Vector3* dst)
 {
-    // Determine viewport coords to use.
-    float vpx = viewport.x;
-    float vpy = viewport.y;
-    float vpw = viewport.width;
-    float vph = viewport.height;
+    GP_ASSERT(dst);
     
     // Create our screen space position in NDC.
-    Vector4 screen(
-        ((float)x - (float)vpx) / (float)vpw,
-        ((float)(vph - y) - (float)vpy) / (float)vph,
-        depth,
-        1.0f );
+    GP_ASSERT(viewport.width != 0.0f && viewport.height != 0.0f);
+    Vector4 screen((x - viewport.x) / viewport.width, ((viewport.height - y) - viewport.y) / viewport.height, depth, 1.0f);
 
     // Map to range -1 to 1.
     screen.x = screen.x * 2.0f - 1.0f;
@@ -305,6 +296,8 @@ void Camera::unproject(const Rectangle& viewport, float x, float y, float depth,
 
 void Camera::pickRay(const Rectangle& viewport, float x, float y, Ray* dst)
 {
+    GP_ASSERT(dst);
+
     // Get the world-space position at the near clip plane.
     Vector3 nearPoint;
     unproject(viewport, x, y, 0.0f, &nearPoint);

+ 2 - 2
gameplay/src/Container.cpp

@@ -121,7 +121,7 @@ void Container::addControls(Theme* theme, Properties* properties)
         }
         else
         {
-            GP_ERROR("Failed to create control; unrecognized control name \'%s\'.", controlName.c_str());
+            GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
         }
 
         // Add the new control to the form.
@@ -264,7 +264,7 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
     if (_skin && needsClear)
     {
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
-        GL_ASSERT( glClearColor(0, 0, 0, 1) );
+        GL_ASSERT( glClearColor(0, 0, 0, 0) );
         float clearY = targetHeight - _clearBounds.y - _clearBounds.height;
         GL_ASSERT( glScissor(_clearBounds.x, clearY,
             _clearBounds.width, _clearBounds.height) );

+ 3 - 3
gameplay/src/Control.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-        _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
+    _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false), _skin(NULL), _clearBounds(Rectangle::empty())
 {
 }
 
@@ -881,7 +881,7 @@ void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsCl
     if (needsClear)
     {
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
-        GL_ASSERT( glClearColor(0, 0, 0, 1) );
+        GL_ASSERT( glClearColor(0, 0, 0, 0) );
         GL_ASSERT( glScissor(_clearBounds.x, targetHeight - _clearBounds.y - _clearBounds.height,
             _clearBounds.width, _clearBounds.height) );
         GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
@@ -1273,7 +1273,7 @@ Control::Alignment Control::getAlignment(const char* alignment)
     }
     else
     {
-        GP_ERROR("Failed to get corresponding control alignment for unsupported value \'%s\'.", alignment);
+        GP_ERROR("Failed to get corresponding control alignment for unsupported value '%s'.", alignment);
     }
 
     // Default.

+ 10 - 10
gameplay/src/DebugNew.cpp

@@ -122,7 +122,7 @@ void* debugAlloc(std::size_t size, const char* file, int line)
         if (!initialized)
         {
             if (!SymInitialize(GetCurrentProcess(), NULL, true))
-                gameplay::printError("Stack trace tracking will not work.");
+                gameplay::printError("Stack trace tracking will not work.\n");
             initialized = true;
         }
     
@@ -181,7 +181,7 @@ void debugFree(void* p)
     // Sanity check: ensure that address in record matches passed in address
     if (rec->address != (unsigned long)p)
     {
-        gameplay::printError("[memory] CORRUPTION: Attempting to free memory address with invalid memory allocation record.");
+        gameplay::printError("[memory] CORRUPTION: Attempting to free memory address with invalid memory allocation record.\n");
         return;
     }
 
@@ -221,7 +221,7 @@ void printStackTrace(MemoryAllocationRecord* rec)
         symbol->MaxNameLength = bufferSize;
         if (!SymGetSymFromAddr64(GetCurrentProcess(), pc, &displacement, symbol))
         {
-            gameplay::printError("[memory] STACK TRACE: <unknown location>");
+            gameplay::printError("[memory] STACK TRACE: <unknown location>\n");
         }
         else
         {
@@ -243,7 +243,7 @@ void printStackTrace(MemoryAllocationRecord* rec)
                     line.SizeOfStruct = sizeof(line);
                     if (!SymGetLineFromAddr64(GetCurrentProcess(), pc, &displacement, &line))
                     {
-                        gameplay::printError("[memory] STACK TRACE: %s - <unknown file>:<unknown line number>", symbol->Name);
+                        gameplay::printError("[memory] STACK TRACE: %s - <unknown file>:<unknown line number>\n", symbol->Name);
                     }
                     else
                     {
@@ -253,7 +253,7 @@ void printStackTrace(MemoryAllocationRecord* rec)
                         else
                             file++;
                         
-                        gameplay::printError("[memory] STACK TRACE: %s - %s:%d", symbol->Name, file, line.LineNumber);
+                        gameplay::printError("[memory] STACK TRACE: %s - %s:%d\n", symbol->Name, file, line.LineNumber);
                     }
                 }
             }
@@ -267,24 +267,24 @@ extern void printMemoryLeaks()
     // Dump general heap memory leaks
     if (__memoryAllocationCount == 0)
     {
-        gameplay::printError("[memory] All HEAP allocations successfully cleaned up (no leaks detected).");
+        gameplay::printError("[memory] All HEAP allocations successfully cleaned up (no leaks detected).\n");
     }
     else
     {
-        gameplay::printError("[memory] WARNING: %d HEAP allocations still active in memory.", __memoryAllocationCount);
+        gameplay::printError("[memory] WARNING: %d HEAP allocations still active in memory.\n", __memoryAllocationCount);
         MemoryAllocationRecord* rec = __memoryAllocations;
         while (rec)
         {
 #ifdef WIN32
             if (rec->trackStackTrace)
             {
-                gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d:", rec->address, rec->size);
+                gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d:\n", rec->address, rec->size);
                 printStackTrace(rec);
             }
             else
-                gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.", rec->address, rec->size, rec->line, rec->file);
+                gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.\n", rec->address, rec->size, rec->line, rec->file);
 #else
-            gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.", rec->address, rec->size, rec->line, rec->file);
+            gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.\n", rec->address, rec->size, rec->line, rec->file);
 #endif
             rec = rec->next;
         }

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

+ 148 - 27
gameplay/src/FileSystem.cpp

@@ -1,13 +1,20 @@
 #include "Base.h"
 #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__
@@ -18,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)
@@ -50,7 +58,7 @@ void makepath(std::string path, int mode)
             // Directory does not exist.
             if (mkdir(dirPath.c_str(), 0777) != 0)
             {
-                GP_WARN("Failed to create directory: '%s'", dirPath.c_str());
+                GP_ERROR("Failed to create directory: '%s'", dirPath.c_str());
                 return;
             }
         }
@@ -61,6 +69,7 @@ void makepath(std::string path, int mode)
 #endif
 
 static std::string __resourcePath("./");
+static std::map<std::string, std::string> __aliases;
 
 FileSystem::FileSystem()
 {
@@ -80,6 +89,48 @@ const char* FileSystem::getResourcePath()
     return __resourcePath.c_str();
 }
 
+void FileSystem::loadResourceAliases(const char* aliasFilePath)
+{
+    Properties* properties = Properties::create(aliasFilePath);
+    if (properties)
+    {
+        Properties* aliases;
+        while ((aliases = properties->getNextNamespace()) != NULL)
+        {
+            loadResourceAliases(aliases);
+        }
+    }
+    SAFE_DELETE(properties);
+}
+
+void FileSystem::loadResourceAliases(Properties* properties)
+{
+    assert(properties);
+
+    const char* name;
+    while ((name = properties->getNextProperty()) != NULL)
+    {
+        __aliases[name] = properties->getString();
+    }
+}
+
+const char* FileSystem::resolvePath(const char* path)
+{
+    GP_ASSERT(path);
+
+    size_t len = strlen(path);
+    if (len > 1 && path[0] == '@')
+    {
+        std::string alias(path + 1);
+        std::map<std::string, std::string>::const_iterator itr = __aliases.find(alias);
+        if (itr == __aliases.end())
+            return path; // no matching alias found
+        return itr->second.c_str();
+    }
+
+    return path;
+}
+
 bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
 {
     // TODO make this method work with absolute and relative paths.
@@ -149,32 +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 += 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);
+    fullPath += resolvePath(path);
+
+    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");
+        fullPath = __resourcePath;
+        fullPath += "../../gameplay/";
+        fullPath += path;
         
-            int ret = fwrite(data, sizeof(unsigned char), length, file);
-            GP_ASSERT(ret == length);
-            fclose(file);
-        }
+        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);
     
@@ -204,17 +263,24 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     }
 
     // Obtain file length.
-    fseek(file, 0, SEEK_END);
+    if (fseek(file, 0, SEEK_END) != 0)
+    {
+        GP_ERROR("Failed to seek to the end of the file '%s' to obtain the file length.", filePath);
+        return NULL;
+    }
     int size = (int)ftell(file);
-     fseek(file, 0, SEEK_SET);
+    if (fseek(file, 0, SEEK_SET) != 0)
+    {
+        GP_ERROR("Failed to seek to beginning of the file '%s' to begin reading in the entire file.", filePath);
+        return NULL;
+    }
 
     // Read entire file contents.
     char* buffer = new char[size + 1];
     int read = (int)fread(buffer, 1, size, file);
-    GP_ASSERT(read == size);
     if (read != size)
     {
-        GP_ERROR("Read error for file: %s (%d < %d)", filePath, (int)read, (int)size);
+        GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %d < %d).", filePath, (int)read, (int)size);
         SAFE_DELETE_ARRAY(buffer);
         return NULL;
     }
@@ -223,7 +289,11 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     buffer[size] = '\0';
 
     // Close file and return.
-    fclose(file);
+    if (fclose(file) != 0)
+    {
+        GP_ERROR("Failed to close file '%s'.", filePath);
+    }
+
     if (fileSize)
     {
         *fileSize = size; 
@@ -231,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
+}
+
 }

+ 57 - 0
gameplay/src/FileSystem.h

@@ -4,6 +4,8 @@
 namespace gameplay
 {
 
+class Properties;
+
 /**
  * Defines a set of functions for interacting with the device filesystem.
  */
@@ -33,6 +35,53 @@ public:
      */
     static const char* getResourcePath();
 
+    /**
+     * Loads a properties file containing a list of filesystem aliases.
+     *
+     * The specified aliases file is a valid properties file that contains one
+     * or more namespaces with a list of fielsystem aliases that will be used
+     * to establish soft links to files when reading files through this class.
+     *
+     * This can be helpful for managing loading of resources that may change
+     * from one platform to another (such as texture formats). An aliases
+     * file per-platform can be maintained and asset loading code can refer
+     * to the alias name instead of the actual hard file name.
+     *
+     * @param aliasFilePath Path to a properties file containing filesystem aliases.
+     * @see Properties
+     */
+    static void loadResourceAliases(const char* aliasFilePath);
+
+    /**
+     * Loads a set of filesystem aliases from the given Properties object.
+     *
+     * The specified properties object contains a single namespace with a list
+     * of fielsystem aliases that will be used to establish soft links to files
+     * when reading files through this class.
+     *
+     * This can be helpful for managing loading of resources that may change
+     * from one platform to another (such as texture formats). An aliases
+     * file per-platform can be maintained and asset loading code can refer
+     * to the alias name instead of the actual hard file name.
+     *
+     * @param properties Properties object containing filesystem aliases.
+     * @see Properties
+     */
+    static void loadResourceAliases(Properties* properties);
+
+    /**
+     * Resolves a filesystem path.
+     *
+     * If the specified path is a filesystem alias, the alias will be
+     * resolved and the physical file will be returned.
+     *
+     * Note that this method does not convert a relative path to an
+     * absolute filesystem path.
+     *
+     * @param path Path to resolve.
+     */
+    static const char* resolvePath(const char* path);
+
     /**
      * Lists the files in the specified directory and adds the files to the vector. Excludes directories.
      * 
@@ -43,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.
      *

+ 72 - 8
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();
@@ -302,9 +319,9 @@ Font::Text* Font::createText(const char* text, const Rectangle& area, const Vect
                         {
                             // Simply copy values directly into the start of the index array
                             batch->_indices[batch->_vertexCount] = batch->_vertexCount;
-                            batch->_indices[batch->_vertexCount] = batch->_vertexCount + 1;
-                            batch->_indices[batch->_vertexCount] = batch->_vertexCount + 2;
-                            batch->_indices[batch->_vertexCount] = batch->_vertexCount + 3;
+                            batch->_indices[batch->_vertexCount + 1] = batch->_vertexCount + 1;
+                            batch->_indices[batch->_vertexCount + 2] = batch->_vertexCount + 2;
+                            batch->_indices[batch->_vertexCount + 3] = batch->_vertexCount + 3;
                             batch->_vertexCount += 4;
                             batch->_indexCount += 4;
                         }
@@ -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);
     }
 }
@@ -1702,14 +1766,14 @@ Font::Justify Font::getJustify(const char* justify)
     }
     else
     {
-        GP_ERROR("Failed to get corresponding font justification for unsupported value \'%s\'.", justify);
+        GP_ERROR("Failed to get corresponding font justification for unsupported value '%s'.", justify);
     }
 
     // Default.
     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];

+ 123 - 3
gameplay/src/Form.cpp

@@ -80,7 +80,7 @@ Form* Form::create(const char* url)
         layout = ScrollLayout::create();
         break;
     default:
-        GP_ERROR("Unsupported layout type \'%d\'.", getLayoutType(layoutString));
+        GP_ERROR("Unsupported layout type '%d'.", getLayoutType(layoutString));
     }
 
     Theme* theme = Theme::create(themeFile);
@@ -93,6 +93,25 @@ Form* Form::create(const char* url)
     const char* styleName = formProperties->getString("style");
     form->initialize(theme->getStyle(styleName), formProperties);
 
+    // Alignment
+    if ((form->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
+    {
+        form->_bounds.y = Game::getInstance()->getHeight() - form->_bounds.height;
+    }
+    else if ((form->_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
+    {
+        form->_bounds.y = Game::getInstance()->getHeight() * 0.5f - form->_bounds.height * 0.5f;
+    }
+
+    if ((form->_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
+    {
+        form->_bounds.x = Game::getInstance()->getWidth() - form->_bounds.width;
+    }
+    else if ((form->_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
+    {
+        form->_bounds.x = Game::getInstance()->getWidth() * 0.5f - form->_bounds.width * 0.5f;
+    }
+
     // Add all the controls to the form.
     form->addControls(theme, formProperties);
 
@@ -266,7 +285,108 @@ void Form::update()
 {
     if (isDirty())
     {
-        Container::update(Rectangle(0, 0, _bounds.width, _bounds.height), Vector2::zero());
+        _clearBounds.set(_absoluteClipBounds);
+
+        // Calculate the clipped bounds.
+        float x = 0;
+        float y = 0;
+        float width = _bounds.width;
+        float height = _bounds.height;
+
+        Rectangle clip(0, 0, _bounds.width, _bounds.height);
+
+        float clipX2 = clip.x + clip.width;
+        float x2 = clip.x + x + width;
+        if (x2 > clipX2)
+            width -= x2 - clipX2;
+
+        float clipY2 = clip.y + clip.height;
+        float y2 = clip.y + y + height;
+        if (y2 > clipY2)
+            height -= y2 - clipY2;
+
+        if (x < 0)
+        {
+            width += x;
+            x = -x;
+        }
+        else
+        {
+            x = 0;
+        }
+
+        if (y < 0)
+        {
+            height += y;
+            y = -y;
+        }
+        else
+        {
+            y = 0;
+        }
+
+        _clipBounds.set(x, y, width, height);
+
+        // Calculate the absolute bounds.
+        x = 0;
+        y = 0;
+        _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
+
+        // Calculate the absolute viewport bounds.
+        // Absolute bounds minus border and padding.
+        const Theme::Border& border = getBorder(_state);
+        const Theme::Padding& padding = getPadding();
+
+        x += border.left + padding.left;
+        y += border.top + padding.top;
+        width = _bounds.width - border.left - padding.left - border.right - padding.right;
+        height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
+
+        _viewportBounds.set(x, y, width, height);
+
+        // Calculate the clip area.
+        // Absolute bounds, minus border and padding,
+        // clipped to the parent container's clip area.
+        clipX2 = clip.x + clip.width;
+        x2 = x + width;
+        if (x2 > clipX2)
+            width = clipX2 - x;
+
+        clipY2 = clip.y + clip.height;
+        y2 = y + height;
+        if (y2 > clipY2)
+            height = clipY2 - y;
+
+        if (x < clip.x)
+        {
+            float dx = clip.x - x;
+            width -= dx;
+            x = clip.x;
+        }
+
+        if (y < clip.y)
+        {
+            float dy = clip.y - y;
+            height -= dy;
+            y = clip.y;
+        }
+ 
+        _viewportClipBounds.set(x, y, width, height);
+
+        _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
+            width + border.left + padding.left + border.right + padding.right,
+            height + border.top + padding.top + border.bottom + padding.bottom);
+        if (_clearBounds.isEmpty())
+        {
+            _clearBounds.set(_absoluteClipBounds);
+        }
+
+        // Cache themed attributes for performance.
+        _skin = getSkin(_state);
+        _opacity = getOpacity(_state);
+
+        GP_ASSERT(_layout);
+        _layout->update(this);
     }
 }
 
@@ -293,7 +413,7 @@ void Form::draw()
 
         Game* game = Game::getInstance();
         Rectangle prevViewport = game->getViewport();
-        game->setViewport(Rectangle(_bounds.x, _bounds.y, _bounds.width, _bounds.height));
+        game->setViewport(Rectangle(0, 0, _bounds.width, _bounds.height));
 
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);

+ 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

+ 1 - 0
gameplay/src/Frustum.cpp

@@ -57,6 +57,7 @@ const Plane& Frustum::getTop() const
 
 void Frustum::getMatrix(Matrix* dst) const
 {
+    GP_ASSERT(dst);
     dst->set(_matrix);
 }
 

+ 59 - 15
gameplay/src/Game.cpp

@@ -2,6 +2,7 @@
 #include "Game.h"
 #include "Platform.h"
 #include "RenderState.h"
+#include "FileSystem.h"
 
 // Extern global variables
 GLenum __gl_error_code = GL_NO_ERROR;
@@ -17,7 +18,7 @@ long Game::_pausedTimeTotal = 0L;
 Game::Game() 
     : _initialized(false), _state(UNINITIALIZED), 
       _frameLastFPS(0), _frameCount(0), _frameRate(0), 
-      _clearDepth(1.0f), _clearStencil(0),
+      _clearDepth(1.0f), _clearStencil(0), _properties(NULL),
       _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL)
 {
     GP_ASSERT(__gameInstance == NULL);
@@ -33,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();
@@ -66,20 +67,15 @@ bool Game::isVsync()
     return Platform::isVsync();
 }
 
-int Game::run(int width, int height)
+int Game::run()
 {
     if (_state != UNINITIALIZED)
         return -1;
 
-    if (width == -1)
-        _width = Platform::getDisplayWidth();
-    else
-        _width = width;
-    
-    if (height == -1)
-        _height = Platform::getDisplayHeight();
-    else
-        _height = height;
+    loadConfig();
+
+    _width = Platform::getDisplayWidth();
+    _height = Platform::getDisplayHeight();
 
     // Start up game systems.
     if (!startup())
@@ -118,6 +114,10 @@ void Game::shutdown()
     // Call user finalization.
     if (_state != UNINITIALIZED)
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         Platform::signalShutdown();
         finalize();
 
@@ -133,7 +133,9 @@ void Game::shutdown()
         SAFE_DELETE(_audioListener);
 
         RenderState::finalize();
-        
+
+        SAFE_DELETE(_properties);
+
         _state = UNINITIALIZED;
     }
 }
@@ -142,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();
@@ -154,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();
@@ -177,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();
@@ -290,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);
 }
@@ -302,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();
@@ -314,6 +332,31 @@ void Game::updateOnce()
     _audioController->update(elapsedTime);
 }
 
+Properties* Game::getConfig() const
+{
+    if (_properties == NULL)
+        const_cast<Game*>(this)->loadConfig();
+
+    return _properties;
+}
+
+void Game::loadConfig()
+{
+    if (_properties == NULL)
+    {
+        // Try to load custom config from file.
+        if (FileSystem::fileExists("game.config"))
+        {
+            _properties = Properties::create("game.config");
+            
+            // Load filesystem aliases.
+            Properties* aliases = _properties->getNamespace("aliases", true);
+            if (aliases)
+                FileSystem::loadResourceAliases(aliases);
+        }
+    }
+}
+
 void Game::fireTimeEvents(long frameTime)
 {
     while (_timeEvents->size() > 0)
@@ -323,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();
     }
 }

+ 17 - 4
gameplay/src/Game.h

@@ -99,14 +99,21 @@ public:
     inline State getState() const;
 
     /**
-     * Call this method to initialize the game, and begin running the game.
+     * Returns the game configuration object.
      *
-     * @param width The width of the game window to run at. Default is -1 meaning native resolution width.
-     * @param height The height of the game window to run at. Default is -1 meaning native resolution height.
+     * This method returns a Properties object containing the contents
+     * of the game.config file.
+     *
+     * @return The game conifguration Properties object.
+     */
+    Properties* getConfig() const;
+
+    /**
+     * Called to initialize the game, and begin running the game.
      * 
      * @return Zero for normal termination, or non-zero if an error occurred.
      */
-    int run(int width = -1, int height = -1);
+    int run();
 
     /**
      * Pauses the game after being run.
@@ -393,6 +400,11 @@ private:
      */
     void fireTimeEvents(long frameTime);
 
+    /**
+     * Loads the game configuration.
+     */
+    void loadConfig();
+
     bool _initialized;                          // If game has initialized yet.
     State _state;                               // The game state.
     static long _pausedTimeLast;                // The last time paused.
@@ -406,6 +418,7 @@ private:
     Vector4 _clearColor;                        // The clear color value last used for clearing the color buffer.
     float _clearDepth;                          // The clear depth value last used for clearing the depth buffer.
     int _clearStencil;                          // The clear stencil value last used for clearing the stencil buffer.
+    Properties* _properties;                    // Game configuration properties object.
     AnimationController* _animationController;  // Controls the scheduling and running of animations.
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     PhysicsController* _physicsController;      // Controls the simulation of a physics scene and entities.

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

+ 2 - 0
gameplay/src/Joint.cpp

@@ -22,6 +22,7 @@ Joint* Joint::create(const char* id)
 Node* Joint::cloneSingleNode(NodeCloneContext &context) const
 {
     Joint* copy = Joint::create(getId());
+    GP_ASSERT(copy);
     context.registerClonedNode(this, copy);
     copy->_bindPose = _bindPose;
     copy->_skinCount = _skinCount;
@@ -54,6 +55,7 @@ void Joint::updateJointMatrix(const Matrix& bindShape, Vector4* matrixPalette)
         Matrix::multiply(Node::getWorldMatrix(), getInverseBindPose(), &t);
         Matrix::multiply(t, bindShape, &t);
 
+        GP_ASSERT(matrixPalette);
         matrixPalette[0].set(t.m[0], t.m[4], t.m[8], t.m[12]);
         matrixPalette[1].set(t.m[1], t.m[5], t.m[9], t.m[13]);
         matrixPalette[2].set(t.m[2], t.m[6], t.m[10], t.m[14]);

+ 1 - 0
gameplay/src/Layout.h

@@ -19,6 +19,7 @@ class Control;
 class Layout : public Ref
 {
     friend class Container;
+    friend class Form;
 
 public:
     /**

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

+ 44 - 27
gameplay/src/Matrix.cpp

@@ -112,12 +112,22 @@ void Matrix::createPerspective(float fieldOfView, float aspectRatio,
                                      float zNearPlane, float zFarPlane, Matrix* dst)
 {
     GP_ASSERT(dst);
+    GP_ASSERT(zFarPlane != zNearPlane);
 
     float f_n = 1.0f / (zFarPlane - zNearPlane);
-    float factor = 1.0f / tanf(MATH_DEG_TO_RAD(fieldOfView) * 0.5f);
+    float theta = MATH_DEG_TO_RAD(fieldOfView) * 0.5f;
+    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;
+    }
+    float divisor = tan(theta);
+    GP_ASSERT(divisor);
+    float factor = 1.0f / divisor;
 
     memset(dst, 0, MATRIX_SIZE);
 
+    GP_ASSERT(aspectRatio);
     dst->m[0] = (1.0f / aspectRatio) * factor;
     dst->m[5] = factor;
     dst->m[10] = (-(zFarPlane + zNearPlane)) * f_n;
@@ -136,6 +146,9 @@ void Matrix::createOrthographicOffCenter(float left, float right, float bottom,
                                          float zNearPlane, float zFarPlane, Matrix* dst)
 {
     GP_ASSERT(dst);
+    GP_ASSERT(right != left);
+    GP_ASSERT(top != bottom);
+    GP_ASSERT(zFarPlane != zNearPlane);
 
     float r_l = 1.0f / (right - left);
     float t_b = 1.0f / (top - bottom);
@@ -464,35 +477,37 @@ bool Matrix::decompose(Vector3* scale, Quaternion* rotation, Vector3* translatio
     {
         float s = 0.5f / sqrt(trace);
         rotation->w = 0.25f / s;
-        rotation->x = ( yaxis.z - zaxis.y ) * s;
-        rotation->y = ( zaxis.x - xaxis.z ) * s;
-        rotation->z = ( xaxis.y - yaxis.x ) * s;
+        rotation->x = (yaxis.z - zaxis.y) * s;
+        rotation->y = (zaxis.x - xaxis.z) * s;
+        rotation->z = (xaxis.y - yaxis.x) * s;
     }
     else
     {
+        // Note: since xaxis, yaxis, and zaxis are normalized, 
+        // we will never divide by zero in the code below.
         if (xaxis.x > yaxis.y && xaxis.x > zaxis.z)
         {
-            float s = 2.0f * sqrt(1.0f + xaxis.x - yaxis.y - zaxis.z);
-            rotation->w = (yaxis.z - zaxis.y ) / s;
-            rotation->x = 0.25f * s;
-            rotation->y = (yaxis.x + xaxis.y ) / s;
-            rotation->z = (zaxis.x + xaxis.z ) / s;
+            float s = 0.5f / sqrt(1.0f + xaxis.x - yaxis.y - zaxis.z);
+            rotation->w = (yaxis.z - zaxis.y) * s;
+            rotation->x = 0.25f / s;
+            rotation->y = (yaxis.x + xaxis.y) * s;
+            rotation->z = (zaxis.x + xaxis.z) * s;
         }
         else if (yaxis.y > zaxis.z)
         {
-            float s = 2.0f * sqrt(1.0f + yaxis.y - xaxis.x - zaxis.z);
-            rotation->w = (zaxis.x - xaxis.z ) / s;
-            rotation->x = (yaxis.x + xaxis.y ) / s;
-            rotation->y = 0.25f * s;
-            rotation->z = (zaxis.y + yaxis.z ) / s;
+            float s = 0.5f / sqrt(1.0f + yaxis.y - xaxis.x - zaxis.z);
+            rotation->w = (zaxis.x - xaxis.z) * s;
+            rotation->x = (yaxis.x + xaxis.y) * s;
+            rotation->y = 0.25f / s;
+            rotation->z = (zaxis.y + yaxis.z) * s;
         }
         else
         {
-            float s = 2.0f * sqrt(1.0f + zaxis.z - xaxis.x - yaxis.y );
-            rotation->w = (xaxis.y - yaxis.x ) / s;
-            rotation->x = (zaxis.x + xaxis.z ) / s;
-            rotation->y = (zaxis.y + yaxis.z ) / s;
-            rotation->z = 0.25f * s;
+            float s = 0.5f / sqrt(1.0f + zaxis.z - xaxis.x - yaxis.y );
+            rotation->w = (xaxis.y - yaxis.x ) * s;
+            rotation->x = (zaxis.x + xaxis.z ) * s;
+            rotation->y = (zaxis.y + yaxis.z ) * s;
+            rotation->z = 0.25f / s;
         }
     }
 
@@ -545,6 +560,7 @@ void Matrix::getUpVector(Vector3* dst) const
 void Matrix::getDownVector(Vector3* dst) const
 {
     GP_ASSERT(dst);
+    
     dst->x = -m[4];
     dst->y = -m[5];
     dst->z = -m[6];
@@ -719,6 +735,8 @@ void Matrix::negate()
 
 void Matrix::negate(Matrix* dst) const
 {
+    GP_ASSERT(dst);
+
     dst->m[0]  = -m[0];
     dst->m[1]  = -m[1];
     dst->m[2]  = -m[2];
@@ -792,8 +810,6 @@ void Matrix::rotateZ(float angle)
 
 void Matrix::rotateZ(float angle, Matrix* dst) const
 {
-    GP_ASSERT(dst);
-
     Matrix r;
     createRotationZ(angle, &r);
     multiply(*this, r, dst);
@@ -816,8 +832,6 @@ void Matrix::scale(float xScale, float yScale, float zScale)
 
 void Matrix::scale(float xScale, float yScale, float zScale, Matrix* dst) const
 {
-    GP_ASSERT(dst);
-
     Matrix s;
     createScale(xScale, yScale, zScale, &s);
     multiply(*this, s, dst);
@@ -881,6 +895,8 @@ void Matrix::subtract(const Matrix& m)
 
 void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
+    GP_ASSERT(dst);
+
     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];
@@ -901,6 +917,7 @@ void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
 
 void Matrix::transformPoint(Vector3* point) const
 {
+    GP_ASSERT(point);
     transformVector(point->x, point->y, point->z, 1.0f, point);
 }
 
@@ -911,6 +928,7 @@ void Matrix::transformPoint(const Vector3& point, Vector3* dst) const
 
 void Matrix::transformVector(Vector3* vector) const
 {
+    GP_ASSERT(vector);
     transformVector(vector->x, vector->y, vector->z, 0.0f, vector);
 }
 
@@ -926,11 +944,12 @@ void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) c
     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] );
+        x * m[2] + y * m[6] + z * m[10] + w * m[14]);
 }
 
 void Matrix::transformVector(Vector4* vector) const
 {
+    GP_ASSERT(vector);
     transformVector(*vector, vector);
 }
 
@@ -942,7 +961,7 @@ void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
         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[3] + vector.y * m[7] + vector.z * m[11] + vector.w * m[15] );
+        vector.x * m[3] + vector.y * m[7] + vector.z * m[11] + vector.w * m[15]);
 }
 
 void Matrix::translate(float x, float y, float z)
@@ -952,8 +971,6 @@ void Matrix::translate(float x, float y, float z)
 
 void Matrix::translate(float x, float y, float z, Matrix* dst) const
 {
-    GP_ASSERT(dst);
-
     Matrix t;
     createTranslation(x, y, z, &t);
     multiply(*this, t, dst);

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

+ 24 - 20
gameplay/src/Node.cpp

@@ -402,11 +402,9 @@ const Matrix& Node::getWorldMatrix() const
 
         // Our world matrix was just updated, so call getWorldMatrix() on all child nodes
         // to force their resolved world matrices to be updated.
-        Node* node = getFirstChild();
-        while (node)
+        for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
         {
-            node->getWorldMatrix();
-            node = node->getNextSibling();
+            child->getWorldMatrix();
         }
     }
 
@@ -546,8 +544,6 @@ Vector3 Node::getForwardVectorView() const
     Vector3 vector;
     getWorldMatrix().getForwardVector(&vector);
     getViewMatrix().transformVector(&vector);
-    //getForwardVector(&vector);
-    //getWorldViewMatrix().transformVector(&vector);
     return vector;
 }
 
@@ -601,8 +597,7 @@ void Node::transformChanged()
     _dirtyBits |= NODE_DIRTY_WORLD | NODE_DIRTY_BOUNDS;
 
     // Notify our children that their transform has also changed (since transforms are inherited).
-    Node* n = getFirstChild();
-    while (n)
+    for (Node* n = getFirstChild(); n != NULL; n = n->getNextSibling())
     {
         if (Transform::isTransformChangedSuspended())
         {
@@ -617,7 +612,6 @@ void Node::transformChanged()
         {
             n->transformChanged();
         }
-        n = n->getNextSibling();
     }
 
     Transform::transformChanged();
@@ -647,10 +641,10 @@ Animation* Node::getAnimation(const char* id) const
         MeshSkin* skin = model->getSkin();
         if (skin)
         {
-            Joint* rootJoint = skin->getRootJoint();
-            if (rootJoint)
+            Node* rootNode = skin->_rootNode;
+            if (rootNode)
             {
-                animation = rootJoint->getAnimation(id);
+                animation = rootNode->getAnimation(id);
                 if (animation)
                     return animation;
             }
@@ -665,6 +659,7 @@ Animation* Node::getAnimation(const char* id) const
             std::vector<MaterialParameter*>::iterator itr = material->_parameters.begin();
             for (; itr != material->_parameters.end(); itr++)
             {
+                GP_ASSERT(*itr);
                 animation = ((MaterialParameter*)(*itr))->getAnimation(id);
                 if (animation)
                     return animation;
@@ -682,15 +677,11 @@ Animation* Node::getAnimation(const char* id) const
     }
 
     // Look through this node's children for an animation with the specified ID.
-    unsigned int childCount = this->getChildCount();
-    Node* child = this->getFirstChild();
-    for (unsigned int i = 0; i < childCount; i++)
+    for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     {
         animation = child->getAnimation(id);
         if (animation)
             return animation;
-
-        child = child->getNextSibling();
     }
     
     return NULL;
@@ -833,6 +824,7 @@ const BoundingSphere& Node::getBoundingSphere() const
                 // since joint parent nodes that are not in the matrix pallette do not need to
                 // be considered as directly transforming vertices on the GPU (they can instead
                 // be applied directly to the bounding volume transformation below).
+                GP_ASSERT(_model->getSkin()->getRootJoint());
                 Node* jointParent = _model->getSkin()->getRootJoint()->getParent();
                 if (jointParent)
                 {
@@ -890,6 +882,7 @@ Node* Node::cloneSingleNode(NodeCloneContext &context) const
 Node* Node::cloneRecursive(NodeCloneContext &context) const
 {
     Node* copy = cloneSingleNode(context);
+    GP_ASSERT(copy);
 
     Node* lastChild = NULL;
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
@@ -900,6 +893,7 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
     for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     {
         Node* childCopy = child->cloneRecursive(context);
+        GP_ASSERT(childCopy);
         copy->addChild(childCopy);
         childCopy->release();
     }
@@ -908,6 +902,7 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
 
 void Node::cloneInto(Node* node, NodeCloneContext &context) const
 {
+    GP_ASSERT(node);
     Transform::cloneInto(node, context);
 
     // TODO: Clone the rest of the node data.
@@ -1027,10 +1022,9 @@ PhysicsCollisionObject* Node::setCollisionObject(const char* url)
 {
     // Load the collision object properties from file.
     Properties* properties = Properties::create(url);
-    GP_ASSERT(properties);
     if (properties == NULL)
     {
-        GP_WARN("Failed to load collision object file: %s", url);
+        GP_ERROR("Failed to load collision object file: %s", url);
         return NULL;
     }
 
@@ -1050,7 +1044,7 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "rigidBody") == 0))
     {
-        GP_WARN("Failed to load collision object from properties object: must be non-null object and have namespace equal to \'character\', \'ghostObject\', or \'rigidBody\'.");
+        GP_ERROR("Failed to load collision object from properties object: must be non-null object and have namespace equal to 'character', 'ghostObject', or 'rigidBody'.");
         return NULL;
     }
 
@@ -1081,23 +1075,33 @@ NodeCloneContext::~NodeCloneContext()
 
 Animation* NodeCloneContext::findClonedAnimation(const Animation* animation)
 {
+    GP_ASSERT(animation);
+
     AnimationMap::iterator it = _clonedAnimations.find(animation);
     return it != _clonedAnimations.end() ? it->second : NULL;
 }
 
 void NodeCloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
 {
+    GP_ASSERT(original);
+    GP_ASSERT(clone);
+
     _clonedAnimations[original] = clone;
 }
 
 Node* NodeCloneContext::findClonedNode(const Node* node)
 {
+    GP_ASSERT(node);
+
     NodeMap::iterator it = _clonedNodes.find(node);
     return it != _clonedNodes.end() ? it->second : NULL;
 }
 
 void NodeCloneContext::registerClonedNode(const Node* original, Node* clone)
 {
+    GP_ASSERT(original);
+    GP_ASSERT(clone);
+
     _clonedNodes[original] = clone;
 }
 

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

+ 10 - 0
gameplay/src/PhysicsCollisionObject.cpp

@@ -37,11 +37,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 +70,35 @@ 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();
 }
 
 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;

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

+ 14 - 2
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()))
     {
@@ -145,7 +156,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
         else
         {
             // Warn the user that the model has no bounding volume.
-            GP_WARN("Model \'%s\' has no bounding volume - center of mass is defaulting to local coordinate origin.", model->getNode()->getId());
+            GP_WARN("Model '%s' has no bounding volume - center of mass is defaulting to local coordinate origin.", model->getNode()->getId());
             model->getNode()->getWorldMatrix().transformPoint(&center);
         }
     }
@@ -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);
 }

+ 5 - 1
gameplay/src/Plane.cpp

@@ -54,6 +54,8 @@ float Plane::distance(const Vector3& point) const
 
 void Plane::intersection(const Plane& p1, const Plane& p2, const Plane& p3, Vector3* point)
 {
+    GP_ASSERT(point);
+
     // The planes' normals must be all normalized (which we guarantee in the Plane class).
     // Calculate the determinant of the matrix (i.e | n1 n2 n3 |).
     float det = p1._normal.x * (p2._normal.y * p3._normal.z -
@@ -252,7 +254,9 @@ void Plane::transform(const Matrix& matrix)
         float ny = _normal.x * inverted.m[4] + _normal.y * inverted.m[5] + _normal.z * inverted.m[6] + _distance * inverted.m[7];
         float nz = _normal.x * inverted.m[8] + _normal.y * inverted.m[9] + _normal.z * inverted.m[10] + _distance * inverted.m[11];
         float d = _normal.x * inverted.m[12]+ _normal.y * inverted.m[13] + _normal.z * inverted.m[14] + _distance * inverted.m[15];
-        float factor = 1.0f / sqrt(nx * nx + ny * ny + nz * nz);
+        float divisor = sqrt(nx * nx + ny * ny + nz * nz);
+        GP_ASSERT(divisor);
+        float factor = 1.0f / divisor;
 
         _normal.x = nx * factor;
         _normal.y = ny * factor;

+ 34 - 15
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;
@@ -666,7 +672,7 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd)
 
             if (Game::getInstance()->getState() == Game::UNINITIALIZED)
             {
-                Game::getInstance()->run(__width, __height);
+                Game::getInstance()->run();
             }
             else
             {
@@ -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()

+ 111 - 34
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(WINDOW_WIDTH, WINDOW_HEIGHT);
+    _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);
 }
     

+ 15 - 7
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;
@@ -788,7 +793,7 @@ int Platform::enterMessagePump()
     __timeStart = timespec2millis(&__timespec);
     __timeAbsolute = 0L;
 
-    _game->run(__screenWindowSize[0], __screenWindowSize[1]);
+    _game->run();
 
     // Message loop.
     while (true)
@@ -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);
 }
 

+ 109 - 28
gameplay/src/PlatformWin32.cpp

@@ -8,9 +8,11 @@
 #include <GL/wglew.h>
 #include <windowsx.h>
 
+using gameplay::printError;
+
 // Default to 720p
-#define WINDOW_WIDTH    1280
-#define WINDOW_HEIGHT   720
+static int __width = 1280;
+static int __height = 720;
 
 static long __timeTicksPerMillis;
 static long __timeStart;
@@ -257,13 +259,15 @@ 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.
-    static const float ACCELEROMETER_X_FACTOR = 90.0f / WINDOW_WIDTH;
-    static const float ACCELEROMETER_Y_FACTOR = 90.0f / WINDOW_HEIGHT;
+    GP_ASSERT(__width);
+    GP_ASSERT(__height);
+    static const float ACCELEROMETER_X_FACTOR = 90.0f / __width;
+    static const float ACCELEROMETER_Y_FACTOR = 90.0f / __height;
 
     static int lx, ly;
 
@@ -332,7 +336,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         break;
 
     case WM_MOUSEMOVE:
-    {
+    {
         int x = GET_X_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
 
@@ -359,7 +363,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                 lx = x;
                 ly = y;
             }
-        }
+        }
         break;
     }
 
@@ -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);
@@ -457,9 +463,40 @@ Platform* Platform::create(Game* game)
     __hinstance = ::GetModuleHandle(NULL);
 
     LPCTSTR windowClass = L"gameplay";
-    LPCTSTR windowName = L"";
+    std::wstring windowName;
+    bool fullscreen = false;
+    
+    // Read window settings from config.
+    if (game->getConfig())
+    {
+        Properties* config = game->getConfig()->getNamespace("window", true);
+        if (config)
+        {
+            // 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);
+            }
 
-    RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_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");
+        }
+    }
+
+    RECT rect = { 0, 0, __width, __height };
 
     // Register our window class.
     WNDCLASSEX wc;
@@ -477,24 +514,47 @@ Platform* Platform::create(Game* game)
     wc.lpszClassName  = windowClass;
 
     if (!::RegisterClassEx(&wc))
+    {
+        GP_ERROR("Failed to register window class.");
         goto error;
-    
+    }
+
+    if (fullscreen)
+    {
+        DEVMODE dm;
+        memset(&dm, 0, sizeof(dm));
+        dm.dmSize= sizeof(dm);
+        dm.dmPelsWidth	= __width;
+        dm.dmPelsHeight	= __height;
+        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.
+        if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
+        {
+            fullscreen = false;
+            GP_ERROR("Failed to start game in full-screen mode with resolution %dx%d.", __width, __height);
+            goto error;
+        }
+    }
+
     // Set the window style.
-    DWORD style   = ( WINDOW_FULLSCREEN ? WS_POPUP : WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
-    DWORD styleEx = (WINDOW_FULLSCREEN ? WS_EX_APPWINDOW : WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
+    DWORD style   = (fullscreen ? WS_POPUP : WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+    //DWORD style   = (fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+    DWORD styleEx = (fullscreen ? WS_EX_APPWINDOW : WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
 
     // Adjust the window rectangle so the client size is the requested size.
     AdjustWindowRectEx(&rect, style, FALSE, styleEx);
-    
+
     // Create the native Windows window.
-    __hwnd = CreateWindowEx(styleEx, windowClass, windowName, style, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, __hinstance, NULL);
+    __hwnd = CreateWindowEx(styleEx, windowClass, windowName.c_str(), style, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, __hinstance, NULL);
 
     // Get the drawing context.
     __hdc = GetDC(__hwnd);
 
     // Center the window
-    const int screenX = (GetSystemMetrics(SM_CXSCREEN) - WINDOW_WIDTH) / 2;
-    const int screenY = (GetSystemMetrics(SM_CYSCREEN) - WINDOW_HEIGHT) / 2;
+    const int screenX = (GetSystemMetrics(SM_CXSCREEN) - __width) / 2;
+    const int screenY = (GetSystemMetrics(SM_CYSCREEN) - __height) / 2;
     ::SetWindowPos(__hwnd, __hwnd, screenX, screenY, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
 
     // Choose pixel format. 32-bit. RGBA.
@@ -510,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[] =
     {
@@ -529,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;
@@ -551,7 +626,8 @@ error:
 }
 
 int Platform::enterMessagePump()
-{  
+{
+    GP_ASSERT(_game);
     int rc = 0;
 
     // Get the initial time.
@@ -560,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.
@@ -569,7 +646,7 @@ int Platform::enterMessagePump()
     SwapBuffers(__hdc);
 
     if (_game->getState() != Game::RUNNING)
-        _game->run(WINDOW_WIDTH, WINDOW_HEIGHT);
+        _game->run();
 
     // Enter event dispatch loop.
     MSG msg;
@@ -602,18 +679,19 @@ void Platform::signalShutdown()
 
 unsigned int Platform::getDisplayWidth()
 {
-    return WINDOW_WIDTH;
+    return __width;
 }
 
 unsigned int Platform::getDisplayHeight()
 {
-    return WINDOW_HEIGHT;
+    return __height;
 }
     
 long Platform::getAbsoluteTime()
 {
-       LARGE_INTEGER queryTime;
+    LARGE_INTEGER queryTime;
     QueryPerformanceCounter(&queryTime);
+    GP_ASSERT(__timeTicksPerMillis);
     __timeAbsolute = queryTime.QuadPart / __timeTicksPerMillis;
 
     return __timeAbsolute;
@@ -646,6 +724,9 @@ bool Platform::isMultiTouch()
 
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
     *pitch = __pitch;
     *roll = __roll;
 }
@@ -671,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);
 }
 

+ 4 - 2
gameplay/src/PlatformiOS.mm

@@ -146,7 +146,7 @@ int getKey(unichar keyCode);
         
         _game = Game::getInstance();
         __timeStart = getMachTimeInMilliseconds();
-        _game->run(WINDOW_WIDTH, WINDOW_HEIGHT);          
+        _game->run();
     }
     return self;
 }
@@ -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);
 }
     

+ 57 - 28
gameplay/src/Properties.cpp

@@ -21,6 +21,7 @@ Properties::Properties(const Properties& copy)
     std::vector<Properties*>::const_iterator it;
     for (it = copy._namespaces.begin(); it < copy._namespaces.end(); it++)
     {
+        GP_ASSERT(*it);
         _namespaces.push_back(new Properties(**it));
     }
     rewind();
@@ -48,11 +49,9 @@ Properties::Properties(FILE* file, const char* name, const char* id, const char*
 
 Properties* Properties::create(const char* url)
 {
-    GP_ASSERT(url);
-
     if (!url || strlen(url) == 0)
     {
-        GP_WARN("Attempting to create a Properties object from an empty URL!");
+        GP_ERROR("Attempting to create a Properties object from an empty URL!");
         return NULL;
     }
 
@@ -82,6 +81,7 @@ Properties* Properties::create(const char* url)
     FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     if (!file)
     {
+        GP_ERROR("Failed to open file '%s'.", fileString.c_str());
         return NULL;
     }
 
@@ -102,6 +102,12 @@ Properties* Properties::create(const char* url)
         {
             while (true)
             {
+                if (iter == NULL)
+                {
+                    GP_ERROR("Failed to load Properties object from URL '%s'.", url);
+                    return NULL;
+                }
+
                 if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
                 {
                     if (i != size - 1)
@@ -117,11 +123,6 @@ Properties* Properties::create(const char* url)
                 }
                 
                 iter = properties->getNextNamespace();
-                if (iter == NULL)
-                {
-                    GP_WARN("Failed to load Properties object from URL '%s'.", url);
-                    return NULL;
-                }
             }
         }
 
@@ -135,6 +136,8 @@ Properties* Properties::create(const char* url)
 
 void Properties::readProperties(FILE* file)
 {
+    GP_ASSERT(file);
+
     char line[2048];
     int c;
     char* name;
@@ -155,6 +158,7 @@ void Properties::readProperties(FILE* file)
         rc = fgets(line, 2048, file);
         if (rc == NULL)
         {
+            GP_ERROR("Error reading line from file.");
             return;
         }
 
@@ -173,7 +177,7 @@ void Properties::readProperties(FILE* file)
                 name = strtok(line, " =\t");
                 if (name == NULL)
                 {
-                    GP_ERROR("Error parsing properties file: value without name.");
+                    GP_ERROR("Error parsing properties file: attribute without name.");
                     return;
                 }
 
@@ -181,7 +185,8 @@ void Properties::readProperties(FILE* file)
                 value = strtok(NULL, "=");
                 if (value == NULL)
                 {
-                    GP_ERROR("Error parsing properties file: name without value.");
+                    GP_ERROR("Error parsing properties file: attribute with name ('%s') but no value.", name);
+                    return;
                 }
 
                 // Remove white-space from value.
@@ -214,7 +219,8 @@ void Properties::readProperties(FILE* file)
                 name = trimWhiteSpace(name);
                 if (name == NULL)
                 {
-                    GP_ERROR("Error parsing properties file: unknown error.");
+                    GP_ERROR("Error parsing properties file: failed to determine a valid token for line '%s'.", line);
+                    return;
                 }
                 else if (name[0] == '}')
                 {
@@ -260,7 +266,8 @@ void Properties::readProperties(FILE* file)
                         else
                         {
                             // Back up from fgetc()
-                            fseek(file, -1, SEEK_CUR);
+                            if (fseek(file, -1, SEEK_CUR) != 0)
+                                GP_ERROR("Failed to seek backwards a single character after testing if the next line starts with '{'.");
 
                             // Store "name value" as a name/value pair, or even just "name".
                             if (value != NULL)
@@ -300,7 +307,12 @@ void Properties::skipWhiteSpace(FILE* file)
     // If we are not at the end of the file, then since we found a
     // non-whitespace character, we put the cursor back in front of it.
     if (c != EOF)
-        fseek(file, -1, SEEK_CUR);
+    {
+        if (fseek(file, -1, SEEK_CUR) != 0)
+        {
+            GP_ERROR("Failed to seek backwards one character after skipping whitespace.");
+        }
+    }
 }
 
 char* Properties::trimWhiteSpace(char *str)
@@ -375,6 +387,7 @@ void Properties::resolveInheritance(const char* id)
                 std::vector<Properties*>::const_iterator itt;
                 for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); itt++)
                 {
+                    GP_ASSERT(*itt);
                     derived->_namespaces.push_back(new Properties(**itt));
                 }
                 derived->rewind();
@@ -404,6 +417,8 @@ void Properties::resolveInheritance(const char* id)
 
 void Properties::mergeWith(Properties* overrides)
 {
+    GP_ASSERT(overrides);
+
     // Overwrite or add each property found in child.
     char* value = new char[255];
     overrides->rewind();
@@ -505,15 +520,17 @@ void Properties::rewind()
     _namespacesItr = _namespaces.end();
 }
 
-Properties* Properties::getNamespace(const char* id) const
+Properties* Properties::getNamespace(const char* id, bool searchNames) const
 {
+    GP_ASSERT(id);
+
     Properties* ret = NULL;
     std::vector<Properties*>::const_iterator it;
     
     for (it = _namespaces.begin(); it < _namespaces.end(); it++)
     {
         ret = *it;
-        if (strcmp(ret->_id.c_str(), id) == 0)
+        if (strcmp(searchNames ? ret->_namespace.c_str() : ret->_id.c_str(), id) == 0)
         {
             return ret;
         }
@@ -547,6 +564,8 @@ bool Properties::exists(const char* name) const
 
 const bool isStringNumeric(const char* str)
 {
+    GP_ASSERT(str);
+
     // The first character may be '-'
     if (*str == '-')
         str++;
@@ -587,7 +606,6 @@ Properties::Type Properties::getType(const char* name) const
 
     // Parse the value to determine the format
     unsigned int commaCount = 0;
-    //unsigned int length = strlen(value);
     char* valuePtr = const_cast<char*>(value);
     while (valuePtr = strchr(valuePtr, ','))
     {
@@ -654,7 +672,7 @@ int Properties::getInt(const char* name) const
         scanned = sscanf(valueString, "%d", &value);
         if (scanned != 1)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as an integer.", name);
             return 0;
         }
         return value;
@@ -673,7 +691,7 @@ float Properties::getFloat(const char* name) const
         scanned = sscanf(valueString, "%f", &value);
         if (scanned != 1)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as a float.", name);
             return 0.0f;
         }
         return value;
@@ -692,7 +710,7 @@ long Properties::getLong(const char* name) const
         scanned = sscanf(valueString, "%ld", &value);
         if (scanned != 1)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as a long integer.", name);
             return 0L;
         }
         return value;
@@ -716,7 +734,7 @@ bool Properties::getMatrix(const char* name, Matrix* out) const
 
         if (scanned != 16)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as a matrix.", name);
             out->setIdentity();
             return false;
         }
@@ -741,7 +759,7 @@ bool Properties::getVector2(const char* name, Vector2* out) const
         scanned = sscanf(valueString, "%f,%f", &x, &y);
         if (scanned != 2)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as a two-dimensional vector.", name);
             out->set(0.0f, 0.0f);
             return false;
         }
@@ -766,7 +784,7 @@ bool Properties::getVector3(const char* name, Vector3* out) const
         scanned = sscanf(valueString, "%f,%f,%f", &x, &y, &z);
         if (scanned != 3)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as a three-dimensional vector.", name);
             out->set(0.0f, 0.0f, 0.0f);
             return false;
         }
@@ -791,7 +809,7 @@ bool Properties::getVector4(const char* name, Vector4* out) const
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &w);
         if (scanned != 4)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as a four-dimensional vector.", name);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             return false;
         }
@@ -816,7 +834,7 @@ bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) c
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &theta);
         if (scanned != 4)
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as an axis-angle rotation.", name);
             out->set(0.0f, 0.0f, 0.0f, 1.0f);
             return false;
         }
@@ -840,14 +858,19 @@ bool Properties::getColor(const char* name, Vector3* out) const
             valueString[0] != '#')
         {
             // Not a color string.
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as an RGB color (not specified as a color string).", name);
             out->set(0.0f, 0.0f, 0.0f);
             return false;
         }
 
         // Read the string into an int as hex.
         unsigned int color;
-        sscanf(valueString+1, "%x", &color);
+        if (sscanf(valueString+1, "%x", &color) != 1)
+        {
+            GP_ERROR("Error attempting to parse property '%s' as an RGB color.", name);
+            out->set(0.0f, 0.0f, 0.0f);
+            return false;
+        }
 
         out->set(Vector3::fromColor(color));
         return true;
@@ -868,14 +891,19 @@ bool Properties::getColor(const char* name, Vector4* out) const
             valueString[0] != '#')
         {
             // Not a color string.
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as an RGBA color (not specified as a color string).", name);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             return false;
         }
 
         // Read the string into an int as hex.
         unsigned int color;
-        sscanf(valueString+1, "%x", &color);
+        if (sscanf(valueString+1, "%x", &color) != 1)
+        {
+            GP_ERROR("Error attempting to parse property '%s' as an RGBA color.", name);
+            out->set(0.0f, 0.0f, 0.0f, 0.0f);
+            return false;
+        }
 
         out->set(Vector4::fromColor(color));
         return true;
@@ -898,6 +926,7 @@ Properties* Properties::clone()
     unsigned int count = _namespaces.size();
     for (unsigned int i = 0; i < count; i++)
     {
+        GP_ASSERT(_namespaces[i]);
         p->_namespaces.push_back(_namespaces[i]->clone());
     }
     p->_namespacesItr = p->_namespaces.end();

+ 13 - 6
gameplay/src/Properties.h

@@ -31,7 +31,8 @@ namespace gameplay
     // This line defines a namespace of type "mynamespace" without an ID:
     mynamespace
     {
-        // This namespace can be retrieved by searching for its ID, "spriteTexture":
+        // This namespace can be retrieved by searching for its ID, "spriteTexture",
+        // or by its name "texture":
         texture spriteTexture 
         {
             fileName = sprite.png
@@ -125,6 +126,8 @@ namespace gameplay
  */
 class Properties
 {
+    friend class Game;
+
 public:
 
     /**
@@ -176,14 +179,18 @@ public:
     void rewind();
 
     /**
-     * Get a specific namespace by ID.  This method will perform a depth-first search
-     * on all namespaces and inner namespaces within this Property.
+     * Get a specific namespace by ID or name. This method will perform
+     * a depth-first search on all namespaces and inner namespaces within
+     * this Property.
      *
-     * @param id The ID of a specific namespace.
+     * @param id The ID or name of the namespace to find.
+     * @param searchNames If true, namespace names are used in the search,
+     *      instead of namespace IDs. By default this parameter is false
+     *      and namespace IDs are searched.
      * 
-     * @return A properties object with the given ID.
+     * @return A properties object with the given ID or name.
      */
-    Properties* getNamespace(const char* id) const;
+    Properties* getNamespace(const char* id, bool searchNames = false) const;
 
     /**
      * Get the name of this Property's namespace.

+ 10 - 4
gameplay/src/Quaternion.cpp

@@ -62,8 +62,6 @@ bool Quaternion::isZero() const
 
 void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
 {
-    GP_ASSERT(dst);
-
     m.getRotation(dst);
 }
 
@@ -89,6 +87,8 @@ void Quaternion::conjugate()
 
 void Quaternion::conjugate(Quaternion* dst) const
 {
+    GP_ASSERT(dst);
+
     dst->x = -x;
     dst->y = -y;
     dst->z = -z;
@@ -102,6 +102,8 @@ bool Quaternion::inverse()
 
 bool Quaternion::inverse(Quaternion* dst) const
 {
+    GP_ASSERT(dst);
+
     float n = x * x + y * y + z * z + w * w;
     if (n == 1.0f)
     {
@@ -113,7 +115,7 @@ bool Quaternion::inverse(Quaternion* dst) const
         return true;
     }
 
-    // too close to zero
+    // Too close to zero.
     if (n < 0.000001f)
         return false;
 
@@ -133,6 +135,8 @@ void Quaternion::multiply(const Quaternion& q)
 
 void Quaternion::multiply(const Quaternion& q1, const Quaternion& q2, Quaternion* dst)
 {
+    GP_ASSERT(dst);
+
     float x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
     float y = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x;
     float z = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w;
@@ -263,12 +267,12 @@ void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quate
 
 void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
 {
+    GP_ASSERT(dst);
     slerp(q1.x, q1.y, q1.z, q1.w, q2.x, q2.y, q2.z, q2.w, t, &dst->x, &dst->y, &dst->z, &dst->w);
 }
 
 void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
 {
-    GP_ASSERT(dst);
     GP_ASSERT(!(t < 0.0f || t > 1.0f));
 
     Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
@@ -380,6 +384,8 @@ void Quaternion::slerp(float q1x, float q1y, float q1z, float q1w, float q2x, fl
 
 void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
 {
+    GP_ASSERT(dst);
+
     // cos(omega) = q1 * q2;
     // slerp(q1, q2, t) = (q1*sin((1-t)*omega) + q2*sin(t*omega))/sin(omega);
     // q1 = +- q2, slerp(q1,q2,t) = q1.

+ 12 - 10
gameplay/src/Ray.cpp

@@ -85,20 +85,20 @@ float Ray::intersects(const Frustum& frustum) const
 
     // If the ray's origin is in the negative half-space of one of the frustum's planes
     // and it does not intersect that same plane, then it does not intersect the frustum.
-    if ( (nOD < 0.0f && nD < 0.0f) || (fOD < 0.0f && fD < 0.0f) ||
+    if ((nOD < 0.0f && nD < 0.0f) || (fOD < 0.0f && fD < 0.0f) ||
         (lOD < 0.0f && lD < 0.0f)  || (rOD < 0.0f && rD < 0.0f) ||
-        (bOD < 0.0f && bD < 0.0f)  || (tOD < 0.0f && tD < 0.0f) )
+        (bOD < 0.0f && bD < 0.0f)  || (tOD < 0.0f && tD < 0.0f))
     {
         return Ray::INTERSECTS_NONE;
     }
 
     // Otherwise, the intersection distance is the minimum positive intersection distance.
     float d = (nD > 0.0f) ? nD : 0.0f;
-    d = (fD > 0.0f) ? ( (d == 0.0f) ? fD : min(fD, d) ) : d;
-    d = (lD > 0.0f) ? ( (d == 0.0f) ? lD : min(lD, d) ) : d;
-    d = (rD > 0.0f) ? ( (d == 0.0f) ? rD : min(rD, d) ) : d;
-    d = (tD > 0.0f) ? ( (d == 0.0f) ? bD : min(bD, d) ) : d;
-    d = (bD > 0.0f) ? ( (d == 0.0f) ? tD : min(tD, d) ) : d;
+    d = (fD > 0.0f) ? ((d == 0.0f) ? fD : min(fD, d)) : d;
+    d = (lD > 0.0f) ? ((d == 0.0f) ? lD : min(lD, d)) : d;
+    d = (rD > 0.0f) ? ((d == 0.0f) ? rD : min(rD, d)) : d;
+    d = (tD > 0.0f) ? ((d == 0.0f) ? bD : min(bD, d)) : d;
+    d = (bD > 0.0f) ? ((d == 0.0f) ? tD : min(tD, d)) : d;
 
     return d;
 }
@@ -117,7 +117,7 @@ float Ray::intersects(const Plane& plane) const
     
     // If the dot product of the plane's normal and this ray's direction is zero,
     // then the ray is parallel to the plane and does not intersect it.
-    if ( dot == 0.0f )
+    if (dot == 0.0f)
     {
         return INTERSECTS_NONE;
     }
@@ -156,12 +156,14 @@ void Ray::transform(const Matrix& matrix)
 void Ray::normalize()
 {
     if (_direction.isZero())
+    {
+        GP_ERROR("Invalid ray object; a ray's direction must be non-zero.");
         return;
+    }
 
     // Normalize the ray's direction vector.
     float normalizeFactor = 1.0f / sqrt(_direction.x * _direction.x + _direction.y * _direction.y + _direction.z * _direction.z);
-
-    if ( normalizeFactor != 1.0f )
+    if (normalizeFactor != 1.0f)
     {
         _direction.x *= normalizeFactor;
         _direction.y *= normalizeFactor;

+ 2 - 0
gameplay/src/Rectangle.cpp

@@ -110,6 +110,8 @@ bool Rectangle::intersects(const Rectangle& r) const
 
 void Rectangle::combine(const Rectangle& r1, const Rectangle& r2, Rectangle* dst)
 {
+    GP_ASSERT(dst);
+
     dst->x = min(r1.x, r2.x);
     dst->y = min(r1.y, r2.y);
     dst->width = max(r1.x + r1.width, r2.x + r2.width) - dst->x;

+ 15 - 6
gameplay/src/Ref.cpp

@@ -68,23 +68,26 @@ void Ref::printLeaks()
     // Dump Ref object memory leaks
     if (__refAllocationCount == 0)
     {
-        printError("[memory] All Ref objects successfully cleaned up (no leaks detected).");
+        printError("[memory] All Ref objects successfully cleaned up (no leaks detected).\n");
     }
     else
     {
-        printError("[memory] WARNING: %d Ref objects still active in memory.", __refAllocationCount);
+        printError("[memory] WARNING: %d Ref objects still active in memory.\n", __refAllocationCount);
         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.", (type ? type : ""), ref->getRefCount());
+            printError("[memory] LEAK: Ref object '%s' still active with reference count %d.\n", (type ? type : ""), ref->getRefCount());
         }
     }
 }
 
 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,14 +103,20 @@ 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)
     {
-        printError("[memory] CORRUPTION: Attempting to free Ref with invalid ref tracking record.");
+        printError("[memory] CORRUPTION: Attempting to free Ref with invalid ref tracking record.\n");
         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;

+ 16 - 9
gameplay/src/Scene.cpp

@@ -119,6 +119,7 @@ unsigned int Scene::findNodes(const char* id, std::vector<Node*>& nodes, bool re
 Node* Scene::addNode(const char* id)
 {
     Node* node = Node::create(id);
+    GP_ASSERT(node);
     addNode(node);
 
     // Call release to decrement the ref count to 1 before returning.
@@ -237,7 +238,7 @@ void Scene::setActiveCamera(Camera* camera)
             // Unbind the active camera from the audio listener
             if (audioListener && (audioListener->getCamera() == _activeCamera))
             {
-                AudioListener::getInstance()->setCamera(NULL);
+                audioListener->setCamera(NULL);
             }
 
             SAFE_RELEASE(_activeCamera);
@@ -251,7 +252,7 @@ void Scene::setActiveCamera(Camera* camera)
 
             if (audioListener && _bindAudioListenerToCamera)
             {
-                AudioListener::getInstance()->setCamera(_activeCamera);
+                audioListener->setCamera(_activeCamera);
             }
         }
     }
@@ -309,6 +310,7 @@ Material* createDebugMaterial()
 
     Effect* effect = Effect::createFromSource(vs_str, fs_str);
     Material* material = Material::create(effect);
+    GP_ASSERT(material && material->getStateBlock());
     material->getStateBlock()->setDepthTest(true);
 
     SAFE_RELEASE(effect);
@@ -359,6 +361,8 @@ struct DebugVertex
 
 void drawDebugLine(MeshBatch* batch, const Vector3& point1, const Vector3& point2, const Vector3& color)
 {
+    GP_ASSERT(batch);
+
     static DebugVertex verts[2];
 
     verts[0].x = point1.x;
@@ -457,12 +461,15 @@ void drawDebugSphere(MeshBatch* batch, const BoundingSphere& sphere)
 
 void drawDebugNode(MeshBatch* batch, Node* node, unsigned int debugFlags)
 {
+    GP_ASSERT(node);
     Model* model = node->getModel();
 
     if ((debugFlags & Scene::DEBUG_BOXES) && model)
     {
+        GP_ASSERT(model->getMesh());
+
         MeshSkin* skin = model->getSkin();
-        if (skin && skin->getRootJoint()->getParent())
+        if (skin && skin->getRootJoint() && skin->getRootJoint()->getParent())
         {
             // For skinned meshes that have a parent node to the skin's root joint,
             // we need to transform the bounding volume by that parent node's transform
@@ -480,11 +487,9 @@ void drawDebugNode(MeshBatch* batch, Node* node, unsigned int debugFlags)
         drawDebugSphere(batch, node->getBoundingSphere());
     }
 
-    Node* child = node->getFirstChild();
-    while (child)
+    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
     {
         drawDebugNode(batch, child, debugFlags);
-        child = child->getNextSibling();
     }
 }
 
@@ -507,17 +512,19 @@ void Scene::drawDebug(unsigned int debugFlags)
 
     _debugBatch->begin();
 
-    Node* node = _firstNode;
-    while (node)
+    for (Node* node = _firstNode; node != NULL; node = node->_nextSibling)
     {
         drawDebugNode(_debugBatch, node, debugFlags);
-        node = node->_nextSibling;
     }
 
     _debugBatch->end();
 
     if (_activeCamera)
+    {
+        GP_ASSERT(_debugBatch->getMaterial());
+        GP_ASSERT(_debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix"));
         _debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_activeCamera->getViewProjectionMatrix());
+    }
 
     _debugBatch->draw();
 }

+ 125 - 59
gameplay/src/SceneLoader.cpp

@@ -13,23 +13,19 @@ std::string SceneLoader::_path;
 
 Scene* SceneLoader::load(const char* url)
 {
-    GP_ASSERT(url);
-
     // Load the scene properties from file.
     Properties* properties = Properties::create(url);
-    GP_ASSERT(properties);
     if (properties == NULL)
     {
-        GP_WARN("Failed to load scene file: %s", url);
+        GP_ERROR("Failed to load scene file '%s'.", url);
         return NULL;
     }
 
     // Check if the properties object is valid and has a valid namespace.
     Properties* sceneProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
-    GP_ASSERT(sceneProperties);
     if (!sceneProperties || !(strcmp(sceneProperties->getNamespace(), "scene") == 0))
     {
-        GP_WARN("Failed to load scene from properties object: must be non-null object and have namespace equal to 'scene'.");
+        GP_ERROR("Failed to load scene from properties object: must be non-null object and have namespace equal to 'scene'.");
         SAFE_DELETE(properties);
         return NULL;
     }
@@ -45,6 +41,7 @@ Scene* SceneLoader::load(const char* url)
     Scene* scene = loadMainSceneData(sceneProperties);
     if (!scene)
     {
+        GP_ERROR("Failed to load main scene from bundle.");
         SAFE_DELETE(properties);
         return NULL;
     }
@@ -152,14 +149,15 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
     for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
     {
         SceneNode& sceneNode = _sceneNodes[i];
+        GP_ASSERT(sceneNode._nodeID);
 
         if (sceneNode._exactMatch)
         {
-            // Find the node matching the specified ID exactly
+            // Find the node matching the specified ID exactly.
             Node* node = scene->findNode(sceneNode._nodeID);
             if (!node)
             {
-                GP_WARN("Attempting to set a property for node '%s', which does not exist in the scene.", sceneNode._nodeID);
+                GP_ERROR("Failed to set property for node '%s', which does not exist in the scene.", sceneNode._nodeID);
                 continue;
             }
 
@@ -172,11 +170,14 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
         }
         else
         {
-            // Find all nodes matching the specified ID
+            // Find all nodes matching the specified ID.
             std::vector<Node*> nodes;
             unsigned int nodeCount = scene->findNodes(sceneNode._nodeID, nodes, true, false);
             if (nodeCount == 0)
+            {
+                GP_ERROR("Failed to set property for nodes with id matching '%s'; no such nodes exist in the scene.", sceneNode._nodeID);
                 continue;
+            }
             
             for (unsigned int j = 0, pcount = sceneNode._properties.size(); j < pcount; ++j)
             {
@@ -204,7 +205,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         Properties* p = _propertiesFromFile[snp._file];
         if (!p)
         {
-            GP_WARN("The referenced node data in file '%s' failed to load.", snp._file.c_str());
+            GP_ERROR("The referenced node data in file '%s' failed to load.", snp._file.c_str());
             return;
         }
 
@@ -214,7 +215,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             p = p->getNamespace(snp._id.c_str());
             if (!p)
             {
-                GP_WARN("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
+                GP_ERROR("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
                 return;
             }
         }
@@ -236,7 +237,10 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         }
         case SceneNodeProperty::MATERIAL:
             if (!node->getModel())
-                GP_WARN("Attempting to set a material on node '%s', which has no model.", sceneNode._nodeID);
+            {
+                GP_ERROR("Attempting to set a material on node '%s', which has no model.", sceneNode._nodeID);
+                return;
+            }
             else
             {
                 Material* material = Material::create(p);
@@ -259,7 +263,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             Properties* p = _propertiesFromFile[snp._file];
             if (!p)
             {
-                GP_WARN("The referenced node data in file '%s' failed to load.", snp._file.c_str());
+                GP_ERROR("The referenced node data in file '%s' failed to load.", snp._file.c_str());
                 return;
             }
 
@@ -269,7 +273,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 p = p->getNamespace(snp._id.c_str());
                 if (!p)
                 {
-                    GP_WARN("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
+                    GP_ERROR("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
                     return;
                 }
             }
@@ -283,15 +287,18 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             // Check to make sure the type of the namespace used to load the physics collision object is correct.
             if (snp._type == SceneNodeProperty::CHARACTER && strcmp(p->getNamespace(), "character") != 0)
             {
-                GP_WARN("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_ERROR("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                return;
             }
             else if (snp._type == SceneNodeProperty::GHOSTOBJECT && strcmp(p->getNamespace(), "ghostObject") != 0)
             {
-                GP_WARN("Attempting to set a 'ghostObject' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_ERROR("Attempting to set a 'ghostObject' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                return;
             }
             else if (snp._type == SceneNodeProperty::RIGIDBODY && strcmp(p->getNamespace(), "rigidBody") != 0)
             {
-                GP_WARN("Attempting to set a 'rigidBody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_ERROR("Attempting to set a 'rigidBody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                return;
             }
             else
             {
@@ -300,13 +307,19 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 const char* name = NULL;
                 if (np && (name = np->getString("rigidBodyModel")))
                 {
+                    GP_ASSERT(scene);
                     Node* modelNode = scene->findNode(name);
                     if (!modelNode)
-                        GP_WARN("Node '%s' does not exist; attempting to use its model for collision object creation.", name);
+                    {
+                        GP_ERROR("Node '%s' does not exist; attempting to use its model for collision object creation.", name);
+                        return;
+                    }
                     else
                     {
                         if (!modelNode->getModel())
-                            GP_WARN("Node '%s' does not have a model; attempting to use its model for collision object creation.", name);
+                        {
+                            GP_ERROR("Node '%s' does not have a model; attempting to use its model for collision object creation.", name);
+                        }
                         else
                         {
                             // Temporarily set rigidBody model on model so it's used during collision object creation.
@@ -335,13 +348,13 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             break;
         }
         default:
-            // This cannot happen.
+            GP_ERROR("Unsupported node property type (%d).", snp._type);
             break;
         }
     }
     else
     {
-        // Handle Scale, Rotate and Translate
+        // Handle scale, rotate and translate.
         Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
         const char* name = NULL;
 
@@ -379,7 +392,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             break;
         }
         default:
-            GP_WARN("Unsupported node property type: %d.", snp._type);
+            GP_ERROR("Unsupported node property type (%d).", snp._type);
             break;
         }
     }
@@ -387,6 +400,8 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
 
 void SceneLoader::applyNodeUrls(Scene* scene)
 {
+    GP_ASSERT(scene);
+
     // Apply all URL node properties so that when we go to apply
     // the other node properties, the node is in the scene.
     for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
@@ -416,7 +431,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     }
                     else
                     {
-                        GP_WARN("Could not find node '%s' in main scene GPB file.", snp._id.c_str());
+                        GP_ERROR("Could not find node '%s' in main scene GPB file.", snp._id.c_str());
                     }
                 }
                 else
@@ -427,10 +442,13 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     unsigned int nodeCount = scene->findNodes(snp._id.c_str(), nodes, true, false);
                     if (nodeCount > 0)
                     {
+                        GP_ASSERT(sceneNode._nodeID);
+
                         for (unsigned int k = 0; k < nodeCount; ++k)
                         {
                             // Construct a new node ID using _nodeID plus the remainder of the partial match.
                             Node* node = nodes[k];
+                            GP_ASSERT(node);
                             std::string newID(sceneNode._nodeID);
                             newID += (node->getId() + snp._id.length());
                             node->setId(newID.c_str());
@@ -438,7 +456,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     }
                     else
                     {
-                        GP_WARN("Could not find any nodes matching '%s' in main scene GPB file.", snp._id.c_str());
+                        GP_ERROR("Could not find any nodes matching '%s' in main scene GPB file.", snp._id.c_str());
                     }
                 }
             }
@@ -463,7 +481,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                         }
                         else
                         {
-                            GP_WARN("Could not load node '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
+                            GP_ERROR("Could not load node '%s' from GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
                         }
                     }
                     else
@@ -494,7 +512,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                         }
                         if (matchCount == 0)
                         {
-                            GP_WARN("Could not find any nodes matching '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
+                            GP_ERROR("Could not find any nodes matching '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
                         }
                     }
 
@@ -502,7 +520,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                 }
                 else
                 {
-                    GP_WARN("Failed to load GPB file '%s' for node stitching.", snp._file.c_str());
+                    GP_ERROR("Failed to load GPB file '%s' for node stitching.", snp._file.c_str());
                 }
             }
 
@@ -523,11 +541,11 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
         {
             if (strlen(ns->getId()) == 0)
             {
-                GP_WARN("Nodes must have an ID; skipping the current node.");
+                GP_ERROR("Attempting to load a node without an ID.");
                 continue;
             }
 
-            // Add a SceneNode to the end of the list
+            // Add a SceneNode to the end of the list.
             _sceneNodes.resize(_sceneNodes.size() + 1);
             SceneNode& sceneNode = _sceneNodes[_sceneNodes.size()-1];
             sceneNode._nodeID = ns->getId();
@@ -596,7 +614,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 else
                 {
-                    GP_WARN("Unsupported node property: %s = %s", name, ns->getString());
+                    GP_ERROR("Unsupported node property: %s = %s", name, ns->getString());
                 }
             }
         }
@@ -611,20 +629,20 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                     const char* animationID = animation->getId();
                     if (strlen(animationID) == 0)
                     {
-                        GP_WARN("Animations must have an ID; skipping the current animation.");
+                        GP_ERROR("Attempting to load an animation without an ID.");
                         continue;
                     }
 
                     const char* url = animation->getString("url");
                     if (!url)
                     {
-                        GP_WARN("Animations must have a URL; skipping animation '%s'.", animationID);
+                        GP_ERROR("Attempting to load animation '%s' without a URL.", animationID);
                         continue;
                     }
                     const char* targetID = animation->getString("target");
                     if (!targetID)
                     {
-                        GP_WARN("Animations must have a target; skipping animation '%s'.", animationID);
+                        GP_ERROR("Attempting to load animation '%s' without a target.", animationID);
                         continue;
                     }
 
@@ -632,7 +650,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 else
                 {
-                    GP_WARN("Unsupported child namespace (of 'animations'): %s", ns->getNamespace());
+                    GP_ERROR("Unsupported child namespace (of 'animations'): %s", ns->getNamespace());
                 }
             }
         }
@@ -644,7 +662,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
         else
         {
             // TODO: Should we ignore these items? They could be used for generic properties file inheritance.
-            GP_WARN("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
+            GP_ERROR("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
         }
     }
 }
@@ -659,7 +677,7 @@ void SceneLoader::createAnimations(const Scene* scene)
         Node* node = scene->findNode(_animations[i]._targetID);
         if (!node)
         {
-            GP_WARN("Attempting to create an animation targeting node '%s', which does not exist in the scene.", _animations[i]._targetID);
+            GP_ERROR("Attempting to create an animation targeting node '%s', which does not exist in the scene.", _animations[i]._targetID);
             continue;
         }
 
@@ -667,7 +685,7 @@ void SceneLoader::createAnimations(const Scene* scene)
         Properties* p = _propertiesFromFile[_animations[i]._file];
         if (!p)
         {
-            GP_WARN("The referenced animation data in file '%s' failed to load.", _animations[i]._file.c_str());
+            GP_ERROR("The referenced animation data in file '%s' failed to load.", _animations[i]._file.c_str());
             continue;
         }
         if (_animations[i]._id.size() > 0)
@@ -675,7 +693,7 @@ void SceneLoader::createAnimations(const Scene* scene)
             p = p->getNamespace(_animations[i]._id.c_str());
             if (!p)
             {
-                GP_WARN("The referenced animation data at '%s#%s' failed to load.", _animations[i]._file.c_str(), _animations[i]._id.c_str());
+                GP_ERROR("The referenced animation data at '%s#%s' failed to load.", _animations[i]._file.c_str(), _animations[i]._id.c_str());
                 continue;
             }
         }
@@ -686,7 +704,10 @@ void SceneLoader::createAnimations(const Scene* scene)
 
 PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
-    PhysicsGenericConstraint* physicsConstraint;
+    GP_ASSERT(rbA);
+    GP_ASSERT(constraint);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+    PhysicsGenericConstraint* physicsConstraint = NULL;
 
     // Create the constraint from the specified properties.
     Quaternion roA;
@@ -714,6 +735,7 @@ PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constrai
     {
         physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, rbB);
     }
+    GP_ASSERT(physicsConstraint);
 
     // Set the optional parameters that were specified.
     Vector3 v;
@@ -731,6 +753,9 @@ PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constrai
 
 PhysicsConstraint* SceneLoader::loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
+    GP_ASSERT(rbA);
+    GP_ASSERT(constraint);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     PhysicsHingeConstraint* physicsConstraint = NULL;
 
     // Create the constraint from the specified properties.
@@ -752,25 +777,44 @@ PhysicsConstraint* SceneLoader::loadHingeConstraint(const Properties* constraint
         physicsConstraint = Game::getInstance()->getPhysicsController()->createHingeConstraint(rbA, roA, toA);
     }
 
-    // Attempt to load the hinge limits first as a Vector3 and if that doesn't work, try loading as a Vector2.
-    // We do this because the user can specify just the min and max angle, or both angle along with bounciness.
-    Vector3 fullLimits;
-    Vector2 angleLimits;
-    if (constraint->getVector3("limits", &fullLimits))
-        physicsConstraint->setLimits(MATH_DEG_TO_RAD(fullLimits.x), MATH_DEG_TO_RAD(fullLimits.y), fullLimits.z);
-    else if (constraint->getVector2("limits", &angleLimits))
-        physicsConstraint->setLimits(angleLimits.x, angleLimits.y);
+    // Load the hinge angle limits (lower and upper) and the hinge bounciness (if specified).
+    const char* limitsString = constraint->getString("limits");
+    if (limitsString)
+    {
+        float lowerLimit, upperLimit;
+        int scanned;
+        scanned = sscanf(limitsString, "%f,%f", &lowerLimit, &upperLimit);
+        if (scanned == 2)
+        {
+            physicsConstraint->setLimits(MATH_DEG_TO_RAD(lowerLimit), MATH_DEG_TO_RAD(upperLimit));
+        }
+        else
+        {
+            float bounciness;
+            scanned = sscanf(limitsString, "%f,%f,%f", &lowerLimit, &upperLimit, &bounciness);
+            if (scanned == 3)
+            {
+                physicsConstraint->setLimits(MATH_DEG_TO_RAD(lowerLimit), MATH_DEG_TO_RAD(upperLimit), bounciness);
+            }
+            else
+            {
+                GP_ERROR("Failed to parse 'limits' attribute for hinge constraint '%s'.", constraint->getId());
+            }
+        }
+    }
 
     return physicsConstraint;
 }
 
 Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
 {
+    GP_ASSERT(sceneProperties);
+
     // Load the main scene from the specified path.
     Bundle* bundle = Bundle::create(_path.c_str());
     if (!bundle)
     {
-        GP_WARN("Failed to load scene GPB file '%s'.", _path.c_str());
+        GP_ERROR("Failed to load scene GPB file '%s'.", _path.c_str());
         return NULL;
     }
 
@@ -778,7 +822,7 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
     Scene* scene = bundle->loadScene(NULL);
     if (!scene)
     {
-        GP_WARN("Failed to load scene from '%s'.", _path.c_str());
+        GP_ERROR("Failed to load scene from '%s'.", _path.c_str());
         SAFE_RELEASE(bundle);
         return NULL;
     }
@@ -798,6 +842,10 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
 
 void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
 {
+    GP_ASSERT(physics);
+    GP_ASSERT(scene);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+
     // Go through the supported global physics properties and apply them.
     Vector3 gravity;
     if (physics->getVector3("gravity", &gravity))
@@ -817,18 +865,18 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
             name = constraint->getString("rigidBodyA");
             if (!name)
             {
-                GP_WARN("Missing property 'rigidBodyA' for constraint %s", constraint->getId());
+                GP_ERROR("Missing property 'rigidBodyA' for constraint '%s'.", constraint->getId());
                 continue;
             }
             Node* rbANode = scene->findNode(name);
             if (!rbANode)
             {
-                GP_WARN("Node '%s' to be used as 'rigidBodyA' for constraint %s cannot be found.", name, constraint->getId());
+                GP_ERROR("Node '%s' to be used as 'rigidBodyA' for constraint '%s' cannot be found.", name, constraint->getId());
                 continue;
             }
             if (!rbANode->getCollisionObject() || rbANode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
             {
-                GP_WARN("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
+                GP_ERROR("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
                 continue;
             }
             PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
@@ -844,12 +892,12 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                 Node* rbBNode = scene->findNode(name);
                 if (!rbBNode)
                 {
-                    GP_WARN("Node '%s' to be used as 'rigidBodyB' for constraint %s cannot be found.", name, constraint->getId());
+                    GP_ERROR("Node '%s' to be used as 'rigidBodyB' for constraint '%s' cannot be found.", name, constraint->getId());
                     continue;
                 }
                 if (!rbBNode->getCollisionObject() || rbBNode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
                 {
-                    GP_WARN("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
+                    GP_ERROR("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
                     continue;
                 }
                 rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
@@ -878,18 +926,25 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
             {
                 physicsConstraint = loadSpringConstraint(constraint, rbA, rbB);
             }
+            else
+            {
+                GP_ERROR("Unsupported physics constraint type '%s'.", type.c_str());
+            }
             
             // If the constraint failed to load, continue on to the next one.
             if (!physicsConstraint)
+            {
+                GP_ERROR("Failed to create physics constraint.");
                 continue;
+            }
 
             // If the breaking impulse was specified, apply it to the constraint.
-            if (constraint->getString("breakingImpulse"))
+            if (constraint->exists("breakingImpulse"))
                 physicsConstraint->setBreakingImpulse(constraint->getFloat("breakingImpulse"));
         }
         else
         {
-            GP_WARN("Unsupported child namespace (of 'physics'): %s", physics->getNamespace());
+            GP_ERROR("Unsupported 'physics' child namespace '%s'.", physics->getNamespace());
         }
     }
 }
@@ -901,9 +956,8 @@ void SceneLoader::loadReferencedFiles()
     for (; iter != _propertiesFromFile.end(); iter++)
     {
         Properties* p = Properties::create(iter->first.c_str());
-        GP_ASSERT(p);
         if (p == NULL)
-            GP_WARN("Failed to load referenced file: %s", iter->first.c_str());
+            GP_ERROR("Failed to load referenced file '%s'.", iter->first.c_str());
 
         iter->second = p;
     }
@@ -911,6 +965,10 @@ void SceneLoader::loadReferencedFiles()
 
 PhysicsConstraint* SceneLoader::loadSocketConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
+    GP_ASSERT(rbA);
+    GP_ASSERT(constraint);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+
     PhysicsSocketConstraint* physicsConstraint = NULL;
     Vector3 toA;
     bool offsetSpecified = constraint->getVector3("translationOffsetA", &toA);
@@ -939,9 +997,13 @@ PhysicsConstraint* SceneLoader::loadSocketConstraint(const Properties* constrain
 
 PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
+    GP_ASSERT(rbA);
+    GP_ASSERT(constraint);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+
     if (!rbB)
     {
-        GP_WARN("Spring constraints require two rigid bodies.");
+        GP_ERROR("Spring constraints require two rigid bodies.");
         return NULL;
     }
 
@@ -963,6 +1025,7 @@ PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constrain
     {
         physicsConstraint = Game::getInstance()->getPhysicsController()->createSpringConstraint(rbA, rbB);
     }
+    GP_ASSERT(physicsConstraint);
 
     // Set the optional parameters that were specified.
     Vector3 v;
@@ -1005,7 +1068,10 @@ PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constrain
 void SceneLoader::splitURL(const char* url, std::string* file, std::string* id)
 {
     if (!url)
+    {
+        // This is allowed since many scene node properties do not use the URL.
         return;
+    }
 
     std::string urlString = url;
 

+ 36 - 13
gameplay/src/SpriteBatch.cpp

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

+ 5 - 4
gameplay/src/Technique.cpp

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

+ 544 - 82
gameplay/src/Texture.cpp

@@ -3,12 +3,48 @@
 #include "Texture.h"
 #include "FileSystem.h"
 
+// PVRTC (GL_IMG_texture_compression_pvrtc) : Imagination based gpus
+#ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#endif
+
+// S3TC/DXT (GL_EXT_texture_compression_s3tc) : Most desktop/console gpus
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+
+// ATC (GL_AMD_compressed_ATC_texture) : Qualcomm/Adreno based gpus
+#ifndef ATC_RGB_AMD
+#define ATC_RGB_AMD 0x8C92
+#endif
+#ifndef ATC_RGBA_EXPLICIT_ALPHA_AMD
+#define ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93
+#endif
+#ifndef ATC_RGBA_INTERPOLATED_ALPHA_AMD
+#define ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
+#endif
+
 namespace gameplay
 {
 
 static std::vector<Texture*> __textureCache;
 
-Texture::Texture() : _handle(0), _mipmapped(false), _cached(false)
+Texture::Texture() : _handle(0), _mipmapped(false), _cached(false), _compressed(false)
 {
 }
 
@@ -20,7 +56,7 @@ Texture::~Texture()
 {
     if (_handle)
     {
-        glDeleteTextures(1, &_handle);
+        GL_ASSERT( glDeleteTextures(1, &_handle) );
         _handle = 0;
     }
 
@@ -37,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 
@@ -60,7 +99,7 @@ Texture* Texture::create(const char* path, bool generateMipmaps)
     Texture* texture = NULL;
 
     // Filter loading based on file extension.
-    const char* ext = strrchr(path, '.');
+    const char* ext = strrchr(FileSystem::resolvePath(path), '.');
     if (ext)
     {
         switch (strlen(ext))
@@ -75,12 +114,13 @@ Texture* Texture::create(const char* path, bool generateMipmaps)
             }
             else if (tolower(ext[1]) == 'p' && tolower(ext[2]) == 'v' && tolower(ext[3]) == 'r')
             {
-#ifdef USE_PVRTC
-                // PowerVR Compressed Texture RGBA
+                // PowerVR Compressed Texture RGBA.
                 texture = createCompressedPVRTC(path);
-#else
-                texture = NULL; // Cannot handle PVRTC if not supported on platform
-#endif
+            }
+            else if (tolower(ext[1]) == 'd' && tolower(ext[2]) == 'd' && tolower(ext[3]) == 's')
+            {
+                // DDS file format (DXT/S3TC) compressed textures
+                texture = createCompressedDDS(path);
             }
             break;
         }
@@ -97,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)
@@ -131,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();
@@ -147,18 +190,240 @@ Texture* Texture::create(Format format, unsigned int width, unsigned int height,
     return texture;
 }
 
-#ifdef USE_PVRTC
+// Computes the size of a PVRTC data chunk for a mipmap level of the given size.
+unsigned int computePVRTCDataSize(int width, int height, int bpp)
+{
+    int blockSize;
+    int widthBlocks;
+    int heightBlocks;
+
+    if (bpp == 4)
+    {
+        blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
+        widthBlocks = std::max(width >> 2, 2);
+        heightBlocks = std::max(height >> 2, 2);
+    }
+    else
+    {
+        blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
+        widthBlocks = std::max(width >> 3, 2);
+        heightBlocks = std::max(height >> 2, 2);
+    }
+
+    return widthBlocks * heightBlocks * ((blockSize  * bpp) >> 3);
+}
+
 Texture* Texture::createCompressedPVRTC(const char* path)
 {
-    char PVRTCIdentifier[] = "PVR!";
+    FILE* file = FileSystem::openFile(path, "rb");
+    if (file == NULL)
+    {
+        GP_ERROR("Failed to load file '%s'.", path);
+        return NULL;
+    }
 
-    enum
+    // Read first 4 bytes to determine PVRTC format.
+    unsigned int read;
+    unsigned int version;
+    read = fread(&version, sizeof(unsigned int), 1, file);
+    if (read != 1)
     {
-        PVRTC_2 = 24,
-        PVRTC_4
-    };
+        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.
+    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.
+    GLsizei width, height;
+    GLenum format;
+    GLubyte* data = NULL;
+    unsigned int mipMapCount;
+
+    if (version == 0x03525650)
+    {
+        // 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);
+    }
+    if (data == 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);
+
+    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;
+        unsigned int flags;
+        unsigned int pixelFormat[2];
+        unsigned int colorSpace;
+        unsigned int channelType;
+        unsigned int height;
+        unsigned int width;
+        unsigned int depth;
+        unsigned int surfaceCount;
+        unsigned int faceCount;
+        unsigned int mipMapCount;
+        unsigned int metaDataSize;
+    };
+
+    unsigned int read;
+
+    // Read header data.
+    pvrtc_file_header header;
+    read = fread(&header, sizeof(pvrtc_file_header), 1, file);
+    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 '%s'. (MSB == %d != 0)", path, header.pixelFormat[1]);
+        return NULL;
+    }
+
+    int bpp;
+    switch (header.pixelFormat[0])
+    {
+    case 0:
+        *format = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+        bpp = 2;
+        break;
+    case 1:
+        *format = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+        bpp = 2;
+        break;
+    case 2:
+        *format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+        bpp = 4;
+        break;
+    case 3:
+        *format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+        bpp = 4;
+        break;
+    default:
+        // Unsupported format.
+        GP_ERROR("Unsupported pixel format value (%d) in PVR file '%s'.", header.pixelFormat[0], path);
+        return NULL;
+    }
+
+    *width = (GLsizei)header.width;
+    *height = (GLsizei)header.height;
+    *mipMapCount = header.mipMapCount;
+
+    // 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;
+    int h = *height;
+    unsigned int dataSize = 0;
+    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);
+    }
+
+    // Read data.
+    GLubyte* data = new GLubyte[dataSize];
+    read = fread(data, 1, dataSize, file);
+    if (read != dataSize)
+    {
+        GP_ERROR("Failed to read texture data from PVR file '%s'.", path);
+        return NULL;
+    }
+
+    return data;
+}
+
+GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
+{
+    char PVRTCIdentifier[] = "PVR!";
+
+    struct pvrtc_file_header_legacy
     {
         unsigned int size;                  // size of the structure
         unsigned int height;                // height of surface to be created
@@ -173,114 +438,304 @@ Texture* Texture::createCompressedPVRTC(const char* path)
         unsigned int alphaBitMask;          // mask for alpha channel
         unsigned int pvrtcTag;              // magic number identifying pvrtc file
         unsigned int surfaceCount;          // number of surfaces present in the pvrtc
-    } ;
-
-    FILE* file = FileSystem::openFile(path, "rb");
-    if (file == NULL)
-    {
-        GP_ERROR("Failed to load file: %s", path);
-        return NULL;
-    }
+    };
 
-    // Read the file header
-    unsigned int size = sizeof(pvrtc_file_header);
-    pvrtc_file_header 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);
-        fclose(file);
+        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);
-        fclose(file);
+        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
-    GLenum format;
-    unsigned int formatFlags = header.formatflags & 0xff;
-    if (formatFlags == PVRTC_4)
+    // Format flags for GLenum format.
+    if (header.bpp == 4)
     {
-        format = header.alphaBitMask ? COMPRESSED_RGBA_PVRTC_4BPP : COMPRESSED_RGB_PVRTC_4BPP;
+        *format = header.alphaBitMask ? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
     }
-    else if (formatFlags == PVRTC_2)
+    else if (header.bpp == 2)
     {
-        format = header.alphaBitMask ? COMPRESSED_RGBA_PVRTC_2BPP : COMPRESSED_RGB_PVRTC_2BPP;
+        *format = header.alphaBitMask ? GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
     }
     else
     {
-        GP_ERROR("Invalid PVRTC compressed texture format flags for file: %s", path);
-        fclose(file);
+        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;
     }
 
-    unsigned char* data = new unsigned char[header.dataSize];
+    *width = (GLsizei)header.width;
+    *height = (GLsizei)header.height;
+    *mipMapCount = header.mipmapCount + 1; // +1 because mipmapCount does not include the base level
+
+    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);
-        fclose(file);
         return NULL;
     }
-    // Close file
-    fclose(file);
 
-    // Load our texture.
+    return data;
+}
+
+Texture* Texture::createCompressedDDS(const char* path)
+{
+    GP_ASSERT(path);
+
+    // DDS file structures.
+    struct dds_pixel_format
+    {
+        unsigned int dwSize;
+        unsigned int dwFlags;
+        unsigned int dwFourCC;
+        unsigned int dwRGBBitCount;
+        unsigned int dwRBitMask;
+        unsigned int dwGBitMask;
+        unsigned int dwBBitMask;
+        unsigned int dwABitMask;
+    };
+
+    struct dds_header
+    {
+        unsigned int     dwSize;
+        unsigned int     dwFlags;
+        unsigned int     dwHeight;
+        unsigned int     dwWidth;
+        unsigned int     dwPitchOrLinearSize;
+        unsigned int     dwDepth;
+        unsigned int     dwMipMapCount;
+        unsigned int     dwReserved1[11];
+        dds_pixel_format ddspf;
+        unsigned int     dwCaps;
+        unsigned int     dwCaps2;
+        unsigned int     dwCaps3;
+        unsigned int     dwCaps4;
+        unsigned int     dwReserved2;
+    };
+
+    struct dds_mip_level
+    {
+        GLubyte* data;
+        GLsizei width;
+        GLsizei height;
+        GLsizei size;
+    };
+
+    Texture* texture = NULL;
+
+    // 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.
+    char code[4];
+    if (fread(code, 1, 4, fp) != 4 || strncmp(code, "DDS ", 4) != 0)
+    {
+        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.
+    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).
+        header.dwMipMapCount = 1;
+    }
+
+    // Allocate mip level structures.
+    dds_mip_level* mipLevels = new dds_mip_level[header.dwMipMapCount];
+    memset(mipLevels, 0, sizeof(dds_mip_level) * header.dwMipMapCount);
+
+    GLenum format, internalFormat;
+    bool compressed = false;
+    GLsizei width = header.dwWidth;
+    GLsizei height = header.dwHeight;
+    int bytesPerBlock;
+
+    if (header.ddspf.dwFlags & 0x4/*DDPF_FOURCC*/)
+    {
+        compressed = true;
+
+        // Compressed.
+        switch (header.ddspf.dwFourCC)
+        {
+        case ('D'|('X'<<8)|('T'<<16)|('1'<<24)):
+            format = internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+            bytesPerBlock = 8;
+            break;
+        case ('D'|('X'<<8)|('T'<<16)|('3'<<24)):
+            format = internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+            bytesPerBlock = 16;
+            break;
+        case ('D'|('X'<<8)|('T'<<16)|('5'<<24)):
+            format = internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+            bytesPerBlock = 16;
+            break;
+        case ('A'|('T'<<8)|('C'<<16)|(' '<<24)):
+            format = internalFormat = ATC_RGB_AMD;
+            bytesPerBlock = 8;
+            break;
+        case ('A'|('T'<<8)|('C'<<16)|('A'<<24)):
+            format = internalFormat = ATC_RGBA_EXPLICIT_ALPHA_AMD;
+            bytesPerBlock = 16;
+            break;
+        case ('A'|('T'<<8)|('C'<<16)|('I'<<24)):
+            format = internalFormat = ATC_RGBA_INTERPOLATED_ALPHA_AMD;
+            bytesPerBlock = 16;
+            break;
+        default:
+            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;
+        }
+
+        for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
+        {
+            mipLevels[i].width = width;
+            mipLevels[i].height = height;
+            mipLevels[i].size =  std::max(1, (width+3) >> 2) * std::max(1, (height+3) >> 2) * bytesPerBlock;
+            mipLevels[i].data = new GLubyte[mipLevels[i].size];
+
+            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);
+                
+                // 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);
+            height = std::max(1, height >> 1);
+        }
+    }
+    else if (header.ddspf.dwFlags == 0x40/*DDPF_RGB*/)
+    {
+        // RGB (uncompressed)
+        // Note: Use GL_BGR as internal format to flip bytes.
+        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.
+        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.
+        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.
     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.mipmapCount > 0 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) );
+    GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, header.dwMipMapCount > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) );
 
-    Texture* texture = new Texture();
+    // Create gameplay texture.
+    texture = new Texture();
     texture->_handle = textureId;
-    texture->_width = header.width;
-    texture->_height = header.height;
-
-    // Load the data for each level
-    unsigned int width = header.width;
-    unsigned int height = header.height;
-    unsigned int blockSize = 0;
-    unsigned int widthBlocks = 0;
-    unsigned int heightBlocks = 0;
-    unsigned int bpp = 0;
-    unsigned int dataSize = 0;
-    unsigned char* dataOffset = data;
+    texture->_width = header.dwWidth;
+    texture->_height = header.dwHeight;
+    texture->_compressed = compressed;
+    texture->_mipmapped = header.dwMipMapCount > 1;
 
-    for (unsigned int level = 0; level <= header.mipmapCount; level++)
+    // Load texture data.
+    for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
     {
-        if (formatFlags == PVRTC_4)
+        if (compressed)
         {
-            dataSize = ( max((int)width, 8) * max((int)height, 8) * 4 + 7) / 8;
+            GL_ASSERT( glCompressedTexImage2D(GL_TEXTURE_2D, i, format, mipLevels[i].width, mipLevels[i].height, 0, mipLevels[i].size, mipLevels[i].data) );
         }
         else
         {
-            dataSize = ( max((int)width, 16) * max((int)height, 8) * 2 + 7) / 8;
+            GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, mipLevels[i].width, mipLevels[i].height, 0, format, GL_UNSIGNED_INT, mipLevels[i].data) );
         }
-
-        GL_ASSERT( glCompressedTexImage2D(GL_TEXTURE_2D, level, (GLenum)format, width, height, 0, dataSize, dataOffset) );
-
-        dataOffset += dataSize;
-        width = max((int)width >> 1, 1);
-        height = max((int)height >> 1, 1);
     }
-
-    SAFE_DELETE_ARRAY(data);
-
+    
     return texture;
 }
-#endif
-    
+
 unsigned int Texture::getWidth() const
 {
     return _width;
@@ -335,9 +790,15 @@ bool Texture::isMipmapped() const
     return _mipmapped;
 }
 
+bool Texture::isCompressed() const
+{
+    return _compressed;
+}
+
 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;
 }
 
@@ -348,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);
 }
@@ -379,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) );

+ 15 - 10
gameplay/src/Texture.h

@@ -29,13 +29,7 @@ public:
         RGB     = GL_RGB,
         RGBA    = GL_RGBA,
         ALPHA   = GL_ALPHA,
-        DEPTH   = GL_DEPTH_COMPONENT,
-#ifdef USE_PVRTC
-        COMPRESSED_RGB_PVRTC_4BPP = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG,
-        COMPRESSED_RGBA_PVRTC_4BPP = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG,
-        COMPRESSED_RGB_PVRTC_2BPP = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG,
-        COMPRESSED_RGBA_PVRTC_2BPP = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
-#endif
+        DEPTH   = GL_DEPTH_COMPONENT
     };
 
     /**
@@ -134,6 +128,9 @@ public:
     /**
      * Creates a texture from the given image resource.
      *
+     * Note that for textures that include mipmap data in the source data (such as most compressed textures),
+     * the generateMipmaps flags should NOT be set to true.
+     *
      * @param path The image resource path.
      * @param generateMipmaps true to auto-generate a full mipmap chain, false otherwise.
      * 
@@ -189,6 +186,11 @@ public:
      */
     bool isMipmapped() const;
 
+    /**
+     * Determines if this texture is a compressed teture.
+     */
+    bool isCompressed() const;
+
     /**
      * Returns the texture handle.
      *
@@ -213,16 +215,19 @@ private:
      */
     virtual ~Texture();
 
-#ifdef USE_PVRTC
     static Texture* createCompressedPVRTC(const char* path);
-#endif
-    
+    static Texture* createCompressedDDS(const char* path);
+
+    static GLubyte* readCompressedPVRTC(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
+    static GLubyte* readCompressedPVRTCLegacy(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
+
     std::string _path;
     TextureHandle _handle;
     unsigned int _width;
     unsigned int _height;
     bool _mipmapped;
     bool _cached;
+    bool _compressed;
 };
 
 }

+ 11 - 0
gameplay/src/Theme.cpp

@@ -271,6 +271,8 @@ Theme* Theme::create(const char* url)
                     if (!font)
                     {
                         font = normal->getFont();
+                        if (font)
+                            font->addRef();
                     }
 
                     unsigned int fontSize;
@@ -349,7 +351,10 @@ Theme* Theme::create(const char* url)
                         focus->setOpacity(opacity);
 
                         if (font)
+                        {
                             theme->_fonts.insert(font);
+                            font->release();
+                        }
                     }
                     else if (strcmp(innerSpacename, "stateActive") == 0)
                     {
@@ -366,7 +371,10 @@ Theme* Theme::create(const char* url)
                         active->setOpacity(opacity);
 
                         if (font)
+                        {
                             theme->_fonts.insert(font);
+                            font->release();
+                        }
                     }
                     else if (strcmp(innerSpacename, "stateDisabled") == 0)
                     {
@@ -383,7 +391,10 @@ Theme* Theme::create(const char* url)
                         disabled->setOpacity(opacity);
 
                         if (font)
+                        {
                             theme->_fonts.insert(font);
+                            font->release();
+                        }
                     }
                 }
 

+ 16 - 8
gameplay/src/Transform.cpp

@@ -59,6 +59,7 @@ void Transform::resumeTransformChanged()
         for (unsigned int i = 0; i < transformCount; i++)
         {
             Transform* t = _transformsChanged.at(i);
+            GP_ASSERT(t);
             t->transformChanged();
         }
 
@@ -68,6 +69,7 @@ void Transform::resumeTransformChanged()
         for (unsigned int i = 0; i < transformCount; i++)
         {
             Transform* t = _transformsChanged.at(i);
+            GP_ASSERT(t);
             t->_matrixDirtyBits &= ~DIRTY_NOTIFY;
         }
 
@@ -130,6 +132,7 @@ const Vector3& Transform::getScale() const
 
 void Transform::getScale(Vector3* scale) const
 {
+    GP_ASSERT(scale);
     scale->set(_scale);
 }
 
@@ -156,14 +159,12 @@ const Quaternion& Transform::getRotation() const
 void Transform::getRotation(Quaternion* rotation) const
 {
     GP_ASSERT(rotation);
-
     rotation->set(_rotation);
 }
 
 void Transform::getRotation(Matrix* rotation) const
 {
     GP_ASSERT(rotation);
-
     Matrix::createRotation(_rotation, rotation);
 }
 
@@ -180,6 +181,7 @@ const Vector3& Transform::getTranslation() const
 
 void Transform::getTranslation(Vector3* translation) const
 {
+    GP_ASSERT(translation);
     translation->set(_translation);
 }
 
@@ -573,24 +575,18 @@ void Transform::translateForward(float amount)
 
 void Transform::transformPoint(Vector3* point)
 {
-    GP_ASSERT(point);
-
     getMatrix();
     _matrix.transformPoint(point);
 }
 
 void Transform::transformPoint(const Vector3& point, Vector3* dst)
 {
-    GP_ASSERT(dst);
-
     getMatrix();
     _matrix.transformPoint(point, dst);
 }
 
 void Transform::transformVector(Vector3* normal)
 {
-    GP_ASSERT(normal);
-
     getMatrix();
     _matrix.transformVector(normal);
 }
@@ -635,6 +631,8 @@ unsigned int Transform::getAnimationPropertyComponentCount(int propertyId) const
 
 void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 {
+    GP_ASSERT(value);
+
     switch (propertyId)
     {
         case ANIMATE_SCALE_UNIT:
@@ -702,6 +700,7 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 
 void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
+    GP_ASSERT(value);
     GP_ASSERT(blendWeight >= 0.0f && blendWeight <= 1.0f);
 
     switch (propertyId)
@@ -798,12 +797,15 @@ bool Transform::isDirty(char matrixDirtyBits) const
 
 void Transform::suspendTransformChange(Transform* transform)
 {
+    GP_ASSERT(transform);
     transform->_matrixDirtyBits |= DIRTY_NOTIFY;
     _transformsChanged.push_back(transform);
 }
 
 void Transform::addListener(Transform::Listener* listener, long cookie)
 {
+    GP_ASSERT(listener);
+
     if (_listeners == NULL)
         _listeners = new std::list<TransformListener>();
 
@@ -815,6 +817,8 @@ void Transform::addListener(Transform::Listener* listener, long cookie)
 
 void Transform::removeListener(Transform::Listener* listener)
 {
+    GP_ASSERT(listener);
+
     if (_listeners)
     {
         for (std::list<TransformListener>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
@@ -835,6 +839,7 @@ void Transform::transformChanged()
         for (std::list<TransformListener>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
         {
             TransformListener& l = *itr;
+            GP_ASSERT(l.listener);
             l.listener->transformChanged(this, l.cookie);
         }
     }
@@ -842,6 +847,8 @@ void Transform::transformChanged()
 
 void Transform::cloneInto(Transform* transform, NodeCloneContext &context) const
 {
+    GP_ASSERT(transform);
+
     AnimationTarget::cloneInto(transform, context);
     transform->_scale.set(_scale);
     transform->_rotation.set(_rotation);
@@ -850,6 +857,7 @@ void Transform::cloneInto(Transform* transform, NodeCloneContext &context) const
 
 void Transform::applyAnimationValueRotation(AnimationValue* value, unsigned int index, float blendWeight)
 {
+    GP_ASSERT(value);
     Quaternion::slerp(_rotation.x, _rotation.y, _rotation.z, _rotation.w, value->getFloat(index), value->getFloat(index + 1), value->getFloat(index + 2), value->getFloat(index + 3), blendWeight, 
         &_rotation.x, &_rotation.y, &_rotation.z, &_rotation.w);
     dirty(DIRTY_ROTATION);

+ 10 - 10
gameplay/src/Vector2.cpp

@@ -89,38 +89,38 @@ void Vector2::add(const Vector2& v1, const Vector2& v2, Vector2* dst)
 
 void Vector2::clamp(const Vector2& min, const Vector2& max)
 {
-    GP_ASSERT(!( min.x > max.x || min.y > max.y ));
+    GP_ASSERT(!(min.x > max.x || min.y > max.y ));
 
     // Clamp the x value.
-    if ( x < min.x )
+    if (x < min.x)
         x = min.x;
-    if ( x > max.x )
+    if (x > max.x)
         x = max.x;
 
     // Clamp the y value.
-    if ( y < min.y )
+    if (y < min.y)
         y = min.y;
-    if ( y > max.y )
+    if (y > max.y)
         y = max.y;
 }
 
 void Vector2::clamp(const Vector2& v, const Vector2& min, const Vector2& max, Vector2* dst)
 {
     GP_ASSERT(dst);
-    GP_ASSERT(!( min.x > max.x || min.y > max.y ));
+    GP_ASSERT(!(min.x > max.x || min.y > max.y ));
 
     // Clamp the x value.
     dst->x = v.x;
-    if ( dst->x < min.x )
+    if (dst->x < min.x)
         dst->x = min.x;
-    if ( dst->x > max.x )
+    if (dst->x > max.x)
         dst->x = max.x;
 
     // Clamp the y value.
     dst->y = v.y;
-    if ( dst->y < min.y )
+    if (dst->y < min.y)
         dst->y = min.y;
-    if ( dst->y > max.y )
+    if (dst->y > max.y)
         dst->y = max.y;
 }
 

+ 14 - 14
gameplay/src/Vector3.cpp

@@ -115,51 +115,51 @@ void Vector3::add(const Vector3& v1, const Vector3& v2, Vector3* dst)
 
 void Vector3::clamp(const Vector3& min, const Vector3& max)
 {
-    GP_ASSERT(!( min.x > max.x || min.y > max.y || min.z > max.z));
+    GP_ASSERT(!(min.x > max.x || min.y > max.y || min.z > max.z));
 
     // Clamp the x value.
-    if ( x < min.x )
+    if (x < min.x)
         x = min.x;
-    if ( x > max.x )
+    if (x > max.x)
         x = max.x;
 
     // Clamp the y value.
-    if ( y < min.y )
+    if (y < min.y)
         y = min.y;
-    if ( y > max.y )
+    if (y > max.y)
         y = max.y;
 
     // Clamp the z value.
-    if ( z < min.z )
+    if (z < min.z)
         z = min.z;
-    if ( z > max.z )
+    if (z > max.z)
         z = max.z;
 }
 
 void Vector3::clamp(const Vector3& v, const Vector3& min, const Vector3& max, Vector3* dst)
 {
     GP_ASSERT(dst);
-    GP_ASSERT(!( min.x > max.x || min.y > max.y || min.z > max.z));
+    GP_ASSERT(!(min.x > max.x || min.y > max.y || min.z > max.z));
 
     // Clamp the x value.
     dst->x = v.x;
-    if ( dst->x < min.x )
+    if (dst->x < min.x)
         dst->x = min.x;
-    if ( dst->x > max.x )
+    if (dst->x > max.x)
         dst->x = max.x;
 
     // Clamp the y value.
     dst->y = v.y;
-    if ( dst->y < min.y )
+    if (dst->y < min.y)
         dst->y = min.y;
-    if ( dst->y > max.y )
+    if (dst->y > max.y)
         dst->y = max.y;
 
     // Clamp the z value.
     dst->z = v.z;
-    if ( dst->z < min.z )
+    if (dst->z < min.z)
         dst->z = min.z;
-    if ( dst->z > max.z )
+    if (dst->z > max.z)
         dst->z = max.z;
 }
 

+ 18 - 18
gameplay/src/Vector4.cpp

@@ -123,64 +123,64 @@ void Vector4::add(const Vector4& v1, const Vector4& v2, Vector4* dst)
 
 void Vector4::clamp(const Vector4& min, const Vector4& max)
 {
-    GP_ASSERT(!( min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w));
+    GP_ASSERT(!(min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w));
 
     // Clamp the x value.
-    if ( x < min.x )
+    if (x < min.x)
         x = min.x;
-    if ( x > max.x )
+    if (x > max.x)
         x = max.x;
 
     // Clamp the y value.
-    if ( y < min.y )
+    if (y < min.y)
         y = min.y;
-    if ( y > max.y )
+    if (y > max.y)
         y = max.y;
 
     // Clamp the z value.
-    if ( z < min.z )
+    if (z < min.z)
         z = min.z;
-    if ( z > max.z )
+    if (z > max.z)
         z = max.z;
 
     // Clamp the z value.
-    if ( w < min.w )
+    if (w < min.w)
         w = min.w;
-    if ( w > max.w )
+    if (w > max.w)
         w = max.w;
 }
 
 void Vector4::clamp(const Vector4& v, const Vector4& min, const Vector4& max, Vector4* dst)
 {
     GP_ASSERT(dst);
-    GP_ASSERT(!( min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w));
+    GP_ASSERT(!(min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w));
 
     // Clamp the x value.
     dst->x = v.x;
-    if ( dst->x < min.x )
+    if (dst->x < min.x)
         dst->x = min.x;
-    if ( dst->x > max.x )
+    if (dst->x > max.x)
         dst->x = max.x;
 
     // Clamp the y value.
     dst->y = v.y;
-    if ( dst->y < min.y )
+    if (dst->y < min.y)
         dst->y = min.y;
-    if ( dst->y > max.y )
+    if (dst->y > max.y)
         dst->y = max.y;
 
     // Clamp the z value.
     dst->z = v.z;
-    if ( dst->z < min.z )
+    if (dst->z < min.z)
         dst->z = min.z;
-    if ( dst->z > max.z )
+    if (dst->z > max.z)
         dst->z = max.z;
 
     // Clamp the w value.
     dst->w = v.w;
-    if ( dst->w < min.w )
+    if (dst->w < min.w)
         dst->w = min.w;
-    if ( dst->w > max.w )
+    if (dst->w > max.w)
         dst->w = max.w;
 }
 

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

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác