Kaynağa Gözat

Adds capsule collision shape support.

Chris Culy 14 yıl önce
ebeveyn
işleme
d43c3f26f4

+ 23 - 0
gameplay/src/PhysicsController.cpp

@@ -353,6 +353,29 @@ btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3
     return box;
 }
 
+btCollisionShape* PhysicsController::createCapsule(float radius, float height)
+{
+    // Return the capsule shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
+    {
+        if (_shapes[i]->_shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE)
+        {
+            btCapsuleShape* capsule = static_cast<btCapsuleShape*>(_shapes[i]->_shape);
+            if (capsule->getRadius() == radius && capsule->getHalfHeight() == 0.5f * height)
+            {
+                _shapes[i]->addRef();
+                return capsule;
+            }
+        }
+    }
+    
+    // Create the capsule shape and add it to the cache.
+    btCapsuleShape* capsule = bullet_new<btCapsuleShape>(radius, height);
+    _shapes.push_back(new PhysicsCollisionShape(capsule));
+
+    return capsule;
+}
+
 btCollisionShape* PhysicsController::createSphere(float radius, const btVector3& scale)
 {
     // Since sphere shapes depend only on the radius, the best we can do is take

+ 3 - 0
gameplay/src/PhysicsController.h

@@ -246,6 +246,9 @@ private:
     // Creates a box collision shape to be used in the creation of a rigid body.
     btCollisionShape* createBox(const Vector3& min, const Vector3& max, const btVector3& scale);
 
+    // Creates a capsule collision shape to be used in the creation of a rigid body.
+    btCollisionShape* createCapsule(float radius, float height);
+
     // Creates a sphere collision shape to be used in the creation of a rigid body.
     btCollisionShape* createSphere(float radius, const btVector3& scale);
 

+ 79 - 25
gameplay/src/PhysicsRigidBody.cpp

@@ -14,15 +14,16 @@ const int PhysicsRigidBody::Listener::DIRTY         = 0x01;
 const int PhysicsRigidBody::Listener::COLLISION     = 0x02;
 const int PhysicsRigidBody::Listener::REGISTERED    = 0x04;
 
-// Internal values used for creating mesh and heightfield rigid bodies.
+// 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))
 
 // 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, 
-        float friction, float restitution, float linearDamping, float angularDamping)
+    float friction, float restitution, float linearDamping, float angularDamping)
         : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
         _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
         _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
@@ -144,6 +145,29 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, Image* image, float mass,
     _node->addListener(this);
 }
 
+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),
+        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
+        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
+{
+    // Create the capsule collision shape.
+    _shape = Game::getInstance()->getPhysicsController()->createCapsule(radius, height);
+
+    // Use the center of the bounding sphere as the center of mass offset.
+    Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
+    c.negate();
+
+    // Create the Bullet rigid body.
+    if (c.lengthSquared() > MATH_EPSILON)
+        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
+    else
+        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
+
+    // Add the rigid body to the physics world.
+    Game::getInstance()->getPhysicsController()->addRigidBody(this);
+}
+
 PhysicsRigidBody::~PhysicsRigidBody()
 {
     // Clean up all constraints linked to this rigid body.
@@ -292,6 +316,8 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     Vector3* gravity = NULL;
     Vector3* anisotropicFriction = NULL;
     const char* imagePath = NULL;
+    float radius = -1.0f;
+    float height = -1.0f;
 
     // Load the defined properties.
     properties->rewind();
@@ -309,6 +335,8 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
                 type = SHAPE_MESH;
             else if (typeStr == "HEIGHTFIELD")
                 type = SHAPE_HEIGHTFIELD;
+            else if (typeStr == "CAPSULE")
+                type = SHAPE_CAPSULE;
             else
             {
                 WARN_VARG("Could not create rigid body; unsupported value for rigid body type: '%s'.", typeStr.c_str());
@@ -353,6 +381,14 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         {
             imagePath = properties->getString();
         }
+        else if (strcmp(name, "radius") == 0)
+        {
+            radius = properties->getFloat();
+        }
+        else if (strcmp(name, "height") == 0)
+        {
+            height = properties->getFloat();
+        }
     }
 
     // If the rigid body type is equal to mesh, check that the node's mesh's primitive type is supported.
@@ -378,30 +414,48 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 
     // Create the rigid body.
     PhysicsRigidBody* body = NULL;
-    if (imagePath == NULL)
-    {
-        body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
-    }
-    else
+    switch (type)
     {
-        // Load the image data from the given file path.
-        Image* image = Image::create(imagePath);
-        if (!image)
-            return NULL;
-
-        // Ensure that the image's pixel format is supported.
-        switch (image->getFormat())
-        {
-            case Image::RGB:
-            case Image::RGBA:
-                break;
-            default:
-                WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
-                return NULL;
-        }
-
-        body = new PhysicsRigidBody(node, image, mass, friction, restitution, linearDamping, angularDamping);
-        SAFE_RELEASE(image);
+        case SHAPE_HEIGHTFIELD:
+            if (imagePath == NULL)
+            {
+                WARN("Heightfield rigid body requires an image path.");
+            }
+            else
+            {
+                // Load the image data from the given file path.
+                Image* image = Image::create(imagePath);
+                if (!image)
+                    return NULL;
+
+                // Ensure that the image's pixel format is supported.
+                switch (image->getFormat())
+                {
+                    case Image::RGB:
+                    case Image::RGBA:
+                        break;
+                    default:
+                        WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
+                        return NULL;
+                }
+
+                body = new PhysicsRigidBody(node, image, mass, friction, restitution, linearDamping, angularDamping);
+                SAFE_RELEASE(image);
+            }
+            break;
+        case SHAPE_CAPSULE:
+            if (radius == -1.0f || height == -1.0f)
+            {
+                WARN("Both 'radius' and 'height' must be specified for a capsule rigid body.");
+            }
+            else
+            {
+                body = new PhysicsRigidBody(node, radius, height, mass, friction, restitution, linearDamping, angularDamping);
+            }
+            break;
+        default:
+            body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
+            break;
     }
 
     // Set any initially defined properties.

+ 17 - 0
gameplay/src/PhysicsRigidBody.h

@@ -325,6 +325,23 @@ private:
     PhysicsRigidBody(Node* node, Image* image, float mass, float friction = 0.5,
         float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
 
+    /**
+     * Creates a capsule rigid body.
+     * 
+     * @param node The node to create the heightfield rigid body for; note that the node must have
+     *      a model attached to it prior to creating a rigid body for it.
+     * @param radius The radius of the capsule.
+     * @param height The height of the cylindrical part of the capsule (not including the ends).
+     * @param mass The mass of the rigid body, in kilograms.
+     * @param friction The friction of the rigid body (non-zero values give best simulation results).
+     * @param restitution The restitution of the rigid body (this controls the bounciness of
+     *      the rigid body; use zero for best simulation results).
+     * @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, float radius, float height, float mass, float friction = 0.5,
+        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
+
     /**
      * Destructor.
      */