Просмотр исходного кода

Added new PhysicsCollisionObject base class for PhysicsRigidBody and PhysicsCharacter.cpp.
Added support for collision listeners on PhysicsCharacter.
Modified all collision related APIs to accept PhysicsCollisionObject instead of PhysicsRigidBody.

Steve Grenier 14 лет назад
Родитель
Сommit
d4bc602ca9

+ 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>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -658,4 +664,4 @@
       <Filter>src</Filter>
     </None>
   </ItemGroup>
-</Project>
+</Project>

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

@@ -29,9 +29,7 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-	float ddot = abs(dot(normalVector, lightDirection));
-	if (ddot < 0)
-		ddot = abs(ddot) * 0.25f; // simulate light bounce at partial intensity
+    float ddot = abs(dot(normalVector, lightDirection));
     float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;

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

+ 1 - 3
gameplay/res/shaders/diffuse-specular.fsh

@@ -25,9 +25,7 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-	float ddot = dot(normalVector, lightDirection);
-	if (ddot < 0)
-		ddot = abs(ddot) * 0.25f; // simulate light bounce at partial intensity
+    float ddot = dot(normalVector, lightDirection);
     float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;

+ 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,37 +86,53 @@ 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);
 }
 
+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;
@@ -339,19 +365,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();
@@ -425,12 +454,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)
@@ -439,6 +462,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;
 
@@ -578,7 +607,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();
@@ -590,10 +619,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)
@@ -602,11 +629,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;
-		}
+		//}
 	}
 }
 
@@ -639,8 +666,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)
 			{
@@ -651,6 +678,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
@@ -659,7 +687,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;
 				}
 			}

+ 22 - 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,6 +51,11 @@ public:
          ANIMATION_REPEAT
     };
 
+    /**
+     * @see PhysicsCollisionObject#getType
+     */
+    PhysicsCollisionObject::Type getType() const;
+
     /**
      * Returns the character node for this PhysicsCharacter.
      *
@@ -247,6 +250,18 @@ public:
      */
 	void debugDraw(btIDebugDraw* debugDrawer);
 
+protected:
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionObject
+     */
+    btCollisionObject* getCollisionObject() const;
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionShape
+     */
+    btCollisionShape* getCollisionShape() const;
+
 private:
 
     struct CharacterAnimation
@@ -264,6 +279,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 +290,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;
+}
+
+}

+ 181 - 0
gameplay/src/PhysicsCollisionObject.h

@@ -0,0 +1,181 @@
+#ifndef PHYSICSCOLLISIONOBJECT_H_
+#define PHYSICSCOLLISIONOBJECT_H_
+
+#include "Vector3.h"
+
+namespace gameplay
+{
+
+/**
+ * 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;
+
+    /**
+     * 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

@@ -140,7 +140,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);
@@ -152,7 +152,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;
@@ -162,65 +162,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()));
             }
         }
@@ -229,8 +221,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;
 }
 
@@ -324,7 +316,6 @@ void PhysicsController::update(long elapsedTime)
                 (*_listeners)[k]->statusEvent(_status);
             }
         }
-        
     }
 
     // All statuses are set with the DIRTY bit before collision processing occurs.
@@ -360,10 +351,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);
         }
     }
 
@@ -373,12 +364,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);
                 }
             }
 
@@ -387,85 +378,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)
@@ -837,4 +846,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);
@@ -370,7 +376,7 @@ private:
         const Matrix* _viewProjection;
         MeshBatch* _meshBatch;
     };
-    
+
     btDefaultCollisionConfiguration* _collisionConfiguration;
     btCollisionDispatcher* _dispatcher;
     btBroadphaseInterface* _overlappingPairCache;
@@ -381,9 +387,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;
+
 };
 
 }

+ 33 - 45
gameplay/src/PhysicsRigidBody.cpp

@@ -11,16 +11,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)
 {
@@ -63,12 +63,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)
 {
@@ -142,7 +142,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);
@@ -150,7 +150,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)
 {
@@ -175,7 +175,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()
@@ -189,17 +189,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);
@@ -209,13 +208,30 @@ PhysicsRigidBody::~PhysicsRigidBody()
     {
         SAFE_DELETE_ARRAY(_indexData[i]);
     }
-    SAFE_DELETE_ARRAY(_heightfieldData);
     SAFE_DELETE(_inverse);
+
+    // Special case: For heightfield rigid bodies, we need to delete the collision
+    // shape here since it is not stored and managed by the PhysicsController.
+    if (_heightfieldData)
+    {
+        SAFE_DELETE(_shape);
+        SAFE_DELETE_ARRAY(_heightfieldData);
+    }
+}
+
+PhysicsCollisionObject::Type PhysicsRigidBody::getType() const
+{
+    return PhysicsCollisionObject::RIGID_BODY;
+}
+
+btCollisionObject* PhysicsRigidBody::getCollisionObject() const
+{
+    return _body;
 }
 
-void PhysicsRigidBody::addCollisionListener(Listener* listener, PhysicsRigidBody* body)
+btCollisionShape* PhysicsRigidBody::getCollisionShape() const
 {
-    Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, body);
+    return _shape;
 }
 
 void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
@@ -271,15 +287,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);
@@ -310,7 +317,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;
@@ -560,25 +567,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;

+ 18 - 95
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.
@@ -305,6 +227,18 @@ public:
      */
     inline void setRestitution(float restitution);
 
+protected:
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionObject
+     */
+    btCollisionObject* getCollisionObject() const;
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionShape
+     */
+    btCollisionShape* getCollisionShape() const;
+
 private:
 
     /**
@@ -320,7 +254,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 +338,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 - 17
gameplay/src/PhysicsRigidBody.inl

@@ -133,21 +133,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"