BGE: Support for collision group/mask from the api + activated on EndObject.
authorPorteries Tristan <republicthunderbolt9@gmail.com>
Sat, 18 Apr 2015 23:01:17 +0000 (01:01 +0200)
committerJorge Bernal <jbernalmartinez@gmail.com>
Sat, 18 Apr 2015 23:04:22 +0000 (01:04 +0200)
A Python API for the collision group / mask has been added:
```
KX_GameObject.collisionGroup
KX_GameObject.collisionMask
```
The maximum number of collision groups and masked has been increased from eight to sixteen.
This means that the max value of collisionGroup/Mask is (2 ** 16) - 1

EndObject will now activate objects that were sleeping and colliding with the removed object.
This means that, unlike now, if a rigid body starts sleeping on top of another object, when the latter is removed the rigid body will activate and fall, rather than float midair as before.

Collision groups that do not intersect used to collide on the first frame. Now this has been fixed so that they collide appropriately.

Thanks to agoose77 for his help.

Reviewers: scorpion81, hg1, agoose77, sergof

Reviewed By: agoose77, sergof

Subscribers: sergof, moguri

Projects: #game_physics, #game_engine

Differential Revision: https://developer.blender.org/D1243

doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst
source/blender/blenkernel/intern/object.c
source/blender/makesdna/DNA_object_types.h
source/gameengine/Converter/BL_BlenderDataConversion.cpp
source/gameengine/Ketsji/KX_GameObject.cpp
source/gameengine/Ketsji/KX_GameObject.h
source/gameengine/Physics/Bullet/CcdPhysicsController.cpp
source/gameengine/Physics/Bullet/CcdPhysicsController.h
source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp
source/gameengine/Physics/common/PHY_IPhysicsController.h

index b2cfeb5edc70b641967f49e587648569313ec3a8..7b2f68b019c98d6c61ca6d6260b906c6b72941c5 100644 (file)
@@ -161,6 +161,18 @@ base class --- :class:`SCA_IObject`
 
       :type: :class:`KX_GameObject` or None
 
+   .. attribute:: collisionGroup
+
+      The object's collision group.
+
+      :type: bitfield
+
+   .. attribute:: collisionMask
+
+      The object's collision mask.
+
+      :type: bitfield
+
    .. attribute:: collisionCallbacks
 
       A list of functions to be called when a collision occurs.
index c485e00ed693353bec1781138876c8a0f3da4fdd..f14f72614f44e160468ae2d87705a083b0837453 100644 (file)
@@ -1048,7 +1048,7 @@ Object *BKE_object_add_only_object(Main *bmain, int type, const char *name)
        ob->jump_speed = 10.0f;
        ob->fall_speed = 55.0f;
        ob->col_group = 0x01;
-       ob->col_mask = 0xff;
+       ob->col_mask = 0xffff;
 
        /* NT fluid sim defaults */
        ob->fluidsimSettings = NULL;
index 286461f1b6983fa6b14ce331ad3b9c1aecaee0d2..8bd676caa5e42ebfa782255e9a027a740280cf04 100644 (file)
@@ -530,7 +530,7 @@ enum {
 #define OB_MAX_STATES       30
 
 /* collision masks */
-#define OB_MAX_COL_MASKS    8
+#define OB_MAX_COL_MASKS    16
 
 /* ob->gameflag */
 enum {
index de5e27baf586046773df1bd0bba7d7110ed24683..2f62ffdd69822943fd8072c4e23290458a6cb12c 100644 (file)
@@ -1347,8 +1347,8 @@ static void BL_CreatePhysicsObjectNew(KX_GameObject* gameobj,
        if (!(blenderobject->gameflag & OB_COLLISION)) {
                // Respond to all collisions so that Near sensors work on No Collision
                // objects.
-               gameobj->SetUserCollisionGroup(0xff);
-               gameobj->SetUserCollisionMask(0xff);
+               gameobj->SetUserCollisionGroup(0xffff);
+               gameobj->SetUserCollisionMask(0xffff);
                return;
        }
 
index 412a0b823c79f151e8fff0b901552b20010bfcda..1536b31d1baec65ab11a5764dc84faf6df267e8b 100644 (file)
@@ -47,6 +47,7 @@
 #include "KX_MeshProxy.h"
 #include "KX_PolyProxy.h"
 #include <stdio.h> // printf
+#include <climits> // USHRT_MAX
 #include "SG_Controller.h"
 #include "PHY_IGraphicController.h"
 #include "SG_Node.h"
@@ -561,13 +562,26 @@ void KX_GameObject::ActivateGraphicController(bool recurse)
        }
 }
 
-void KX_GameObject::SetUserCollisionGroup(short group)
+void KX_GameObject::SetUserCollisionGroup(unsigned short group)
 {
        m_userCollisionGroup = group;
+       if (m_pPhysicsController)
+               m_pPhysicsController->RefreshCollisions();
 }
-void KX_GameObject::SetUserCollisionMask(short mask)
+void KX_GameObject::SetUserCollisionMask(unsigned short mask)
 {
        m_userCollisionMask = mask;
+       if (m_pPhysicsController)
+               m_pPhysicsController->RefreshCollisions();
+}
+
+unsigned short KX_GameObject::GetUserCollisionGroup()
+{
+       return m_userCollisionGroup;
+}
+unsigned short KX_GameObject::GetUserCollisionMask()
+{
+       return m_userCollisionMask;
 }
 
 bool KX_GameObject::CheckCollision(KX_GameObject* other)
@@ -2003,6 +2017,8 @@ PyAttributeDef KX_GameObject::Attributes[] = {
        KX_PYATTRIBUTE_RW_FUNCTION("scaling",   KX_GameObject, pyattr_get_worldScaling, pyattr_set_localScaling),
        KX_PYATTRIBUTE_RW_FUNCTION("timeOffset",KX_GameObject, pyattr_get_timeOffset,pyattr_set_timeOffset),
        KX_PYATTRIBUTE_RW_FUNCTION("collisionCallbacks",                KX_GameObject, pyattr_get_collisionCallbacks,   pyattr_set_collisionCallbacks),
+       KX_PYATTRIBUTE_RW_FUNCTION("collisionGroup",                    KX_GameObject, pyattr_get_collisionGroup, pyattr_set_collisionGroup),
+       KX_PYATTRIBUTE_RW_FUNCTION("collisionMask",                             KX_GameObject, pyattr_get_collisionMask, pyattr_set_collisionMask),
        KX_PYATTRIBUTE_RW_FUNCTION("state",             KX_GameObject, pyattr_get_state,        pyattr_set_state),
        KX_PYATTRIBUTE_RO_FUNCTION("meshes",    KX_GameObject, pyattr_get_meshes),
        KX_PYATTRIBUTE_RW_FUNCTION("localOrientation",KX_GameObject,pyattr_get_localOrientation,pyattr_set_localOrientation),
@@ -2349,6 +2365,56 @@ int KX_GameObject::pyattr_set_collisionCallbacks(void *self_v, const KX_PYATTRIB
        return PY_SET_ATTR_SUCCESS;
 }
 
+PyObject *KX_GameObject::pyattr_get_collisionGroup(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+       KX_GameObject* self = static_cast<KX_GameObject*>(self_v);
+       return PyLong_FromLong(self->GetUserCollisionGroup());
+}
+
+int KX_GameObject::pyattr_set_collisionGroup(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+       KX_GameObject* self = static_cast<KX_GameObject*>(self_v);
+       int val = PyLong_AsLong(value);
+
+       if (val == -1 && PyErr_Occurred()) {
+               PyErr_SetString(PyExc_TypeError, "gameOb.collisionGroup = int: KX_GameObject, expected an int bit field");
+               return PY_SET_ATTR_FAIL;
+       }
+
+       if (val < 0 || val > USHRT_MAX) {
+               PyErr_Format(PyExc_AttributeError, "gameOb.collisionGroup = int: KX_GameObject, expected a int bit field between 0 and %i", USHRT_MAX);
+               return PY_SET_ATTR_FAIL;
+       }
+
+       self->SetUserCollisionGroup(val);
+       return PY_SET_ATTR_SUCCESS;
+}
+
+PyObject *KX_GameObject::pyattr_get_collisionMask(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+       KX_GameObject* self = static_cast<KX_GameObject*>(self_v);
+       return PyLong_FromLong(self->GetUserCollisionMask());
+}
+
+int KX_GameObject::pyattr_set_collisionMask(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+       KX_GameObject* self = static_cast<KX_GameObject*>(self_v);
+       int val = PyLong_AsLong(value);
+
+       if (val == -1 && PyErr_Occurred()) {
+               PyErr_SetString(PyExc_TypeError, "gameOb.collisionMask = int: KX_GameObject, expected an int bit field");
+               return PY_SET_ATTR_FAIL;
+       }
+
+       if (val < 0 || val > USHRT_MAX) {
+               PyErr_Format(PyExc_AttributeError, "gameOb.collisionMask = int: KX_GameObject, expected a int bit field between 0 and %i", USHRT_MAX);
+               return PY_SET_ATTR_FAIL;
+       }
+
+       self->SetUserCollisionMask(val);
+       return PY_SET_ATTR_SUCCESS;
+}
+
 PyObject* KX_GameObject::pyattr_get_scene(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
 {
        KX_GameObject *self = static_cast<KX_GameObject*>(self_v);
index 5038feb2d680e33181424131ce119c6a9ab2b163..54bb6ee260547cb882debd47237264da76455a52 100644 (file)
@@ -100,8 +100,8 @@ protected:
        MT_Vector4                                                      m_objectColor;
 
        // Bit fields for user control over physics collisions
-       short                                                           m_userCollisionGroup;
-       short                                                           m_userCollisionMask;
+       unsigned short                                          m_userCollisionGroup;
+       unsigned short                                          m_userCollisionMask;
 
        // visible = user setting
        // culled = while rendering, depending on camera
@@ -517,8 +517,10 @@ public:
         */
        void ActivateGraphicController(bool recurse);
 
-       void SetUserCollisionGroup(short filter);
-       void SetUserCollisionMask(short mask);
+       void SetUserCollisionGroup(unsigned short filter);
+       void SetUserCollisionMask(unsigned short mask);
+       unsigned short GetUserCollisionGroup();
+       unsigned short GetUserCollisionMask();
        /**
         * Extra broadphase check for user controllable collisions
         */
@@ -1087,6 +1089,10 @@ public:
        static int                      pyattr_set_obcolor(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
        static PyObject*        pyattr_get_collisionCallbacks(void *selv_v, const KX_PYATTRIBUTE_DEF *attrdef);
        static int                      pyattr_set_collisionCallbacks(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+       static PyObject*        pyattr_get_collisionGroup(void *selv_v, const KX_PYATTRIBUTE_DEF *attrdef);
+       static int                      pyattr_set_collisionGroup(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+       static PyObject*        pyattr_get_collisionMask(void *selv_v, const KX_PYATTRIBUTE_DEF *attrdef);
+       static int                      pyattr_set_collisionMask(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
        static PyObject*        pyattr_get_debug(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
        static int                      pyattr_set_debug(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
        static PyObject*        pyattr_get_debugRecursive(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
index 5adff8f7f024b51682baddabaf06ef27c3d80b4f..b2b8f30a7060301f3e9b6ef98a32d5bde99f7ee6 100644 (file)
@@ -65,7 +65,6 @@ extern bool gDisableDeactivation;
 float gLinearSleepingTreshold;
 float gAngularSleepingTreshold;
 
-
 BlenderBulletCharacterController::BlenderBulletCharacterController(btMotionState *motionState, btPairCachingGhostObject *ghost, btConvexShape* shape, float stepHeight)
        : btKinematicCharacterController(ghost,shape,stepHeight,2),
                m_motionState(motionState),
@@ -118,6 +117,20 @@ const btVector3& BlenderBulletCharacterController::getWalkDirection()
        return m_walkDirection;
 }
 
+bool CleanPairCallback::processOverlap(btBroadphasePair &pair)
+{
+       if ((pair.m_pProxy0 == m_cleanProxy) || (pair.m_pProxy1 == m_cleanProxy)) {
+               m_pairCache->cleanOverlappingPair(pair, m_dispatcher);
+               CcdPhysicsController *ctrl0 = (CcdPhysicsController*)(((btCollisionObject*)pair.m_pProxy0->m_clientObject)->getUserPointer());
+               CcdPhysicsController *ctrl1 = (CcdPhysicsController*)(((btCollisionObject*)pair.m_pProxy1->m_clientObject)->getUserPointer());
+               if (ctrl0 && ctrl1) {
+                       ctrl0->GetRigidBody()->activate(true);
+                       ctrl1->GetRigidBody()->activate(true);
+               }
+       }
+       return false;
+}
+
 CcdPhysicsController::CcdPhysicsController (const CcdConstructionInfo& ci)
 :m_cci(ci)
 {
@@ -1082,6 +1095,19 @@ void             CcdPhysicsController::ResolveCombinedVelocities(float linvelX,float linvel
 {
 }
 
+void CcdPhysicsController::RefreshCollisions()
+{
+       btSoftRigidDynamicsWorld *dw = GetPhysicsEnvironment()->GetDynamicsWorld();
+       btBroadphaseProxy *proxy = m_object->getBroadphaseHandle();
+       btDispatcher *dispatcher = dw->getDispatcher();
+       btOverlappingPairCache *pairCache = dw->getPairCache();
+
+       CleanPairCallback cleanPairs(proxy, pairCache, dispatcher);
+       pairCache->processAllOverlappingPairs(&cleanPairs, dispatcher);
+       // Forcibly recreate the physics object
+       GetPhysicsEnvironment()->UpdateCcdPhysicsController(this, m_cci.m_mass, m_cci.m_collisionFlags, m_cci.m_collisionFilterGroup, m_cci.m_collisionFilterMask);
+}
+
 void   CcdPhysicsController::SuspendDynamics(bool ghost)
 {
        btRigidBody *body = GetRigidBody();
index 56d67ca2f64a30e674cd4589d710b23aedd513c7..4fcdc70c5a8eea0cd6bae6259813085c8a425193 100644 (file)
@@ -447,6 +447,23 @@ public:
 #endif
 };
 
+class CleanPairCallback : public btOverlapCallback
+{
+       btBroadphaseProxy *m_cleanProxy;
+       btOverlappingPairCache *m_pairCache;
+       btDispatcher *m_dispatcher;
+
+public:
+       CleanPairCallback(btBroadphaseProxy *cleanProxy, btOverlappingPairCache *pairCache, btDispatcher *dispatcher)
+               :m_cleanProxy(cleanProxy),
+               m_pairCache(pairCache),
+               m_dispatcher(dispatcher)
+       {
+       }
+
+       virtual bool processOverlap(btBroadphasePair &pair);
+};
+
 ///CcdPhysicsController is a physics object that supports continuous collision detection and time of impact based physics resolution.
 class CcdPhysicsController : public PHY_IPhysicsController
 {
@@ -598,7 +615,7 @@ protected:
 
                
                virtual void            ResolveCombinedVelocities(float linvelX,float linvelY,float linvelZ,float angVelX,float angVelY,float angVelZ);
-
+               virtual void            RefreshCollisions();
                virtual void            SuspendDynamics(bool ghost);
                virtual void            RestoreDynamics();
 
index 6d3f6d9ddcbb135d8d3635c0ba5e74fc295e6db3..984273ec63e91d1bd78f5c99825acd9d9740eb8c 100644 (file)
@@ -509,6 +509,13 @@ bool       CcdPhysicsEnvironment::RemoveCcdPhysicsController(CcdPhysicsController* ctr
        btRigidBody* body = ctrl->GetRigidBody();
        if (body)
        {
+               btBroadphaseProxy *proxy = ctrl->GetCollisionObject()->getBroadphaseHandle();
+               btDispatcher *dispatcher = m_dynamicsWorld->getDispatcher();
+               btOverlappingPairCache *pairCache = m_dynamicsWorld->getPairCache();
+
+               CleanPairCallback cleanPairs(proxy, pairCache, dispatcher);
+               pairCache->processAllOverlappingPairs(&cleanPairs, dispatcher);
+
                for (int i = ctrl->getNumCcdConstraintRefs() - 1; i >= 0; i--)
                {
                        btTypedConstraint* con = ctrl->getCcdConstraintRef(i);
@@ -3525,12 +3532,14 @@ void CcdPhysicsEnvironment::ConvertObject(KX_GameObject *gameobj, RAS_MeshObject
        if (isbulletdyna)
                gameobj->SetRecordAnimation(true);
 
+       physicscontroller->SetNewClientInfo(gameobj->getClientInfo());
+
        // don't add automatically sensor object, they are added when a collision sensor is registered
        if (!isbulletsensor && (blenderobject->lay & activeLayerBitInfo) != 0)
        {
                this->AddCcdPhysicsController( physicscontroller);
        }
-       physicscontroller->SetNewClientInfo(gameobj->getClientInfo());
+
        {
                btRigidBody* rbody = physicscontroller->GetRigidBody();
 
index b6cd480e655c039aa9a78092cacfd8afdec89cd0..3e2337f01ea231c05b835382896522394dc60fcd 100644 (file)
@@ -96,6 +96,7 @@ class PHY_IPhysicsController : public PHY_IController
                virtual void            SetAngularDamping(float damping)=0;
                virtual void            SetDamping(float linear, float angular)=0;
 
+               virtual void            RefreshCollisions() = 0;
                virtual void            SuspendDynamics(bool ghost=false)=0;
                virtual void            RestoreDynamics()=0;