2
0
Эх сурвалжийг харах

Optimized collision shape update to not recreate the whole compound shape every time something changes.
Remove a sub-shape from the compound shape first before deleting it.

Lasse Öörni 13 жил өмнө
parent
commit
731dc2d6ca

+ 1 - 2
Engine/Physics/BoxShape.cpp

@@ -60,8 +60,7 @@ void BoxShape::UpdateCollisionShape()
 {
     if (node_)
     {
-        delete shape_;
-        shape_ = 0;
+        ReleaseShape();
         
         shape_ = new btBoxShape(ToBtVector3(size_ * 0.5f));
         shape_->setLocalScaling(ToBtVector3(node_->GetWorldScale()));

+ 1 - 2
Engine/Physics/CapsuleShape.cpp

@@ -75,8 +75,7 @@ void CapsuleShape::UpdateCollisionShape()
 {
     if (node_)
     {
-        delete shape_;
-        shape_ = 0;
+        ReleaseShape();
         
         shape_ = new btCapsuleShape(radius_, Max(height_ - 2.0f * radius_, 0.0f));
         shape_->setLocalScaling(ToBtVector3(node_->GetWorldScale()));

+ 38 - 10
Engine/Physics/CollisionShape.cpp

@@ -34,6 +34,7 @@
 #include "Scene.h"
 
 #include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
+#include <BulletCollision/CollisionShapes/btCompoundShape.h>
 #include <BulletCollision/CollisionShapes/btTriangleMesh.h>
 #include <hull.h>
 
@@ -254,10 +255,7 @@ CollisionShape::CollisionShape(Context* context) :
 
 CollisionShape::~CollisionShape()
 {
-    delete shape_;
-    shape_ = 0;
-    
-    NotifyRigidBody();
+    ReleaseShape();
     
     if (physicsWorld_)
         physicsWorld_->RemoveCollisionShape(this);
@@ -306,6 +304,14 @@ void CollisionShape::SetTransform(const Vector3& position, const Quaternion& rot
     }
 }
 
+btCompoundShape* CollisionShape::GetParentCompoundShape()
+{
+    if (!rigidBody_)
+        rigidBody_ = GetComponent<RigidBody>();
+    
+    return rigidBody_ ? rigidBody_->GetCompoundShape() : 0;
+}
+
 void CollisionShape::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 {
     /// \todo Implement
@@ -343,12 +349,34 @@ void CollisionShape::OnMarkedDirty(Node* node)
 
 void CollisionShape::NotifyRigidBody()
 {
-    // We need to notify the rigid body also after having been removed from the node, so maintain a weak pointer to it
-    if (!rigidBody_)
-        rigidBody_ = GetComponent<RigidBody>();
-    
-    if (rigidBody_)
-        rigidBody_->RefreshCollisionShapes();
+    btCompoundShape* compound = GetParentCompoundShape();
+    if (node_ && shape_ && compound)
+    {
+        // Remove the shape first to ensure it is not added twice
+        compound->removeChildShape(shape_);
+        
+        // Then add with updated offset
+        btTransform offset;
+        offset.setOrigin(ToBtVector3(node_->GetWorldScale() * position_));
+        offset.setRotation(ToBtQuaternion(rotation_));
+        compound->addChildShape(offset, shape_);
+        
+        // Finally tell the rigid body to update its mass
+        rigidBody_->UpdateMass();
+    }
     
     dirty_ = false;
 }
+
+void CollisionShape::ReleaseShape()
+{
+    btCompoundShape* compound = GetParentCompoundShape();
+    if (shape_ && compound)
+    {
+        compound->removeChildShape(shape_);
+        rigidBody_->UpdateMass();
+    }
+    
+    delete shape_;
+    shape_ = 0;
+}

+ 7 - 2
Engine/Physics/CollisionShape.h

@@ -36,6 +36,7 @@ class RigidBody;
 
 class btBvhTriangleMeshShape;
 class btCollisionShape;
+class btCompoundShape;
 class btTriangleMesh;
 
 /// Base class for collision shape geometry data.
@@ -108,8 +109,10 @@ public:
     /// %Set offset transform.
     void SetTransform(const Vector3& position, const Quaternion& rotation);
     
-    /// Return Bullet collision shape
+    /// Return Bullet collision shape.
     btCollisionShape* GetCollisionShape() const { return shape_; }
+    /// Find the parent rigid body component and return its compound collision shape.
+    btCompoundShape* GetParentCompoundShape();
     /// Return physics world.
     PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
     /// Return offset position.
@@ -127,8 +130,10 @@ protected:
     virtual void OnMarkedDirty(Node* node);
     /// Update the collision shape after attribute changes.
     virtual void UpdateCollisionShape() = 0;
-    /// Notify the RigidBody of changed or removed collision shape.
+    /// Update the new collision shape to the RigidBody, and tell it to update its mass.
     void NotifyRigidBody();
+    /// Release the collision shape.
+    void ReleaseShape();
     
     /// Physics world.
     WeakPtr<PhysicsWorld> physicsWorld_;

+ 1 - 2
Engine/Physics/ConeShape.cpp

@@ -75,8 +75,7 @@ void ConeShape::UpdateCollisionShape()
 {
     if (node_)
     {
-        delete shape_;
-        shape_ = 0;
+        ReleaseShape();
         
         shape_ = new btConeShape(radius_, height_);
         shape_->setLocalScaling(ToBtVector3(node_->GetWorldScale()));

+ 1 - 2
Engine/Physics/CylinderShape.cpp

@@ -75,8 +75,7 @@ void CylinderShape::UpdateCollisionShape()
 {
     if (node_)
     {
-        delete shape_;
-        shape_ = 0;
+        ReleaseShape();
         
         shape_ = new btCylinderShape(btVector3(radius_, height_ * 0.5f, radius_));
         shape_->setLocalScaling(ToBtVector3(node_->GetWorldScale()));

+ 30 - 35
Engine/Physics/RigidBody.cpp

@@ -124,7 +124,7 @@ void RigidBody::SetMass(float mass)
     if (mass != mass_)
     {
         mass_ = mass;
-        CreateBody();
+        AddBodyToWorld();
         
         if (mass > 0.0f)
             Activate();
@@ -279,7 +279,7 @@ void RigidBody::SetKinematic(bool enable)
             flags &= ~btCollisionObject::CF_KINEMATIC_OBJECT;
         body_->setCollisionFlags(flags),
         
-        CreateBody();
+        AddBodyToWorld();
     }
 }
 
@@ -294,7 +294,7 @@ void RigidBody::SetPhantom(bool enable)
             flags &= ~btCollisionObject::CF_NO_CONTACT_RESPONSE;
         body_->setCollisionFlags(flags);
         
-        CreateBody();
+        AddBodyToWorld();
     }
 }
 
@@ -305,7 +305,7 @@ void RigidBody::SetCollisionGroup(unsigned group)
     if (group != collisionGroup_)
     {
         collisionGroup_ = group;
-        CreateBody();
+        AddBodyToWorld();
     }
 }
 
@@ -316,7 +316,7 @@ void RigidBody::SetCollisionMask(unsigned mask)
     if (mask != collisionMask_)
     {
         collisionMask_ = mask;
-        CreateBody();
+        AddBodyToWorld();
     }
 }
 
@@ -329,7 +329,7 @@ void RigidBody::SetCollisionGroupAndMask(unsigned group, unsigned mask)
     {
         collisionGroup_ = group;
         collisionMask_ = mask;
-        CreateBody();
+        AddBodyToWorld();
     }
 }
 
@@ -509,11 +509,11 @@ bool RigidBody::IsActive() const
         return false;
 }
 
-void RigidBody::RefreshCollisionShapes()
+void RigidBody::RebuildCollision()
 {
     if (node_ && compoundShape_)
     {
-        PROFILE(RefreshCollisionShapes);
+        PROFILE(RebuildCollision);
         
         // Remove all existing shapes first
         while (compoundShape_->getNumChildShapes())
@@ -528,21 +528,23 @@ void RigidBody::RefreshCollisionShapes()
             btCollisionShape* shape = (*i)->GetCollisionShape();
             if (shape)
             {
-                btTransform shapeTransform;
-                shapeTransform.setOrigin(ToBtVector3(node_->GetWorldScale() * (*i)->GetPosition()));
-                shapeTransform.setRotation(ToBtQuaternion((*i)->GetRotation()));
-                compoundShape_->addChildShape(shapeTransform, shape);
+                btTransform offset;
+                offset.setOrigin(ToBtVector3(node_->GetWorldScale() * (*i)->GetPosition()));
+                offset.setRotation(ToBtQuaternion((*i)->GetRotation()));
+                compoundShape_->addChildShape(offset, shape);
             }
         }
-        
-        // Refresh inertia whenever collision shapes change
-        if (body_)
-        {
-            btVector3 localInertia(0.0f, 0.0f, 0.0f);
-            if (mass_ > 0.0f)
-                compoundShape_->calculateLocalInertia(mass_, localInertia);
-            body_->setMassProps(mass_, localInertia);
-        }
+    }
+}
+
+void RigidBody::UpdateMass()
+{
+    if (body_)
+    {
+        btVector3 localInertia(0.0f, 0.0f, 0.0f);
+        if (mass_ > 0.0f)
+            compoundShape_->calculateLocalInertia(mass_, localInertia);
+        body_->setMassProps(mass_, localInertia);
     }
 }
 
@@ -602,40 +604,33 @@ void RigidBody::OnNodeSet(Node* node)
             else
                 LOGERROR("Null physics world, can not create rigid body");
             
-            CreateBody();
+            AddBodyToWorld();
         }
         node->AddListener(this);
     }
 }
 
-void RigidBody::CreateBody()
+void RigidBody::AddBodyToWorld()
 {
     if (!physicsWorld_)
         return;
     
-    btVector3 localInertia(0.0f, 0.0f, 0.0f);
-    if (mass_ > 0.0f)
-        compoundShape_->calculateLocalInertia(mass_, localInertia);
-    
     btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
     if (body_)
-    {
         world->removeRigidBody(body_);
-        body_->setMassProps(mass_, localInertia);
-    }
     else
     {
-        // Build the compound shape, then create the Bullet rigid body
-        RefreshCollisionShapes();
+        // Check if CollisionShapes already exist in the node and add them first
+        RebuildCollision();
+        btVector3 localInertia(0.0f, 0.0f, 0.0f);
         body_ = new btRigidBody(mass_, this, compoundShape_, localInertia);
     }
     
+    UpdateMass();
+    
     int flags = body_->getCollisionFlags();
     if (mass_ > 0.0f)
-    {
         flags &= ~btCollisionObject::CF_STATIC_OBJECT;
-        
-    }
     else
         flags |= btCollisionObject::CF_STATIC_OBJECT;
     body_->setCollisionFlags(flags);

+ 7 - 5
Engine/Physics/RigidBody.h

@@ -159,9 +159,11 @@ public:
     /// Return Bullet rigid body.
     btRigidBody* GetBody() const { return body_; }
     /// Return Bullet compound collision shape.
-    btCompoundShape* GetCollisionShape() const { return compoundShape_; }
-    /// Refresh collision shape(s).
-    void RefreshCollisionShapes();
+    btCompoundShape* GetCompoundShape() const { return compoundShape_; }
+    /// Rebuild the compound collision shape.
+    void RebuildCollision();
+    /// Update mass and inertia of rigid body.
+    void UpdateMass();
     /// %Set network angular velocity attribute.
     void SetNetAngularVelocityAttr(const PODVector<unsigned char>& value);
     /// Return network angular velocity attribute.
@@ -177,8 +179,8 @@ protected:
     virtual void OnMarkedDirty(Node* node);
     
 private:
-    /// Create the rigid body, or re-add to the physics world with changed flags.
-    void CreateBody();
+    /// Create the rigid body, or re-add to the physics world with changed flags. Calls UpdateMass().
+    void AddBodyToWorld();
     /// Remove the rigid body.
     void ReleaseBody();
     

+ 1 - 2
Engine/Physics/SphereShape.cpp

@@ -62,8 +62,7 @@ void SphereShape::UpdateCollisionShape()
 {
     if (node_)
     {
-        delete shape_;
-        shape_ = 0;
+        ReleaseShape();
         
         shape_ = new btSphereShape(radius_);
         shape_->setLocalScaling(ToBtVector3(node_->GetWorldScale()));

+ 3 - 4
Engine/Physics/TriangleMeshShape.cpp

@@ -44,8 +44,8 @@ TriangleMeshShape::TriangleMeshShape(Context* context) :
 
 TriangleMeshShape::~TriangleMeshShape()
 {
-    delete shape_;
-    shape_ = 0;
+    // Release shape first before letting go of the mesh geometry
+    ReleaseShape();
     
     geometry_.Reset();
     if (physicsWorld_)
@@ -114,8 +114,7 @@ void TriangleMeshShape::UpdateCollisionShape()
 {
     if (node_)
     {
-        delete shape_;
-        shape_ = 0;
+        ReleaseShape();
         
         if (model_ && physicsWorld_)
         {