Browse Source

Added PhysicsController::sweepTest() method for performing sweep tests with physics objects.
Modified PhysicsCharacter to no longer perform collision response on ghost objects.
Fixed issue that prevent ghost objects from being transformed properly if they were not the root of a node hierarchy.
Much improved camera control in character sample.

Steve Grenier 13 năm trước cách đây
mục cha
commit
d709227f97

+ 15 - 11
gameplay/src/PhysicsCharacter.cpp

@@ -17,15 +17,17 @@ class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexR
 {
 public:
 
-	ClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot)
+	ClosestNotMeConvexResultCallback(PhysicsCollisionObject* me, const btVector3& up, btScalar minSlopeDot)
         : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), _me(me), _up(up), _minSlopeDot(minSlopeDot)
 	{
 	}
 
 	btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
 	{
-		if (convexResult.m_hitCollisionObject == _me)
-			return btScalar(1.0);
+        PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
+
+		if (object == _me || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
+            return 1.0f;
 
         /*
 		btVector3 hitNormalWorld;
@@ -50,7 +52,7 @@ public:
 
 protected:
 
-	btCollisionObject* _me;
+	PhysicsCollisionObject* _me;
 	const btVector3 _up;
 	btScalar _minSlopeDot;
 };
@@ -64,7 +66,7 @@ PhysicsCharacter::PhysicsCharacter(Node* node, const PhysicsCollisionShape::Defi
 	setMaxSlopeAngle(45.0f);
 
     // Set the collision flags on the ghost object to indicate it's a character
-    _ghostObject->setCollisionFlags(_ghostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT);
+    _ghostObject->setCollisionFlags(_ghostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE);
 
     // Register ourselves as an action on the physics world so we are called back during physics ticks
     Game::getInstance()->getPhysicsController()->_world->addAction(this);
@@ -353,7 +355,6 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     // dynamic objects (i.e. objects that moved and now intersect the character).
     if (_physicsEnabled)
     {
-        //_colliding = fixCollision(collisionWorld);
         _colliding = false;
         int stepCount = 0;
 	    while (fixCollision(collisionWorld))
@@ -483,7 +484,7 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
 		btVector3 sweepDirNegative(_currentPosition - targetPosition);
 
-		ClosestNotMeConvexResultCallback callback(_ghostObject, sweepDirNegative, btScalar(0.0));
+		ClosestNotMeConvexResultCallback callback(this, sweepDirNegative, btScalar(0.0));
 		callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
 		callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
 
@@ -544,7 +545,7 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
 	start.setOrigin(_currentPosition);
 	end.setOrigin(targetPosition);
 
-    ClosestNotMeConvexResultCallback callback(_ghostObject, btVector3(0, 1, 0), _cosSlopeAngle);
+    ClosestNotMeConvexResultCallback callback(this, btVector3(0, 1, 0), _cosSlopeAngle);
 	callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
 	callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
 
@@ -657,6 +658,12 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
             // 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;
 
+            // Skip ghost objects
+            PhysicsCollisionObject* object = Game::getInstance()->getPhysicsController()->getCollisionObject(
+                (btCollisionObject*)(manifold->getBody0() == _ghostObject ? manifold->getBody1() : manifold->getBody0()));
+            if (!object || object->getType() == PhysicsCollisionObject::GHOST_OBJECT)
+                continue;
+
 			for (int p = 0, contactCount = manifold->getNumContacts(); p < contactCount; ++p)
 			{
 				const btManifoldPoint& pt = manifold->getContactPoint(p);
@@ -674,14 +681,11 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
                         _collisionNormal = pt.m_normalWorldOnB * directionSign;
 					}
 
-					//Node* node = Game::getInstance()->getPhysicsController()->getCollisionObject((btCollisionObject*)(manifold->getBody0() == _ghostObject ? manifold->getBody1() : manifold->getBody0()))->getNode();
-
                     // Calculate new position for object, which is translated back along the collision normal
 					currentPosition += pt.m_normalWorldOnB * directionSign * dist * 0.2f;
 					collision = true;
 				}
 			}
-			//manifold->clearManifold();
 		}
 	}
 

+ 9 - 1
gameplay/src/PhysicsCollisionObject.cpp

@@ -54,7 +54,15 @@ PhysicsMotionState* PhysicsCollisionObject::getMotionState() const
 
 bool PhysicsCollisionObject::isKinematic() const
 {
-	return getCollisionObject()->isKinematicObject();
+    switch (getType())
+    {
+    case GHOST_OBJECT:
+    case CHARACTER:
+        return true;
+    default:
+        return getCollisionObject()->isKinematicObject();
+    }
+	
 }
 
 bool PhysicsCollisionObject::isDynamic() const

+ 0 - 1
gameplay/src/PhysicsCollisionShape.h

@@ -214,7 +214,6 @@ private:
 	 */
 	~PhysicsCollisionShape();
 
-
     // Shape type
     Type _type;
 

+ 85 - 7
gameplay/src/PhysicsController.cpp

@@ -134,22 +134,100 @@ void PhysicsController::drawDebug(const Matrix& viewProjection)
     _debugDrawer->end();
 }
 
-PhysicsCollisionObject* PhysicsController::rayTest(const Ray& ray, float distance, Vector3* hitPoint, float* hitFraction)
+bool PhysicsController::rayTest(const Ray& ray, float distance, PhysicsController::HitResult* result)
 {
     btCollisionWorld::ClosestRayResultCallback callback(BV(ray.getOrigin()), BV(distance * ray.getDirection()));
     _world->rayTest(BV(ray.getOrigin()), BV(distance * ray.getDirection()), callback);
     if (callback.hasHit())
     {
-        if (hitPoint)
-            hitPoint->set(callback.m_hitPointWorld.x(), callback.m_hitPointWorld.y(), callback.m_hitPointWorld.z());
+        if (result)
+        {
+            result->object = getCollisionObject(callback.m_collisionObject);
+            result->point.set(callback.m_hitPointWorld.x(), callback.m_hitPointWorld.y(), callback.m_hitPointWorld.z());
+            result->fraction = callback.m_closestHitFraction;
+            result->normal.set(callback.m_hitNormalWorld.x(), callback.m_hitNormalWorld.y(), callback.m_hitNormalWorld.z());
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3& endPosition, PhysicsController::HitResult* result)
+{
+    class SweepTestCallback : public btCollisionWorld::ClosestConvexResultCallback
+    {
+    public:
 
-        if (hitFraction)
-            *hitFraction = callback.m_closestHitFraction;
+	    SweepTestCallback(PhysicsCollisionObject* me)
+            : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), me(me)
+	    {
+	    }
+
+	    btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
+	    {
+            PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
+
+		    if (object == me)
+			    return 1.0f;
+
+		    return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
+	    }
+
+	    PhysicsCollisionObject* me;
+    };
+
+    PhysicsCollisionShape* shape = object->getCollisionShape();
+    PhysicsCollisionShape::Type type = shape->getType();
+    if (type != PhysicsCollisionShape::SHAPE_BOX && type != PhysicsCollisionShape::SHAPE_SPHERE && type != PhysicsCollisionShape::SHAPE_CAPSULE)
+        return false; // unsupported type
+
+    // Define the start transform
+    btTransform start;
+    if (object->getNode())
+        start.setFromOpenGLMatrix(object->getNode()->getWorldMatrix().m);
+    else
+        start.setIdentity();
+
+    // Define the end transform
+    btTransform end(start);
+    end.setOrigin(BV(endPosition));
+
+    // Perform bullet convex sweep test
+    SweepTestCallback callback(object);
+
+    // 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.
+    /*switch (object->getType())
+    {
+    case PhysicsCollisionObject::GHOST_OBJECT:
+    case PhysicsCollisionObject::CHARACTER:
+        static_cast<PhysicsGhostObject*>(object)->_ghostObject->convexSweepTest(static_cast<btConvexShape*>(shape->getShape()), start, end, callback, _world->getDispatchInfo().m_allowedCcdPenetration);
+        break;
+
+    default:
+        _world->convexSweepTest(static_cast<btConvexShape*>(shape->getShape()), start, end, callback, _world->getDispatchInfo().m_allowedCcdPenetration);
+        break;
+    }
+    */
+    _world->convexSweepTest(static_cast<btConvexShape*>(shape->getShape()), start, end, callback, _world->getDispatchInfo().m_allowedCcdPenetration);
+
+    // Check for hits and store results
+    if (callback.hasHit())
+    {
+        if (result)
+        {
+            result->object = getCollisionObject(callback.m_hitCollisionObject);
+            result->point.set(callback.m_hitPointWorld.x(), callback.m_hitPointWorld.y(), callback.m_hitPointWorld.z());
+            result->fraction = callback.m_closestHitFraction;
+            result->normal.set(callback.m_hitNormalWorld.x(), callback.m_hitNormalWorld.y(), callback.m_hitNormalWorld.z());
+        }
 
-        return getCollisionObject(callback.m_collisionObject);
+        return true;
     }
 
-    return NULL;
+    return false;
 }
 
 btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 

+ 43 - 7
gameplay/src/PhysicsController.h

@@ -56,6 +56,32 @@ public:
         virtual void statusEvent(EventType type) = 0;
     };
 
+    /**
+     * Stucture that stores hit test results for ray and sweep tests.
+     */
+    struct HitResult
+    {
+        /**
+         * The collision object that was hit.
+         */
+        PhysicsCollisionObject* object;
+
+        /**
+         * The point where the collision occurred, in world space.
+         */
+        Vector3 point;
+
+        /**
+         * The fraction (0-1) of the test distance to the collision point.
+         */
+        float fraction;
+
+        /**
+         * The normal vector of the collision surface, in world space.
+         */
+        Vector3 normal;
+    };
+
     /**
      * Adds a listener to the physics controller.
      * 
@@ -185,7 +211,7 @@ public:
      * @param gravity The gravity vector.
      */
     void setGravity(const Vector3& gravity);
-   
+
     /**
      * Draws debugging information (rigid body outlines, etc.) using the given view projection matrix.
      * 
@@ -194,16 +220,26 @@ public:
     void drawDebug(const Matrix& viewProjection);
 
     /**
-     * Gets the first rigid body that the given ray intersects.
+     * Performs a ray test on the physics world.
      * 
      * @param ray The ray to test intersection with.
      * @param distance How far along the given ray to test for intersections.
-     * @param hitPoint Optional Vector3 point that is populated with the world-space point of intersection.
-     * @param hitFraction Optional float pointer that is populated with the distance along the ray
-     *      (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.
+     * @param result Optioanl pointer to a HitTest structure to store hit test result information in.
+     * @return True if the ray test collided with a physics object, false otherwise.
+     */
+    bool rayTest(const Ray& ray, float distance, PhysicsController::HitResult* result = NULL);
+
+    /**
+     * Performs a sweep test of the given collision object on the physics world.
+     *
+     * The start position of the sweep test is defined by the current world position
+     * of the specified collision object.
+     *
+     * @param object The collision object to test.
+     * @param endPosition The end position of the sweep test, in world space.
+     * @return True if the object intersects any other physics objects, false otherwise.
      */
-    PhysicsCollisionObject* rayTest(const Ray& ray, float distance, Vector3* hitPoint = NULL, float* hitFraction = NULL);
+    bool sweepTest(PhysicsCollisionObject* object, const Vector3& endPosition, PhysicsController::HitResult* result = NULL);
 
 protected:
 

+ 1 - 0
gameplay/src/PhysicsGhostObject.cpp

@@ -18,6 +18,7 @@ PhysicsGhostObject::PhysicsGhostObject(Node* node, const PhysicsCollisionShape::
     // Create the ghost object.
     _ghostObject = bullet_new<btPairCachingGhostObject>();
 	_ghostObject->setCollisionShape(_collisionShape->getShape());
+    _ghostObject->setCollisionFlags(_ghostObject->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE);
 
     // Initialize a physics motion state object for syncing the transform.
     _motionState = new PhysicsMotionState(_node, &centerOfMassOffset);

+ 1 - 0
gameplay/src/PhysicsGhostObject.h

@@ -16,6 +16,7 @@ class PhysicsMotionState;
 class PhysicsGhostObject : public PhysicsCollisionObject, public Transform::Listener
 {
     friend class Node;
+    friend class PhysicsController;
 
 public: