Browse Source

Added GetCollidingBodies() function to RigidBody which returns collisions from last physics step.
The iterator versions of HashMap & HashSet Erase() return an iterator to next element.

Lasse Öörni 13 years ago
parent
commit
88896fe805

+ 9 - 1
Docs/Reference.dox

@@ -944,7 +944,7 @@ CollisionShape provides two APIs for defining the collision geometry. Either set
 
 RigidBodies can be either static or moving. A body is static if its mass is 0, and moving if the mass is greater than 0. Note that the triangle mesh collision shape is not supported for moving objects; it will not collide properly due to limitations in the Bullet library. In this case the convex hull shape can be used instead.
 
-The collision behaviour of a rigid body is controlled by several variables. First, the collision layer and mask define which other objects to collide with: see \ref RigidBody::SetCollisionLayer "SetCollisionLayer()" and \ref RigidBody::SetCollisionMask "SetCollisionMask()". By default a rigid body is on layer 1; the layer will be ANDed with the other body's collision mask to see if the collision should be reported. A rigid body can also be set to \ref RigidBody::SetPhantom "phantom mode" to only report collisions without actually applying collision forces. Finally, the \ref RigidBody::SetFriction "friction" and \ref RigidBody::SetRestitution "restitution" coefficients (between 0 - 1) control how kinetic energy is transferred in the collisions.
+The collision behaviour of a rigid body is controlled by several variables. First, the collision layer and mask define which other objects to collide with: see \ref RigidBody::SetCollisionLayer "SetCollisionLayer()" and \ref RigidBody::SetCollisionMask "SetCollisionMask()". By default a rigid body is on layer 1; the layer will be ANDed with the other body's collision mask to see if the collision should be reported. A rigid body can also be set to \ref RigidBody::SetPhantom "phantom mode" to only report collisions without actually applying collision forces. This can be used to implement trigger areas. Finally, the \ref RigidBody::SetFriction "friction" and \ref RigidBody::SetRestitution "restitution" coefficients (between 0 - 1) control how kinetic energy is transferred in the collisions.
 
 By default rigid bodies can move and rotate about all 3 coordinate axes when forces are applied. To limit the movement, use \ref RigidBody::SetLinearFactor "SetLinearFactor()" and \ref RigidBody::SetAngularFactor "SetAngularFactor()" and set the axes you wish to use to 1 and those you do not wish to use to 0. For example moving humanoid characters are often represented by a capsule shape: to ensure they stay upright and only rotate when you explicitly set the rotation in code, set the angular factor to 0, 0, 0.
 
@@ -1002,6 +1002,14 @@ void HandleNodeCollision(StringHash eventType, VariantMap& eventData)
 }
 \endcode
 
+\section Physics_Queries Physics queries
+
+The following queries into the physics world are provided:
+
+- Raycasts, see \ref PhysicsWorld::Raycast "Raycast()" and \ref PhysicsWorld::RaycastSingle "RaycastSingle()".
+- %Sphere cast (raycast with thickness), see \ref PhysicsWorld::SphereCast "SphereCast()".
+- %Sphere and box overlap tests, see \ref PhysicsWorld::GetRigidBodies() "GetRigidBodies()".
+- Which other rigid bodies are colliding with a body, see \ref RigidBody::GetCollidingBodies() "GetCollidingBodies()". In script this maps into the collidingBodies property.
 
 \page UI User interface
 

+ 27 - 3
Docs/ScriptAPI.dox

@@ -2628,6 +2628,30 @@ Properties:<br>
 - bool compressed (readonly)
 
 
+SoundListener
+
+Methods:<br>
+- bool Load(File@)
+- bool Save(File@)
+- bool LoadXML(const XMLElement&)
+- bool SaveXML(XMLElement&)
+- void ApplyAttributes()
+- bool SetAttribute(const String&, const Variant&)
+- Variant GetAttribute(const String&)
+- void Remove()
+- void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
+
+Properties:<br>
+- ShortStringHash type (readonly)
+- String typeName (readonly)
+- uint numAttributes (readonly)
+- Variant[] attributes
+- AttributeInfo[] attributeInfos (readonly)
+- uint id (readonly)
+- Node@ node (readonly)
+
+
 SoundSource
 
 Methods:<br>
@@ -2714,14 +2738,12 @@ Methods:<br>
 - void SetMode(int, int, bool, bool arg3 = true)
 - bool Play()
 - void Stop()
-- void SetListenerTransform(const Vector3&, const Quaternion&)
 
 Properties:<br>
 - ShortStringHash type (readonly)
 - String typeName (readonly)
 - float[] masterGain
-- Vector3 listenerPosition
-- Quaternion listenerRotation
+- SoundListener@ listener
 - uint sampleSize (readonly)
 - int mixRate (readonly)
 - bool stereo (readonly)
@@ -4496,6 +4518,7 @@ Properties:<br>
 - uint collisionLayer
 - uint collisionMask
 - CollisionEventMode collisionEventMode
+- RigidBody@[]@ collidingBodies (readonly)
 
 
 Constraint
@@ -4564,6 +4587,7 @@ Methods:<br>
 - PhysicsRaycastResult SphereCast(const Ray&, float, float arg2 = M_INFINITY, uint arg3 = 0xffff)
 - RigidBody@[]@ GetRigidBodies(const Sphere&, uint arg1 = 0xffff)
 - RigidBody@[]@ GetRigidBodies(const BoundingBox&, uint arg1 = 0xffff)
+- RigidBody@[]@ GetRigidBodies(RigidBody@)
 - void DrawDebugGeometry(bool)
 
 Properties:<br>

+ 31 - 2
Engine/Container/HashMap.h

@@ -27,6 +27,8 @@
 #include "Pair.h"
 #include "Sort.h"
 
+#include <cassert>
+
 namespace Urho3D
 {
 
@@ -297,13 +299,40 @@ public:
             previous->down_ = node->down_;
         else
             Ptrs()[hashKey] = node->down_;
-        EraseNode(node);
         
+        EraseNode(node);
         return true;
     }
     
     /// Erase a pair by iterator.
-    void Erase(const Iterator& it) { Erase(it->first_); }
+    Iterator Erase(const Iterator& it)
+    {
+        if (!ptrs_ || !it.ptr_)
+            return End();
+        
+        Node* node = reinterpret_cast<Node*>(it.ptr_);
+        Node* next = node->Next();
+        
+        unsigned hashKey = MakeHash(node->pair_.first_) & (NumBuckets() - 1);
+        
+        Node* previous = 0;
+        Node* current = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        while (current && current != node)
+        {
+            previous = current;
+            current = current->Down();
+        }
+        
+        assert(current == node);
+        
+        if (previous)
+            previous->down_ = node->down_;
+        else
+            Ptrs()[hashKey] = node->down_;
+        
+        EraseNode(node);
+        return Iterator(next);
+    }
     
     /// Clear the map.
     void Clear()

+ 31 - 2
Engine/Container/HashSet.h

@@ -26,6 +26,8 @@
 #include "HashBase.h"
 #include "Sort.h"
 
+#include <cassert>
+
 namespace Urho3D
 {
 
@@ -267,13 +269,40 @@ public:
             previous->down_ = node->down_;
         else
             Ptrs()[hashKey] = node->down_;
-        EraseNode(node);
         
+        EraseNode(node);
         return true;
     }
     
     /// Erase a key by iterator.
-    void Erase(const Iterator& it) { Erase(*it); }
+    Iterator Erase(const Iterator& it)
+    {
+        if (!ptrs_ || !it.ptr_)
+            return End();
+        
+        Node* node = reinterpret_cast<Node*>(it.ptr_);
+        Node* next = node->Next();
+        
+        unsigned hashKey = MakeHash(node->key_) & (NumBuckets() - 1);
+        
+        Node* previous = 0;
+        Node* current = reinterpret_cast<Node*>(Ptrs()[hashKey]);
+        while (current && current != node)
+        {
+            previous = current;
+            current = current->Down();
+        }
+        
+        assert(current == node);
+        
+        if (previous)
+            previous->down_ = node->down_;
+        else
+            Ptrs()[hashKey] = node->down_;
+        
+        EraseNode(node);
+        return Iterator(next);
+    }
     
     /// Clear the set.
     void Clear()

+ 16 - 0
Engine/Engine/PhysicsAPI.cpp

@@ -99,6 +99,13 @@ static void RegisterCollisionShape(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Variant", "CollisionShape@+ GetCollisionShape() const", asFUNCTION(GetVariantPtr<CollisionShape>), asCALL_CDECL_OBJLAST);
 }
 
+static CScriptArray* RigidBodyGetCollidingBodies(RigidBody* ptr)
+{
+    PODVector<RigidBody*> result;
+    ptr->GetCollidingBodies(result);
+    return VectorToHandleArray<RigidBody>(result, "Array<RigidBody@>");
+}
+
 static void RegisterRigidBody(asIScriptEngine* engine)
 {
     engine->RegisterEnum("CollisionEventMode");
@@ -160,6 +167,7 @@ static void RegisterRigidBody(asIScriptEngine* engine)
     engine->RegisterObjectMethod("RigidBody", "uint get_collisionMask() const", asMETHOD(RigidBody, GetCollisionMask), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_collisionEventMode(CollisionEventMode)", asMETHOD(RigidBody, SetCollisionEventMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "CollisionEventMode get_collisionEventMode() const", asMETHOD(RigidBody, GetCollisionEventMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RigidBody", "Array<RigidBody@>@ get_collidingBodies() const", asFUNCTION(RigidBodyGetCollidingBodies), asCALL_CDECL_OBJLAST);
     
     // Register Variant GetPtr() for RigidBody
     engine->RegisterObjectMethod("Variant", "RigidBody@+ GetRigidBody() const", asFUNCTION(GetVariantPtr<RigidBody>), asCALL_CDECL_OBJLAST);
@@ -234,6 +242,13 @@ static CScriptArray* PhysicsWorldGetRigidBodiesBox(const BoundingBox& box, unsig
     return VectorToHandleArray<RigidBody>(result, "Array<RigidBody@>");
 }
 
+static CScriptArray* PhysicsWorldGetRigidBodiesBody(RigidBody* body, PhysicsWorld* ptr)
+{
+    PODVector<RigidBody*> result;
+    ptr->GetRigidBodies(result, body);
+    return VectorToHandleArray<RigidBody>(result, "Array<RigidBody@>");
+}
+
 static void RegisterPhysicsWorld(asIScriptEngine* engine)
 {
     engine->RegisterObjectType("PhysicsRaycastResult", sizeof(PhysicsRaycastResult), asOBJ_VALUE | asOBJ_APP_CLASS_C);
@@ -253,6 +268,7 @@ static void RegisterPhysicsWorld(asIScriptEngine* engine)
     engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult SphereCast(const Ray&in, float, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldSphereCast), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "Array<RigidBody@>@ GetRigidBodies(const Sphere&in, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldGetRigidBodiesSphere), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "Array<RigidBody@>@ GetRigidBodies(const BoundingBox&in, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldGetRigidBodiesBox), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("PhysicsWorld", "Array<RigidBody@>@ GetRigidBodies(RigidBody@+)", asFUNCTION(PhysicsWorldGetRigidBodiesBody), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "void DrawDebugGeometry(bool)", asMETHODPR(PhysicsWorld, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void set_gravity(Vector3)", asMETHOD(PhysicsWorld, SetGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "Vector3 get_gravity() const", asMETHOD(PhysicsWorld, GetGravity), asCALL_THISCALL);

+ 24 - 0
Engine/Physics/PhysicsWorld.cpp

@@ -382,6 +382,21 @@ void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const BoundingB
     delete tempRigidBody;
 }
 
+void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody* body)
+{
+    PROFILE(PhysicsWorld_GetRigidBodies);
+    
+    result.Clear();
+    
+    for (HashSet<Pair<RigidBody*, RigidBody*> >::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_);
+    }
+}
+
 Vector3 PhysicsWorld::GetGravity() const
 {
     return ToVector3(world_->getGravity());
@@ -395,6 +410,15 @@ void PhysicsWorld::AddRigidBody(RigidBody* body)
 void PhysicsWorld::RemoveRigidBody(RigidBody* body)
 {
     rigidBodies_.Erase(rigidBodies_.Find(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;
+    }
 }
 
 void PhysicsWorld::AddCollisionShape(CollisionShape* shape)

+ 2 - 0
Engine/Physics/PhysicsWorld.h

@@ -139,6 +139,8 @@ public:
     void GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask = M_MAX_UNSIGNED);
     /// Return rigid bodies by a box query.
     void GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask = M_MAX_UNSIGNED);
+    /// Return rigid bodies that have been in collision with a specific body on the last simulation step.
+    void GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody* body);
     /// Return gravity.
     Vector3 GetGravity() const;
     /// Return whether interpolation between simulation steps is enabled.

+ 8 - 0
Engine/Physics/RigidBody.cpp

@@ -642,6 +642,14 @@ bool RigidBody::IsActive() const
         return false;
 }
 
+void RigidBody::GetCollidingBodies(PODVector<RigidBody*>& result) const
+{
+    if (physicsWorld_)
+        physicsWorld_->GetRigidBodies(result, this);
+    else
+        result.Clear();
+}
+
 void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
 {
     physicsWorld_->SetApplyingTransforms(true);

+ 2 - 0
Engine/Physics/RigidBody.h

@@ -187,6 +187,8 @@ public:
     unsigned GetCollisionMask() const { return collisionMask_; }
     /// Return collision event signaling mode.
     CollisionEventMode GetCollisionEventMode() const { return collisionEventMode_; }
+    /// Return colliding rigid bodies from the last simulation step.
+    void GetCollidingBodies(PODVector<RigidBody*>& result) const;
     
     /// Apply new world transform after a simulation step. Called internally.
     void ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation);