BGE: Support for collision group/mask from the api + activated on EndObject.

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
This commit is contained in:
Porteries Tristan 2015-04-19 01:01:17 +02:00 committed by Jorge Bernal
parent a2f9a0cfd9
commit 3d55859924
Notes: blender-bot 2023-02-14 11:00:17 +01:00
Referenced by commit 546031b694, BGE : fix bugs with physics collision mask/group
Referenced by issue #44460, Ctrl+Shift+Click - makes extrude+lassoSelection
10 changed files with 150 additions and 13 deletions

View 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.

View 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;

View 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 {

View 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;
}

View 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);

View 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);

View 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();

View 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();

View 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();

View 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;