ソースを参照

Merge pull request #418 from blackberry-gaming/next-cculy

Error handling improvements (7 of 7 - Physics).
Sean Paul Taylor 13 年 前
コミット
ae208c32f8

+ 4 - 0
gameplay/src/Base.h

@@ -77,6 +77,9 @@ extern void printError(const char* format, ...);
 #endif
 
 // Error macro.
+#ifdef GP_ERRORS_AS_WARNINGS
+#define GP_ERROR GP_WARN
+#else
 #define GP_ERROR(...) do \
     { \
         printError("%s -- ", __current__func__); \
@@ -85,6 +88,7 @@ extern void printError(const char* format, ...);
         GP_FORCE_ASSERTION_FAILURE; \
         std::exit(-1); \
     } while (0)
+#endif
 
 // Warning macro.
 #define GP_WARN(...) do \

+ 70 - 34
gameplay/src/PhysicsCharacter.cpp

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

+ 10 - 0
gameplay/src/PhysicsCollisionObject.cpp

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

+ 33 - 13
gameplay/src/PhysicsCollisionShape.cpp

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

+ 13 - 1
gameplay/src/PhysicsConstraint.cpp

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

+ 4 - 0
gameplay/src/PhysicsConstraint.inl

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

+ 129 - 44
gameplay/src/PhysicsController.cpp

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

+ 7 - 0
gameplay/src/PhysicsGenericConstraint.cpp

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

+ 12 - 0
gameplay/src/PhysicsGenericConstraint.inl

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

+ 10 - 3
gameplay/src/PhysicsGhostObject.cpp

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

+ 5 - 0
gameplay/src/PhysicsHingeConstraint.cpp

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

+ 5 - 0
gameplay/src/PhysicsMotionState.cpp

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

+ 34 - 8
gameplay/src/PhysicsRigidBody.cpp

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

+ 16 - 0
gameplay/src/PhysicsRigidBody.inl

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

+ 6 - 0
gameplay/src/PhysicsSocketConstraint.cpp

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

+ 8 - 0
gameplay/src/PhysicsSpringConstraint.cpp

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