Parcourir la source

Merge pull request #181 from blackberry-gaming/next-sgrenier

Next sgrenier
Steve Grenier il y a 14 ans
Parent
commit
60826b463f

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -62,6 +62,7 @@
     <ClCompile Include="src\Package.cpp" />
     <ClCompile Include="src\ParticleEmitter.cpp" />
     <ClCompile Include="src\PhysicsCharacter.cpp" />
+    <ClCompile Include="src\PhysicsCollisionObject.cpp" />
     <ClCompile Include="src\PhysicsConstraint.cpp" />
     <ClCompile Include="src\PhysicsController.cpp" />
     <ClCompile Include="src\PhysicsFixedConstraint.cpp" />
@@ -150,6 +151,7 @@
     <ClInclude Include="src\Package.h" />
     <ClInclude Include="src\ParticleEmitter.h" />
     <ClInclude Include="src\PhysicsCharacter.h" />
+    <ClInclude Include="src\PhysicsCollisionObject.h" />
     <ClInclude Include="src\PhysicsConstraint.h" />
     <ClInclude Include="src\PhysicsController.h" />
     <ClInclude Include="src\PhysicsFixedConstraint.h" />

+ 7 - 1
gameplay/gameplay.vcxproj.filters

@@ -267,6 +267,9 @@
     <ClCompile Include="src\PhysicsCharacter.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\PhysicsCollisionObject.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -524,6 +527,9 @@
     <ClInclude Include="src\PhysicsCharacter.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\PhysicsCollisionObject.h">
+      <Filter>src</Filter>
+    </ClInclude>
     <ClInclude Include="src\TimeListener.h">
       <Filter>src</Filter>
     </ClInclude>
@@ -661,4 +667,4 @@
       <Filter>src</Filter>
     </None>
   </ItemGroup>
-</Project>
+</Project>

+ 2 - 2
gameplay/res/shaders/colored-specular.fsh

@@ -29,7 +29,7 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-	float ddot = abs(dot(normalVector, lightDirection));
+    float ddot = abs(dot(normalVector, lightDirection));
     float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
@@ -122,4 +122,4 @@ void main()
     // Light the pixel
     gl_FragColor.a = _baseColor.a;
     gl_FragColor.rgb = _ambientColor + _diffuseColor + _specularColor;
-}
+}

+ 1 - 1
gameplay/res/shaders/colored.fsh

@@ -21,7 +21,7 @@ void lighting(vec3 normalVector, vec3 lightDirection, float attenuation)
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-	float ddot = dot(normalVector, lightDirection);
+    float ddot = dot(normalVector, lightDirection);
     float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;

+ 2 - 2
gameplay/res/shaders/diffuse-specular.fsh

@@ -25,7 +25,7 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-	float ddot = dot(normalVector, lightDirection);
+    float ddot = dot(normalVector, lightDirection);
     float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
@@ -114,4 +114,4 @@ void main()
     // Light the pixel
     gl_FragColor.a = _baseColor.a;
     gl_FragColor.rgb = _ambientColor + _diffuseColor + _specularColor;
-}
+}

+ 1 - 1
gameplay/src/Node.cpp

@@ -754,7 +754,7 @@ PhysicsRigidBody* Node::getRigidBody() const
     return _physicsRigidBody;
 }
 
-void Node::setRigidBody(PhysicsRigidBody::Type type, float mass, float friction,
+void Node::setRigidBody(PhysicsRigidBody::ShapeType type, float mass, float friction,
         float restitution, float linearDamping, float angularDamping)
 {
     SAFE_DELETE(_physicsRigidBody);

+ 1 - 1
gameplay/src/Node.h

@@ -387,7 +387,7 @@ public:
      * @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::Type type, float mass = 0.0f, float friction = 0.5f,
+    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);
 
     /**

+ 65 - 35
gameplay/src/PhysicsCharacter.cpp

@@ -10,6 +10,16 @@
 #include "Game.h"
 #include "PhysicsController.h"
 
+// Amount to walk collision normal when attempting to repair a collision.
+// To small a value will result in inefficient collision repairs (several iterations
+// to fix a collision and slow resolution), whereas larger values will result
+// in less accurate collision resolution.
+//#define COLLISION_REPAIR_INCREMENT 0.2f
+#define COLLISION_REPAIR_MARGIN 1.0f
+
+// Maximum number of iterations used to perform perform collision repair each update.
+#define COLLISION_REPAIR_MAX_ITERATIONS 4
+
 namespace gameplay
 {
 
@@ -76,38 +86,54 @@ PhysicsCharacter::PhysicsCharacter(Node* node, float radius, float height, const
     // Set initial transform
     _motionState->getWorldTransform(_ghostObject->getWorldTransform());
 
-    // Create a capsule collision shape
-    _collisionShape = bullet_new<btCapsuleShape>((btScalar)radius, (btScalar)(height - radius*2));
+    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);
 
-    btDynamicsWorld* world = Game::getInstance()->getPhysicsController()->_world;
-    
-    // Register the ghost object for collisions with the world.
-    // For now specify static flag only, so character does not interact with dynamic objects
-    world->addCollisionObject(_ghostObject, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter);
+    // Add the collision object to the physics system
+    pc->addCollisionObject(this);
 
     // Register ourselves as an action on the physics world so we are called back during physics ticks
-    world->addAction(this);
+    pc->_world->addAction(this);
 }
 
 PhysicsCharacter::~PhysicsCharacter()
 {
-    // Unregister ourself with world
-    btDynamicsWorld* world = Game::getInstance()->getPhysicsController()->_world;
-    world->removeCollisionObject(_ghostObject);
-    world->removeAction(this);
+    // 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);
-    SAFE_DELETE(_collisionShape);
 
     _node->removeListener(this);
     SAFE_RELEASE(_node);
     SAFE_DELETE(_motionState);
 }
 
+PhysicsCollisionObject::Type PhysicsCharacter::getType() const
+{
+    return PhysicsCollisionObject::CHARACTER;
+}
+
+btCollisionObject* PhysicsCharacter::getCollisionObject() const
+{
+    return _ghostObject;
+}
+
+btCollisionShape* PhysicsCharacter::getCollisionShape() const
+{
+    return _collisionShape;
+}
+
 Node* PhysicsCharacter::getNode() const
 {
     return _node;
@@ -354,19 +380,22 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     // the following steps (movement) start from a clean slate, where the character
     // is not colliding with anything. Also, this step handles collision between
     // dynamic objects (i.e. objects that moved and now intersect the character).
-    _colliding = false;
+    _colliding = fixCollision(collisionWorld);
+    /*_colliding = false;
     int stepCount = 0;
 	while (fixCollision(collisionWorld))
 	{
         _colliding = true;
 
-        // After a small number of attempts to fix a collision/penetration, give up...
-        if (++stepCount > 4)
+        // After a small number of attempts to fix a collision/penetration, give up.
+        // This hanldes the case where we are deeply penetrating some object and attempting
+        // to step out of it (by COLLISION_REPAIR_INCREMENT units) does not fix the collision.
+        if (++stepCount > COLLISION_REPAIR_MAX_ITERATIONS)
 		{
             WARN_VARG("Character '%s' could not recover from collision.", _node->getId());
 			break;
 		}
-	}
+	}*/
 
     // Update current and target world positions
     btTransform transform = _ghostObject->getWorldTransform();
@@ -440,12 +469,6 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
     updateCurrentVelocity();
 
-    if (_currentVelocity.isZero())
-    {
-        // No velocity, so we aren't moving
-        return;
-    }
-
     // Calculate final velocity
     btVector3 velocity(_currentVelocity);
     if (animationCount > 0)
@@ -454,6 +477,12 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
     }
     velocity *= time; // since velocity is in meters per second
 
+    if (velocity.isZero())
+    {
+        // No velocity, so we aren't moving
+        return;
+    }
+
     // Translate the target position by the velocity vector (already scaled by t)
     btVector3 targetPosition = _currentPosition + velocity;
 
@@ -593,7 +622,7 @@ btVector3 perpindicularComponent(const btVector3& direction, const btVector3& no
 void PhysicsCharacter::updateTargetPositionFromCollision(btVector3& targetPosition, const btVector3& collisionNormal)
 {
     //btScalar tangentMag = 0.0;
-    btScalar normalMag = 1.0;
+    //btScalar normalMag = 1.0;
 
 	btVector3 movementDirection = targetPosition - _currentPosition;
 	btScalar movementLength = movementDirection.length();
@@ -605,10 +634,8 @@ void PhysicsCharacter::updateTargetPositionFromCollision(btVector3& targetPositi
 		btVector3 reflectDir = computeReflectionDirection(movementDirection, collisionNormal);
 		reflectDir.normalize();
 
-		btVector3 parallelDir, perpindicularDir;
-
-		parallelDir = parallelComponent(reflectDir, collisionNormal);
-		perpindicularDir = perpindicularComponent(reflectDir, collisionNormal);
+		//btVector3 parallelDir = parallelComponent(reflectDir, collisionNormal);
+		btVector3 perpindicularDir = perpindicularComponent(reflectDir, collisionNormal);
 
 		targetPosition = _currentPosition;
 		/*if (tangentMag != 0.0)
@@ -617,11 +644,11 @@ void PhysicsCharacter::updateTargetPositionFromCollision(btVector3& targetPositi
 			targetPosition +=  parComponent;
 		}*/
 
-		if (normalMag != 0.0)
-		{
-			btVector3 perpComponent = perpindicularDir * btScalar (normalMag * movementLength);
+		//if (normalMag != 0.0)
+		//{
+			btVector3 perpComponent = perpindicularDir * btScalar (/*normalMag **/ movementLength);
 			targetPosition += perpComponent;
-		}
+		//}
 	}
 }
 
@@ -654,8 +681,8 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
 		{
 			btPersistentManifold* manifold = _manifoldArray[j];
 
-            // Get the direction of the contact points (used to scale normal vector in the correct direction)
-			btScalar directionSign = manifold->getBody0() == _ghostObject ? btScalar(-1.0) : btScalar(1.0);
+            // Get the direction of the contact points (used to scale normal vector in the correct direction).
+            btScalar directionSign = manifold->getBody0() == _ghostObject ? -1.0f : 1.0f;
 
 			for (int p = 0, contactCount = manifold->getNumContacts(); p < contactCount; ++p)
 			{
@@ -666,6 +693,7 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
 
 				if (dist < 0.0)
 				{
+                    // A negative distance means the objects are overlapping
 					if (dist < maxPenetration)
 					{
                         // Store collision normal for this point
@@ -674,7 +702,9 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
 					}
 
                     // Calculate new position for object, which is translated back along the collision normal
-					currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
+                    btVector3 n(pt.m_normalWorldOnB);
+                    n.normalize();
+					currentPosition += /*pt.m_normalWorldOnB*/n * directionSign * dist * COLLISION_REPAIR_MARGIN;// + _collisionShape->getMargin());// * COLLISION_REPAIR_INCREMENT;
 					collision = true;
 				}
 			}

+ 24 - 3
gameplay/src/PhysicsCharacter.h

@@ -23,10 +23,8 @@ namespace gameplay
  * This class can also be used to control animations for a character. Animation
  * 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.
- *
- * @todo Add support for collision listeners.
  */
-class PhysicsCharacter : public Transform::Listener, public btActionInterface
+class PhysicsCharacter : public PhysicsCollisionObject, public Transform::Listener, public btActionInterface
 {
     friend class PhysicsController;
 
@@ -53,10 +51,17 @@ public:
          ANIMATION_REPEAT
     };
 
+    /**
+     * @see PhysicsCollisionObject#getType
+     */
+    PhysicsCollisionObject::Type getType() const;
+
     /**
      * Returns the character node for this PhysicsCharacter.
      *
      * @return The character Node.
+     *
+     * @see PhysicsCollisionObject::getNode.
      */
     Node* getNode() const;
 
@@ -247,6 +252,18 @@ public:
      */
 	void debugDraw(btIDebugDraw* debugDrawer);
 
+protected:
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionObject
+     */
+    btCollisionObject* getCollisionObject() const;
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionShape
+     */
+    btCollisionShape* getCollisionShape() const;
+
 private:
 
     struct CharacterAnimation
@@ -264,6 +281,8 @@ private:
     /**
      * Creates a new PhysicsCharacter.
      *
+     * 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.
@@ -273,6 +292,8 @@ private:
 
     /**
      * Destructor.
+     *
+     * Use PhysicsController::destroyCharacter to destroy physics characters.
      */
     virtual ~PhysicsCharacter();
 

+ 66 - 0
gameplay/src/PhysicsCollisionObject.cpp

@@ -0,0 +1,66 @@
+#include "Base.h"
+#include "PhysicsCollisionObject.h"
+#include "PhysicsController.h"
+#include "Game.h"
+
+namespace gameplay
+{
+
+PhysicsCollisionObject::PhysicsCollisionObject()
+{
+}
+
+PhysicsCollisionObject::~PhysicsCollisionObject()
+{
+}
+
+void PhysicsCollisionObject::addCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
+{
+    Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, object);
+}
+
+void PhysicsCollisionObject::removeCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
+{
+    Game::getInstance()->getPhysicsController()->removeCollisionListener(listener, this, object);
+}
+
+bool PhysicsCollisionObject::collidesWith(PhysicsCollisionObject* object) const
+{
+    static CollidesWithCallback callback;
+
+    callback.result = false;
+    Game::getInstance()->getPhysicsController()->_world->contactPairTest(getCollisionObject(), object->getCollisionObject(), callback);
+    return callback.result;
+}
+
+PhysicsCollisionObject::CollisionPair::CollisionPair(PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB)
+    : objectA(objectA), objectB(objectB)
+{
+    // unused
+}
+
+bool PhysicsCollisionObject::CollisionPair::operator < (const CollisionPair& collisionPair) const
+{
+    // If the pairs are equal, then return false.
+    if ((objectA == collisionPair.objectA && objectB == collisionPair.objectB) || (objectA == collisionPair.objectB && objectB == collisionPair.objectA))
+        return false;
+
+    // We choose to compare based on objectA arbitrarily.
+    if (objectA < collisionPair.objectA)
+        return true;
+
+    if (objectA == collisionPair.objectA)
+        return objectB < collisionPair.objectB;
+
+    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;
+}
+
+}

+ 188 - 0
gameplay/src/PhysicsCollisionObject.h

@@ -0,0 +1,188 @@
+#ifndef PHYSICSCOLLISIONOBJECT_H_
+#define PHYSICSCOLLISIONOBJECT_H_
+
+#include "Vector3.h"
+
+namespace gameplay
+{
+
+class Node;
+
+/**
+ * Base class for all gameplay physics objects that support collision events.
+ */
+class PhysicsCollisionObject
+{
+    friend class PhysicsController;
+
+public:
+
+    /**
+     * Enumeration of all possible collision object types.
+     */
+    enum Type
+    {
+        /**
+         * PhysicsRigidBody type.
+         */
+        RIGID_BODY,
+
+        /**
+         * PhysicsCharacter type.
+         */
+        CHARACTER
+    };
+
+    /** 
+     * Defines a pair of rigid bodies that collided (or may collide).
+     */
+    class CollisionPair
+    {
+    public:
+
+        /**
+         * Constructor.
+         */
+        CollisionPair(PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB);
+
+        /**
+         * Less than operator (needed for use as a key in map).
+         * 
+         * @param collisionPair The collision pair to compare.
+         * @return True if this pair is "less than" the given pair; false otherwise.
+         */
+        bool operator < (const CollisionPair& collisionPair) const;
+
+        /**
+         * The first object in the collision.
+         */
+        PhysicsCollisionObject* objectA;
+
+        /**
+         * The second object in the collision.
+         */
+        PhysicsCollisionObject* objectB;
+    };
+
+    /**
+     * Collision listener interface.
+     */
+    class CollisionListener
+    {
+        friend class PhysicsCollisionObject;
+        friend class PhysicsController;
+
+    public:
+
+        /**
+         * The type of collision event.
+         */
+        enum EventType
+        {
+            /**
+             * Event fired when the two rigid bodies start colliding.
+             */
+            COLLIDING,
+
+            /**
+             * Event fired when the two rigid bodies no longer collide.
+             */
+            NOT_COLLIDING
+        };
+
+        /**
+         * Virtual destructor.
+         */
+        virtual ~CollisionListener() { }
+
+        /**
+         * Called when a collision occurs between two objects in the physics world.
+         * 
+         * @param type The type of collision event.
+         * @param collisionPair The two collision objects involved in the collision.
+         * @param contactPointA The contact point with the first object (in world space).
+         * @param contactPointB The contact point with the second object (in world space).
+         */
+        virtual void collisionEvent(PhysicsCollisionObject::CollisionListener::EventType type,
+                                    const PhysicsCollisionObject::CollisionPair& collisionPair,
+                                    const Vector3& contactPointA = Vector3::zero(),
+                                    const Vector3& contactPointB = Vector3::zero()) = 0;
+    };
+
+    /**
+     * Returns the type of the collision object.
+     */
+    virtual PhysicsCollisionObject::Type getType() const = 0;
+
+    /**
+     * Returns the node associated with this collision object.
+     */
+    virtual Node* getNode() const = 0;
+
+    /**
+     * Adds a collision listener for this collision object.
+     * 
+     * @param listener The listener to add.
+     * @param object Optional collision object used to filter the collision event.
+     */
+    void addCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object = NULL);
+
+    /**
+     * Removes a collision listener.
+     *
+     * @param listener The listener to remove.
+     */
+    void removeCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object = NULL);
+
+    /**
+     * Checks if this collision object collides with the given object.
+     * 
+     * @param object The collision object to test for collision with.
+     * 
+     * @return True if this object collides with the specified one; false otherwise.
+     */
+    bool collidesWith(PhysicsCollisionObject* object) const;
+
+protected:
+
+    /**
+     * Constructor.
+     */
+    PhysicsCollisionObject();
+
+    /**
+     * Virtual destructor.
+     */
+    virtual ~PhysicsCollisionObject();
+
+    /**
+     * Returns the Bullet Physics collision object.
+     *
+     * @return The Bullet collision object.
+     */
+    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;
+    };
+
+};
+
+}
+
+#endif

+ 109 - 88
gameplay/src/PhysicsController.cpp

@@ -142,7 +142,7 @@ void PhysicsController::drawDebug(const Matrix& viewProjection)
     _debugDrawer->end();
 }
 
-PhysicsRigidBody* PhysicsController::rayTest(const Ray& ray, float distance, Vector3* hitPoint, float* hitFraction)
+PhysicsCollisionObject* PhysicsController::rayTest(const Ray& ray, float distance, Vector3* hitPoint, float* hitFraction)
 {
     btCollisionWorld::ClosestRayResultCallback callback(BV(ray.getOrigin()), BV(distance * ray.getDirection()));
     _world->rayTest(BV(ray.getOrigin()), BV(distance * ray.getDirection()), callback);
@@ -154,7 +154,7 @@ PhysicsRigidBody* PhysicsController::rayTest(const Ray& ray, float distance, Vec
         if (hitFraction)
             *hitFraction = callback.m_closestHitFraction;
 
-        return getRigidBody(callback.m_collisionObject);
+        return getCollisionObject(callback.m_collisionObject);
     }
 
     return NULL;
@@ -164,65 +164,57 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
     const btCollisionObject* b, int partIdB, int indexB)
 {
     // Get pointers to the PhysicsRigidBody objects.
-    PhysicsRigidBody* rbA = Game::getInstance()->getPhysicsController()->getRigidBody(a);
-    PhysicsRigidBody* rbB = Game::getInstance()->getPhysicsController()->getRigidBody(b);
-    
+    PhysicsCollisionObject* rbA = Game::getInstance()->getPhysicsController()->getCollisionObject(a);
+    PhysicsCollisionObject* rbB = Game::getInstance()->getPhysicsController()->getCollisionObject(b);
+
     // If the given rigid body pair has collided in the past, then
     // we notify the listeners only if the pair was not colliding
     // during the previous frame. Otherwise, it's a new pair, so add a
     // new entry to the cache with the appropriate listeners and notify them.
     PhysicsRigidBody::CollisionPair pair(rbA, rbB);
+
+    CollisionInfo* collisionInfo;
     if (_collisionStatus.count(pair) > 0)
     {
-        const CollisionInfo& collisionInfo = _collisionStatus[pair];
-        if ((collisionInfo._status & COLLISION) == 0)
-        {
-            std::vector<PhysicsRigidBody::Listener*>::const_iterator iter = collisionInfo._listeners.begin();
-            for (; iter != collisionInfo._listeners.end(); iter++)
-            {
-                if ((collisionInfo._status & REMOVE) == 0)
-                {
-                    (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
-                        Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
-                }
-            }
-        }
+        collisionInfo = &_collisionStatus[pair];
     }
     else
     {
-        CollisionInfo& collisionInfo = _collisionStatus[pair];
-
-        // Initialized the status for the new entry.
-        collisionInfo._status = 0;
+        // Add a new collision pair for these objects
+        collisionInfo = &_collisionStatus[pair];
 
         // Add the appropriate listeners.
-        PhysicsRigidBody::CollisionPair p1(pair.rigidBodyA, NULL);
+        PhysicsRigidBody::CollisionPair p1(pair.objectA, NULL);
         if (_collisionStatus.count(p1) > 0)
         {
             const CollisionInfo& ci = _collisionStatus[p1];
-            std::vector<PhysicsRigidBody::Listener*>::const_iterator iter = ci._listeners.begin();
+            std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = ci._listeners.begin();
             for (; iter != ci._listeners.end(); iter++)
             {
-                collisionInfo._listeners.push_back(*iter);
+                collisionInfo->_listeners.push_back(*iter);
             }
         }
-        PhysicsRigidBody::CollisionPair p2(pair.rigidBodyB, NULL);
+        PhysicsRigidBody::CollisionPair p2(pair.objectB, NULL);
         if (_collisionStatus.count(p2) > 0)
         {
             const CollisionInfo& ci = _collisionStatus[p2];
-            std::vector<PhysicsRigidBody::Listener*>::const_iterator iter = ci._listeners.begin();
+            std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = ci._listeners.begin();
             for (; iter != ci._listeners.end(); iter++)
             {
-                collisionInfo._listeners.push_back(*iter);
+                collisionInfo->_listeners.push_back(*iter);
             }
         }
+    }
 
-        std::vector<PhysicsRigidBody::Listener*>::iterator iter = collisionInfo._listeners.begin();
-        for (; iter != collisionInfo._listeners.end(); iter++)
+    // Fire collision event
+    if ((collisionInfo->_status & COLLISION) == 0)
+    {
+        std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = collisionInfo->_listeners.begin();
+        for (; iter != collisionInfo->_listeners.end(); iter++)
         {
-            if ((collisionInfo._status & REMOVE) == 0)
+            if ((collisionInfo->_status & REMOVE) == 0)
             {
-                (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
+                (*iter)->collisionEvent(PhysicsCollisionObject::CollisionListener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
                     Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
             }
         }
@@ -231,8 +223,8 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
     // Update the collision status cache (we remove the dirty bit
     // set in the controller's update so that this particular collision pair's
     // status is not reset to 'no collision' when the controller's update completes).
-    _collisionStatus[pair]._status &= ~DIRTY;
-    _collisionStatus[pair]._status |= COLLISION;
+    collisionInfo->_status &= ~DIRTY;
+    collisionInfo->_status |= COLLISION;
     return 0.0f;
 }
 
@@ -326,7 +318,6 @@ void PhysicsController::update(long elapsedTime)
                 (*_listeners)[k]->statusEvent(_status);
             }
         }
-        
     }
 
     // All statuses are set with the DIRTY bit before collision processing occurs.
@@ -362,10 +353,10 @@ void PhysicsController::update(long elapsedTime)
         // of collision pairs in the status cache that we did not explicitly register for.)
         if ((iter->second._status & REGISTERED) != 0 && (iter->second._status & REMOVE) == 0)
         {
-            if (iter->first.rigidBodyB)
-                _world->contactPairTest(iter->first.rigidBodyA->_body, iter->first.rigidBodyB->_body, *this);
+            if (iter->first.objectB)
+                _world->contactPairTest(iter->first.objectA->getCollisionObject(), iter->first.objectB->getCollisionObject(), *this);
             else
-                _world->contactTest(iter->first.rigidBodyA->_body, *this);
+                _world->contactTest(iter->first.objectA->getCollisionObject(), *this);
         }
     }
 
@@ -375,12 +366,12 @@ void PhysicsController::update(long elapsedTime)
     {
         if ((iter->second._status & DIRTY) != 0)
         {
-            if ((iter->second._status & COLLISION) != 0 && iter->first.rigidBodyB)
+            if ((iter->second._status & COLLISION) != 0 && iter->first.objectB)
             {
                 unsigned int size = iter->second._listeners.size();
                 for (unsigned int i = 0; i < size; i++)
                 {
-                    iter->second._listeners[i]->collisionEvent(PhysicsRigidBody::Listener::NOT_COLLIDING, iter->first);
+                    iter->second._listeners[i]->collisionEvent(PhysicsCollisionObject::CollisionListener::NOT_COLLIDING, iter->first);
                 }
             }
 
@@ -389,85 +380,103 @@ void PhysicsController::update(long elapsedTime)
     }
 }
 
-void PhysicsController::addCollisionListener(PhysicsRigidBody::Listener* listener, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
+void PhysicsController::addCollisionListener(PhysicsCollisionObject::CollisionListener* listener, PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB)
 {
-    PhysicsRigidBody::CollisionPair pair(rbA, rbB);
-
-    // Make sure the status of the entry is initialized properly.
-    if (_collisionStatus.count(pair) == 0)
-        _collisionStatus[pair]._status = 0;
+    PhysicsCollisionObject::CollisionPair pair(objectA, objectB);
 
     // Add the listener and ensure the status includes that this collision pair is registered.
-    _collisionStatus[pair]._listeners.push_back(listener);
-    if ((_collisionStatus[pair]._status & PhysicsController::REGISTERED) == 0)
-        _collisionStatus[pair]._status |= PhysicsController::REGISTERED;
+    CollisionInfo& info = _collisionStatus[pair];
+    info._listeners.push_back(listener);
+    info._status |= PhysicsController::REGISTERED;
 }
 
-void PhysicsController::addRigidBody(PhysicsRigidBody* body)
+void PhysicsController::removeCollisionListener(PhysicsCollisionObject::CollisionListener* listener, PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB)
 {
-    _world->addRigidBody(body->_body);
-    _bodies.push_back(body);
+    // Mark the collision pair for these objects for removal
+    PhysicsCollisionObject::CollisionPair pair(objectA, objectB);
+    if (_collisionStatus.count(pair) > 0)
+    {
+        _collisionStatus[pair]._status |= REMOVE;
+    }
 }
-    
-void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
+
+void PhysicsController::addCollisionObject(PhysicsCollisionObject* object)
 {
-    // Find the rigid body and remove it from the world.
-    for (int i = _world->getNumCollisionObjects() - 1; i >= 0 ; i--)
+    // Assign user pointer for the bullet collision object to allow efficient
+    // lookups of bullet objects -> gameplay objects.
+    object->getCollisionObject()->setUserPointer(object);
+
+    // Add the object to the physics world
+    switch (object->getType())
     {
-        btCollisionObject* obj = _world->getCollisionObjectArray()[i];
-        if (rigidBody->_body == obj)
-        {
-            _world->removeCollisionObject(obj);
-            break;
-        }
+    case PhysicsCollisionObject::RIGID_BODY:
+        _world->addRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));
+        break;
+
+    case PhysicsCollisionObject::CHARACTER:
+        _world->addCollisionObject(object->getCollisionObject(), btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter);
+        break;
+
+    default:
+        assert(0); // unexpected (new type?)
+        break;
     }
+}
 
-    // Find the rigid body's collision shape and release the rigid body's reference to it.
-    for (unsigned int i = 0; i < _shapes.size(); i++)
+void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
+{
+    // Remove the collision object from the world
+    if (object->getCollisionObject())
     {
-        if (_shapes[i]->_shape == rigidBody->_shape)
+        switch (object->getType())
         {
-            if (_shapes[i]->getRefCount() == 1)
-            {
-                _shapes[i]->release();
-                _shapes.erase(_shapes.begin() + i);
-            }
-            else
-                _shapes[i]->release();
+        case PhysicsCollisionObject::RIGID_BODY:
+            _world->removeRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));
+            break;
 
+        case PhysicsCollisionObject::CHARACTER:
+            _world->removeCollisionObject(object->getCollisionObject());
+            break;
+
+        default:
+            assert(0); // unexpected (new type?)
             break;
         }
     }
 
-    // Remove the rigid body from the controller's list.
-    for (unsigned int i = 0; i < _bodies.size(); i++)
+    // Release collision shape
+    if (object->getCollisionShape())
     {
-        if (_bodies[i] == rigidBody)
+        PhysicsCollisionShape* shape = reinterpret_cast<PhysicsCollisionShape*>(object->getCollisionShape()->getUserPointer());
+        if (shape)
         {
-            _bodies.erase(_bodies.begin() + i);
-            break;
+            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 rigid body in the collision status cache and mark them for removal.
+    // 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();
     for (; iter != _collisionStatus.end(); iter++)
     {
-        if (iter->first.rigidBodyA == rigidBody || iter->first.rigidBodyB == rigidBody)
+        if (iter->first.objectA == object || iter->first.objectB == object)
             iter->second._status |= REMOVE;
     }
 }
 
-PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* collisionObject)
+PhysicsCollisionObject* PhysicsController::getCollisionObject(const btCollisionObject* collisionObject) const
 {
-    // Find the rigid body and remove it from the world.
-    for (unsigned int i = 0; i < _bodies.size(); i++)
-    {
-        if (_bodies[i]->_body == collisionObject)
-            return _bodies[i];
-    }
-    
-    return NULL;
+    // Gameplay rigid bodies are stored in the userPointer data of bullet collision objects
+    return reinterpret_cast<PhysicsCollisionObject*>(collisionObject->getUserPointer());
 }
 
 btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const Vector3& scale)
@@ -846,4 +855,16 @@ 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);
+}
+
 }

+ 20 - 14
gameplay/src/PhysicsController.h

@@ -22,6 +22,7 @@ class PhysicsController : public btCollisionWorld::ContactResultCallback
     friend class PhysicsConstraint;
     friend class PhysicsRigidBody;
     friend class PhysicsCharacter;
+    friend class PhysicsCollisionObject;
 
 public:
 
@@ -237,7 +238,7 @@ public:
      *      (as a fraction between 0-1) where the intersection occurred.
      * @return The first rigid body that the ray intersects, or NULL if no intersection was found.
      */
-    PhysicsRigidBody* rayTest(const Ray& ray, float distance, Vector3* hitPoint = NULL, float* hitFraction = NULL);
+    PhysicsCollisionObject* rayTest(const Ray& ray, float distance, Vector3* hitPoint = NULL, float* hitFraction = NULL);
 
 protected:
 
@@ -257,15 +258,17 @@ private:
     // Represents the collision listeners and status for a given collision pair (used by the collision status cache).
     struct CollisionInfo
     {
-        std::vector<PhysicsRigidBody::Listener*> _listeners;
+        CollisionInfo() : _status(0) { }
+
+        std::vector<PhysicsCollisionObject::CollisionListener*> _listeners;
         int _status;
     };
 
     // Wraps Bullet collision shapes (used for implementing shape caching).
     struct PhysicsCollisionShape : public Ref
     {
-        PhysicsCollisionShape(btCollisionShape* shape) : _shape(shape) {}
-        ~PhysicsCollisionShape() { SAFE_DELETE(_shape); }
+        PhysicsCollisionShape(btCollisionShape* shape);
+        ~PhysicsCollisionShape();
 
         btCollisionShape* _shape;
     };
@@ -305,17 +308,20 @@ private:
      */
     void update(long elapsedTime);
 
-    // Adds the given collision listener for the two given rigid bodies.
-    void addCollisionListener(PhysicsRigidBody::Listener* listener, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
+    // Adds the given collision listener for the two given collision objects.
+    void addCollisionListener(PhysicsCollisionObject::CollisionListener* listener, PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB);
+
+    // Removes the given collision listener.
+    void removeCollisionListener(PhysicsCollisionObject::CollisionListener* listener, PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB);
 
-    // Adds the given rigid body to the world.
-    void addRigidBody(PhysicsRigidBody* body);
+    // Adds the given collision object to the world.
+    void addCollisionObject(PhysicsCollisionObject* object);
     
-    // Removes the given rigid body from the simulated physics world.
-    void removeRigidBody(PhysicsRigidBody* rigidBody);
+    // Removes the given collision object from the simulated physics world.
+    void removeCollisionObject(PhysicsCollisionObject* object);
     
     // Gets the corresponding GamePlay object for the given Bullet object.
-    PhysicsRigidBody* getRigidBody(const btCollisionObject* collisionObject);
+    PhysicsCollisionObject* getCollisionObject(const btCollisionObject* collisionObject) const;
     
     // 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);
@@ -373,7 +379,7 @@ private:
         const Matrix* _viewProjection;
         MeshBatch* _meshBatch;
     };
-    
+
     btDefaultCollisionConfiguration* _collisionConfiguration;
     btCollisionDispatcher* _dispatcher;
     btBroadphaseInterface* _overlappingPairCache;
@@ -384,9 +390,9 @@ private:
     DebugDrawer* _debugDrawer;
     Listener::EventType _status;
     std::vector<Listener*>* _listeners;
-    std::vector<PhysicsRigidBody*> _bodies;
     Vector3 _gravity;
-    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo> _collisionStatus;  
+    std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo> _collisionStatus;
+
 };
 
 }

+ 30 - 44
gameplay/src/PhysicsRigidBody.cpp

@@ -9,16 +9,16 @@ namespace gameplay
 {
 
 // Internal values used for creating mesh, heightfield, and capsule rigid bodies.
-#define SHAPE_MESH ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 1))
-#define SHAPE_HEIGHTFIELD ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 2))
-#define SHAPE_CAPSULE ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 3))
+#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::Type type, float mass, 
+PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::ShapeType type, float mass, 
     float friction, float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
+        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
         _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
         _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
 {
@@ -61,12 +61,12 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, floa
         _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
 
     // Add the rigid body to the physics world.
-    Game::getInstance()->getPhysicsController()->addRigidBody(this);
+    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), _listeners(NULL), _angularVelocity(NULL),
+        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
         _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
         _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
 {
@@ -139,7 +139,7 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, Image* image, float mass,
         _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
 
     // Add the rigid body to the physics world.
-    Game::getInstance()->getPhysicsController()->addRigidBody(this);
+    Game::getInstance()->getPhysicsController()->addCollisionObject(this);
 
     // Add the rigid body as a listener on the node's transform.
     _node->addListener(this);
@@ -147,7 +147,7 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, Image* image, float mass,
 
 PhysicsRigidBody::PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction,
     float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
+        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
         _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
         _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
 {
@@ -172,7 +172,7 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, float radius, float height, float
         _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
 
     // Add the rigid body to the physics world.
-    Game::getInstance()->getPhysicsController()->addRigidBody(this);
+    Game::getInstance()->getPhysicsController()->addCollisionObject(this);
 }
 
 PhysicsRigidBody::~PhysicsRigidBody()
@@ -186,17 +186,16 @@ PhysicsRigidBody::~PhysicsRigidBody()
         SAFE_DELETE(ptr);
     }
 
+    Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
+
     // Clean up the rigid body and its related objects.
     if (_body)
     {
         if (_body->getMotionState())
             delete _body->getMotionState();
-
-        Game::getInstance()->getPhysicsController()->removeRigidBody(this);
         SAFE_DELETE(_body);
     }
 
-    SAFE_DELETE(_listeners);
     SAFE_DELETE(_angularVelocity);
     SAFE_DELETE(_anisotropicFriction);
     SAFE_DELETE(_gravity);
@@ -210,9 +209,24 @@ PhysicsRigidBody::~PhysicsRigidBody()
     SAFE_DELETE(_inverse);
 }
 
-void PhysicsRigidBody::addCollisionListener(Listener* listener, PhysicsRigidBody* body)
+Node* PhysicsRigidBody::getNode() const
+{
+    return _node;
+}
+
+PhysicsCollisionObject::Type PhysicsRigidBody::getType() const
+{
+    return PhysicsCollisionObject::RIGID_BODY;
+}
+
+btCollisionObject* PhysicsRigidBody::getCollisionObject() const
+{
+    return _body;
+}
+
+btCollisionShape* PhysicsRigidBody::getCollisionShape() const
 {
-    Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, body);
+    return _shape;
 }
 
 void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
@@ -268,15 +282,6 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
     }
 }
 
-bool PhysicsRigidBody::collidesWith(PhysicsRigidBody* body)
-{
-    static CollidesWithCallback callback;
-
-    callback.result = false;
-    Game::getInstance()->getPhysicsController()->_world->contactPairTest(_body, body->_body, callback);
-    return callback.result;
-}
-
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, const char* filePath)
 {
     assert(filePath);
@@ -307,7 +312,7 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     }
 
     // Set values to their defaults.
-    PhysicsRigidBody::Type type = PhysicsRigidBody::SHAPE_NONE;
+    PhysicsRigidBody::ShapeType type = PhysicsRigidBody::SHAPE_NONE;
     float mass = 0.0;
     float friction = 0.5;
     float restitution = 0.0;
@@ -557,25 +562,6 @@ void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
     _inverseIsDirty = true;
 }
 
-PhysicsRigidBody::CollisionPair::CollisionPair(PhysicsRigidBody* rigidBodyA, PhysicsRigidBody* rigidBodyB)
-    : rigidBodyA(rigidBodyA), rigidBodyB(rigidBodyB)
-{
-    // Unused
-}
-
-PhysicsRigidBody::Listener::~Listener()
-{
-    // Unused
-}
-
-btScalar PhysicsRigidBody::CollidesWithCallback::addSingleResult(btManifoldPoint& cp, 
-                                                                 const btCollisionObject* a, int partIdA, int indexA, 
-                                                                 const btCollisionObject* b, int partIdB, int indexB)
-{
-    result = true;
-    return 0.0f;
-}
-
 float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
 {
     unsigned int x1 = x;

+ 21 - 96
gameplay/src/PhysicsRigidBody.h

@@ -5,6 +5,7 @@
 #include "PhysicsConstraint.h"
 #include "Transform.h"
 #include "Vector3.h"
+#include "PhysicsCollisionObject.h"
 
 namespace gameplay
 {
@@ -15,7 +16,7 @@ class PhysicsConstraint;
 /**
  * Defines a class for physics rigid bodies.
  */
-class PhysicsRigidBody : public Transform::Listener
+class PhysicsRigidBody : public PhysicsCollisionObject, public Transform::Listener
 {
     friend class Node;
     friend class PhysicsCharacter;
@@ -32,7 +33,7 @@ public:
     /**
      * Represents the different types of rigid bodies.
      */
-    enum Type
+    enum ShapeType
     {
         SHAPE_BOX,
         SHAPE_SPHERE,
@@ -40,81 +41,10 @@ public:
         SHAPE_MAX = 10
     };
 
-    /** 
-     * Defines a pair of rigid bodies that collided (or may collide).
-     */
-    class CollisionPair
-    {
-    public:
-
-        /**
-         * Constructor.
-         */
-        CollisionPair(PhysicsRigidBody* rigidBodyA, PhysicsRigidBody* rigidBodyB);
-
-        /**
-         * Less than operator (needed for use as a key in map).
-         * 
-         * @param collisionPair The collision pair to compare.
-         * @return True if this pair is "less than" the given pair; false otherwise.
-         */
-        bool operator<(const CollisionPair& collisionPair) const;
-
-        /** The first rigid body in the collision. */
-        PhysicsRigidBody* rigidBodyA;
-
-        /** The second rigid body in the collision. */
-        PhysicsRigidBody* rigidBodyB;
-    };
-
     /**
-     * Collision listener interface.
+     * @see PhysicsCollisionObject#getType
      */
-    class Listener
-    {
-        friend class PhysicsRigidBody;
-        friend class PhysicsController;
-
-    public:
-        /**
-         * The type of collision event.
-         */
-        enum EventType
-        {
-            /**
-             * Event fired when the two rigid bodies start colliding.
-             */
-            COLLIDING,
-
-            /**
-             * Event fired when the two rigid bodies no longer collide.
-             */
-            NOT_COLLIDING
-        };
-
-        /**
-         * Destructor.
-         */
-        virtual ~Listener();
-
-        /**
-         * Handles when a collision starts or stops occurring for the rigid body where this listener is registered.
-         * 
-         * @param type The type of collision event.
-         * @param collisionPair The two rigid bodies involved in the collision.
-         * @param contactPointA The contact point with the first rigid body (in world space).
-         * @param contactPointB The contact point with the second rigid body (in world space).
-         */
-        virtual void collisionEvent(EventType type, const CollisionPair& collisionPair, const Vector3& contactPointA = Vector3(), const Vector3& contactPointB = Vector3()) = 0;
-    };
-
-    /**
-     * Adds a collision listener for this rigid body.
-     * 
-     * @param listener The listener to add.
-     * @param body Specifies that only collisions with the given rigid body should trigger a notification.
-     */
-    void addCollisionListener(Listener* listener, PhysicsRigidBody* body = NULL);
+    PhysicsCollisionObject::Type getType() const;
 
     /**
      * Applies the given force to the rigid body (optionally, from the given relative position).
@@ -145,14 +75,6 @@ public:
      * @param torque The torque impulse to be applied.
      */
     void applyTorqueImpulse(const Vector3& torque);
-    
-    /**
-     * Checks if this rigid body collides with the given rigid body.
-     * 
-     * @param body The rigid body to test collision with.
-     * @return True if this rigid body collides with the given rigid body; false otherwise.
-     */
-    bool collidesWith(PhysicsRigidBody* body);
 
     /**
      * Gets the rigid body's angular damping.
@@ -217,8 +139,10 @@ public:
      * Gets the node that the rigid body is attached to.
      * 
      * @return The node.
+     *
+     * @see PhysicsCollisionObject::getNode.
      */
-    inline Node* getNode();
+    Node* getNode() const;
 
     /**
      * Gets the rigid body's restitution.
@@ -305,6 +229,18 @@ public:
      */
     inline void setRestitution(float restitution);
 
+protected:
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionObject
+     */
+    btCollisionObject* getCollisionObject() const;
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionShape
+     */
+    btCollisionShape* getCollisionShape() const;
+
 private:
 
     /**
@@ -320,7 +256,7 @@ private:
      * @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, PhysicsRigidBody::Type type, float mass, float friction = 0.5,
+    PhysicsRigidBody(Node* node, PhysicsRigidBody::ShapeType type, float mass, float friction = 0.5,
         float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
 
     /**
@@ -404,21 +340,10 @@ private:
     // Used for implementing getHeight() when the heightfield has a transform that can change.
     void transformChanged(Transform* transform, long cookie);
 
-    // 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;
-    };
-
     btCollisionShape* _shape;
     btRigidBody* _body;
     Node* _node;
     std::vector<PhysicsConstraint*> _constraints;
-    std::vector<Listener*>* _listeners;
     mutable Vector3* _angularVelocity;
     mutable Vector3* _anisotropicFriction;
     mutable Vector3* _gravity;

+ 0 - 22
gameplay/src/PhysicsRigidBody.inl

@@ -59,11 +59,6 @@ inline const Vector3& PhysicsRigidBody::getLinearVelocity() const
     return *_linearVelocity;
 }
 
-inline Node* PhysicsRigidBody::getNode()
-{
-    return _node;
-}
-
 inline float PhysicsRigidBody::getRestitution() const
 {
     return _body->getRestitution();
@@ -133,21 +128,4 @@ inline void PhysicsRigidBody::setRestitution(float restitution)
     _body->setRestitution(restitution);
 }
 
-inline bool PhysicsRigidBody::CollisionPair::operator<(const CollisionPair& collisionPair) const
-{
-    // If the pairs are equal, then return false.
-    if ((rigidBodyA == collisionPair.rigidBodyA && rigidBodyB == collisionPair.rigidBodyB) || (rigidBodyA == collisionPair.rigidBodyB && rigidBodyB == collisionPair.rigidBodyA))
-        return false;
-    else
-    {
-        // We choose to compare based on rigidBodyA arbitrarily.
-        if (rigidBodyA < collisionPair.rigidBodyA)
-            return true;
-        else if (rigidBodyA == collisionPair.rigidBodyA)
-            return rigidBodyB < collisionPair.rigidBodyB;
-        else
-            return false;
-    }
-}
-
 }

+ 1 - 1
gameplay/src/VertexAttributeBinding.cpp

@@ -195,7 +195,7 @@ VertexAttributeBinding* VertexAttributeBinding::create(Mesh* mesh, const VertexF
 
         if (attrib == -1)
         {
-            WARN_VARG("Warning: Vertex attribute not found for usage %d", (int)e.usage);
+            WARN_VARG("Warning: Vertex element with usage '%s' in mesh '%s' does not correspond to an attribute in effect '%s'.", VertexFormat::toString(e.usage), mesh->getUrl(), effect->getId());
         }
         else
         {

+ 39 - 0
gameplay/src/VertexFormat.cpp

@@ -80,4 +80,43 @@ bool VertexFormat::Element::operator != (const VertexFormat::Element& e) const
     return !(*this == e);
 }
 
+const char* VertexFormat::toString(Usage usage)
+{
+    switch (usage)
+    {
+    case POSITION:
+        return "POSITION";
+    case NORMAL:
+        return "NORMAL";
+    case COLOR:
+        return "COLOR";
+    case TANGENT:
+        return "TANGENT";
+    case BINORMAL:
+        return "BINORMAL";
+    case BLENDWEIGHTS:
+        return "BLENDWEIGHTS";
+    case BLENDINDICES:
+        return "BLENDINDICES";
+    case TEXCOORD0:
+        return "TEXCOORD0";
+    case TEXCOORD1:
+        return "TEXCOORD1";
+    case TEXCOORD2:
+        return "TEXCOORD2";
+    case TEXCOORD3:
+        return "TEXCOORD3";
+    case TEXCOORD4:
+        return "TEXCOORD4";
+    case TEXCOORD5:
+        return "TEXCOORD5";
+    case TEXCOORD6:
+        return "TEXCOORD6";
+    case TEXCOORD7:
+        return "TEXCOORD7";
+    default:
+        return "UNKNOWN";
+    }
+}
+
 }

+ 5 - 0
gameplay/src/VertexFormat.h

@@ -141,6 +141,11 @@ public:
      */
     bool operator != (const VertexFormat& f) const;
 
+    /**
+     * Returns a string representation of a Usage enumeration value.
+     */
+    static const char* toString(Usage usage);
+
 private:
 
     std::vector<Element> _elements;

+ 1 - 1
gameplay/src/gameplay.h

@@ -66,10 +66,10 @@
 #include "PhysicsHingeConstraint.h"
 #include "PhysicsSocketConstraint.h"
 #include "PhysicsSpringConstraint.h"
+#include "PhysicsCollisionObject.h"
 #include "PhysicsRigidBody.h"
 #include "PhysicsCharacter.h"
 
-
 // UI
 #include "Theme.h"
 #include "Control.h"