Sfoglia il codice sorgente

Adds physics (world) status listeners and rigid body collision listeners.
Updates the sandbox sample to include sample code on using the listeners.
Removes triangle mesh and heightfield collision shape stub code.
Adds some missing const qualifiers (in Model, Node, PhysicsConstraint).
Adds getNode on PhysicsRigidBody.
Adds lazy caching of constraint and rigid body attributes that have object type (i.e. Vector3 or Quaternion).
Fixes all physics TODOs.

Chris Culy 14 anni fa
parent
commit
29541dd3a9

+ 1 - 1
gameplay/src/Model.cpp

@@ -42,7 +42,7 @@ Model* Model::create(Mesh* mesh)
     return new Model(mesh);
 }
 
-Mesh* Model::getMesh()
+Mesh* Model::getMesh() const
 {
     return _mesh;
 }

+ 1 - 1
gameplay/src/Model.h

@@ -38,7 +38,7 @@ public:
      * 
      * @return The Mesh for this Model.
      */
-    Mesh* getMesh();
+    Mesh* getMesh() const;
 
     /**
      * Returns the number of parts in the Mesh for this Model.

+ 1 - 1
gameplay/src/Node.cpp

@@ -789,7 +789,7 @@ void Node::setParticleEmitter(ParticleEmitter* emitter)
     }
 }
 
-PhysicsRigidBody* Node::getPhysicsRigidBody()
+PhysicsRigidBody* Node::getPhysicsRigidBody() const
 {
     return _physicsRigidBody;
 }

+ 1 - 1
gameplay/src/Node.h

@@ -362,7 +362,7 @@ public:
      *
      * @return The pointer to this node's physics rigid body or NULL.
      */
-    PhysicsRigidBody* getPhysicsRigidBody();
+    PhysicsRigidBody* getPhysicsRigidBody() const;
 
     /**
      * Assigns a physics rigid body to this node.

+ 12 - 8
gameplay/src/PhysicsConstraint.cpp

@@ -30,7 +30,7 @@ PhysicsConstraint::~PhysicsConstraint()
     SAFE_DELETE(_constraint);
 }
 
-Vector3 PhysicsConstraint::centerOfMassMidpoint(Node* a, Node* b)
+Vector3 PhysicsConstraint::centerOfMassMidpoint(const Node* a, const Node* b)
 {
     Vector3 tA, tB;
     a->getWorldMatrix().getTranslation(&tA);
@@ -47,7 +47,7 @@ Vector3 PhysicsConstraint::centerOfMassMidpoint(Node* a, Node* b)
     return c;
 }
 
-Quaternion PhysicsConstraint::getRotationOffset(Node* node, const Vector3& point)
+Quaternion PhysicsConstraint::getRotationOffset(const Node* node, const Vector3& point)
 {
     // Create a translation matrix that translates to the given origin.
     Matrix m;
@@ -66,7 +66,7 @@ Quaternion PhysicsConstraint::getRotationOffset(Node* node, const Vector3& point
     return r;
 }
 
-Vector3 PhysicsConstraint::getTranslationOffset(Node* node, const Vector3& point)
+Vector3 PhysicsConstraint::getTranslationOffset(const Node* node, const Vector3& point)
 {
     // Create a translation matrix that translates to the given origin.
     Matrix m;
@@ -94,7 +94,7 @@ Vector3 PhysicsConstraint::getTranslationOffset(Node* node, const Vector3& point
     return t;
 }
 
-btTransform PhysicsConstraint::getTransformOffset(Node* node, const Vector3& origin)
+btTransform PhysicsConstraint::getTransformOffset(const Node* node, const Vector3& origin)
 {
     // Create a translation matrix that translates to the given origin.
     Matrix m;
@@ -125,8 +125,7 @@ btTransform PhysicsConstraint::getTransformOffset(Node* node, const Vector3& ori
     return btTransform(btQuaternion(r.x, r.y, r.z, r.w), btVector3(t.x, t.y, t.z));
 }
 
-// TODO!!!
-Vector3 PhysicsConstraint::getWorldCenterOfMass(Model* model)
+Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 {
     Vector3 center;
 
@@ -147,13 +146,18 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(Model* model)
         {
             model->getNode()->getWorldMatrix().transformPoint(sphere.center, &center);
         }
+        else
+        {
+            // Warn the user that the model has no bounding volume.
+            WARN_VARG("Model \'%s\' has no bounding volume - center of mass is defaulting to local coordinate origin.", model->getNode()->getId());
+            model->getNode()->getWorldMatrix().transformPoint(&center);
+        }
     }
 
-    // TODO: This case needs to be fixed (maybe return an error somehow?).
     return center;
 }
 
-Vector3 PhysicsConstraint::offsetByCenterOfMass(Node* node, const Vector3& v)
+Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 {
     btVector3 centerOfMassOffset = ((PhysicsMotionState*)node->getPhysicsRigidBody()->_body->getMotionState())->_centerOfMassOffset.getOrigin();
     return Vector3(v.x + centerOfMassOffset.x(), v.y + centerOfMassOffset.y(), v.z + centerOfMassOffset.z());

+ 6 - 6
gameplay/src/PhysicsConstraint.h

@@ -60,7 +60,7 @@ public:
      * @param a The first node.
      * @param b The second node.
      */
-    static Vector3 centerOfMassMidpoint(Node* a, Node* b);
+    static Vector3 centerOfMassMidpoint(const Node* a, const Node* b);
 
     /**
      * Calculates the rotation offset to the given point in the given node's local space.
@@ -68,7 +68,7 @@ public:
      * @param node The node to calculate a rotation offset for.
      * @param point The point to calculate the rotation offset to.
      */
-    static Quaternion getRotationOffset(Node* node, const Vector3& point);
+    static Quaternion getRotationOffset(const Node* node, const Vector3& point);
 
     /**
      * Calculates the translation offset to the given point in the given node's local space.
@@ -76,7 +76,7 @@ public:
      * @param node The node to calculate a translation offset for.
      * @param point The point to calculate the translation offset to.
      */
-    static Vector3 getTranslationOffset(Node* node, const Vector3& point);
+    static Vector3 getTranslationOffset(const Node* node, const Vector3& point);
 
 protected:
     /**
@@ -93,17 +93,17 @@ protected:
      * Calculates the transform to be used as the offset (i.e. "frame in" 
      * parameter in Bullet terms) to the given constraint origin.
      */
-    static btTransform getTransformOffset(Node* node, const Vector3& origin);
+    static btTransform getTransformOffset(const Node* node, const Vector3& origin);
     
     /**
      * Calculates the center of mass in world space of the given model.
      */
-    static Vector3 getWorldCenterOfMass(Model* model);
+    static Vector3 getWorldCenterOfMass(const Model* model);
 
     /**
      * Offsets the given vector by the given node's center of mass.
      */
-    static Vector3 offsetByCenterOfMass(Node* node, const Vector3& v);
+    static Vector3 offsetByCenterOfMass(const Node* node, const Vector3& v);
 
     PhysicsRigidBody* _a;
     PhysicsRigidBody* _b;

+ 105 - 28
gameplay/src/PhysicsController.cpp

@@ -3,6 +3,7 @@
  */
 
 #include "Base.h"
+#include "Game.h"
 #include "PhysicsController.h"
 #include "PhysicsMotionState.h"
 
@@ -12,24 +13,21 @@ namespace gameplay
 // Default gravity is 9.8 along the negative Y axis.
 PhysicsController::PhysicsController()
     : _gravity(btScalar(0.0), btScalar(-9.8), btScalar(0.0)), _collisionConfiguration(NULL), _dispatcher(NULL),
-    _overlappingPairCache(NULL), _solver(NULL), _world(NULL)
+    _overlappingPairCache(NULL), _solver(NULL), _world(NULL), _status(Listener::DEACTIVATED), _listeners(NULL)
 {
 }
 
-PhysicsController::~PhysicsController()
+void PhysicsController::addStatusListener(Listener* listener)
 {
+    if (!_listeners)
+        _listeners = new std::vector<Listener*>();
+
+    _listeners->push_back(listener);
 }
 
-void PhysicsController::setGravity(const Vector3& gravity)
+PhysicsController::~PhysicsController()
 {
-    _gravity.setX(gravity.x);
-    _gravity.setY(gravity.y);
-    _gravity.setZ(gravity.z);
-
-    if (_world)
-    {
-        _world->setGravity(_gravity);
-    }
+    SAFE_DELETE(_listeners);
 }
 
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
@@ -99,9 +97,23 @@ PhysicsSpringConstraint* PhysicsController::createSpringConstraint(PhysicsRigidB
     return constraint;
 }
 
+const Vector3& PhysicsController::getGravity(const Vector3& gravity) const
+{
+    return _gravity;
+}
+
+void PhysicsController::setGravity(const Vector3& gravity)
+{
+    _gravity = gravity;
+
+    if (_world)
+    {
+        _world->setGravity(btVector3(_gravity.x, _gravity.y, _gravity.z));
+    }
+}
+
 void PhysicsController::initialize()
 {
-    // TODO: Should any of this be configurable?
     _collisionConfiguration = new btDefaultCollisionConfiguration();
     _dispatcher = new btCollisionDispatcher(_collisionConfiguration);
     _overlappingPairCache = new btDbvtBroadphase();
@@ -109,7 +121,7 @@ void PhysicsController::initialize()
 
     // Create the world.
     _world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
-    _world->setGravity(_gravity);
+    _world->setGravity(btVector3(_gravity.x, _gravity.y, _gravity.z));
 }
 
 void PhysicsController::finalize()
@@ -140,6 +152,69 @@ void PhysicsController::update(long elapsedTime)
     // Note that stepSimulation takes elapsed time in seconds
     // so we divide by 1000 to convert from milliseconds.
     _world->stepSimulation((float)elapsedTime * 0.001, 10);
+
+    // If we have status listeners, then check if our status has changed.
+    if (_listeners)
+    {
+        Listener::EventType oldStatus = _status;
+
+        if (_status = Listener::DEACTIVATED)
+        {
+            for (int i = 0; i < _world->getNumCollisionObjects(); i++)
+            {
+                if (_world->getCollisionObjectArray()[i]->isActive())
+                {
+                    _status = Listener::ACTIVATED;
+                    break;
+                }
+            }
+        }
+        else
+        {
+            bool allInactive = true;
+            for (int i = 0; i < _world->getNumCollisionObjects(); i++)
+            {
+                if (_world->getCollisionObjectArray()[i]->isActive())
+                {
+                    allInactive = false;
+                    break;
+                }
+            }
+
+            if (allInactive)
+                _status = Listener::DEACTIVATED;
+        }
+
+        // If the status has changed, notify our listeners.
+        if (oldStatus != _status)
+        {
+            for (unsigned int k = 0; k < _listeners->size(); k++)
+            {
+                (*_listeners)[k]->statusEvent(_status);
+            }
+        }
+    }
+
+    // Go through the physics rigid bodies and update the collision listeners.
+    for (unsigned int i = 0; i < _bodies.size(); i++)
+    {
+        if (_bodies[i]->_listeners)
+        {
+            for (unsigned int k = 0; k < _bodies[i]->_listeners->size(); k++)
+            {
+                if ((*_bodies[i]->_listeners)[k]->_rbB)
+                    Game::getInstance()->getPhysicsController()->_world->contactPairTest((*_bodies[i]->_listeners)[k]->_rbA->_body, (*_bodies[i]->_listeners)[k]->_rbB->_body, *(*_bodies[i]->_listeners)[k]);
+                else
+                    Game::getInstance()->getPhysicsController()->_world->contactTest((*_bodies[i]->_listeners)[k]->_rbA->_body, *(*_bodies[i]->_listeners)[k]);
+            }
+        }
+    }
+}
+
+void PhysicsController::addRigidBody(PhysicsRigidBody* body)
+{
+    _world->addRigidBody(body->_body);
+    _bodies.push_back(body);
 }
 
 btCollisionShape* PhysicsController::getBox(const Vector3& min, const Vector3& max, const btVector3& scale)
@@ -167,25 +242,16 @@ btCollisionShape* PhysicsController::getSphere(float radius, const btVector3& sc
     return sphere;
 }
 
-btCollisionShape* PhysicsController::getTriangleMesh(float* vertexData, int vertexPositionStride, unsigned char* indexData, Mesh::IndexFormat indexFormat)
-{
-    return NULL;
-}
-
-btCollisionShape* PhysicsController::getHeightfield(void* data, int width, int height)
-{
-    return NULL;
-}
-
-void PhysicsController::setupConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
+PhysicsRigidBody* PhysicsController::getPhysicsRigidBody(const btCollisionObject* collisionObject)
 {
-    a->addConstraint(constraint);
-    if (b)
+    // Find the rigid body and remove it from the world.
+    for (unsigned int i = 0; i < _bodies.size(); i++)
     {
-        b->addConstraint(constraint);
+        if (_bodies[i]->_body == collisionObject)
+            return _bodies[i];
     }
 
-    _world->addConstraint(constraint->_constraint);
+    return NULL;
 }
 
 void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
@@ -216,4 +282,15 @@ void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
     }
 }
 
+void PhysicsController::setupConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
+{
+    a->addConstraint(constraint);
+    if (b)
+    {
+        b->addConstraint(constraint);
+    }
+
+    _world->addConstraint(constraint->_constraint);
+}
+
 }

+ 59 - 13
gameplay/src/PhysicsController.h

@@ -27,11 +27,40 @@ class PhysicsController
 
 public:
     /**
-     * Sets the gravity vector for the simulated physics world.
+     * Status listener interface.
+     */
+    class Listener
+    {
+    public:
+
+        /**
+         * The type of physics status event.
+         */
+        enum EventType 
+        {
+            /**
+             * Event fired when there were no active physics objects and at least one is now active.
+             */
+            ACTIVATED,
+
+            /**
+             * Event fired when there are no more active physics objects in the world.
+             */
+            DEACTIVATED
+        };
+
+        /**
+         * Handles when the physics world status changes.
+         */
+        virtual void statusEvent(EventType type) = 0;
+    };
+
+    /**
+     * Adds a status listener.
      * 
-     * @param gravity The gravity vector.
+     * @param listener The listener to add.
      */
-    void setGravity(const Vector3& gravity);
+    void addStatusListener(Listener* listener);
 
     /**
      * Creates a fixed constraint.
@@ -144,6 +173,20 @@ public:
     PhysicsSpringConstraint* createSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
         const Vector3& translationOffsetA, PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB);
 
+    /**
+     * Gets the gravity vector for the simulated physics world.
+     * 
+     * @return The gravity vector.
+     */
+    const Vector3& getGravity(const Vector3& gravity) const;
+
+    /**
+     * Sets the gravity vector for the simulated physics world.
+     * 
+     * @param gravity The gravity vector.
+     */
+    void setGravity(const Vector3& gravity);
+
 private:
     /**
      * Constructor.
@@ -180,34 +223,37 @@ private:
      */
     void update(long elapsedTime);
 
+    // Adds the given rigid body to the world.
+    void addRigidBody(PhysicsRigidBody* body);
+
     // Creates a box collision shape to be used in the creation of a rigid body.
     btCollisionShape* getBox(const Vector3& min, const Vector3& max, const btVector3& scale);
 
+    // Gets the corresponding GamePlay object for the given Bullet object.
+    PhysicsRigidBody* getPhysicsRigidBody(const btCollisionObject* collisionObject);
+
     // Creates a sphere collision shape to be used in the creation of a rigid body.
     btCollisionShape* getSphere(float radius, const btVector3& scale);
 
-    // Creates a triangle mesh collision shape to be used in the creation of a rigid body.
-    btCollisionShape* getTriangleMesh(float* vertexData, int vertexPositionStride, unsigned char* indexData, Mesh::IndexFormat indexFormat);
-
-    // Creates a heightfield collision shape to be used in the creation of a rigid body.
-    btCollisionShape* getHeightfield(void* data, int width, int height);
-    
-    // Sets up the given constraint for the given two rigid bodies.
-    void setupConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint);
-    
     // Removes the given constraint from the simulated physics world.
     void removeConstraint(PhysicsConstraint* constraint);
 
     // Removes the given rigid body from the simulated physics world.
     void removeRigidBody(PhysicsRigidBody* rigidBody);
 
-    btVector3 _gravity;
+    // Sets up the given constraint for the given two rigid bodies.
+    void setupConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint);
+    
+    Vector3 _gravity;
     btDefaultCollisionConfiguration* _collisionConfiguration;
     btCollisionDispatcher* _dispatcher;
     btBroadphaseInterface* _overlappingPairCache;
     btSequentialImpulseConstraintSolver* _solver;
     btDynamicsWorld* _world;
     btAlignedObjectArray<btCollisionShape*> _shapes;
+    Listener::EventType _status;
+    std::vector<PhysicsRigidBody*> _bodies;
+    std::vector<Listener*>* _listeners;
 };
 
 }

+ 13 - 8
gameplay/src/PhysicsGenericConstraint.cpp

@@ -12,18 +12,20 @@ namespace gameplay
 {
 
 PhysicsGenericConstraint::PhysicsGenericConstraint()
-    : PhysicsConstraint(NULL, NULL)
+    : PhysicsConstraint(NULL, NULL), _rotationOffsetA(NULL), _rotationOffsetB(NULL),
+    _translationOffsetA(NULL), _translationOffsetB(NULL)
 {
     // DUMMY FUNCTION
 }
 
 PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
-    : PhysicsConstraint(a, b)
+    : PhysicsConstraint(a, b), _rotationOffsetA(NULL), _rotationOffsetB(NULL),
+    _translationOffsetA(NULL), _translationOffsetB(NULL)
 {
     if (b)
     {
-        Vector3 origin = centerOfMassMidpoint(a->_node, b->_node);
-        _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, getTransformOffset(a->_node, origin), getTransformOffset(b->_node, origin), true);
+        Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
+        _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
     }
     else
     {
@@ -33,18 +35,18 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, PhysicsR
 
 PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
     PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
-    : PhysicsConstraint(a, b)
+    : PhysicsConstraint(a, b), _rotationOffsetA(NULL), _rotationOffsetB(NULL), _translationOffsetA(NULL), _translationOffsetB(NULL)
 {
     // Take scale into account for the first node's translation offset.
     Vector3 sA;
-    a->_node->getWorldMatrix().getScale(&sA);
+    a->getNode()->getWorldMatrix().getScale(&sA);
     Vector3 tA(translationOffsetA.x * sA.x, translationOffsetA.y * sA.y, translationOffsetA.z * sA.z);
 
     if (b)
     {
         // Take scale into account for the second node's translation offset.
         Vector3 sB;
-        b->_node->getWorldMatrix().getScale(&sB);
+        b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
         btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));
@@ -60,7 +62,10 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, const Qu
 
 PhysicsGenericConstraint::~PhysicsGenericConstraint()
 {
-    // DUMMY FUNCTION
+    SAFE_DELETE(_rotationOffsetA);
+    SAFE_DELETE(_rotationOffsetB);
+    SAFE_DELETE(_translationOffsetA);
+    SAFE_DELETE(_translationOffsetB);
 }
 
 }

+ 10 - 4
gameplay/src/PhysicsGenericConstraint.h

@@ -28,28 +28,28 @@ public:
      * 
      * @return The rotation offset.
      */
-    inline Quaternion getRotationOffsetA();
+    inline const Quaternion& getRotationOffsetA() const;
 
     /**
      * Gets the rotation offset for the second rigid body in the constraint.
      * 
      * @return The rotation offset.
      */
-    inline Quaternion getRotationOffsetB();
+    inline const Quaternion& getRotationOffsetB() const;
 
     /**
      * Gets the translation offset for the first rigid body in the constraint.
      * 
      * @return The translation offset.
      */
-    inline Vector3 getTranslationOffsetA();
+    inline const Vector3& getTranslationOffsetA() const;
 
     /**
      * Gets the translation offset for the second rigid body in the constraint.
      * 
      * @return The translation offset.
      */
-    inline Vector3 getTranslationOffsetB();
+    inline const Vector3& getTranslationOffsetB() const;
 
     /**
      * Sets the lower angular limits along the constraint's local
@@ -152,6 +152,12 @@ protected:
      * Destructor.
      */
     virtual ~PhysicsGenericConstraint();
+
+private:
+    mutable Quaternion* _rotationOffsetA;
+    mutable Quaternion* _rotationOffsetB;
+    mutable Vector3* _translationOffsetA;
+    mutable Vector3* _translationOffsetB;
 };
 
 }

+ 24 - 8
gameplay/src/PhysicsGenericConstraint.inl

@@ -7,28 +7,44 @@
 namespace gameplay
 {
 
-inline Quaternion PhysicsGenericConstraint::getRotationOffsetA()
+inline const Quaternion& PhysicsGenericConstraint::getRotationOffsetA() const
 {
+    if (!_rotationOffsetA)
+        _rotationOffsetA = new Quaternion();
+
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getRotation();
-    return Quaternion(ro.x(), ro.y(), ro.z(), ro.w());
+    _rotationOffsetA->set(ro.x(), ro.y(), ro.z(), ro.w());
+    return *_rotationOffsetA;
 }
 
-inline Quaternion PhysicsGenericConstraint::getRotationOffsetB()
+inline const Quaternion& PhysicsGenericConstraint::getRotationOffsetB() const
 {
+    if (!_rotationOffsetB)
+        _rotationOffsetB = new Quaternion();
+
     btQuaternion ro = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getRotation();
-    return Quaternion(ro.x(), ro.y(), ro.z(), ro.w());
+    _rotationOffsetB->set(ro.x(), ro.y(), ro.z(), ro.w());
+    return *_rotationOffsetB;
 }
 
-inline Vector3 PhysicsGenericConstraint::getTranslationOffsetA()
+inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetA() const
 {
+    if (!_translationOffsetA)
+        _translationOffsetA = new Vector3();
+
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().getOrigin();
-    return Vector3(to.x(), to.y(), to.z());
+    _translationOffsetA->set(to.x(), to.y(), to.z());
+    return *_translationOffsetA;
 }
 
-inline Vector3 PhysicsGenericConstraint::getTranslationOffsetB()
+inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetB() const
 {
+    if (!_translationOffsetB)
+        _translationOffsetB = new Vector3();
+
     btVector3 to = static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().getOrigin();
-    return Vector3(to.x(), to.y(), to.z());
+    _translationOffsetB->set(to.x(), to.y(), to.z());
+    return *_translationOffsetB;
 }
 
 inline void PhysicsGenericConstraint::setAngularLowerLimit(const Vector3& limits)

+ 2 - 2
gameplay/src/PhysicsHingeConstraint.cpp

@@ -20,14 +20,14 @@ PhysicsHingeConstraint::PhysicsHingeConstraint(PhysicsRigidBody* a, const Quater
 {
     // Take scale into account for the first node's translation offset.
     Vector3 sA;
-    a->_node->getWorldMatrix().getScale(&sA);
+    a->getNode()->getWorldMatrix().getScale(&sA);
     Vector3 tA(translationOffsetA.x * sA.x, translationOffsetA.y * sA.y, translationOffsetA.z * sA.z);
 
     if (b)
     {
         // Take scale into account for the second node's translation offset.
         Vector3 sB;
-        b->_node->getWorldMatrix().getScale(&sB);
+        b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
         btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));

+ 43 - 20
gameplay/src/PhysicsRigidBody.cpp

@@ -13,7 +13,8 @@ namespace gameplay
 
 PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass, 
         float friction, float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node)
+        : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
+        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL)
 {
     switch (type)
     {
@@ -55,22 +56,10 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, floa
 
             break;
         }
-        case PhysicsRigidBody::SHAPE_TRIANGLE_MESH:
-        {
-            //btTriangleIndexVertexArray meshShape(numTriangles, indexPointer, indexStride, numVertices, vertexPointer, vertexStride);
-            //_shape = btBvhTriangleMeshShape(meshShape, true);
-
-            //_body = createBulletRigidBody(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-            break;
-        }
-        case PhysicsRigidBody::SHAPE_HEIGHTFIELD:
-        {
-            //_shape = btHeightfieldTerrainShape(width, length, data, scale, minHeight, maxHeight, upAxis, dataType, false);
-
-            //_body = createBulletRigidBody(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-            break;
-        }
     }
+
+    // Add the rigid body to the physics world.
+    Game::getInstance()->getPhysicsController()->addRigidBody(this);
 }
 
 PhysicsRigidBody::~PhysicsRigidBody()
@@ -96,6 +85,24 @@ PhysicsRigidBody::~PhysicsRigidBody()
         Game::getInstance()->getPhysicsController()->removeRigidBody(this);
         SAFE_DELETE(_body);
     }
+
+    SAFE_DELETE(_listeners);
+    SAFE_DELETE(_angularVelocity);
+    SAFE_DELETE(_anisotropicFriction);
+    SAFE_DELETE(_gravity);
+    SAFE_DELETE(_linearVelocity);
+}
+
+void PhysicsRigidBody::addCollisionListener(Listener* listener, PhysicsRigidBody* body)
+{
+    if (!_listeners)
+        _listeners = new std::vector<Listener*>();
+
+    listener->_rbA = this;
+    if (body)
+        listener->_rbB = body;
+
+    _listeners->push_back(listener);
 }
 
 void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
@@ -169,10 +176,6 @@ btRigidBody* PhysicsRigidBody::createBulletRigidBody(btCollisionShape* shape, fl
     rbInfo.m_angularDamping = angularDamping;
     btRigidBody* body = new btRigidBody(rbInfo);
 
-    // Add the rigid body to the physics world.
-    PhysicsController* physics = Game::getInstance()->getPhysicsController();
-    physics->_world->addRigidBody(body);
-
     return body;
 }
 
@@ -193,4 +196,24 @@ void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
     }
 }
 
+PhysicsRigidBody::Listener::Listener()
+    : _rbA(NULL), _rbB(NULL)
+{
+    // DUMMY FUNCTION
+}
+
+btScalar PhysicsRigidBody::Listener::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a,
+    int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB)
+{
+    PhysicsRigidBody* rbA = Game::getInstance()->getPhysicsController()->getPhysicsRigidBody(a);
+    PhysicsRigidBody* rbB = Game::getInstance()->getPhysicsController()->getPhysicsRigidBody(b);
+
+    if (rbA == _rbA)
+        collisionEvent(rbB, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()));
+    else
+        collisionEvent(rbA, Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
+
+    return 0.0f;
+}
+
 }

+ 57 - 6
gameplay/src/PhysicsRigidBody.h

@@ -39,11 +39,50 @@ public:
     {
         SHAPE_BOX,
         SHAPE_SPHERE,
-        SHAPE_TRIANGLE_MESH,
-        SHAPE_HEIGHTFIELD,
         SHAPE_NONE
     };
 
+    /**
+     * Collision listener interface.
+     */
+    class Listener : public btCollisionWorld::ContactResultCallback
+    {
+        friend class PhysicsRigidBody;
+        friend class PhysicsController;
+
+    public:
+        /**
+         * Constructor.
+         */
+        Listener();
+
+        /**
+         * Handles when a collision occurs for the rigid body where this listener is registered.
+         * 
+         * @param body The other rigid body in the collision.
+         * @param contactPoint The point (in world space) where the collision occurred.
+         */
+        virtual void collisionEvent(PhysicsRigidBody* body, const Vector3& contactPoint) = 0;
+
+        /**
+         * Internal function used for Bullet integration (do not use or override).
+         */
+        btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA,
+            int indexA, const btCollisionObject* b, int partIdB, int indexB);
+        
+    protected:
+        PhysicsRigidBody* _rbA;
+        PhysicsRigidBody* _rbB;
+    };
+
+    /**
+     * 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);
+
     /**
      * Applies the given force to the rigid body (optionally, from the given relative position).
      * 
@@ -86,14 +125,14 @@ public:
      * 
      * @return The angular velocity.
      */
-    inline Vector3 getAngularVelocity() const;
+    inline const Vector3& getAngularVelocity() const;
 
     /**
      * Gets the rigid body's anisotropic friction.
      * 
      * @return The anisotropic friction.
      */
-    inline Vector3 getAnisotropicFriction() const;
+    inline const Vector3& getAnisotropicFriction() const;
 
     /**
      * Gets the rigid body's friction.
@@ -108,7 +147,7 @@ public:
      * 
      * @return The gravity.
      */
-    inline Vector3 getGravity() const;
+    inline const Vector3& getGravity() const;
 
     /**
      * Gets the rigid body's linear damping.
@@ -122,7 +161,14 @@ public:
      * 
      * @return The linear velocity.
      */
-    inline Vector3 getLinearVelocity() const;
+    inline const Vector3& getLinearVelocity() const;
+
+    /**
+     * Gets the node that the rigid body is attached to.
+     * 
+     * @return The node.
+     */
+    inline const Node* getNode() const;
 
     /**
      * Gets the rigid body's restitution.
@@ -238,6 +284,11 @@ private:
     btRigidBody* _body;
     Node* _node;
     std::vector<PhysicsConstraint*> _constraints;
+    std::vector<Listener*>* _listeners;
+    mutable Vector3* _angularVelocity;
+    mutable Vector3* _anisotropicFriction;
+    mutable Vector3* _gravity;
+    mutable Vector3* _linearVelocity;
 };
 
 }

+ 29 - 8
gameplay/src/PhysicsRigidBody.inl

@@ -15,16 +15,24 @@ inline float PhysicsRigidBody::getAngularDamping() const
     return _body->getAngularDamping();
 }
 
-inline Vector3 PhysicsRigidBody::getAngularVelocity() const
+inline const Vector3& PhysicsRigidBody::getAngularVelocity() const
 {
+    if (!_angularVelocity)
+        _angularVelocity = new Vector3();
+
     const btVector3& v = _body->getAngularVelocity();
-    return Vector3(v.x(), v.y(), v.z());
+    _angularVelocity->set(v.x(), v.y(), v.z());
+    return *_angularVelocity;
 }
 
-inline Vector3 PhysicsRigidBody::getAnisotropicFriction() const
+inline const Vector3& PhysicsRigidBody::getAnisotropicFriction() const
 {
+    if (!_anisotropicFriction)
+        _anisotropicFriction = new Vector3();
+
     const btVector3& af = _body->getAnisotropicFriction();
-    return Vector3(af.x(), af.y(), af.z());
+    _anisotropicFriction->set(af.x(), af.y(), af.z());
+    return *_anisotropicFriction;
 }
 
 inline float PhysicsRigidBody::getFriction() const
@@ -32,10 +40,14 @@ inline float PhysicsRigidBody::getFriction() const
     return _body->getFriction();
 }
 
-inline Vector3 PhysicsRigidBody::getGravity() const
+inline const Vector3& PhysicsRigidBody::getGravity() const
 {
+    if (!_gravity)
+        _gravity = new Vector3();
+
     const btVector3& g = _body->getGravity();
-    return Vector3(g.x(), g.y(), g.z());
+    _gravity->set(g.x(), g.y(), g.z());
+    return *_gravity;
 }
 
 inline float PhysicsRigidBody::getLinearDamping() const
@@ -43,10 +55,19 @@ inline float PhysicsRigidBody::getLinearDamping() const
     return _body->getLinearDamping();
 }
 
-inline Vector3 PhysicsRigidBody::getLinearVelocity() const
+inline const Vector3& PhysicsRigidBody::getLinearVelocity() const
 {
+    if (!_linearVelocity)
+        _linearVelocity = new Vector3();
+
     const btVector3& v = _body->getLinearVelocity();
-    return Vector3(v.x(), v.y(), v.z());
+    _linearVelocity->set(v.x(), v.y(), v.z());
+    return *_linearVelocity;
+}
+
+inline const Node* PhysicsRigidBody::getNode() const
+{
+    return _node;
 }
 
 inline float PhysicsRigidBody::getRestitution() const

+ 5 - 5
gameplay/src/PhysicsSocketConstraint.cpp

@@ -14,9 +14,9 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, PhysicsRig
 {
     if (b)
     {
-        Vector3 origin = centerOfMassMidpoint(a->_node, b->_node);
-        btTransform frameInA = getTransformOffset(a->_node, origin);
-        btTransform frameInB = getTransformOffset(b->_node, origin);
+        Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
+        btTransform frameInA = getTransformOffset(a->getNode(), origin);
+        btTransform frameInB = getTransformOffset(b->getNode(), origin);
 
         _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, frameInA.getOrigin(), frameInB.getOrigin());
     }
@@ -32,14 +32,14 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, const Vect
 {
     // Take scale into account for the first node's translation offset.
     Vector3 sA;
-    a->_node->getWorldMatrix().getScale(&sA);
+    a->getNode()->getWorldMatrix().getScale(&sA);
     Vector3 tA(translationOffsetA.x * sA.x, translationOffsetA.y * sA.y, translationOffsetA.z * sA.z);
 
     if (b)
     {
         // Take scale into account for the second node's translation offset.
         Vector3 sB;
-        b->_node->getWorldMatrix().getScale(&sB);
+        b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
         _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, btVector3(tA.x, tA.y, tA.z), btVector3(tB.x, tB.y, tB.z));

+ 4 - 4
gameplay/src/PhysicsSpringConstraint.cpp

@@ -16,8 +16,8 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, PhysicsRig
     _a = a;
     _b = b;
 
-    Vector3 origin = centerOfMassMidpoint(a->_node, b->_node);
-    _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, getTransformOffset(a->_node, origin), getTransformOffset(b->_node, origin), true);
+    Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
+    _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
 }
 
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
@@ -29,11 +29,11 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quat
 
     // Take scale into account for the translation offsets.
     Vector3 sA;
-    a->_node->getWorldMatrix().getScale(&sA);
+    a->getNode()->getWorldMatrix().getScale(&sA);
     Vector3 tA(translationOffsetA.x * sA.x, translationOffsetA.y * sA.y, translationOffsetA.z * sA.z);
 
     Vector3 sB;
-    b->_node->getWorldMatrix().getScale(&sB);
+    b->getNode()->getWorldMatrix().getScale(&sB);
     Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
     btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));