浏览代码

Added support for explicitly specifying collision shapes for physics collision objects (rigid bodies, ghost objects, characters).
Changed PhysicsCharacter to inherit PhysicsGhostObject.
Updated PhysicsCharacter collision code.
Added a Scene::drawDebug method for drawing scene debugging information (such as bounding volumes).

Steve Grenier 13 年之前
父节点
当前提交
61e580c539

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -63,6 +63,7 @@
     <ClCompile Include="src\ParticleEmitter.cpp" />
     <ClCompile Include="src\PhysicsCharacter.cpp" />
     <ClCompile Include="src\PhysicsCollisionObject.cpp" />
+    <ClCompile Include="src\PhysicsCollisionShape.cpp" />
     <ClCompile Include="src\PhysicsConstraint.cpp" />
     <ClCompile Include="src\PhysicsController.cpp" />
     <ClCompile Include="src\PhysicsFixedConstraint.cpp" />
@@ -153,6 +154,7 @@
     <ClInclude Include="src\ParticleEmitter.h" />
     <ClInclude Include="src\PhysicsCharacter.h" />
     <ClInclude Include="src\PhysicsCollisionObject.h" />
+    <ClInclude Include="src\PhysicsCollisionShape.h" />
     <ClInclude Include="src\PhysicsConstraint.h" />
     <ClInclude Include="src\PhysicsController.h" />
     <ClInclude Include="src\PhysicsFixedConstraint.h" />

+ 6 - 0
gameplay/gameplay.vcxproj.filters

@@ -273,6 +273,9 @@
     <ClCompile Include="src\PhysicsGhostObject.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\PhysicsCollisionShape.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -539,6 +542,9 @@
     <ClInclude Include="src\PhysicsGhostObject.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\PhysicsCollisionShape.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 7 - 0
gameplay/src/BoundingBox.cpp

@@ -55,6 +55,13 @@ void BoundingBox::getCorners(Vector3* dst) const
     dst[7].set(min.x, max.y, min.z);
 }
 
+Vector3 BoundingBox::getCenter() const
+{
+	Vector3 center;
+	getCenter(&center);
+	return center;
+}
+
 void BoundingBox::getCenter(Vector3* dst) const
 {
     dst->set(min, max);

+ 10 - 1
gameplay/src/BoundingBox.h

@@ -53,6 +53,15 @@ public:
      */
     static const BoundingBox& empty();
 
+	/**
+     * Gets the center point of the bounding box.
+     *
+     * This method computes the center point of the box from its min and max.
+     *
+     * @return The center point of the bounding box.
+     */
+    Vector3 getCenter() const;
+
     /**
      * Gets the center point of the bounding box.
      *
@@ -71,7 +80,7 @@ public:
      * specify the far face starting at the upper left point when looking towards the origin from the negative
      * z-axis in a counter-clockwise fashion.
      *
-     * @param dst The array to store the corners in. Must be size 6.
+     * @param dst The array to store the corners in. Must be size 8.
      */
     void getCorners(Vector3* dst) const;
 

+ 1 - 0
gameplay/src/Model.cpp

@@ -4,6 +4,7 @@
 #include "Scene.h"
 #include "Technique.h"
 #include "Pass.h"
+#include "Node.h"
 
 namespace gameplay
 {

+ 1 - 1
gameplay/src/Model.h

@@ -4,13 +4,13 @@
 #include "Mesh.h"
 #include "MeshSkin.h"
 #include "Material.h"
-#include "Node.h"
 
 namespace gameplay
 {
 
 class Package;
 class MeshSkin;
+class Node;
 
 /**
  * Defines a Model which is an instance of a Mesh that can be drawn

+ 43 - 30
gameplay/src/Node.cpp

@@ -2,6 +2,9 @@
 #include "Node.h"
 #include "Scene.h"
 #include "Joint.h"
+#include "PhysicsRigidBody.h"
+#include "PhysicsGhostObject.h"
+#include "PhysicsCharacter.h"
 
 #define NODE_DIRTY_WORLD 1
 #define NODE_DIRTY_BOUNDS 2
@@ -12,8 +15,8 @@ namespace gameplay
 
 Node::Node(const char* id)
     : _scene(NULL), _firstChild(NULL), _nextSibling(NULL), _prevSibling(NULL), _parent(NULL), _childCount(NULL),
-    _camera(NULL), _light(NULL), _model(NULL), _form(NULL), _audioSource(NULL), _particleEmitter(NULL), _physicsRigidBody(NULL), 
-    _ghostObject(NULL), _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true)
+    _camera(NULL), _light(NULL), _model(NULL), _form(NULL), _audioSource(NULL), _particleEmitter(NULL),
+	_collisionObject(NULL), _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true)
 {
     if (id)
     {
@@ -45,8 +48,7 @@ Node::~Node()
     SAFE_RELEASE(_audioSource);
     SAFE_RELEASE(_particleEmitter);
     SAFE_RELEASE(_form);
-    SAFE_DELETE(_physicsRigidBody);
-    SAFE_DELETE(_ghostObject);
+    SAFE_DELETE(_collisionObject);
 }
 
 Node* Node::create(const char* id)
@@ -296,7 +298,7 @@ const Matrix& Node::getWorldMatrix() const
         // If we have a parent, multiply our parent world transform by our local
         // transform to obtain our final resolved world transform.
         Node* parent = getParent();
-        if (parent && (!_physicsRigidBody || _physicsRigidBody->isKinematic()) )
+		if (parent && (!_collisionObject || _collisionObject->isKinematic()))
         {
             Matrix::multiply(parent->getWorldMatrix(), getMatrix(), &_world);
         }
@@ -753,45 +755,56 @@ void Node::setParticleEmitter(ParticleEmitter* emitter)
     }
 }
 
-PhysicsRigidBody* Node::getRigidBody() const
+PhysicsCollisionObject* Node::getCollisionObject() const
 {
-    return _physicsRigidBody;
+    return _collisionObject;
 }
 
-void Node::setRigidBody(PhysicsRigidBody::ShapeType type, float mass, float friction,
-        float restitution, float linearDamping, float angularDamping)
+PhysicsCollisionObject* Node::setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape, PhysicsRigidBody::Parameters* rigidBodyParameters)
 {
-    SAFE_DELETE(_physicsRigidBody);
-    
-    if (type != PhysicsRigidBody::SHAPE_NONE)
-        _physicsRigidBody = new PhysicsRigidBody(this, type, mass, friction, restitution, linearDamping, angularDamping);
-}
+	SAFE_DELETE(_collisionObject);
 
-void Node::setRigidBody(const char* filePath)
-{
-    SAFE_DELETE(_physicsRigidBody);
+	switch (type)
+	{
+	case PhysicsCollisionObject::RIGID_BODY:
+		{
+			_collisionObject = new PhysicsRigidBody(this, shape, rigidBodyParameters ? *rigidBodyParameters : PhysicsRigidBody::Parameters());
+		}
+		break;
 
-    _physicsRigidBody = PhysicsRigidBody::create(this, filePath);
-}
+	case PhysicsCollisionObject::GHOST_OBJECT:
+		{
+			_collisionObject = new PhysicsGhostObject(this, shape);
+		}
+		break;
 
-void Node::setRigidBody(Properties* properties)
-{
-    SAFE_DELETE(_physicsRigidBody);
+	case PhysicsCollisionObject::CHARACTER:
+		{
+			_collisionObject = new PhysicsCharacter(this, shape);
+		}
+		break;
+	}
 
-    _physicsRigidBody = PhysicsRigidBody::create(this, properties);
+	return _collisionObject;
 }
 
-PhysicsGhostObject* Node::getGhostObject()
+PhysicsCollisionObject* Node::setCollisionObject(const char* filePath)
 {
-    return _ghostObject;
+    SAFE_DELETE(_collisionObject);
+
+	// TODO: Support other collision object types from file
+    _collisionObject = PhysicsRigidBody::create(this, filePath);
+
+	return _collisionObject;
 }
 
-void Node::setGhostObject(PhysicsRigidBody::ShapeType type)
+PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
 {
-    SAFE_DELETE(_ghostObject);
-    
-    if (type != PhysicsRigidBody::SHAPE_NONE)
-        _ghostObject = new PhysicsGhostObject(this, type);
+    SAFE_DELETE(_collisionObject);
+
+    _collisionObject = PhysicsRigidBody::create(this, properties);
+
+	return _collisionObject;
 }
 
 }

+ 59 - 52
gameplay/src/Node.h

@@ -8,8 +8,9 @@
 #include "Form.h"
 #include "AudioSource.h"
 #include "ParticleEmitter.h"
-#include "PhysicsGhostObject.h"
 #include "PhysicsRigidBody.h"
+#include "PhysicsCollisionObject.h"
+#include "PhysicsCollisionShape.h"
 #include "BoundingBox.h"
 
 namespace gameplay
@@ -366,61 +367,68 @@ public:
      */
     void setParticleEmitter(ParticleEmitter* emitter);
 
-    /**
-     * Returns the pointer to this node's physics rigid body or NULL.
-     *
-     * @return The pointer to this node's physics rigid body or NULL.
-     */
-    PhysicsRigidBody* getRigidBody() const;
-
-    /**
-     * Sets (or disables) the physics rigid body for this node.
-     * 
-     * Note: This is only allowed for nodes that have a model attached to them.
-     *
-     * @param type The type of rigid body to set; to disable the physics rigid
-     *      body, pass PhysicsRigidBody#SHAPE_NONE.
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (between 0.0 and 1.0, where 0.0 is
-     *      minimal friction and 1.0 is maximal friction).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; between 0.0 and 1.0, where 0.0 is minimal bounciness and 1.0 is maximal bounciness).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
-     */
-    void setRigidBody(PhysicsRigidBody::ShapeType type, float mass = 0.0f, float friction = 0.5f,
-        float restitution = 0.0f, float linearDamping = 0.0f, float angularDamping = 0.0f);
-
-    /**
-     * Returns the pointer to this node's physics ghost object or NULL.
-     * 
-     * @return The pointer to this node's physics ghost object or NULL.
-     */
-    PhysicsGhostObject* getGhostObject();
-
-    /**
-     * Sets (or disables) the physics ghost object for this node.
-     * 
-     * Note: This is only allowed for nodes that have a model attached to them.
-     *
-     * @param type The type of physics ghost object to set; to disable the physics ghost
-     *      object, pass PhysicsRigidBody#SHAPE_NONE.
-     */
-    void setGhostObject(PhysicsRigidBody::ShapeType type);
-
-    /**
-     * Sets the physics rigid body for this node using the rigid body definition in the given file.
+	/**
+	 * Returns the pointer to this node's physics collision object.
+	 *
+	 * The type of the returned collision object can be queried using
+	 * the PhysicsCollisionObject::getType() method.
+	 *
+	 * @return The pointer to this node's physics collision object.
+	 */
+	PhysicsCollisionObject* getCollisionObject() const;
+
+	/**
+	 * Sets (or disables) the physics collision object for this node.
+	 *
+	 * The supported collision object types include rigid bodies, ghost objects and 
+	 * characters.
+	 *
+	 * Rigid bodies are used to represent most physical objects in a game. The important
+	 * feature of rigid bodies is that they can be simulated by the physics system as other
+	 * rigid bodies or collision objects collide with them. To support this physics simulation,
+	 * rigid bodies require additional parameters, such as mass, friction and restitution to
+	 * define their physical features. These parameters can be passed into the
+	 * 'rigidBodyParameters' parameter.
+	 *
+	 * Ghost objects are a simple type of collision object that are not simulated. By default
+	 * they pass through other objects in the scene without affecting them. Ghost objects do
+	 * receive collision events however, which makes them useful for representing non-simulated
+	 * entities in a game that still require collision events, such as volumetric triggers, 
+	 * power-ups, etc.
+	 *
+	 * Characters are an extention of ghost objects which provide a number of additional features
+	 * for animating and moving characters within a game. Characters are represented as ghost
+	 * objects instead of rigid bodies to allow more direct control over character movement,
+	 * since attempting to model a physics character with a simulated rigid body usually results
+	 * in unresponse and unpredictable character movement. Unlike normal ghost objects,
+	 * characters to react to other characters and rigid bodies in the world. Characters react
+	 * to gravity and collide (and respond) with rigid bodies to allow them to walk on the ground,
+	 * slide along walls and walk up/down slopes and stairs.
+	 *
+	 * @param type The type of the collision object to set; to disable the physics
+	 *		collision object, pass PhysicsCollisionObject::NONE.
+	 * @param shape Definition of a physics collision shape to be used for this collision object.
+	 *		Use the static shape methods on the PhysicsCollisionShape class to specificy a shape
+	 *		definition, such as PhysicsCollisionShape::box().
+	 * @param rigidBodyParameters If type is PhysicsCollisionObject::RIGID_BODY, this
+	 *		must point to a valid rigid body parameters object containing information
+	 *		about the rigid body; otherwise, this parmater may be NULL.
+	 */
+	PhysicsCollisionObject* setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape, PhysicsRigidBody::Parameters* rigidBodyParameters = NULL);
+
+    /**
+     * Sets the physics collision object for this node using the definition in the given file.
      * 
-     * @param filePath The path to the file that contains the rigid body definition.
+     * @param filePath The path to the file that contains the collision object definition.
      */
-    void setRigidBody(const char* filePath);
+    PhysicsCollisionObject* setCollisionObject(const char* filePath);
 
     /**
-     * Sets the physics rigid body for this node from the given properties object.
+     * Sets the physics collision object for this node from the given properties object.
      * 
-     * @param properties The properties object defining the rigid body (must have namespace equal to 'rigidbody').
+     * @param properties The properties object defining the collision ojbect.
      */
-    void setRigidBody(Properties* properties);
+    PhysicsCollisionObject* setCollisionObject(Properties* properties);
 
     /**
      * Returns the bounding sphere for the Node, in world space.
@@ -490,8 +498,7 @@ protected:
     Form* _form;
     AudioSource* _audioSource;
     ParticleEmitter* _particleEmitter;
-    PhysicsRigidBody* _physicsRigidBody;
-    PhysicsGhostObject* _ghostObject;
+    PhysicsCollisionObject* _collisionObject;
     mutable Matrix _world;
     mutable int _dirtyBits;
     bool _notifyHierarchyChanged;

+ 27 - 93
gameplay/src/PhysicsCharacter.cpp

@@ -63,60 +63,25 @@ protected:
 	btScalar _minSlopeDot;
 };
 
-PhysicsCharacter::PhysicsCharacter(Node* node, float radius, float height, const Vector3 center)
-    : _node(node), _motionState(NULL), _moveVelocity(0,0,0), _forwardVelocity(0.0f), _rightVelocity(0.0f),
+PhysicsCharacter::PhysicsCharacter(Node* node, const PhysicsCollisionShape::Definition& shape)
+    : PhysicsGhostObject(node, shape), _moveVelocity(0,0,0), _forwardVelocity(0.0f), _rightVelocity(0.0f),
     _fallVelocity(0, 0, 0), _currentVelocity(0,0,0), _normalizedVelocity(0,0,0),
     _colliding(false), _collisionNormal(0,0,0), _currentPosition(0,0,0),
-    _ghostObject(NULL), _collisionShape(NULL), _ignoreTransformChanged(0),
     _stepHeight(0.2f), _slopeAngle(0.0f), _cosSlopeAngle(0.0f), _physicsEnabled(true)
 {
-    setMaxSlopeAngle(45.0f);
+	setMaxSlopeAngle(45.0f);
 
-    node->addRef();
-    node->addListener(this);
-
-    // Create physics motion state for syncing transform between gameplay and bullet
-    Vector3 centerOfMassOffset(-center);
-    _motionState = new PhysicsMotionState(node, &centerOfMassOffset);
-
-    // Create ghost object, which is used as an efficient way to detect
-    // collisions between pairs of objects.
-    _ghostObject = bullet_new<btPairCachingGhostObject>();
-
-    // Set initial transform
-    _motionState->getWorldTransform(_ghostObject->getWorldTransform());
-
-    PhysicsController* pc = Game::getInstance()->getPhysicsController();
-
-    // Create a capsule collision shape (this is automatically deleted by PhysicsController when our collision object is removed)
-    _collisionShape = static_cast<btConvexShape*>(pc->createCapsule(radius, height - radius*2.0f));
-
-    // Set the collision shape on the ghost object (get it from the node's rigid body)
-    _ghostObject->setCollisionShape(_collisionShape);
-    _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
-
-    // Add the collision object to the physics system
-    pc->addCollisionObject(this);
+    // Set the collision flags on the ghost object to indicate it's a character
+    //_ghostObject->setCollisionFlags(_ghostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT);
 
     // Register ourselves as an action on the physics world so we are called back during physics ticks
-    pc->_world->addAction(this);
+    Game::getInstance()->getPhysicsController()->_world->addAction(this);
 }
 
 PhysicsCharacter::~PhysicsCharacter()
 {
-    // Remove ourself from physics system
-    PhysicsController* pc = Game::getInstance()->getPhysicsController();
-
-    pc->removeCollisionObject(this);
-
     // Unregister ourselves as action from world
-    pc->_world->removeAction(this);
-
-    SAFE_DELETE(_ghostObject);
-
-    _node->removeListener(this);
-    SAFE_RELEASE(_node);
-    SAFE_DELETE(_motionState);
+    Game::getInstance()->getPhysicsController()->_world->removeAction(this);
 }
 
 PhysicsCollisionObject::Type PhysicsCharacter::getType() const
@@ -139,16 +104,6 @@ void PhysicsCharacter::setPhysicsEnabled(bool enabled)
     _physicsEnabled = enabled;
 }
 
-btCollisionShape* PhysicsCharacter::getCollisionShape() const
-{
-    return _collisionShape;
-}
-
-Node* PhysicsCharacter::getNode() const
-{
-    return _node;
-}
-
 float PhysicsCharacter::getMaxStepHeight() const
 {
     return _stepHeight;
@@ -370,18 +325,6 @@ void PhysicsCharacter::updateCurrentVelocity()
     }
 }
 
-void PhysicsCharacter::transformChanged(Transform* transform, long cookie)
-{
-    if (!_ignoreTransformChanged)
-    {
-        // Update motion state with transform from node
-        _motionState->updateTransformFromNode();
-
-        // Update transform on ghost object
-        _motionState->getWorldTransform(_ghostObject->getWorldTransform());
-    }
-}
-
 void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep)
 {
     // First check for existing collisions and attempt to respond/fix them.
@@ -392,7 +335,7 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     // dynamic objects (i.e. objects that moved and now intersect the character).
     if (_physicsEnabled)
     {
-        _colliding = fixCollision(collisionWorld);
+        //_colliding = fixCollision(collisionWorld);
         /*_colliding = false;
         int stepCount = 0;
 	    while (fixCollision(collisionWorld))
@@ -411,8 +354,9 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     }
 
     // Update current and target world positions
-    btTransform transform = _ghostObject->getWorldTransform();
-    _currentPosition = transform.getOrigin();
+	Vector3 startPosition;
+	_node->getWorldMatrix().getTranslation(&startPosition);
+    _currentPosition = BV(startPosition);
 
     // Process movement in the up direction
     if (_physicsEnabled)
@@ -422,19 +366,11 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     stepForwardAndStrafe(collisionWorld, deltaTimeStep);
 
     // Process movement in the down direction
-    if (_physicsEnabled)
-        stepDown(collisionWorld, deltaTimeStep);
+	if (_physicsEnabled)
+		stepDown(collisionWorld, deltaTimeStep);
 
     // Set new position
-    transform.setOrigin(_currentPosition);
-
-    // Update world transform
-    ++_ignoreTransformChanged;
-    _motionState->setWorldTransform(transform);
-    --_ignoreTransformChanged;
-
-    // Update ghost object transform
-    _motionState->getWorldTransform(_ghostObject->getWorldTransform());
+	_node->translate(Vector3(_currentPosition.x(), _currentPosition.y(), _currentPosition.z()) - startPosition);
 }
 
 void PhysicsCharacter::stepUp(btCollisionWorld* collisionWorld, btScalar time)
@@ -509,12 +445,11 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
     }
 
     // Check for collisions by performing a bullet convex sweep test
-    btTransform start, end;
-	start.setIdentity();
-	end.setIdentity();
+    btTransform start = _ghostObject->getWorldTransform();
+	btTransform end = _ghostObject->getWorldTransform();
 
 	btScalar fraction = 1.0;
-	btScalar distance2 = (_currentPosition-targetPosition).length2();
+	btScalar distance2;
 
 	if (_colliding && (_normalizedVelocity.dot(_collisionNormal) > btScalar(0.0)))
 	{
@@ -530,16 +465,10 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 		btVector3 sweepDirNegative(_currentPosition - targetPosition);
 
 		ClosestNotMeConvexResultCallback callback(_ghostObject, sweepDirNegative, btScalar(0.0));
-		callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
-		callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
+		callback.m_collisionFilterGroup = btBroadphaseProxy::CharacterFilter;
+		callback.m_collisionFilterMask = btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter;
 
-        // Temporarily increase collision margin by a bit
-        //btScalar margin = _collisionShape->getMargin();
-        //_collisionShape->setMargin(margin + m_addedMargin);
-
-        _ghostObject->convexSweepTest(_collisionShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
-
-		//m_convexShape->setMargin(margin);
+		_ghostObject->convexSweepTest(_collisionShape->getShape<btConvexShape>(), start, end, callback, 0.01f/*collisionWorld->getDispatchInfo().m_allowedCcdPenetration*/);
 
 		fraction -= callback.m_closestHitFraction;
 
@@ -548,7 +477,12 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 			// We hit something so can move only a fraction
 			//btScalar hitDistance = (callback.m_hitPointWorld - _currentPosition).length();
 
-            //_currentPosition.setInterpolate3(_currentPosition, targetPosition, callback.m_closestHitFraction);
+            //targetPosition = _currentPosition;
+			//targetPosition.setInterpolate3(_currentPosition, targetPosition, callback.m_closestHitFraction * 0.1f);
+
+			//btVector3 normalDir = callback.m_hitNormalWorld;
+			//normalDir.normalize();
+			//targetPosition = callback.m_hitPointWorld + (normalDir * 0.2f);
 
 			updateTargetPositionFromCollision(targetPosition, callback.m_hitNormalWorld);
 			btVector3 currentDir = targetPosition - _currentPosition;
@@ -600,7 +534,7 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
     ClosestNotMeConvexResultCallback callback(_ghostObject, btVector3(0, 1, 0), _cosSlopeAngle);
 	callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
 	callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
-    _ghostObject->convexSweepTest(_collisionShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
+	_ghostObject->convexSweepTest(_collisionShape->getShape<btConvexShape>(), start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
 	if (callback.hasHit())
 	{
         // Collision detected, fix it

+ 5 - 34
gameplay/src/PhysicsCharacter.h

@@ -1,10 +1,7 @@
 #ifndef PHYSICSCHARACTER_H_
 #define PHYSICSCHARACTER_H_
 
-#include "Node.h"
-#include "PhysicsRigidBody.h"
-#include "PhysicsMotionState.h"
-#include "Vector3.h"
+#include "PhysicsGhostObject.h"
 
 namespace gameplay
 {
@@ -24,9 +21,9 @@ namespace gameplay
  * clips can be setup for typical character animations, such as walk, run, jump,
  * etc; and the controller will handle blending between these animations as needed.
  */
-class PhysicsCharacter : public PhysicsCollisionObject, public Transform::Listener, public btActionInterface
+class PhysicsCharacter : public PhysicsGhostObject, public btActionInterface
 {
-    friend class PhysicsController;
+    friend class Node;
 
 public:
 
@@ -56,15 +53,6 @@ public:
      */
     PhysicsCollisionObject::Type getType() const;
 
-    /**
-     * Returns the character node for this PhysicsCharacter.
-     *
-     * @return The character Node.
-     *
-     * @see PhysicsCollisionObject::getNode.
-     */
-    Node* getNode() const;
-
     /**
      * Returns whether physics simulation is enabled for the physics character.
      *
@@ -260,11 +248,6 @@ public:
      */
     void jump(float height);
 
-    /**
-     * @see Transform::Listener::transformChanged
-     */
-    void transformChanged(Transform* transform, long cookie);
-
     /**
      * @see btActionInterface::updateAction
      */
@@ -282,11 +265,6 @@ protected:
      */
     btCollisionObject* getCollisionObject() const;
 
-    /**
-     * @see PhysicsCollisionObject::getCollisionShape
-     */
-    btCollisionShape* getCollisionShape() const;
-
 private:
 
     struct CharacterAnimation
@@ -307,11 +285,9 @@ private:
      * Use PhysicsController::createCharacter to create physics characters.
      *
      * @param node Scene node that represents the character.
-     * @param radius Radius of capsule volume used for character collisions.
-     * @param height Height of the capsule volume used for character collisions.
-     * @param center Center point of the capsule volume for the character.
+	 * @param shape Physis collision shape definition.
      */
-    PhysicsCharacter(Node* node, float radius, float height, const Vector3 center = Vector3::zero());
+	PhysicsCharacter(Node* node, const PhysicsCollisionShape::Definition& shape);
 
     /**
      * Destructor.
@@ -334,8 +310,6 @@ private:
 
     bool fixCollision(btCollisionWorld* world);
 
-    Node* _node;
-    PhysicsMotionState* _motionState;
     btVector3 _moveVelocity;
     float _forwardVelocity;
     float _rightVelocity;
@@ -347,10 +321,7 @@ private:
     btVector3 _currentPosition;
     std::map<const char*, CharacterAnimation> _animations;
     std::map<unsigned int, CharacterAnimation*> _layers;
-    btPairCachingGhostObject* _ghostObject;
-    btConvexShape* _collisionShape;
     btManifoldArray	_manifoldArray;
-    int _ignoreTransformChanged;
     float _stepHeight;
     float _slopeAngle;
     float _cosSlopeAngle;

+ 49 - 9
gameplay/src/PhysicsCollisionObject.cpp

@@ -6,12 +6,60 @@
 namespace gameplay
 {
 
-PhysicsCollisionObject::PhysicsCollisionObject()
+// Internal class used to implement the collidesWith(PhysicsCollisionObject*) function.
+struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
+{
+    btScalar addSingleResult(btManifoldPoint& cp, 
+		const btCollisionObject* a, int partIdA, int indexA, 
+		const btCollisionObject* b, int partIdB, int indexB)
+	{
+		result = true;
+		return 0.0f;
+	}
+
+    bool result;
+};
+
+PhysicsCollisionObject::PhysicsCollisionObject(Node* node)
+	: _node(node), _motionState(NULL), _collisionShape(NULL)
 {
 }
 
 PhysicsCollisionObject::~PhysicsCollisionObject()
 {
+	SAFE_DELETE(_motionState);
+
+	Game::getInstance()->getPhysicsController()->destroyShape(_collisionShape);
+}
+
+PhysicsCollisionShape::Type PhysicsCollisionObject::getShapeType() const
+{
+	return getCollisionShape()->getType();
+}
+
+Node* PhysicsCollisionObject::getNode() const
+{
+	return _node;
+}
+
+PhysicsCollisionShape* PhysicsCollisionObject::getCollisionShape() const
+{
+	return _collisionShape;
+}
+
+PhysicsMotionState* PhysicsCollisionObject::getMotionState() const
+{
+	return _motionState;
+}
+
+bool PhysicsCollisionObject::isKinematic() const
+{
+	return getCollisionObject()->isKinematicObject();
+}
+
+bool PhysicsCollisionObject::isDynamic() const
+{
+	return !getCollisionObject()->isStaticOrKinematicObject();
 }
 
 void PhysicsCollisionObject::addCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
@@ -55,12 +103,4 @@ bool PhysicsCollisionObject::CollisionPair::operator < (const CollisionPair& col
     return false;
 }
 
-btScalar PhysicsCollisionObject::CollidesWithCallback::addSingleResult(btManifoldPoint& cp,
-    const btCollisionObject* a, int partIdA, int indexA, 
-    const btCollisionObject* b, int partIdB, int indexB)
-{
-    result = true;
-    return 0.0f;
-}
-
 }

+ 60 - 27
gameplay/src/PhysicsCollisionObject.h

@@ -2,6 +2,8 @@
 #define PHYSICSCOLLISIONOBJECT_H_
 
 #include "Vector3.h"
+#include "PhysicsCollisionShape.h"
+#include "PhysicsMotionState.h"
 
 namespace gameplay
 {
@@ -14,11 +16,12 @@ class Node;
 class PhysicsCollisionObject
 {
     friend class PhysicsController;
+	friend class PhysicsConstraint;
 
 public:
 
     /**
-     * Enumeration of all possible collision object types.
+     * Represents the different types of collision objects.
      */
     enum Type
     {
@@ -35,7 +38,12 @@ public:
         /** 
          * PhysicsGhostObject type.
          */
-        GHOST_OBJECT
+        GHOST_OBJECT,
+
+		/**
+		 * No collision object.
+		 */
+		NONE
     };
 
     /** 
@@ -114,15 +122,52 @@ public:
                                     const Vector3& contactPointB = Vector3::zero()) = 0;
     };
 
+	/**
+     * Virtual destructor.
+     */
+    virtual ~PhysicsCollisionObject();
+
     /**
      * Returns the type of the collision object.
      */
     virtual PhysicsCollisionObject::Type getType() const = 0;
 
+	/**
+	 * Returns the type of the shape for this collision object.
+	 */
+	PhysicsCollisionShape::Type getShapeType() const;
+
     /**
      * Returns the node associated with this collision object.
      */
-    virtual Node* getNode() const = 0;
+    Node* getNode() const;
+
+	/**
+     * Returns the collision shape.
+     *
+     * @return The collision shape.
+     */
+    PhysicsCollisionShape* getCollisionShape() const;
+
+	/**
+	 * Returns whether this collision object is kinematic.
+	 *
+	 * A kinematic collision object is an object that is not simulated by
+	 * the physics system and instead has its transform driven manually.
+	 *
+	 * @return Whether the collision object is kinematic.
+	 */
+	bool isKinematic() const;
+
+    /**
+     * Returns whether this collision object is dynamic.
+	 *
+	 * A dynamic collision object is simulated entirely by the physics system,
+	 * such as with dynamic rigid bodies. 
+     *
+     * @return Whether the collision object is dynamic.
+     */
+    bool isDynamic() const;
 
     /**
      * Adds a collision listener for this collision object.
@@ -153,12 +198,7 @@ protected:
     /**
      * Constructor.
      */
-    PhysicsCollisionObject();
-
-    /**
-     * Virtual destructor.
-     */
-    virtual ~PhysicsCollisionObject();
+    PhysicsCollisionObject(Node* node);
 
     /**
      * Returns the Bullet Physics collision object.
@@ -167,24 +207,17 @@ protected:
      */
     virtual btCollisionObject* getCollisionObject() const = 0;
 
-    /**
-     * Returns the Bullet Physics collision shape.
-     *
-     * @return The Bullet collision shape.
-     */
-    virtual btCollisionShape* getCollisionShape() const = 0;
-
-private:
-
-    // Internal class used to implement the collidesWith(PhysicsRigidBody*) function.
-    struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
-    {
-        btScalar addSingleResult(btManifoldPoint& cp, 
-                                 const btCollisionObject* a, int partIdA, int indexA, 
-                                 const btCollisionObject* b, int partIdB, int indexB);
-
-        bool result;
-    };
+	/**
+	 * Returns the physics motion state.
+	 *
+	 * @return The motion state object.
+	 */
+	PhysicsMotionState* getMotionState() const;
+
+	// Common member variables
+	Node* _node;
+    PhysicsMotionState* _motionState;
+    PhysicsCollisionShape* _collisionShape;
 
 };
 

+ 155 - 0
gameplay/src/PhysicsCollisionShape.cpp

@@ -0,0 +1,155 @@
+#include "Base.h"
+#include "PhysicsCollisionShape.h"
+
+namespace gameplay
+{
+
+PhysicsCollisionShape::PhysicsCollisionShape(Type type, btCollisionShape* shape)
+	: _type(type), _shape(shape)
+{
+	memset(&_shapeData, 0, sizeof(_shapeData));
+}
+
+PhysicsCollisionShape::~PhysicsCollisionShape()
+{
+	if (_shape)
+	{
+		// Cleanup shape-specific cached data
+		switch (_type)
+		{
+		case SHAPE_MESH:
+			if (_shapeData.meshData)
+			{
+				SAFE_DELETE_ARRAY(_shapeData.meshData->vertexData);
+				for (unsigned int i = 0; i < _shapeData.meshData->indexData.size(); i++)
+				{
+					SAFE_DELETE_ARRAY(_shapeData.meshData->indexData[i]);
+				}
+				SAFE_DELETE(_shapeData.meshData);
+			}
+			break;
+		case SHAPE_HEIGHTFIELD:
+			if (_shapeData.heightfieldData)
+			{
+				SAFE_DELETE_ARRAY(_shapeData.heightfieldData->heightData);
+				SAFE_DELETE(_shapeData.heightfieldData);
+			}
+			break;
+		}
+
+		// Free the bullet shape
+		SAFE_DELETE(_shape);
+	}
+}
+
+PhysicsCollisionShape::Type PhysicsCollisionShape::getType() const
+{
+	return _type;
+}
+
+PhysicsCollisionShape::Definition::Definition()
+	: isExplicit(false), centerAbsolute(false)
+{
+	memset(&data, 0, sizeof(data));
+}
+
+PhysicsCollisionShape::Definition::~Definition()
+{
+	switch (type)
+	{
+	case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+		SAFE_RELEASE(data.heightfield);
+		break;
+
+	case PhysicsCollisionShape::SHAPE_MESH:
+		SAFE_RELEASE(data.mesh);
+		break;
+	}
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::box()
+{
+	Definition d;
+	d.type = SHAPE_BOX;
+	d.isExplicit = false;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::box(const Vector3& extents, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_BOX;
+	d.data.boxExtents = extents;
+	d.data.boxCenter = center;
+	d.isExplicit = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere()
+{
+	Definition d;
+	d.type = SHAPE_SPHERE;
+	d.isExplicit = false;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere(float radius, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_SPHERE;
+	d.data.sphereRadius = radius;
+	d.data.sphereCenter = center;
+	d.isExplicit  = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule()
+{
+	Definition d;
+	d.type = SHAPE_CAPSULE;
+	d.isExplicit = false;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule(float radius, float height, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_CAPSULE;
+	d.data.capsuleRadius = radius;
+	d.data.capsuleHeight = height;
+	d.data.capsuleCenter = center;
+	d.isExplicit = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* image)
+{
+	image->addRef();
+
+	Definition d;
+	d.type = SHAPE_HEIGHTFIELD;
+	d.data.heightfield = image;
+	d.isExplicit = true;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::mesh(Mesh* mesh)
+{
+	mesh->addRef();
+
+	Definition d;
+	d.type = SHAPE_MESH;
+	d.data.mesh = mesh;
+	d.isExplicit = true;
+	d.centerAbsolute = false;
+	return d;
+}
+
+}

+ 220 - 0
gameplay/src/PhysicsCollisionShape.h

@@ -0,0 +1,220 @@
+#ifndef PHYSICSCOLLISIONSHAPE_H_
+#define PHYSICSCOLLISIONSHAPE_H_
+
+#include "Vector3.h"
+#include "Image.h"
+#include "Mesh.h"
+
+namespace gameplay
+{
+
+/**
+ * Base physics collision shape class that all supported shapes derive from.
+ */
+class PhysicsCollisionShape : public Ref
+{
+	friend class PhysicsController;
+	friend class PhysicsRigidBody;
+
+public:
+
+	/**
+	 * Defines the supported collision shape types.
+	 */
+	enum Type
+	{
+		SHAPE_BOX,
+		SHAPE_SPHERE,
+		SHAPE_CAPSULE,
+		SHAPE_MESH,
+		SHAPE_HEIGHTFIELD
+	};
+
+	/**
+	 * Structure representing the definition of a collision shape, which is used
+	 * during collision shape construction time.
+	 *
+	 * Use the static methods on the PhysicsCollisionShape class to return
+	 * 
+	 */
+	struct Definition
+	{
+		friend class PhysicsCollisionShape;
+		friend class PhysicsController;
+		friend class PhysicsRigidBody;
+
+	public:
+
+		~Definition();
+
+	private:
+
+		Definition();
+
+		// Shape type.
+		PhysicsCollisionShape::Type type;
+
+		// Shape data.
+		union
+		{
+			struct { Vector3 boxExtents, boxCenter; };
+			struct { Vector3 sphereCenter; float sphereRadius; };
+			struct { Vector3 capsuleCenter; float capsuleRadius, capsuleHeight; };
+			struct { Image* heightfield; };
+			struct { Mesh* mesh; };
+		} data;
+
+		// Whether the shape definition is explicit, or if it is inherited from node bounds.
+		bool isExplicit;
+
+		// Whether the center position is absolute or relative to the node position.
+		bool centerAbsolute;
+	};
+
+	/**
+	 * Returns the type of this collision shape.
+	 *
+	 * @return The collision shape type.
+	 */
+	PhysicsCollisionShape::Type getType() const;
+
+	/**
+	 * Returns the internal bullet physics shape object.
+	 *
+	 * @return The bullet shape object.
+	 */
+	template <class T> T* getShape() const
+	{
+		return static_cast<T*>(_shape);
+	}
+
+	/**
+	 * Returns the internal bullet physics shape object.
+	 *
+	 * @return The bullet shape object.
+	 */
+	template <> btCollisionShape* getShape<btCollisionShape>() const
+	{
+		return _shape;
+	}
+
+	/**
+	 * Defines a box shape, using the bounding volume of the node it is attached to.
+	 *
+	 * @return Definition of a box shape.
+	 */
+	static PhysicsCollisionShape::Definition box();
+
+	/**
+	 * Defines a box shape, using the specified shape information and center.
+	 *
+	 * @param extents Extents of the box shape along the x, y and z axes.
+	 * @param center Center point of the box.
+	 * @param absolute True to specifiy that the given center point is an absolute position.
+	 *		By default the center point is treated as relative to the location of the node
+	 *		that the shape is attached to.
+	 *
+	 * @return Definition of a box shape.
+	 */
+	static PhysicsCollisionShape::Definition box(const Vector3& extents, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+	/**
+	 * Defines a sphere shape, using the bounding volume of the node it is attached to.
+	 *
+	 * @return Definition of a sphere shape.
+	 */
+	static PhysicsCollisionShape::Definition sphere();
+
+	/**
+	 * Defines a sphere shape, using the specified shape information and center.
+	 *
+	 * @param radius Radius of the sphere.
+	 * @param center Center point of the sphere.
+	 * @param absolute True to specifiy that the given center point is an absolute position.
+	 *		By default the center point is treated as relative to the location of the node
+	 *		that the shape is attached to.
+	 *
+	 * @return Definition of a sphere shape.
+	 */
+	static PhysicsCollisionShape::Definition sphere(float radius, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+	/**
+	 * Defines a capsule shape, using the bounding volume of the node it is attached to.
+	 *
+	 * @return Definition of a capsule shape.
+	 */
+	static PhysicsCollisionShape::Definition capsule();
+
+	/**
+	 * Defines a capsule shape, using the specified shape information and center.
+	 *
+	 * @param radius Radius of the capsule.
+	 * @param height Height of the capsule.
+	 * @param center Center point of the capsule.
+	 * @param absolute True to specifiy that the given center point is an absolute position.
+	 *		By default the center point is treated as relative to the location of the node
+	 *		that the shape is attached to.
+	 *
+	 * @return Definition of a capsule shape.
+	 */
+	static PhysicsCollisionShape::Definition capsule(float radius, float height, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+	/**
+	 * Defines a heightfield shape using the specified heightfield image.
+	 *
+	 * @return Definition of a heightfield shape.
+	 */
+	static PhysicsCollisionShape::Definition heightfield(Image* image);
+
+	/**
+	 * Defines a mesh shape using the specified mehs.
+	 *
+	 * @return Definition of a mesh shape.
+	 */
+	static PhysicsCollisionShape::Definition mesh(Mesh* mesh);
+
+private:
+
+	struct MeshData
+	{
+		float* vertexData;
+		std::vector<unsigned char*> indexData;
+	};
+
+	struct HeightfieldData
+	{
+		float* heightData;
+		unsigned int width;
+		unsigned int height;
+		mutable Matrix inverse;
+		mutable bool inverseIsDirty;
+	};
+
+	/**
+	 * Constructor.
+	 */
+	PhysicsCollisionShape(Type type, btCollisionShape* shape);
+
+	/**
+	 * Destructor.
+	 */
+	~PhysicsCollisionShape();
+
+	// Shape type
+	Type _type;
+
+	// Bullet shape object
+	btCollisionShape* _shape;
+
+	// Shape specific cached data
+	union
+	{
+		MeshData* meshData;
+		HeightfieldData* heightfieldData;
+	} _shapeData;
+
+};
+
+}
+
+#endif

+ 1 - 1
gameplay/src/PhysicsConstraint.cpp

@@ -155,7 +155,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 
 Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 {
-    btVector3 centerOfMassOffset = ((PhysicsMotionState*)node->getRigidBody()->_body->getMotionState())->_centerOfMassOffset.getOrigin();
+	btVector3 centerOfMassOffset = (node->getCollisionObject()->getMotionState())->_centerOfMassOffset.getOrigin();
     return Vector3(v.x + centerOfMassOffset.x(), v.y + centerOfMassOffset.y(), v.z + centerOfMassOffset.z());
 }
 

+ 425 - 125
gameplay/src/PhysicsController.cpp

@@ -1,8 +1,10 @@
 #include "Base.h"
-#include "Game.h"
-#include "MeshPart.h"
 #include "PhysicsController.h"
+#include "PhysicsRigidBody.h"
+#include "PhysicsCharacter.h"
 #include "PhysicsMotionState.h"
+#include "Game.h"
+#include "MeshPart.h"
 #include "Package.h"
 
 #include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
@@ -42,16 +44,6 @@ void PhysicsController::addStatusListener(Listener* listener)
     _listeners->push_back(listener);
 }
 
-PhysicsCharacter* PhysicsController::createCharacter(Node* node, float radius, float height, const Vector3& center)
-{
-    return new PhysicsCharacter(node, radius, height, center);
-}
-
-void PhysicsController::destroyCharacter(PhysicsCharacter* character)
-{
-    SAFE_DELETE(character);
-}
-
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
     checkConstraintRigidBodies(a, b);
@@ -163,7 +155,7 @@ PhysicsCollisionObject* PhysicsController::rayTest(const Ray& ray, float distanc
 btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 
     const btCollisionObject* b, int partIdB, int indexB)
 {
-    // Get pointers to the PhysicsRigidBody objects.
+    // Get pointers to the PhysicsCollisionObject objects.
     PhysicsCollisionObject* rbA = Game::getInstance()->getPhysicsController()->getCollisionObject(a);
     PhysicsCollisionObject* rbB = Game::getInstance()->getPhysicsController()->getCollisionObject(b);
 
@@ -171,7 +163,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
     // 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.
-    PhysicsRigidBody::CollisionPair pair(rbA, rbB);
+    PhysicsCollisionObject::CollisionPair pair(rbA, rbB);
 
     CollisionInfo* collisionInfo;
     if (_collisionStatus.count(pair) > 0)
@@ -184,7 +176,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
         collisionInfo = &_collisionStatus[pair];
 
         // Add the appropriate listeners.
-        PhysicsRigidBody::CollisionPair p1(pair.objectA, NULL);
+        PhysicsCollisionObject::CollisionPair p1(pair.objectA, NULL);
         if (_collisionStatus.count(p1) > 0)
         {
             const CollisionInfo& ci = _collisionStatus[p1];
@@ -194,7 +186,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
                 collisionInfo->_listeners.push_back(*iter);
             }
         }
-        PhysicsRigidBody::CollisionPair p2(pair.objectB, NULL);
+        PhysicsCollisionObject::CollisionPair p2(pair.objectB, NULL);
         if (_collisionStatus.count(p2) > 0)
         {
             const CollisionInfo& ci = _collisionStatus[p2];
@@ -328,12 +320,12 @@ void PhysicsController::update(long elapsedTime)
     // If an entry was marked for removal in the last frame, remove it now.
 
     // Dirty the collision status cache entries.
-    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
+    std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
     for (; iter != _collisionStatus.end();)
     {
         if ((iter->second._status & REMOVE) != 0)
         {
-            std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
+            std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
             iter++;
             _collisionStatus.erase(eraseIter);
         }
@@ -410,15 +402,15 @@ void PhysicsController::addCollisionObject(PhysicsCollisionObject* object)
     switch (object->getType())
     {
     case PhysicsCollisionObject::RIGID_BODY:
-        _world->addRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));
+		_world->addRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);
         break;
 
     case PhysicsCollisionObject::CHARACTER:
-        _world->addCollisionObject(object->getCollisionObject(), btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter);
+		_world->addCollisionObject(object->getCollisionObject());//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);//, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::CharacterFilter );// | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter*/ 0);
         break;
 
     case PhysicsCollisionObject::GHOST_OBJECT:
-        _world->addCollisionObject(object->getCollisionObject(), btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::AllFilter);
+		_world->addCollisionObject(object->getCollisionObject());//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::DefaultFilter | btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter);
         break;
 
     default:
@@ -449,28 +441,8 @@ void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
         }
     }
 
-    // Release collision shape
-    if (object->getCollisionShape())
-    {
-        PhysicsCollisionShape* shape = reinterpret_cast<PhysicsCollisionShape*>(object->getCollisionShape()->getUserPointer());
-        if (shape)
-        {
-            if (shape->getRefCount() == 1)
-            {
-                std::vector<PhysicsCollisionShape*>::iterator shapeItr = std::find(_shapes.begin(), _shapes.end(), shape);
-                shape->release();
-                if (shapeItr != _shapes.end())
-                    _shapes.erase(shapeItr);
-            }
-            else
-            {
-                shape->release();
-            }
-        }
-    }
-
     // Find all references to the object in the collision status cache and mark them for removal.
-    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
+    std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
     for (; iter != _collisionStatus.end(); iter++)
     {
         if (iter->first.objectA == object || iter->first.objectB == object)
@@ -484,55 +456,207 @@ PhysicsCollisionObject* PhysicsController::getCollisionObject(const btCollisionO
     return reinterpret_cast<PhysicsCollisionObject*>(collisionObject->getUserPointer());
 }
 
-btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const Vector3& scale)
+void getBoundingBox(Node* node, BoundingBox* out, bool merge = false)
 {
-    btVector3 halfExtents(scale.x * 0.5 * abs(max.x - min.x), scale.y * 0.5 * abs(max.y - min.y), scale.z * 0.5 * abs(max.z - min.z));
+	if (node->getModel())
+	{
+		if (merge)
+			out->merge(node->getModel()->getMesh()->getBoundingBox());
+		else
+		{
+			out->set(node->getModel()->getMesh()->getBoundingBox());
+			merge = true;
+		}
+	}
+
+	Node* child = node->getFirstChild();
+	while (child)
+	{
+		getBoundingBox(child, out, merge);
+		child = child->getNextSibling();
+	}
+}
 
-    // Return the box shape from the cache if it already exists.
-    for (unsigned int i = 0; i < _shapes.size(); i++)
+void getBoundingSphere(Node* node, BoundingSphere* out, bool merge = false)
+{
+	if (node->getModel())
+	{
+		if (merge)
+			out->merge(node->getModel()->getMesh()->getBoundingSphere());
+		else
+		{
+			out->set(node->getModel()->getMesh()->getBoundingSphere());
+			merge = true;
+		}
+	}
+
+	Node* child = node->getFirstChild();
+	while (child)
+	{
+		getBoundingSphere(child, out, merge);
+		child = child->getNextSibling();
+	}
+}
+
+void computeCenterOfMass(const Vector3& center, const Vector3& scale, Vector3* centerOfMassOffset)
+{
+	// Update center of mass offset
+	*centerOfMassOffset = center;
+	centerOfMassOffset->x *= scale.x;
+	centerOfMassOffset->y *= scale.y;
+	centerOfMassOffset->z *= scale.z;
+	centerOfMassOffset->negate();
+}
+
+PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsCollisionShape::Definition& shape, Vector3* centerOfMassOffset)
+{
+	PhysicsCollisionShape* collisionShape = NULL;
+
+    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
+    Vector3 scale;
+    node->getWorldMatrix().getScale(&scale);
+
+	switch (shape.type)
     {
-        if (_shapes[i]->_shape->getShapeType() == BOX_SHAPE_PROXYTYPE)
+	case PhysicsCollisionShape::SHAPE_BOX:
         {
-            btBoxShape* box = static_cast<btBoxShape*>(_shapes[i]->_shape);
-            if (box->getHalfExtentsWithMargin() == halfExtents)
-            {
-                _shapes[i]->addRef();
-                return box;
-            }
+			if (shape.isExplicit)
+			{
+				// Use the passed in box information
+				collisionShape = createBox(shape.data.boxExtents, Vector3::one());
+
+				if (shape.centerAbsolute)
+				{
+					computeCenterOfMass(shape.data.boxCenter, scale, centerOfMassOffset);
+				}
+				else
+				{
+					BoundingBox box;
+					getBoundingBox(node, &box);
+					computeCenterOfMass(box.getCenter() + shape.data.boxCenter, scale, centerOfMassOffset);
+				}
+			}
+			else
+			{
+				// 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);
+
+				computeCenterOfMass(box.getCenter(), scale, centerOfMassOffset);
+			}
+        }
+		break;
+
+	case PhysicsCollisionShape::SHAPE_SPHERE:
+        {
+			if (shape.isExplicit)
+			{
+				// Use the passed in sphere information
+				collisionShape = createSphere(shape.data.sphereRadius, Vector3::one());
+
+				if (shape.centerAbsolute)
+				{
+					computeCenterOfMass(shape.data.sphereCenter, scale, centerOfMassOffset);
+				}
+				else
+				{
+					BoundingSphere sphere;
+					getBoundingSphere(node, &sphere);
+					computeCenterOfMass(sphere.center + shape.data.sphereCenter, scale, centerOfMassOffset);
+				}
+			}
+			else
+			{
+				// Automatically compute bounding sphere from mesh's bounding sphere
+				BoundingSphere sphere;
+				getBoundingSphere(node, &sphere);
+				collisionShape = createSphere(sphere.radius, scale);
+
+				computeCenterOfMass(sphere.center, scale, centerOfMassOffset);
+			}
+        }
+		break;
+
+	case PhysicsCollisionShape::SHAPE_CAPSULE:
+		{
+			if (shape.isExplicit)
+			{
+				// Use the passed in capsule information
+				collisionShape = createCapsule(shape.data.capsuleRadius, shape.data.capsuleHeight, Vector3::one());
+
+				if (shape.centerAbsolute)
+				{
+					computeCenterOfMass(shape.data.capsuleCenter, scale, centerOfMassOffset);
+				}
+				else
+				{
+					BoundingBox box;
+					getBoundingBox(node, &box);
+					computeCenterOfMass(box.getCenter() + shape.data.capsuleCenter, scale, centerOfMassOffset);
+				}
+			}
+			else
+			{
+				// 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);
+				float height = (box.max.y - box.min.y) - radius * 2.0f;
+				collisionShape = createCapsule(radius, height, scale);
+
+				computeCenterOfMass(box.getCenter(), scale, centerOfMassOffset);
+			}
+		}
+		break;
+
+	case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+		{
+			// 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
+			collisionShape = createMesh(shape.data.mesh, scale);
         }
+		break;
     }
-    
-    // Create the box shape and add it to the cache.
-    btBoxShape* box = bullet_new<btBoxShape>(halfExtents);
-    _shapes.push_back(new PhysicsCollisionShape(box));
 
-    return box;
+	return collisionShape;
 }
 
-btCollisionShape* PhysicsController::createCapsule(float radius, float height)
+PhysicsCollisionShape* PhysicsController::createBox(const Vector3& extents, const Vector3& scale)
 {
-    // Return the capsule shape from the cache if it already exists.
-    for (unsigned int i = 0; i < _shapes.size(); i++)
+    btVector3 halfExtents(scale.x * 0.5 * extents.x, scale.y * 0.5 * extents.y, scale.z * 0.5 * extents.z);
+
+	PhysicsCollisionShape* shape;
+
+    // Return the box shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); ++i)
     {
-        if (_shapes[i]->_shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE)
+		shape = _shapes[i];
+		if (shape->getType() == PhysicsCollisionShape::SHAPE_BOX)
         {
-            btCapsuleShape* capsule = static_cast<btCapsuleShape*>(_shapes[i]->_shape);
-            if (capsule->getRadius() == radius && capsule->getHalfHeight() == 0.5f * height)
+			btBoxShape* box = static_cast<btBoxShape*>(shape->_shape);
+            if (box->getHalfExtentsWithMargin() == halfExtents)
             {
-                _shapes[i]->addRef();
-                return capsule;
+				shape->addRef();
+                return shape;
             }
         }
     }
-    
-    // Create the capsule shape and add it to the cache.
-    btCapsuleShape* capsule = bullet_new<btCapsuleShape>(radius, height);
-    _shapes.push_back(new PhysicsCollisionShape(capsule));
 
-    return capsule;
+    // Create the box shape and add it to the cache.
+	shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_BOX, bullet_new<btBoxShape>(halfExtents));
+    _shapes.push_back(shape);
+
+    return shape;
 }
 
-btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& scale)
+PhysicsCollisionShape* PhysicsController::createSphere(float radius, const Vector3& scale)
 {
     // Since sphere shapes depend only on the radius, the best we can do is take
     // the largest dimension and apply that as the uniform scale to the rigid body.
@@ -541,48 +665,188 @@ btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& s
         uniformScale = scale.y;
     if (uniformScale < scale.z)
         uniformScale = scale.z;
-    
+
+	float scaledRadius = radius * uniformScale;
+
+	PhysicsCollisionShape* shape;
+
     // Return the sphere shape from the cache if it already exists.
-    for (unsigned int i = 0; i < _shapes.size(); i++)
+    for (unsigned int i = 0; i < _shapes.size(); ++i)
     {
-        if (_shapes[i]->_shape->getShapeType() == SPHERE_SHAPE_PROXYTYPE)
+		shape = _shapes[i];
+		if (shape->getType() == PhysicsCollisionShape::SHAPE_SPHERE)
         {
-            btSphereShape* sphere = static_cast<btSphereShape*>(_shapes[i]->_shape);
-            if (sphere->getRadius() == uniformScale * radius)
+            btSphereShape* sphere = static_cast<btSphereShape*>(shape->_shape);
+            if (sphere->getRadius() == scaledRadius)
             {
-                _shapes[i]->addRef();
-                return sphere;
+                shape->addRef();
+                return shape;
             }
         }
     }
 
     // Create the sphere shape and add it to the cache.
-    btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
-    _shapes.push_back(new PhysicsCollisionShape(sphere));
+	shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_SPHERE, bullet_new<btSphereShape>(scaledRadius));
+    _shapes.push_back(shape);
 
-    return sphere;
+    return shape;
 }
 
-btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Vector3& scale)
+PhysicsCollisionShape* PhysicsController::createCapsule(float radius, float height, const Vector3& scale)
 {
-    assert(body);
+	float girthScale = scale.x;
+	if (girthScale < scale.z)
+		girthScale = scale.z;
+	float scaledRadius = radius * girthScale;
+	float scaledHeight = height * scale.y;
 
-    // Retrieve the mesh rigid body data from the node's mesh.
-    Model* model = body->_node ? body->_node->getModel() : NULL;
-    Mesh* mesh = model ? model->getMesh() : NULL;
-    if (mesh == NULL)
+	PhysicsCollisionShape* shape;
+
+    // Return the capsule shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
     {
-        LOG_ERROR("Cannot create mesh rigid body for node without model/mesh.");
-        return NULL;
+		shape = _shapes[i];
+		if (shape->getType() == PhysicsCollisionShape::SHAPE_CAPSULE)
+        {
+            btCapsuleShape* capsule = static_cast<btCapsuleShape*>(shape->_shape);
+            if (capsule->getRadius() == scaledRadius && capsule->getHalfHeight() == 0.5f * scaledHeight)
+            {
+                shape->addRef();
+                return shape;
+            }
+        }
+    }
+
+    // Create the capsule shape and add it to the cache.
+	shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_CAPSULE, bullet_new<btCapsuleShape>(scaledRadius, scaledHeight));
+    _shapes.push_back(shape);
+
+    return shape;
+}
+
+PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* image, Vector3* 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).
+	float width, length, minHeight, maxHeight;
+	if (node->getModel() && node->getModel()->getMesh())
+	{
+		const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
+		width = box.max.x - box.min.x;
+		length = box.max.z - box.min.z;
+		minHeight = box.min.y;
+		maxHeight = box.max.y;
+	}
+	else
+	{
+		width = image->getWidth();
+		length = image->getHeight();
+		minHeight = 0.0f;
+		maxHeight = 255.0f;
+	}
+
+    // Get the size in bytes of a pixel (we ensure that the image's
+    // pixel format is actually supported before calling this constructor).
+    unsigned int pixelSize = 0;
+    switch (image->getFormat())
+    {
+        case Image::RGB:
+            pixelSize = 3;
+            break;
+        case Image::RGBA:
+            pixelSize = 4;
+            break;
+		default:
+			LOG_ERROR("Unsupported pixel format for heightmap image.");
+			return NULL;
     }
 
-    // Only support meshes with triangle list primitive types
-    if (mesh->getPrimitiveType() != Mesh::TRIANGLES)
+    // Calculate the heights for each pixel.
+    float* heights = new float[image->getWidth() * image->getHeight()];
+	unsigned char* data = image->getData();
+    for (unsigned int x = 0, w = image->getWidth(); x < w; ++x)
     {
-        LOG_ERROR("Cannot create mesh rigid body for mesh without TRIANGLES primitive type.");
-        return NULL;
+        for (unsigned int y = 0, h = image->getHeight(); y < h; ++y)
+        {
+            heights[x + y * w] = ((((float)data[(x + y * h) * pixelSize + 0]) +
+                ((float)data[(x + y * h) * pixelSize + 1]) +
+                ((float)data[(x + y * h) * pixelSize + 2])) / 768.0f) * (maxHeight - minHeight) + minHeight;
+        }
     }
 
+	PhysicsCollisionShape::HeightfieldData* heightfieldData = new PhysicsCollisionShape::HeightfieldData();
+	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;
+	heightfieldData->width = sizeWidth + 1;
+    heightfieldData->height = sizeHeight + 1;
+	heightfieldData->heightData = new float[heightfieldData->width * heightfieldData->height];
+    unsigned int heightIndex = 0;
+    float widthImageFactor = (float)(image->getWidth() - 1) / sizeWidth;
+    float heightImageFactor = (float)(image->getHeight() - 1) / sizeHeight;
+    float x = 0.0f;
+    float z = 0.0f;
+    for (unsigned int row = 0, z = 0.0f; row <= sizeHeight; row++, z += 1.0f)
+    {
+        for (unsigned int col = 0, x = 0.0f; col <= sizeWidth; col++, x += 1.0f)
+        {
+			heightIndex = row * heightfieldData->width + col;
+			heightfieldData->heightData[heightIndex] = calculateHeight(heights, image->getWidth(), image->getHeight(), x * widthImageFactor, (sizeHeight - z) * heightImageFactor);
+        }
+    }
+    SAFE_DELETE_ARRAY(heights);
+
+    // Offset the heightmap's center of mass according to the way that Bullet calculates the origin 
+    // of its heightfield collision shape; see documentation for the btHeightfieldTerrainShape for more info.
+    Vector3 s;
+    node->getWorldMatrix().getScale(&s);
+	centerOfMassOffset->set(0.0f, -(maxHeight - (0.5f * (maxHeight - minHeight))) / s.y, 0.0f);
+
+	// 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
+	PhysicsCollisionShape* shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_HEIGHTFIELD, terrainShape);
+	shape->_shapeData.heightfieldData = heightfieldData;
+
+    _shapes.push_back(shape);
+
+    return shape;
+}
+
+PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3& scale)
+{
+    assert(mesh);
+
+	// Only support meshes with triangle list primitive types
+	bool triMesh = true;
+	if (mesh->getPartCount() > 0)
+	{
+		for (unsigned int i = 0; i < mesh->getPartCount(); ++i)
+		{
+			if (mesh->getPart(i)->getPrimitiveType() != Mesh::TRIANGLES)
+			{
+				triMesh = false;
+				break;
+			}
+		}
+	}
+	else
+	{
+		triMesh = mesh->getPrimitiveType() == Mesh::TRIANGLES;
+	}
+
+	if (!triMesh)
+	{
+		LOG_ERROR("Mesh rigid bodies are currently only supported on meshes with TRIANGLES primitive type.");
+		return NULL;
+	}
+
     // The mesh must have a valid URL (i.e. it must have been loaded from a Package)
     // in order to fetch mesh data for computing mesh rigid body.
     if (strlen(mesh->getUrl()) == 0)
@@ -597,11 +861,15 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         return NULL;
     }
 
+	// Create mesh data to be populated and store in returned collision shape
+	PhysicsCollisionShape::MeshData* shapeMeshData = new PhysicsCollisionShape::MeshData();
+	shapeMeshData->vertexData = NULL;
+
     // Copy the scaled vertex position data to the rigid body's local buffer.
     Matrix m;
     Matrix::createScale(scale, &m);
     unsigned int vertexCount = data->vertexCount;
-    body->_vertexData = new float[vertexCount * 3];
+	shapeMeshData->vertexData = new float[vertexCount * 3];
     Vector3 v;
     int vertexStride = data->vertexFormat.getVertexSize();
     for (unsigned int i = 0; i < data->vertexCount; i++)
@@ -610,7 +878,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
               *((float*)&data->vertexData[i * vertexStride + 1 * sizeof(float)]),
               *((float*)&data->vertexData[i * vertexStride + 2 * sizeof(float)]));
         v *= m;
-        memcpy(&(body->_vertexData[i * 3]), &v, sizeof(float) * 3);
+		memcpy(&(shapeMeshData->vertexData[i * 3]), &v, sizeof(float) * 3);
     }
 
     btTriangleIndexVertexArray* meshInterface = bullet_new<btTriangleIndexVertexArray>();
@@ -643,7 +911,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
 
             // Move the index data into the rigid body's local buffer.
             // Set it to NULL in the MeshPartData so it is not released when the data is freed.
-            body->_indexData.push_back(meshPart->indexData);
+			shapeMeshData->indexData.push_back(meshPart->indexData);
             meshPart->indexData = NULL;
 
             // Create a btIndexedMesh object for the current mesh part.
@@ -651,9 +919,9 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
             indexedMesh.m_indexType = indexType;
             indexedMesh.m_numTriangles = meshPart->indexCount / 3; // assume TRIANGLES primitive type
             indexedMesh.m_numVertices = meshPart->indexCount;
-            indexedMesh.m_triangleIndexBase = (const unsigned char*)body->_indexData[i];
+			indexedMesh.m_triangleIndexBase = (const unsigned char*)shapeMeshData->indexData[i];
             indexedMesh.m_triangleIndexStride = indexStride*3;
-            indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
+			indexedMesh.m_vertexBase = (const unsigned char*)shapeMeshData->vertexData;
             indexedMesh.m_vertexStride = sizeof(float)*3;
             indexedMesh.m_vertexType = PHY_FLOAT;
 
@@ -669,16 +937,16 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         {
             indexData[i] = i;
         }
-        body->_indexData.push_back((unsigned char*)indexData);
+        shapeMeshData->indexData.push_back((unsigned char*)indexData);
 
         // Create a single btIndexedMesh object for the mesh interface.
         btIndexedMesh indexedMesh;
         indexedMesh.m_indexType = PHY_INTEGER;
         indexedMesh.m_numTriangles = data->vertexCount / 3; // assume TRIANGLES primitive type
         indexedMesh.m_numVertices = data->vertexCount;
-        indexedMesh.m_triangleIndexBase = body->_indexData[0];
+        indexedMesh.m_triangleIndexBase = shapeMeshData->indexData[0];
         indexedMesh.m_triangleIndexStride = sizeof(unsigned int);
-        indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
+        indexedMesh.m_vertexBase = (const unsigned char*)shapeMeshData->vertexData;
         indexedMesh.m_vertexStride = sizeof(float)*3;
         indexedMesh.m_vertexType = PHY_FLOAT;
 
@@ -686,8 +954,11 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         meshInterface->addIndexedMesh(indexedMesh, indexedMesh.m_indexType);
     }
 
-    btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
-    _shapes.push_back(new PhysicsCollisionShape(shape));
+	// 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
     SAFE_DELETE(data);
@@ -695,11 +966,52 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
     return shape;
 }
 
-btCollisionShape* PhysicsController::createHeightfield(int width, int height, void* heightfieldData, float minHeight, float maxHeight)
+void PhysicsController::destroyShape(PhysicsCollisionShape* shape)
 {
-    btCollisionShape* shape = bullet_new<btHeightfieldTerrainShape>(width, height, heightfieldData, 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);
-    _shapes.push_back(new PhysicsCollisionShape(shape));
-    return shape;
+    if (shape)
+    {
+        if (shape->getRefCount() == 1)
+        {
+			// 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
+		shape->release();
+    }
+}
+
+float PhysicsController::calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
+{
+    unsigned int x1 = x;
+    unsigned int y1 = y;
+    unsigned int x2 = x1 + 1;
+    unsigned int y2 = y1 + 1;
+    float tmp;
+    float xFactor = modf(x, &tmp);
+    float yFactor = modf(y, &tmp);
+    float xFactorI = 1.0f - xFactor;
+    float yFactorI = 1.0f - yFactor;
+
+    if (x2 >= width && y2 >= height)
+    {
+        return data[x1 + y1 * width];
+    }
+    else if (x2 >= width)
+    {
+        return data[x1 + y1 * width] * yFactorI + data[x1 + y2 * width] * yFactor;
+    }
+    else if (y2 >= height)
+    {
+        return data[x1 + y1 * width] * xFactorI + data[x2 + y1 * width] * xFactor;
+    }
+    else
+    {
+        return data[x1 + y1 * width] * xFactorI * yFactorI + data[x1 + y2 * width] * xFactorI * yFactor + 
+            data[x2 + y2 * width] * xFactor * yFactor + data[x2 + y1 * width] * xFactor * yFactorI;
+    }
 }
 
 void PhysicsController::addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
@@ -743,7 +1055,7 @@ void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
         }
     }
 }
-    
+
 PhysicsController::DebugDrawer::DebugDrawer()
     : _mode(btIDebugDraw::DBG_DrawAabb | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints | 
        btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _viewProjection(NULL), _meshBatch(NULL)
@@ -760,7 +1072,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
         "    gl_Position = u_viewProjectionMatrix * a_position;\n"
         "}"
     };
-        
+
     // Fragment shader for drawing colored lines.
     const char* fs_str = 
     {
@@ -772,7 +1084,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
         "   gl_FragColor = v_color;\n"
         "}"
     };
-        
+
     Effect* effect = Effect::createFromSource(vs_str, fs_str);
     Material* material = Material::create(effect);
     material->getStateBlock()->setDepthTest(true);
@@ -783,7 +1095,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
         VertexFormat::Element(VertexFormat::COLOR, 4),
     };
     _meshBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false);
-    
+
     SAFE_RELEASE(material);
     SAFE_RELEASE(effect);
 }
@@ -809,7 +1121,7 @@ void PhysicsController::DebugDrawer::end()
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
 {
     static DebugDrawer::DebugVertex fromVertex, toVertex;
-    
+
     fromVertex.x = from.getX();
     fromVertex.y = from.getY();
     fromVertex.z = from.getZ();
@@ -860,16 +1172,4 @@ int	PhysicsController::DebugDrawer::getDebugMode() const
     return _mode;
 }
 
-PhysicsController::PhysicsCollisionShape::PhysicsCollisionShape(btCollisionShape* shape)
-    : _shape(shape)
-{
-    // Assign user pointer to allow efficient lookup of PhysicsCollisionShape from bullet object
-    shape->setUserPointer(this);
-}
-
-PhysicsController::PhysicsCollisionShape::~PhysicsCollisionShape()
-{
-    SAFE_DELETE(_shape);
-}
-
 }

+ 24 - 59
gameplay/src/PhysicsController.h

@@ -7,12 +7,12 @@
 #include "PhysicsHingeConstraint.h"
 #include "PhysicsSocketConstraint.h"
 #include "PhysicsSpringConstraint.h"
-#include "PhysicsRigidBody.h"
-#include "PhysicsCharacter.h"
+#include "PhysicsCollisionObject.h"
+#include "MeshBatch.h"
 
 namespace gameplay
 {
-    
+
 /**
  * Defines a class for controlling game physics.
  */
@@ -63,42 +63,6 @@ public:
      */
     void addStatusListener(PhysicsController::Listener* listener);
 
-    /**
-     * Creates a new PhysicsCharacter.
-     *
-     * The created character is added to the physics world and automatically receives
-     * physics updates to handle interactions and collisions between the character
-     * and other physics objects in the world. The character will continue to receive
-     * updates until it is destroyed via the destroyCharacter(PhysicsCharacter*) method.
-     *
-     * The node may point to any node in the scene that you wish to control as a character.
-     * When a PhysicsCharacter is created for a particular node, the game will normally
-     * perform all movement directly through the PhysicsCharacter interface and not through
-     * the node itself.
-     *
-     * The radius, height and center parameters define a capsule volume that is used
-     * to represent the character in the physics world. All collision handling is 
-     * performed using this capsule.
-     *
-     * Note that PhysicsCharacter should not be mixed with rigid bodies. Therefore, you 
-     * should ensure that the node (and any of its children) used to create the
-     * PhysicsCharacter does not have any rigid bodies assigned. Doing so will cause
-     * unexpected results.
-     *
-     * @param node Scene node that represents the character.
-     * @param radius Radius of capsule volume used for character collisions.
-     * @param height Height of the capsule volume used for character collisions.
-     * @param center Center point of the capsule volume for the character.
-     */
-    PhysicsCharacter* createCharacter(Node* node, float radius, float height, const Vector3& center = Vector3::zero());
-
-    /**
-     * Destroys a PhysicsCharacter and removes it from the physics world.
-     *
-     * @param character PhysicsCharacter to destroy.
-     */
-    void destroyCharacter(PhysicsCharacter* character);
-
     /**
      * Creates a fixed constraint.
      * 
@@ -265,16 +229,7 @@ private:
         int _status;
     };
 
-    // Wraps Bullet collision shapes (used for implementing shape caching).
-    struct PhysicsCollisionShape : public Ref
-    {
-        PhysicsCollisionShape(btCollisionShape* shape);
-        ~PhysicsCollisionShape();
-
-        btCollisionShape* _shape;
-    };
-
-    /**
+	/**
      * Constructor.
      */
     PhysicsController();
@@ -323,21 +278,31 @@ private:
     
     // Gets the corresponding GamePlay object for the given Bullet object.
     PhysicsCollisionObject* getCollisionObject(const btCollisionObject* collisionObject) const;
+
+	// Creates a collision shape for the given node and gameplay shape definition.
+	// Populates 'centerOfMassOffset' with the correct calculated center of mass offset.
+	PhysicsCollisionShape* createShape(Node* node, const PhysicsCollisionShape::Definition& shape, Vector3* centerOfMassOffset);
     
-    // Creates a box collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createBox(const Vector3& min, const Vector3& max, const Vector3& scale);
+    // Creates a box collision shape.
+    PhysicsCollisionShape* createBox(const Vector3& extents, const Vector3& scale);
+
+	// Creates a sphere collision shape.
+    PhysicsCollisionShape* createSphere(float radius, const Vector3& scale);
+
+    // Creates a capsule collision shape.
+    PhysicsCollisionShape* createCapsule(float radius, float height, const Vector3& scale);
 
-    // Creates a capsule collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createCapsule(float radius, float height);
+	// Creates a heightfield collision shape.
+    PhysicsCollisionShape* createHeightfield(Node* node, Image* image, Vector3* centerOfMassOffset);
 
-    // Creates a sphere collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createSphere(float radius, const Vector3& scale);
+    // Creates a triangle mesh collision shape.
+    PhysicsCollisionShape* createMesh(Mesh* mesh, const Vector3& scale);
 
-    // Creates a triangle mesh collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createMesh(PhysicsRigidBody* body, const Vector3& scale);
+	// Destroys a collision shape created through PhysicsController
+	void destroyShape(PhysicsCollisionShape* shape);
 
-    // Creates a heightfield collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createHeightfield(int width, int height, void* heightfieldData, float minHeight, float maxHeight);
+	// Helper function for calculating heights from heightmap (image) or heightfield data.
+	static float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y);
 
     // Sets up the given constraint for the given two rigid bodies.
     void addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint);

+ 16 - 49
gameplay/src/PhysicsGhostObject.cpp

@@ -1,64 +1,41 @@
 #include "Base.h"
-#include "Game.h"
 #include "PhysicsGhostObject.h"
-#include "Vector3.h"
+#include "Node.h"
+#include "Game.h"
 
 namespace gameplay
 {
 
-PhysicsGhostObject::PhysicsGhostObject(Node* node, PhysicsRigidBody::ShapeType type)
-    : _node(node), _motionState(NULL), _shape(NULL), _ghostObject(NULL)
+PhysicsGhostObject::PhysicsGhostObject(Node* node, const PhysicsCollisionShape::Definition& shape)
+    : PhysicsCollisionObject(node), _ghostObject(NULL)
 {
-    _node->addListener(this);
+	Vector3 centerOfMassOffset;
+    PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
 
-    // Create the ghost object.
-    _ghostObject = bullet_new<btPairCachingGhostObject>();
+    // Create and set the collision shape for the ghost object.
+	_collisionShape = physicsController->createShape(node, shape, &centerOfMassOffset);
 
-    // Get the node's world scale (we need to apply this during creation since ghost objects don't scale dynamically).
-    Vector3 scale;
-    _node->getWorldMatrix().getScale(&scale);
+	// Create the ghost object.
+    _ghostObject = bullet_new<btPairCachingGhostObject>();
+	_ghostObject->setCollisionShape(_collisionShape->getShape<btCollisionShape>());
 
-    // Use the center of the bounding sphere as the center of mass offset.
-    Vector3 c(_node->getModel()->getMesh()->getBoundingSphere().center);
-    c.x *= scale.x;
-    c.y *= scale.y;
-    c.z *= scale.z;
-        
     // Initialize a physics motion state object for syncing the transform.
-    Vector3 centerOfMassOffset(-c);
-    _motionState = new PhysicsMotionState(_node, &centerOfMassOffset);
+	_motionState = new PhysicsMotionState(_node, &centerOfMassOffset);
     _motionState->getWorldTransform(_ghostObject->getWorldTransform());
 
-    // Create and set the collision shape for the ghost object.
-    PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
-    switch (type)
-    {
-        case PhysicsRigidBody::SHAPE_BOX:
-        {
-            const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
-            _shape = physicsController->createBox(box.min, box.max, scale);
-            break;
-        }
-        case PhysicsRigidBody::SHAPE_SPHERE:
-        {
-            const BoundingSphere& sphere = node->getModel()->getMesh()->getBoundingSphere();
-            _shape = physicsController->createSphere(sphere.radius, scale);
-            break;
-        }
-    }
-    _ghostObject->setCollisionShape(_shape);
-
     // Add the ghost object to the physics world.
     physicsController->addCollisionObject(this);
+
+	_node->addListener(this);
 }
 
 PhysicsGhostObject::~PhysicsGhostObject()
 {
+	_node->removeListener(this);
+
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
-    _shape = NULL;
     SAFE_DELETE(_ghostObject);
-    SAFE_DELETE(_motionState);
 }
 
 PhysicsCollisionObject::Type PhysicsGhostObject::getType() const
@@ -66,21 +43,11 @@ PhysicsCollisionObject::Type PhysicsGhostObject::getType() const
     return GHOST_OBJECT;
 }
 
-Node* PhysicsGhostObject::getNode() const
-{
-    return _node;
-}
-
 btCollisionObject* PhysicsGhostObject::getCollisionObject() const
 {
     return _ghostObject;
 }
 
-btCollisionShape* PhysicsGhostObject::getCollisionShape() const
-{
-    return _shape;
-}
-
 void PhysicsGhostObject::transformChanged(Transform* transform, long cookie)
 {
     // Update the motion state with the transform from the node.

+ 10 - 24
gameplay/src/PhysicsGhostObject.h

@@ -16,7 +16,6 @@ class PhysicsMotionState;
 class PhysicsGhostObject : public PhysicsCollisionObject, public Transform::Listener
 {
     friend class Node;
-    friend class PhysicsController;
 
 public:
 
@@ -25,10 +24,10 @@ public:
      */
     PhysicsCollisionObject::Type getType() const;
 
-    /**
-     * @see PhysicsCollisionObject#getNode
+	/**
+     * Used to synchronize the transform between GamePlay and Bullet.
      */
-    Node* getNode() const;
+    void transformChanged(Transform* transform, long cookie);
 
 protected:
 
@@ -37,37 +36,24 @@ protected:
      */
     btCollisionObject* getCollisionObject() const;
 
-    /**
-     * @see PhysicsCollisionObject::getCollisionShape
-     */
-    btCollisionShape* getCollisionShape() const;
-
-private:
+protected:
 
     /**
      * Constructor.
      * 
      * @param node The node to attach the ghost object to.
-     * @param type The type of ghost object (collision shape type).
+     * @param shape The collision shape definition for the ghost object.
      */
-    PhysicsGhostObject(Node* node, PhysicsRigidBody::ShapeType type);
-    
-    /**
-     * Destructor.
-     */
-    ~PhysicsGhostObject();
+	PhysicsGhostObject(Node* node, const PhysicsCollisionShape::Definition& shape);
 
     /**
-     * Used to synchronize the transform between GamePlay and Bullet.
+     * Destructor.
      */
-    void transformChanged(Transform* transform, long cookie);
+    virtual ~PhysicsGhostObject();
 
-    Node* _node;
-    PhysicsMotionState* _motionState;
-    btCollisionShape* _shape;
-    btGhostObject* _ghostObject;
+    btPairCachingGhostObject* _ghostObject;
 };
 
 }
 
-#endif
+#endif

+ 2 - 1
gameplay/src/PhysicsMotionState.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "PhysicsMotionState.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -22,7 +23,7 @@ PhysicsMotionState::~PhysicsMotionState()
 
 void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 {
-    if (_node->getRigidBody() && _node->getRigidBody()->isKinematic())
+	if (_node->getCollisionObject() && _node->getCollisionObject()->isKinematic())
         updateTransformFromNode();
 
     transform = _centerOfMassOffset.inverse() * _worldTransform;

+ 7 - 5
gameplay/src/PhysicsMotionState.h

@@ -1,12 +1,13 @@
 #ifndef PHYSICSMOTIONSTATE_H_
 #define PHYSICSMOTIONSTATE_H_
 
-#include "Node.h"
-#include "PhysicsRigidBody.h"
+#include "Vector3.h"
 
 namespace gameplay
 {
 
+class Node;
+
 /**
  * Interface between GamePlay and Bullet to keep object transforms synchronized properly.
  * 
@@ -14,10 +15,11 @@ namespace gameplay
  */
 class PhysicsMotionState : public btMotionState
 {
-    friend class PhysicsRigidBody;
-    friend class PhysicsCharacter;
+	friend class PhysicsCollisionObject;
+	friend class PhysicsRigidBody;
+	friend class PhysicsGhostObject;
+	friend class PhysicsCharacter;
     friend class PhysicsConstraint;
-    friend class PhysicsGhostObject;
 
 protected:
 

+ 236 - 376
gameplay/src/PhysicsRigidBody.cpp

@@ -1,217 +1,81 @@
 #include "Base.h"
+#include "PhysicsRigidBody.h"
+#include "PhysicsMotionState.h"
+#include "PhysicsController.h"
 #include "Game.h"
 #include "Image.h"
-#include "PhysicsController.h"
-#include "PhysicsMotionState.h"
-#include "PhysicsRigidBody.h"
+#include "MeshPart.h"
+#include "Node.h"
 
 namespace gameplay
 {
 
-// Internal values used for creating mesh, heightfield, and capsule rigid bodies.
-#define SHAPE_MESH ((PhysicsRigidBody::ShapeType)(PhysicsRigidBody::SHAPE_NONE + 1))
-#define SHAPE_HEIGHTFIELD ((PhysicsRigidBody::ShapeType)(PhysicsRigidBody::SHAPE_NONE + 2))
-#define SHAPE_CAPSULE ((PhysicsRigidBody::ShapeType)(PhysicsRigidBody::SHAPE_NONE + 3))
-
-// Helper function for calculating heights from heightmap (image) or heightfield data.
-static float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y);
-
-PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::ShapeType type, float mass, 
-    float friction, float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
-        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
-        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
+PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters)
+        : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL)
 {
-    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
-    Vector3 scale;
-    node->getWorldMatrix().getScale(&scale);
+	// Create our collision sh ape
+	Vector3 centerOfMassOffset;
+	_collisionShape = Game::getInstance()->getPhysicsController()->createShape(node, shape, &centerOfMassOffset);
 
-    switch (type)
-    {
-        case SHAPE_BOX:
-        {
-            const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
-            _shape = Game::getInstance()->getPhysicsController()->createBox(box.min, box.max, scale);
-            break;
-        }
-        case SHAPE_SPHERE:
-        {
-            const BoundingSphere& sphere = node->getModel()->getMesh()->getBoundingSphere();
-            _shape = Game::getInstance()->getPhysicsController()->createSphere(sphere.radius, scale);
-            break;
-        }
-        case SHAPE_MESH:
-        {
-            _shape = Game::getInstance()->getPhysicsController()->createMesh(this, scale);
-            break;
-        }
-    }
-
-    // Use the center of the bounding sphere as the center of mass offset.
-    Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
-    c.x *= scale.x;
-    c.y *= scale.y;
-    c.z *= scale.z;
-    c.negate();
-
-    // Create the Bullet rigid body (we don't apply center of mass offsets on mesh rigid bodies).
-    if (c.lengthSquared() > MATH_EPSILON && type != SHAPE_MESH)
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-    else
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-
-    // Add the rigid body to the physics world.
-    Game::getInstance()->getPhysicsController()->addCollisionObject(this);
-}
-
-PhysicsRigidBody::PhysicsRigidBody(Node* node, Image* image, float mass,
-    float friction, float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
-        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
-        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
-{
-    // Get the width, length and minimum and maximum height of the heightfield.
-    const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
-    float width = box.max.x - box.min.x;
-    float minHeight = box.min.y;
-    float maxHeight = box.max.y;
-    float length = box.max.z - box.min.z;
-
-    // Get the size in bytes of a pixel (we ensure that the image's
-    // pixel format is actually supported before calling this constructor).
-    unsigned int pixelSize = 0;
-    switch (image->getFormat())
-    {
-        case Image::RGB:
-            pixelSize = 3;
-            break;
-        case Image::RGBA:
-            pixelSize = 4;
-            break;
-    }
-
-    // Calculate the heights for each pixel.
-    float* data = new float[image->getWidth() * image->getHeight()];
-    for (unsigned int x = 0; x < image->getWidth(); x++)
-    {
-        for (unsigned int y = 0; y < image->getHeight(); y++)
-        {
-            data[x + y * image->getWidth()] = ((((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 0]) +
-                ((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 1]) +
-                ((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 2])) / 768.0f) * (maxHeight - minHeight) + minHeight;
-        }
-    }
+	// Create motion state object
+	_motionState = new PhysicsMotionState(node, (centerOfMassOffset.lengthSquared() > MATH_EPSILON) ? &centerOfMassOffset : NULL);
 
-    // Generate the heightmap data needed for physics (one height per world unit).
-    unsigned int sizeWidth = width;
-    unsigned int sizeHeight = length;
-    _width = sizeWidth + 1;
-    _height = sizeHeight + 1;
-    _heightfieldData = new float[_width * _height];
-    unsigned int heightIndex = 0;
-    float widthImageFactor = (float)(image->getWidth() - 1) / sizeWidth;
-    float heightImageFactor = (float)(image->getHeight() - 1) / sizeHeight;
-    float x = 0.0f;
-    float z = 0.0f;
-    for (unsigned int row = 0, z = 0.0f; row <= sizeHeight; row++, z += 1.0f)
-    {
-        for (unsigned int col = 0, x = 0.0f; col <= sizeWidth; col++, x += 1.0f)
-        {
-            heightIndex = row * _width + col;
-            _heightfieldData[heightIndex] = calculateHeight(data, image->getWidth(), image->getHeight(), x * widthImageFactor, (sizeHeight - z) * heightImageFactor);
-        }
-    }
-    SAFE_DELETE_ARRAY(data);
+    // If the mass is non-zero, then the object is dynamic so we calculate the local 
+    // inertia. However, if the collision shape is a triangle mesh, we don't calculate 
+    // inertia since Bullet doesn't currently support this.
+    btVector3 localInertia(0.0, 0.0, 0.0);
+	if (parameters.mass != 0.0 && _collisionShape->getType() != PhysicsCollisionShape::SHAPE_MESH)
+		_collisionShape->getShape<btCollisionShape>()->calculateLocalInertia(parameters.mass, localInertia);
 
-    // Create the heightfield collision shape.
-    _shape = Game::getInstance()->getPhysicsController()->createHeightfield(_width, _height, _heightfieldData, minHeight, maxHeight);
+    // Create the Bullet physics rigid body object.
+    btRigidBody::btRigidBodyConstructionInfo rbInfo(parameters.mass, _motionState, _collisionShape->getShape<btCollisionShape>(), localInertia);
+    rbInfo.m_friction = parameters.friction;
+    rbInfo.m_restitution = parameters.restitution;
+    rbInfo.m_linearDamping = parameters.linearDamping;
+    rbInfo.m_angularDamping = parameters.angularDamping;
 
-    // Offset the heightmap's center of mass according to the way that Bullet calculates the origin 
-    // of its heightfield collision shape; see documentation for the btHeightfieldTerrainShape for more info.
-    Vector3 s;
-    node->getWorldMatrix().getScale(&s);
-    Vector3 c (0.0f, -(maxHeight - (0.5f * (maxHeight - minHeight))) / s.y, 0.0f);
+	// Create + assign the new bullet rigid body object.
+	_body = bullet_new<btRigidBody>(rbInfo);
 
-    // Create the Bullet rigid body.
-    if (c.lengthSquared() > MATH_EPSILON)
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-    else
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
+	// Set other initially defined properties.
+    setKinematic(parameters.kinematic);
+	setAnisotropicFriction(parameters.anisotropicFriction);
+	setGravity(parameters.gravity);
 
-    // Add the rigid body to the physics world.
+    // Add ourself to the physics world.
     Game::getInstance()->getPhysicsController()->addCollisionObject(this);
 
-    // Add the rigid body as a listener on the node's transform.
-    _node->addListener(this);
-}
-
-PhysicsRigidBody::PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction,
-    float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
-        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
-        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
-{
-    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
-    Vector3 scale;
-    node->getWorldMatrix().getScale(&scale);
-
-    // Create the capsule collision shape.
-    _shape = Game::getInstance()->getPhysicsController()->createCapsule(radius, height);
-
-    // Use the center of the bounding sphere as the center of mass offset.
-    Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
-    c.x *= scale.x;
-    c.y *= scale.y;
-    c.z *= scale.z;
-    c.negate();
-
-    // Create the Bullet rigid body.
-    if (c.lengthSquared() > MATH_EPSILON)
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-    else
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-
-    // Add the rigid body to the physics world.
-    Game::getInstance()->getPhysicsController()->addCollisionObject(this);
+	if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+	{
+		// Add a listener on the node's transform so we can track dirty changes to calculate
+		// an inverse matrix for transforming heightfield points between world and local space.
+		_node->addListener(this);
+	}
 }
 
 PhysicsRigidBody::~PhysicsRigidBody()
 {
-    // Clean up all constraints linked to this rigid body.
-    PhysicsConstraint* ptr = NULL;
-    while (_constraints.size() > 0)
-    {
-        ptr = _constraints.back();
-        _constraints.pop_back();
-        SAFE_DELETE(ptr);
-    }
-
+	// Clean up all constraints linked to this rigid body.
+	if (_constraints)
+	{
+		for (unsigned int i = 0; i < _constraints->size(); ++i)
+		{
+			SAFE_DELETE((*_constraints)[i]);
+		}
+		SAFE_DELETE(_constraints);
+	}
+
+	// Remove collision object from physics controller
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
     // Clean up the rigid body and its related objects.
-    if (_body)
-    {
-        if (_body->getMotionState())
-            delete _body->getMotionState();
-        SAFE_DELETE(_body);
-    }
+	SAFE_DELETE(_body);
 
-    SAFE_DELETE(_angularVelocity);
-    SAFE_DELETE(_anisotropicFriction);
-    SAFE_DELETE(_gravity);
-    SAFE_DELETE(_linearVelocity);
-    SAFE_DELETE_ARRAY(_vertexData);
-    for (unsigned int i = 0; i < _indexData.size(); i++)
-    {
-        SAFE_DELETE_ARRAY(_indexData[i]);
-    }
-    SAFE_DELETE_ARRAY(_heightfieldData);
-    SAFE_DELETE(_inverse);
-}
-
-Node* PhysicsRigidBody::getNode() const
-{
-    return _node;
+	// Unregister node listener (only registered for heihgtfield collision shape types)
+	if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+	{
+		_node->removeListener(this);
+	}
 }
 
 PhysicsCollisionObject::Type PhysicsRigidBody::getType() const
@@ -224,11 +88,6 @@ btCollisionObject* PhysicsRigidBody::getCollisionObject() const
     return _body;
 }
 
-btCollisionShape* PhysicsRigidBody::getCollisionShape() const
-{
-    return _shape;
-}
-
 void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
 {
     // If the force is significant enough, activate the rigid body 
@@ -312,18 +171,13 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     }
 
     // Set values to their defaults.
-    PhysicsRigidBody::ShapeType type = PhysicsRigidBody::SHAPE_NONE;
-    float mass = 0.0;
-    float friction = 0.5;
-    float restitution = 0.0;
-    float linearDamping = 0.0;
-    float angularDamping = 0.0;
-    bool kinematic = false;
-    Vector3* gravity = NULL;
-    Vector3* anisotropicFriction = NULL;
+	bool typeSpecified = false;
+	PhysicsCollisionShape::Type type;
+	Parameters parameters;
     const char* imagePath = NULL;
-    float radius = -1.0f;
-    float height = -1.0f;
+    float radius, height;
+	Vector3 center, min, max;
+	int bits = 0;
 
     // Load the defined properties.
     properties->rewind();
@@ -334,54 +188,53 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         {
             std::string typeStr = properties->getString();
             if (typeStr == "BOX")
-                type = SHAPE_BOX;
+                type = PhysicsCollisionShape::SHAPE_BOX;
             else if (typeStr == "SPHERE")
-                type = SHAPE_SPHERE;
+                type = PhysicsCollisionShape::SHAPE_SPHERE;
+			else if (typeStr == "CAPSULE")
+                type = PhysicsCollisionShape::SHAPE_CAPSULE;
+			else if (typeStr == "HEIGHTFIELD")
+                type = PhysicsCollisionShape::SHAPE_HEIGHTFIELD;
             else if (typeStr == "MESH")
-                type = SHAPE_MESH;
-            else if (typeStr == "HEIGHTFIELD")
-                type = SHAPE_HEIGHTFIELD;
-            else if (typeStr == "CAPSULE")
-                type = SHAPE_CAPSULE;
+                type = PhysicsCollisionShape::SHAPE_MESH;
             else
             {
                 WARN_VARG("Could not create rigid body; unsupported value for rigid body type: '%s'.", typeStr.c_str());
                 return NULL;
             }
+			typeSpecified = true;
         }
         else if (strcmp(name, "mass") == 0)
         {
-            mass = properties->getFloat();
+            parameters.mass = properties->getFloat();
         }
         else if (strcmp(name, "friction") == 0)
         {
-            friction = properties->getFloat();
+            parameters.friction = properties->getFloat();
         }
         else if (strcmp(name, "restitution") == 0)
         {
-            restitution = properties->getFloat();
+            parameters.restitution = properties->getFloat();
         }
         else if (strcmp(name, "linearDamping") == 0)
         {
-            linearDamping = properties->getFloat();
+            parameters.linearDamping = properties->getFloat();
         }
         else if (strcmp(name, "angularDamping") == 0)
         {
-            angularDamping = properties->getFloat();
+            parameters.angularDamping = properties->getFloat();
         }
         else if (strcmp(name, "kinematic") == 0)
         {
-            kinematic = properties->getBool();
+            parameters.kinematic = properties->getBool();
         }
-        else if (strcmp(name, "gravity") == 0)
+        else if (strcmp(name, "anisotropicFriction") == 0)
         {
-            gravity = new Vector3();
-            properties->getVector3(NULL, gravity);
+            properties->getVector3(NULL, &parameters.anisotropicFriction);
         }
-        else if (strcmp(name, "anisotropicFriction") == 0)
+        else if (strcmp(name, "gravity") == 0)
         {
-            anisotropicFriction = new Vector3();
-            properties->getVector3(NULL, anisotropicFriction);
+            properties->getVector3(NULL, &parameters.gravity);
         }
         else if (strcmp(name, "image") == 0)
         {
@@ -389,208 +242,215 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         }
         else if (strcmp(name, "radius") == 0)
         {
-            radius = properties->getFloat();
+			radius = properties->getFloat();
+			bits |= 1;
         }
         else if (strcmp(name, "height") == 0)
         {
             height = properties->getFloat();
+			bits |= 2;
         }
+		else if (strcmp(name, "center") == 0)
+		{
+			properties->getVector3(NULL, &center);
+			bits |= 4;
+		}
+		else if (strcmp(name, "min") == 0)
+		{
+			properties->getVector3(NULL, &min);
+			bits |= 8;
+		}
+		else if (strcmp(name, "max") == 0)
+		{
+			properties->getVector3(NULL, &max);
+			bits |= 16;
+		}
     }
 
-    // If the rigid body type is equal to mesh, check that the node's mesh's primitive type is supported.
-    if (type == SHAPE_MESH)
-    {
-        Mesh* mesh = node->getModel()->getMesh();
-
-        switch (mesh->getPrimitiveType())
-        {
-        case Mesh::TRIANGLES:
-            break;
-        case Mesh::LINES:
-        case Mesh::LINE_STRIP:
-        case Mesh::POINTS:
-        case Mesh::TRIANGLE_STRIP:
-            WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
-
-            SAFE_DELETE(gravity);
-            SAFE_DELETE(anisotropicFriction);
-            return NULL;
-        }
-    }
+	if (!typeSpecified)
+	{
+		WARN("Missing 'type' specifier for rigid body definition.");
+		return NULL;
+	}
+
+	PhysicsRigidBody* body = NULL;
+
+	switch (type)
+	{
+	case PhysicsCollisionShape::SHAPE_BOX:
+		if ((bits & 8/*min*/) || (bits & 16/*max*/))
+		{
+			// Explicitly defined box shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::box(min, max), parameters);
+		}
+		else
+		{
+			// Auto box shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::box(), parameters);
+		}
+		break;
+
+	case PhysicsCollisionShape::SHAPE_SPHERE:
+		if ((bits & 4/*center*/) || (bits & 1/*radius*/))
+		{
+			// Explicitly defined sphere shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::sphere(radius, center), parameters);
+		}
+		else
+		{
+			// Auto sphere shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::sphere(), parameters);
+		}
+		break;
+
+    case PhysicsCollisionShape::SHAPE_CAPSULE:
+		if ((bits & 1/*radius*/) || (bits & 2/*height*/))
+		{
+			// Explicitly defined capsule shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::capsule(radius, height, center), parameters);
+		}
+		else
+		{
+			// Auto capsule shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::capsule(), parameters);
+		}
+        break;
+
+    case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+		{
+			if (imagePath == NULL)
+			{
+				WARN("Heightfield rigid body requires an image path.");
+				return NULL;
+			}
+
+            // Load the image data from the given file path.
+            Image* image = Image::create(imagePath);
+            if (!image)
+			{
+				WARN_VARG("Failed to load heightmap image: %s", imagePath);
+                return NULL;
+			}
 
-    // Create the rigid body.
-    PhysicsRigidBody* body = NULL;
-    switch (type)
-    {
-        case SHAPE_HEIGHTFIELD:
-            if (imagePath == NULL)
-            {
-                WARN("Heightfield rigid body requires an image path.");
-            }
-            else
+            // Ensure that the image's pixel format is supported.
+            switch (image->getFormat())
             {
-                // Load the image data from the given file path.
-                Image* image = Image::create(imagePath);
-                if (!image)
+                case Image::RGB:
+                case Image::RGBA:
+                    break;
+                default:
+                    WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
                     return NULL;
-
-                // Ensure that the image's pixel format is supported.
-                switch (image->getFormat())
-                {
-                    case Image::RGB:
-                    case Image::RGBA:
-                        break;
-                    default:
-                        WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
-                        return NULL;
-                }
-
-                body = new PhysicsRigidBody(node, image, mass, friction, restitution, linearDamping, angularDamping);
-                SAFE_RELEASE(image);
-            }
-            break;
-        case SHAPE_CAPSULE:
-            if (radius == -1.0f || height == -1.0f)
-            {
-                WARN("Both 'radius' and 'height' must be specified for a capsule rigid body.");
             }
-            else
-            {
-                body = new PhysicsRigidBody(node, radius, height, mass, friction, restitution, linearDamping, angularDamping);
-            }
-            break;
-        default:
-            body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
-            break;
-    }
 
-    // Set any initially defined properties.
-    if (kinematic)
-        body->setKinematic(kinematic);
-    if (gravity)
-        body->setGravity(*gravity);
-    if (anisotropicFriction)
-        body->setAnisotropicFriction(*anisotropicFriction);
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::heightfield(image), parameters);
 
-    // Clean up any loaded properties that are on the heap.
-    SAFE_DELETE(gravity);
-    SAFE_DELETE(anisotropicFriction);
+			SAFE_RELEASE(image);
+		}
+        break;
+
+	case PhysicsCollisionShape::SHAPE_MESH:
+		{
+			// Mesh is required on node
+			Mesh* mesh = node->getModel() ? node->getModel()->getMesh() : NULL;
+			if (mesh == NULL)
+			{
+				WARN("Cannot create mesh rigid body for node without mode/mesh.");
+				return NULL;
+			}
+
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::mesh(mesh), parameters);
+		}
+		break;
+
+	default:
+		break;
+	}
 
     return body;
 }
 
+void PhysicsRigidBody::setKinematic(bool kinematic)
+{
+    if (kinematic)
+    {
+        _body->setCollisionFlags(_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
+        _body->setActivationState(DISABLE_DEACTIVATION);
+    }
+    else
+    {
+        _body->setCollisionFlags(_body->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
+        _body->setActivationState(ACTIVE_TAG);
+    }
+}
+
 float PhysicsRigidBody::getHeight(float x, float y) const
 {
     // This function is only supported for heightfield rigid bodies.
-    if (_shape->getShapeType() != TERRAIN_SHAPE_PROXYTYPE)
+	if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
         WARN("Attempting to get the height of a non-heightfield rigid body.");
         return 0.0f;
     }
 
     // Calculate the correct x, y position relative to the heightfield data.
-    if (_inverseIsDirty)
+	if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
     {
-        if (_inverse == NULL)
-            _inverse = new Matrix();
-
-        _node->getWorldMatrix().invert(_inverse);
-        _inverseIsDirty = false;
+		_node->getWorldMatrix().invert(&_collisionShape->_shapeData.heightfieldData->inverse);
+		_collisionShape->_shapeData.heightfieldData->inverseIsDirty = false;
     }
 
-    Vector3 v = (*_inverse) * Vector3(x, 0.0f, y);
-    x = (v.x + (0.5f * (_width - 1))) * _width / (_width - 1);
-    y = (v.z + (0.5f * (_height - 1))) * _height / (_height - 1);
+	float w = _collisionShape->_shapeData.heightfieldData->width;
+	float h = _collisionShape->_shapeData.heightfieldData->height;
+
+    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);
 
     // Check that the x, y position is within the bounds.
-    if (x < 0.0f || x > _width || y < 0.0f || y > _height)
+    if (x < 0.0f || x > w || y < 0.0f || y > h)
     {
-        WARN_VARG("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, _width, _height);
+        WARN_VARG("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 calculateHeight(_heightfieldData, _width, _height, x, y);
-}
-
-btRigidBody* PhysicsRigidBody::createRigidBodyInternal(btCollisionShape* shape, float mass, Node* node,
-                                                       float friction, float restitution, float linearDamping, float angularDamping, 
-                                                       const Vector3* centerOfMassOffset)
-{
-    // If the mass is non-zero, then the object is dynamic so we calculate the local 
-    // inertia. However, if the collision shape is a triangle mesh, we don't calculate 
-    // inertia since Bullet doesn't currently support this.
-    btVector3 localInertia(0.0, 0.0, 0.0);
-    if (mass != 0.0 && shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE)
-        shape->calculateLocalInertia(mass, localInertia);
-
-    // Create the Bullet physics rigid body object.
-    PhysicsMotionState* motionState = new PhysicsMotionState(node, centerOfMassOffset);
-    btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, motionState, shape, localInertia);
-    rbInfo.m_friction = friction;
-    rbInfo.m_restitution = restitution;
-    rbInfo.m_linearDamping = linearDamping;
-    rbInfo.m_angularDamping = angularDamping;
-    btRigidBody* body = bullet_new<btRigidBody>(rbInfo);
-
-    return body;
+	return PhysicsController::calculateHeight(_collisionShape->_shapeData.heightfieldData->heightData, w, h, x, y);
 }
 
 void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 {
-    _constraints.push_back(constraint);
+	if (_constraints == NULL)
+		_constraints = new std::vector<PhysicsConstraint*>();
+
+    _constraints->push_back(constraint);
 }
 
 void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
 {
-    for (unsigned int i = 0; i < _constraints.size(); i++)
-    {
-        if (_constraints[i] == constraint)
-        {
-            _constraints.erase(_constraints.begin() + i);
-            break;
-        }
-    }
+	if (_constraints)
+	{
+		for (unsigned int i = 0; i < _constraints->size(); ++i)
+		{
+			if ((*_constraints)[i] == constraint)
+			{
+				_constraints->erase(_constraints->begin() + i);
+				break;
+			}
+		}
+	}
 }
 
 bool PhysicsRigidBody::supportsConstraints()
 {
-    return _shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE && _shape->getShapeType() != TERRAIN_SHAPE_PROXYTYPE;
+	return (getShapeType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD && getShapeType() != PhysicsCollisionShape::SHAPE_MESH);
 }
 
 void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
 {
-    _inverseIsDirty = true;
-}
-
-float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
-{
-    unsigned int x1 = x;
-    unsigned int y1 = y;
-    unsigned int x2 = x1 + 1;
-    unsigned int y2 = y1 + 1;
-    float tmp;
-    float xFactor = modf(x, &tmp);
-    float yFactor = modf(y, &tmp);
-    float xFactorI = 1.0f - xFactor;
-    float yFactorI = 1.0f - yFactor;
-
-    if (x2 >= width && y2 >= height)
-    {
-        return data[x1 + y1 * width];
-    }
-    else if (x2 >= width)
-    {
-        return data[x1 + y1 * width] * yFactorI + data[x1 + y2 * width] * yFactor;
-    }
-    else if (y2 >= height)
-    {
-        return data[x1 + y1 * width] * xFactorI + data[x2 + y1 * width] * xFactor;
-    }
-    else
-    {
-        return data[x1 + y1 * width] * xFactorI * yFactorI + data[x1 + y2 * width] * xFactorI * yFactor + 
-            data[x2 + y2 * width] * xFactor * yFactor + data[x2 + y1 * width] * xFactor * yFactorI;
-    }
+	if (getShapeType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+	{
+		_collisionShape->_shapeData.heightfieldData->inverseIsDirty = true;
+	}
 }
 
 }

+ 153 - 183
gameplay/src/PhysicsRigidBody.h

@@ -30,204 +30,235 @@ class PhysicsRigidBody : public PhysicsCollisionObject, public Transform::Listen
 
 public:
 
-    /**
-     * Represents the different types of rigid bodies.
-     */
-    enum ShapeType
-    {
-        SHAPE_BOX,
-        SHAPE_SPHERE,
-        SHAPE_NONE,
-        SHAPE_MAX = 10
-    };
+	/**
+	 * Rigid body construction parameters.
+	 */
+	struct Parameters
+	{
+		/**
+		 * The mass of the rigid body, in kilograms.
+		 */
+		float mass;
+
+		/**
+		 * The friction of the rigid body (non-zero values give best simulation results).
+		 */
+		float friction;
+
+		/**
+		 * The restitution of the rigid body (this controls the bounciness of
+		 * the rigid body; use zero for best simulation results).
+		 */
+        float restitution;
+
+		/**
+		 * The percentage of linear velocity lost per second (between 0.0 and 1.0).
+		 */
+		float linearDamping;
+
+		/**
+		 * The percentage of angular velocity lost per second (between 0.0 and 1.0).
+		 */
+		float angularDamping;
+
+		/**
+		 * Whether the rigid body is kinematic.
+		 */
+		bool kinematic;
+
+		/**
+		 * The ansitropic friction term for the rigid body.
+		 */
+		Vector3 anisotropicFriction;
+
+		/**
+		 * The gravity acceleration factor for the rigid body.
+		 */
+		Vector3 gravity;
+
+		/**
+		 * Constructor.
+		 */
+		Parameters(float mass = 0.0f, float friction = 0.5f, float resititution = 0.0f,
+			float linearDamping = 0.0f, float angularDamping = 0.0f, bool kinematic = false,
+			const Vector3& anisotropicFriction = Vector3::one(), const Vector3& gravity = Vector3::zero())
+			: mass(mass), friction(friction), restitution(restitution), linearDamping(linearDamping), angularDamping(angularDamping),
+			  kinematic(kinematic), anisotropicFriction(anisotropicFriction), gravity(gravity)
+		{
+		}
+	};
 
     /**
      * @see PhysicsCollisionObject#getType
      */
     PhysicsCollisionObject::Type getType() const;
 
-    /**
-     * Applies the given force to the rigid body (optionally, from the given relative position).
-     * 
-     * @param force The force to be applied.
-     * @param relativePosition The relative position from which to apply the force.
-     */
-    void applyForce(const Vector3& force, const Vector3* relativePosition = NULL);
+	/**
+	 * Gets the rigid body's mass.
+	 *
+	 * @return The mass.
+	 */
+	inline float getMass() const;
 
     /**
-     * Applies the given force impulse to the rigid body (optionally, from the given relative position).
+     * Gets the rigid body's friction.
      * 
-     * @param impulse The force impulse to be applied.
-     * @param relativePosition The relative position from which to apply the force.
+     * @return The friction.
      */
-    void applyImpulse(const Vector3& impulse, const Vector3* relativePosition = NULL);
+    inline float getFriction() const;
 
     /**
-     * Applies the given torque to the rigid body.
+     * Sets the rigid body's friction.
      * 
-     * @param torque The torque to be applied.
+     * @param friction The friction.
      */
-    void applyTorque(const Vector3& torque);
+    inline void setFriction(float friction);
 
-    /**
-     * Applies the given torque impulse to the rigid body.
-     * 
-     * @param torque The torque impulse to be applied.
-     */
-    void applyTorqueImpulse(const Vector3& torque);
+	/**
+	 * Gets the rigid body's restitution.
+	 *
+	 * @return The restitution.
+	 */
+	inline float getRestitution() const;
 
     /**
-     * Gets the rigid body's angular damping.
+     * Sets the rigid body's restitution (or bounciness).
      * 
-     * @return The angular damping.
+     * @param restitution The restitution.
      */
-    inline float getAngularDamping() const;
+    inline void setRestitution(float restitution);
 
-    /**
-     * Gets the rigid body's angular velocity.
-     * 
-     * @return The angular velocity.
-     */
-    inline const Vector3& getAngularVelocity() const;
+	/**
+	 * Gets the rigid body's linear damping.
+	 *
+	 * @return The linear damping.
+	 */
+	inline float getLinearDamping() const;
 
-    /**
-     * Gets the rigid body's anisotropic friction.
-     * 
-     * @return The anisotropic friction.
-     */
-    inline const Vector3& getAnisotropicFriction() const;
+	/**
+	 * Gets the rigid body's angular damping.
+	 *
+	 * @return The angular damping.
+	 */
+	inline float getAngularDamping() const;
 
     /**
-     * Gets the rigid body's friction.
+     * Sets the rigid body's linear and angular damping.
      * 
-     * @return The friction.
+     * @param linearDamping The linear damping; between 0.0 (minimum) and 1.0 (maximum).
+     * @param angularDamping The angular damping; between 0.0 (minimum) and 1.0 (maximum).
      */
-    inline float getFriction() const;
+    inline void setDamping(float linearDamping, float angularDamping);
 
-    /**
-     * Gets the gravity that affects the rigid body (this can
-     * be different from the global gravity; @see #setGravity).
+	/**
+     * Gets the rigid body's linear velocity.
      * 
-     * @return The gravity.
+     * @return The linear velocity.
      */
-    inline const Vector3& getGravity() const;
+    inline Vector3 getLinearVelocity() const;
 
     /**
-     * Gets the height at the given point (only for rigid bodies of type HEIGHTFIELD).
+     * Sets the rigid body's linear velocity.
      * 
-     * @param x The x position.
-     * @param y The y position.
-     * @return The height at the given point.
+     * @param velocity The linear velocity.
      */
-    float getHeight(float x, float y) const;
+    inline void setLinearVelocity(const Vector3& velocity);
 
     /**
-     * Gets the rigid body's linear damping.
+     * Gets the rigid body's angular velocity.
      * 
-     * @return The linear damping.
+     * @return The angular velocity.
      */
-    inline float getLinearDamping() const;
+    inline Vector3 getAngularVelocity() const;
 
     /**
-     * Gets the rigid body's linear velocity.
+     * Sets the rigid body's angular velocity.
      * 
-     * @return The linear velocity.
+     * @param velocity The angular velocity.
      */
-    inline const Vector3& getLinearVelocity() const;
+    inline void setAngularVelocity(const Vector3& velocity);
 
     /**
-     * Gets the node that the rigid body is attached to.
+     * Gets the rigid body's anisotropic friction.
      * 
-     * @return The node.
-     *
-     * @see PhysicsCollisionObject::getNode.
+     * @return The anisotropic friction.
      */
-    Node* getNode() const;
+    inline Vector3 getAnisotropicFriction() const;
 
     /**
-     * Gets the rigid body's restitution.
+     * Sets the rigid body's anisotropic friction.
      * 
-     * @return The restitution.
+     * @param friction The anisotropic friction.
      */
-    inline float getRestitution() const;
+    inline void setAnisotropicFriction(const Vector3& friction);
 
     /**
-     * Gets whether the rigid body is a kinematic rigid body or not.
+     * Gets the gravity that affects the rigid body (this can
+     * be different from the global gravity; @see #setGravity).
      * 
-     * @return Whether the rigid body is kinematic or not.
-     */
-    inline bool isKinematic() const;
-
-    /**
-     * Gets whether the rigid body is a static rigid body or not.
-     *
-     * @return Whether the rigid body is static.
-     */
-    inline bool isStatic() const;
-
-    /**
-     * Gets whether the rigid body is a dynamic rigid body or not.
-     *
-     * @return Whether the rigid body is dynamic.
+     * @return The gravity.
      */
-    inline bool isDynamic() const;
+    inline Vector3 getGravity() const;
 
     /**
-     * Sets the rigid body's angular velocity.
+     * Sets the rigid body's gravity (this overrides the global gravity for this rigid body).
      * 
-     * @param velocity The angular velocity.
+     * @param gravity The gravity.
      */
-    inline void setAngularVelocity(const Vector3& velocity);
+    inline void setGravity(const Vector3& gravity);
 
     /**
-     * Sets the rigid body's anisotropic friction.
+     * Sets whether the rigid body is a kinematic rigid body or not.
      * 
-     * @param friction The anisotropic friction.
+     * @param kinematic Whether the rigid body is kinematic or not.
      */
-    inline void setAnisotropicFriction(const Vector3& friction);
+    void setKinematic(bool kinematic);
 
     /**
-     * Sets the rigid body's linear and angular damping.
+     * Gets the height at the given point (only for rigid bodies of type HEIGHTFIELD).
      * 
-     * @param linearDamping The linear damping; between 0.0 (minimum) and 1.0 (maximum).
-     * @param angularDamping The angular damping; between 0.0 (minimum) and 1.0 (maximum).
+     * @param x The x position.
+     * @param y The y position.
+     * @return The height at the given point, or zero if this is not a heightfield rigid body.
      */
-    inline void setDamping(float linearDamping, float angularDamping);
+    float getHeight(float x, float y) const;
 
     /**
-     * Sets the rigid body's friction.
-     * 
-     * @param friction The friction.
+     * Gets whether the rigid body is a static rigid body or not.
+     *
+     * @return Whether the rigid body is static.
      */
-    inline void setFriction(float friction);
+    bool isStatic() const;
 
     /**
-     * Sets the rigid body's gravity (this overrides the global gravity for this rigid body).
+     * Applies the given force to the rigid body (optionally, from the given relative position).
      * 
-     * @param gravity The gravity.
+     * @param force The force to be applied.
+     * @param relativePosition The relative position from which to apply the force.
      */
-    inline void setGravity(const Vector3& gravity);
+    void applyForce(const Vector3& force, const Vector3* relativePosition = NULL);
 
     /**
-     * Sets whether the rigid body is a kinematic rigid body or not.
+     * Applies the given force impulse to the rigid body (optionally, from the given relative position).
      * 
-     * @param kinematic Whether the rigid body is kinematic or not.
+     * @param impulse The force impulse to be applied.
+     * @param relativePosition The relative position from which to apply the force.
      */
-    inline void setKinematic(bool kinematic);
+    void applyImpulse(const Vector3& impulse, const Vector3* relativePosition = NULL);
 
     /**
-     * Sets the rigid body's linear velocity.
+     * Applies the given torque to the rigid body.
      * 
-     * @param velocity The linear velocity.
+     * @param torque The torque to be applied.
      */
-    inline void setLinearVelocity(const Vector3& velocity);
+    void applyTorque(const Vector3& torque);
 
     /**
-     * Sets the rigid body's restitution (or bounciness).
+     * Applies the given torque impulse to the rigid body.
      * 
-     * @param restitution The restitution.
+     * @param torque The torque impulse to be applied.
      */
-    inline void setRestitution(float restitution);
+    void applyTorqueImpulse(const Vector3& torque);
 
 protected:
 
@@ -236,11 +267,6 @@ protected:
      */
     btCollisionObject* getCollisionObject() const;
 
-    /**
-     * @see PhysicsCollisionObject::getCollisionShape
-     */
-    btCollisionShape* getCollisionShape() const;
-
 private:
 
     /**
@@ -248,49 +274,10 @@ private:
      * 
      * @param node The node to create a rigid body for; note that the node must have
      *      a model attached to it prior to creating a rigid body for it.
-     * @param type The type of rigid body to set.
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (non-zero values give best simulation results).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; use zero for best simulation results).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
+     * @param shape The rigid body shape construction information.
+     * @param parameters The rigid body construction parameters.
      */
-    PhysicsRigidBody(Node* node, PhysicsRigidBody::ShapeType type, float mass, float friction = 0.5,
-        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
-
-    /**
-     * Creates a heightfield rigid body.
-     * 
-     * @param node The node to create the heightfield rigid body for; note that the node must have
-     *      a model attached to it prior to creating a rigid body for it.
-     * @param image The heightfield image.
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (non-zero values give best simulation results).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; use zero for best simulation results).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
-     */
-    PhysicsRigidBody(Node* node, Image* image, float mass, float friction = 0.5,
-        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
-
-    /**
-     * Creates a capsule rigid body.
-     * 
-     * @param node The node to create the heightfield rigid body for; note that the node must have
-     *      a model attached to it prior to creating a rigid body for it.
-     * @param radius The radius of the capsule.
-     * @param height The height of the cylindrical part of the capsule (not including the ends).
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (non-zero values give best simulation results).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; use zero for best simulation results).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
-     */
-    PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction = 0.5,
-        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
+	PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters);
 
     /**
      * Destructor.
@@ -322,39 +309,22 @@ private:
      */
     static PhysicsRigidBody* create(Node* node, Properties* properties);
 
-    // Creates the underlying Bullet Physics rigid body object
-    // for a PhysicsRigidBody object using the given parameters.
-    static btRigidBody* createRigidBodyInternal(btCollisionShape* shape, float mass, Node* node,
-                                                float friction, float restitution, float linearDamping, float angularDamping,
-                                                const Vector3* centerOfMassOffset = NULL);
-
     // Adds a constraint to this rigid body.
     void addConstraint(PhysicsConstraint* constraint);
 
     // Removes a constraint from this rigid body (used by the constraint destructor).
     void removeConstraint(PhysicsConstraint* constraint);
-    
+
     // Whether or not the rigid body supports constraints fully.
     bool supportsConstraints();
 
     // Used for implementing getHeight() when the heightfield has a transform that can change.
     void transformChanged(Transform* transform, long cookie);
 
-    btCollisionShape* _shape;
     btRigidBody* _body;
-    Node* _node;
-    std::vector<PhysicsConstraint*> _constraints;
-    mutable Vector3* _angularVelocity;
-    mutable Vector3* _anisotropicFriction;
-    mutable Vector3* _gravity;
-    mutable Vector3* _linearVelocity;
-    float* _vertexData;
-    std::vector<unsigned char*> _indexData;
-    float* _heightfieldData;
-    unsigned int _width;
-    unsigned int _height;
-    mutable Matrix* _inverse;
-    mutable bool _inverseIsDirty;
+	float _mass;
+	std::vector<PhysicsConstraint*>* _constraints;
+
 };
 
 }

+ 32 - 67
gameplay/src/PhysicsRigidBody.inl

@@ -4,44 +4,29 @@
 namespace gameplay
 {
 
-inline float PhysicsRigidBody::getAngularDamping() const
+inline float PhysicsRigidBody::getMass() const
 {
-    return _body->getAngularDamping();
+	return _mass;
 }
 
-inline const Vector3& PhysicsRigidBody::getAngularVelocity() const
+inline float PhysicsRigidBody::getFriction() const
 {
-    if (!_angularVelocity)
-        _angularVelocity = new Vector3();
-
-    const btVector3& v = _body->getAngularVelocity();
-    _angularVelocity->set(v.x(), v.y(), v.z());
-    return *_angularVelocity;
+    return _body->getFriction();
 }
 
-inline const Vector3& PhysicsRigidBody::getAnisotropicFriction() const
+inline void PhysicsRigidBody::setFriction(float friction)
 {
-    if (!_anisotropicFriction)
-        _anisotropicFriction = new Vector3();
-
-    const btVector3& af = _body->getAnisotropicFriction();
-    _anisotropicFriction->set(af.x(), af.y(), af.z());
-    return *_anisotropicFriction;
+    _body->setFriction(friction);
 }
 
-inline float PhysicsRigidBody::getFriction() const
+inline float PhysicsRigidBody::getRestitution() const
 {
-    return _body->getFriction();
+    return _body->getRestitution();
 }
 
-inline const Vector3& PhysicsRigidBody::getGravity() const
+inline void PhysicsRigidBody::setRestitution(float restitution)
 {
-    if (!_gravity)
-        _gravity = new Vector3();
-
-    const btVector3& g = _body->getGravity();
-    _gravity->set(g.x(), g.y(), g.z());
-    return *_gravity;
+    _body->setRestitution(restitution);
 }
 
 inline float PhysicsRigidBody::getLinearDamping() const
@@ -49,34 +34,31 @@ inline float PhysicsRigidBody::getLinearDamping() const
     return _body->getLinearDamping();
 }
 
-inline const Vector3& PhysicsRigidBody::getLinearVelocity() const
+inline float PhysicsRigidBody::getAngularDamping() const
 {
-    if (!_linearVelocity)
-        _linearVelocity = new Vector3();
-
-    const btVector3& v = _body->getLinearVelocity();
-    _linearVelocity->set(v.x(), v.y(), v.z());
-    return *_linearVelocity;
+    return _body->getAngularDamping();
 }
 
-inline float PhysicsRigidBody::getRestitution() const
+inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
 {
-    return _body->getRestitution();
+    _body->setDamping(linearDamping, angularDamping);
 }
 
-inline bool PhysicsRigidBody::isKinematic() const
+inline Vector3 PhysicsRigidBody::getLinearVelocity() const
 {
-    return _body->isKinematicObject();
+    const btVector3& v = _body->getLinearVelocity();
+	return Vector3(v.x(), v.y(), v.z());
 }
 
-inline bool PhysicsRigidBody::isStatic() const
+inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
 {
-    return _body->isStaticObject();
+    _body->setLinearVelocity(BV(velocity));
 }
 
-inline bool PhysicsRigidBody::isDynamic() const
+inline Vector3 PhysicsRigidBody::getAngularVelocity() const
 {
-    return !_body->isStaticOrKinematicObject();
+    const btVector3& v = _body->getAngularVelocity();
+    return Vector3(v.x(), v.y(), v.z());
 }
 
 inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
@@ -84,19 +66,21 @@ inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
     _body->setAngularVelocity(BV(velocity));
 }
 
-inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
+inline Vector3 PhysicsRigidBody::getAnisotropicFriction() const
 {
-    _body->setAnisotropicFriction(BV(friction));
+    const btVector3& af = _body->getAnisotropicFriction();
+    return Vector3(af.x(), af.y(), af.z());
 }
 
-inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
+inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
 {
-    _body->setDamping(linearDamping, angularDamping);
+    _body->setAnisotropicFriction(BV(friction));
 }
 
-inline void PhysicsRigidBody::setFriction(float friction)
+inline Vector3 PhysicsRigidBody::getGravity() const
 {
-    _body->setFriction(friction);
+    const btVector3& g = _body->getGravity();
+    return Vector3(g.x(), g.y(), g.z());
 }
 
 inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
@@ -104,28 +88,9 @@ inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
     _body->setGravity(BV(gravity));
 }
 
-inline void PhysicsRigidBody::setKinematic(bool kinematic)
-{
-    if (kinematic)
-    {
-        _body->setCollisionFlags(_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
-        _body->setActivationState(DISABLE_DEACTIVATION);
-    }
-    else
-    {
-        _body->setCollisionFlags(_body->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
-        _body->setActivationState(ACTIVE_TAG);
-    }
-}
-
-inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
-{
-    _body->setLinearVelocity(BV(velocity));
-}
-
-inline void PhysicsRigidBody::setRestitution(float restitution)
+inline bool PhysicsRigidBody::isStatic() const
 {
-    _body->setRestitution(restitution);
+    return _body->isStaticObject();
 }
 
 }

+ 1 - 0
gameplay/src/PlatformWin32.cpp

@@ -4,6 +4,7 @@
 #include "Platform.h"
 #include "FileSystem.h"
 #include "Game.h"
+#include "Form.h"
 #include <GL/wglew.h>
 
 // Default to 720p

+ 166 - 1
gameplay/src/Scene.cpp

@@ -2,11 +2,13 @@
 #include "AudioListener.h"
 #include "Scene.h"
 #include "SceneLoader.h"
+#include "MeshSkin.h"
+#include "Joint.h"
 
 namespace gameplay
 {
 
-Scene::Scene() : _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), _bindAudioListenerToCamera(true)
+Scene::Scene() : _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), _bindAudioListenerToCamera(true), _debugBatch(NULL)
 {
 }
 
@@ -288,4 +290,167 @@ void Scene::setAmbientColor(float red, float green, float blue)
     _ambientColor.set(red, green, blue);
 }
 
+Material* createDebugMaterial()
+{
+	// Vertex shader for drawing colored lines.
+	const char* vs_str = 
+	{
+		"uniform mat4 u_viewProjectionMatrix;\n"
+		"attribute vec4 a_position;\n"
+		"attribute vec4 a_color;\n"
+		"varying vec4 v_color;\n"
+		"void main(void) {\n"
+		"    v_color = a_color;\n"
+		"    gl_Position = u_viewProjectionMatrix * a_position;\n"
+		"}"
+	};
+
+	// Fragment shader for drawing colored lines.
+	const char* fs_str = 
+	{
+	#ifdef OPENGL_ES
+		"precision highp float;\n"
+	#endif
+		"varying vec4 v_color;\n"
+		"void main(void) {\n"
+		"   gl_FragColor = v_color;\n"
+		"}"
+	};
+
+	Effect* effect = Effect::createFromSource(vs_str, fs_str);
+	Material* material = Material::create(effect);
+	material->getStateBlock()->setDepthTest(true);
+
+	SAFE_RELEASE(effect);
+
+	return material;
+}
+
+struct DebugVertex
+{
+    float x, y, z;
+    float r, g, b, a;
+};
+
+void drawDebugLine(MeshBatch* batch, const Vector3& point1, const Vector3& point2, const Vector3& color)
+{
+    static DebugVertex verts[2];
+
+    verts[0].x = point1.x;
+    verts[0].y = point1.y;
+    verts[0].z = point1.z;
+	verts[0].r = color.x;
+    verts[0].g = color.y;
+    verts[0].b = color.z;
+    verts[0].a = 1.0f;
+
+    verts[1].x = point2.x;
+    verts[1].y = point2.y;
+    verts[1].z = point2.z;
+    verts[1].r = color.x;
+    verts[1].g = color.y;
+    verts[1].b = color.z;
+    verts[1].a = 1.0f;
+
+	batch->add(verts, 2);
+}
+
+#define DEBUG_BOX_COLOR Vector3(0, 1, 0)
+
+void drawDebugBox(MeshBatch* batch, const BoundingBox& box, const Matrix& matrix)
+{
+	// Transform box into world space (since we only store local boxes on mesh)
+	BoundingBox worldSpaceBox(box);
+	worldSpaceBox.transform(matrix);
+
+	// Get box corners
+    static Vector3 corners[8];
+    worldSpaceBox.getCorners(corners);
+
+	// Draw box lines
+	drawDebugLine(batch, corners[0], corners[1], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[1], corners[2], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[2], corners[3], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[3], corners[0], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[4], corners[5], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[5], corners[6], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[6], corners[7], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[7], corners[4], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[0], corners[7], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[1], corners[6], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[2], corners[5], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[3], corners[4], DEBUG_BOX_COLOR);
+}
+
+void drawDebugSphere(MeshBatch* batch, const BoundingSphere& sphere)
+{
+}
+
+void drawDebugNode(MeshBatch* batch, Node* node, unsigned int debugFlags)
+{
+	Model* model = node->getModel();
+
+	if ((debugFlags & Scene::DEBUG_BOXES) && model)
+	{
+		MeshSkin* skin = model->getSkin();
+		if (skin && skin->getRootJoint()->getParent())
+		{
+			// For skinned meshes that have a parent node to the skin's root joint,
+			// we need to transform the bounding volume by that parent node's transform
+			// as well to get the full skinned bounding volume.
+			drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix() * skin->getRootJoint()->getParent()->getWorldMatrix());
+		}
+		else
+		{
+			drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix());
+		}
+	}
+
+	if ((debugFlags & Scene::DEBUG_SPHERES) && !node->getBoundingSphere().isEmpty())
+	{
+		drawDebugSphere(batch, node->getBoundingSphere());
+	}
+
+	Node* child = node->getFirstChild();
+	while (child)
+	{
+		drawDebugNode(batch, child, debugFlags);
+		child = child->getNextSibling();
+	}
+}
+
+void Scene::drawDebug(unsigned int debugFlags)
+{
+	if (_debugBatch == NULL)
+	{
+		Material* material = createDebugMaterial();
+
+		VertexFormat::Element elements[] =
+		{
+			VertexFormat::Element(VertexFormat::POSITION, 3),
+			VertexFormat::Element(VertexFormat::COLOR, 4)
+		};
+
+		_debugBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false);
+
+		SAFE_RELEASE(material);
+	}
+
+	_debugBatch->begin();
+
+	Node* node = _firstNode;
+	while (node)
+	{
+		drawDebugNode(_debugBatch, node, debugFlags);
+		node = node->_nextSibling;
+	}
+
+	_debugBatch->end();
+
+	if (_activeCamera)
+		_debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_activeCamera->getViewProjectionMatrix());
+
+	_debugBatch->draw();
+}
+
 }

+ 19 - 0
gameplay/src/Scene.h

@@ -2,6 +2,7 @@
 #define SCENE_H_
 
 #include "Node.h"
+#include "MeshBatch.h"
 
 namespace gameplay
 {
@@ -13,6 +14,15 @@ class Scene : public Ref
 {
 public:
 
+	/**
+	 * Enumeration of supported scene debug flags for debug drawing.
+	 */
+	enum DebugFlags
+	{
+		DEBUG_BOXES = 1,
+		DEBUG_SPHERES = 2
+	};
+
     /**
      * Creates a new empty scene.
      * 
@@ -180,6 +190,14 @@ public:
     template <class T>
     void visit(T* instance, bool (T::*visitMethod)(Node*,void*), void* cookie = 0);
 
+	/**
+	 * Draws debugging information (bounding volumes, etc.) for the scene.
+	 *
+	 * @param debugFlags Bitwise combination of debug flags from mthe DebugFlags 
+	 *		enumeration, specifying which debugging information to draw.
+	 */
+	void drawDebug(unsigned int debugFlags);
+
 private:
 
     /**
@@ -211,6 +229,7 @@ private:
     unsigned int _nodeCount;
     Vector3 _ambientColor;
     bool _bindAudioListenerToCamera;
+	MeshBatch* _debugBatch;
 };
 
 template <class T>

+ 13 - 7
gameplay/src/SceneLoader.cpp

@@ -288,18 +288,24 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                         WARN_VARG("Node '%s' does not have a model; attempting to use its model for rigid body creation.", name);
                     else
                     {
-                        // Set the specified model during physics rigid body creation.
+                        // Temporarily set rigidbody model on model to it's used during rigid body creation.
                         Model* model = node->getModel();
+						model->addRef(); // up ref count to prevent node from releasing the model when we swap it
                         node->setModel(modelNode->getModel());
-                        node->setRigidBody(p);
+
+						// Create collision object with new rigidbodymodel set.
+						node->setCollisionObject(p);
+
+						// Restore original model
                         node->setModel(model);
+						model->release(); // decrement temporarily added reference
                     }
                 }
             }
             else if (!node->getModel())
                 WARN_VARG("Attempting to set a rigid body on node '%s', which has no model.", sceneNode._nodeID);
             else
-                node->setRigidBody(p);
+				node->setCollisionObject(p);
             break;
         }
         default:
@@ -768,12 +774,12 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                 WARN_VARG("Node '%s' to be used as 'rigidBodyA' for constraint %s cannot be found.", name, constraint->getId());
                 continue;
             }
-            PhysicsRigidBody* rbA = rbANode->getRigidBody();
-            if (!rbA)
+			if (!rbANode->getCollisionObject() || rbANode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
             {
                 WARN_VARG("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
                 continue;
             }
+			PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
 
             // Attempt to load the second rigid body. If the second rigid body is not
             // specified, that is usually okay (only spring constraints require both and
@@ -789,12 +795,12 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                     WARN_VARG("Node '%s' to be used as 'rigidBodyB' for constraint %s cannot be found.", name, constraint->getId());
                     continue;
                 }
-                rbB = rbBNode->getRigidBody();
-                if (!rbB)
+				if (!rbBNode->getCollisionObject() || rbBNode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
                 {
                     WARN_VARG("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
                     continue;
                 }
+				rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
             }
 
             PhysicsConstraint* physicsConstraint = NULL;

+ 6 - 0
gameplay/src/SceneLoader.h

@@ -12,6 +12,12 @@ namespace gameplay
 
 /**
  * Helper class for loading scenes from .scene files.
+ *
+ * @todo Add support for loading ghost objects and characters for nodes.
+ * @todo Add support for explicitly specifying collision shapes for rigid bodies/ghost objects/characters.
+ * @todo Consider supporting 'rigidbodymodel' on models/meshes that are not part of the scene to allow
+ *		mesh data to be exported from a modelling tool for the sole purpose of representing a physics
+ *		rigid body, but not have it get loaded into the scene and rendering context.
  */
 class SceneLoader
 {