浏览代码

Store colliding bodies first as weak pointers to allow safe deleting of them during collision event handling.

Lasse Öörni 12 年之前
父节点
当前提交
ae9ff0d8df
共有 4 个文件被更改,包括 64 次插入86 次删除
  1. 7 20
      Bin/Data/Scripts/TestScene.as
  2. 7 18
      Bin/Data/Scripts/TestSceneOld.as
  3. 46 45
      Engine/Physics/PhysicsWorld.cpp
  4. 4 3
      Engine/Physics/PhysicsWorld.h

+ 7 - 20
Bin/Data/Scripts/TestScene.as

@@ -10,8 +10,6 @@ int drawDebug = 0;
 
 
 Text@ downloadsText;
 Text@ downloadsText;
 
 
-Array<Node@> hitObjects;
-
 void Start()
 void Start()
 {
 {
     if (!engine.headless)
     if (!engine.headless)
@@ -34,7 +32,6 @@ void Start()
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
     SubscribeToEvent("SpawnBox", "HandleSpawnBox");
     SubscribeToEvent("SpawnBox", "HandleSpawnBox");
     SubscribeToEvent("PhysicsCollision", "HandlePhysicsCollision");
     SubscribeToEvent("PhysicsCollision", "HandlePhysicsCollision");
-    SubscribeToEvent("PhysicsPostStep", "HandlePhysicsPostStep");
 
 
     network.RegisterRemoteEvent("SpawnBox");
     network.RegisterRemoteEvent("SpawnBox");
 
 
@@ -509,27 +506,17 @@ void HandlePhysicsCollision(StringHash eventType, VariantMap& eventData)
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     if (nodeA.HasComponent("AnimatedModel"))
     if (nodeA.HasComponent("AnimatedModel"))
-        hitObjects.Push(nodeA);
+        HandleHit(nodeA);
     else if (nodeB.HasComponent("AnimatedModel"))
     else if (nodeB.HasComponent("AnimatedModel"))
-        hitObjects.Push(nodeB);
+        HandleHit(nodeB);
 }
 }
 
 
-void HandlePhysicsPostStep()
+void HandleHit(Node@ node)
 {
 {
-    if (hitObjects.empty)
-        return;
-
-    for (uint i = 0; i < hitObjects.length; ++i)
-    {
-        Node@ node = hitObjects[i];
-
-        // Remove the trigger physics shape, and create the ragdoll
-        node.RemoveComponent("RigidBody");
-        node.RemoveComponent("CollisionShape");
-        CreateRagdoll(node.GetComponent("AnimatedModel"));
-    }
-
-    hitObjects.Clear();
+    // Remove the trigger physics shape, and create the ragdoll
+    node.RemoveComponent("RigidBody");
+    node.RemoveComponent("CollisionShape");
+    CreateRagdoll(node.GetComponent("AnimatedModel"));
 }
 }
 
 
 void CreateRagdoll(AnimatedModel@ model)
 void CreateRagdoll(AnimatedModel@ model)

+ 7 - 18
Bin/Data/Scripts/TestSceneOld.as

@@ -22,7 +22,6 @@ int drawDebug = 0;
 Array<Node@> animatingObjects;
 Array<Node@> animatingObjects;
 Array<Node@> billboards;
 Array<Node@> billboards;
 Array<Node@> lights;
 Array<Node@> lights;
-Array<Node@> hitObjects;
 
 
 void Start()
 void Start()
 {
 {
@@ -607,27 +606,17 @@ void HandlePhysicsCollision(StringHash eventType, VariantMap& eventData)
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeA = eventData["NodeA"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     Node@ nodeB = eventData["NodeB"].GetNode();
     if (nodeA.HasComponent("AnimatedModel"))
     if (nodeA.HasComponent("AnimatedModel"))
-        hitObjects.Push(nodeA);
+        HandleHit(nodeA);
     else if (nodeB.HasComponent("AnimatedModel"))
     else if (nodeB.HasComponent("AnimatedModel"))
-        hitObjects.Push(nodeB);
+        HandleHit(nodeB);
 }
 }
 
 
-void HandlePhysicsPostStep()
+void HandleHit(Node@ node)
 {
 {
-    if (hitObjects.empty)
-        return;
-
-    for (uint i = 0; i < hitObjects.length; ++i)
-    {
-        Node@ node = hitObjects[i];
-
-        // Remove the trigger physics shape, and create the ragdoll
-        node.RemoveComponent("RigidBody");
-        node.RemoveComponent("CollisionShape");
-        CreateRagdoll(node.GetComponent("AnimatedModel"));
-    }
-
-    hitObjects.Clear();
+    // Remove the trigger physics shape, and create the ragdoll
+    node.RemoveComponent("RigidBody");
+    node.RemoveComponent("CollisionShape");
+    CreateRagdoll(node.GetComponent("AnimatedModel"));
 }
 }
 
 
 void CreateRagdoll(AnimatedModel@ model)
 void CreateRagdoll(AnimatedModel@ model)

+ 46 - 45
Engine/Physics/PhysicsWorld.cpp

@@ -383,12 +383,13 @@ void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody
     
     
     result.Clear();
     result.Clear();
     
     
-    for (HashSet<Pair<RigidBody*, RigidBody*> >::Iterator i = currentCollisions_.Begin(); i != currentCollisions_.End(); ++i)
+    for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
+        i != currentCollisions_.End(); ++i)
     {
     {
-        if (i->first_ == body)
-            result.Push(i->second_);
-        else if (i->second_ == body)
-            result.Push(i->first_);
+        if (i->first_.first_ == body)
+            result.Push(i->first_.second_);
+        else if (i->first_.second_ == body)
+            result.Push(i->first_.first_);
     }
     }
 }
 }
 
 
@@ -405,23 +406,6 @@ void PhysicsWorld::AddRigidBody(RigidBody* body)
 void PhysicsWorld::RemoveRigidBody(RigidBody* body)
 void PhysicsWorld::RemoveRigidBody(RigidBody* body)
 {
 {
     rigidBodies_.Remove(body);
     rigidBodies_.Remove(body);
-    
-    // Erase from collision pairs so that they can be used to safely find overlapping bodies
-    for (HashSet<Pair<RigidBody*, RigidBody*> >::Iterator i = currentCollisions_.Begin(); i != currentCollisions_.End();)
-    {
-        if (i->first_ == body || i->second_ == body)
-            i = currentCollisions_.Erase(i);
-        else
-            ++i;
-    }
-    
-    for (HashSet<Pair<RigidBody*, RigidBody*> >::Iterator i = previousCollisions_.Begin(); i != previousCollisions_.End();)
-    {
-        if (i->first_ == body || i->second_ == body)
-            i = previousCollisions_.Erase(i);
-        else
-            ++i;
-    }
 }
 }
 
 
 void PhysicsWorld::AddCollisionShape(CollisionShape* shape)
 void PhysicsWorld::AddCollisionShape(CollisionShape* shape)
@@ -563,9 +547,6 @@ void PhysicsWorld::SendCollisionEvents()
             if (!bodyA || !bodyB)
             if (!bodyA || !bodyB)
                 continue;
                 continue;
             
             
-            WeakPtr<RigidBody> bodyWeakA(bodyA);
-            WeakPtr<RigidBody> bodyWeakB(bodyB);
-            
             // Skip collision event signaling if both objects are static, or if collision event mode does not match
             // Skip collision event signaling if both objects are static, or if collision event mode does not match
             if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
             if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
                 continue;
                 continue;
@@ -575,19 +556,38 @@ void PhysicsWorld::SendCollisionEvents()
                 !bodyA->IsActive() && !bodyB->IsActive())
                 !bodyA->IsActive() && !bodyB->IsActive())
                 continue;
                 continue;
             
             
+            WeakPtr<RigidBody> bodyWeakA(bodyA);
+            WeakPtr<RigidBody> bodyWeakB(bodyB);
+            
+            Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> > bodyPair;
+            if (bodyA < bodyB)
+                bodyPair = MakePair(bodyWeakA, bodyWeakB);
+            else
+                bodyPair = MakePair(bodyWeakB, bodyWeakA);
+            
+            // First only store the collision pair as weak pointers and the manifold pointer, so user code can safely destroy
+            // objects during collision event handling
+            currentCollisions_[bodyPair] = contactManifold;
+        }
+        
+        for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
+            i != currentCollisions_.End(); ++i)
+        {
+            RigidBody* bodyA = i->first_.first_;
+            RigidBody* bodyB = i->first_.second_;
+            if (!bodyA || !bodyB)
+                continue;
+            
+            btPersistentManifold* contactManifold = i->second_;
+            int numContacts = contactManifold->getNumContacts();
+            
             Node* nodeA = bodyA->GetNode();
             Node* nodeA = bodyA->GetNode();
             Node* nodeB = bodyB->GetNode();
             Node* nodeB = bodyB->GetNode();
             WeakPtr<Node> nodeWeakA(nodeA);
             WeakPtr<Node> nodeWeakA(nodeA);
             WeakPtr<Node> nodeWeakB(nodeB);
             WeakPtr<Node> nodeWeakB(nodeB);
             
             
-            Pair<RigidBody*, RigidBody*> bodyPair;
-            if (bodyA < bodyB)
-                bodyPair = MakePair(bodyA, bodyB);
-            else
-                bodyPair = MakePair(bodyB, bodyA);
-            currentCollisions_.Insert(bodyPair);
-            bool newCollision = !previousCollisions_.Contains(bodyPair);
             bool phantom = bodyA->IsPhantom() || bodyB->IsPhantom();
             bool phantom = bodyA->IsPhantom() || bodyB->IsPhantom();
+            bool newCollision = !previousCollisions_.Contains(i->first_);
             
             
             physicsCollisionData[PhysicsCollision::P_NODEA] = (void*)nodeA;
             physicsCollisionData[PhysicsCollision::P_NODEA] = (void*)nodeA;
             physicsCollisionData[PhysicsCollision::P_NODEB] = (void*)nodeB;
             physicsCollisionData[PhysicsCollision::P_NODEB] = (void*)nodeB;
@@ -613,13 +613,13 @@ void PhysicsWorld::SendCollisionEvents()
             {
             {
                 SendEvent(E_PHYSICSCOLLISIONSTART, physicsCollisionData);
                 SendEvent(E_PHYSICSCOLLISIONSTART, physicsCollisionData);
                 // Skip rest of processing if either of the nodes or bodies is removed as a response to the event
                 // Skip rest of processing if either of the nodes or bodies is removed as a response to the event
-                if (!nodeWeakA || !nodeWeakB || !bodyWeakA || !bodyWeakB)
+                if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
                     continue;
             }
             }
             
             
             // Then send the ongoing collision event
             // Then send the ongoing collision event
             SendEvent(E_PHYSICSCOLLISION, physicsCollisionData);
             SendEvent(E_PHYSICSCOLLISION, physicsCollisionData);
-            if (!nodeWeakA || !nodeWeakB || !bodyWeakA || !bodyWeakB)
+            if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                 continue;
                 continue;
             
             
             nodeCollisionData[NodeCollision::P_BODY] = (void*)bodyA;
             nodeCollisionData[NodeCollision::P_BODY] = (void*)bodyA;
@@ -631,12 +631,12 @@ void PhysicsWorld::SendCollisionEvents()
             if (newCollision)
             if (newCollision)
             {
             {
                 nodeA->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData);
                 nodeA->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData);
-                if (!nodeWeakA || !nodeWeakB || !bodyWeakA || !bodyWeakB)
+                if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
                     continue;
             }
             }
             
             
             nodeA->SendEvent(E_NODECOLLISION, nodeCollisionData);
             nodeA->SendEvent(E_NODECOLLISION, nodeCollisionData);
-            if (!nodeWeakA || !nodeWeakB || !bodyWeakA || !bodyWeakB)
+            if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                 continue;
                 continue;
             
             
             contacts.Clear();
             contacts.Clear();
@@ -657,7 +657,7 @@ void PhysicsWorld::SendCollisionEvents()
             if (newCollision)
             if (newCollision)
             {
             {
                 nodeB->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData);
                 nodeB->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData);
-                if (!nodeWeakA || !nodeWeakB || !bodyWeakA || !bodyWeakB)
+                if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
                     continue;
             }
             }
             
             
@@ -672,14 +672,15 @@ void PhysicsWorld::SendCollisionEvents()
         
         
         physicsCollisionData[PhysicsCollisionEnd::P_WORLD] = (void*)this;
         physicsCollisionData[PhysicsCollisionEnd::P_WORLD] = (void*)this;
         
         
-        for (HashSet<Pair<RigidBody*, RigidBody*> >::Iterator i = previousCollisions_.Begin(); i != previousCollisions_.End(); ++i)
+        for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = previousCollisions_.Begin(); i != previousCollisions_.End(); ++i)
         {
         {
-            if (!currentCollisions_.Contains(*i))
+            if (!currentCollisions_.Contains(i->first_))
             {
             {
-                RigidBody* bodyA = i->first_;
-                RigidBody* bodyB = i->second_;
-                WeakPtr<RigidBody> bodyWeakA(bodyA);
-                WeakPtr<RigidBody> bodyWeakB(bodyB);
+                RigidBody* bodyA = i->first_.first_;
+                RigidBody* bodyB = i->first_.second_;
+                if (!bodyA || !bodyB)
+                    continue;
+                
                 bool phantom = bodyA->IsPhantom() || bodyB->IsPhantom();
                 bool phantom = bodyA->IsPhantom() || bodyB->IsPhantom();
                 
                 
                 // Skip collision event signaling if both objects are static, or if collision event mode does not match
                 // Skip collision event signaling if both objects are static, or if collision event mode does not match
@@ -704,7 +705,7 @@ void PhysicsWorld::SendCollisionEvents()
                 
                 
                 SendEvent(E_PHYSICSCOLLISIONEND, physicsCollisionData);
                 SendEvent(E_PHYSICSCOLLISIONEND, physicsCollisionData);
                 // Skip rest of processing if either of the nodes or bodies is removed as a response to the event
                 // Skip rest of processing if either of the nodes or bodies is removed as a response to the event
-                if (!nodeWeakA || !nodeWeakB || !bodyWeakA || !bodyWeakB)
+                if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
                     continue;
                 
                 
                 nodeCollisionData[NodeCollisionEnd::P_BODY] = (void*)bodyA;
                 nodeCollisionData[NodeCollisionEnd::P_BODY] = (void*)bodyA;
@@ -713,7 +714,7 @@ void PhysicsWorld::SendCollisionEvents()
                 nodeCollisionData[NodeCollisionEnd::P_PHANTOM] = phantom;
                 nodeCollisionData[NodeCollisionEnd::P_PHANTOM] = phantom;
                 
                 
                 nodeA->SendEvent(E_NODECOLLISIONEND, nodeCollisionData);
                 nodeA->SendEvent(E_NODECOLLISIONEND, nodeCollisionData);
-                if (!nodeWeakA || !nodeWeakB || !bodyWeakA || !bodyWeakB)
+                if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                     continue;
                     continue;
                 
                 
                 nodeCollisionData[NodeCollisionEnd::P_BODY] = (void*)bodyB;
                 nodeCollisionData[NodeCollisionEnd::P_BODY] = (void*)bodyB;

+ 4 - 3
Engine/Physics/PhysicsWorld.h

@@ -34,6 +34,7 @@ class btConstraintSolver;
 class btDiscreteDynamicsWorld;
 class btDiscreteDynamicsWorld;
 class btDispatcher;
 class btDispatcher;
 class btDynamicsWorld;
 class btDynamicsWorld;
+class btPersistentManifold;
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {
@@ -214,9 +215,9 @@ private:
     /// Constraints in the world.
     /// Constraints in the world.
     PODVector<Constraint*> constraints_;
     PODVector<Constraint*> constraints_;
     /// Collision pairs on this frame.
     /// Collision pairs on this frame.
-    HashSet<Pair<RigidBody*, RigidBody*> > currentCollisions_;
-    /// Collision pairs on the previous frame. Used to check if a collision is "new."
-    HashSet<Pair<RigidBody*, RigidBody*> > previousCollisions_;
+    HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold* > currentCollisions_;
+    /// Collision pairs on the previous frame. Used to check if a collision is "new." Manifolds are not guaranteed to exist anymore.
+    HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold* > previousCollisions_;
     /// Delayed (parented) world transform assignments.
     /// Delayed (parented) world transform assignments.
     HashMap<RigidBody*, DelayedWorldTransform> delayedWorldTransforms_;
     HashMap<RigidBody*, DelayedWorldTransform> delayedWorldTransforms_;
     /// Cache for collision geometry data.
     /// Cache for collision geometry data.