Bladeren bron

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

Next sgrenier
Steve Grenier 13 jaren geleden
bovenliggende
commit
a0ef78fd88
2 gewijzigde bestanden met toevoegingen van 166 en 14 verwijderingen
  1. 104 10
      gameplay/src/PhysicsController.cpp
  2. 62 4
      gameplay/src/PhysicsController.h

+ 104 - 10
gameplay/src/PhysicsController.cpp

@@ -155,14 +155,63 @@ void PhysicsController::drawDebug(const Matrix& viewProjection)
     _debugDrawer->end();
 }
 
-bool PhysicsController::rayTest(const Ray& ray, float distance, PhysicsController::HitResult* result)
+bool PhysicsController::rayTest(const Ray& ray, float distance, PhysicsController::HitResult* result, PhysicsController::HitFilter* filter)
 {
+    class RayTestCallback : public btCollisionWorld::ClosestRayResultCallback
+    {
+    private:
+
+        HitFilter* filter;
+        HitResult hitResult;
+
+    public:
+
+        RayTestCallback(const btVector3& rayFromWorld, const btVector3& rayToWorld, PhysicsController::HitFilter* filter)
+            : btCollisionWorld::ClosestRayResultCallback(rayFromWorld, rayToWorld), filter(filter)
+        {
+        }
+
+		virtual bool needsCollision(btBroadphaseProxy* proxy0) const
+		{
+            if (!btCollisionWorld::ClosestRayResultCallback::needsCollision(proxy0))
+                return false;
+
+            btCollisionObject* co = reinterpret_cast<btCollisionObject*>(proxy0->m_clientObject);
+            PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(co->getUserPointer());
+            if (object == NULL)
+                return false;
+
+            return filter ? !filter->filter(object) : true;
+        }
+
+        btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
+        {
+            GP_ASSERT(rayResult.m_collisionObject);
+            PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(rayResult.m_collisionObject->getUserPointer());
+
+            if (object == NULL)
+                return 1.0f; // ignore
+
+            float result = btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
+
+            hitResult.object = object;
+            hitResult.point.set(m_hitPointWorld.x(), m_hitPointWorld.y(), m_hitPointWorld.z());
+            hitResult.fraction = m_closestHitFraction;
+            hitResult.normal.set(m_hitNormalWorld.x(), m_hitNormalWorld.y(), m_hitNormalWorld.z());
+
+            if (filter && !filter->hit(hitResult))
+                return 1.0f; // process next collision
+
+            return result; // continue normally
+        }
+    };
+
     GP_ASSERT(_world);
 
     btVector3 rayFromWorld(BV(ray.getOrigin()));
     btVector3 rayToWorld(rayFromWorld + BV(ray.getDirection() * distance));
 
-    btCollisionWorld::ClosestRayResultCallback callback(rayFromWorld, rayToWorld);
+    RayTestCallback callback(rayFromWorld, rayToWorld, filter);
     _world->rayTest(rayFromWorld, rayToWorld, callback);
     if (callback.hasHit())
     {
@@ -180,29 +229,56 @@ bool PhysicsController::rayTest(const Ray& ray, float distance, PhysicsControlle
     return false;
 }
 
-bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3& endPosition, PhysicsController::HitResult* result)
+bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3& endPosition, PhysicsController::HitResult* result, PhysicsController::HitFilter* filter)
 {
     class SweepTestCallback : public btCollisionWorld::ClosestConvexResultCallback
     {
+    private:
+
+        PhysicsCollisionObject* me;
+        PhysicsController::HitFilter* filter;
+        PhysicsController::HitResult hitResult;
+
     public:
 
-        SweepTestCallback(PhysicsCollisionObject* me)
-            : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), me(me)
+        SweepTestCallback(PhysicsCollisionObject* me, PhysicsController::HitFilter* filter)
+            : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), me(me), filter(filter)
         {
         }
 
+		virtual bool needsCollision(btBroadphaseProxy* proxy0) const
+		{
+            if (!btCollisionWorld::ClosestConvexResultCallback::needsCollision(proxy0))
+                return false;
+
+            btCollisionObject* co = reinterpret_cast<btCollisionObject*>(proxy0->m_clientObject);
+            PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(co->getUserPointer());
+            if (object == NULL || object == me)
+                return false;
+
+            return filter ? !filter->filter(object) : true;
+        }
+
         btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
         {
             GP_ASSERT(convexResult.m_hitCollisionObject);
             PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
 
-            if (object == me)
+            if (object == NULL)
                 return 1.0f;
 
-            return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
-        }
+            float result = ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
 
-        PhysicsCollisionObject* me;
+            hitResult.object = object;
+            hitResult.point.set(m_hitPointWorld.x(), m_hitPointWorld.y(), m_hitPointWorld.z());
+            hitResult.fraction = m_closestHitFraction;
+            hitResult.normal.set(m_hitNormalWorld.x(), m_hitNormalWorld.y(), m_hitNormalWorld.z());
+
+            if (filter && !filter->hit(hitResult))
+                return 1.0f;
+
+            return result;
+        }
     };
 
     GP_ASSERT(object && object->getCollisionShape());
@@ -232,7 +308,7 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
     end.setOrigin(BV(endPosition));
 
     // Perform bullet convex sweep test.
-    SweepTestCallback callback(object);
+    SweepTestCallback callback(object, filter);
 
     // If the object is represented by a ghost object, use the ghost object's convex sweep test
     // since it is much faster than the world's version.
@@ -1376,4 +1452,22 @@ PhysicsController::Listener::~Listener()
     Game::getInstance()->getPhysicsController()->removeStatusListener(this);
 }
 
+PhysicsController::HitFilter::HitFilter()
+{
+}
+
+PhysicsController::HitFilter::~HitFilter()
+{
+}
+
+bool PhysicsController::HitFilter::filter(PhysicsCollisionObject* object)
+{
+    return false;
+}
+
+bool PhysicsController::HitFilter::hit(const PhysicsController::HitResult& result)
+{
+    return true;
+}
+
 }

+ 62 - 4
gameplay/src/PhysicsController.h

@@ -89,6 +89,54 @@ public:
         Vector3 normal;
     };
 
+    /**
+     * Class that can be overridden to provide custom hit test filters for ray
+     * and sweep tests.
+     *
+     * The default implementation of this class returns only the closest object
+     * that intersects a ray of volume.
+     */
+    class HitFilter
+    {
+    public:
+
+        /**
+         * Constructor.
+         */
+        HitFilter();
+
+        /**
+         * Virtual destructor.
+         */
+        virtual ~HitFilter();
+
+        /**
+         * Called before performing a hit test with an object to determine
+         * whether or not the object should be tested.
+         *
+         * @param object Object to be queried.
+         * 
+         * @return True if the object should be filtered out, or false to include the object in the test (default).
+         */
+        virtual bool filter(PhysicsCollisionObject* object);
+
+        /**
+         * Called when a ray or sweep test collides with a collision object.
+         *
+         * Each collision object that is hit during the ray or sweep test is passed
+         * to this method, along with details of the hit result. Returning true to
+         * this method will continue with normal hit test processing, where only
+         * closer objects are returned. Returning false results in this method being
+         * called for all objects that intersect the ray or volume.
+         *
+         * @param result HitResult object containing information about the hit.
+         * 
+         * @return True (default) to continue with defautl behavior where closer
+         *      objects are processed, false to process all intersecting objects.
+         */
+        virtual bool hit(const HitResult& result);
+    };
+
     /**
      * Adds a listener to the physics controller.
      * 
@@ -246,10 +294,15 @@ public:
      * 
      * @param ray The ray to test intersection with.
      * @param distance How far along the given ray to test for intersections.
-     * @param result Optional pointer to a HitTest structure to store hit test result information in.
+     * @param result Optional pointer to a HitTest structure to store the hit test result information in.
+     *      When using a default (or no) filter, this will always be the closest object hit. Otherwise, if 
+     *      using a custom filter, it will be the last object passed to the HitFilter::hit method (which
+     *      is not neccessarily the closest or furthest).
+     * @param filter Optional filter pointer used to control which objects are tested.
+     *
      * @return True if the ray test collided with a physics object, false otherwise.
      */
-    bool rayTest(const Ray& ray, float distance, PhysicsController::HitResult* result = NULL);
+    bool rayTest(const Ray& ray, float distance, PhysicsController::HitResult* result = NULL, PhysicsController::HitFilter* filter = NULL);
 
     /**
      * Performs a sweep test of the given collision object on the physics world.
@@ -259,10 +312,15 @@ public:
      *
      * @param object The collision object to test.
      * @param endPosition The end position of the sweep test, in world space.
-     * @param result Optional pointer to a HitTest structure to store hit test result information in.
+     * @param result Optional pointer to a HitTest structure to store the hit test result information in.
+     *      When using a default (or no) filter, this will always be the closest object hit. Otherwise, if 
+     *      using a custom filter, it will be the last object passed to the HitFilter::hit method (which
+     *      is not neccessarily the closest or furthest).
+     * @param filter Optional filter pointer used to control which objects are tested.
+     * 
      * @return True if the object intersects any other physics objects, false otherwise.
      */
-    bool sweepTest(PhysicsCollisionObject* object, const Vector3& endPosition, PhysicsController::HitResult* result = NULL);
+    bool sweepTest(PhysicsCollisionObject* object, const Vector3& endPosition, PhysicsController::HitResult* result = NULL, PhysicsController::HitFilter* filter = NULL);
 
 private: