forked from bartvdbraak/blender
BGE: Adding a Python collision API. The initial patch was provided by agoose77, with some edits by me.
KX_GameObject now has a collisionCallbacks list which is a list of callables that are called when a collision occurs. The callables will be called with an argument that contains a reference to the other object involved in the collision (i.e., not self).
This commit is contained in:
parent
9f87d9963e
commit
6365678109
@ -134,6 +134,12 @@ base class --- :class:`SCA_IObject`
|
||||
|
||||
:type: :class:`KX_GameObject` or None
|
||||
|
||||
.. attribute:: collisionCallbacks
|
||||
|
||||
A list of callables to be run when a collision occurs.
|
||||
|
||||
:type: list
|
||||
|
||||
.. attribute:: scene
|
||||
|
||||
The object's scene. (read-only).
|
||||
|
@ -114,8 +114,10 @@ KX_GameObject::KX_GameObject(
|
||||
m_pDupliGroupObject(NULL),
|
||||
m_actionManager(NULL),
|
||||
m_isDeformable(false)
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
, m_attr_dict(NULL)
|
||||
, m_attr_dict(NULL),
|
||||
m_collisionCallbacks(NULL)
|
||||
#endif
|
||||
{
|
||||
m_ignore_activity_culling = false;
|
||||
@ -133,6 +135,20 @@ KX_GameObject::KX_GameObject(
|
||||
|
||||
KX_GameObject::~KX_GameObject()
|
||||
{
|
||||
#ifdef WITH_PYTHON
|
||||
if (m_attr_dict) {
|
||||
PyDict_Clear(m_attr_dict); /* in case of circular refs or other weird cases */
|
||||
/* Py_CLEAR: Py_DECREF's and NULL's */
|
||||
Py_CLEAR(m_attr_dict);
|
||||
}
|
||||
// Unregister collision callbacks
|
||||
// Do this before we start freeing physics information like m_pClient_info
|
||||
if (m_collisionCallbacks){
|
||||
UnregisterCollisionCallbacks();
|
||||
Py_CLEAR(m_collisionCallbacks);
|
||||
}
|
||||
#endif // WITH_PYTHON
|
||||
|
||||
RemoveMeshes();
|
||||
|
||||
// is this delete somewhere ?
|
||||
@ -180,13 +196,6 @@ KX_GameObject::~KX_GameObject()
|
||||
{
|
||||
m_pInstanceObjects->Release();
|
||||
}
|
||||
#ifdef WITH_PYTHON
|
||||
if (m_attr_dict) {
|
||||
PyDict_Clear(m_attr_dict); /* in case of circular refs or other weird cases */
|
||||
/* Py_CLEAR: Py_DECREF's and NULL's */
|
||||
Py_CLEAR(m_attr_dict);
|
||||
}
|
||||
#endif // WITH_PYTHON
|
||||
}
|
||||
|
||||
KX_GameObject* KX_GameObject::GetClientObject(KX_ClientObjectInfo *info)
|
||||
@ -1336,6 +1345,77 @@ const MT_Point3& KX_GameObject::NodeGetLocalPosition() const
|
||||
}
|
||||
|
||||
|
||||
void KX_GameObject::UnregisterCollisionCallbacks()
|
||||
{
|
||||
if (!GetPhysicsController()) {
|
||||
printf("Warning, trying to unregister collision callbacks for object without collisions: %s!\n", GetName().ReadPtr());
|
||||
return;
|
||||
}
|
||||
|
||||
// Unregister from callbacks
|
||||
KX_Scene* scene = GetScene();
|
||||
PHY_IPhysicsEnvironment* pe = scene->GetPhysicsEnvironment();
|
||||
PHY_IPhysicsController* spc = static_cast<PHY_IPhysicsController*> (GetPhysicsController()->GetUserData());
|
||||
// If we are the last to unregister on this physics controller
|
||||
if (pe->removeCollisionCallback(spc)){
|
||||
// If we are a sensor object
|
||||
if (m_pClient_info->isSensor())
|
||||
// Remove sensor body from physics world
|
||||
pe->removeSensor(spc);
|
||||
}
|
||||
}
|
||||
|
||||
void KX_GameObject::RegisterCollisionCallbacks()
|
||||
{
|
||||
if (!GetPhysicsController()) {
|
||||
printf("Warning, trying to register collision callbacks for object without collisions: %s!\n", GetName().ReadPtr());
|
||||
return;
|
||||
}
|
||||
|
||||
// Register from callbacks
|
||||
KX_Scene* scene = GetScene();
|
||||
PHY_IPhysicsEnvironment* pe = scene->GetPhysicsEnvironment();
|
||||
PHY_IPhysicsController* spc = static_cast<PHY_IPhysicsController*> (GetPhysicsController()->GetUserData());
|
||||
// If we are the first to register on this physics controller
|
||||
if (pe->requestCollisionCallback(spc)){
|
||||
// If we are a sensor object
|
||||
if (m_pClient_info->isSensor())
|
||||
// Add sensor body to physics world
|
||||
pe->addSensor(spc);
|
||||
}
|
||||
}
|
||||
void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider)
|
||||
{
|
||||
#ifdef WITH_PYTHON
|
||||
Py_ssize_t len;
|
||||
PyObject* collision_callbacks = m_collisionCallbacks;
|
||||
|
||||
if (collision_callbacks && (len=PyList_GET_SIZE(collision_callbacks)))
|
||||
{
|
||||
PyObject* args = Py_BuildValue("(O)", collider->GetProxy()); // save python creating each call
|
||||
PyObject *func;
|
||||
PyObject *ret;
|
||||
|
||||
// Iterate the list and run the callbacks
|
||||
for (Py_ssize_t pos=0; pos < len; pos++)
|
||||
{
|
||||
func = PyList_GET_ITEM(collision_callbacks, pos);
|
||||
ret = PyObject_Call(func, args, NULL);
|
||||
|
||||
if (ret == NULL) {
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
}
|
||||
else {
|
||||
Py_DECREF(ret);
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(args);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Suspend/ resume: for the dynamic behavior, there is a simple
|
||||
* method. For the residual motion, there is not. I wonder what the
|
||||
* correct solution is for Sumo. Remove from the motion-update tree?
|
||||
@ -1716,6 +1796,7 @@ PyAttributeDef KX_GameObject::Attributes[] = {
|
||||
KX_PYATTRIBUTE_RW_FUNCTION("orientation",KX_GameObject,pyattr_get_worldOrientation,pyattr_set_localOrientation),
|
||||
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("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),
|
||||
@ -2008,6 +2089,51 @@ PyObject *KX_GameObject::pyattr_get_group_members(void *self_v, const KX_PYATTRI
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject* KX_GameObject::pyattr_get_collisionCallbacks(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
|
||||
{
|
||||
KX_GameObject* self = static_cast<KX_GameObject*>(self_v);
|
||||
|
||||
// Only objects with a physics controller should have collision callbacks
|
||||
if (!self->GetPhysicsController()) {
|
||||
PyErr_SetString(PyExc_AttributeError, "KX_GameObject.collisionCallbacks: attribute only available for objects with collisions enabled");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return the existing callbacks
|
||||
if (self->m_collisionCallbacks == NULL)
|
||||
{
|
||||
self->m_collisionCallbacks = PyList_New(0);
|
||||
// Subscribe to collision update from KX_TouchManager
|
||||
self->RegisterCollisionCallbacks();
|
||||
}
|
||||
Py_INCREF(self->m_collisionCallbacks);
|
||||
return self->m_collisionCallbacks;
|
||||
}
|
||||
|
||||
int KX_GameObject::pyattr_set_collisionCallbacks(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
|
||||
{
|
||||
KX_GameObject* self = static_cast<KX_GameObject*>(self_v);
|
||||
|
||||
// Only objects with a physics controller should have collision callbacks
|
||||
if (!self->GetPhysicsController()) {
|
||||
PyErr_SetString(PyExc_AttributeError, "KX_GameObject.collisionCallbacks: attribute only available for objects with collisions enabled");
|
||||
return PY_SET_ATTR_FAIL;
|
||||
}
|
||||
|
||||
if (!PyList_CheckExact(value))
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Expected a list");
|
||||
return PY_SET_ATTR_FAIL;
|
||||
}
|
||||
|
||||
Py_XDECREF(self->m_collisionCallbacks);
|
||||
Py_INCREF(value);
|
||||
|
||||
self->m_collisionCallbacks = value;
|
||||
|
||||
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);
|
||||
|
@ -152,6 +152,7 @@ public:
|
||||
// * when assigning a value, first see if it can be a CValue, if it can remove the "m_attr_dict" and set the CValue
|
||||
//
|
||||
PyObject* m_attr_dict;
|
||||
PyObject* m_collisionCallbacks;
|
||||
#endif
|
||||
|
||||
virtual void /* This function should be virtual - derived classed override it */
|
||||
@ -872,6 +873,9 @@ public:
|
||||
* \section Logic bubbling methods.
|
||||
*/
|
||||
|
||||
void RegisterCollisionCallbacks();
|
||||
void UnregisterCollisionCallbacks();
|
||||
void RunCollisionCallbacks(KX_GameObject *collider);
|
||||
/**
|
||||
* Stop making progress
|
||||
*/
|
||||
@ -1040,6 +1044,8 @@ public:
|
||||
static PyObject* pyattr_get_attrDict(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
|
||||
static PyObject* pyattr_get_obcolor(void *selv_v, const KX_PYATTRIBUTE_DEF *attrdef);
|
||||
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);
|
||||
|
||||
/* Experimental! */
|
||||
static PyObject* pyattr_get_sensors(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
|
||||
|
@ -81,28 +81,42 @@ bool KX_TouchEventManager::newBroadphaseResponse(void *client_data,
|
||||
void *object2,
|
||||
const PHY_CollData *coll_data)
|
||||
{
|
||||
PHY_IPhysicsController* ctrl = static_cast<PHY_IPhysicsController*>(object1);
|
||||
KX_ClientObjectInfo *info = (ctrl) ? static_cast<KX_ClientObjectInfo*>(ctrl->getNewClientInfo()) : NULL;
|
||||
PHY_IPhysicsController* ctrl1 = static_cast<PHY_IPhysicsController*>(object1);
|
||||
PHY_IPhysicsController* ctrl2 = static_cast<PHY_IPhysicsController*>(object2);
|
||||
|
||||
KX_ClientObjectInfo *info1 = (ctrl1) ? static_cast<KX_ClientObjectInfo*>(ctrl1->getNewClientInfo()) : NULL;
|
||||
KX_ClientObjectInfo *info2 = (ctrl1) ? static_cast<KX_ClientObjectInfo*>(ctrl2->getNewClientInfo()) : NULL;
|
||||
|
||||
// This call back should only be called for controllers of Near and Radar sensor
|
||||
if (!info)
|
||||
if (!info1)
|
||||
return true;
|
||||
|
||||
switch (info->m_type)
|
||||
// Get KX_GameObjects for callbacks
|
||||
KX_GameObject* gobj1 = info1->m_gameobject;
|
||||
KX_GameObject* gobj2 = (info2) ? info1->m_gameobject : NULL;
|
||||
|
||||
bool has_py_callbacks = false;
|
||||
|
||||
// Consider callbacks for broadphase inclusion if it's a sensor object type
|
||||
if (gobj1 && gobj2)
|
||||
has_py_callbacks = gobj1->m_collisionCallbacks || gobj2->m_collisionCallbacks;
|
||||
|
||||
switch (info1->m_type)
|
||||
{
|
||||
case KX_ClientObjectInfo::SENSOR:
|
||||
if (info->m_sensors.size() == 1)
|
||||
if (info1->m_sensors.size() == 1)
|
||||
{
|
||||
// only one sensor for this type of object
|
||||
KX_TouchSensor* touchsensor = static_cast<KX_TouchSensor*>(*info->m_sensors.begin());
|
||||
return touchsensor->BroadPhaseFilterCollision(object1,object2);
|
||||
KX_TouchSensor* touchsensor = static_cast<KX_TouchSensor*>(*info1->m_sensors.begin());
|
||||
return touchsensor->BroadPhaseFilterCollision(object1, object2);
|
||||
}
|
||||
break;
|
||||
case KX_ClientObjectInfo::OBSENSOR:
|
||||
case KX_ClientObjectInfo::OBACTORSENSOR:
|
||||
// this object may have multiple collision sensors,
|
||||
// check is any of them is interested in this object
|
||||
for (std::list<SCA_ISensor*>::iterator it = info->m_sensors.begin();
|
||||
it != info->m_sensors.end();
|
||||
for (std::list<SCA_ISensor*>::iterator it = info1->m_sensors.begin();
|
||||
it != info1->m_sensors.end();
|
||||
++it)
|
||||
{
|
||||
if ((*it)->GetSensorType() == SCA_ISensor::ST_TOUCH)
|
||||
@ -112,7 +126,8 @@ bool KX_TouchEventManager::newBroadphaseResponse(void *client_data,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
return has_py_callbacks;
|
||||
|
||||
// quiet the compiler
|
||||
case KX_ClientObjectInfo::STATIC:
|
||||
@ -155,32 +170,43 @@ void KX_TouchEventManager::EndFrame()
|
||||
|
||||
void KX_TouchEventManager::NextFrame()
|
||||
{
|
||||
if (!m_sensors.Empty())
|
||||
{
|
||||
SG_DList::iterator<KX_TouchSensor> it(m_sensors);
|
||||
for (it.begin();!it.end();++it)
|
||||
(*it)->SynchronizeTransform();
|
||||
|
||||
for (std::set<NewCollision>::iterator cit = m_newCollisions.begin(); cit != m_newCollisions.end(); ++cit)
|
||||
{
|
||||
// Controllers
|
||||
PHY_IPhysicsController* ctrl1 = (*cit).first;
|
||||
// PHY_IPhysicsController* ctrl2 = (*cit).second;
|
||||
// KX_GameObject* gameOb1 = ctrl1->getClientInfo();
|
||||
// KX_GameObject* gameOb1 = ctrl1->getClientInfo();
|
||||
PHY_IPhysicsController* ctrl2 = (*cit).second;
|
||||
|
||||
KX_ClientObjectInfo *client_info = static_cast<KX_ClientObjectInfo *>(ctrl1->getNewClientInfo());
|
||||
// Sensor iterator
|
||||
list<SCA_ISensor*>::iterator sit;
|
||||
|
||||
// First client info
|
||||
KX_ClientObjectInfo *client_info = static_cast<KX_ClientObjectInfo*>(ctrl1->getNewClientInfo());
|
||||
// First gameobject
|
||||
KX_GameObject *kxObj1 = KX_GameObject::GetClientObject(client_info);
|
||||
// Invoke sensor response for each object
|
||||
if (client_info) {
|
||||
for ( sit = client_info->m_sensors.begin(); sit != client_info->m_sensors.end(); ++sit) {
|
||||
static_cast<KX_TouchSensor*>(*sit)->NewHandleCollision((*cit).first, (*cit).second, NULL);
|
||||
static_cast<KX_TouchSensor*>(*sit)->NewHandleCollision(ctrl1, ctrl2, NULL);
|
||||
}
|
||||
}
|
||||
client_info = static_cast<KX_ClientObjectInfo *>((*cit).second->getNewClientInfo());
|
||||
|
||||
// Second client info
|
||||
client_info = static_cast<KX_ClientObjectInfo *>(ctrl2->getNewClientInfo());
|
||||
// Second gameobject
|
||||
KX_GameObject *kxObj2 = KX_GameObject::GetClientObject(client_info);
|
||||
if (client_info) {
|
||||
for ( sit = client_info->m_sensors.begin(); sit != client_info->m_sensors.end(); ++sit) {
|
||||
static_cast<KX_TouchSensor*>(*sit)->NewHandleCollision((*cit).second, (*cit).first, NULL);
|
||||
static_cast<KX_TouchSensor*>(*sit)->NewHandleCollision(ctrl2, ctrl1, NULL);
|
||||
}
|
||||
}
|
||||
// Run python callbacks
|
||||
kxObj1->RunCollisionCallbacks(kxObj2);
|
||||
kxObj2->RunCollisionCallbacks(kxObj1);
|
||||
|
||||
}
|
||||
|
||||
m_newCollisions.clear();
|
||||
@ -188,4 +214,3 @@ void KX_TouchEventManager::NextFrame()
|
||||
for (it.begin();!it.end();++it)
|
||||
(*it)->Activate(m_logicmgr);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user