diff --git a/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst b/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst index b2cfeb5edc7..7b2f68b019c 100644 --- a/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst +++ b/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst @@ -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. diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index c485e00ed69..f14f72614f4 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -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; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 286461f1b69..8bd676caa5e 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -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 { diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp index de5e27baf58..2f62ffdd698 100644 --- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp +++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp @@ -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; } diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index 412a0b823c7..1536b31d1ba 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -47,6 +47,7 @@ #include "KX_MeshProxy.h" #include "KX_PolyProxy.h" #include // printf +#include // 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(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(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(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(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(self_v); diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 5038feb2d68..54bb6ee2605 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -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); diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp index 5adff8f7f02..b2b8f30a706 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp @@ -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(); diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.h b/source/gameengine/Physics/Bullet/CcdPhysicsController.h index 56d67ca2f64..4fcdc70c5a8 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.h @@ -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(); diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index 6d3f6d9ddcb..984273ec63e 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -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(); diff --git a/source/gameengine/Physics/common/PHY_IPhysicsController.h b/source/gameengine/Physics/common/PHY_IPhysicsController.h index b6cd480e655..3e2337f01ea 100644 --- a/source/gameengine/Physics/common/PHY_IPhysicsController.h +++ b/source/gameengine/Physics/common/PHY_IPhysicsController.h @@ -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;