Przeglądaj źródła

Merge pull request #101 from blackberry-gaming/next-cculy

Collision shape caching, capsule collision shape support, and minor optimizations
Sean Paul Taylor 14 lat temu
rodzic
commit
369dc151af

+ 0 - 2
gameplay/src/Matrix.cpp

@@ -635,8 +635,6 @@ bool Matrix::invert(Matrix* dst) const
     inverse.m[14] = -m[12] * a3 + m[13] * a1 - m[14] * a0;
     inverse.m[15] = m[8] * a3 - m[9] * a1 + m[10] * a0;
 
-    memcpy(dst->m, inverse.m, MATRIX_SIZE);
-
     multiply(inverse, 1.0f / det, dst);
 
     return true;

+ 136 - 81
gameplay/src/PhysicsController.cpp

@@ -297,6 +297,23 @@ void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
             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++)
+    {
+        if (_shapes[i]->_shape == rigidBody->_shape)
+        {
+            if (_shapes[i]->getRefCount() == 1)
+            {
+                _shapes[i]->release();
+                _shapes.erase(_shapes.begin() + i);
+            }
+            else
+                _shapes[i]->release();
+
+            return;
+        }
+    }
 }
 
 PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* collisionObject)
@@ -314,12 +331,51 @@ PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* colli
 btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const btVector3& scale)
 {
     btVector3 halfExtents(scale.x() * 0.5 * abs(max.x - min.x), scale.y() * 0.5 * abs(max.y - min.y), scale.z() * 0.5 * abs(max.z - min.z));
+
+    // Return the box shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
+    {
+        if (_shapes[i]->_shape->getShapeType() == BOX_SHAPE_PROXYTYPE)
+        {
+            btBoxShape* box = static_cast<btBoxShape*>(_shapes[i]->_shape);
+            if (box->getHalfExtentsWithMargin() == halfExtents)
+            {
+                _shapes[i]->addRef();
+                return box;
+            }
+        }
+    }
+    
+    // Create the box shape and add it to the cache.
     btBoxShape* box = bullet_new<btBoxShape>(halfExtents);
-    _shapes.push_back(box);
+    _shapes.push_back(new PhysicsCollisionShape(box));
 
     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
@@ -330,8 +386,23 @@ btCollisionShape* PhysicsController::createSphere(float radius, const btVector3&
     if (uniformScale < scale.z())
         uniformScale = scale.z();
     
+    // Return the sphere shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
+    {
+        if (_shapes[i]->_shape->getShapeType() == SPHERE_SHAPE_PROXYTYPE)
+        {
+            btSphereShape* sphere = static_cast<btSphereShape*>(_shapes[i]->_shape);
+            if (sphere->getRadius() == uniformScale * radius)
+            {
+                _shapes[i]->addRef();
+                return sphere;
+            }
+        }
+    }
+
+    // Create the sphere shape and add it to the cache.
     btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
-    _shapes.push_back(sphere);
+    _shapes.push_back(new PhysicsCollisionShape(sphere));
     
     return sphere;
 }
@@ -431,7 +502,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
     }
 
     btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
-    _shapes.push_back(shape);
+    _shapes.push_back(new PhysicsCollisionShape(shape));
 
     return shape;
 }
@@ -480,103 +551,87 @@ void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
     
 PhysicsController::DebugDrawer::DebugDrawer()
     : _mode(btIDebugDraw::DBG_DrawAabb | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints | 
-       btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _effect(NULL), _positionAttrib(0), _colorAttrib(0),
-       _viewProjectionMatrixUniform(NULL), _viewProjection(NULL), _vertexData(NULL), _vertexCount(0), _vertexDataSize(0)
+       btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _viewProjection(NULL), _meshBatch(NULL)
 {
-    // Unused
+    // Vertex shader for drawing colored lines.
+    const char* vs_str = 
+    {
+        "uniform mat4 u_viewProjectionMatrix;\n"
+        "attribute vec4 a_position;\n"
+        "attribute vec4 a_color;\n"
+        "varying vec4 v_color;\n"
+        "void main(void) {\n"
+        "    v_color = a_color;\n"
+        "    gl_Position = u_viewProjectionMatrix * a_position;\n"
+        "}"
+    };
+        
+    // Fragment shader for drawing colored lines.
+    const char* fs_str = 
+    {
+    #ifdef OPENGL_ES
+        "precision highp float;\n"
+    #endif
+        "varying vec4 v_color;\n"
+        "void main(void) {\n"
+        "   gl_FragColor = v_color;\n"
+        "}"
+    };
+        
+    Effect* effect = Effect::createFromSource(vs_str, fs_str);
+    Material* material = Material::create(effect);
+
+    VertexFormat::Element elements[] =
+    {
+        VertexFormat::Element(VertexFormat::POSITION, 3),
+        VertexFormat::Element(VertexFormat::COLOR, 4),
+    };
+    _meshBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false);
+    
+    SAFE_RELEASE(material);
+    SAFE_RELEASE(effect);
 }
 
 PhysicsController::DebugDrawer::~DebugDrawer()
 {
-    SAFE_RELEASE(_effect);
-    SAFE_DELETE_ARRAY(_vertexData);
+    SAFE_DELETE(_meshBatch);
 }
 
 void PhysicsController::DebugDrawer::begin(const Matrix& viewProjection)
 {
     _viewProjection = &viewProjection;
-    _vertexCount = 0;
+    _meshBatch->begin();
 }
 
 void PhysicsController::DebugDrawer::end()
 {
-    // Lazy load the effect for drawing.
-    if (!_effect)
-    {
-        // Vertex shader for drawing colored lines.
-        const char* vs_str = 
-        {
-            "uniform mat4 u_viewProjectionMatrix;\n"
-            "attribute vec4 a_position;\n"
-            "attribute vec4 a_color;\n"
-            "varying vec4 v_color;\n"
-            "void main(void) {\n"
-            "    v_color = a_color;\n"
-            "    gl_Position = u_viewProjectionMatrix * a_position;\n"
-            "}"
-        };
-        
-        // Fragment shader for drawing colored lines.
-        const char* fs_str = 
-        {
-        #ifdef OPENGL_ES
-            "precision highp float;\n"
-        #endif
-            "varying vec4 v_color;\n"
-            "void main(void) {\n"
-            "   gl_FragColor = v_color;\n"
-            "}"
-        };
-        
-        _effect = Effect::createFromSource(vs_str, fs_str);
-        _positionAttrib = _effect->getVertexAttribute("a_position");
-        _colorAttrib = _effect->getVertexAttribute("a_color");
-        _viewProjectionMatrixUniform = _effect->getUniform("u_viewProjectionMatrix");
-    }
-    
-    // Bind the effect and set the vertex attributes.
-    _effect->bind();
-    GL_ASSERT( glEnableVertexAttribArray(_positionAttrib) );
-    GL_ASSERT( glEnableVertexAttribArray(_colorAttrib) );
-    GL_ASSERT( glVertexAttribPointer(_positionAttrib, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 7, _vertexData) );
-    GL_ASSERT( glVertexAttribPointer(_colorAttrib, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 7, &_vertexData[3]) );
-    
-    // Set the camera's view projection matrix and draw.
-    _effect->setValue( _viewProjectionMatrixUniform, _viewProjection);
-    GL_ASSERT( glDrawArrays(GL_LINES, 0, _vertexCount / 7) );
+    _meshBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_viewProjection);
+    _meshBatch->draw();
+    _meshBatch->end();
 }
 
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
 {
-    // Allocate extra space in the vertex data batch if it is needed.
-    if (_vertexDataSize - _vertexCount < 14)
-    {
-        if (_vertexDataSize > 0)
-        {
-            unsigned int newVertexDataSize = _vertexDataSize * 2;
-            float* newVertexData = new float[newVertexDataSize];
-            memcpy(newVertexData, _vertexData, _vertexDataSize * sizeof(float));
-            SAFE_DELETE_ARRAY(_vertexData);
-            _vertexData = newVertexData;
-            _vertexDataSize = newVertexDataSize;
-        }
-        else
-        {
-            _vertexDataSize = INITIAL_CAPACITY;
-            _vertexData = new float[_vertexDataSize];
-        }
-    }
+    static DebugDrawer::DebugVertex fromVertex, toVertex;
     
-    // Create the vertex data for the line and copy it into the batch.
-    float vertexData[] = 
-    {
-        from.getX(), from.getY(), from.getZ(), 
-        fromColor.getX(), fromColor.getY(), fromColor.getZ(), 1.0f,
-        to.getX(), to.getY(), to.getZ(),
-        toColor.getX(), toColor.getY(), toColor.getZ(), 1.0f
-    };
-    memcpy(&_vertexData[_vertexCount], vertexData, sizeof(float) * 14);
-    _vertexCount += 14;
+    fromVertex.x = from.getX();
+    fromVertex.y = from.getY();
+    fromVertex.z = from.getZ();
+    fromVertex.r = fromColor.getX();
+    fromVertex.g = fromColor.getY();
+    fromVertex.b = fromColor.getZ();
+    fromVertex.a = 1.0f;
+
+    toVertex.x = to.getX();
+    toVertex.y = to.getY();
+    toVertex.z = to.getZ();
+    toVertex.r = toColor.getX();
+    toVertex.g = toColor.getY();
+    toVertex.b = toColor.getZ();
+    toVertex.a = 1.0f;
+
+    _meshBatch->add(&fromVertex, 1);
+    _meshBatch->add(&toVertex, 1);
 }
 
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)

+ 20 - 9
gameplay/src/PhysicsController.h

@@ -191,6 +191,14 @@ public:
 
 private:
 
+    struct PhysicsCollisionShape : public Ref
+    {
+        PhysicsCollisionShape(btCollisionShape* shape) : _shape(shape) {}
+        ~PhysicsCollisionShape() { SAFE_DELETE(_shape); }
+
+        btCollisionShape* _shape;
+    };
+
     /**
      * Constructor.
      */
@@ -238,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);
 
@@ -258,6 +269,12 @@ private:
     {
     public:
 
+        struct DebugVertex
+        {
+            float x, y, z;
+            float r, g, b, a;
+        };
+
         DebugDrawer();        
         ~DebugDrawer();
         
@@ -268,7 +285,7 @@ private:
         void drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor);        
         void drawLine(const btVector3& from, const btVector3& to, const btVector3& color);        
         void drawContactPoint(const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color);        
-        void reportErrorWarning(const char* warningString);        
+        void reportErrorWarning(const char* warningString);
         void draw3dText(const btVector3& location, const char* textString);        
         void setDebugMode(int mode);        
         int	getDebugMode() const;
@@ -276,14 +293,8 @@ private:
     private:
         
         int _mode;
-        Effect* _effect;
-        VertexAttribute _positionAttrib;
-        VertexAttribute _colorAttrib;
-        Uniform* _viewProjectionMatrixUniform;
         const Matrix* _viewProjection;
-        float* _vertexData;
-        unsigned int _vertexCount;
-        unsigned int _vertexDataSize;
+        MeshBatch* _meshBatch;
     };
     
     btDefaultCollisionConfiguration* _collisionConfiguration;
@@ -291,7 +302,7 @@ private:
     btBroadphaseInterface* _overlappingPairCache;
     btSequentialImpulseConstraintSolver* _solver;
     btDynamicsWorld* _world;
-    btAlignedObjectArray<btCollisionShape*> _shapes;
+    std::vector<PhysicsCollisionShape*> _shapes;
     DebugDrawer* _debugDrawer;
     Listener::EventType _status;
     std::vector<Listener*>* _listeners;

+ 79 - 27
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.
@@ -161,8 +185,6 @@ PhysicsRigidBody::~PhysicsRigidBody()
         if (_body->getMotionState())
             delete _body->getMotionState();
 
-        SAFE_DELETE(_shape);
-
         Game::getInstance()->getPhysicsController()->removeRigidBody(this);
         SAFE_DELETE(_body);
     }
@@ -294,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();
@@ -311,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());
@@ -355,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.
@@ -380,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.
      */