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.
 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.
 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
 \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
 \page UI User interface
 
 

+ 27 - 3
Docs/ScriptAPI.dox

@@ -2628,6 +2628,30 @@ Properties:<br>
 - bool compressed (readonly)
 - 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
 SoundSource
 
 
 Methods:<br>
 Methods:<br>
@@ -2714,14 +2738,12 @@ Methods:<br>
 - void SetMode(int, int, bool, bool arg3 = true)
 - void SetMode(int, int, bool, bool arg3 = true)
 - bool Play()
 - bool Play()
 - void Stop()
 - void Stop()
-- void SetListenerTransform(const Vector3&, const Quaternion&)
 
 
 Properties:<br>
 Properties:<br>
 - ShortStringHash type (readonly)
 - ShortStringHash type (readonly)
 - String typeName (readonly)
 - String typeName (readonly)
 - float[] masterGain
 - float[] masterGain
-- Vector3 listenerPosition
-- Quaternion listenerRotation
+- SoundListener@ listener
 - uint sampleSize (readonly)
 - uint sampleSize (readonly)
 - int mixRate (readonly)
 - int mixRate (readonly)
 - bool stereo (readonly)
 - bool stereo (readonly)
@@ -4496,6 +4518,7 @@ Properties:<br>
 - uint collisionLayer
 - uint collisionLayer
 - uint collisionMask
 - uint collisionMask
 - CollisionEventMode collisionEventMode
 - CollisionEventMode collisionEventMode
+- RigidBody@[]@ collidingBodies (readonly)
 
 
 
 
 Constraint
 Constraint
@@ -4564,6 +4587,7 @@ Methods:<br>
 - PhysicsRaycastResult SphereCast(const Ray&, float, float arg2 = M_INFINITY, uint arg3 = 0xffff)
 - PhysicsRaycastResult SphereCast(const Ray&, float, float arg2 = M_INFINITY, uint arg3 = 0xffff)
 - RigidBody@[]@ GetRigidBodies(const Sphere&, uint arg1 = 0xffff)
 - RigidBody@[]@ GetRigidBodies(const Sphere&, uint arg1 = 0xffff)
 - RigidBody@[]@ GetRigidBodies(const BoundingBox&, uint arg1 = 0xffff)
 - RigidBody@[]@ GetRigidBodies(const BoundingBox&, uint arg1 = 0xffff)
+- RigidBody@[]@ GetRigidBodies(RigidBody@)
 - void DrawDebugGeometry(bool)
 - void DrawDebugGeometry(bool)
 
 
 Properties:<br>
 Properties:<br>

+ 31 - 2
Engine/Container/HashMap.h

@@ -27,6 +27,8 @@
 #include "Pair.h"
 #include "Pair.h"
 #include "Sort.h"
 #include "Sort.h"
 
 
+#include <cassert>
+
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
@@ -297,13 +299,40 @@ public:
             previous->down_ = node->down_;
             previous->down_ = node->down_;
         else
         else
             Ptrs()[hashKey] = node->down_;
             Ptrs()[hashKey] = node->down_;
-        EraseNode(node);
         
         
+        EraseNode(node);
         return true;
         return true;
     }
     }
     
     
     /// Erase a pair by iterator.
     /// 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.
     /// Clear the map.
     void Clear()
     void Clear()

+ 31 - 2
Engine/Container/HashSet.h

@@ -26,6 +26,8 @@
 #include "HashBase.h"
 #include "HashBase.h"
 #include "Sort.h"
 #include "Sort.h"
 
 
+#include <cassert>
+
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
@@ -267,13 +269,40 @@ public:
             previous->down_ = node->down_;
             previous->down_ = node->down_;
         else
         else
             Ptrs()[hashKey] = node->down_;
             Ptrs()[hashKey] = node->down_;
-        EraseNode(node);
         
         
+        EraseNode(node);
         return true;
         return true;
     }
     }
     
     
     /// Erase a key by iterator.
     /// 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.
     /// Clear the set.
     void Clear()
     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);
     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)
 static void RegisterRigidBody(asIScriptEngine* engine)
 {
 {
     engine->RegisterEnum("CollisionEventMode");
     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", "uint get_collisionMask() const", asMETHOD(RigidBody, GetCollisionMask), asCALL_THISCALL);
     engine->RegisterObjectMethod("RigidBody", "void set_collisionEventMode(CollisionEventMode)", asMETHOD(RigidBody, SetCollisionEventMode), 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", "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
     // Register Variant GetPtr() for RigidBody
     engine->RegisterObjectMethod("Variant", "RigidBody@+ GetRigidBody() const", asFUNCTION(GetVariantPtr<RigidBody>), asCALL_CDECL_OBJLAST);
     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@>");
     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)
 static void RegisterPhysicsWorld(asIScriptEngine* engine)
 {
 {
     engine->RegisterObjectType("PhysicsRaycastResult", sizeof(PhysicsRaycastResult), asOBJ_VALUE | asOBJ_APP_CLASS_C);
     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", "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 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(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 DrawDebugGeometry(bool)", asMETHODPR(PhysicsWorld, DrawDebugGeometry, (bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void set_gravity(Vector3)", asMETHOD(PhysicsWorld, SetGravity), 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);
     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;
     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
 Vector3 PhysicsWorld::GetGravity() const
 {
 {
     return ToVector3(world_->getGravity());
     return ToVector3(world_->getGravity());
@@ -395,6 +410,15 @@ void PhysicsWorld::AddRigidBody(RigidBody* body)
 void PhysicsWorld::RemoveRigidBody(RigidBody* body)
 void PhysicsWorld::RemoveRigidBody(RigidBody* body)
 {
 {
     rigidBodies_.Erase(rigidBodies_.Find(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)
 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);
     void GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask = M_MAX_UNSIGNED);
     /// Return rigid bodies by a box query.
     /// Return rigid bodies by a box query.
     void GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask = M_MAX_UNSIGNED);
     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.
     /// Return gravity.
     Vector3 GetGravity() const;
     Vector3 GetGravity() const;
     /// Return whether interpolation between simulation steps is enabled.
     /// Return whether interpolation between simulation steps is enabled.

+ 8 - 0
Engine/Physics/RigidBody.cpp

@@ -642,6 +642,14 @@ bool RigidBody::IsActive() const
         return false;
         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)
 void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
 {
 {
     physicsWorld_->SetApplyingTransforms(true);
     physicsWorld_->SetApplyingTransforms(true);

+ 2 - 0
Engine/Physics/RigidBody.h

@@ -187,6 +187,8 @@ public:
     unsigned GetCollisionMask() const { return collisionMask_; }
     unsigned GetCollisionMask() const { return collisionMask_; }
     /// Return collision event signaling mode.
     /// Return collision event signaling mode.
     CollisionEventMode GetCollisionEventMode() const { return collisionEventMode_; }
     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.
     /// Apply new world transform after a simulation step. Called internally.
     void ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation);
     void ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation);