Selaa lähdekoodia

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

Conflicts:
	gameplay/src/Matrix.cpp
Kieran Cunney 13 vuotta sitten
vanhempi
sitoutus
cfd3c580e8
90 muutettua tiedostoa jossa 3647 lisäystä ja 1565 poistoa
  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. 19 7
      gameplay/src/Control.cpp
  11. 4 4
      gameplay/src/Control.h
  12. 10 10
      gameplay/src/DebugNew.cpp
  13. 10 7
      gameplay/src/DepthStencilTarget.cpp
  14. 31 10
      gameplay/src/Effect.cpp
  15. 148 27
      gameplay/src/FileSystem.cpp
  16. 57 0
      gameplay/src/FileSystem.h
  17. 348 408
      gameplay/src/Font.cpp
  18. 61 18
      gameplay/src/Font.h
  19. 207 31
      gameplay/src/Form.cpp
  20. 29 0
      gameplay/src/Form.h
  21. 19 9
      gameplay/src/FrameBuffer.cpp
  22. 1 0
      gameplay/src/Frustum.cpp
  23. 59 15
      gameplay/src/Game.cpp
  24. 17 4
      gameplay/src/Game.h
  25. 1 0
      gameplay/src/Game.inl
  26. 32 8
      gameplay/src/Image.cpp
  27. 2 0
      gameplay/src/Joint.cpp
  28. 1 0
      gameplay/src/Layout.h
  29. 32 4
      gameplay/src/Light.cpp
  30. 66 26
      gameplay/src/Material.cpp
  31. 52 11
      gameplay/src/MaterialParameter.cpp
  32. 46 29
      gameplay/src/Matrix.cpp
  33. 18 3
      gameplay/src/Mesh.cpp
  34. 25 9
      gameplay/src/MeshBatch.cpp
  35. 10 5
      gameplay/src/MeshBatch.inl
  36. 10 0
      gameplay/src/MeshPart.cpp
  37. 6 2
      gameplay/src/MeshSkin.cpp
  38. 48 7
      gameplay/src/Model.cpp
  39. 24 20
      gameplay/src/Node.cpp
  40. 62 29
      gameplay/src/ParticleEmitter.cpp
  41. 7 6
      gameplay/src/Pass.cpp
  42. 70 34
      gameplay/src/PhysicsCharacter.cpp
  43. 10 0
      gameplay/src/PhysicsCollisionObject.cpp
  44. 33 13
      gameplay/src/PhysicsCollisionShape.cpp
  45. 14 2
      gameplay/src/PhysicsConstraint.cpp
  46. 4 0
      gameplay/src/PhysicsConstraint.inl
  47. 139 54
      gameplay/src/PhysicsController.cpp
  48. 7 0
      gameplay/src/PhysicsGenericConstraint.cpp
  49. 12 0
      gameplay/src/PhysicsGenericConstraint.inl
  50. 10 3
      gameplay/src/PhysicsGhostObject.cpp
  51. 5 0
      gameplay/src/PhysicsHingeConstraint.cpp
  52. 5 0
      gameplay/src/PhysicsMotionState.cpp
  53. 34 8
      gameplay/src/PhysicsRigidBody.cpp
  54. 16 0
      gameplay/src/PhysicsRigidBody.inl
  55. 6 0
      gameplay/src/PhysicsSocketConstraint.cpp
  56. 8 0
      gameplay/src/PhysicsSpringConstraint.cpp
  57. 5 1
      gameplay/src/Plane.cpp
  58. 34 15
      gameplay/src/PlatformAndroid.cpp
  59. 111 34
      gameplay/src/PlatformMacOSX.mm
  60. 31 23
      gameplay/src/PlatformQNX.cpp
  61. 143 62
      gameplay/src/PlatformWin32.cpp
  62. 4 2
      gameplay/src/PlatformiOS.mm
  63. 57 28
      gameplay/src/Properties.cpp
  64. 13 6
      gameplay/src/Properties.h
  65. 10 4
      gameplay/src/Quaternion.cpp
  66. 12 10
      gameplay/src/Ray.cpp
  67. 2 0
      gameplay/src/Rectangle.cpp
  68. 15 6
      gameplay/src/Ref.cpp
  69. 99 53
      gameplay/src/RenderState.cpp
  70. 7 3
      gameplay/src/RenderTarget.cpp
  71. 16 9
      gameplay/src/Scene.cpp
  72. 125 59
      gameplay/src/SceneLoader.cpp
  73. 4 4
      gameplay/src/ScreenDisplayer.h
  74. 124 59
      gameplay/src/SpriteBatch.cpp
  75. 68 53
      gameplay/src/SpriteBatch.h
  76. 5 4
      gameplay/src/Technique.cpp
  77. 544 82
      gameplay/src/Texture.cpp
  78. 15 10
      gameplay/src/Texture.h
  79. 11 0
      gameplay/src/Theme.cpp
  80. 16 8
      gameplay/src/Transform.cpp
  81. 10 10
      gameplay/src/Vector2.cpp
  82. 14 14
      gameplay/src/Vector3.cpp
  83. 18 18
      gameplay/src/Vector4.cpp
  84. 13 6
      gameplay/src/VertexAttributeBinding.cpp
  85. 2 1
      gameplay/src/VertexFormat.cpp
  86. 1 1
      gameplay/src/gameplay-main-android.cpp
  87. 1 1
      gameplay/src/gameplay-main-ios.mm
  88. 1 1
      gameplay/src/gameplay-main-macosx.mm
  89. 1 1
      gameplay/src/gameplay-main-qnx.cpp
  90. 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)
 include $(CLEAR_VARS)
 LOCAL_MODULE    := libgameplay
 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_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
 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");
     const char* propertyIdStr = animationProperties->getString("property");
     if (propertyIdStr == NULL)
     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;
         return NULL;
     }
     }
     
     
@@ -138,28 +138,28 @@ Animation* AnimationTarget::createAnimation(const char* id, Properties* animatio
     unsigned int keyCount = animationProperties->getInt("keyCount");
     unsigned int keyCount = animationProperties->getInt("keyCount");
     if (keyCount == 0)
     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;
         return NULL;
     }
     }
 
 
     const char* keyTimesStr = animationProperties->getString("keyTimes");
     const char* keyTimesStr = animationProperties->getString("keyTimes");
     if (keyTimesStr == NULL)
     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;
         return NULL;
     }
     }
     
     
     const char* keyValuesStr = animationProperties->getString("keyValues");
     const char* keyValuesStr = animationProperties->getString("keyValues");
     if (keyValuesStr == NULL)
     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;
         return NULL;
     }
     }
     
     
     const char* curveStr = animationProperties->getString("curve");
     const char* curveStr = animationProperties->getString("curve");
     if (curveStr == NULL)
     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;
         return NULL;
     }
     }
     
     

+ 2 - 4
gameplay/src/AudioSource.cpp

@@ -32,16 +32,14 @@ AudioSource::~AudioSource()
 
 
 AudioSource* AudioSource::create(const char* url)
 AudioSource* AudioSource::create(const char* url)
 {
 {
-    GP_ASSERT(url);
-
     // Load from a .audio file.
     // Load from a .audio file.
     std::string pathStr = url;
     std::string pathStr = url;
     if (pathStr.find(".audio") != pathStr.npos)
     if (pathStr.find(".audio") != pathStr.npos)
     {
     {
         Properties* properties = Properties::create(url);
         Properties* properties = Properties::create(url);
-        GP_ASSERT(properties);
         if (properties == NULL)
         if (properties == NULL)
         {
         {
+            GP_ERROR("Failed to create audio source from .audio file.");
             return NULL;
             return NULL;
         }
         }
 
 
@@ -75,7 +73,7 @@ AudioSource* AudioSource::create(Properties* properties)
     GP_ASSERT(properties);
     GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "audio") == 0))
     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;
         return NULL;
     }
     }
 
 

+ 20 - 13
gameplay/src/Base.h

@@ -51,6 +51,13 @@ namespace gameplay
 extern void printError(const char* format, ...);
 extern void printError(const char* format, ...);
 }
 }
 
 
+// Current function macro.
+#ifdef WIN32
+#define __current__func__ __FUNCTION__
+#else
+#define __current__func__ __func__
+#endif
+
 // Assert macros.
 // Assert macros.
 #ifdef _DEBUG
 #ifdef _DEBUG
 #ifdef WIN32
 #ifdef WIN32
@@ -58,11 +65,10 @@ extern void printError(const char* format, ...);
 #else
 #else
 #define GP_FORCE_ASSERTION_FAILURE do { assert(0); } while (0)
 #define GP_FORCE_ASSERTION_FAILURE do { assert(0); } while (0)
 #endif
 #endif
-
 #define GP_ASSERT(expression) do { \
 #define GP_ASSERT(expression) do { \
     if (!(expression)) \
     if (!(expression)) \
     { \
     { \
-        printError("%s -- Assertion \'" #expression "\' failed.\n", __FUNCTION__); \
+        printError("%s -- Assertion '" #expression "' failed.\n", __current__func__); \
         GP_FORCE_ASSERTION_FAILURE; \
         GP_FORCE_ASSERTION_FAILURE; \
     } } while (0)
     } } while (0)
 #else
 #else
@@ -71,19 +77,23 @@ extern void printError(const char* format, ...);
 #endif
 #endif
 
 
 // Error macro.
 // Error macro.
+#ifdef GP_ERRORS_AS_WARNINGS
+#define GP_ERROR GP_WARN
+#else
 #define GP_ERROR(...) do \
 #define GP_ERROR(...) do \
     { \
     { \
-        printError("%s -- ", __FUNCTION__); \
+        printError("%s -- ", __current__func__); \
         printError(__VA_ARGS__); \
         printError(__VA_ARGS__); \
         printError("\n"); \
         printError("\n"); \
         GP_FORCE_ASSERTION_FAILURE; \
         GP_FORCE_ASSERTION_FAILURE; \
         std::exit(-1); \
         std::exit(-1); \
     } while (0)
     } while (0)
+#endif
 
 
 // Warning macro.
 // Warning macro.
 #define GP_WARN(...) do \
 #define GP_WARN(...) do \
     { \
     { \
-        printError("%s -- ", __FUNCTION__); \
+        printError("%s -- ", __current__func__); \
         printError(__VA_ARGS__); \
         printError(__VA_ARGS__); \
         printError("\n"); \
         printError("\n"); \
     } while (0)
     } while (0)
@@ -168,7 +178,6 @@ extern void printError(const char* format, ...);
 #include <png.h>
 #include <png.h>
 
 
 #define WINDOW_VSYNC        1
 #define WINDOW_VSYNC        1
-#define WINDOW_FULLSCREEN   0
 
 
 // Graphics (OpenGL)
 // Graphics (OpenGL)
 #ifdef __QNX__
 #ifdef __QNX__
@@ -193,7 +202,6 @@ extern void printError(const char* format, ...);
     extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
     extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
     #define glClearDepth glClearDepthf
     #define glClearDepth glClearDepthf
     #define OPENGL_ES
     #define OPENGL_ES
-    #define USE_PVRTC
 #elif WIN32
 #elif WIN32
     #define WIN32_LEAN_AND_MEAN
     #define WIN32_LEAN_AND_MEAN
     #include <GL/glew.h>
     #include <GL/glew.h>
@@ -209,7 +217,6 @@ extern void printError(const char* format, ...);
         #define glIsVertexArray glIsVertexArrayOES
         #define glIsVertexArray glIsVertexArrayOES
         #define glClearDepth glClearDepthf
         #define glClearDepth glClearDepthf
         #define OPENGL_ES
         #define OPENGL_ES
-        #define USE_PVRTC
         #define USE_VAO
         #define USE_VAO
     #elif TARGET_OS_MAC
     #elif TARGET_OS_MAC
         #include <OpenGL/gl.h>
         #include <OpenGL/gl.h>
@@ -256,12 +263,12 @@ typedef GLuint RenderBufferHandle;
 #ifdef NDEBUG
 #ifdef NDEBUG
 #define GL_ASSERT( gl_code ) gl_code
 #define GL_ASSERT( gl_code ) gl_code
 #else
 #else
-#define GL_ASSERT( gl_code ) \
+#define GL_ASSERT( gl_code ) do \
     { \
     { \
         gl_code; \
         gl_code; \
         __gl_error_code = glGetError(); \
         __gl_error_code = glGetError(); \
         GP_ASSERT(__gl_error_code == GL_NO_ERROR); \
         GP_ASSERT(__gl_error_code == GL_NO_ERROR); \
-    }
+    } while(0)
 #endif
 #endif
 
 
 /**
 /**
@@ -273,7 +280,7 @@ typedef GLuint RenderBufferHandle;
  * macro can be used afterwards to check whether a GL error was
  * macro can be used afterwards to check whether a GL error was
  * encountered executing the specified code.
  * encountered executing the specified code.
  */
  */
-#define GL_CHECK( gl_code ) \
+#define GL_CHECK( gl_code ) do \
     { \
     { \
         while (glGetError() != GL_NO_ERROR) ; \
         while (glGetError() != GL_NO_ERROR) ; \
         gl_code; \
         gl_code; \
@@ -282,7 +289,7 @@ typedef GLuint RenderBufferHandle;
         { \
         { \
             GP_ERROR(#gl_code ": %d", (int)__gl_error_code); \
             GP_ERROR(#gl_code ": %d", (int)__gl_error_code); \
         } \
         } \
-    }
+    } while(0)
 
 
 // Global variable to hold GL errors
 // Global variable to hold GL errors
 extern GLenum __gl_error_code;
 extern GLenum __gl_error_code;
@@ -299,7 +306,7 @@ extern GLenum __gl_error_code;
  * The AL_LAST_ERROR macro can be used afterwards to check whether a AL error was
  * The AL_LAST_ERROR macro can be used afterwards to check whether a AL error was
  * encountered executing the specified code.
  * encountered executing the specified code.
  */
  */
-#define AL_CHECK( al_code ) \
+#define AL_CHECK( al_code ) do \
     { \
     { \
         while (alGetError() != AL_NO_ERROR) ; \
         while (alGetError() != AL_NO_ERROR) ; \
         al_code; \
         al_code; \
@@ -308,7 +315,7 @@ extern GLenum __gl_error_code;
         { \
         { \
             GP_ERROR(#al_code ": %d", (int)__al_error_code); \
             GP_ERROR(#al_code ": %d", (int)__al_error_code); \
         } \
         } \
-    }
+    } while(0)
 
 
 // Global variable to hold AL errors
 // Global variable to hold AL errors
 extern ALenum __al_error_code;
 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
 void BoundingBox::getCenter(Vector3* dst) const
 {
 {
+    GP_ASSERT(dst);
+
     dst->set(min, max);
     dst->set(min, max);
     dst->scale(0.5f);
     dst->scale(0.5f);
     dst->add(min);
     dst->add(min);
@@ -247,6 +249,10 @@ void BoundingBox::set(const Vector3& min, const Vector3& max)
 
 
 void updateMinMax(Vector3* point, Vector3* min, Vector3* max)
 void updateMinMax(Vector3* point, Vector3* min, Vector3* max)
 {
 {
+    GP_ASSERT(point);
+    GP_ASSERT(min);
+    GP_ASSERT(max);
+
     // Leftmost point.
     // Leftmost point.
     if (point->x < min->x)
     if (point->x < min->x)
     {
     {
@@ -292,10 +298,6 @@ void BoundingBox::set(const BoundingBox& box)
 
 
 void BoundingBox::set(const BoundingSphere& sphere)
 void BoundingBox::set(const BoundingSphere& sphere)
 {
 {
-    std::vector<int> v;
-    v.push_back(0);
-    std::vector<int> v2 = v;
-
     const Vector3& center = sphere.center;
     const Vector3& center = sphere.center;
     float radius = sphere.radius;
     float radius = sphere.radius;
 
 

+ 8 - 0
gameplay/src/BoundingSphere.cpp

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

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 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)
 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.
     // Transform the point to clip-space.
     Vector4 clipPos;
     Vector4 clipPos;
     getViewProjectionMatrix().transformVector(Vector4(position.x, position.y, position.z, 1.0f), &clipPos);
     getViewProjectionMatrix().transformVector(Vector4(position.x, position.y, position.z, 1.0f), &clipPos);
 
 
     // Compute normalized device coordinates.
     // Compute normalized device coordinates.
+    GP_ASSERT(clipPos.w != 0.0f);
     float ndcX = clipPos.x / clipPos.w;
     float ndcX = clipPos.x / clipPos.w;
     float ndcY = clipPos.y / clipPos.w;
     float ndcY = clipPos.y / clipPos.w;
 
 
     // Compute screen coordinates by applying our viewport transformation.
     // 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)
     if (depth)
     {
     {
         float ndcZ = clipPos.z / clipPos.w;
         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)
 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.
     // 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.
     // Map to range -1 to 1.
     screen.x = screen.x * 2.0f - 1.0f;
     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)
 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.
     // Get the world-space position at the near clip plane.
     Vector3 nearPoint;
     Vector3 nearPoint;
     unproject(viewport, x, y, 0.0f, &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
         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.
         // Add the new control to the form.
@@ -264,7 +264,7 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
     if (_skin && needsClear)
     if (_skin && needsClear)
     {
     {
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
         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;
         float clearY = targetHeight - _clearBounds.y - _clearBounds.height;
         GL_ASSERT( glScissor(_clearBounds.x, clearY,
         GL_ASSERT( glScissor(_clearBounds.x, clearY,
             _clearBounds.width, _clearBounds.height) );
             _clearBounds.width, _clearBounds.height) );

+ 19 - 7
gameplay/src/Control.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 
 
 Control::Control()
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
     : _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())
 {
 {
 }
 }
 
 
@@ -64,7 +64,7 @@ void Control::initialize(Theme::Style* style, Properties* properties)
         size.x = properties->getFloat("width");
         size.x = properties->getFloat("width");
         size.y = properties->getFloat("height");
         size.y = properties->getFloat("height");
     }
     }
-    _bounds.set(position.x, position.y, size.x, size.y);
+    setBounds(Rectangle(position.x, position.y, size.x, size.y));
 
 
     _state = Control::getState(properties->getString("state"));
     _state = Control::getState(properties->getString("state"));
 
 
@@ -139,7 +139,11 @@ void Control::setSize(float width, float height)
 
 
 void Control::setBounds(const Rectangle& bounds)
 void Control::setBounds(const Rectangle& bounds)
 {
 {
-    _bounds.set(bounds);
+    if (bounds != _bounds)
+    {
+        _bounds.set(bounds);
+        _dirty = true;
+    }
 }
 }
 
 
 const Rectangle& Control::getBounds() const
 const Rectangle& Control::getBounds() const
@@ -179,7 +183,11 @@ Control::Alignment Control::getAlignment() const
 
 
 void Control::setAutoWidth(bool autoWidth)
 void Control::setAutoWidth(bool autoWidth)
 {
 {
-    _autoWidth = autoWidth;
+    if (_autoWidth != autoWidth)
+    {
+        _autoWidth = autoWidth;
+        _dirty = true;
+    }
 }
 }
 
 
 bool Control::getAutoWidth() const
 bool Control::getAutoWidth() const
@@ -189,7 +197,11 @@ bool Control::getAutoWidth() const
 
 
 void Control::setAutoHeight(bool autoHeight)
 void Control::setAutoHeight(bool autoHeight)
 {
 {
-    _autoHeight = autoHeight;
+    if (_autoHeight != autoHeight)
+    {
+        _autoHeight = autoHeight;
+        _dirty = true;
+    }
 }
 }
 
 
 void Control::setOpacity(float opacity, unsigned char states)
 void Control::setOpacity(float opacity, unsigned char states)
@@ -869,7 +881,7 @@ void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsCl
     if (needsClear)
     if (needsClear)
     {
     {
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
         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,
         GL_ASSERT( glScissor(_clearBounds.x, targetHeight - _clearBounds.y - _clearBounds.height,
             _clearBounds.width, _clearBounds.height) );
             _clearBounds.width, _clearBounds.height) );
         GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
         GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
@@ -1261,7 +1273,7 @@ Control::Alignment Control::getAlignment(const char* alignment)
     }
     }
     else
     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.
     // Default.

+ 4 - 4
gameplay/src/Control.h

@@ -193,7 +193,7 @@ public:
      * @param width The width.
      * @param width The width.
      * @param height The height.
      * @param height The height.
      */
      */
-    void setSize(float width, float height);
+    virtual void setSize(float width, float height);
 
 
     /**
     /**
      * Set the bounds of this control, relative to its parent container and including its
      * Set the bounds of this control, relative to its parent container and including its
@@ -201,7 +201,7 @@ public:
      *
      *
      * @param bounds The new bounds to set.
      * @param bounds The new bounds to set.
      */
      */
-    void setBounds(const Rectangle& bounds);
+    virtual void setBounds(const Rectangle& bounds);
 
 
     /**
     /**
      * Get the bounds of this control, relative to its parent container and including its
      * Get the bounds of this control, relative to its parent container and including its
@@ -258,7 +258,7 @@ public:
      *
      *
      * @param autoWidth Whether to size this control to fit horizontally within its parent container.
      * @param autoWidth Whether to size this control to fit horizontally within its parent container.
      */
      */
-    void setAutoWidth(bool autoWidth);
+    virtual void setAutoWidth(bool autoWidth);
 
 
     /**
     /**
      * Get whether this control's width is set to automatically adjust to
      * Get whether this control's width is set to automatically adjust to
@@ -273,7 +273,7 @@ public:
      *
      *
      * @param autoHeight Whether to size this control to fit vertically within its parent container.
      * @param autoHeight Whether to size this control to fit vertically within its parent container.
      */
      */
-    void setAutoHeight(bool autoHeight);
+    virtual void setAutoHeight(bool autoHeight);
 
 
     /**
     /**
      * Get whether this control's height is set to automatically adjust to
      * Get whether this control's height is set to automatically adjust to

+ 10 - 10
gameplay/src/DebugNew.cpp

@@ -122,7 +122,7 @@ void* debugAlloc(std::size_t size, const char* file, int line)
         if (!initialized)
         if (!initialized)
         {
         {
             if (!SymInitialize(GetCurrentProcess(), NULL, true))
             if (!SymInitialize(GetCurrentProcess(), NULL, true))
-                gameplay::printError("Stack trace tracking will not work.");
+                gameplay::printError("Stack trace tracking will not work.\n");
             initialized = true;
             initialized = true;
         }
         }
     
     
@@ -181,7 +181,7 @@ void debugFree(void* p)
     // Sanity check: ensure that address in record matches passed in address
     // Sanity check: ensure that address in record matches passed in address
     if (rec->address != (unsigned long)p)
     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;
         return;
     }
     }
 
 
@@ -221,7 +221,7 @@ void printStackTrace(MemoryAllocationRecord* rec)
         symbol->MaxNameLength = bufferSize;
         symbol->MaxNameLength = bufferSize;
         if (!SymGetSymFromAddr64(GetCurrentProcess(), pc, &displacement, symbol))
         if (!SymGetSymFromAddr64(GetCurrentProcess(), pc, &displacement, symbol))
         {
         {
-            gameplay::printError("[memory] STACK TRACE: <unknown location>");
+            gameplay::printError("[memory] STACK TRACE: <unknown location>\n");
         }
         }
         else
         else
         {
         {
@@ -243,7 +243,7 @@ void printStackTrace(MemoryAllocationRecord* rec)
                     line.SizeOfStruct = sizeof(line);
                     line.SizeOfStruct = sizeof(line);
                     if (!SymGetLineFromAddr64(GetCurrentProcess(), pc, &displacement, &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
                     else
                     {
                     {
@@ -253,7 +253,7 @@ void printStackTrace(MemoryAllocationRecord* rec)
                         else
                         else
                             file++;
                             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
     // Dump general heap memory leaks
     if (__memoryAllocationCount == 0)
     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
     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;
         MemoryAllocationRecord* rec = __memoryAllocations;
         while (rec)
         while (rec)
         {
         {
 #ifdef WIN32
 #ifdef WIN32
             if (rec->trackStackTrace)
             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);
                 printStackTrace(rec);
             }
             }
             else
             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
 #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
 #endif
             rec = rec->next;
             rec = rec->next;
         }
         }

+ 10 - 7
gameplay/src/DepthStencilTarget.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 static std::vector<DepthStencilTarget*> __depthStencilTargets;
 static std::vector<DepthStencilTarget*> __depthStencilTargets;
 
 
 DepthStencilTarget::DepthStencilTarget(const char* id, Format format)
 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;
         return NULL;
     }
     }
 
 
-    // Create stencil renderbuffer if format is DEPTH24_STENCIL8
+    // Create stencil renderbuffer if format is DEPTH24_STENCIL8.
     RenderBufferHandle stencilBuffer = 0;
     RenderBufferHandle stencilBuffer = 0;
     if (format == DEPTH24_STENCIL8)
     if (format == DEPTH24_STENCIL8)
     {
     {
-        // Backup the existing render buffer
+        // Backup the existing render buffer.
         GLint currentRbo = 0;
         GLint currentRbo = 0;
         GL_ASSERT( glGetIntegerv(GL_RENDERBUFFER_BINDING, &currentRbo) );
         GL_ASSERT( glGetIntegerv(GL_RENDERBUFFER_BINDING, &currentRbo) );
 
 
-        // Create the new render buffer
+        // Create the new render buffer.
         GL_ASSERT( glGenRenderbuffers(1, &stencilBuffer) );
         GL_ASSERT( glGenRenderbuffers(1, &stencilBuffer) );
         GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer) );
         GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer) );
         GL_ASSERT( glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height) );
         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) );
         GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, currentRbo) );
     }
     }
 
 
-    // Create the depth stencil target
+    // Create the depth stencil target.
     DepthStencilTarget* depthStencilTarget = new DepthStencilTarget(id, format);
     DepthStencilTarget* depthStencilTarget = new DepthStencilTarget(id, format);
     depthStencilTarget->_depthTexture = depthTexture;
     depthStencilTarget->_depthTexture = depthTexture;
     depthStencilTarget->_stencilBuffer = stencilBuffer;
     depthStencilTarget->_stencilBuffer = stencilBuffer;
 
 
-    // Add it to the cache
+    // Add it to the cache.
     __depthStencilTargets.push_back(depthStencilTarget);
     __depthStencilTargets.push_back(depthStencilTarget);
 
 
     return depthStencilTarget;
     return depthStencilTarget;
@@ -68,11 +68,14 @@ DepthStencilTarget* DepthStencilTarget::create(const char* id, Format format, un
 
 
 DepthStencilTarget* DepthStencilTarget::getDepthStencilTarget(const char* id)
 DepthStencilTarget* DepthStencilTarget::getDepthStencilTarget(const char* id)
 {
 {
+    GP_ASSERT(id);
+
     // Search the vector for a matching ID.
     // Search the vector for a matching ID.
     std::vector<DepthStencilTarget*>::const_iterator it;
     std::vector<DepthStencilTarget*>::const_iterator it;
     for (it = __depthStencilTargets.begin(); it < __depthStencilTargets.end(); it++)
     for (it = __depthStencilTargets.begin(); it < __depthStencilTargets.end(); it++)
     {
     {
         DepthStencilTarget* dst = *it;
         DepthStencilTarget* dst = *it;
+        GP_ASSERT(dst);
         if (strcmp(id, dst->getID()) == 0)
         if (strcmp(id, dst->getID()) == 0)
         {
         {
             return dst;
             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)
 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.
     // Search the effect cache for an identical effect that is already loaded.
     std::string uniqueId = vshPath;
     std::string uniqueId = vshPath;
     uniqueId += ';';
     uniqueId += ';';
@@ -55,6 +58,7 @@ Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const c
     if (itr != __effectCache.end())
     if (itr != __effectCache.end())
     {
     {
         // Found an exiting effect with this id, so increase its ref count and return it.
         // Found an exiting effect with this id, so increase its ref count and return it.
+        GP_ASSERT(itr->second);
         itr->second->addRef();
         itr->second->addRef();
         return itr->second;
         return itr->second;
     }
     }
@@ -63,11 +67,13 @@ Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const c
     char* vshSource = FileSystem::readAll(vshPath);
     char* vshSource = FileSystem::readAll(vshPath);
     if (vshSource == NULL)
     if (vshSource == NULL)
     {
     {
+        GP_ERROR("Failed to read vertex shader from file '%s'.", vshPath);
         return NULL;
         return NULL;
     }
     }
     char* fshSource = FileSystem::readAll(fshPath);
     char* fshSource = FileSystem::readAll(fshPath);
     if (fshSource == NULL)
     if (fshSource == NULL)
     {
     {
+        GP_ERROR("Failed to read fragment shader from file '%s'.", fshPath);
         SAFE_DELETE_ARRAY(vshSource);
         SAFE_DELETE_ARRAY(vshSource);
         return NULL;
         return NULL;
     }
     }
@@ -79,7 +85,7 @@ Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const c
 
 
     if (effect == NULL)
     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
     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)
 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 unsigned int SHADER_SOURCE_LENGTH = 3;
     const GLchar* shaderSource[SHADER_SOURCE_LENGTH];
     const GLchar* shaderSource[SHADER_SOURCE_LENGTH];
     char* infoLog = NULL;
     char* infoLog = NULL;
@@ -130,7 +139,7 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
             GL_ASSERT( glGetShaderInfoLog(vertexShader, length, NULL, infoLog) );
             GL_ASSERT( glGetShaderInfoLog(vertexShader, length, NULL, infoLog) );
             infoLog[length-1] = '\0';
             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);
         SAFE_DELETE_ARRAY(infoLog);
 
 
         // Clean up.
         // Clean up.
@@ -140,14 +149,6 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
     }
     }
 
 
     // Compile the fragment shader.
     // 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;
     shaderSource[2] = fshSource;
     GL_ASSERT( fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) );
     GL_ASSERT( fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) );
     GL_ASSERT( glShaderSource(fragmentShader, SHADER_SOURCE_LENGTH, shaderSource, NULL) );
     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)
 void Effect::setValue(Uniform* uniform, float value)
 {
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform1f(uniform->_location, value) );
     GL_ASSERT( glUniform1f(uniform->_location, value) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const float* values, unsigned int count)
 void Effect::setValue(Uniform* uniform, const float* values, unsigned int count)
 {
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniform1fv(uniform->_location, count, values) );
     GL_ASSERT( glUniform1fv(uniform->_location, count, values) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, int value)
 void Effect::setValue(Uniform* uniform, int value)
 {
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform1i(uniform->_location, value) );
     GL_ASSERT( glUniform1i(uniform->_location, value) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const int* values, unsigned int count)
 void Effect::setValue(Uniform* uniform, const int* values, unsigned int count)
 {
 {
+    GP_ASSERT(uniform);
+    GP_ASSERT(values);
     GL_ASSERT( glUniform1iv(uniform->_location, count, values) );
     GL_ASSERT( glUniform1iv(uniform->_location, count, values) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Matrix& value)
 void Effect::setValue(Uniform* uniform, const Matrix& value)
 {
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniformMatrix4fv(uniform->_location, 1, GL_FALSE, value.m) );
     GL_ASSERT( glUniformMatrix4fv(uniform->_location, 1, GL_FALSE, value.m) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Matrix* values, unsigned int count)
 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) );
     GL_ASSERT( glUniformMatrix4fv(uniform->_location, count, GL_FALSE, (GLfloat*)values) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Vector2& value)
 void Effect::setValue(Uniform* uniform, const Vector2& value)
 {
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform2f(uniform->_location, value.x, value.y) );
     GL_ASSERT( glUniform2f(uniform->_location, value.x, value.y) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Vector2* values, unsigned int count)
 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) );
     GL_ASSERT( glUniform2fv(uniform->_location, count, (GLfloat*)values) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Vector3& value)
 void Effect::setValue(Uniform* uniform, const Vector3& value)
 {
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform3f(uniform->_location, value.x, value.y, value.z) );
     GL_ASSERT( glUniform3f(uniform->_location, value.x, value.y, value.z) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Vector3* values, unsigned int count)
 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) );
     GL_ASSERT( glUniform3fv(uniform->_location, count, (GLfloat*)values) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Vector4& value)
 void Effect::setValue(Uniform* uniform, const Vector4& value)
 {
 {
+    GP_ASSERT(uniform);
     GL_ASSERT( glUniform4f(uniform->_location, value.x, value.y, value.z, value.w) );
     GL_ASSERT( glUniform4f(uniform->_location, value.x, value.y, value.z, value.w) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Vector4* values, unsigned int count)
 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) );
     GL_ASSERT( glUniform4fv(uniform->_location, count, (GLfloat*)values) );
 }
 }
 
 
 void Effect::setValue(Uniform* uniform, const Texture::Sampler* sampler)
 void Effect::setValue(Uniform* uniform, const Texture::Sampler* sampler)
 {
 {
+    GP_ASSERT(uniform);
     GP_ASSERT(uniform->_type == GL_SAMPLER_2D);
     GP_ASSERT(uniform->_type == GL_SAMPLER_2D);
+    GP_ASSERT(sampler);
 
 
     GL_ASSERT( glActiveTexture(GL_TEXTURE0 + uniform->_index) );
     GL_ASSERT( glActiveTexture(GL_TEXTURE0 + uniform->_index) );
 
 

+ 148 - 27
gameplay/src/FileSystem.cpp

@@ -1,13 +1,20 @@
 #include "Base.h"
 #include "Base.h"
 #include "FileSystem.h"
 #include "FileSystem.h"
+#include "Properties.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
 
 
 #ifdef WIN32
 #ifdef WIN32
     #include <windows.h>
     #include <windows.h>
     #include <tchar.h>
     #include <tchar.h>
     #include <stdio.h>
     #include <stdio.h>
+    #define gp_stat _stat
+    #define gp_stat_struct struct stat
 #else
 #else
     #include <dirent.h>
     #include <dirent.h>
-    #include <sys/stat.h>
+    #define gp_stat stat
+    #define gp_stat_struct struct stat
 #endif
 #endif
 
 
 #ifdef __ANDROID__
 #ifdef __ANDROID__
@@ -18,9 +25,10 @@ extern AAssetManager* __assetManager;
 namespace gameplay
 namespace gameplay
 {
 {
 
 
+// Creates a file on the file system from the specified asset (Android-specific).
+static void createFileFromAsset(const char* path);
+
 #ifdef __ANDROID__
 #ifdef __ANDROID__
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
 void makepath(std::string path, int mode)
 void makepath(std::string path, int mode)
@@ -50,7 +58,7 @@ void makepath(std::string path, int mode)
             // Directory does not exist.
             // Directory does not exist.
             if (mkdir(dirPath.c_str(), 0777) != 0)
             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;
                 return;
             }
             }
         }
         }
@@ -61,6 +69,7 @@ void makepath(std::string path, int mode)
 #endif
 #endif
 
 
 static std::string __resourcePath("./");
 static std::string __resourcePath("./");
+static std::map<std::string, std::string> __aliases;
 
 
 FileSystem::FileSystem()
 FileSystem::FileSystem()
 {
 {
@@ -80,6 +89,48 @@ const char* FileSystem::getResourcePath()
     return __resourcePath.c_str();
     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)
 bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
 {
 {
     // TODO make this method work with absolute and relative paths.
     // 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
 #endif
 }
 }
 
 
-FILE* FileSystem::openFile(const char* path, const char* mode)
+bool FileSystem::fileExists(const char* path)
 {
 {
+    GP_ASSERT(path);
+
     std::string fullPath(__resourcePath);
     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)
     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
 #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);
     FILE* fp = fopen(fullPath.c_str(), mode);
     
     
@@ -204,17 +263,24 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     }
     }
 
 
     // Obtain file length.
     // 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);
     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.
     // Read entire file contents.
     char* buffer = new char[size + 1];
     char* buffer = new char[size + 1];
     int read = (int)fread(buffer, 1, size, file);
     int read = (int)fread(buffer, 1, size, file);
-    GP_ASSERT(read == size);
     if (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);
         SAFE_DELETE_ARRAY(buffer);
         return NULL;
         return NULL;
     }
     }
@@ -223,7 +289,11 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     buffer[size] = '\0';
     buffer[size] = '\0';
 
 
     // Close file and return.
     // Close file and return.
-    fclose(file);
+    if (fclose(file) != 0)
+    {
+        GP_ERROR("Failed to close file '%s'.", filePath);
+    }
+
     if (fileSize)
     if (fileSize)
     {
     {
         *fileSize = size; 
         *fileSize = size; 
@@ -231,4 +301,55 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     return buffer;
     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
 namespace gameplay
 {
 {
 
 
+class Properties;
+
 /**
 /**
  * Defines a set of functions for interacting with the device filesystem.
  * Defines a set of functions for interacting with the device filesystem.
  */
  */
@@ -33,6 +35,53 @@ public:
      */
      */
     static const char* getResourcePath();
     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.
      * 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);
     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.
      * Opens the specified file.
      *
      *

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 348 - 408
gameplay/src/Font.cpp


+ 61 - 18
gameplay/src/Font.h

@@ -76,11 +76,38 @@ public:
         float uvs[4];
         float uvs[4];
     };
     };
 
 
-    class TextBatch
+    /**
+     * Vertex coordinates, UVs and indices can be computed and stored in a Text object.
+     * For static text labels that do not change frequently, this means these computations
+     * need not be performed every frame.
+     */
+    class Text
     {
     {
+        friend class Font;
+
     public:
     public:
-        SpriteBatch::SpriteVertex* _vertices;
+        /**
+         * Constructor.
+         */
+        Text(const char* text);
+
+        /**
+         * Destructor.
+         */
+        ~Text();
+
+        /**
+         * Get the string that will be drawn from this Text object.
+         */
+        const char* getText();
+
+    private:
+        std::string _text;
         unsigned int _vertexCount;
         unsigned int _vertexCount;
+        SpriteBatch::SpriteVertex* _vertices;
+        unsigned int _indexCount;
+        unsigned short* _indices;
+        Vector4 _color;
     };
     };
 
 
     /**
     /**
@@ -132,17 +159,6 @@ public:
      */
      */
     void end();
     void end();
 
 
-    /**
-     * Compute vertex coordinates and UVs for a given string.
-     */
-    TextBatch* getTextBatch(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0,
-        Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
-
-    /**
-     * Draw a string from a precomputed StringBatch.
-     */
-    void drawTextBatch(TextBatch* textBatch);
-
     /**
     /**
      * Draws the specified text in a solid color, with a scaling factor.
      * Draws the specified text in a solid color, with a scaling factor.
      *
      *
@@ -157,7 +173,7 @@ public:
 
 
     /**
     /**
      * Draws the specified text within a rectangular area, with a specified alignment and scale.
      * Draws the specified text within a rectangular area, with a specified alignment and scale.
-     * Clips text outside the viewport.  Optionally wraps text to fit within the width of the viewport.
+     * Clips text outside the viewport. Optionally wraps text to fit within the width of the viewport.
      *
      *
      * @param text The text to draw.
      * @param text The text to draw.
      * @param area The viewport area to draw within.  Text will be clipped outside this rectangle.
      * @param area The viewport area to draw within.  Text will be clipped outside this rectangle.
@@ -171,6 +187,33 @@ public:
     void drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0, 
     void drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0, 
                   Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
                   Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
 
 
+    /**
+     * Draw a string from a precomputed Text object.
+     *
+     * @param text The text to draw.
+     */
+    void drawText(Text* text);
+
+    /**
+     * Create an immutable Text object from a given string.
+     * Vertex coordinates, UVs and indices will be computed and stored in the Text object.
+     * For static text labels that do not change frequently, this means these computations
+     * need not be performed every frame.
+     *
+     * @param text The text to draw.
+     * @param area The viewport area to draw within.  Text will be clipped outside this rectangle.
+     * @param color The color of text.
+     * @param size The size to draw text (0 for default size).
+     * @param justify Justification of text within the viewport.
+     * @param wrap Wraps text to fit within the width of the viewport if true.
+     * @param rightToLeft Whether to draw text from right to left.
+     * @param clip A region to clip text within after applying justification to the viewport area.
+     *
+     * @return A Text object.
+     */
+    Text* createText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0,
+                     Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
+
     /**
     /**
      * Measures a string's width and height without alignment, wrapping or clipping.
      * Measures a string's width and height without alignment, wrapping or clipping.
      *
      *
@@ -197,10 +240,10 @@ public:
                      Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
                      Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
 
 
     /**
     /**
-     * Get an index into a string corresponding to the character nearest the given location within the clip region.
+     * Get an character index into a string corresponding to the character nearest the given location within the clip region.
      */
      */
     int getIndexAtLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
     int getIndexAtLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
-                                    Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
+                           Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
 
     /**
     /**
      * Get the location of the character at the given index.
      * Get the location of the character at the given index.
@@ -244,10 +287,10 @@ private:
     ~Font();
     ~Font();
 
 
     void getMeasurementInfo(const char* text, const Rectangle& area, unsigned int size, Justify justify, bool wrap, bool rightToLeft,
     void 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);
+                            std::vector<int>* xPositions, int* yPosition, std::vector<unsigned int>* lineLengths);
 
 
     int getIndexOrLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
     int getIndexOrLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
-                                    const int destIndex = -1, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
+                           const int destIndex = -1, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
 
     unsigned int getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale);
     unsigned int getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale);
 
 

+ 207 - 31
gameplay/src/Form.cpp

@@ -80,7 +80,7 @@ Form* Form::create(const char* url)
         layout = ScrollLayout::create();
         layout = ScrollLayout::create();
         break;
         break;
     default:
     default:
-        GP_ERROR("Unsupported layout type \'%d\'.", getLayoutType(layoutString));
+        GP_ERROR("Unsupported layout type '%d'.", getLayoutType(layoutString));
     }
     }
 
 
     Theme* theme = Theme::create(themeFile);
     Theme* theme = Theme::create(themeFile);
@@ -93,45 +93,28 @@ Form* Form::create(const char* url)
     const char* styleName = formProperties->getString("style");
     const char* styleName = formProperties->getString("style");
     form->initialize(theme->getStyle(styleName), formProperties);
     form->initialize(theme->getStyle(styleName), formProperties);
 
 
-    if (form->_autoWidth)
+    // Alignment
+    if ((form->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
     {
     {
-        form->_bounds.width = Game::getInstance()->getWidth();
+        form->_bounds.y = Game::getInstance()->getHeight() - form->_bounds.height;
     }
     }
-
-    if (form->_autoHeight)
+    else if ((form->_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
     {
     {
-        form->_bounds.height = Game::getInstance()->getHeight();
+        form->_bounds.y = Game::getInstance()->getHeight() * 0.5f - form->_bounds.height * 0.5f;
     }
     }
 
 
-    // Add all the controls to the form.
-    form->addControls(theme, formProperties);
-
-        
-    // Width and height must be powers of two to create a texture.
-    int w = (int)form->_bounds.width;
-    int h = (int)form->_bounds.height;
-
-    if (!((w & (w - 1)) == 0))
+    if ((form->_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
     {
     {
-        w = nextHighestPowerOfTwo(w);
+        form->_bounds.x = Game::getInstance()->getWidth() - form->_bounds.width;
     }
     }
-
-    if (!((h & (h - 1)) == 0))
+    else if ((form->_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
     {
     {
-        h = nextHighestPowerOfTwo(h);
+        form->_bounds.x = Game::getInstance()->getWidth() * 0.5f - form->_bounds.width * 0.5f;
     }
     }
 
 
-    form->_u2 = form->_bounds.width / (float)w;
-    form->_v1 = form->_bounds.height / (float)h;
-
-    // Create the frame buffer.
-    form->_frameBuffer = FrameBuffer::create(form->_id.c_str());
-    RenderTarget* rt = RenderTarget::create(form->_id.c_str(), w, h);
-    GP_ASSERT(rt);
-    form->_frameBuffer->setRenderTarget(rt);
-    SAFE_RELEASE(rt);
+    // Add all the controls to the form.
+    form->addControls(theme, formProperties);
 
 
-    Matrix::createOrthographicOffCenter(0, form->_bounds.width, form->_bounds.height, 0, 0, 1, &form->_projectionMatrix);
     Game* game = Game::getInstance();
     Game* game = Game::getInstance();
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
 
 
@@ -158,6 +141,98 @@ Form* Form::getForm(const char* id)
     return NULL;
     return NULL;
 }
 }
 
 
+void Form::setSize(float width, float height)
+{
+    if (_autoWidth)
+    {
+        width = Game::getInstance()->getWidth();
+    }
+
+    if (_autoHeight)
+    {
+        height = Game::getInstance()->getHeight();
+    }
+
+    if (width != _bounds.width || height != _bounds.height)
+    {
+        // Width and height must be powers of two to create a texture.
+        int w = width;
+        int h = height;
+
+        if (!((w & (w - 1)) == 0))
+        {
+            w = nextHighestPowerOfTwo(w);
+        }
+
+        if (!((h & (h - 1)) == 0))
+        {
+            h = nextHighestPowerOfTwo(h);
+        }
+
+        _u2 = width / (float)w;
+        _v1 = height / (float)h;
+
+        // Create framebuffer if necessary.
+        if (!_frameBuffer)
+        {
+            _frameBuffer = FrameBuffer::create(_id.c_str());
+            GP_ASSERT(_frameBuffer);
+        }
+     
+        // Re-create render target.
+        RenderTarget* rt = RenderTarget::create(_id.c_str(), w, h);
+        GP_ASSERT(rt);
+        _frameBuffer->setRenderTarget(rt);
+        SAFE_RELEASE(rt);
+
+        // Re-create projection matrix.
+        Matrix::createOrthographicOffCenter(0, width, height, 0, 0, 1, &_projectionMatrix);
+
+        // Re-create sprite batch.
+        SAFE_DELETE(_spriteBatch);
+        _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
+        GP_ASSERT(_spriteBatch);
+
+        _bounds.width = width;
+        _bounds.height = height;
+        _dirty = true;
+    }
+}
+
+void Form::setBounds(const Rectangle& bounds)
+{
+    setPosition(bounds.x, bounds.y);
+    setSize(bounds.width, bounds.height);
+}
+
+void Form::setAutoWidth(bool autoWidth)
+{
+    if (_autoWidth != autoWidth)
+    {
+        _autoWidth = autoWidth;
+        _dirty = true;
+
+        if (_autoWidth)
+        {
+            setSize(_bounds.width, Game::getInstance()->getWidth());
+        }
+    }
+}
+
+void Form::setAutoHeight(bool autoHeight)
+{
+    if (_autoHeight != autoHeight)
+    {
+        _autoHeight = autoHeight;
+        _dirty = true;
+
+        if (_autoHeight)
+        {
+            setSize(_bounds.width, Game::getInstance()->getHeight());
+        }
+    }
+}
+
 void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
 void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
 {
 {
     Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);
     Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);
@@ -210,7 +285,108 @@ void Form::update()
 {
 {
     if (isDirty())
     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);
     }
     }
 }
 }
 
 
@@ -237,7 +413,7 @@ void Form::draw()
 
 
         Game* game = Game::getInstance();
         Game* game = Game::getInstance();
         Rectangle prevViewport = game->getViewport();
         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);
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
         _theme->setProjectionMatrix(_projectionMatrix);

+ 29 - 0
gameplay/src/Form.h

@@ -67,6 +67,35 @@ public:
      */
      */
     static Form* getForm(const char* id);
     static Form* getForm(const char* id);
 
 
+    /**
+     * Set the desired size of this form.
+     *
+     * @param width The width.
+     * @param height The height.
+     */
+    virtual void setSize(float width, float height);
+
+    /**
+     * Set the bounds of this form.
+     *
+     * @param bounds The new bounds to set.
+     */
+    virtual void setBounds(const Rectangle& bounds);
+
+    /**
+     * Set this form's width to that of the display.
+     *
+     * @param autoWidth Whether to set this form's width to that of the display.
+     */
+    virtual void setAutoWidth(bool autoWidth);
+
+    /**
+     * Set this form's height to that of the display.
+     *
+     * @param autoHeight Whether to set this form's height to that of the display.
+     */
+    virtual void setAutoHeight(bool autoHeight);
+
     /**
     /**
      * Create a 3D quad to texture with this Form.
      * Create a 3D quad to texture with this Form.
      *
      *

+ 19 - 9
gameplay/src/FrameBuffer.cpp

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

+ 59 - 15
gameplay/src/Game.cpp

@@ -2,6 +2,7 @@
 #include "Game.h"
 #include "Game.h"
 #include "Platform.h"
 #include "Platform.h"
 #include "RenderState.h"
 #include "RenderState.h"
+#include "FileSystem.h"
 
 
 // Extern global variables
 // Extern global variables
 GLenum __gl_error_code = GL_NO_ERROR;
 GLenum __gl_error_code = GL_NO_ERROR;
@@ -17,7 +18,7 @@ long Game::_pausedTimeTotal = 0L;
 Game::Game() 
 Game::Game() 
     : _initialized(false), _state(UNINITIALIZED), 
     : _initialized(false), _state(UNINITIALIZED), 
       _frameLastFPS(0), _frameCount(0), _frameRate(0), 
       _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)
       _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL)
 {
 {
     GP_ASSERT(__gameInstance == NULL);
     GP_ASSERT(__gameInstance == NULL);
@@ -33,7 +34,7 @@ Game::~Game()
 {
 {
     // Do not call any virtual functions from the destructor.
     // Do not call any virtual functions from the destructor.
     // Finalization is done from outside this class.
     // Finalization is done from outside this class.
-    delete _timeEvents;
+    SAFE_DELETE(_timeEvents);
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION
     Ref::printLeaks();
     Ref::printLeaks();
     printMemoryLeaks();
     printMemoryLeaks();
@@ -66,20 +67,15 @@ bool Game::isVsync()
     return Platform::isVsync();
     return Platform::isVsync();
 }
 }
 
 
-int Game::run(int width, int height)
+int Game::run()
 {
 {
     if (_state != UNINITIALIZED)
     if (_state != UNINITIALIZED)
         return -1;
         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.
     // Start up game systems.
     if (!startup())
     if (!startup())
@@ -118,6 +114,10 @@ void Game::shutdown()
     // Call user finalization.
     // Call user finalization.
     if (_state != UNINITIALIZED)
     if (_state != UNINITIALIZED)
     {
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         Platform::signalShutdown();
         Platform::signalShutdown();
         finalize();
         finalize();
 
 
@@ -133,7 +133,9 @@ void Game::shutdown()
         SAFE_DELETE(_audioListener);
         SAFE_DELETE(_audioListener);
 
 
         RenderState::finalize();
         RenderState::finalize();
-        
+
+        SAFE_DELETE(_properties);
+
         _state = UNINITIALIZED;
         _state = UNINITIALIZED;
     }
     }
 }
 }
@@ -142,6 +144,10 @@ void Game::pause()
 {
 {
     if (_state == RUNNING)
     if (_state == RUNNING)
     {
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         _state = PAUSED;
         _state = PAUSED;
         _pausedTimeLast = Platform::getAbsoluteTime();
         _pausedTimeLast = Platform::getAbsoluteTime();
         _animationController->pause();
         _animationController->pause();
@@ -154,6 +160,10 @@ void Game::resume()
 {
 {
     if (_state == PAUSED)
     if (_state == PAUSED)
     {
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         _state = RUNNING;
         _state = RUNNING;
         _pausedTimeTotal += Platform::getAbsoluteTime() - _pausedTimeLast;
         _pausedTimeTotal += Platform::getAbsoluteTime() - _pausedTimeLast;
         _animationController->resume();
         _animationController->resume();
@@ -177,6 +187,10 @@ void Game::frame()
 
 
     if (_state == Game::RUNNING)
     if (_state == Game::RUNNING)
     {
     {
+        GP_ASSERT(_animationController);
+        GP_ASSERT(_audioController);
+        GP_ASSERT(_physicsController);
+
         // Update Time.
         // Update Time.
         static long lastFrameTime = Game::getGameTime();
         static long lastFrameTime = Game::getGameTime();
         long frameTime = 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)
 void Game::schedule(long timeOffset, TimeListener* timeListener, void* cookie)
 {
 {
-    GP_ASSERT(timeListener);
+    GP_ASSERT(_timeEvents);
     TimeEvent timeEvent(getGameTime() + timeOffset, timeListener, cookie);
     TimeEvent timeEvent(getGameTime() + timeOffset, timeListener, cookie);
     _timeEvents->push(timeEvent);
     _timeEvents->push(timeEvent);
 }
 }
@@ -302,6 +316,10 @@ bool Game::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 
 
 void Game::updateOnce()
 void Game::updateOnce()
 {
 {
+    GP_ASSERT(_animationController);
+    GP_ASSERT(_audioController);
+    GP_ASSERT(_physicsController);
+
     // Update Time.
     // Update Time.
     static long lastFrameTime = Game::getGameTime();
     static long lastFrameTime = Game::getGameTime();
     long frameTime = Game::getGameTime();
     long frameTime = Game::getGameTime();
@@ -314,6 +332,31 @@ void Game::updateOnce()
     _audioController->update(elapsedTime);
     _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)
 void Game::fireTimeEvents(long frameTime)
 {
 {
     while (_timeEvents->size() > 0)
     while (_timeEvents->size() > 0)
@@ -323,7 +366,8 @@ void Game::fireTimeEvents(long frameTime)
         {
         {
             break;
             break;
         }
         }
-        timeEvent->listener->timeEvent(frameTime - timeEvent->time, timeEvent->cookie);
+        if (timeEvent->listener)
+            timeEvent->listener->timeEvent(frameTime - timeEvent->time, timeEvent->cookie);
         _timeEvents->pop();
         _timeEvents->pop();
     }
     }
 }
 }

+ 17 - 4
gameplay/src/Game.h

@@ -99,14 +99,21 @@ public:
     inline State getState() const;
     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.
      * @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.
      * Pauses the game after being run.
@@ -393,6 +400,11 @@ private:
      */
      */
     void fireTimeEvents(long frameTime);
     void fireTimeEvents(long frameTime);
 
 
+    /**
+     * Loads the game configuration.
+     */
+    void loadConfig();
+
     bool _initialized;                          // If game has initialized yet.
     bool _initialized;                          // If game has initialized yet.
     State _state;                               // The game state.
     State _state;                               // The game state.
     static long _pausedTimeLast;                // The last time paused.
     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.
     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.
     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.
     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.
     AnimationController* _animationController;  // Controls the scheduling and running of animations.
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     PhysicsController* _physicsController;      // Controls the simulation of a physics scene and entities.
     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>
 template <class T>
 void Game::renderOnce(T* instance, void (T::*method)(void*), void* cookie)
 void Game::renderOnce(T* instance, void (T::*method)(void*), void* cookie)
 {
 {
+    GP_ASSERT(instance);
     (instance->*method)(cookie);
     (instance->*method)(cookie);
     Platform::swapBuffers();
     Platform::swapBuffers();
 }
 }

+ 32 - 8
gameplay/src/Image.cpp

@@ -7,10 +7,13 @@ namespace gameplay
 
 
 Image* Image::create(const char* path)
 Image* Image::create(const char* path)
 {
 {
+    GP_ASSERT(path);
+
     // Open the file.
     // Open the file.
     FILE* fp = FileSystem::openFile(path, "rb");
     FILE* fp = FileSystem::openFile(path, "rb");
     if (fp == NULL)
     if (fp == NULL)
     {
     {
+        GP_ERROR("Failed to open image file '%s'.", path);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -18,8 +21,11 @@ Image* Image::create(const char* path)
     unsigned char sig[8];
     unsigned char sig[8];
     if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0)
     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;
         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);
     png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
     if (png == 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;
         return NULL;
     }
     }
 
 
@@ -35,7 +45,11 @@ Image* Image::create(const char* path)
     png_infop info = png_create_info_struct(png);
     png_infop info = png_create_info_struct(png);
     if (info == NULL)
     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);
         png_destroy_read_struct(&png, NULL, NULL);
         return NULL;
         return NULL;
     }
     }
@@ -43,7 +57,11 @@ Image* Image::create(const char* path)
     // Set up error handling (required without using custom error handlers above).
     // Set up error handling (required without using custom error handlers above).
     if (setjmp(png_jmpbuf(png)))
     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);
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
         return NULL;
     }
     }
@@ -73,8 +91,11 @@ Image* Image::create(const char* path)
         break;
         break;
 
 
     default:
     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);
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
         return NULL;
     }
     }
@@ -93,7 +114,10 @@ Image* Image::create(const char* path)
 
 
     // Clean up.
     // Clean up.
     png_destroy_read_struct(&png, &info, NULL);
     png_destroy_read_struct(&png, &info, NULL);
-    fclose(fp);
+    if (fclose(fp) != 0)
+    {
+        GP_ERROR("Failed to close image file '%s'.", path);
+    }
 
 
     return image;
     return image;
 }
 }

+ 2 - 0
gameplay/src/Joint.cpp

@@ -22,6 +22,7 @@ Joint* Joint::create(const char* id)
 Node* Joint::cloneSingleNode(NodeCloneContext &context) const
 Node* Joint::cloneSingleNode(NodeCloneContext &context) const
 {
 {
     Joint* copy = Joint::create(getId());
     Joint* copy = Joint::create(getId());
+    GP_ASSERT(copy);
     context.registerClonedNode(this, copy);
     context.registerClonedNode(this, copy);
     copy->_bindPose = _bindPose;
     copy->_bindPose = _bindPose;
     copy->_skinCount = _skinCount;
     copy->_skinCount = _skinCount;
@@ -54,6 +55,7 @@ void Joint::updateJointMatrix(const Matrix& bindShape, Vector4* matrixPalette)
         Matrix::multiply(Node::getWorldMatrix(), getInverseBindPose(), &t);
         Matrix::multiply(Node::getWorldMatrix(), getInverseBindPose(), &t);
         Matrix::multiply(t, bindShape, &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[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[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]);
         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
 class Layout : public Ref
 {
 {
     friend class Container;
     friend class Container;
+    friend class Form;
 
 
 public:
 public:
     /**
     /**

+ 32 - 4
gameplay/src/Light.cpp

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

+ 66 - 26
gameplay/src/Material.cpp

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

+ 52 - 11
gameplay/src/MaterialParameter.cpp

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

+ 46 - 29
gameplay/src/Matrix.cpp

@@ -112,12 +112,22 @@ void Matrix::createPerspective(float fieldOfView, float aspectRatio,
                                      float zNearPlane, float zFarPlane, Matrix* dst)
                                      float zNearPlane, float zFarPlane, Matrix* dst)
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
+    GP_ASSERT(zFarPlane != zNearPlane);
 
 
     float f_n = 1.0f / (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);
     memset(dst, 0, MATRIX_SIZE);
 
 
+    GP_ASSERT(aspectRatio);
     dst->m[0] = (1.0f / aspectRatio) * factor;
     dst->m[0] = (1.0f / aspectRatio) * factor;
     dst->m[5] = factor;
     dst->m[5] = factor;
     dst->m[10] = (-(zFarPlane + zNearPlane)) * f_n;
     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)
                                          float zNearPlane, float zFarPlane, Matrix* dst)
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
+    GP_ASSERT(right != left);
+    GP_ASSERT(top != bottom);
+    GP_ASSERT(zFarPlane != zNearPlane);
 
 
     float r_l = 1.0f / (right - left);
     float r_l = 1.0f / (right - left);
     float t_b = 1.0f / (top - bottom);
     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);
         float s = 0.5f / sqrt(trace);
         rotation->w = 0.25f / s;
         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
     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)
         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)
         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
         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
 void Matrix::getDownVector(Vector3* dst) const
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
+    
     dst->x = -m[4];
     dst->x = -m[4];
     dst->y = -m[5];
     dst->y = -m[5];
     dst->z = -m[6];
     dst->z = -m[6];
@@ -760,6 +776,8 @@ void Matrix::negate()
 
 
 void Matrix::negate(Matrix* dst) const
 void Matrix::negate(Matrix* dst) const
 {
 {
+    GP_ASSERT(dst);
+
     dst->m[0]  = -m[0];
     dst->m[0]  = -m[0];
     dst->m[1]  = -m[1];
     dst->m[1]  = -m[1];
     dst->m[2]  = -m[2];
     dst->m[2]  = -m[2];
@@ -833,8 +851,6 @@ void Matrix::rotateZ(float angle)
 
 
 void Matrix::rotateZ(float angle, Matrix* dst) const
 void Matrix::rotateZ(float angle, Matrix* dst) const
 {
 {
-    GP_ASSERT(dst);
-
     Matrix r;
     Matrix r;
     createRotationZ(angle, &r);
     createRotationZ(angle, &r);
     multiply(*this, r, dst);
     multiply(*this, r, dst);
@@ -857,8 +873,6 @@ void Matrix::scale(float xScale, float yScale, float zScale)
 
 
 void Matrix::scale(float xScale, float yScale, float zScale, Matrix* dst) const
 void Matrix::scale(float xScale, float yScale, float zScale, Matrix* dst) const
 {
 {
-    GP_ASSERT(dst);
-
     Matrix s;
     Matrix s;
     createScale(xScale, yScale, zScale, &s);
     createScale(xScale, yScale, zScale, &s);
     multiply(*this, s, dst);
     multiply(*this, s, dst);
@@ -922,6 +936,8 @@ void Matrix::subtract(const Matrix& m)
 
 
 void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
 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[0]  = m1.m[0]  - m2.m[0];
     dst->m[1]  = m1.m[1]  - m2.m[1];
     dst->m[1]  = m1.m[1]  - m2.m[1];
     dst->m[2]  = m1.m[2]  - m2.m[2];
     dst->m[2]  = m1.m[2]  - m2.m[2];
@@ -942,6 +958,7 @@ void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
 
 
 void Matrix::transformPoint(Vector3* point) const
 void Matrix::transformPoint(Vector3* point) const
 {
 {
+    GP_ASSERT(point);
     transformVector(point->x, point->y, point->z, 1.0f, point);
     transformVector(point->x, point->y, point->z, 1.0f, point);
 }
 }
 
 
@@ -952,6 +969,7 @@ void Matrix::transformPoint(const Vector3& point, Vector3* dst) const
 
 
 void Matrix::transformVector(Vector3* vector) const
 void Matrix::transformVector(Vector3* vector) const
 {
 {
+    GP_ASSERT(vector);
     transformVector(vector->x, vector->y, vector->z, 0.0f, vector);
     transformVector(vector->x, vector->y, vector->z, 0.0f, vector);
 }
 }
 
 
@@ -967,11 +985,12 @@ void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) c
     dst->set(
     dst->set(
         x * m[0] + y * m[4] + z * m[8] + w * m[12],
         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[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
 void Matrix::transformVector(Vector4* vector) const
 {
 {
+    GP_ASSERT(vector);
     transformVector(*vector, vector);
     transformVector(*vector, vector);
 }
 }
 
 
@@ -1002,10 +1021,10 @@ void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
     dst->set(
     dst->set(
         vector.x * m[0] + vector.y * m[4] + vector.z * m[8] + vector.w * m[12],
         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[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[2] + vector.y * m[6] + vector.z * m[10] + vector.w * m[14],
+        vector.x * m[3] + vector.y * m[7] + vector.z * m[11] + vector.w * m[15]);
 
 
-#endif
+#endif
 }
 }
 
 
 void Matrix::translate(float x, float y, float z)
 void Matrix::translate(float x, float y, float z)
@@ -1015,8 +1034,6 @@ void Matrix::translate(float x, float y, float z)
 
 
 void Matrix::translate(float x, float y, float z, Matrix* dst) const
 void Matrix::translate(float x, float y, float z, Matrix* dst) const
 {
 {
-    GP_ASSERT(dst);
-
     Matrix t;
     Matrix t;
     createTranslation(x, y, z, &t);
     createTranslation(x, y, z, &t);
     multiply(*this, t, dst);
     multiply(*this, t, dst);

+ 18 - 3
gameplay/src/Mesh.cpp

@@ -24,11 +24,14 @@ Mesh::Mesh(const Mesh& copy) :
 
 
 Mesh::~Mesh()
 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)
     if (_vertexBuffer)
     {
     {
@@ -43,12 +46,14 @@ Mesh* Mesh::createMesh(const VertexFormat& vertexFormat, unsigned int vertexCoun
     GL_ASSERT( glGenBuffers(1, &vbo) );
     GL_ASSERT( glGenBuffers(1, &vbo) );
     if (GL_LAST_ERROR())
     if (GL_LAST_ERROR())
     {
     {
+        GP_ERROR("Failed to create VBO for mesh with OpenGL error %d.", GL_LAST_ERROR());
         return NULL;
         return NULL;
     }
     }
 
 
     GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, vbo) );
     GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, vbo) );
     if (GL_LAST_ERROR())
     if (GL_LAST_ERROR())
     {
     {
+        GP_ERROR("Failed to bind VBO for mesh with OpenGL error %d.", GL_LAST_ERROR());
         glDeleteBuffers(1, &vbo);
         glDeleteBuffers(1, &vbo);
         return NULL;
         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) );
     GL_CHECK( glBufferData(GL_ARRAY_BUFFER, vertexFormat.getVertexSize() * vertexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
     if (GL_LAST_ERROR())
     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);
         glBindBuffer(GL_ARRAY_BUFFER, 0);
         glDeleteBuffers(1, &vbo);
         glDeleteBuffers(1, &vbo);
         return NULL;
         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);
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 3), 4, false);
     if (mesh == NULL)
     if (mesh == NULL)
     {
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -124,6 +131,7 @@ Mesh* Mesh::createQuadFullscreen()
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
     if (mesh == NULL)
     if (mesh == NULL)
     {
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
         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);
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 3), 4, false);
     if (mesh == NULL)
     if (mesh == NULL)
     {
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
         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)
 Mesh* Mesh::createLines(Vector3* points, unsigned int pointCount)
 {
 {
+    GP_ASSERT(points);
+    GP_ASSERT(pointCount);
+
     float* vertices = new float[pointCount*3];
     float* vertices = new float[pointCount*3];
     memcpy(vertices, points, pointCount*3*sizeof(float));
     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);
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 1), pointCount, false);
     if (mesh == NULL)
     if (mesh == NULL)
     {
     {
+        GP_ERROR("Failed to create mesh.");
         SAFE_DELETE_ARRAY(vertices);
         SAFE_DELETE_ARRAY(vertices);
         return NULL;
         return NULL;
     }
     }
@@ -226,6 +239,7 @@ Mesh* Mesh::createBoundingBox(const BoundingBox& box)
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 1), 18, false);
     Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 1), 18, false);
     if (mesh == NULL)
     if (mesh == NULL)
     {
     {
+        GP_ERROR("Failed to create mesh.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -325,6 +339,7 @@ unsigned int Mesh::getPartCount() const
 
 
 MeshPart* Mesh::getPart(unsigned int index)
 MeshPart* Mesh::getPart(unsigned int index)
 {
 {
+    GP_ASSERT(_parts);
     return _parts[index];
     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);
     Material* material = Material::create(materialPath);
     if (material == NULL)
     if (material == NULL)
+    {
+        GP_ERROR("Failed to create material for mesh batch from file '%s'.", materialPath);
         return NULL;
         return NULL;
+    }
     MeshBatch* batch = create(vertexFormat, primitiveType, material, indexed, initialCapacity, growSize);
     MeshBatch* batch = create(vertexFormat, primitiveType, material, indexed, initialCapacity, growSize);
     SAFE_RELEASE(material); // batch now owns the material
     SAFE_RELEASE(material); // batch now owns the material
     return batch;
     return batch;
@@ -47,13 +50,17 @@ MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveTy
 
 
 void MeshBatch::updateVertexAttributeBinding()
 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)
     for (unsigned int i = 0, techniqueCount = _material->getTechniqueCount(); i < techniqueCount; ++i)
     {
     {
         Technique* t = _material->getTechnique(i);
         Technique* t = _material->getTechnique(i);
+        GP_ASSERT(t);
         for (unsigned int j = 0, passCount = t->getPassCount(); j < passCount; ++j)
         for (unsigned int j = 0, passCount = t->getPassCount(); j < passCount; ++j)
         {
         {
             Pass* p = t->getPass(j);
             Pass* p = t->getPass(j);
+            GP_ASSERT(p);
             VertexAttributeBinding* b = VertexAttributeBinding::create(_vertexFormat, _vertices, p->getEffect());
             VertexAttributeBinding* b = VertexAttributeBinding::create(_vertexFormat, _vertices, p->getEffect());
             p->setVertexAttributeBinding(b);
             p->setVertexAttributeBinding(b);
             SAFE_RELEASE(b);
             SAFE_RELEASE(b);
@@ -73,14 +80,16 @@ void MeshBatch::setCapacity(unsigned int capacity)
 
 
 bool MeshBatch::resize(unsigned int capacity)
 bool MeshBatch::resize(unsigned int capacity)
 {
 {
-    GP_ASSERT(capacity > 0);
     if (capacity == 0)
     if (capacity == 0)
+    {
+        GP_ERROR("Invalid resize capacity (0).");
         return false;
         return false;
+    }
 
 
     if (capacity == _capacity)
     if (capacity == _capacity)
         return true;
         return true;
 
 
-    // Store old batch data
+    // Store old batch data.
     unsigned char* oldVertices = _vertices;
     unsigned char* oldVertices = _vertices;
     unsigned short* oldIndices = _indices;
     unsigned short* oldIndices = _indices;
 
 
@@ -103,20 +112,21 @@ bool MeshBatch::resize(unsigned int capacity)
         vertexCapacity = capacity + 2;
         vertexCapacity = capacity + 2;
         break;
         break;
     default:
     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 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
     // (we only know how many indices will be stored). Assume the worst case
     // for now, which is the same number of vertices as indices.
     // for now, which is the same number of vertices as indices.
     unsigned int indexCapacity = vertexCapacity;
     unsigned int indexCapacity = vertexCapacity;
-
-    GP_ASSERT(indexCapacity <= USHRT_MAX);
     if (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;
         return false;
+    }
 
 
-    // Allocate new data and reset pointers
+    // Allocate new data and reset pointers.
     unsigned int voffset = _verticesPtr - _vertices;
     unsigned int voffset = _verticesPtr - _vertices;
     unsigned int vBytes = vertexCapacity * _vertexFormat.getVertexSize();
     unsigned int vBytes = vertexCapacity * _vertexFormat.getVertexSize();
     _vertices = new unsigned char[vBytes];
     _vertices = new unsigned char[vBytes];
@@ -173,12 +183,18 @@ void MeshBatch::draw()
     // ARRAY_BUFFER will be unbound automatically during pass->bind().
     // ARRAY_BUFFER will be unbound automatically during pass->bind().
     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0 ) );
     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();
     Technique* technique = _material->getTechnique();
+    GP_ASSERT(technique);
     unsigned int passCount = technique->getPassCount();
     unsigned int passCount = technique->getPassCount();
     for (unsigned int i = 0; i < passCount; ++i)
     for (unsigned int i = 0; i < passCount; ++i)
     {
     {
         Pass* pass = technique->getPass(i);
         Pass* pass = technique->getPass(i);
+        GP_ASSERT(pass);
         pass->bind();
         pass->bind();
 
 
         if (_indexed)
         if (_indexed)

+ 10 - 5
gameplay/src/MeshBatch.inl

@@ -11,6 +11,7 @@ Material* MeshBatch::getMaterial() const
 template <class T>
 template <class T>
 void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 {
 {
+    GP_ASSERT(vertices);
     GP_ASSERT(sizeof(T) == _vertexFormat.getVertexSize());
     GP_ASSERT(sizeof(T) == _vertexFormat.getVertexSize());
     
     
     unsigned int newVertexCount = _vertexCount + vertexCount;
     unsigned int newVertexCount = _vertexCount + vertexCount;
@@ -27,16 +28,20 @@ void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indic
             return; // failed to grow
             return; // failed to grow
     }
     }
     
     
-    // Copy vertex data
+    // Copy vertex data.
+    GP_ASSERT(_verticesPtr);
     unsigned int vBytes = vertexCount * _vertexFormat.getVertexSize();
     unsigned int vBytes = vertexCount * _vertexFormat.getVertexSize();
     memcpy(_verticesPtr, vertices, vBytes);
     memcpy(_verticesPtr, vertices, vBytes);
     
     
-    // Copy index data
+    // Copy index data.
     if (_indexed)
     if (_indexed)
     {
     {
+        GP_ASSERT(indices);
+        GP_ASSERT(_indicesPtr);
+
         if (_vertexCount == 0)
         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));
             memcpy(_indicesPtr, indices, indexCount * sizeof(unsigned short));
         }
         }
         else
         else
@@ -50,8 +55,8 @@ void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indic
                 _indicesPtr += 2;
                 _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)
             for (unsigned int i = 0; i < indexCount; ++i)
             {
             {
                 _indicesPtr[i] = indices[i] + _vertexCount;
                 _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) );
     GL_ASSERT( glGenBuffers(1, &vbo) );
     if (GL_LAST_ERROR())
     if (GL_LAST_ERROR())
     {
     {
+        GP_ERROR("Failed to create VBO for index buffer with OpenGL error %d.", GL_LAST_ERROR());
         return NULL;
         return NULL;
     }
     }
 
 
     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo) );
     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo) );
     if (GL_LAST_ERROR())
     if (GL_LAST_ERROR())
     {
     {
+        GP_ERROR("Failed to bind VBO for index buffer with OpenGL error %d.", GL_LAST_ERROR());
         glDeleteBuffers(1, &vbo);
         glDeleteBuffers(1, &vbo);
         return NULL;
         return NULL;
     }
     }
@@ -51,10 +53,15 @@ MeshPart* MeshPart::create(Mesh* mesh, unsigned int meshIndex, Mesh::PrimitiveTy
     case Mesh::INDEX32:
     case Mesh::INDEX32:
         indexSize = 4;
         indexSize = 4;
         break;
         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) );
     GL_CHECK( glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize * indexCount, NULL, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW) );
     if (GL_LAST_ERROR())
     if (GL_LAST_ERROR())
     {
     {
+        GP_ERROR("Failed to load VBO with index data with OpenGL error %d.", GL_LAST_ERROR());
         glDeleteBuffers(1, &vbo);
         glDeleteBuffers(1, &vbo);
         return NULL;
         return NULL;
     }
     }
@@ -117,6 +124,9 @@ void MeshPart::setIndexData(void* indexData, unsigned int indexStart, unsigned i
     case Mesh::INDEX32:
     case Mesh::INDEX32:
         indexSize = 4;
         indexSize = 4;
         break;
         break;
+    default:
+        GP_ERROR("Unsupported index format (%d).", _indexFormat);
+        return;
     }
     }
 
 
     if (indexStart == 0 && indexCount == 0)
     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)
         for (unsigned int i = 0; i < jointCount; ++i)
         {
         {
             Joint* oldJoint = getJoint(i);
             Joint* oldJoint = getJoint(i);
+            GP_ASSERT(oldJoint);
             
             
             Joint* newJoint = static_cast<Joint*>(skin->_rootJoint->findNode(oldJoint->getId()));
             Joint* newJoint = static_cast<Joint*>(skin->_rootJoint->findNode(oldJoint->getId()));
             if (!newJoint)
             if (!newJoint)
@@ -101,10 +102,10 @@ MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
 
 
 void MeshSkin::setJointCount(unsigned int jointCount)
 void MeshSkin::setJointCount(unsigned int jointCount)
 {
 {
-    // Erase the joints vector and release all joints
+    // Erase the joints vector and release all joints.
     clearJoints();
     clearJoints();
 
 
-    // Resize the joints vector and initialize to NULL
+    // Resize the joints vector and initialize to NULL.
     _joints.resize(jointCount);
     _joints.resize(jointCount);
     for (unsigned int i = 0; i < jointCount; i++)
     for (unsigned int i = 0; i < jointCount; i++)
     {
     {
@@ -147,9 +148,12 @@ void MeshSkin::setJoint(Joint* joint, unsigned int index)
 
 
 Vector4* MeshSkin::getMatrixPalette() const
 Vector4* MeshSkin::getMatrixPalette() const
 {
 {
+    GP_ASSERT(_matrixPalette);
+
     unsigned int count = _joints.size();
     unsigned int count = _joints.size();
     for (unsigned int i = 0; i < count; i++)
     for (unsigned int i = 0; i < count; i++)
     {
     {
+        GP_ASSERT(_joints[i]);
         _joints[i]->updateJointMatrix(getBindShape(), &_matrixPalette[i * PALETTE_ROWS]);
         _joints[i]->updateJointMatrix(getBindShape(), &_matrixPalette[i * PALETTE_ROWS]);
     }
     }
     return _matrixPalette;
     return _matrixPalette;

+ 48 - 7
gameplay/src/Model.cpp

@@ -12,6 +12,7 @@ namespace gameplay
 Model::Model(Mesh* mesh) :
 Model::Model(Mesh* mesh) :
     _mesh(mesh), _material(NULL), _partCount(0), _partMaterials(NULL), _node(NULL), _skin(NULL)
     _mesh(mesh), _material(NULL), _partCount(0), _partMaterials(NULL), _node(NULL), _skin(NULL)
 {
 {
+    GP_ASSERT(mesh);
     _partCount = mesh->getPartCount();
     _partCount = mesh->getPartCount();
 }
 }
 
 
@@ -35,6 +36,7 @@ Model::~Model()
 
 
 Model* Model::create(Mesh* mesh)
 Model* Model::create(Mesh* mesh)
 {
 {
+    GP_ASSERT(mesh);
     mesh->addRef();
     mesh->addRef();
     return new Model(mesh);
     return new Model(mesh);
 }
 }
@@ -46,6 +48,7 @@ Mesh* Model::getMesh() const
 
 
 unsigned int Model::getMeshPartCount() const
 unsigned int Model::getMeshPartCount() const
 {
 {
+    GP_ASSERT(_mesh);
     return _mesh->getPartCount();
     return _mesh->getPartCount();
 }
 }
 
 
@@ -121,11 +124,13 @@ void Model::setMaterial(Material* material, int partIndex)
     // Release existing material and binding.
     // Release existing material and binding.
     if (oldMaterial)
     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)
             for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
             {
             {
+                GP_ASSERT(t->getPass(j));
                 t->getPass(j)->setVertexAttributeBinding(NULL);
                 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)
         for (unsigned int i = 0, tCount = material->getTechniqueCount(); i < tCount; ++i)
         {
         {
             Technique* t = material->getTechnique(i);
             Technique* t = material->getTechnique(i);
+            GP_ASSERT(t);
             for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
             for (unsigned int j = 0, pCount = t->getPassCount(); j < pCount; ++j)
             {
             {
                 Pass* p = t->getPass(j);
                 Pass* p = t->getPass(j);
+                GP_ASSERT(p);
                 VertexAttributeBinding* b = VertexAttributeBinding::create(_mesh, p->getEffect());
                 VertexAttributeBinding* b = VertexAttributeBinding::create(_mesh, p->getEffect());
                 p->setVertexAttributeBinding(b);
                 p->setVertexAttributeBinding(b);
                 SAFE_RELEASE(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);
     Material* material = Material::create(vshPath, fshPath, defines);
     if (material == NULL)
     if (material == NULL)
     {
     {
+        GP_ERROR("Failed to create material for model.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -179,6 +187,7 @@ Material* Model::setMaterial(const char* materialPath, int partIndex)
     Material* material = Material::create(materialPath);
     Material* material = Material::create(materialPath);
     if (material == NULL)
     if (material == NULL)
     {
     {
+        GP_ERROR("Failed to create material for model.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -246,6 +255,8 @@ void Model::setNode(Node* node)
 
 
 void Model::draw(bool wireframe)
 void Model::draw(bool wireframe)
 {
 {
+    GP_ASSERT(_mesh);
+
     unsigned int partCount = _mesh->getPartCount();
     unsigned int partCount = _mesh->getPartCount();
     if (partCount == 0)
     if (partCount == 0)
     {
     {
@@ -253,10 +264,12 @@ void Model::draw(bool wireframe)
         if (_material)
         if (_material)
         {
         {
             Technique* technique = _material->getTechnique();
             Technique* technique = _material->getTechnique();
+            GP_ASSERT(technique);
             unsigned int passCount = technique->getPassCount();
             unsigned int passCount = technique->getPassCount();
             for (unsigned int i = 0; i < passCount; ++i)
             for (unsigned int i = 0; i < passCount; ++i)
             {
             {
                 Pass* pass = technique->getPass(i);
                 Pass* pass = technique->getPass(i);
+                GP_ASSERT(pass);
                 pass->bind();
                 pass->bind();
                 GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) );
                 GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) );
                 if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
                 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)
         for (unsigned int i = 0; i < partCount; ++i)
         {
         {
             MeshPart* part = _mesh->getPart(i);
             MeshPart* part = _mesh->getPart(i);
+            GP_ASSERT(part);
 
 
             // Get the material for this mesh part.
             // Get the material for this mesh part.
             Material* material = getMaterial(i);
             Material* material = getMaterial(i);
             if (material)
             if (material)
             {
             {
                 Technique* technique = material->getTechnique();
                 Technique* technique = material->getTechnique();
+                GP_ASSERT(technique);
                 unsigned int passCount = technique->getPassCount();
                 unsigned int passCount = technique->getPassCount();
                 for (unsigned int j = 0; j < passCount; ++j)
                 for (unsigned int j = 0; j < passCount; ++j)
                 {
                 {
                     Pass* pass = technique->getPass(j);
                     Pass* pass = technique->getPass(j);
+                    GP_ASSERT(pass);
                     pass->bind();
                     pass->bind();
                     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, part->_indexBuffer) );
                     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, part->_indexBuffer) );
                     if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
                     if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
@@ -307,6 +323,9 @@ void Model::draw(bool wireframe)
                         case Mesh::INDEX32:
                         case Mesh::INDEX32:
                             indexSize = 4;
                             indexSize = 4;
                             break;
                             break;
+                        default:
+                            GP_ERROR("Unsupported index format (%d).", part->getIndexFormat());
+                            continue;
                         }
                         }
 
 
                         for (unsigned int k = 0; k < indexCount; k += 3)
                         for (unsigned int k = 0; k < indexCount; k += 3)
@@ -327,6 +346,7 @@ void Model::draw(bool wireframe)
 
 
 void Model::validatePartCount()
 void Model::validatePartCount()
 {
 {
+    GP_ASSERT(_mesh);
     unsigned int partCount = _mesh->getPartCount();
     unsigned int partCount = _mesh->getPartCount();
 
 
     if (_partCount != partCount)
     if (_partCount != partCount)
@@ -337,9 +357,12 @@ void Model::validatePartCount()
             Material** oldArray = _partMaterials;
             Material** oldArray = _partMaterials;
             _partMaterials = new Material*[partCount];
             _partMaterials = new Material*[partCount];
             memset(_partMaterials, 0, sizeof(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);
             SAFE_DELETE_ARRAY(oldArray);
         }
         }
@@ -352,18 +375,34 @@ void Model::validatePartCount()
 Model* Model::clone(NodeCloneContext &context)
 Model* Model::clone(NodeCloneContext &context)
 {
 {
     Model* model = Model::create(getMesh());
     Model* model = Model::create(getMesh());
+    if (!model)
+    {
+        GP_ERROR("Failed to clone model.");
+        return NULL;
+    }
+
     if (getSkin())
     if (getSkin())
     {
     {
         model->setSkin(getSkin()->clone(context));
         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;
     return model;
 }
 }
 
 
 void Model::setMaterialNodeBinding(Material *material)
 void Model::setMaterialNodeBinding(Material *material)
 {
 {
+    GP_ASSERT(material);
+
     if (_node)
     if (_node)
     {
     {
         material->setNodeBinding(_node);
         material->setNodeBinding(_node);
@@ -372,6 +411,7 @@ void Model::setMaterialNodeBinding(Material *material)
         for (unsigned int i = 0; i < techniqueCount; ++i)
         for (unsigned int i = 0; i < techniqueCount; ++i)
         {
         {
             Technique* technique = material->getTechnique(i);
             Technique* technique = material->getTechnique(i);
+            GP_ASSERT(technique);
             
             
             technique->setNodeBinding(_node);
             technique->setNodeBinding(_node);
 
 
@@ -379,6 +419,7 @@ void Model::setMaterialNodeBinding(Material *material)
             for (unsigned int j = 0; j < passCount; ++j)
             for (unsigned int j = 0; j < passCount; ++j)
             {
             {
                 Pass* pass = technique->getPass(j);
                 Pass* pass = technique->getPass(j);
+                GP_ASSERT(pass);
 
 
                 pass->setNodeBinding(_node);
                 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
         // Our world matrix was just updated, so call getWorldMatrix() on all child nodes
         // to force their resolved world matrices to be updated.
         // 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;
     Vector3 vector;
     getWorldMatrix().getForwardVector(&vector);
     getWorldMatrix().getForwardVector(&vector);
     getViewMatrix().transformVector(&vector);
     getViewMatrix().transformVector(&vector);
-    //getForwardVector(&vector);
-    //getWorldViewMatrix().transformVector(&vector);
     return vector;
     return vector;
 }
 }
 
 
@@ -601,8 +597,7 @@ void Node::transformChanged()
     _dirtyBits |= NODE_DIRTY_WORLD | NODE_DIRTY_BOUNDS;
     _dirtyBits |= NODE_DIRTY_WORLD | NODE_DIRTY_BOUNDS;
 
 
     // Notify our children that their transform has also changed (since transforms are inherited).
     // 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())
         if (Transform::isTransformChangedSuspended())
         {
         {
@@ -617,7 +612,6 @@ void Node::transformChanged()
         {
         {
             n->transformChanged();
             n->transformChanged();
         }
         }
-        n = n->getNextSibling();
     }
     }
 
 
     Transform::transformChanged();
     Transform::transformChanged();
@@ -647,10 +641,10 @@ Animation* Node::getAnimation(const char* id) const
         MeshSkin* skin = model->getSkin();
         MeshSkin* skin = model->getSkin();
         if (skin)
         if (skin)
         {
         {
-            Joint* rootJoint = skin->getRootJoint();
-            if (rootJoint)
+            Node* rootNode = skin->_rootNode;
+            if (rootNode)
             {
             {
-                animation = rootJoint->getAnimation(id);
+                animation = rootNode->getAnimation(id);
                 if (animation)
                 if (animation)
                     return animation;
                     return animation;
             }
             }
@@ -665,6 +659,7 @@ Animation* Node::getAnimation(const char* id) const
             std::vector<MaterialParameter*>::iterator itr = material->_parameters.begin();
             std::vector<MaterialParameter*>::iterator itr = material->_parameters.begin();
             for (; itr != material->_parameters.end(); itr++)
             for (; itr != material->_parameters.end(); itr++)
             {
             {
+                GP_ASSERT(*itr);
                 animation = ((MaterialParameter*)(*itr))->getAnimation(id);
                 animation = ((MaterialParameter*)(*itr))->getAnimation(id);
                 if (animation)
                 if (animation)
                     return 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.
     // 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);
         animation = child->getAnimation(id);
         if (animation)
         if (animation)
             return animation;
             return animation;
-
-        child = child->getNextSibling();
     }
     }
     
     
     return NULL;
     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
                 // 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 considered as directly transforming vertices on the GPU (they can instead
                 // be applied directly to the bounding volume transformation below).
                 // be applied directly to the bounding volume transformation below).
+                GP_ASSERT(_model->getSkin()->getRootJoint());
                 Node* jointParent = _model->getSkin()->getRootJoint()->getParent();
                 Node* jointParent = _model->getSkin()->getRootJoint()->getParent();
                 if (jointParent)
                 if (jointParent)
                 {
                 {
@@ -890,6 +882,7 @@ Node* Node::cloneSingleNode(NodeCloneContext &context) const
 Node* Node::cloneRecursive(NodeCloneContext &context) const
 Node* Node::cloneRecursive(NodeCloneContext &context) const
 {
 {
     Node* copy = cloneSingleNode(context);
     Node* copy = cloneSingleNode(context);
+    GP_ASSERT(copy);
 
 
     Node* lastChild = NULL;
     Node* lastChild = NULL;
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     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())
     for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     {
     {
         Node* childCopy = child->cloneRecursive(context);
         Node* childCopy = child->cloneRecursive(context);
+        GP_ASSERT(childCopy);
         copy->addChild(childCopy);
         copy->addChild(childCopy);
         childCopy->release();
         childCopy->release();
     }
     }
@@ -908,6 +902,7 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
 
 
 void Node::cloneInto(Node* node, NodeCloneContext &context) const
 void Node::cloneInto(Node* node, NodeCloneContext &context) const
 {
 {
+    GP_ASSERT(node);
     Transform::cloneInto(node, context);
     Transform::cloneInto(node, context);
 
 
     // TODO: Clone the rest of the node data.
     // 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.
     // Load the collision object properties from file.
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
-    GP_ASSERT(properties);
     if (properties == NULL)
     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;
         return NULL;
     }
     }
 
 
@@ -1050,7 +1044,7 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "rigidBody") == 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;
         return NULL;
     }
     }
 
 
@@ -1081,23 +1075,33 @@ NodeCloneContext::~NodeCloneContext()
 
 
 Animation* NodeCloneContext::findClonedAnimation(const Animation* animation)
 Animation* NodeCloneContext::findClonedAnimation(const Animation* animation)
 {
 {
+    GP_ASSERT(animation);
+
     AnimationMap::iterator it = _clonedAnimations.find(animation);
     AnimationMap::iterator it = _clonedAnimations.find(animation);
     return it != _clonedAnimations.end() ? it->second : NULL;
     return it != _clonedAnimations.end() ? it->second : NULL;
 }
 }
 
 
 void NodeCloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
 void NodeCloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
 {
 {
+    GP_ASSERT(original);
+    GP_ASSERT(clone);
+
     _clonedAnimations[original] = clone;
     _clonedAnimations[original] = clone;
 }
 }
 
 
 Node* NodeCloneContext::findClonedNode(const Node* node)
 Node* NodeCloneContext::findClonedNode(const Node* node)
 {
 {
+    GP_ASSERT(node);
+
     NodeMap::iterator it = _clonedNodes.find(node);
     NodeMap::iterator it = _clonedNodes.find(node);
     return it != _clonedNodes.end() ? it->second : NULL;
     return it != _clonedNodes.end() ? it->second : NULL;
 }
 }
 
 
 void NodeCloneContext::registerClonedNode(const Node* original, Node* clone)
 void NodeCloneContext::registerClonedNode(const Node* original, Node* clone)
 {
 {
+    GP_ASSERT(original);
+    GP_ASSERT(clone);
+
     _clonedNodes[original] = 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),
     _node(NULL), _orbitPosition(false), _orbitVelocity(false), _orbitAcceleration(false),
     _timePerEmission(PARTICLE_EMISSION_RATE_TIME_INTERVAL), _timeLast(0L), _timeRunning(0L)
     _timePerEmission(PARTICLE_EMISSION_RATE_TIME_INTERVAL), _timeLast(0L), _timeRunning(0L)
 {
 {
+    GP_ASSERT(particleCountMax);
     _particles = new Particle[particleCountMax];
     _particles = new Particle[particleCountMax];
 
 
+    GP_ASSERT(_spriteBatch);
+    GP_ASSERT(_spriteBatch->getStateBlock());
     _spriteBatch->getStateBlock()->setDepthWrite(false);
     _spriteBatch->getStateBlock()->setDepthWrite(false);
     _spriteBatch->getStateBlock()->setDepthTest(true);
     _spriteBatch->getStateBlock()->setDepthTest(true);
 }
 }
@@ -45,16 +48,16 @@ ParticleEmitter::~ParticleEmitter()
 
 
 ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlending textureBlending, unsigned int particleCountMax)
 ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlending textureBlending, unsigned int particleCountMax)
 {
 {
-    GP_ASSERT(textureFile);
-
     Texture* texture = NULL;
     Texture* texture = NULL;
     texture = Texture::create(textureFile, false);
     texture = Texture::create(textureFile, false);
 
 
     if (!texture)
     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;
         return NULL;
     }
     }
+    GP_ASSERT(texture->getWidth());
+    GP_ASSERT(texture->getHeight());
 
 
     // Use default SpriteBatch material.
     // Use default SpriteBatch material.
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
@@ -79,12 +82,10 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
 
 
 ParticleEmitter* ParticleEmitter::create(const char* url)
 ParticleEmitter* ParticleEmitter::create(const char* url)
 {
 {
-    GP_ASSERT(url);
-
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
     if (!properties)
     if (!properties)
     {
     {
-        GP_ERROR("Error loading ParticleEmitter: Could not load file: %s", url);
+        GP_ERROR("Failed to create particle emitter from file.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -98,23 +99,23 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 {
 {
     if (!properties || strcmp(properties->getNamespace(), "particle") != 0)
     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;
         return NULL;
     }
     }
 
 
     Properties* sprite = properties->getNextNamespace();
     Properties* sprite = properties->getNextNamespace();
     if (!sprite || strcmp(sprite->getNamespace(), "sprite") != 0)
     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;
         return NULL;
     }
     }
 
 
     // Load sprite properties.
     // Load sprite properties.
     // Path to image file is required.
     // Path to image file is required.
     const char* texturePath = sprite->getString("path");
     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;
         return NULL;
     }
     }
 
 
@@ -143,7 +144,6 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
     }
     }
 
 
     bool ellipsoid = properties->getBool("ellipsoid");
     bool ellipsoid = properties->getBool("ellipsoid");
-
     float sizeStartMin = properties->getFloat("sizeStartMin");
     float sizeStartMin = properties->getFloat("sizeStartMin");
     float sizeStartMax = properties->getFloat("sizeStartMax");
     float sizeStartMax = properties->getFloat("sizeStartMax");
     float sizeEndMin = properties->getFloat("sizeEndMin");
     float sizeEndMin = properties->getFloat("sizeEndMin");
@@ -186,6 +186,11 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 
 
     // Apply all properties to a newly created ParticleEmitter.
     // Apply all properties to a newly created ParticleEmitter.
     ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
     ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
+    if (!emitter)
+    {
+        GP_ERROR("Failed to create particle emitter.");
+        return NULL;
+    }
     emitter->setEmissionRate(emissionRate);
     emitter->setEmissionRate(emissionRate);
     emitter->setEllipsoid(ellipsoid);
     emitter->setEllipsoid(ellipsoid);
     emitter->setSize(sizeStartMin, sizeStartMax, sizeEndMin, sizeEndMax);
     emitter->setSize(sizeStartMin, sizeStartMax, sizeEndMin, sizeEndMax);
@@ -215,6 +220,7 @@ unsigned int ParticleEmitter::getEmissionRate() const
 
 
 void ParticleEmitter::setEmissionRate(unsigned int rate)
 void ParticleEmitter::setEmissionRate(unsigned int rate)
 {
 {
+    GP_ASSERT(rate);
     _emissionRate = rate;
     _emissionRate = rate;
     _timePerEmission = 1000.0f / (float)_emissionRate;
     _timePerEmission = 1000.0f / (float)_emissionRate;
 }
 }
@@ -243,6 +249,7 @@ bool ParticleEmitter::isActive() const
     if (!_node)
     if (!_node)
         return false;
         return false;
 
 
+    GP_ASSERT(_particles);
     bool active = false;
     bool active = false;
     for (unsigned int i = 0; i < _particleCount; i++)
     for (unsigned int i = 0; i < _particleCount; i++)
     {
     {
@@ -258,6 +265,9 @@ bool ParticleEmitter::isActive() const
 
 
 void ParticleEmitter::emit(unsigned int particleCount)
 void ParticleEmitter::emit(unsigned int particleCount)
 {
 {
+    GP_ASSERT(_node);
+    GP_ASSERT(_particles);
+
     // Limit particleCount so as not to go over _particleCountMax.
     // Limit particleCount so as not to go over _particleCountMax.
     if (particleCount + _particleCount > _particleCountMax)
     if (particleCount + _particleCount > _particleCountMax)
     {
     {
@@ -513,6 +523,9 @@ const Vector3& ParticleEmitter::getRotationAxisVariance() const
 
 
 void ParticleEmitter::setTextureBlending(TextureBlending textureBlending)
 void ParticleEmitter::setTextureBlending(TextureBlending textureBlending)
 {
 {
+    GP_ASSERT(_spriteBatch);
+    GP_ASSERT(_spriteBatch->getStateBlock());
+
     switch (textureBlending)
     switch (textureBlending)
     {
     {
         case BLEND_OPAQUE:
         case BLEND_OPAQUE:
@@ -533,6 +546,9 @@ void ParticleEmitter::setTextureBlending(TextureBlending textureBlending)
             _spriteBatch->getStateBlock()->setBlendSrc(RenderState::BLEND_ZERO);
             _spriteBatch->getStateBlock()->setBlendSrc(RenderState::BLEND_ZERO);
             _spriteBatch->getStateBlock()->setBlendDst(RenderState::BLEND_SRC_COLOR);
             _spriteBatch->getStateBlock()->setBlendDst(RenderState::BLEND_SRC_COLOR);
             break;
             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)
 void ParticleEmitter::setSpriteTexCoords(unsigned int frameCount, float* texCoords)
 {
 {
+    GP_ASSERT(frameCount);
+    GP_ASSERT(texCoords);
+
     _spriteFrameCount = frameCount;
     _spriteFrameCount = frameCount;
     _spritePercentPerFrame = 1.0f / (float)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)
 void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, Rectangle* frameCoords)
 {
 {
+    GP_ASSERT(frameCount);
+    GP_ASSERT(frameCoords);
+
     _spriteFrameCount = frameCount;
     _spriteFrameCount = frameCount;
     _spritePercentPerFrame = 1.0f / (float)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.
     // Pre-compute texture coordinates from rects.
     for (unsigned int i = 0; i < frameCount; i++)
     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)
 void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, int width, int height)
 {
 {
+    GP_ASSERT(width);
+    GP_ASSERT(height);
+
     int x;
     int x;
     int y;
     int y;
     Rectangle* frameCoords = new Rectangle[frameCount];
     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)
 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
     // Scale each component of the variance vector by a random float
     // between -1 and 1, then add this to the corresponding base component.
     // between -1 and 1, then add this to the corresponding base component.
     dst->x = base.x + variance.x * MATH_RANDOM_MINUS1_1();
     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)
 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.
     // Generate a point within a unit cube, then reject if the point is not in a unit sphere.
     do
     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)
 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
     // Scale each component of the variance color by a random float
     // between -1 and 1, then add this to the corresponding base component.
     // between -1 and 1, then add this to the corresponding base component.
     dst->x = base.x + variance.x * MATH_RANDOM_MINUS1_1();
     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)
 ParticleEmitter::TextureBlending ParticleEmitter::getTextureBlendingFromString(const char* str)
 {
 {
+    GP_ASSERT(str);
+
     if (strcmp(str, "BLEND_OPAQUE") == 0 || strcmp(str, "OPAQUE") == 0)
     if (strcmp(str, "BLEND_OPAQUE") == 0 || strcmp(str, "OPAQUE") == 0)
     {
     {
         return BLEND_OPAQUE;
         return BLEND_OPAQUE;
@@ -757,8 +780,10 @@ ParticleEmitter::TextureBlending ParticleEmitter::getTextureBlendingFromString(c
     {
     {
         return BLEND_MULTIPLIED;
         return BLEND_MULTIPLIED;
     }
     }
-
-    return BLEND_TRANSPARENT;
+    else
+    {
+        return BLEND_TRANSPARENT;
+    }
 }
 }
 
 
 
 
@@ -778,6 +803,7 @@ void ParticleEmitter::update(long elapsedTime)
         _timeRunning += elapsedTime;
         _timeRunning += elapsedTime;
 
 
         // How many particles should we emit this frame?
         // How many particles should we emit this frame?
+        GP_ASSERT(_timePerEmission);
         unsigned int emitCount = _timeRunning / _timePerEmission;
         unsigned int emitCount = _timeRunning / _timePerEmission;
             
             
         if (emitCount)
         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();
     const Frustum& frustum = _node->getScene()->getActiveCamera()->getFrustum();
 
 
     // Now update all currently living particles.
     // Now update all currently living particles.
+    GP_ASSERT(_particles);
     for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex)
     for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex)
     {
     {
         Particle* p = &_particles[particlesIndex];
         Particle* p = &_particles[particlesIndex];
@@ -893,6 +921,10 @@ void ParticleEmitter::draw()
 
 
     if (_particleCount > 0)
     if (_particleCount > 0)
     {
     {
+        GP_ASSERT(_spriteBatch);
+        GP_ASSERT(_particles);
+        GP_ASSERT(_spriteTextureCoords);
+
         // Set our node's view projection matrix to this emitter's effect.
         // Set our node's view projection matrix to this emitter's effect.
         if (_node)
         if (_node)
         {
         {
@@ -906,6 +938,7 @@ void ParticleEmitter::draw()
         static const Vector2 pivot(0.5f, 0.5f);
         static const Vector2 pivot(0.5f, 0.5f);
 
 
         // 3D Rotation so that particles always face the camera.
         // 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();
         const Matrix& cameraWorldMatrix = _node->getScene()->getActiveCamera()->getNode()->getWorldMatrix();
 
 
         Vector3 right;
         Vector3 right;

+ 7 - 6
gameplay/src/Pass.cpp

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

+ 70 - 34
gameplay/src/PhysicsCharacter.cpp

@@ -31,7 +31,8 @@ public:
     btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
     btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
     {
     {
         PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
         PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
-
+        
+        GP_ASSERT(object);
         if (object == _me || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
         if (object == _me || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
             return 1.0f;
             return 1.0f;
 
 
@@ -71,26 +72,28 @@ PhysicsCharacter::PhysicsCharacter(Node* node, const PhysicsCollisionShape::Defi
 {
 {
     setMaxSlopeAngle(45.0f);
     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);
     _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);
     Game::getInstance()->getPhysicsController()->_world->addAction(this);
 }
 }
 
 
 PhysicsCharacter::~PhysicsCharacter()
 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);
     Game::getInstance()->getPhysicsController()->_world->removeAction(this);
 }
 }
 
 
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "character") == 0))
     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;
         return NULL;
     }
     }
 
 
@@ -98,7 +101,7 @@ PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     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;
         return NULL;
     }
     }
 
 
@@ -122,6 +125,10 @@ PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
         {
         {
             maxSlopeAngle = properties->getFloat();
             maxSlopeAngle = properties->getFloat();
         }
         }
+        else
+        {
+            // Ignore this case (the attributes for the character's collision shape would end up here).
+        }
     }
     }
 
 
     // Create the physics character.
     // Create the physics character.
@@ -181,21 +188,25 @@ void PhysicsCharacter::setVelocity(const Vector3& velocity)
 
 
 void PhysicsCharacter::rotate(const Vector3& axis, float angle)
 void PhysicsCharacter::rotate(const Vector3& axis, float angle)
 {
 {
+    GP_ASSERT(_node);
     _node->rotate(axis, angle);
     _node->rotate(axis, angle);
 }
 }
 
 
 void PhysicsCharacter::rotate(const Quaternion& rotation)
 void PhysicsCharacter::rotate(const Quaternion& rotation)
 {
 {
+    GP_ASSERT(_node);
     _node->rotate(rotation);
     _node->rotate(rotation);
 }
 }
 
 
 void PhysicsCharacter::setRotation(const Vector3& axis, float angle)
 void PhysicsCharacter::setRotation(const Vector3& axis, float angle)
 {
 {
+    GP_ASSERT(_node);
     _node->setRotation(axis, angle);
     _node->setRotation(axis, angle);
 }
 }
 
 
 void PhysicsCharacter::setRotation(const Quaternion& rotation)
 void PhysicsCharacter::setRotation(const Quaternion& rotation)
 {
 {
+    GP_ASSERT(_node);
     _node->setRotation(rotation);
     _node->setRotation(rotation);
 }
 }
 
 
@@ -228,6 +239,7 @@ void PhysicsCharacter::jump(float height)
     //  v0 == initial velocity (zero for jumping)
     //  v0 == initial velocity (zero for jumping)
     //  a == acceleration (inverse gravity)
     //  a == acceleration (inverse gravity)
     //  s == linear displacement (height)
     //  s == linear displacement (height)
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Vector3 jumpVelocity = -Game::getInstance()->getPhysicsController()->getGravity() * height * 2.0f;
     Vector3 jumpVelocity = -Game::getInstance()->getPhysicsController()->getGravity() * height * 2.0f;
     jumpVelocity.set(
     jumpVelocity.set(
         jumpVelocity.x == 0 ? 0 : std::sqrt(jumpVelocity.x),
         jumpVelocity.x == 0 ? 0 : std::sqrt(jumpVelocity.x),
@@ -238,20 +250,22 @@ void PhysicsCharacter::jump(float height)
 
 
 void PhysicsCharacter::updateCurrentVelocity()
 void PhysicsCharacter::updateCurrentVelocity()
 {
 {
+    GP_ASSERT(_node);
+    
     Vector3 temp;
     Vector3 temp;
     btScalar velocity2 = 0;
     btScalar velocity2 = 0;
 
 
-    // Reset velocity vector
+    // Reset velocity vector.
     _normalizedVelocity.setValue(0, 0, 0);
     _normalizedVelocity.setValue(0, 0, 0);
 
 
-    // Add movement velocity contribution
+    // Add movement velocity contribution.
     if (!_moveVelocity.isZero())
     if (!_moveVelocity.isZero())
     {
     {
         _normalizedVelocity = _moveVelocity;
         _normalizedVelocity = _moveVelocity;
         velocity2 = _moveVelocity.length2();
         velocity2 = _moveVelocity.length2();
     }
     }
 
 
-    // Add forward velocity contribution
+    // Add forward velocity contribution.
     if (_forwardVelocity != 0)
     if (_forwardVelocity != 0)
     {
     {
         _node->getWorldMatrix().getForwardVector(&temp);
         _node->getWorldMatrix().getForwardVector(&temp);
@@ -261,7 +275,7 @@ void PhysicsCharacter::updateCurrentVelocity()
         velocity2 = std::max(std::abs(velocity2), std::abs(_forwardVelocity*_forwardVelocity));
         velocity2 = std::max(std::abs(velocity2), std::abs(_forwardVelocity*_forwardVelocity));
     }
     }
 
 
-    // Add right velocity contribution
+    // Add right velocity contribution.
     if (_rightVelocity != 0)
     if (_rightVelocity != 0)
     {
     {
         _node->getWorldMatrix().getRightVector(&temp);
         _node->getWorldMatrix().getRightVector(&temp);
@@ -285,6 +299,9 @@ void PhysicsCharacter::updateCurrentVelocity()
 
 
 void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep)
 void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep)
 {
 {
+    GP_ASSERT(_ghostObject);
+    GP_ASSERT(_node);
+
     // First check for existing collisions and attempt to respond/fix them.
     // 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
     // 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
     // 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)
             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;
                 break;
             }
             }
         }
         }
     }
     }
 
 
-    // Update current and target world positions
+    // Update current and target world positions.
     btVector3 startPosition = _ghostObject->getWorldTransform().getOrigin();
     btVector3 startPosition = _ghostObject->getWorldTransform().getOrigin();
     _currentPosition = startPosition;
     _currentPosition = startPosition;
 
 
-    // Process movement in the up direction
+    // Process movement in the up direction.
     if (_physicsEnabled)
     if (_physicsEnabled)
         stepUp(collisionWorld, deltaTimeStep);
         stepUp(collisionWorld, deltaTimeStep);
-
-    // Process horizontal movement
+    
+    // Process horizontal movement.
     stepForwardAndStrafe(collisionWorld, deltaTimeStep);
     stepForwardAndStrafe(collisionWorld, deltaTimeStep);
 
 
-    // Process movement in the down direction
+    // Process movement in the down direction.
     if (_physicsEnabled)
     if (_physicsEnabled)
         stepDown(collisionWorld, deltaTimeStep);
         stepDown(collisionWorld, deltaTimeStep);
 
 
-    // Set new position
+    // Set new position.
     btVector3 translation = _currentPosition - startPosition;
     btVector3 translation = _currentPosition - startPosition;
     _node->translate(translation.x(), translation.y(), translation.z());
     _node->translate(translation.x(), translation.y(), translation.z());
 }
 }
@@ -383,6 +400,10 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
 
     int maxIter = 10;
     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)
     while (fraction > btScalar(0.01) && maxIter-- > 0)
     {
     {
         start.setOrigin(_currentPosition);
         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());
             Vector3 normal(callback.m_hitNormalWorld.x(), callback.m_hitNormalWorld.y(), callback.m_hitNormalWorld.z());
             PhysicsCollisionObject* o = Game::getInstance()->getPhysicsController()->getCollisionObject(callback.m_hitCollisionObject);
             PhysicsCollisionObject* o = Game::getInstance()->getPhysicsController()->getCollisionObject(callback.m_hitCollisionObject);
+            GP_ASSERT(o);
             if (o->getType() == PhysicsCollisionObject::RIGID_BODY && o->isDynamic())
             if (o->getType() == PhysicsCollisionObject::RIGID_BODY && o->isDynamic())
             {
             {
                 PhysicsRigidBody* rb = static_cast<PhysicsRigidBody*>(o);
                 PhysicsRigidBody* rb = static_cast<PhysicsRigidBody*>(o);
+                GP_ASSERT(rb);
                 normal.normalize();
                 normal.normalize();
                 rb->applyImpulse(_mass * -normal * velocity.length());
                 rb->applyImpulse(_mass * -normal * velocity.length());
             }
             }
@@ -436,6 +459,11 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
 
 void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
 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.
     // Contribute gravity to vertical velocity.
     btVector3 gravity = Game::getInstance()->getPhysicsController()->_world->getGravity();
     btVector3 gravity = Game::getInstance()->getPhysicsController()->_world->getGravity();
     _verticalVelocity += (gravity * time);
     _verticalVelocity += (gravity * time);
@@ -444,7 +472,7 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
     btVector3 targetPosition = _currentPosition + (_verticalVelocity * time);
     btVector3 targetPosition = _currentPosition + (_verticalVelocity * time);
     targetPosition -= btVector3(0, _stepHeight, 0);
     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 start;
     btTransform end;
     btTransform end;
     start.setIdentity();
     start.setIdentity();
@@ -485,9 +513,11 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
             else
             else
             {
             {
                 PhysicsCollisionObject* o = Game::getInstance()->getPhysicsController()->getCollisionObject(callback.m_hitCollisionObject);
                 PhysicsCollisionObject* o = Game::getInstance()->getPhysicsController()->getCollisionObject(callback.m_hitCollisionObject);
+                GP_ASSERT(o);
                 if (o->getType() == PhysicsCollisionObject::RIGID_BODY && o->isDynamic())
                 if (o->getType() == PhysicsCollisionObject::RIGID_BODY && o->isDynamic())
                 {
                 {
                     PhysicsRigidBody* rb = static_cast<PhysicsRigidBody*>(o);
                     PhysicsRigidBody* rb = static_cast<PhysicsRigidBody*>(o);
+                    GP_ASSERT(rb);
                     normal.normalize();
                     normal.normalize();
                     rb->applyImpulse(_mass * -normal * sqrt(BV(normal).dot(_verticalVelocity)));
                     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 
     // want to keep increasing the vertical velocity until the character 
     // randomly drops through the floor when it can finally move due to its
     // randomly drops through the floor when it can finally move due to its
     // vertical velocity having such a great magnitude.
     // 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;
         _verticalVelocity = ((targetPosition + btVector3(0.0, _stepHeight, 0.0)) - _currentPosition) / time;
 
 
     _currentPosition = targetPosition;
     _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)
 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)
 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)
 btVector3 perpindicularComponent(const btVector3& direction, const btVector3& normal)
 {
 {
@@ -566,25 +596,30 @@ void PhysicsCharacter::updateTargetPositionFromCollision(btVector3& targetPositi
 
 
 bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
 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();
     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());
     world->getDispatcher()->dispatchAllCollisionPairs(pairCache, world->getDispatchInfo(), world->getDispatcher());
 
 
-    // Store our current world position
+    // Store our current world position.
     Vector3 startPosition;
     Vector3 startPosition;
     _node->getWorldMatrix().getTranslation(&startPosition);
     _node->getWorldMatrix().getTranslation(&startPosition);
     btVector3 currentPosition = BV(startPosition);
     btVector3 currentPosition = BV(startPosition);
 
 
-    // Handle all collisions/overlappign pairs
+    // Handle all collisions/overlappign pairs.
     btScalar maxPenetration = btScalar(0.0);
     btScalar maxPenetration = btScalar(0.0);
     for (int i = 0, count = pairCache->getNumOverlappingPairs(); i < count; ++i)
     for (int i = 0, count = pairCache->getNumOverlappingPairs(); i < count; ++i)
     {
     {
         _manifoldArray.resize(0);
         _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];
         btBroadphasePair* collisionPair = &pairCache->getOverlappingPairArray()[i];
         if (collisionPair->m_algorithm)
         if (collisionPair->m_algorithm)
         {
         {
@@ -594,11 +629,12 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
         for (int j = 0, manifoldCount = _manifoldArray.size(); j < manifoldCount; ++j)
         for (int j = 0, manifoldCount = _manifoldArray.size(); j < manifoldCount; ++j)
         {
         {
             btPersistentManifold* manifold = _manifoldArray[j];
             btPersistentManifold* manifold = _manifoldArray[j];
+            GP_ASSERT(manifold);
 
 
             // Get the direction of the contact points (used to scale normal vector in the correct direction).
             // 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;
             btScalar directionSign = manifold->getBody0() == _ghostObject ? -1.0f : 1.0f;
 
 
-            // Skip ghost objects
+            // Skip ghost objects.
             PhysicsCollisionObject* object = Game::getInstance()->getPhysicsController()->getCollisionObject(
             PhysicsCollisionObject* object = Game::getInstance()->getPhysicsController()->getCollisionObject(
                 (btCollisionObject*)(manifold->getBody0() == _ghostObject ? manifold->getBody1() : manifold->getBody0()));
                 (btCollisionObject*)(manifold->getBody0() == _ghostObject ? manifold->getBody1() : manifold->getBody0()));
             if (!object || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
             if (!object || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
@@ -608,20 +644,20 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
             {
             {
                 const btManifoldPoint& pt = manifold->getContactPoint(p);
                 const btManifoldPoint& pt = manifold->getContactPoint(p);
 
 
-                // Get penetration distance for this contact point
+                // Get penetration distance for this contact point.
                 btScalar dist = pt.getDistance();
                 btScalar dist = pt.getDistance();
 
 
                 if (dist < 0.0)
                 if (dist < 0.0)
                 {
                 {
-                    // A negative distance means the objects are overlapping
+                    // A negative distance means the objects are overlapping.
                     if (dist < maxPenetration)
                     if (dist < maxPenetration)
                     {
                     {
-                        // Store collision normal for this point
+                        // Store collision normal for this point.
                         maxPenetration = dist;
                         maxPenetration = dist;
                         _collisionNormal = pt.m_normalWorldOnB * directionSign;
                         _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;
                     currentPosition += pt.m_normalWorldOnB * directionSign * dist * 0.2f;
                     collision = true;
                     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);
     _node->translate(Vector3(currentPosition.x(), currentPosition.y(), currentPosition.z()) - startPosition);
 
 
     return collision;
     return collision;

+ 10 - 0
gameplay/src/PhysicsCollisionObject.cpp

@@ -37,11 +37,13 @@ PhysicsCollisionObject::~PhysicsCollisionObject()
 {
 {
     SAFE_DELETE(_motionState);
     SAFE_DELETE(_motionState);
 
 
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->destroyShape(_collisionShape);
     Game::getInstance()->getPhysicsController()->destroyShape(_collisionShape);
 }
 }
 
 
 PhysicsCollisionShape::Type PhysicsCollisionObject::getShapeType() const
 PhysicsCollisionShape::Type PhysicsCollisionObject::getShapeType() const
 {
 {
+    GP_ASSERT(getCollisionShape());
     return getCollisionShape()->getType();
     return getCollisionShape()->getType();
 }
 }
 
 
@@ -68,27 +70,35 @@ bool PhysicsCollisionObject::isKinematic() const
     case CHARACTER:
     case CHARACTER:
         return true;
         return true;
     default:
     default:
+        GP_ASSERT(getCollisionObject());
         return getCollisionObject()->isKinematicObject();
         return getCollisionObject()->isKinematicObject();
     }
     }
 }
 }
 
 
 bool PhysicsCollisionObject::isDynamic() const
 bool PhysicsCollisionObject::isDynamic() const
 {
 {
+    GP_ASSERT(getCollisionObject());
     return !getCollisionObject()->isStaticOrKinematicObject();
     return !getCollisionObject()->isStaticOrKinematicObject();
 }
 }
 
 
 void PhysicsCollisionObject::addCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
 void PhysicsCollisionObject::addCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
 {
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, object);
     Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, object);
 }
 }
 
 
 void PhysicsCollisionObject::removeCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
 void PhysicsCollisionObject::removeCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
 {
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->removeCollisionListener(listener, this, object);
     Game::getInstance()->getPhysicsController()->removeCollisionListener(listener, this, object);
 }
 }
 
 
 bool PhysicsCollisionObject::collidesWith(PhysicsCollisionObject* object) const
 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;
     static CollidesWithCallback callback;
 
 
     callback.result = false;
     callback.result = false;

+ 33 - 13
gameplay/src/PhysicsCollisionShape.cpp

@@ -21,7 +21,7 @@ PhysicsCollisionShape::~PhysicsCollisionShape()
 {
 {
     if (_shape)
     if (_shape)
     {
     {
-        // Cleanup shape-specific cached data
+        // Cleanup shape-specific cached data.
         switch (_type)
         switch (_type)
         {
         {
         case SHAPE_MESH:
         case SHAPE_MESH:
@@ -44,7 +44,7 @@ PhysicsCollisionShape::~PhysicsCollisionShape()
             break;
             break;
         }
         }
 
 
-        // Free the bullet shape
+        // Free the bullet shape.
         SAFE_DELETE(_shape);
         SAFE_DELETE(_shape);
     }
     }
 }
 }
@@ -69,10 +69,12 @@ PhysicsCollisionShape::Definition::Definition(const Definition& definition)
     switch (type)
     switch (type)
     {
     {
     case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
     case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+        GP_ASSERT(data.heightfield);
         data.heightfield->addRef();
         data.heightfield->addRef();
         break;
         break;
 
 
     case PhysicsCollisionShape::SHAPE_MESH:
     case PhysicsCollisionShape::SHAPE_MESH:
+        GP_ASSERT(data.mesh);
         data.mesh->addRef();
         data.mesh->addRef();
         break;
         break;
     }
     }
@@ -103,10 +105,12 @@ PhysicsCollisionShape::Definition& PhysicsCollisionShape::Definition::operator=(
         switch (type)
         switch (type)
         {
         {
         case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
         case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+            GP_ASSERT(data.heightfield);
             data.heightfield->addRef();
             data.heightfield->addRef();
             break;
             break;
 
 
         case PhysicsCollisionShape::SHAPE_MESH:
         case PhysicsCollisionShape::SHAPE_MESH:
+            GP_ASSERT(data.mesh);
             data.mesh->addRef();
             data.mesh->addRef();
             break;
             break;
         }
         }
@@ -117,14 +121,15 @@ PhysicsCollisionShape::Definition& PhysicsCollisionShape::Definition::operator=(
 
 
 PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Node* node, Properties* properties)
 PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Node* node, Properties* properties)
 {
 {
+    GP_ASSERT(node);
+
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || 
     if (!properties || 
         !(strcmp(properties->getNamespace(), "character") == 0 || 
         !(strcmp(properties->getNamespace(), "character") == 0 || 
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "rigidBody") == 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;
         return NULL;
     }
     }
 
 
@@ -158,7 +163,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                 type = SHAPE_CAPSULE;
                 type = SHAPE_CAPSULE;
             else
             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;
                 return NULL;
             }
             }
 
 
@@ -190,11 +195,15 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
         {
         {
             centerIsAbsolute = properties->getBool();
             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)
     if (!typeSpecified)
     {
     {
-        GP_WARN("Missing 'type' specifier for collision shape definition.");
+        GP_ERROR("Missing 'type' specifier for collision shape definition.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -255,11 +264,11 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
             break;
             break;
         case SHAPE_MESH:
         case SHAPE_MESH:
         {
         {
-            // Mesh is required on node
+            // Mesh is required on node.
             Mesh* nodeMesh = node->getModel() ? node->getModel()->getMesh() : NULL;
             Mesh* nodeMesh = node->getModel() ? node->getModel()->getMesh() : NULL;
             if (nodeMesh == 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;
                 return NULL;
             }
             }
 
 
@@ -275,7 +284,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                 case Mesh::LINE_STRIP:
                 case Mesh::LINE_STRIP:
                 case Mesh::POINTS:
                 case Mesh::POINTS:
                 case Mesh::TRIANGLE_STRIP:
                 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);
                     SAFE_DELETE(shape);
                     break;
                     break;
             }
             }
@@ -285,14 +294,20 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
         case SHAPE_HEIGHTFIELD:
         case SHAPE_HEIGHTFIELD:
             if (imagePath == NULL)
             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
             else
             {
             {
                 // Load the image data from the given file path.
                 // Load the image data from the given file path.
                 Image* image = Image::create(imagePath);
                 Image* image = Image::create(imagePath);
                 if (!image)
                 if (!image)
+                {
+                    GP_ERROR("Failed create image for heightfield collision object from file '%s'.", imagePath);
+                    SAFE_DELETE(shape);
                     return NULL;
                     return NULL;
+                }
 
 
                 // Ensure that the image's pixel format is supported.
                 // Ensure that the image's pixel format is supported.
                 switch (image->getFormat())
                 switch (image->getFormat())
@@ -301,7 +316,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                     case Image::RGBA:
                     case Image::RGBA:
                         break;
                         break;
                     default:
                     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;
                         return NULL;
                 }
                 }
 
 
@@ -310,8 +327,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
             }
             }
             break;
             break;
         default:
         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);
     SAFE_DELETE(extents);
@@ -383,6 +401,7 @@ PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule(float radius, f
 
 
 PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* image)
 PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* image)
 {
 {
+    GP_ASSERT(image);
     image->addRef();
     image->addRef();
 
 
     Definition d;
     Definition d;
@@ -395,6 +414,7 @@ PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* imag
 
 
 PhysicsCollisionShape::Definition PhysicsCollisionShape::mesh(Mesh* mesh)
 PhysicsCollisionShape::Definition PhysicsCollisionShape::mesh(Mesh* mesh)
 {
 {
+    GP_ASSERT(mesh);
     mesh->addRef();
     mesh->addRef();
 
 
     Definition d;
     Definition d;

+ 14 - 2
gameplay/src/PhysicsConstraint.cpp

@@ -22,12 +22,16 @@ PhysicsConstraint::~PhysicsConstraint()
         _b->removeConstraint(this);
         _b->removeConstraint(this);
 
 
     // Remove the constraint from the physics world and delete the Bullet object.
     // Remove the constraint from the physics world and delete the Bullet object.
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->removeConstraint(this);
     Game::getInstance()->getPhysicsController()->removeConstraint(this);
     SAFE_DELETE(_constraint);
     SAFE_DELETE(_constraint);
 }
 }
 
 
 Vector3 PhysicsConstraint::centerOfMassMidpoint(const Node* a, const Node* b)
 Vector3 PhysicsConstraint::centerOfMassMidpoint(const Node* a, const Node* b)
 {
 {
+    GP_ASSERT(a);
+    GP_ASSERT(b);
+
     Vector3 tA, tB;
     Vector3 tA, tB;
     a->getWorldMatrix().getTranslation(&tA);
     a->getWorldMatrix().getTranslation(&tA);
     b->getWorldMatrix().getTranslation(&tB);
     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)
 Quaternion PhysicsConstraint::getRotationOffset(const Node* node, const Vector3& point)
 {
 {
+    GP_ASSERT(node);
+
     // Create a translation matrix that translates to the given origin.
     // Create a translation matrix that translates to the given origin.
     Matrix m;
     Matrix m;
     Matrix::createTranslation(point, &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)
 Vector3 PhysicsConstraint::getTranslationOffset(const Node* node, const Vector3& point)
 {
 {
+    GP_ASSERT(node);
+
     // Create a translation matrix that translates to the given origin.
     // Create a translation matrix that translates to the given origin.
     Matrix m;
     Matrix m;
     Matrix::createTranslation(point, &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)
 btTransform PhysicsConstraint::getTransformOffset(const Node* node, const Vector3& origin)
 {
 {
+    GP_ASSERT(node);
+
     // Create a translation matrix that translates to the given origin.
     // Create a translation matrix that translates to the given origin.
     Matrix m;
     Matrix m;
     Matrix::createTranslation(origin, &m);
     Matrix::createTranslation(origin, &m);
@@ -123,8 +133,9 @@ btTransform PhysicsConstraint::getTransformOffset(const Node* node, const Vector
 
 
 Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 {
 {
-    Vector3 center;
+    GP_ASSERT(model && model->getMesh() && model->getNode());
 
 
+    Vector3 center;
     const BoundingBox& box = model->getMesh()->getBoundingBox();
     const BoundingBox& box = model->getMesh()->getBoundingBox();
     if (!(box.min.isZero() && box.max.isZero()))
     if (!(box.min.isZero() && box.max.isZero()))
     {
     {
@@ -145,7 +156,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
         else
         else
         {
         {
             // Warn the user that the model has no bounding volume.
             // 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);
             model->getNode()->getWorldMatrix().transformPoint(&center);
         }
         }
     }
     }
@@ -155,6 +166,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 
 
 Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 {
 {
+    GP_ASSERT(node && node->getCollisionObject() && node->getCollisionObject()->getMotionState());
     btVector3 centerOfMassOffset = (node->getCollisionObject()->getMotionState())->_centerOfMassOffset.getOrigin();
     btVector3 centerOfMassOffset = (node->getCollisionObject()->getMotionState())->_centerOfMassOffset.getOrigin();
     return Vector3(v.x + centerOfMassOffset.x(), v.y + centerOfMassOffset.y(), v.z + centerOfMassOffset.z());
     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
 inline float PhysicsConstraint::getBreakingImpulse() const
 {
 {
+    GP_ASSERT(_constraint);
     return _constraint->getBreakingImpulseThreshold();
     return _constraint->getBreakingImpulseThreshold();
 }
 }
 
 
 inline void PhysicsConstraint::setBreakingImpulse(float impulse)
 inline void PhysicsConstraint::setBreakingImpulse(float impulse)
 {
 {
+    GP_ASSERT(_constraint);
     _constraint->setBreakingImpulseThreshold(impulse);
     _constraint->setBreakingImpulseThreshold(impulse);
 }
 }
 
 
 inline bool PhysicsConstraint::isEnabled() const
 inline bool PhysicsConstraint::isEnabled() const
 {
 {
+    GP_ASSERT(_constraint);
     return _constraint->isEnabled();
     return _constraint->isEnabled();
 }
 }
 
 
 inline void PhysicsConstraint::setEnabled(bool enabled)
 inline void PhysicsConstraint::setEnabled(bool enabled)
 {
 {
+    GP_ASSERT(_constraint);
     _constraint->setEnabled(enabled);
     _constraint->setEnabled(enabled);
 }
 }
 
 

+ 139 - 54
gameplay/src/PhysicsController.cpp

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

+ 12 - 0
gameplay/src/PhysicsGenericConstraint.inl

@@ -8,6 +8,7 @@ inline const Quaternion& PhysicsGenericConstraint::getRotationOffsetA() const
     if (!_rotationOffsetA)
     if (!_rotationOffsetA)
         _rotationOffsetA = new Quaternion();
         _rotationOffsetA = new Quaternion();
 
 
+    GP_ASSERT(_constraint);
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getRotation();
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getRotation();
     _rotationOffsetA->set(ro.x(), ro.y(), ro.z(), ro.w());
     _rotationOffsetA->set(ro.x(), ro.y(), ro.z(), ro.w());
     return *_rotationOffsetA;
     return *_rotationOffsetA;
@@ -18,6 +19,7 @@ inline const Quaternion& PhysicsGenericConstraint::getRotationOffsetB() const
     if (!_rotationOffsetB)
     if (!_rotationOffsetB)
         _rotationOffsetB = new Quaternion();
         _rotationOffsetB = new Quaternion();
 
 
+    GP_ASSERT(_constraint);
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getRotation();
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getRotation();
     _rotationOffsetB->set(ro.x(), ro.y(), ro.z(), ro.w());
     _rotationOffsetB->set(ro.x(), ro.y(), ro.z(), ro.w());
     return *_rotationOffsetB;
     return *_rotationOffsetB;
@@ -28,6 +30,7 @@ inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetA() const
     if (!_translationOffsetA)
     if (!_translationOffsetA)
         _translationOffsetA = new Vector3();
         _translationOffsetA = new Vector3();
 
 
+    GP_ASSERT(_constraint);
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getOrigin();
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getOrigin();
     _translationOffsetA->set(to.x(), to.y(), to.z());
     _translationOffsetA->set(to.x(), to.y(), to.z());
     return *_translationOffsetA;
     return *_translationOffsetA;
@@ -38,6 +41,7 @@ inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetB() const
     if (!_translationOffsetB)
     if (!_translationOffsetB)
         _translationOffsetB = new Vector3();
         _translationOffsetB = new Vector3();
 
 
+    GP_ASSERT(_constraint);
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getOrigin();
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getOrigin();
     _translationOffsetB->set(to.x(), to.y(), to.z());
     _translationOffsetB->set(to.x(), to.y(), to.z());
     return *_translationOffsetB;
     return *_translationOffsetB;
@@ -45,41 +49,49 @@ inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetB() const
 
 
 inline void PhysicsGenericConstraint::setAngularLowerLimit(const Vector3& limits)
 inline void PhysicsGenericConstraint::setAngularLowerLimit(const Vector3& limits)
 {
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setAngularLowerLimit(BV(limits));
     ((btGeneric6DofConstraint*)_constraint)->setAngularLowerLimit(BV(limits));
 }
 }
 
 
 inline void PhysicsGenericConstraint::setAngularUpperLimit(const Vector3& limits)
 inline void PhysicsGenericConstraint::setAngularUpperLimit(const Vector3& limits)
 {
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setAngularUpperLimit(BV(limits));
     ((btGeneric6DofConstraint*)_constraint)->setAngularUpperLimit(BV(limits));
 }
 }
 
 
 inline void PhysicsGenericConstraint::setLinearLowerLimit(const Vector3& limits)
 inline void PhysicsGenericConstraint::setLinearLowerLimit(const Vector3& limits)
 {
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setLinearLowerLimit(BV(limits));
     ((btGeneric6DofConstraint*)_constraint)->setLinearLowerLimit(BV(limits));
 }
 }
     
     
 inline void PhysicsGenericConstraint::setLinearUpperLimit(const Vector3& limits)
 inline void PhysicsGenericConstraint::setLinearUpperLimit(const Vector3& limits)
 {
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofConstraint*)_constraint)->setLinearUpperLimit(BV(limits));
     ((btGeneric6DofConstraint*)_constraint)->setLinearUpperLimit(BV(limits));
 }
 }
 
 
 inline void PhysicsGenericConstraint::setRotationOffsetA(const Quaternion& rotationOffset)
 inline void PhysicsGenericConstraint::setRotationOffsetA(const Quaternion& rotationOffset)
 {
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setRotation(BQ(rotationOffset));
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setRotation(BQ(rotationOffset));
 }
 }
 
 
 inline void PhysicsGenericConstraint::setRotationOffsetB(const Quaternion& rotationOffset)
 inline void PhysicsGenericConstraint::setRotationOffsetB(const Quaternion& rotationOffset)
 {
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setRotation(BQ(rotationOffset));
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setRotation(BQ(rotationOffset));
 }
 }
 
 
 inline void PhysicsGenericConstraint::setTranslationOffsetA(const Vector3& translationOffset)
 inline void PhysicsGenericConstraint::setTranslationOffsetA(const Vector3& translationOffset)
 {
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setOrigin(BV(translationOffset));
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setOrigin(BV(translationOffset));
 }
 }
 
 
 inline void PhysicsGenericConstraint::setTranslationOffsetB(const Vector3& translationOffset)
 inline void PhysicsGenericConstraint::setTranslationOffsetB(const Vector3& translationOffset)
 {
 {
+    GP_ASSERT(_constraint);
     static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setOrigin(BV(translationOffset));
     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;
     Vector3 centerOfMassOffset;
     PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
     PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
+    GP_ASSERT(physicsController);
 
 
     // Create and set the collision shape for the ghost object.
     // Create and set the collision shape for the ghost object.
     _collisionShape = physicsController->createShape(node, shape, &centerOfMassOffset);
     _collisionShape = physicsController->createShape(node, shape, &centerOfMassOffset);
+    GP_ASSERT(_collisionShape);
 
 
     // Create the ghost object.
     // Create the ghost object.
     _ghostObject = bullet_new<btPairCachingGhostObject>();
     _ghostObject = bullet_new<btPairCachingGhostObject>();
@@ -27,13 +29,16 @@ PhysicsGhostObject::PhysicsGhostObject(Node* node, const PhysicsCollisionShape::
     // Add the ghost object to the physics world.
     // Add the ghost object to the physics world.
     physicsController->addCollisionObject(this);
     physicsController->addCollisionObject(this);
 
 
+    GP_ASSERT(_node);
     _node->addListener(this);
     _node->addListener(this);
 }
 }
 
 
 PhysicsGhostObject::~PhysicsGhostObject()
 PhysicsGhostObject::~PhysicsGhostObject()
 {
 {
+    GP_ASSERT(_node);
     _node->removeListener(this);
     _node->removeListener(this);
 
 
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
 
     SAFE_DELETE(_ghostObject);
     SAFE_DELETE(_ghostObject);
@@ -42,10 +47,9 @@ PhysicsGhostObject::~PhysicsGhostObject()
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "ghostObject") == 0))
     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;
         return NULL;
     }
     }
 
 
@@ -53,7 +57,7 @@ PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* propertie
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     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;
         return NULL;
     }
     }
 
 
@@ -76,6 +80,9 @@ btCollisionObject* PhysicsGhostObject::getCollisionObject() const
 
 
 void PhysicsGhostObject::transformChanged(Transform* transform, long cookie)
 void PhysicsGhostObject::transformChanged(Transform* transform, long cookie)
 {
 {
+    GP_ASSERT(_motionState);
+    GP_ASSERT(_ghostObject);
+
     // Update the motion state with the transform from the node.
     // Update the motion state with the transform from the node.
     _motionState->updateTransformFromNode();
     _motionState->updateTransformFromNode();
 
 

+ 5 - 0
gameplay/src/PhysicsHingeConstraint.cpp

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

+ 5 - 0
gameplay/src/PhysicsMotionState.cpp

@@ -23,6 +23,7 @@ PhysicsMotionState::~PhysicsMotionState()
 
 
 void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 {
 {
+    GP_ASSERT(_node);
     if (_node->getCollisionObject() && _node->getCollisionObject()->isKinematic())
     if (_node->getCollisionObject() && _node->getCollisionObject()->isKinematic())
         updateTransformFromNode();
         updateTransformFromNode();
 
 
@@ -31,6 +32,8 @@ void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 
 
 void PhysicsMotionState::setWorldTransform(const btTransform &transform)
 void PhysicsMotionState::setWorldTransform(const btTransform &transform)
 {
 {
+    GP_ASSERT(_node);
+
     _worldTransform = transform * _centerOfMassOffset;
     _worldTransform = transform * _centerOfMassOffset;
         
         
     const btQuaternion& rot = _worldTransform.getRotation();
     const btQuaternion& rot = _worldTransform.getRotation();
@@ -42,6 +45,8 @@ void PhysicsMotionState::setWorldTransform(const btTransform &transform)
 
 
 void PhysicsMotionState::updateTransformFromNode() const
 void PhysicsMotionState::updateTransformFromNode() const
 {
 {
+    GP_ASSERT(_node);
+
     // Store the initial world transform (minus the scale) for use by Bullet later on.
     // Store the initial world transform (minus the scale) for use by Bullet later on.
     Quaternion rotation;
     Quaternion rotation;
     const Matrix& m = _node->getWorldMatrix();
     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)
 PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters)
         : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL)
         : 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;
     Vector3 centerOfMassOffset;
     _collisionShape = Game::getInstance()->getPhysicsController()->createShape(node, shape, &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);
     _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 
     // 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()
 PhysicsRigidBody::~PhysicsRigidBody()
 {
 {
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+    GP_ASSERT(_collisionShape);
+    GP_ASSERT(_node);
+
     // Clean up all constraints linked to this rigid body.
     // Clean up all constraints linked to this rigid body.
     if (_constraints)
     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.
     // to make sure that it isn't sleeping and apply the force.
     if (force.lengthSquared() > MATH_EPSILON)
     if (force.lengthSquared() > MATH_EPSILON)
     {
     {
+        GP_ASSERT(_body);
         _body->activate();
         _body->activate();
         if (relativePosition)
         if (relativePosition)
             _body->applyForce(BV(force), BV(*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.
     // to make sure that it isn't sleeping and apply the impulse.
     if (impulse.lengthSquared() > MATH_EPSILON)
     if (impulse.lengthSquared() > MATH_EPSILON)
     {
     {
+        GP_ASSERT(_body);
         _body->activate();
         _body->activate();
-
         if (relativePosition)
         if (relativePosition)
         {
         {
             _body->applyImpulse(BV(impulse), BV(*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.
     // to make sure that it isn't sleeping and apply the torque.
     if (torque.lengthSquared() > MATH_EPSILON)
     if (torque.lengthSquared() > MATH_EPSILON)
     {
     {
+        GP_ASSERT(_body);
         _body->activate();
         _body->activate();
         _body->applyTorque(BV(torque));
         _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.
     // to make sure that it isn't sleeping and apply the torque impulse.
     if (torque.lengthSquared() > MATH_EPSILON)
     if (torque.lengthSquared() > MATH_EPSILON)
     {
     {
+        GP_ASSERT(_body);
         _body->activate();
         _body->activate();
         _body->applyTorqueImpulse(BV(torque));
         _body->applyTorqueImpulse(BV(torque));
     }
     }
@@ -144,10 +155,9 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "rigidBody") == 0))
     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;
         return NULL;
     }
     }
 
 
@@ -155,7 +165,7 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     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;
         return NULL;
     }
     }
 
 
@@ -199,6 +209,10 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         {
         {
             properties->getVector3(NULL, &parameters.gravity);
             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.
     // Create the rigid body.
@@ -210,6 +224,8 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 
 
 void PhysicsRigidBody::setKinematic(bool kinematic)
 void PhysicsRigidBody::setKinematic(bool kinematic)
 {
 {
+    GP_ASSERT(_body);
+
     if (kinematic)
     if (kinematic)
     {
     {
         _body->setCollisionFlags(_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
         _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
 float PhysicsRigidBody::getHeight(float x, float y) const
 {
 {
+    GP_ASSERT(_collisionShape);
+
     // This function is only supported for heightfield rigid bodies.
     // This function is only supported for heightfield rigid bodies.
     if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     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;
         return 0.0f;
     }
     }
 
 
+    GP_ASSERT(_collisionShape->_shapeData.heightfieldData);
+    GP_ASSERT(_node);
+
     // Calculate the correct x, y position relative to the heightfield data.
     // Calculate the correct x, y position relative to the heightfield data.
     if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
     if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
     {
     {
@@ -241,6 +262,9 @@ float PhysicsRigidBody::getHeight(float x, float y) const
     float w = _collisionShape->_shapeData.heightfieldData->width;
     float w = _collisionShape->_shapeData.heightfieldData->width;
     float h = _collisionShape->_shapeData.heightfieldData->height;
     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);
     Vector3 v = _collisionShape->_shapeData.heightfieldData->inverse * Vector3(x, 0.0f, y);
     x = (v.x + (0.5f * (w - 1))) * w / (w - 1);
     x = (v.x + (0.5f * (w - 1))) * w / (w - 1);
     y = (v.z + (0.5f * (h - 1))) * h / (h - 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.
     // Check that the x, y position is within the bounds.
     if (x < 0.0f || x > w || y < 0.0f || y > h)
     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;
         return 0.0f;
     }
     }
 
 
@@ -257,6 +281,7 @@ float PhysicsRigidBody::getHeight(float x, float y) const
 
 
 void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 {
 {
+    GP_ASSERT(constraint);
     if (_constraints == NULL)
     if (_constraints == NULL)
         _constraints = new std::vector<PhysicsConstraint*>();
         _constraints = new std::vector<PhysicsConstraint*>();
 
 
@@ -287,6 +312,7 @@ void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
 {
 {
     if (getShapeType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     if (getShapeType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
     {
+        GP_ASSERT(_collisionShape && _collisionShape->_shapeData.heightfieldData);
         _collisionShape->_shapeData.heightfieldData->inverseIsDirty = true;
         _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
 inline float PhysicsRigidBody::getFriction() const
 {
 {
+    GP_ASSERT(_body);
     return _body->getFriction();
     return _body->getFriction();
 }
 }
 
 
 inline void PhysicsRigidBody::setFriction(float friction)
 inline void PhysicsRigidBody::setFriction(float friction)
 {
 {
+    GP_ASSERT(_body);
     _body->setFriction(friction);
     _body->setFriction(friction);
 }
 }
 
 
 inline float PhysicsRigidBody::getRestitution() const
 inline float PhysicsRigidBody::getRestitution() const
 {
 {
+    GP_ASSERT(_body);
     return _body->getRestitution();
     return _body->getRestitution();
 }
 }
 
 
 inline void PhysicsRigidBody::setRestitution(float restitution)
 inline void PhysicsRigidBody::setRestitution(float restitution)
 {
 {
+    GP_ASSERT(_body);
     _body->setRestitution(restitution);
     _body->setRestitution(restitution);
 }
 }
 
 
 inline float PhysicsRigidBody::getLinearDamping() const
 inline float PhysicsRigidBody::getLinearDamping() const
 {
 {
+    GP_ASSERT(_body);
     return _body->getLinearDamping();
     return _body->getLinearDamping();
 }
 }
 
 
 inline float PhysicsRigidBody::getAngularDamping() const
 inline float PhysicsRigidBody::getAngularDamping() const
 {
 {
+    GP_ASSERT(_body);
     return _body->getAngularDamping();
     return _body->getAngularDamping();
 }
 }
 
 
 inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
 inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
 {
 {
+    GP_ASSERT(_body);
     _body->setDamping(linearDamping, angularDamping);
     _body->setDamping(linearDamping, angularDamping);
 }
 }
 
 
 inline Vector3 PhysicsRigidBody::getLinearVelocity() const
 inline Vector3 PhysicsRigidBody::getLinearVelocity() const
 {
 {
+    GP_ASSERT(_body);
     const btVector3& v = _body->getLinearVelocity();
     const btVector3& v = _body->getLinearVelocity();
     return Vector3(v.x(), v.y(), v.z());
     return Vector3(v.x(), v.y(), v.z());
 }
 }
 
 
 inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
 inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
 {
 {
+    GP_ASSERT(_body);
     _body->setLinearVelocity(BV(velocity));
     _body->setLinearVelocity(BV(velocity));
 }
 }
 
 
 inline Vector3 PhysicsRigidBody::getAngularVelocity() const
 inline Vector3 PhysicsRigidBody::getAngularVelocity() const
 {
 {
+    GP_ASSERT(_body);
     const btVector3& v = _body->getAngularVelocity();
     const btVector3& v = _body->getAngularVelocity();
     return Vector3(v.x(), v.y(), v.z());
     return Vector3(v.x(), v.y(), v.z());
 }
 }
 
 
 inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
 inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
 {
 {
+    GP_ASSERT(_body);
     _body->setAngularVelocity(BV(velocity));
     _body->setAngularVelocity(BV(velocity));
 }
 }
 
 
 inline Vector3 PhysicsRigidBody::getAnisotropicFriction() const
 inline Vector3 PhysicsRigidBody::getAnisotropicFriction() const
 {
 {
+    GP_ASSERT(_body);
     const btVector3& af = _body->getAnisotropicFriction();
     const btVector3& af = _body->getAnisotropicFriction();
     return Vector3(af.x(), af.y(), af.z());
     return Vector3(af.x(), af.y(), af.z());
 }
 }
 
 
 inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
 inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
 {
 {
+    GP_ASSERT(_body);
     _body->setAnisotropicFriction(BV(friction));
     _body->setAnisotropicFriction(BV(friction));
 }
 }
 
 
 inline Vector3 PhysicsRigidBody::getGravity() const
 inline Vector3 PhysicsRigidBody::getGravity() const
 {
 {
+    GP_ASSERT(_body);
     const btVector3& g = _body->getGravity();
     const btVector3& g = _body->getGravity();
     return Vector3(g.x(), g.y(), g.z());
     return Vector3(g.x(), g.y(), g.z());
 }
 }
 
 
 inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
 inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
 {
 {
+    GP_ASSERT(_body);
     _body->setGravity(BV(gravity));
     _body->setGravity(BV(gravity));
 }
 }
 
 
 inline bool PhysicsRigidBody::isStatic() const
 inline bool PhysicsRigidBody::isStatic() const
 {
 {
+    GP_ASSERT(_body);
     return _body->isStaticObject();
     return _body->isStaticObject();
 }
 }
 
 

+ 6 - 0
gameplay/src/PhysicsSocketConstraint.cpp

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

+ 8 - 0
gameplay/src/PhysicsSpringConstraint.cpp

@@ -8,6 +8,9 @@ namespace gameplay
 
 
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 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.
     // Initialize the physics rigid body references since we don't call the PhysicsConstraint constructor that does it properly automatically.
     _a = a;
     _a = a;
     _b = b;
     _b = b;
@@ -19,6 +22,9 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, PhysicsRig
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
                                                  PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
                                                  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.
     // Initialize the physics rigid body references since we don't call the PhysicsConstraint constructor that does it properly automatically.
     _a = a;
     _a = a;
     _b = b;
     _b = b;
@@ -44,6 +50,7 @@ PhysicsSpringConstraint::~PhysicsSpringConstraint()
 
 
 void PhysicsSpringConstraint::setStrength(SpringProperty property, float strength)
 void PhysicsSpringConstraint::setStrength(SpringProperty property, float strength)
 {
 {
+    GP_ASSERT(_constraint);
     if (strength < MATH_EPSILON)
     if (strength < MATH_EPSILON)
         ((btGeneric6DofSpringConstraint*)_constraint)->enableSpring(property, false);
         ((btGeneric6DofSpringConstraint*)_constraint)->enableSpring(property, false);
     else
     else
@@ -56,6 +63,7 @@ void PhysicsSpringConstraint::setStrength(SpringProperty property, float strengt
 
 
 void PhysicsSpringConstraint::setDamping(SpringProperty property, float damping)
 void PhysicsSpringConstraint::setDamping(SpringProperty property, float damping)
 {
 {
+    GP_ASSERT(_constraint);
     ((btGeneric6DofSpringConstraint*)_constraint)->setDamping(property, damping);
     ((btGeneric6DofSpringConstraint*)_constraint)->setDamping(property, damping);
     ((btGeneric6DofSpringConstraint*)_constraint)->setEquilibriumPoint(property);
     ((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)
 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).
     // 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 |).
     // Calculate the determinant of the matrix (i.e | n1 n2 n3 |).
     float det = p1._normal.x * (p2._normal.y * p3._normal.z -
     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 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 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 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.x = nx * factor;
         _normal.y = ny * factor;
         _normal.y = ny * factor;

+ 34 - 15
gameplay/src/PlatformAndroid.cpp

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

+ 111 - 34
gameplay/src/PlatformMacOSX.mm

@@ -15,11 +15,11 @@ using namespace std;
 using namespace gameplay;
 using namespace gameplay;
 
 
 // Default to 720p
 // 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 __timeStart;
 static long __timeAbsolute;
 static long __timeAbsolute;
@@ -33,6 +33,8 @@ static bool __leftMouseDown = false;
 static bool __rightMouseDown = false;
 static bool __rightMouseDown = false;
 static bool __otherMouseDown = false;
 static bool __otherMouseDown = false;
 static bool __shiftDown = false;
 static bool __shiftDown = false;
+static char* __title = NULL;
+static bool __fullscreen = false;
 
 
 
 
 long getMachTimeInMilliseconds()
 long getMachTimeInMilliseconds()
@@ -44,6 +46,7 @@ long getMachTimeInMilliseconds()
         (void) mach_timebase_info(&s_timebase_info);
         (void) mach_timebase_info(&s_timebase_info);
     
     
     // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
     // 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));
     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
 - (id) initWithFrame: (NSRect) frame
 {    
 {    
-    NSOpenGLPixelFormatAttribute attrs[] = 
+    NSOpenGLPixelFormatAttribute windowedAttrs[] = 
     {
     {
         NSOpenGLPFAAccelerated,
         NSOpenGLPFAAccelerated,
         NSOpenGLPFADoubleBuffer,
         NSOpenGLPFADoubleBuffer,
@@ -117,6 +120,18 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
         NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
         NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
         0
         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];
     NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
     if (!pf)
     if (!pf)
@@ -135,14 +150,14 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 {
 {
     [super prepareOpenGL];
     [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] 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
     // Make all the OpenGL calls to setup rendering and build the necessary rendering objects
     [[self openGLContext] makeCurrentContext];
     [[self openGLContext] makeCurrentContext];
@@ -160,6 +175,10 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
     CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
     CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
     CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
     
     
+    GLint dim[2] = {__width, __height};
+    CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim);
+    CGLEnable(cglContext, kCGLCESurfaceBackingSize);
+    
     // Activate the display link
     // Activate the display link
     CVDisplayLinkStart(displayLink);
     CVDisplayLinkStart(displayLink);
 }
 }
@@ -204,20 +223,20 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 {
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __leftMouseDown = true;
     __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
 - (void) mouseUp: (NSEvent*) event
 {
 {
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __leftMouseDown = false;
     __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 
 - (void)mouseMoved:(NSEvent *) event 
 {
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     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
 - (void) mouseDragged: (NSEvent*) event
@@ -225,7 +244,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     if (__leftMouseDown)
     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;
     __rightMouseDown = true;
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __lx = point.x;
     __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
 - (void) rightMouseUp: (NSEvent*) event
 {
 {
    __rightMouseDown = false;
    __rightMouseDown = false;
     NSPoint point = [event locationInWindow];
     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
 - (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.
         // Update the pitch and roll by adding the scaled deltas.
         __roll += (float)(point.x - __lx) * ACCELEROMETER_FACTOR_X;
         __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.
         // Clamp the values to the valid range.
         __roll = max(min(__roll, 90.0f), -90.0f);
         __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.
         // Update the last X/Y values.
         __lx = point.x;
         __lx = point.x;
-        __ly = (WINDOW_HEIGHT - point.y);
+        __ly = (__height - point.y);
     }
     }
     
     
     // In right-mouse case, whether __rightMouseDown is true or false
     // In right-mouse case, whether __rightMouseDown is true or false
     // this should not matter, mouse move is still occuring
     // 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 
 - (void)otherMouseDown: (NSEvent *) event 
 {
 {
     __otherMouseDown = true;
     __otherMouseDown = true;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     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 
 - (void)otherMouseUp: (NSEvent *) event 
 {
 {
     __otherMouseDown = false;
     __otherMouseDown = false;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     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 
 - (void)otherMouseDragged: (NSEvent *) event 
 {
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     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
 - (void) mouseEntered: (NSEvent*)event
@@ -296,7 +315,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 - (void)scrollWheel: (NSEvent *) event 
 - (void)scrollWheel: (NSEvent *) event 
 {
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     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
 - (void) mouseExited: (NSEvent*)event
@@ -557,12 +576,25 @@ int getKey(unsigned short keyCode, unsigned int modifierFlags)
 
 
 @end
 @end
 
 
+@interface FullscreenWindow : NSWindow
+{ 
+}
+@end
+
+@implementation FullscreenWindow
+- (BOOL)canBecomeKeyWindow
+{
+    return YES;
+}
+@end
+
 
 
 namespace gameplay
 namespace gameplay
 {
 {
 
 
 extern void printError(const char* format, ...)
 extern void printError(const char* format, ...)
 {
 {
+    GP_ASSERT(format);
     va_list argptr;
     va_list argptr;
     va_start(argptr, format);
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
     vfprintf(stderr, format, argptr);
@@ -591,10 +623,40 @@ Platform* Platform::create(Game* game)
 
 
 int Platform::enterMessagePump()
 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];
     NSAutoreleasePool* pool = [NSAutoreleasePool new];
     NSApplication* app = [NSApplication sharedApplication];
     NSApplication* app = [NSApplication sharedApplication];
     NSRect screenBounds = [[NSScreen mainScreen] frame];
     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];
     __view = [[View alloc] initWithFrame:viewBounds];
     
     
@@ -603,11 +665,23 @@ int Platform::enterMessagePump()
                                  viewBounds.size.width, 
                                  viewBounds.size.width, 
                                  viewBounds.size.height);
                                  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 setAcceptsMouseMovedEvents:YES];
     [window setContentView:__view];
     [window setContentView:__view];
@@ -632,12 +706,12 @@ void Platform::signalShutdown()
     
     
 unsigned int Platform::getDisplayWidth()
 unsigned int Platform::getDisplayWidth()
 {
 {
-    return WINDOW_WIDTH;
+    return __width;
 }
 }
 
 
 unsigned int Platform::getDisplayHeight()
 unsigned int Platform::getDisplayHeight()
 {
 {
-    return WINDOW_HEIGHT;
+    return __height;
 }
 }
 
 
 long Platform::getAbsoluteTime()
 long Platform::getAbsoluteTime()
@@ -672,6 +746,9 @@ bool Platform::isMultiTouch()
     
     
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
 {
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
     *pitch = __pitch;
     *pitch = __pitch;
     *roll = __roll;
     *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)
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(evt, key);
     Form::keyEventInternal(evt, key);
 }
 }
     
     

+ 31 - 23
gameplay/src/PlatformQNX.cpp

@@ -401,6 +401,7 @@ static int getUnicode(int qnxKeyCode)
 
 
 extern void printError(const char* format, ...)
 extern void printError(const char* format, ...)
 {
 {
+    GP_ASSERT(format);
     va_list argptr;
     va_list argptr;
     va_start(argptr, format);
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
     vfprintf(stderr, format, argptr);
@@ -409,6 +410,7 @@ extern void printError(const char* format, ...)
 
 
 EGLenum checkErrorEGL(const char* msg)
 EGLenum checkErrorEGL(const char* msg)
 {
 {
+    GP_ASSERT(msg);
     static const char* errmsg[] =
     static const char* errmsg[] =
     {
     {
         "EGL function succeeded",
         "EGL function succeeded",
@@ -508,7 +510,7 @@ Platform* Platform::create(Game* game)
 
 
     int rc = 0;
     int rc = 0;
     int screenFormat = SCREEN_FORMAT_RGBA8888;
     int screenFormat = SCREEN_FORMAT_RGBA8888;
-#if defined(__QNXNTO__) && defined(__X86__)
+#ifdef __X86__
     int screenUsage = SCREEN_USAGE_OPENGL_ES2;
     int screenUsage = SCREEN_USAGE_OPENGL_ES2;
 #else
 #else
     int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
     int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
@@ -751,6 +753,7 @@ error:
  */
  */
 long timespec2millis(struct timespec *a)
 long timespec2millis(struct timespec *a)
 {
 {
+    GP_ASSERT(a);
     return a->tv_sec*1000 + a->tv_nsec/1000000;
     return a->tv_sec*1000 + a->tv_nsec/1000000;
 }
 }
 
 
@@ -767,12 +770,14 @@ void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEven
 {
 {
     if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, 0))
     if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, 0))
     {
     {
-        Game::getInstance()->touchEvent(touchEvent, x, y, 0);
+        Platform::touchEventInternal(touchEvent, x, y, 0);
     }
     }
 }
 }
 
 
 int Platform::enterMessagePump()
 int Platform::enterMessagePump()
 {
 {
+    GP_ASSERT(_game);
+
     int rc;
     int rc;
     int eventType;
     int eventType;
     int flags;
     int flags;
@@ -788,7 +793,7 @@ int Platform::enterMessagePump()
     __timeStart = timespec2millis(&__timespec);
     __timeStart = timespec2millis(&__timespec);
     __timeAbsolute = 0L;
     __timeAbsolute = 0L;
 
 
-    _game->run(__screenWindowSize[0], __screenWindowSize[1]);
+    _game->run();
 
 
     // Message loop.
     // Message loop.
     while (true)
     while (true)
@@ -849,7 +854,7 @@ int Platform::enterMessagePump()
                         // A move event will be fired unless a button state changed.
                         // A move event will be fired unless a button state changed.
                         bool move = true;
                         bool move = true;
                         bool left_move = false;
                         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_BUTTONS, &buttons);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
                         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]);
                             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 (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
                         {
                         {
                             if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0)
                             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);
                             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 (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
                         {
                         {
                             if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0)
                             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);
                             Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, position[0], position[1], 0);
                         }
                         }
 
 
-                        // Handle mouse wheel events
+                        // Handle mouse wheel events.
                         if (wheel)
                         if (wheel)
                         {
                         {
                             Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, position[0], position[1], -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_FLAGS, &flags);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
                         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;
                         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)
                         if ((flags & KEY_REPEAT) == 0)
                         {
                         {
                             keyEventInternal(evt, getKey(value));
                             keyEventInternal(evt, getKey(value));
@@ -956,20 +961,20 @@ int Platform::enterMessagePump()
                     break;
                     break;
                 case NAVIGATOR_WINDOW_STATE:
                 case NAVIGATOR_WINDOW_STATE:
                 {
                 {
-                	navigator_window_state_t state = navigator_event_get_window_state(event);
-                	switch (state)
-                	{
-                	case NAVIGATOR_WINDOW_FULLSCREEN:
-                		_game->resume();
-                		suspended = false;
-                		break;
-                	case NAVIGATOR_WINDOW_THUMBNAIL:
-                	case NAVIGATOR_WINDOW_INVISIBLE:
-                		_game->pause();
-                		suspended = true;
-                		break;
-                	}
-                	break;
+                    navigator_window_state_t state = navigator_event_get_window_state(event);
+                    switch (state)
+                    {
+                    case NAVIGATOR_WINDOW_FULLSCREEN:
+                        _game->resume();
+                        suspended = false;
+                        break;
+                    case NAVIGATOR_WINDOW_THUMBNAIL:
+                    case NAVIGATOR_WINDOW_INVISIBLE:
+                        _game->pause();
+                        suspended = true;
+                        break;
+                    }
+                    break;
                 }
                 }
                 case NAVIGATOR_EXIT:
                 case NAVIGATOR_EXIT:
                     _game->exit();
                     _game->exit();
@@ -1076,6 +1081,9 @@ bool Platform::isMultiTouch()
 
 
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
 {
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
     switch(__orientationAngle)
     switch(__orientationAngle)
     {
     {
     // Landscape based device adjusting for landscape game mode
     // 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)
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(evt, key);
     Form::keyEventInternal(evt, key);
 }
 }
 
 

+ 143 - 62
gameplay/src/PlatformWin32.cpp

@@ -8,9 +8,11 @@
 #include <GL/wglew.h>
 #include <GL/wglew.h>
 #include <windowsx.h>
 #include <windowsx.h>
 
 
+using gameplay::printError;
+
 // Default to 720p
 // Default to 720p
-#define WINDOW_WIDTH    1280
-#define WINDOW_HEIGHT   720
+static int __width = 1280;
+static int __height = 720;
 
 
 static long __timeTicksPerMillis;
 static long __timeTicksPerMillis;
 static long __timeStart;
 static long __timeStart;
@@ -247,23 +249,25 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
 
 
 void UpdateCapture(LPARAM lParam)
 void UpdateCapture(LPARAM lParam)
 {
 {
-	if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
-		SetCapture(__hwnd);
-	else
-		ReleaseCapture();
+    if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
+        SetCapture(__hwnd);
+    else
+        ReleaseCapture();
 }
 }
 
 
 LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
 {
     if (hwnd != __hwnd)
     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.
     // 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;
     static int lx, ly;
 
 
@@ -290,7 +294,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int x = GET_X_LPARAM(lParam);
         int x = GET_X_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
 
 
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
         if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
         {
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0);
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0);
@@ -306,60 +310,60 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         {
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0);
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0);
         }
         }
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         return 0;
         return 0;
     }
     }
     case WM_RBUTTONDOWN:
     case WM_RBUTTONDOWN:
-		UpdateCapture(wParam);
-		lx = GET_X_LPARAM(lParam);
+        UpdateCapture(wParam);
+        lx = GET_X_LPARAM(lParam);
         ly = GET_Y_LPARAM(lParam);
         ly = GET_Y_LPARAM(lParam);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, lx, ly, 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, lx, ly, 0);
         break;
         break;
 
 
     case WM_RBUTTONUP:
     case WM_RBUTTONUP:
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         break;
         break;
 
 
     case WM_MBUTTONDOWN:
     case WM_MBUTTONDOWN:
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         break;
         break;
 
 
     case WM_MBUTTONUP:
     case WM_MBUTTONUP:
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         break;
         break;
 
 
     case WM_MOUSEMOVE:
     case WM_MOUSEMOVE:
-    {
+    {
         int x = GET_X_LPARAM(lParam);
         int x = GET_X_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
 
 
-		// Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
-		if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
-		{
-			if ((wParam & MK_LBUTTON) == MK_LBUTTON)
-			{
-				// Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
-				gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
-				return 0;
-			}
-			else if ((wParam & MK_RBUTTON) == MK_RBUTTON)
-			{
-				// Update the pitch and roll by adding the scaled deltas.
-				__roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
-				__pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
-
-				// Clamp the values to the valid range.
-				__roll = max(min(__roll, 90.0f), -90.0f);
-				__pitch = max(min(__pitch, 90.0f), -90.0f);
-
-				// Update the last X/Y values.
-				lx = x;
-				ly = y;
-			}
-		}
+        // Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
+        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
+        {
+            if ((wParam & MK_LBUTTON) == MK_LBUTTON)
+            {
+                // Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
+                gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
+                return 0;
+            }
+            else if ((wParam & MK_RBUTTON) == MK_RBUTTON)
+            {
+                // Update the pitch and roll by adding the scaled deltas.
+                __roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
+                __pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
+
+                // Clamp the values to the valid range.
+                __roll = max(min(__roll, 90.0f), -90.0f);
+                __pitch = max(min(__pitch, 90.0f), -90.0f);
+
+                // Update the last X/Y values.
+                lx = x;
+                ly = y;
+            }
+        }
         break;
         break;
     }
     }
 
 
@@ -374,7 +378,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         if (wParam == VK_CAPITAL)
         if (wParam == VK_CAPITAL)
             capsOn = !capsOn;
             capsOn = !capsOn;
 
 
-        // Suppress key repeats
+        // Suppress key repeats.
         if ((lParam & 0x40000000) == 0)
         if ((lParam & 0x40000000) == 0)
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown ^ capsOn));
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown ^ capsOn));
         break;
         break;
@@ -387,13 +391,13 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         break;
         break;
 
 
     case WM_CHAR:
     case WM_CHAR:
-        // Suppress key repeats
+        // Suppress key repeats.
         if ((lParam & 0x40000000) == 0)
         if ((lParam & 0x40000000) == 0)
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
         break;
         break;
 
 
     case WM_UNICHAR:
     case WM_UNICHAR:
-        // Suppress key repeats
+        // Suppress key repeats.
         if ((lParam & 0x40000000) == 0)
         if ((lParam & 0x40000000) == 0)
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
             gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
         break;
         break;
@@ -449,6 +453,8 @@ Platform::~Platform()
 
 
 Platform* Platform::create(Game* game)
 Platform* Platform::create(Game* game)
 {
 {
+    GP_ASSERT(game);
+
     FileSystem::setResourcePath("./");
     FileSystem::setResourcePath("./");
 
 
     Platform* platform = new Platform(game);
     Platform* platform = new Platform(game);
@@ -457,9 +463,40 @@ Platform* Platform::create(Game* game)
     __hinstance = ::GetModuleHandle(NULL);
     __hinstance = ::GetModuleHandle(NULL);
 
 
     LPCTSTR windowClass = L"gameplay";
     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.
     // Register our window class.
     WNDCLASSEX wc;
     WNDCLASSEX wc;
@@ -477,24 +514,47 @@ Platform* Platform::create(Game* game)
     wc.lpszClassName  = windowClass;
     wc.lpszClassName  = windowClass;
 
 
     if (!::RegisterClassEx(&wc))
     if (!::RegisterClassEx(&wc))
+    {
+        GP_ERROR("Failed to register window class.");
         goto error;
         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.
     // 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.
     // Adjust the window rectangle so the client size is the requested size.
     AdjustWindowRectEx(&rect, style, FALSE, styleEx);
     AdjustWindowRectEx(&rect, style, FALSE, styleEx);
-    
+
     // Create the native Windows window.
     // 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.
     // Get the drawing context.
     __hdc = GetDC(__hwnd);
     __hdc = GetDC(__hwnd);
 
 
     // Center the window
     // 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);
     ::SetWindowPos(__hwnd, __hwnd, screenX, screenY, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
 
 
     // Choose pixel format. 32-bit. RGBA.
     // Choose pixel format. 32-bit. RGBA.
@@ -510,14 +570,25 @@ Platform* Platform::create(Game* game)
 
 
     int pixelFormat = ChoosePixelFormat(__hdc, &pfd);
     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;
         goto error;
+    }
+    if (!SetPixelFormat (__hdc, pixelFormat, &pfd))
+    {
+        GP_ERROR("Failed to set the pixel format.");
+        goto error;
+    }
 
 
     HGLRC tempContext = wglCreateContext(__hdc);
     HGLRC tempContext = wglCreateContext(__hdc);
     wglMakeCurrent(__hdc, tempContext);
     wglMakeCurrent(__hdc, tempContext);
 
 
     if (GLEW_OK != glewInit())
     if (GLEW_OK != glewInit())
+    {
+        GP_ERROR("Failed to initialize GLEW.");
         goto error;
         goto error;
+    }
 
 
     int attribs[] =
     int attribs[] =
     {
     {
@@ -529,17 +600,21 @@ Platform* Platform::create(Game* game)
     if (!(__hrc = wglCreateContextAttribsARB(__hdc, 0, attribs) ) )
     if (!(__hrc = wglCreateContextAttribsARB(__hdc, 0, attribs) ) )
     {
     {
         wglDeleteContext(tempContext);
         wglDeleteContext(tempContext);
+        GP_ERROR("Failed to create OpenGL context.");
         goto error;
         goto error;
     }
     }
     wglDeleteContext(tempContext);
     wglDeleteContext(tempContext);
 
 
     if (!wglMakeCurrent(__hdc, __hrc) || !__hrc)
     if (!wglMakeCurrent(__hdc, __hrc) || !__hrc)
+    {
+        GP_ERROR("Failed to make the window current.");
         goto error;
         goto error;
+    }
 
 
     // Vertical sync.
     // Vertical sync.
     wglSwapIntervalEXT(__vsync ? 1 : 0);
     wglSwapIntervalEXT(__vsync ? 1 : 0);
 
 
-    // Show the window
+    // Show the window.
     ShowWindow(__hwnd, SW_SHOW);
     ShowWindow(__hwnd, SW_SHOW);
 
 
     return platform;
     return platform;
@@ -551,7 +626,8 @@ error:
 }
 }
 
 
 int Platform::enterMessagePump()
 int Platform::enterMessagePump()
-{  
+{
+    GP_ASSERT(_game);
     int rc = 0;
     int rc = 0;
 
 
     // Get the initial time.
     // Get the initial time.
@@ -560,6 +636,7 @@ int Platform::enterMessagePump()
     __timeTicksPerMillis = (long)(tps.QuadPart / 1000L);
     __timeTicksPerMillis = (long)(tps.QuadPart / 1000L);
     LARGE_INTEGER queryTime;
     LARGE_INTEGER queryTime;
     QueryPerformanceCounter(&queryTime);
     QueryPerformanceCounter(&queryTime);
+    GP_ASSERT(__timeTicksPerMillis);
     __timeStart = queryTime.QuadPart / __timeTicksPerMillis;
     __timeStart = queryTime.QuadPart / __timeTicksPerMillis;
 
 
     // Set the initial pitch and roll values.
     // Set the initial pitch and roll values.
@@ -569,7 +646,7 @@ int Platform::enterMessagePump()
     SwapBuffers(__hdc);
     SwapBuffers(__hdc);
 
 
     if (_game->getState() != Game::RUNNING)
     if (_game->getState() != Game::RUNNING)
-        _game->run(WINDOW_WIDTH, WINDOW_HEIGHT);
+        _game->run();
 
 
     // Enter event dispatch loop.
     // Enter event dispatch loop.
     MSG msg;
     MSG msg;
@@ -602,18 +679,19 @@ void Platform::signalShutdown()
 
 
 unsigned int Platform::getDisplayWidth()
 unsigned int Platform::getDisplayWidth()
 {
 {
-    return WINDOW_WIDTH;
+    return __width;
 }
 }
 
 
 unsigned int Platform::getDisplayHeight()
 unsigned int Platform::getDisplayHeight()
 {
 {
-    return WINDOW_HEIGHT;
+    return __height;
 }
 }
     
     
 long Platform::getAbsoluteTime()
 long Platform::getAbsoluteTime()
 {
 {
-       LARGE_INTEGER queryTime;
+    LARGE_INTEGER queryTime;
     QueryPerformanceCounter(&queryTime);
     QueryPerformanceCounter(&queryTime);
+    GP_ASSERT(__timeTicksPerMillis);
     __timeAbsolute = queryTime.QuadPart / __timeTicksPerMillis;
     __timeAbsolute = queryTime.QuadPart / __timeTicksPerMillis;
 
 
     return __timeAbsolute;
     return __timeAbsolute;
@@ -646,6 +724,9 @@ bool Platform::isMultiTouch()
 
 
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
 {
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
     *pitch = __pitch;
     *pitch = __pitch;
     *roll = __roll;
     *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)
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(evt, key);
     Form::keyEventInternal(evt, key);
 }
 }
 
 

+ 4 - 2
gameplay/src/PlatformiOS.mm

@@ -146,7 +146,7 @@ int getKey(unichar keyCode);
         
         
         _game = Game::getInstance();
         _game = Game::getInstance();
         __timeStart = getMachTimeInMilliseconds();
         __timeStart = getMachTimeInMilliseconds();
-        _game->run(WINDOW_WIDTH, WINDOW_HEIGHT);          
+        _game->run();
     }
     }
     return self;
     return self;
 }
 }
@@ -549,6 +549,7 @@ long getMachTimeInMilliseconds()
         (void) mach_timebase_info(&s_timebase_info);
         (void) mach_timebase_info(&s_timebase_info);
     
     
     // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
     // 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));
     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, ...)
 extern void printError(const char* format, ...)
 {
 {
+    GP_ASSERT(format);
     va_list argptr;
     va_list argptr;
     va_start(argptr, format);
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
     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)
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
 {
-    gameplay::Game::getInstance()->keyEvent(evt, key);
+    Game::getInstance()->keyEvent(evt, key);
     Form::keyEventInternal(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;
     std::vector<Properties*>::const_iterator it;
     for (it = copy._namespaces.begin(); it < copy._namespaces.end(); it++)
     for (it = copy._namespaces.begin(); it < copy._namespaces.end(); it++)
     {
     {
+        GP_ASSERT(*it);
         _namespaces.push_back(new Properties(**it));
         _namespaces.push_back(new Properties(**it));
     }
     }
     rewind();
     rewind();
@@ -48,11 +49,9 @@ Properties::Properties(FILE* file, const char* name, const char* id, const char*
 
 
 Properties* Properties::create(const char* url)
 Properties* Properties::create(const char* url)
 {
 {
-    GP_ASSERT(url);
-
     if (!url || strlen(url) == 0)
     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;
         return NULL;
     }
     }
 
 
@@ -82,6 +81,7 @@ Properties* Properties::create(const char* url)
     FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     if (!file)
     if (!file)
     {
     {
+        GP_ERROR("Failed to open file '%s'.", fileString.c_str());
         return NULL;
         return NULL;
     }
     }
 
 
@@ -102,6 +102,12 @@ Properties* Properties::create(const char* url)
         {
         {
             while (true)
             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 (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
                 {
                 {
                     if (i != size - 1)
                     if (i != size - 1)
@@ -117,11 +123,6 @@ Properties* Properties::create(const char* url)
                 }
                 }
                 
                 
                 iter = properties->getNextNamespace();
                 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)
 void Properties::readProperties(FILE* file)
 {
 {
+    GP_ASSERT(file);
+
     char line[2048];
     char line[2048];
     int c;
     int c;
     char* name;
     char* name;
@@ -155,6 +158,7 @@ void Properties::readProperties(FILE* file)
         rc = fgets(line, 2048, file);
         rc = fgets(line, 2048, file);
         if (rc == NULL)
         if (rc == NULL)
         {
         {
+            GP_ERROR("Error reading line from file.");
             return;
             return;
         }
         }
 
 
@@ -173,7 +177,7 @@ void Properties::readProperties(FILE* file)
                 name = strtok(line, " =\t");
                 name = strtok(line, " =\t");
                 if (name == NULL)
                 if (name == NULL)
                 {
                 {
-                    GP_ERROR("Error parsing properties file: value without name.");
+                    GP_ERROR("Error parsing properties file: attribute without name.");
                     return;
                     return;
                 }
                 }
 
 
@@ -181,7 +185,8 @@ void Properties::readProperties(FILE* file)
                 value = strtok(NULL, "=");
                 value = strtok(NULL, "=");
                 if (value == 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.
                 // Remove white-space from value.
@@ -214,7 +219,8 @@ void Properties::readProperties(FILE* file)
                 name = trimWhiteSpace(name);
                 name = trimWhiteSpace(name);
                 if (name == NULL)
                 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] == '}')
                 else if (name[0] == '}')
                 {
                 {
@@ -260,7 +266,8 @@ void Properties::readProperties(FILE* file)
                         else
                         else
                         {
                         {
                             // Back up from fgetc()
                             // 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".
                             // Store "name value" as a name/value pair, or even just "name".
                             if (value != NULL)
                             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
     // 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.
     // non-whitespace character, we put the cursor back in front of it.
     if (c != EOF)
     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)
 char* Properties::trimWhiteSpace(char *str)
@@ -375,6 +387,7 @@ void Properties::resolveInheritance(const char* id)
                 std::vector<Properties*>::const_iterator itt;
                 std::vector<Properties*>::const_iterator itt;
                 for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); itt++)
                 for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); itt++)
                 {
                 {
+                    GP_ASSERT(*itt);
                     derived->_namespaces.push_back(new Properties(**itt));
                     derived->_namespaces.push_back(new Properties(**itt));
                 }
                 }
                 derived->rewind();
                 derived->rewind();
@@ -404,6 +417,8 @@ void Properties::resolveInheritance(const char* id)
 
 
 void Properties::mergeWith(Properties* overrides)
 void Properties::mergeWith(Properties* overrides)
 {
 {
+    GP_ASSERT(overrides);
+
     // Overwrite or add each property found in child.
     // Overwrite or add each property found in child.
     char* value = new char[255];
     char* value = new char[255];
     overrides->rewind();
     overrides->rewind();
@@ -505,15 +520,17 @@ void Properties::rewind()
     _namespacesItr = _namespaces.end();
     _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;
     Properties* ret = NULL;
     std::vector<Properties*>::const_iterator it;
     std::vector<Properties*>::const_iterator it;
     
     
     for (it = _namespaces.begin(); it < _namespaces.end(); it++)
     for (it = _namespaces.begin(); it < _namespaces.end(); it++)
     {
     {
         ret = *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;
             return ret;
         }
         }
@@ -547,6 +564,8 @@ bool Properties::exists(const char* name) const
 
 
 const bool isStringNumeric(const char* str)
 const bool isStringNumeric(const char* str)
 {
 {
+    GP_ASSERT(str);
+
     // The first character may be '-'
     // The first character may be '-'
     if (*str == '-')
     if (*str == '-')
         str++;
         str++;
@@ -587,7 +606,6 @@ Properties::Type Properties::getType(const char* name) const
 
 
     // Parse the value to determine the format
     // Parse the value to determine the format
     unsigned int commaCount = 0;
     unsigned int commaCount = 0;
-    //unsigned int length = strlen(value);
     char* valuePtr = const_cast<char*>(value);
     char* valuePtr = const_cast<char*>(value);
     while (valuePtr = strchr(valuePtr, ','))
     while (valuePtr = strchr(valuePtr, ','))
     {
     {
@@ -654,7 +672,7 @@ int Properties::getInt(const char* name) const
         scanned = sscanf(valueString, "%d", &value);
         scanned = sscanf(valueString, "%d", &value);
         if (scanned != 1)
         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 0;
         }
         }
         return value;
         return value;
@@ -673,7 +691,7 @@ float Properties::getFloat(const char* name) const
         scanned = sscanf(valueString, "%f", &value);
         scanned = sscanf(valueString, "%f", &value);
         if (scanned != 1)
         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 0.0f;
         }
         }
         return value;
         return value;
@@ -692,7 +710,7 @@ long Properties::getLong(const char* name) const
         scanned = sscanf(valueString, "%ld", &value);
         scanned = sscanf(valueString, "%ld", &value);
         if (scanned != 1)
         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 0L;
         }
         }
         return value;
         return value;
@@ -716,7 +734,7 @@ bool Properties::getMatrix(const char* name, Matrix* out) const
 
 
         if (scanned != 16)
         if (scanned != 16)
         {
         {
-            GP_ERROR("Error parsing property: %s", name);
+            GP_ERROR("Error attempting to parse property '%s' as a matrix.", name);
             out->setIdentity();
             out->setIdentity();
             return false;
             return false;
         }
         }
@@ -741,7 +759,7 @@ bool Properties::getVector2(const char* name, Vector2* out) const
         scanned = sscanf(valueString, "%f,%f", &x, &y);
         scanned = sscanf(valueString, "%f,%f", &x, &y);
         if (scanned != 2)
         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);
             out->set(0.0f, 0.0f);
             return false;
             return false;
         }
         }
@@ -766,7 +784,7 @@ bool Properties::getVector3(const char* name, Vector3* out) const
         scanned = sscanf(valueString, "%f,%f,%f", &x, &y, &z);
         scanned = sscanf(valueString, "%f,%f,%f", &x, &y, &z);
         if (scanned != 3)
         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);
             out->set(0.0f, 0.0f, 0.0f);
             return false;
             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);
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &w);
         if (scanned != 4)
         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);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             return false;
             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);
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &theta);
         if (scanned != 4)
         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);
             out->set(0.0f, 0.0f, 0.0f, 1.0f);
             return false;
             return false;
         }
         }
@@ -840,14 +858,19 @@ bool Properties::getColor(const char* name, Vector3* out) const
             valueString[0] != '#')
             valueString[0] != '#')
         {
         {
             // Not a color string.
             // 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);
             out->set(0.0f, 0.0f, 0.0f);
             return false;
             return false;
         }
         }
 
 
         // Read the string into an int as hex.
         // Read the string into an int as hex.
         unsigned int color;
         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));
         out->set(Vector3::fromColor(color));
         return true;
         return true;
@@ -868,14 +891,19 @@ bool Properties::getColor(const char* name, Vector4* out) const
             valueString[0] != '#')
             valueString[0] != '#')
         {
         {
             // Not a color string.
             // 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);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             return false;
             return false;
         }
         }
 
 
         // Read the string into an int as hex.
         // Read the string into an int as hex.
         unsigned int color;
         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));
         out->set(Vector4::fromColor(color));
         return true;
         return true;
@@ -898,6 +926,7 @@ Properties* Properties::clone()
     unsigned int count = _namespaces.size();
     unsigned int count = _namespaces.size();
     for (unsigned int i = 0; i < count; i++)
     for (unsigned int i = 0; i < count; i++)
     {
     {
+        GP_ASSERT(_namespaces[i]);
         p->_namespaces.push_back(_namespaces[i]->clone());
         p->_namespaces.push_back(_namespaces[i]->clone());
     }
     }
     p->_namespacesItr = p->_namespaces.end();
     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:
     // This line defines a namespace of type "mynamespace" without an ID:
     mynamespace
     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 
         texture spriteTexture 
         {
         {
             fileName = sprite.png
             fileName = sprite.png
@@ -125,6 +126,8 @@ namespace gameplay
  */
  */
 class Properties
 class Properties
 {
 {
+    friend class Game;
+
 public:
 public:
 
 
     /**
     /**
@@ -176,14 +179,18 @@ public:
     void rewind();
     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.
      * 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)
 void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
 {
 {
-    GP_ASSERT(dst);
-
     m.getRotation(dst);
     m.getRotation(dst);
 }
 }
 
 
@@ -89,6 +87,8 @@ void Quaternion::conjugate()
 
 
 void Quaternion::conjugate(Quaternion* dst) const
 void Quaternion::conjugate(Quaternion* dst) const
 {
 {
+    GP_ASSERT(dst);
+
     dst->x = -x;
     dst->x = -x;
     dst->y = -y;
     dst->y = -y;
     dst->z = -z;
     dst->z = -z;
@@ -102,6 +102,8 @@ bool Quaternion::inverse()
 
 
 bool Quaternion::inverse(Quaternion* dst) const
 bool Quaternion::inverse(Quaternion* dst) const
 {
 {
+    GP_ASSERT(dst);
+
     float n = x * x + y * y + z * z + w * w;
     float n = x * x + y * y + z * z + w * w;
     if (n == 1.0f)
     if (n == 1.0f)
     {
     {
@@ -113,7 +115,7 @@ bool Quaternion::inverse(Quaternion* dst) const
         return true;
         return true;
     }
     }
 
 
-    // too close to zero
+    // Too close to zero.
     if (n < 0.000001f)
     if (n < 0.000001f)
         return false;
         return false;
 
 
@@ -133,6 +135,8 @@ void Quaternion::multiply(const Quaternion& q)
 
 
 void Quaternion::multiply(const Quaternion& q1, const Quaternion& q2, Quaternion* dst)
 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 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 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;
     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)
 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);
     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)
 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));
     GP_ASSERT(!(t < 0.0f || t > 1.0f));
 
 
     Quaternion dstQ(0.0f, 0.0f, 0.0f, 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)
 void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
 {
 {
+    GP_ASSERT(dst);
+
     // cos(omega) = q1 * q2;
     // cos(omega) = q1 * q2;
     // slerp(q1, q2, t) = (q1*sin((1-t)*omega) + q2*sin(t*omega))/sin(omega);
     // slerp(q1, q2, t) = (q1*sin((1-t)*omega) + q2*sin(t*omega))/sin(omega);
     // q1 = +- q2, slerp(q1,q2,t) = q1.
     // 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
     // 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.
     // 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) ||
         (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;
         return Ray::INTERSECTS_NONE;
     }
     }
 
 
     // Otherwise, the intersection distance is the minimum positive intersection distance.
     // Otherwise, the intersection distance is the minimum positive intersection distance.
     float d = (nD > 0.0f) ? nD : 0.0f;
     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;
     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,
     // 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.
     // then the ray is parallel to the plane and does not intersect it.
-    if ( dot == 0.0f )
+    if (dot == 0.0f)
     {
     {
         return INTERSECTS_NONE;
         return INTERSECTS_NONE;
     }
     }
@@ -156,12 +156,14 @@ void Ray::transform(const Matrix& matrix)
 void Ray::normalize()
 void Ray::normalize()
 {
 {
     if (_direction.isZero())
     if (_direction.isZero())
+    {
+        GP_ERROR("Invalid ray object; a ray's direction must be non-zero.");
         return;
         return;
+    }
 
 
     // Normalize the ray's direction vector.
     // Normalize the ray's direction vector.
     float normalizeFactor = 1.0f / sqrt(_direction.x * _direction.x + _direction.y * _direction.y + _direction.z * _direction.z);
     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.x *= normalizeFactor;
         _direction.y *= 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)
 void Rectangle::combine(const Rectangle& r1, const Rectangle& r2, Rectangle* dst)
 {
 {
+    GP_ASSERT(dst);
+
     dst->x = min(r1.x, r2.x);
     dst->x = min(r1.x, r2.x);
     dst->y = min(r1.y, r2.y);
     dst->y = min(r1.y, r2.y);
     dst->width = max(r1.x + r1.width, r2.x + r2.width) - dst->x;
     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
     // Dump Ref object memory leaks
     if (__refAllocationCount == 0)
     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
     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)
         for (RefAllocationRecord* rec = __refAllocations; rec != NULL; rec = rec->next)
         {
         {
             Ref* ref = rec->ref;
             Ref* ref = rec->ref;
+            GP_ASSERT(ref);
             const char* type = typeid(*ref).name();
             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)
 void* trackRef(Ref* ref)
 {
 {
-    // Create memory allocation record
+    GP_ASSERT(ref);
+
+    // Create memory allocation record.
     RefAllocationRecord* rec = (RefAllocationRecord*)malloc(sizeof(RefAllocationRecord));
     RefAllocationRecord* rec = (RefAllocationRecord*)malloc(sizeof(RefAllocationRecord));
     rec->ref = ref;
     rec->ref = ref;
     rec->next = __refAllocations;
     rec->next = __refAllocations;
@@ -100,14 +103,20 @@ void* trackRef(Ref* ref)
 
 
 void untrackRef(Ref* ref, void* record)
 void untrackRef(Ref* ref, void* record)
 {
 {
+    if (!record)
+    {
+        printError("[memory] ERROR: Attempting to free null ref tracking record.\n");
+        return;
+    }
+
     RefAllocationRecord* rec = (RefAllocationRecord*)record;
     RefAllocationRecord* rec = (RefAllocationRecord*)record;
     if (rec->ref != ref)
     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;
         return;
     }
     }
 
 
-    // Link this item out
+    // Link this item out.
     if (__refAllocations == rec)
     if (__refAllocations == rec)
         __refAllocations = rec->next;
         __refAllocations = rec->next;
     if (rec->prev)
     if (rec->prev)

+ 99 - 53
gameplay/src/RenderState.cpp

@@ -29,11 +29,7 @@ RenderState::~RenderState()
     // Destroy all the material parameters
     // Destroy all the material parameters
     for (unsigned int i = 0, count = _parameters.size(); i < count; ++i)
     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);
     GP_ASSERT(name);
 
 
+    // Search for an existing parameter with this name.
     MaterialParameter* param;
     MaterialParameter* param;
-
-    // Search for an existing parameter with this name
     for (unsigned int i = 0, count = _parameters.size(); i < count; ++i)
     for (unsigned int i = 0, count = _parameters.size(); i < count; ++i)
     {
     {
         param = _parameters[i];
         param = _parameters[i];
+        GP_ASSERT(param);
         if (strcmp(param->getName(), name) == 0)
         if (strcmp(param->getName(), name) == 0)
         {
         {
             return param;
             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);
     param = new MaterialParameter(name);
     _parameters.push_back(param);
     _parameters.push_back(param);
 
 
@@ -75,10 +71,12 @@ MaterialParameter* RenderState::getParameter(const char* name) const
 
 
 void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBinding)
 void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBinding)
 {
 {
-    // Store the auto-binding
+    GP_ASSERT(name);
+
+    // Store the auto-binding.
     if (autoBinding == NONE)
     if (autoBinding == NONE)
     {
     {
-        // Clear current auto binding
+        // Clear current auto binding.
         std::map<std::string, AutoBinding>::iterator itr = _autoBindings.find(name);
         std::map<std::string, AutoBinding>::iterator itr = _autoBindings.find(name);
         if (itr != _autoBindings.end())
         if (itr != _autoBindings.end())
         {
         {
@@ -87,11 +85,11 @@ void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBind
     }
     }
     else
     else
     {
     {
-        // Set new auto binding
+        // Set new auto binding.
         _autoBindings[name] = autoBinding;
         _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)
     if (_nodeBinding)
     {
     {
         applyAutoBinding(name, autoBinding);
         applyAutoBinding(name, autoBinding);
@@ -100,9 +98,10 @@ void RenderState::setParameterAutoBinding(const char* name, AutoBinding autoBind
 
 
 void RenderState::setParameterAutoBinding(const char* name, const char* autoBinding)
 void RenderState::setParameterAutoBinding(const char* name, const char* autoBinding)
 {
 {
+    GP_ASSERT(autoBinding);
     AutoBinding value = NONE;
     AutoBinding value = NONE;
 
 
-    // Parse the passed in autoBinding string
+    // Parse the passed in autoBinding string.
     if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
     if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
     {
     {
         value = WORLD_MATRIX;
         value = WORLD_MATRIX;
@@ -147,6 +146,10 @@ void RenderState::setParameterAutoBinding(const char* name, const char* autoBind
     {
     {
         value = MATRIX_PALETTE;
         value = MATRIX_PALETTE;
     }
     }
+    else
+    {
+        // Ignore all other cases (the value was previously set to the default of NONE).
+    }
 
 
     if (value != NONE)
     if (value != NONE)
     {
     {
@@ -185,7 +188,7 @@ void RenderState::setNodeBinding(Node* node)
 
 
     if (_nodeBinding)
     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();
         std::map<std::string, AutoBinding>::const_iterator itr = _autoBindings.begin();
         while (itr != _autoBindings.end())
         while (itr != _autoBindings.end())
         {
         {
@@ -197,46 +200,57 @@ void RenderState::setNodeBinding(Node* node)
 
 
 void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBinding)
 void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBinding)
 {
 {
+    MaterialParameter* param = getParameter(uniformName);
     switch (autoBinding)
     switch (autoBinding)
     {
     {
     case WORLD_MATRIX:
     case WORLD_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getWorldMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getWorldMatrix);
         break;
         break;
 
 
     case VIEW_MATRIX:
     case VIEW_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getViewMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getViewMatrix);
         break;
         break;
 
 
     case PROJECTION_MATRIX:
     case PROJECTION_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getProjectionMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getProjectionMatrix);
         break;
         break;
 
 
     case WORLD_VIEW_MATRIX:
     case WORLD_VIEW_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getWorldViewMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getWorldViewMatrix);
         break;
         break;
 
 
     case VIEW_PROJECTION_MATRIX:
     case VIEW_PROJECTION_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getViewProjectionMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getViewProjectionMatrix);
         break;
         break;
 
 
     case WORLD_VIEW_PROJECTION_MATRIX:
     case WORLD_VIEW_PROJECTION_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
         break;
         break;
 
 
     case INVERSE_TRANSPOSE_WORLD_MATRIX:
     case INVERSE_TRANSPOSE_WORLD_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
         break;
         break;
 
 
     case INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX:
     case INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
         break;
         break;
 
 
     case CAMERA_WORLD_POSITION:
     case CAMERA_WORLD_POSITION:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
         break;
         break;
 
 
     case CAMERA_VIEW_POSITION:
     case CAMERA_VIEW_POSITION:
-        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
+        GP_ASSERT(param);
+        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
         break;
         break;
 
 
     case MATRIX_PALETTE:
     case MATRIX_PALETTE:
@@ -245,15 +259,22 @@ void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBind
             MeshSkin* skin = model ? model->getSkin() : NULL;
             MeshSkin* skin = model ? model->getSkin() : NULL;
             if (skin)
             if (skin)
             {
             {
-                getParameter(uniformName)->bindValue(skin, &MeshSkin::getMatrixPalette, &MeshSkin::getMatrixPaletteSize);
+                GP_ASSERT(param);
+                param->bindValue(skin, &MeshSkin::getMatrixPalette, &MeshSkin::getMatrixPaletteSize);
             }
             }
         }
         }
         break;
         break;
+
+    default:
+        GP_ERROR("Unsupported auto binding type (%d).", autoBinding);
+        break;
     }
     }
 }
 }
 
 
 void RenderState::bind(Pass* pass)
 void RenderState::bind(Pass* pass)
 {
 {
+    GP_ASSERT(pass);
+
     // Get the combined modified state bits for our RenderState hierarchy.
     // Get the combined modified state bits for our RenderState hierarchy.
     long stateOverrideBits = _state ? _state->_bits : 0;
     long stateOverrideBits = _state ? _state->_bits : 0;
     RenderState* rs = _parent;
     RenderState* rs = _parent;
@@ -276,6 +297,7 @@ void RenderState::bind(Pass* pass)
     {
     {
         for (unsigned int i = 0, count = rs->_parameters.size(); i < count; ++i)
         for (unsigned int i = 0, count = rs->_parameters.size(); i < count; ++i)
         {
         {
+            GP_ASSERT(rs->_parameters[i]);
             rs->_parameters[i]->bind(effect);
             rs->_parameters[i]->bind(effect);
         }
         }
 
 
@@ -291,7 +313,7 @@ RenderState* RenderState::getTopmost(RenderState* below)
     RenderState* rs = this;
     RenderState* rs = this;
     if (rs == below)
     if (rs == below)
     {
     {
-        // Nothing below ourself
+        // Nothing below ourself.
         return NULL;
         return NULL;
     }
     }
 
 
@@ -299,7 +321,7 @@ RenderState* RenderState::getTopmost(RenderState* below)
     {
     {
         if (rs->_parent == below || rs->_parent == NULL)
         if (rs->_parent == below || rs->_parent == NULL)
         {
         {
-            // Stop traversing up here
+            // Stop traversing up here.
             return rs;
             return rs;
         }
         }
         rs = rs->_parent;
         rs = rs->_parent;
@@ -310,6 +332,8 @@ RenderState* RenderState::getTopmost(RenderState* below)
 
 
 void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context) const
 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)
     for (std::map<std::string, AutoBinding>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
     {
     {
         renderState->setParameterAutoBinding(it->first.c_str(), it->second);
         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)
     for (std::vector<MaterialParameter*>::const_iterator it = _parameters.begin(); it != _parameters.end(); ++it)
     {
     {
         const MaterialParameter* param = *it;
         const MaterialParameter* param = *it;
+        GP_ASSERT(param);
 
 
         MaterialParameter* paramCopy = new MaterialParameter(param->getName());
         MaterialParameter* paramCopy = new MaterialParameter(param->getName());
         param->cloneInto(paramCopy);
         param->cloneInto(paramCopy);
@@ -368,31 +393,42 @@ void RenderState::StateBlock::bind()
 
 
 void RenderState::StateBlock::bindNoRestore()
 void RenderState::StateBlock::bindNoRestore()
 {
 {
+    GP_ASSERT(_defaultState);
+
     // Update any state that differs from _defaultState and flip _defaultState bits
     // Update any state that differs from _defaultState and flip _defaultState bits
     if ((_bits & RS_BLEND) && (_blendEnabled != _defaultState->_blendEnabled))
     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;
         _defaultState->_blendEnabled = _blendEnabled;
     }
     }
     if ((_bits & RS_BLEND_FUNC) && (_srcBlend != _defaultState->_srcBlend || _dstBlend != _defaultState->_dstBlend))
     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->_srcBlend = _srcBlend;
         _defaultState->_dstBlend = _dstBlend;
         _defaultState->_dstBlend = _dstBlend;
     }
     }
     if ((_bits & RS_CULL_FACE) && (_cullFaceEnabled != _defaultState->_cullFaceEnabled))
     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;
         _defaultState->_cullFaceEnabled = _cullFaceEnabled;
     }
     }
     if ((_bits & RS_DEPTH_TEST) && (_depthTestEnabled != _defaultState->_depthTestEnabled))
     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;
         _defaultState->_depthTestEnabled = _depthTestEnabled;
     }
     }
     if ((_bits & RS_DEPTH_WRITE) && (_depthWriteEnabled != _defaultState->_depthWriteEnabled))
     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;
         _defaultState->_depthWriteEnabled = _depthWriteEnabled;
     }
     }
 
 
@@ -401,7 +437,9 @@ void RenderState::StateBlock::bindNoRestore()
 
 
 void RenderState::StateBlock::restore(long stateOverrideBits)
 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)
     if (_defaultState->_bits == 0)
     {
     {
         return;
         return;
@@ -410,32 +448,32 @@ void RenderState::StateBlock::restore(long stateOverrideBits)
     // Restore any state that is not overridden and is not default
     // Restore any state that is not overridden and is not default
     if (!(stateOverrideBits & RS_BLEND) && (_defaultState->_bits & RS_BLEND))
     if (!(stateOverrideBits & RS_BLEND) && (_defaultState->_bits & RS_BLEND))
     {
     {
-        glDisable(GL_BLEND);
+        GL_ASSERT( glDisable(GL_BLEND) );
         _defaultState->_bits &= ~RS_BLEND;
         _defaultState->_bits &= ~RS_BLEND;
         _defaultState->_blendEnabled = false;
         _defaultState->_blendEnabled = false;
     }
     }
     if (!(stateOverrideBits & RS_BLEND_FUNC) && (_defaultState->_bits & RS_BLEND_FUNC))
     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->_bits &= ~RS_BLEND_FUNC;
         _defaultState->_srcBlend = RenderState::BLEND_ONE;
         _defaultState->_srcBlend = RenderState::BLEND_ONE;
         _defaultState->_dstBlend = RenderState::BLEND_ONE;
         _defaultState->_dstBlend = RenderState::BLEND_ONE;
     }
     }
     if (!(stateOverrideBits & RS_CULL_FACE) && (_defaultState->_bits & RS_CULL_FACE))
     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->_bits &= ~RS_CULL_FACE;
         _defaultState->_cullFaceEnabled = false;
         _defaultState->_cullFaceEnabled = false;
     }
     }
     if (!(stateOverrideBits & RS_DEPTH_TEST) && (_defaultState->_bits & RS_DEPTH_TEST))
     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->_bits &= ~RS_DEPTH_TEST;
         _defaultState->_depthTestEnabled = false;
         _defaultState->_depthTestEnabled = false;
     }
     }
     if (!(stateOverrideBits & RS_DEPTH_WRITE) && (_defaultState->_bits & RS_DEPTH_WRITE))
     if (!(stateOverrideBits & RS_DEPTH_WRITE) && (_defaultState->_bits & RS_DEPTH_WRITE))
     {
     {
-        glDepthMask(GL_TRUE);
+        GL_ASSERT( glDepthMask(GL_TRUE) );
         _defaultState->_bits &= ~RS_DEPTH_WRITE;
         _defaultState->_bits &= ~RS_DEPTH_WRITE;
         _defaultState->_depthWriteEnabled = true;
         _defaultState->_depthWriteEnabled = true;
     }
     }
@@ -443,12 +481,14 @@ void RenderState::StateBlock::restore(long stateOverrideBits)
 
 
 void RenderState::StateBlock::enableDepthWrite()
 void RenderState::StateBlock::enableDepthWrite()
 {
 {
+    GP_ASSERT(_defaultState);
+
     // Internal method used by Game::clear() to restore depth writing before a
     // 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
     // clear operation. This is neccessary if the last code to draw before the
     // next frame leaves depth writing disabled.
     // next frame leaves depth writing disabled.
     if (!_defaultState->_depthWriteEnabled)
     if (!_defaultState->_depthWriteEnabled)
     {
     {
-        glDepthMask(GL_TRUE);
+        GL_ASSERT( glDepthMask(GL_TRUE) );
         _defaultState->_bits &= ~RS_DEPTH_WRITE;
         _defaultState->_bits &= ~RS_DEPTH_WRITE;
         _defaultState->_depthWriteEnabled = true;
         _defaultState->_depthWriteEnabled = true;
     }
     }
@@ -456,6 +496,8 @@ void RenderState::StateBlock::enableDepthWrite()
 
 
 bool parseBoolean(const char* value)
 bool parseBoolean(const char* value)
 {
 {
+    GP_ASSERT(value);
+
     if (strlen(value) == 4)
     if (strlen(value) == 4)
     {
     {
         return (
         return (
@@ -470,35 +512,39 @@ bool parseBoolean(const char* value)
 
 
 RenderState::Blend parseBlend(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::string upper(value);
     std::transform(upper.begin(), upper.end(), upper.begin(), (int(*)(int))toupper);
     std::transform(upper.begin(), upper.end(), upper.begin(), (int(*)(int))toupper);
     if (upper == "ZERO")
     if (upper == "ZERO")
         return RenderState::BLEND_ZERO;
         return RenderState::BLEND_ZERO;
-    if (upper == "ONE")
+    else if (upper == "ONE")
         return RenderState::BLEND_ONE;
         return RenderState::BLEND_ONE;
-    if (upper == "SRC_ALPHA")
+    else if (upper == "SRC_ALPHA")
         return RenderState::BLEND_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;
         return RenderState::BLEND_ONE_MINUS_SRC_ALPHA;
-    if (upper == "DST_ALPHA")
+    else if (upper == "DST_ALPHA")
         return RenderState::BLEND_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;
         return RenderState::BLEND_ONE_MINUS_DST_ALPHA;
-    if (upper == "CONSTANT_ALPHA")
+    else if (upper == "CONSTANT_ALPHA")
         return RenderState::BLEND_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;
         return RenderState::BLEND_ONE_MINUS_CONSTANT_ALPHA;
-    if (upper == "SRC_ALPHA_SATURATE")
+    else if (upper == "SRC_ALPHA_SATURATE")
         return RenderState::BLEND_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)
 void RenderState::StateBlock::setState(const char* name, const char* value)
 {
 {
-    GP_ASSERT(name && value);
+    GP_ASSERT(name);
 
 
     if (strcmp(name, "blend") == 0)
     if (strcmp(name, "blend") == 0)
     {
     {
@@ -526,7 +572,7 @@ void RenderState::StateBlock::setState(const char* name, const char* value)
     }
     }
     else
     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;
 static std::vector<RenderTarget*> __renderTargets;
 
 
 RenderTarget::RenderTarget(const char* id)
 RenderTarget::RenderTarget(const char* id)
-    : _id(id), _texture(NULL)
+    : _id(id ? id : ""), _texture(NULL)
 {
 {
 }
 }
 
 
@@ -15,7 +15,7 @@ RenderTarget::~RenderTarget()
 {
 {
     SAFE_RELEASE(_texture);
     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);
     std::vector<RenderTarget*>::iterator it = std::find(__renderTargets.begin(), __renderTargets.end(), this);
     if (it != __renderTargets.end())
     if (it != __renderTargets.end())
     {
     {
@@ -25,10 +25,11 @@ RenderTarget::~RenderTarget()
 
 
 RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned int height)
 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);
     Texture* texture = Texture::create(Texture::RGBA, width, height, NULL, false);
     if (texture == NULL)
     if (texture == NULL)
     {
     {
+        GP_ERROR("Failed to create texture for render target.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -42,11 +43,14 @@ RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned
 
 
 RenderTarget* RenderTarget::getRenderTarget(const char* id)
 RenderTarget* RenderTarget::getRenderTarget(const char* id)
 {
 {
+    GP_ASSERT(id);
+
     // Search the vector for a matching ID.
     // Search the vector for a matching ID.
     std::vector<RenderTarget*>::const_iterator it;
     std::vector<RenderTarget*>::const_iterator it;
     for (it = __renderTargets.begin(); it < __renderTargets.end(); it++)
     for (it = __renderTargets.begin(); it < __renderTargets.end(); it++)
     {
     {
         RenderTarget* dst = *it;
         RenderTarget* dst = *it;
+        GP_ASSERT(dst);
         if (strcmp(id, dst->getID()) == 0)
         if (strcmp(id, dst->getID()) == 0)
         {
         {
             return dst;
             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* Scene::addNode(const char* id)
 {
 {
     Node* node = Node::create(id);
     Node* node = Node::create(id);
+    GP_ASSERT(node);
     addNode(node);
     addNode(node);
 
 
     // Call release to decrement the ref count to 1 before returning.
     // 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
             // Unbind the active camera from the audio listener
             if (audioListener && (audioListener->getCamera() == _activeCamera))
             if (audioListener && (audioListener->getCamera() == _activeCamera))
             {
             {
-                AudioListener::getInstance()->setCamera(NULL);
+                audioListener->setCamera(NULL);
             }
             }
 
 
             SAFE_RELEASE(_activeCamera);
             SAFE_RELEASE(_activeCamera);
@@ -251,7 +252,7 @@ void Scene::setActiveCamera(Camera* camera)
 
 
             if (audioListener && _bindAudioListenerToCamera)
             if (audioListener && _bindAudioListenerToCamera)
             {
             {
-                AudioListener::getInstance()->setCamera(_activeCamera);
+                audioListener->setCamera(_activeCamera);
             }
             }
         }
         }
     }
     }
@@ -309,6 +310,7 @@ Material* createDebugMaterial()
 
 
     Effect* effect = Effect::createFromSource(vs_str, fs_str);
     Effect* effect = Effect::createFromSource(vs_str, fs_str);
     Material* material = Material::create(effect);
     Material* material = Material::create(effect);
+    GP_ASSERT(material && material->getStateBlock());
     material->getStateBlock()->setDepthTest(true);
     material->getStateBlock()->setDepthTest(true);
 
 
     SAFE_RELEASE(effect);
     SAFE_RELEASE(effect);
@@ -359,6 +361,8 @@ struct DebugVertex
 
 
 void drawDebugLine(MeshBatch* batch, const Vector3& point1, const Vector3& point2, const Vector3& color)
 void drawDebugLine(MeshBatch* batch, const Vector3& point1, const Vector3& point2, const Vector3& color)
 {
 {
+    GP_ASSERT(batch);
+
     static DebugVertex verts[2];
     static DebugVertex verts[2];
 
 
     verts[0].x = point1.x;
     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)
 void drawDebugNode(MeshBatch* batch, Node* node, unsigned int debugFlags)
 {
 {
+    GP_ASSERT(node);
     Model* model = node->getModel();
     Model* model = node->getModel();
 
 
     if ((debugFlags & Scene::DEBUG_BOXES) && model)
     if ((debugFlags & Scene::DEBUG_BOXES) && model)
     {
     {
+        GP_ASSERT(model->getMesh());
+
         MeshSkin* skin = model->getSkin();
         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,
             // 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
             // 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());
         drawDebugSphere(batch, node->getBoundingSphere());
     }
     }
 
 
-    Node* child = node->getFirstChild();
-    while (child)
+    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
     {
     {
         drawDebugNode(batch, child, debugFlags);
         drawDebugNode(batch, child, debugFlags);
-        child = child->getNextSibling();
     }
     }
 }
 }
 
 
@@ -507,17 +512,19 @@ void Scene::drawDebug(unsigned int debugFlags)
 
 
     _debugBatch->begin();
     _debugBatch->begin();
 
 
-    Node* node = _firstNode;
-    while (node)
+    for (Node* node = _firstNode; node != NULL; node = node->_nextSibling)
     {
     {
         drawDebugNode(_debugBatch, node, debugFlags);
         drawDebugNode(_debugBatch, node, debugFlags);
-        node = node->_nextSibling;
     }
     }
 
 
     _debugBatch->end();
     _debugBatch->end();
 
 
     if (_activeCamera)
     if (_activeCamera)
+    {
+        GP_ASSERT(_debugBatch->getMaterial());
+        GP_ASSERT(_debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix"));
         _debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_activeCamera->getViewProjectionMatrix());
         _debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_activeCamera->getViewProjectionMatrix());
+    }
 
 
     _debugBatch->draw();
     _debugBatch->draw();
 }
 }

+ 125 - 59
gameplay/src/SceneLoader.cpp

@@ -13,23 +13,19 @@ std::string SceneLoader::_path;
 
 
 Scene* SceneLoader::load(const char* url)
 Scene* SceneLoader::load(const char* url)
 {
 {
-    GP_ASSERT(url);
-
     // Load the scene properties from file.
     // Load the scene properties from file.
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
-    GP_ASSERT(properties);
     if (properties == NULL)
     if (properties == NULL)
     {
     {
-        GP_WARN("Failed to load scene file: %s", url);
+        GP_ERROR("Failed to load scene file '%s'.", url);
         return NULL;
         return NULL;
     }
     }
 
 
     // Check if the properties object is valid and has a valid namespace.
     // Check if the properties object is valid and has a valid namespace.
     Properties* sceneProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
     Properties* sceneProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
-    GP_ASSERT(sceneProperties);
     if (!sceneProperties || !(strcmp(sceneProperties->getNamespace(), "scene") == 0))
     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);
         SAFE_DELETE(properties);
         return NULL;
         return NULL;
     }
     }
@@ -45,6 +41,7 @@ Scene* SceneLoader::load(const char* url)
     Scene* scene = loadMainSceneData(sceneProperties);
     Scene* scene = loadMainSceneData(sceneProperties);
     if (!scene)
     if (!scene)
     {
     {
+        GP_ERROR("Failed to load main scene from bundle.");
         SAFE_DELETE(properties);
         SAFE_DELETE(properties);
         return NULL;
         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)
     for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
     {
     {
         SceneNode& sceneNode = _sceneNodes[i];
         SceneNode& sceneNode = _sceneNodes[i];
+        GP_ASSERT(sceneNode._nodeID);
 
 
         if (sceneNode._exactMatch)
         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);
             Node* node = scene->findNode(sceneNode._nodeID);
             if (!node)
             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;
                 continue;
             }
             }
 
 
@@ -172,11 +170,14 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
         }
         }
         else
         else
         {
         {
-            // Find all nodes matching the specified ID
+            // Find all nodes matching the specified ID.
             std::vector<Node*> nodes;
             std::vector<Node*> nodes;
             unsigned int nodeCount = scene->findNodes(sceneNode._nodeID, nodes, true, false);
             unsigned int nodeCount = scene->findNodes(sceneNode._nodeID, nodes, true, false);
             if (nodeCount == 0)
             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;
                 continue;
+            }
             
             
             for (unsigned int j = 0, pcount = sceneNode._properties.size(); j < pcount; ++j)
             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];
         Properties* p = _propertiesFromFile[snp._file];
         if (!p)
         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;
             return;
         }
         }
 
 
@@ -214,7 +215,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             p = p->getNamespace(snp._id.c_str());
             p = p->getNamespace(snp._id.c_str());
             if (!p)
             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;
                 return;
             }
             }
         }
         }
@@ -236,7 +237,10 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         }
         }
         case SceneNodeProperty::MATERIAL:
         case SceneNodeProperty::MATERIAL:
             if (!node->getModel())
             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
             else
             {
             {
                 Material* material = Material::create(p);
                 Material* material = Material::create(p);
@@ -259,7 +263,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             Properties* p = _propertiesFromFile[snp._file];
             Properties* p = _propertiesFromFile[snp._file];
             if (!p)
             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;
                 return;
             }
             }
 
 
@@ -269,7 +273,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 p = p->getNamespace(snp._id.c_str());
                 p = p->getNamespace(snp._id.c_str());
                 if (!p)
                 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;
                     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.
             // 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)
             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)
             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)
             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
             else
             {
             {
@@ -300,13 +307,19 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 const char* name = NULL;
                 const char* name = NULL;
                 if (np && (name = np->getString("rigidBodyModel")))
                 if (np && (name = np->getString("rigidBodyModel")))
                 {
                 {
+                    GP_ASSERT(scene);
                     Node* modelNode = scene->findNode(name);
                     Node* modelNode = scene->findNode(name);
                     if (!modelNode)
                     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
                     else
                     {
                     {
                         if (!modelNode->getModel())
                         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
                         else
                         {
                         {
                             // Temporarily set rigidBody model on model so it's used during collision object creation.
                             // 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;
             break;
         }
         }
         default:
         default:
-            // This cannot happen.
+            GP_ERROR("Unsupported node property type (%d).", snp._type);
             break;
             break;
         }
         }
     }
     }
     else
     else
     {
     {
-        // Handle Scale, Rotate and Translate
+        // Handle scale, rotate and translate.
         Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
         Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
         const char* name = NULL;
         const char* name = NULL;
 
 
@@ -379,7 +392,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             break;
             break;
         }
         }
         default:
         default:
-            GP_WARN("Unsupported node property type: %d.", snp._type);
+            GP_ERROR("Unsupported node property type (%d).", snp._type);
             break;
             break;
         }
         }
     }
     }
@@ -387,6 +400,8 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
 
 
 void SceneLoader::applyNodeUrls(Scene* scene)
 void SceneLoader::applyNodeUrls(Scene* scene)
 {
 {
+    GP_ASSERT(scene);
+
     // Apply all URL node properties so that when we go to apply
     // Apply all URL node properties so that when we go to apply
     // the other node properties, the node is in the scene.
     // the other node properties, the node is in the scene.
     for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
     for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
@@ -416,7 +431,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     }
                     }
                     else
                     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
                 else
@@ -427,10 +442,13 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     unsigned int nodeCount = scene->findNodes(snp._id.c_str(), nodes, true, false);
                     unsigned int nodeCount = scene->findNodes(snp._id.c_str(), nodes, true, false);
                     if (nodeCount > 0)
                     if (nodeCount > 0)
                     {
                     {
+                        GP_ASSERT(sceneNode._nodeID);
+
                         for (unsigned int k = 0; k < nodeCount; ++k)
                         for (unsigned int k = 0; k < nodeCount; ++k)
                         {
                         {
                             // Construct a new node ID using _nodeID plus the remainder of the partial match.
                             // Construct a new node ID using _nodeID plus the remainder of the partial match.
                             Node* node = nodes[k];
                             Node* node = nodes[k];
+                            GP_ASSERT(node);
                             std::string newID(sceneNode._nodeID);
                             std::string newID(sceneNode._nodeID);
                             newID += (node->getId() + snp._id.length());
                             newID += (node->getId() + snp._id.length());
                             node->setId(newID.c_str());
                             node->setId(newID.c_str());
@@ -438,7 +456,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     }
                     }
                     else
                     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
                         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
                     else
@@ -494,7 +512,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                         }
                         }
                         if (matchCount == 0)
                         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
                 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)
             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;
                 continue;
             }
             }
 
 
-            // Add a SceneNode to the end of the list
+            // Add a SceneNode to the end of the list.
             _sceneNodes.resize(_sceneNodes.size() + 1);
             _sceneNodes.resize(_sceneNodes.size() + 1);
             SceneNode& sceneNode = _sceneNodes[_sceneNodes.size()-1];
             SceneNode& sceneNode = _sceneNodes[_sceneNodes.size()-1];
             sceneNode._nodeID = ns->getId();
             sceneNode._nodeID = ns->getId();
@@ -596,7 +614,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 }
                 else
                 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();
                     const char* animationID = animation->getId();
                     if (strlen(animationID) == 0)
                     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;
                         continue;
                     }
                     }
 
 
                     const char* url = animation->getString("url");
                     const char* url = animation->getString("url");
                     if (!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;
                         continue;
                     }
                     }
                     const char* targetID = animation->getString("target");
                     const char* targetID = animation->getString("target");
                     if (!targetID)
                     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;
                         continue;
                     }
                     }
 
 
@@ -632,7 +650,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 }
                 else
                 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
         else
         {
         {
             // TODO: Should we ignore these items? They could be used for generic properties file inheritance.
             // 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);
         Node* node = scene->findNode(_animations[i]._targetID);
         if (!node)
         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;
             continue;
         }
         }
 
 
@@ -667,7 +685,7 @@ void SceneLoader::createAnimations(const Scene* scene)
         Properties* p = _propertiesFromFile[_animations[i]._file];
         Properties* p = _propertiesFromFile[_animations[i]._file];
         if (!p)
         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;
             continue;
         }
         }
         if (_animations[i]._id.size() > 0)
         if (_animations[i]._id.size() > 0)
@@ -675,7 +693,7 @@ void SceneLoader::createAnimations(const Scene* scene)
             p = p->getNamespace(_animations[i]._id.c_str());
             p = p->getNamespace(_animations[i]._id.c_str());
             if (!p)
             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;
                 continue;
             }
             }
         }
         }
@@ -686,7 +704,10 @@ void SceneLoader::createAnimations(const Scene* scene)
 
 
 PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 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.
     // Create the constraint from the specified properties.
     Quaternion roA;
     Quaternion roA;
@@ -714,6 +735,7 @@ PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constrai
     {
     {
         physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, rbB);
         physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, rbB);
     }
     }
+    GP_ASSERT(physicsConstraint);
 
 
     // Set the optional parameters that were specified.
     // Set the optional parameters that were specified.
     Vector3 v;
     Vector3 v;
@@ -731,6 +753,9 @@ PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constrai
 
 
 PhysicsConstraint* SceneLoader::loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 PhysicsConstraint* SceneLoader::loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
 {
+    GP_ASSERT(rbA);
+    GP_ASSERT(constraint);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
     PhysicsHingeConstraint* physicsConstraint = NULL;
     PhysicsHingeConstraint* physicsConstraint = NULL;
 
 
     // Create the constraint from the specified properties.
     // 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);
         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;
     return physicsConstraint;
 }
 }
 
 
 Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
 Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
 {
 {
+    GP_ASSERT(sceneProperties);
+
     // Load the main scene from the specified path.
     // Load the main scene from the specified path.
     Bundle* bundle = Bundle::create(_path.c_str());
     Bundle* bundle = Bundle::create(_path.c_str());
     if (!bundle)
     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;
         return NULL;
     }
     }
 
 
@@ -778,7 +822,7 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
     Scene* scene = bundle->loadScene(NULL);
     Scene* scene = bundle->loadScene(NULL);
     if (!scene)
     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);
         SAFE_RELEASE(bundle);
         return NULL;
         return NULL;
     }
     }
@@ -798,6 +842,10 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
 
 
 void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
 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.
     // Go through the supported global physics properties and apply them.
     Vector3 gravity;
     Vector3 gravity;
     if (physics->getVector3("gravity", &gravity))
     if (physics->getVector3("gravity", &gravity))
@@ -817,18 +865,18 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
             name = constraint->getString("rigidBodyA");
             name = constraint->getString("rigidBodyA");
             if (!name)
             if (!name)
             {
             {
-                GP_WARN("Missing property 'rigidBodyA' for constraint %s", constraint->getId());
+                GP_ERROR("Missing property 'rigidBodyA' for constraint '%s'.", constraint->getId());
                 continue;
                 continue;
             }
             }
             Node* rbANode = scene->findNode(name);
             Node* rbANode = scene->findNode(name);
             if (!rbANode)
             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;
                 continue;
             }
             }
             if (!rbANode->getCollisionObject() || rbANode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
             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;
                 continue;
             }
             }
             PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
             PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
@@ -844,12 +892,12 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                 Node* rbBNode = scene->findNode(name);
                 Node* rbBNode = scene->findNode(name);
                 if (!rbBNode)
                 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;
                     continue;
                 }
                 }
                 if (!rbBNode->getCollisionObject() || rbBNode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
                 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;
                     continue;
                 }
                 }
                 rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
                 rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
@@ -878,18 +926,25 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
             {
             {
                 physicsConstraint = loadSpringConstraint(constraint, rbA, rbB);
                 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 the constraint failed to load, continue on to the next one.
             if (!physicsConstraint)
             if (!physicsConstraint)
+            {
+                GP_ERROR("Failed to create physics constraint.");
                 continue;
                 continue;
+            }
 
 
             // If the breaking impulse was specified, apply it to the constraint.
             // If the breaking impulse was specified, apply it to the constraint.
-            if (constraint->getString("breakingImpulse"))
+            if (constraint->exists("breakingImpulse"))
                 physicsConstraint->setBreakingImpulse(constraint->getFloat("breakingImpulse"));
                 physicsConstraint->setBreakingImpulse(constraint->getFloat("breakingImpulse"));
         }
         }
         else
         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++)
     for (; iter != _propertiesFromFile.end(); iter++)
     {
     {
         Properties* p = Properties::create(iter->first.c_str());
         Properties* p = Properties::create(iter->first.c_str());
-        GP_ASSERT(p);
         if (p == NULL)
         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;
         iter->second = p;
     }
     }
@@ -911,6 +965,10 @@ void SceneLoader::loadReferencedFiles()
 
 
 PhysicsConstraint* SceneLoader::loadSocketConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 PhysicsConstraint* SceneLoader::loadSocketConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
 {
+    GP_ASSERT(rbA);
+    GP_ASSERT(constraint);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+
     PhysicsSocketConstraint* physicsConstraint = NULL;
     PhysicsSocketConstraint* physicsConstraint = NULL;
     Vector3 toA;
     Vector3 toA;
     bool offsetSpecified = constraint->getVector3("translationOffsetA", &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)
 PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
 {
+    GP_ASSERT(rbA);
+    GP_ASSERT(constraint);
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+
     if (!rbB)
     if (!rbB)
     {
     {
-        GP_WARN("Spring constraints require two rigid bodies.");
+        GP_ERROR("Spring constraints require two rigid bodies.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -963,6 +1025,7 @@ PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constrain
     {
     {
         physicsConstraint = Game::getInstance()->getPhysicsController()->createSpringConstraint(rbA, rbB);
         physicsConstraint = Game::getInstance()->getPhysicsController()->createSpringConstraint(rbA, rbB);
     }
     }
+    GP_ASSERT(physicsConstraint);
 
 
     // Set the optional parameters that were specified.
     // Set the optional parameters that were specified.
     Vector3 v;
     Vector3 v;
@@ -1005,7 +1068,10 @@ PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constrain
 void SceneLoader::splitURL(const char* url, std::string* file, std::string* id)
 void SceneLoader::splitURL(const char* url, std::string* file, std::string* id)
 {
 {
     if (!url)
     if (!url)
+    {
+        // This is allowed since many scene node properties do not use the URL.
         return;
         return;
+    }
 
 
     std::string urlString = url;
     std::string urlString = url;
 
 

+ 4 - 4
gameplay/src/ScreenDisplayer.h

@@ -14,10 +14,10 @@ class ScreenDisplayer
 {
 {
 public:
 public:
 
 
-	/**
-	 * Constructor.
-	 */
-	ScreenDisplayer();
+    /**
+     * Constructor.
+     */
+    ScreenDisplayer();
 
 
     /**
     /**
      * Displays a screen using the {@link Game#renderOnce} mechanism for at least the given amount of time.
      * Displays a screen using the {@link Game#renderOnce} mechanism for at least the given amount of time.

+ 124 - 59
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)
 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);
     bool customEffect = (effect != NULL);
     if (!customEffect)
     if (!customEffect)
@@ -94,7 +96,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
             __spriteEffect = Effect::createFromSource(SPRITE_VSH, SPRITE_FSH);
             __spriteEffect = Effect::createFromSource(SPRITE_VSH, SPRITE_FSH);
             if (__spriteEffect == NULL)
             if (__spriteEffect == NULL)
             {
             {
-                GP_ERROR("Unable to load sprite effect.");
+                GP_ERROR("Unable to create default sprite effect.");
                 return NULL;
                 return NULL;
             }
             }
 
 
@@ -120,25 +122,33 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     }
     }
     if (!samplerUniform)
     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);
         SAFE_RELEASE(effect);
         return NULL;
         return NULL;
     }
     }
 
 
-    // Wrap the effect in a material
+    // Wrap the effect in a material.
     Material* material = Material::create(effect); // +ref effect
     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()->setBlend(true);
     material->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
     material->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
     material->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_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
     Texture::Sampler* sampler = Texture::Sampler::create(texture); // +ref texture
+    GP_ASSERT(material->getParameter(samplerUniform->getName()));
     material->getParameter(samplerUniform->getName())->setValue(sampler);
     material->getParameter(samplerUniform->getName())->setValue(sampler);
     SAFE_RELEASE(sampler);
     SAFE_RELEASE(sampler);
 
 
-    // Define the vertex format for the batch
+    // Define the vertex format for the batch.
     VertexFormat::Element vertexElements[] =
     VertexFormat::Element vertexElements[] =
     {
     {
         VertexFormat::Element(VertexFormat::POSITION, 3),
         VertexFormat::Element(VertexFormat::POSITION, 3),
@@ -147,11 +157,11 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     };
     };
     VertexFormat vertexFormat(vertexElements, 3);
     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);
     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
     material->release(); // don't call SAFE_RELEASE since material is used below
 
 
-    // Create the batch
+    // Create the batch.
     SpriteBatch* batch = new SpriteBatch();
     SpriteBatch* batch = new SpriteBatch();
     batch->_customEffect = customEffect;
     batch->_customEffect = customEffect;
     batch->_batch = meshBatch;
     batch->_batch = meshBatch;
@@ -159,6 +169,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
 
 
     // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
     // 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);
     material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getOrthoMatrix);
 
 
     return batch;
     return batch;
@@ -166,6 +177,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
 
 
 void SpriteBatch::begin()
 void SpriteBatch::begin()
 {
 {
+    GP_ASSERT(_batch);
     _batch->begin();
     _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,
 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)
                        const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
 {
 {
+    GP_ASSERT(_batch);
+
     float x = dst.x;
     float x = dst.x;
     float y = dst.y;
     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,
 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)
     float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
 {
 {
+    GP_ASSERT(_batch);
+
     // Calculate the vertex positions.
     // Calculate the vertex positions.
     //static Vector3 p[4];
     //static Vector3 p[4];
 
 
@@ -345,65 +361,49 @@ void SpriteBatch::draw(float x, float y, float width, float height, float u1, fl
 
 
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip)
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip)
 {
 {
-    // Clip the rectangle given by { x, y, width, height } into clip.
-    // We need to scale the uvs accordingly as we do this.
-
-    // First check to see if we need to draw at all.
-    if (x + width < clip.x || x > clip.x + clip.width ||
-        y + height < clip.y || y > clip.y + clip.height)
-    {
-        return;
-    }
-
-    const float uvWidth = u2 - u1;
-    const float uvHeight = v2 - v1;
+    // Only draw if at least part of the sprite is within the clip region.
+    if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
+        draw(x, y, 0, width, height, u1, v1, u2, v2, color);
+}
 
 
-    // Moving x to the right.
-    if (x < clip.x)
-    {
-        const float percent = (clip.x - x) / width;
-        const float dx = clip.x - x;
-        x = clip.x;
-        width -= dx;
-        u1 += uvWidth * percent;
-    }
+void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, SpriteBatch::SpriteVertex* vertices)
+{
+    GP_ASSERT(vertices);
 
 
-    // Moving y down.
-    if (y < clip.y)
-    {
-        const float percent = (clip.y - y) / height;
-        const float dy = clip.y - y;
-        y = clip.y;
-        height -= dy;
-        v1 += uvHeight * percent;
-    }
+    const float x2 = x + width;
+    const float y2 = y + height;
+    ADD_SPRITE_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
+}
 
 
-    // Moving width to the left.
-    const float clipX2 = clip.x + clip.width;
-    float x2 = x + width;
-    if (x2 > clipX2)
-    {
-        const float percent = (x2 - clipX2) / width;
-        width = clipX2 - x;
-        u2 -= uvWidth * percent;
-    }
+void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip, SpriteBatch::SpriteVertex* vertices)
+{
+    GP_ASSERT(vertices);
 
 
-    // Moving height up.
-    const float clipY2 = clip.y + clip.height;
-    float y2 = y + height;
-    if (y2 > clipY2)
+    // Only add a sprite if at least part of the sprite is within the clip region.
+    if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
     {
     {
-        const float percent = (y2 - clipY2) / height;
-        height = clipY2 - y;
-        v2 -= uvHeight * percent;
+        const float x2 = x + width;
+        const float y2 = y + height;
+        ADD_SPRITE_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
     }
     }
+}
 
 
-    // Now we can perform a normal draw call.
-    draw(x, y, 0, width, height, u1, v1, u2, v2, color);
+void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
+{
+    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)
 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.
     // Treat the given position as the center if the user specified it as such.
     if (positionIsCenter)
     if (positionIsCenter)
     {
     {
@@ -427,24 +427,28 @@ void SpriteBatch::draw(float x, float y, float z, float width, float height, flo
 
 
 void SpriteBatch::end()
 void SpriteBatch::end()
 {
 {
-    // Finish and draw the batch
+    // Finish and draw the batch.
+    GP_ASSERT(_batch);
     _batch->end();
     _batch->end();
     _batch->draw();
     _batch->draw();
 }
 }
 
 
 RenderState::StateBlock* SpriteBatch::getStateBlock() const
 RenderState::StateBlock* SpriteBatch::getStateBlock() const
 {
 {
+    GP_ASSERT(_batch && _batch->getMaterial());
     return _batch->getMaterial()->getStateBlock();
     return _batch->getMaterial()->getStateBlock();
 }
 }
 
 
-Material* SpriteBatch::getMaterial()
+Material* SpriteBatch::getMaterial() const
 {
 {
+    GP_ASSERT(_batch);
     return _batch->getMaterial();
     return _batch->getMaterial();
 }
 }
 
 
 void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 {
 {
     // Bind the specified matrix to a parameter named 'u_projectionMatrix' (assumed to exist).
     // 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);
     _batch->getMaterial()->getParameter("u_projectionMatrix")->setValue(matrix);
 }
 }
 
 
@@ -456,4 +460,65 @@ const Matrix& SpriteBatch::getOrthoMatrix() const
     return _projectionMatrix;
     return _projectionMatrix;
 }
 }
 
 
+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.
+
+    // First check to see if we need to draw at all.
+    if (x + width < clip.x || x > clip.x + clip.width ||
+        y + height < clip.y || y > clip.y + clip.height)
+    {
+        return false;
+    }
+
+    const float uvWidth = u2 - u1;
+    const float uvHeight = v2 - v1;
+
+    // Moving x to the right.
+    if (x < clip.x)
+    {
+        const float percent = (clip.x - x) / width;
+        const float dx = clip.x - x;
+        x = clip.x;
+        width -= dx;
+        u1 += uvWidth * percent;
+    }
+
+    // Moving y down.
+    if (y < clip.y)
+    {
+        const float percent = (clip.y - y) / height;
+        const float dy = clip.y - y;
+        y = clip.y;
+        height -= dy;
+        v1 += uvHeight * percent;
+    }
+
+    // Moving width to the left.
+    const float clipX2 = clip.x + clip.width;
+    float x2 = x + width;
+    if (x2 > clipX2)
+    {
+        const float percent = (x2 - clipX2) / width;
+        width = clipX2 - x;
+        u2 -= uvWidth * percent;
+    }
+
+    // Moving height up.
+    const float clipY2 = clip.y + clip.height;
+    float y2 = y + height;
+    if (y2 > clipY2)
+    {
+        const float percent = (y2 - clipY2) / height;
+        height = clipY2 - y;
+        v2 -= uvHeight * percent;
+    }
+
+    return true;
+}
+
 }
 }

+ 68 - 53
gameplay/src/SpriteBatch.h

@@ -25,60 +25,10 @@ namespace gameplay
 class SpriteBatch
 class SpriteBatch
 {
 {
     friend class Bundle;
     friend class Bundle;
+    friend class Font;
 
 
 public:
 public:
 
 
-    /**
-     * Sprite vertex structure used for batching.
-     */
-    struct SpriteVertex
-    {
-        /**
-         * The x coordinate of the vertex.
-         */
-        float x;
-    
-        /**
-         * The y coordinate of the vertex.
-         */
-        float y;
-    
-        /**
-         * The z coordinate of the vertex.
-         */
-        float z;
-
-        /**
-         * The u component of the (u, v) texture coordinates for the vertex.
-         */
-        float u;
-    
-        /**
-         * The v component of the (u, v) texture coordinates for the vertex.
-         */
-        float v;
-
-        /**
-         * The red color component of the vertex.
-         */
-        float r;
-    
-        /**
-         * The green color component of the vertex.
-         */
-        float g;
-    
-        /**
-         * The blue color component of the vertex.
-         */
-        float b;
-    
-        /**
-         * The alpha component of the vertex.
-         */
-        float a;
-    };
-
     /**
     /**
      * Creates a new SpriteBatch for drawing sprites with the given texture.
      * Creates a new SpriteBatch for drawing sprites with the given texture.
      *
      *
@@ -214,7 +164,7 @@ public:
      * @param rotationAngle The rotation angle.
      * @param rotationAngle The rotation angle.
      */
      */
     void draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height, 
     void 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);
+              float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle);
 
 
     /**
     /**
      * Draws a single sprite.
      * Draws a single sprite.
@@ -288,7 +238,7 @@ public:
      * 
      * 
      * @return The material.
      * @return The material.
      */
      */
-    Material* getMaterial();
+    Material* getMaterial() const;
 
 
     /**
     /**
      * Sets a custom projection matrix to use with the sprite batch.
      * Sets a custom projection matrix to use with the sprite batch.
@@ -304,6 +254,22 @@ public:
 
 
 private:
 private:
 
 
+    /**
+     * Sprite vertex structure used for batching.
+     */
+    struct SpriteVertex
+    {
+        float x;        
+        float y;
+        float z;
+        float u;
+        float v;
+        float r;
+        float g;
+        float b;
+        float a;
+    };
+
     /**
     /**
      * Constructor.
      * Constructor.
      */
      */
@@ -318,6 +284,55 @@ private:
 
 
     const Matrix& getOrthoMatrix() const;
     const Matrix& getOrthoMatrix() const;
 
 
+    /**
+     * Adds a single sprite to a SpriteVertex array.
+     * 
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param width The sprite width.
+     * @param height The sprite height
+     * @param u1 Texture coordinate.
+     * @param v1 Texture coordinate.
+     * @param u2 Texture coordinate.
+     * @param v2 Texture coordinate.
+     * @param color The color to tint the sprite. Use white for no tint.
+     * @param clip The clip rectangle.
+     */
+    void addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, SpriteBatch::SpriteVertex* vertices);
+
+    /**
+     * Adds a single sprite to a SpriteVertex array, clipped within a rectangle.
+     * 
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param width The sprite width.
+     * @param height The sprite height
+     * @param u1 Texture coordinate.
+     * @param v1 Texture coordinate.
+     * @param u2 Texture coordinate.
+     * @param v2 Texture coordinate.
+     * @param color The color to tint the sprite. Use white for no tint.
+     * @param clip The clip rectangle.
+     */
+    void addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip, SpriteBatch::SpriteVertex* vertices);
+
+    /**
+     * Draws an array of vertices.
+     *
+     * @param vertices The vertices to draw.
+     * @param vertexCount The number of vertices within the vertex array.
+     * @param indices The vertex indices.
+     * @param indexCount The number of indices within the index array.
+     */
+    void draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount);
+
+    /**
+     * Clip position and size to fit within clip region.
+     *
+     * @return true if any part of sprite intersects with the clip region and therefore needs drawing, false otherwise.
+     */
+    bool clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2);
+
     MeshBatch* _batch;
     MeshBatch* _batch;
     bool _customEffect;
     bool _customEffect;
     float _textureWidthRatio;
     float _textureWidthRatio;

+ 5 - 4
gameplay/src/Technique.cpp

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

+ 544 - 82
gameplay/src/Texture.cpp

@@ -3,12 +3,48 @@
 #include "Texture.h"
 #include "Texture.h"
 #include "FileSystem.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
 namespace gameplay
 {
 {
 
 
 static std::vector<Texture*> __textureCache;
 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)
     if (_handle)
     {
     {
-        glDeleteTextures(1, &_handle);
+        GL_ASSERT( glDeleteTextures(1, &_handle) );
         _handle = 0;
         _handle = 0;
     }
     }
 
 
@@ -37,10 +73,13 @@ Texture::~Texture()
 
 
 Texture* Texture::create(const char* path, bool generateMipmaps)
 Texture* Texture::create(const char* path, bool generateMipmaps)
 {
 {
+    GP_ASSERT(path);
+
     // Search texture cache first.
     // Search texture cache first.
     for (unsigned int i = 0, count = __textureCache.size(); i < count; ++i)
     for (unsigned int i = 0, count = __textureCache.size(); i < count; ++i)
     {
     {
         Texture* t = __textureCache[i];
         Texture* t = __textureCache[i];
+        GP_ASSERT(t);
         if (t->_path == path)
         if (t->_path == path)
         {
         {
             // If 'generateMipmaps' is true, call Texture::generateMipamps() to force the 
             // 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;
     Texture* texture = NULL;
 
 
     // Filter loading based on file extension.
     // Filter loading based on file extension.
-    const char* ext = strrchr(path, '.');
+    const char* ext = strrchr(FileSystem::resolvePath(path), '.');
     if (ext)
     if (ext)
     {
     {
         switch (strlen(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')
             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);
                 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;
             break;
         }
         }
@@ -97,21 +137,24 @@ Texture* Texture::create(const char* path, bool generateMipmaps)
         return texture;
         return texture;
     }
     }
 
 
-    GP_ERROR("Failed to load texture: %s", path);
+    GP_ERROR("Failed to load texture from file '%s'.", path);
     return NULL;
     return NULL;
 }
 }
 
 
 Texture* Texture::create(Image* image, bool generateMipmaps)
 Texture* Texture::create(Image* image, bool generateMipmaps)
 {
 {
+    GP_ASSERT(image);
+
     switch (image->getFormat())
     switch (image->getFormat())
     {
     {
     case Image::RGB:
     case Image::RGB:
         return create(Texture::RGB, image->getWidth(), image->getHeight(), image->getData(), generateMipmaps);
         return create(Texture::RGB, image->getWidth(), image->getHeight(), image->getData(), generateMipmaps);
     case Image::RGBA:
     case Image::RGBA:
         return create(Texture::RGBA, image->getWidth(), image->getHeight(), image->getData(), generateMipmaps);
         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)
 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) );
         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) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generateMipmaps ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR) );
 
 
     Texture* texture = new Texture();
     Texture* texture = new Texture();
@@ -147,18 +190,240 @@ Texture* Texture::create(Format format, unsigned int width, unsigned int height,
     return texture;
     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)
 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
     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 size;                  // size of the structure
         unsigned int height;                // height of surface to be created
         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 alphaBitMask;          // mask for alpha channel
         unsigned int pvrtcTag;              // magic number identifying pvrtc file
         unsigned int pvrtcTag;              // magic number identifying pvrtc file
         unsigned int surfaceCount;          // number of surfaces present in the pvrtc
         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);
     unsigned int read = (int)fread(&header, 1, size, file);
-    GP_ASSERT(read == size);
     if (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;
         return NULL;
     }
     }
 
 
-    // Proper file header identifier
+    // Proper file header identifier.
     if (PVRTCIdentifier[0] != (char)((header.pvrtcTag >>  0) & 0xff) ||
     if (PVRTCIdentifier[0] != (char)((header.pvrtcTag >>  0) & 0xff) ||
         PVRTCIdentifier[1] != (char)((header.pvrtcTag >>  8) & 0xff) ||
         PVRTCIdentifier[1] != (char)((header.pvrtcTag >>  8) & 0xff) ||
         PVRTCIdentifier[2] != (char)((header.pvrtcTag >> 16) & 0xff) ||
         PVRTCIdentifier[2] != (char)((header.pvrtcTag >> 16) & 0xff) ||
         PVRTCIdentifier[3] != (char)((header.pvrtcTag >> 24) & 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;
         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
     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;
         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);
     read = (int)fread(data, 1, header.dataSize, file);
-    GP_ASSERT(read == header.dataSize);
     if (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);
         SAFE_DELETE_ARRAY(data);
-        fclose(file);
         return NULL;
         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;
     GLuint textureId;
     GL_ASSERT( glGenTextures(1, &textureId) );
     GL_ASSERT( glGenTextures(1, &textureId) );
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, 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->_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
         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;
     return texture;
 }
 }
-#endif
-    
+
 unsigned int Texture::getWidth() const
 unsigned int Texture::getWidth() const
 {
 {
     return _width;
     return _width;
@@ -335,9 +790,15 @@ bool Texture::isMipmapped() const
     return _mipmapped;
     return _mipmapped;
 }
 }
 
 
+bool Texture::isCompressed() const
+{
+    return _compressed;
+}
+
 Texture::Sampler::Sampler(Texture* texture)
 Texture::Sampler::Sampler(Texture* texture)
     : _texture(texture), _wrapS(Texture::REPEAT), _wrapT(Texture::REPEAT), _magFilter(Texture::LINEAR)
     : _texture(texture), _wrapS(Texture::REPEAT), _wrapT(Texture::REPEAT), _magFilter(Texture::LINEAR)
 {
 {
+    GP_ASSERT(texture);
     _minFilter = texture->isMipmapped() ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR;
     _minFilter = texture->isMipmapped() ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR;
 }
 }
 
 
@@ -348,8 +809,7 @@ Texture::Sampler::~Sampler()
 
 
 Texture::Sampler* Texture::Sampler::create(Texture* texture)
 Texture::Sampler* Texture::Sampler::create(Texture* texture)
 {
 {
-    GP_ASSERT(texture != NULL);
-
+    GP_ASSERT(texture);
     texture->addRef();
     texture->addRef();
     return new Sampler(texture);
     return new Sampler(texture);
 }
 }
@@ -379,6 +839,8 @@ Texture* Texture::Sampler::getTexture() const
 
 
 void Texture::Sampler::bind()
 void Texture::Sampler::bind()
 {
 {
+    GP_ASSERT(_texture);
+
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _texture->_handle) );
     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_S, (GLenum)_wrapS) );
     GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLenum)_wrapT) );
     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,
         RGB     = GL_RGB,
         RGBA    = GL_RGBA,
         RGBA    = GL_RGBA,
         ALPHA   = GL_ALPHA,
         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.
      * 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 path The image resource path.
      * @param generateMipmaps true to auto-generate a full mipmap chain, false otherwise.
      * @param generateMipmaps true to auto-generate a full mipmap chain, false otherwise.
      * 
      * 
@@ -189,6 +186,11 @@ public:
      */
      */
     bool isMipmapped() const;
     bool isMipmapped() const;
 
 
+    /**
+     * Determines if this texture is a compressed teture.
+     */
+    bool isCompressed() const;
+
     /**
     /**
      * Returns the texture handle.
      * Returns the texture handle.
      *
      *
@@ -213,16 +215,19 @@ private:
      */
      */
     virtual ~Texture();
     virtual ~Texture();
 
 
-#ifdef USE_PVRTC
     static Texture* createCompressedPVRTC(const char* path);
     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;
     std::string _path;
     TextureHandle _handle;
     TextureHandle _handle;
     unsigned int _width;
     unsigned int _width;
     unsigned int _height;
     unsigned int _height;
     bool _mipmapped;
     bool _mipmapped;
     bool _cached;
     bool _cached;
+    bool _compressed;
 };
 };
 
 
 }
 }

+ 11 - 0
gameplay/src/Theme.cpp

@@ -271,6 +271,8 @@ Theme* Theme::create(const char* url)
                     if (!font)
                     if (!font)
                     {
                     {
                         font = normal->getFont();
                         font = normal->getFont();
+                        if (font)
+                            font->addRef();
                     }
                     }
 
 
                     unsigned int fontSize;
                     unsigned int fontSize;
@@ -349,7 +351,10 @@ Theme* Theme::create(const char* url)
                         focus->setOpacity(opacity);
                         focus->setOpacity(opacity);
 
 
                         if (font)
                         if (font)
+                        {
                             theme->_fonts.insert(font);
                             theme->_fonts.insert(font);
+                            font->release();
+                        }
                     }
                     }
                     else if (strcmp(innerSpacename, "stateActive") == 0)
                     else if (strcmp(innerSpacename, "stateActive") == 0)
                     {
                     {
@@ -366,7 +371,10 @@ Theme* Theme::create(const char* url)
                         active->setOpacity(opacity);
                         active->setOpacity(opacity);
 
 
                         if (font)
                         if (font)
+                        {
                             theme->_fonts.insert(font);
                             theme->_fonts.insert(font);
+                            font->release();
+                        }
                     }
                     }
                     else if (strcmp(innerSpacename, "stateDisabled") == 0)
                     else if (strcmp(innerSpacename, "stateDisabled") == 0)
                     {
                     {
@@ -383,7 +391,10 @@ Theme* Theme::create(const char* url)
                         disabled->setOpacity(opacity);
                         disabled->setOpacity(opacity);
 
 
                         if (font)
                         if (font)
+                        {
                             theme->_fonts.insert(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++)
         for (unsigned int i = 0; i < transformCount; i++)
         {
         {
             Transform* t = _transformsChanged.at(i);
             Transform* t = _transformsChanged.at(i);
+            GP_ASSERT(t);
             t->transformChanged();
             t->transformChanged();
         }
         }
 
 
@@ -68,6 +69,7 @@ void Transform::resumeTransformChanged()
         for (unsigned int i = 0; i < transformCount; i++)
         for (unsigned int i = 0; i < transformCount; i++)
         {
         {
             Transform* t = _transformsChanged.at(i);
             Transform* t = _transformsChanged.at(i);
+            GP_ASSERT(t);
             t->_matrixDirtyBits &= ~DIRTY_NOTIFY;
             t->_matrixDirtyBits &= ~DIRTY_NOTIFY;
         }
         }
 
 
@@ -130,6 +132,7 @@ const Vector3& Transform::getScale() const
 
 
 void Transform::getScale(Vector3* scale) const
 void Transform::getScale(Vector3* scale) const
 {
 {
+    GP_ASSERT(scale);
     scale->set(_scale);
     scale->set(_scale);
 }
 }
 
 
@@ -156,14 +159,12 @@ const Quaternion& Transform::getRotation() const
 void Transform::getRotation(Quaternion* rotation) const
 void Transform::getRotation(Quaternion* rotation) const
 {
 {
     GP_ASSERT(rotation);
     GP_ASSERT(rotation);
-
     rotation->set(_rotation);
     rotation->set(_rotation);
 }
 }
 
 
 void Transform::getRotation(Matrix* rotation) const
 void Transform::getRotation(Matrix* rotation) const
 {
 {
     GP_ASSERT(rotation);
     GP_ASSERT(rotation);
-
     Matrix::createRotation(_rotation, rotation);
     Matrix::createRotation(_rotation, rotation);
 }
 }
 
 
@@ -180,6 +181,7 @@ const Vector3& Transform::getTranslation() const
 
 
 void Transform::getTranslation(Vector3* translation) const
 void Transform::getTranslation(Vector3* translation) const
 {
 {
+    GP_ASSERT(translation);
     translation->set(_translation);
     translation->set(_translation);
 }
 }
 
 
@@ -573,24 +575,18 @@ void Transform::translateForward(float amount)
 
 
 void Transform::transformPoint(Vector3* point)
 void Transform::transformPoint(Vector3* point)
 {
 {
-    GP_ASSERT(point);
-
     getMatrix();
     getMatrix();
     _matrix.transformPoint(point);
     _matrix.transformPoint(point);
 }
 }
 
 
 void Transform::transformPoint(const Vector3& point, Vector3* dst)
 void Transform::transformPoint(const Vector3& point, Vector3* dst)
 {
 {
-    GP_ASSERT(dst);
-
     getMatrix();
     getMatrix();
     _matrix.transformPoint(point, dst);
     _matrix.transformPoint(point, dst);
 }
 }
 
 
 void Transform::transformVector(Vector3* normal)
 void Transform::transformVector(Vector3* normal)
 {
 {
-    GP_ASSERT(normal);
-
     getMatrix();
     getMatrix();
     _matrix.transformVector(normal);
     _matrix.transformVector(normal);
 }
 }
@@ -635,6 +631,8 @@ unsigned int Transform::getAnimationPropertyComponentCount(int propertyId) const
 
 
 void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 {
 {
+    GP_ASSERT(value);
+
     switch (propertyId)
     switch (propertyId)
     {
     {
         case ANIMATE_SCALE_UNIT:
         case ANIMATE_SCALE_UNIT:
@@ -702,6 +700,7 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 
 
 void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
 {
+    GP_ASSERT(value);
     GP_ASSERT(blendWeight >= 0.0f && blendWeight <= 1.0f);
     GP_ASSERT(blendWeight >= 0.0f && blendWeight <= 1.0f);
 
 
     switch (propertyId)
     switch (propertyId)
@@ -798,12 +797,15 @@ bool Transform::isDirty(char matrixDirtyBits) const
 
 
 void Transform::suspendTransformChange(Transform* transform)
 void Transform::suspendTransformChange(Transform* transform)
 {
 {
+    GP_ASSERT(transform);
     transform->_matrixDirtyBits |= DIRTY_NOTIFY;
     transform->_matrixDirtyBits |= DIRTY_NOTIFY;
     _transformsChanged.push_back(transform);
     _transformsChanged.push_back(transform);
 }
 }
 
 
 void Transform::addListener(Transform::Listener* listener, long cookie)
 void Transform::addListener(Transform::Listener* listener, long cookie)
 {
 {
+    GP_ASSERT(listener);
+
     if (_listeners == NULL)
     if (_listeners == NULL)
         _listeners = new std::list<TransformListener>();
         _listeners = new std::list<TransformListener>();
 
 
@@ -815,6 +817,8 @@ void Transform::addListener(Transform::Listener* listener, long cookie)
 
 
 void Transform::removeListener(Transform::Listener* listener)
 void Transform::removeListener(Transform::Listener* listener)
 {
 {
+    GP_ASSERT(listener);
+
     if (_listeners)
     if (_listeners)
     {
     {
         for (std::list<TransformListener>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
         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++)
         for (std::list<TransformListener>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
         {
         {
             TransformListener& l = *itr;
             TransformListener& l = *itr;
+            GP_ASSERT(l.listener);
             l.listener->transformChanged(this, l.cookie);
             l.listener->transformChanged(this, l.cookie);
         }
         }
     }
     }
@@ -842,6 +847,8 @@ void Transform::transformChanged()
 
 
 void Transform::cloneInto(Transform* transform, NodeCloneContext &context) const
 void Transform::cloneInto(Transform* transform, NodeCloneContext &context) const
 {
 {
+    GP_ASSERT(transform);
+
     AnimationTarget::cloneInto(transform, context);
     AnimationTarget::cloneInto(transform, context);
     transform->_scale.set(_scale);
     transform->_scale.set(_scale);
     transform->_rotation.set(_rotation);
     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)
 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, 
     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);
         &_rotation.x, &_rotation.y, &_rotation.z, &_rotation.w);
     dirty(DIRTY_ROTATION);
     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)
 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.
     // Clamp the x value.
-    if ( x < min.x )
+    if (x < min.x)
         x = min.x;
         x = min.x;
-    if ( x > max.x )
+    if (x > max.x)
         x = max.x;
         x = max.x;
 
 
     // Clamp the y value.
     // Clamp the y value.
-    if ( y < min.y )
+    if (y < min.y)
         y = min.y;
         y = min.y;
-    if ( y > max.y )
+    if (y > max.y)
         y = max.y;
         y = max.y;
 }
 }
 
 
 void Vector2::clamp(const Vector2& v, const Vector2& min, const Vector2& max, Vector2* dst)
 void Vector2::clamp(const Vector2& v, const Vector2& min, const Vector2& max, Vector2* dst)
 {
 {
     GP_ASSERT(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.
     // Clamp the x value.
     dst->x = v.x;
     dst->x = v.x;
-    if ( dst->x < min.x )
+    if (dst->x < min.x)
         dst->x = min.x;
         dst->x = min.x;
-    if ( dst->x > max.x )
+    if (dst->x > max.x)
         dst->x = max.x;
         dst->x = max.x;
 
 
     // Clamp the y value.
     // Clamp the y value.
     dst->y = v.y;
     dst->y = v.y;
-    if ( dst->y < min.y )
+    if (dst->y < min.y)
         dst->y = min.y;
         dst->y = min.y;
-    if ( dst->y > max.y )
+    if (dst->y > max.y)
         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)
 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.
     // Clamp the x value.
-    if ( x < min.x )
+    if (x < min.x)
         x = min.x;
         x = min.x;
-    if ( x > max.x )
+    if (x > max.x)
         x = max.x;
         x = max.x;
 
 
     // Clamp the y value.
     // Clamp the y value.
-    if ( y < min.y )
+    if (y < min.y)
         y = min.y;
         y = min.y;
-    if ( y > max.y )
+    if (y > max.y)
         y = max.y;
         y = max.y;
 
 
     // Clamp the z value.
     // Clamp the z value.
-    if ( z < min.z )
+    if (z < min.z)
         z = min.z;
         z = min.z;
-    if ( z > max.z )
+    if (z > max.z)
         z = max.z;
         z = max.z;
 }
 }
 
 
 void Vector3::clamp(const Vector3& v, const Vector3& min, const Vector3& max, Vector3* dst)
 void Vector3::clamp(const Vector3& v, const Vector3& min, const Vector3& max, Vector3* dst)
 {
 {
     GP_ASSERT(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.
     // Clamp the x value.
     dst->x = v.x;
     dst->x = v.x;
-    if ( dst->x < min.x )
+    if (dst->x < min.x)
         dst->x = min.x;
         dst->x = min.x;
-    if ( dst->x > max.x )
+    if (dst->x > max.x)
         dst->x = max.x;
         dst->x = max.x;
 
 
     // Clamp the y value.
     // Clamp the y value.
     dst->y = v.y;
     dst->y = v.y;
-    if ( dst->y < min.y )
+    if (dst->y < min.y)
         dst->y = min.y;
         dst->y = min.y;
-    if ( dst->y > max.y )
+    if (dst->y > max.y)
         dst->y = max.y;
         dst->y = max.y;
 
 
     // Clamp the z value.
     // Clamp the z value.
     dst->z = v.z;
     dst->z = v.z;
-    if ( dst->z < min.z )
+    if (dst->z < min.z)
         dst->z = min.z;
         dst->z = min.z;
-    if ( dst->z > max.z )
+    if (dst->z > max.z)
         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)
 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.
     // Clamp the x value.
-    if ( x < min.x )
+    if (x < min.x)
         x = min.x;
         x = min.x;
-    if ( x > max.x )
+    if (x > max.x)
         x = max.x;
         x = max.x;
 
 
     // Clamp the y value.
     // Clamp the y value.
-    if ( y < min.y )
+    if (y < min.y)
         y = min.y;
         y = min.y;
-    if ( y > max.y )
+    if (y > max.y)
         y = max.y;
         y = max.y;
 
 
     // Clamp the z value.
     // Clamp the z value.
-    if ( z < min.z )
+    if (z < min.z)
         z = min.z;
         z = min.z;
-    if ( z > max.z )
+    if (z > max.z)
         z = max.z;
         z = max.z;
 
 
     // Clamp the z value.
     // Clamp the z value.
-    if ( w < min.w )
+    if (w < min.w)
         w = min.w;
         w = min.w;
-    if ( w > max.w )
+    if (w > max.w)
         w = max.w;
         w = max.w;
 }
 }
 
 
 void Vector4::clamp(const Vector4& v, const Vector4& min, const Vector4& max, Vector4* dst)
 void Vector4::clamp(const Vector4& v, const Vector4& min, const Vector4& max, Vector4* dst)
 {
 {
     GP_ASSERT(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.
     // Clamp the x value.
     dst->x = v.x;
     dst->x = v.x;
-    if ( dst->x < min.x )
+    if (dst->x < min.x)
         dst->x = min.x;
         dst->x = min.x;
-    if ( dst->x > max.x )
+    if (dst->x > max.x)
         dst->x = max.x;
         dst->x = max.x;
 
 
     // Clamp the y value.
     // Clamp the y value.
     dst->y = v.y;
     dst->y = v.y;
-    if ( dst->y < min.y )
+    if (dst->y < min.y)
         dst->y = min.y;
         dst->y = min.y;
-    if ( dst->y > max.y )
+    if (dst->y > max.y)
         dst->y = max.y;
         dst->y = max.y;
 
 
     // Clamp the z value.
     // Clamp the z value.
     dst->z = v.z;
     dst->z = v.z;
-    if ( dst->z < min.z )
+    if (dst->z < min.z)
         dst->z = min.z;
         dst->z = min.z;
-    if ( dst->z > max.z )
+    if (dst->z > max.z)
         dst->z = max.z;
         dst->z = max.z;
 
 
     // Clamp the w value.
     // Clamp the w value.
     dst->w = v.w;
     dst->w = v.w;
-    if ( dst->w < min.w )
+    if (dst->w < min.w)
         dst->w = min.w;
         dst->w = min.w;
-    if ( dst->w > max.w )
+    if (dst->w > max.w)
         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(_mesh);
-
     SAFE_RELEASE(_effect);
     SAFE_RELEASE(_effect);
-
     SAFE_DELETE_ARRAY(_attributes);
     SAFE_DELETE_ARRAY(_attributes);
 
 
     if (_handle)
     if (_handle)
@@ -38,11 +36,14 @@ VertexAttributeBinding::~VertexAttributeBinding()
 
 
 VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effect)
 VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, Effect* effect)
 {
 {
+    GP_ASSERT(mesh);
+
     // Search for an existing vertex attribute binding that can be used.
     // Search for an existing vertex attribute binding that can be used.
     VertexAttributeBinding* b;
     VertexAttributeBinding* b;
     for (unsigned int i = 0, count = __vertexAttributeBindingCache.size(); i < count; ++i)
     for (unsigned int i = 0, count = __vertexAttributeBindingCache.size(); i < count; ++i)
     {
     {
         b = __vertexAttributeBindingCache[i];
         b = __vertexAttributeBindingCache[i];
+        GP_ASSERT(b);
         if (b->_mesh == mesh && b->_effect == effect)
         if (b->_mesh == mesh && b->_effect == effect)
         {
         {
             // Found a match!
             // 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)
 VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexFormat& vertexFormat, void* vertexPointer, Effect* effect)
 {
 {
+    GP_ASSERT(effect);
+
     // One-time initialization.
     // One-time initialization.
     if (__maxVertexAttribs == 0)
     if (__maxVertexAttribs == 0)
     {
     {
@@ -76,9 +79,9 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
         GL_ASSERT( glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &temp) );
         GL_ASSERT( glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &temp) );
 
 
         __maxVertexAttribs = temp;
         __maxVertexAttribs = temp;
-        GP_ASSERT(__maxVertexAttribs > 0);
         if (__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;
             return NULL;
         }
         }
     }
     }
@@ -97,6 +100,7 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
 
 
         if (b->_handle == 0)
         if (b->_handle == 0)
         {
         {
+            GP_ERROR("Failed to create VAO handle.");
             SAFE_DELETE(b);
             SAFE_DELETE(b);
             return NULL;
             return NULL;
         }
         }
@@ -140,7 +144,6 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
     for (unsigned int i = 0, count = vertexFormat.getElementCount(); i < count; ++i)
     for (unsigned int i = 0, count = vertexFormat.getElementCount(); i < count; ++i)
     {
     {
         const VertexFormat::Element& e = vertexFormat.getElement(i);
         const VertexFormat::Element& e = vertexFormat.getElement(i);
-
         gameplay::VertexAttribute attrib;
         gameplay::VertexAttribute attrib;
 
 
         // Constructor vertex attribute name expected in shader.
         // Constructor vertex attribute name expected in shader.
@@ -189,6 +192,7 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
             attrib = effect->getVertexAttribute(name.c_str());
             attrib = effect->getVertexAttribute(name.c_str());
             break;
             break;
         default:
         default:
+            // This happens whenever vertex data contains extra information (not an error).
             attrib = -1;
             attrib = -1;
             break;
             break;
         }
         }
@@ -220,13 +224,14 @@ void VertexAttributeBinding::setVertexAttribPointer(GLuint indx, GLint size, GLe
 
 
     if (_handle)
     if (_handle)
     {
     {
-        // Hardware mode
+        // Hardware mode.
         GL_ASSERT( glVertexAttribPointer(indx, size, type, normalize, stride, pointer) );
         GL_ASSERT( glVertexAttribPointer(indx, size, type, normalize, stride, pointer) );
         GL_ASSERT( glEnableVertexAttribArray(indx) );
         GL_ASSERT( glEnableVertexAttribArray(indx) );
     }
     }
     else
     else
     {
     {
-        // Software mode
+        // Software mode.
+        GP_ASSERT(_attributes);
         _attributes[indx].enabled = true;
         _attributes[indx].enabled = true;
         _attributes[indx].size = size;
         _attributes[indx].size = size;
         _attributes[indx].type = type;
         _attributes[indx].type = type;
@@ -255,6 +260,7 @@ void VertexAttributeBinding::bind()
             GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
             GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
         }
         }
 
 
+        GP_ASSERT(_attributes);
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         {
         {
             VertexAttribute& a = _attributes[i];
             VertexAttribute& a = _attributes[i];
@@ -282,6 +288,7 @@ void VertexAttributeBinding::unbind()
             GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
             GL_ASSERT( glBindBuffer(GL_ARRAY_BUFFER, 0) );
         }
         }
 
 
+        GP_ASSERT(_attributes);
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         for (unsigned int i = 0; i < __maxVertexAttribs; ++i)
         {
         {
             if (_attributes[i].enabled)
             if (_attributes[i].enabled)

+ 2 - 1
gameplay/src/VertexFormat.cpp

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

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

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

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

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

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

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

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

@@ -10,8 +10,8 @@ using namespace gameplay;
 int main(int argc, char** argv)
 int main(int argc, char** argv)
 {
 {
     Game* game = Game::getInstance();
     Game* game = Game::getInstance();
-    GP_ASSERT(game != NULL);
     Platform* platform = Platform::create(game);
     Platform* platform = Platform::create(game);
+    GP_ASSERT(platform);
     int result = platform->enterMessagePump();
     int result = platform->enterMessagePump();
     delete platform;
     delete platform;
     return result;
     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)
 extern "C" int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
 {
 {
     Game* game = Game::getInstance();
     Game* game = Game::getInstance();
-    GP_ASSERT(game != NULL);
     Platform* platform = Platform::create(game);
     Platform* platform = Platform::create(game);
+    GP_ASSERT(platform);
     int result = platform->enterMessagePump();
     int result = platform->enterMessagePump();
     delete platform;
     delete platform;
     return result;
     return result;

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä