Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitchell Stokes <mogurijin@gmail.com>2013-09-14 06:02:58 +0400
committerMitchell Stokes <mogurijin@gmail.com>2013-09-14 06:02:58 +0400
commit63656781098e1443cd3f89fa435688727076eb33 (patch)
treedf5ba9da8daf43a3c149c9af5eb460c24f6d2974
parent9f87d9963eb8c5320c07429c1590beff54658440 (diff)
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).
-rw-r--r--doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst6
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp142
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.h16
-rw-r--r--source/gameengine/Ketsji/KX_TouchEventManager.cpp65
4 files changed, 196 insertions, 33 deletions
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 3be148556ef..5d699637eb7 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
@@ -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).
diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp
index e0ec4983739..e06f7ab6633 100644
--- a/source/gameengine/Ketsji/KX_GameObject.cpp
+++ b/source/gameengine/Ketsji/KX_GameObject.cpp
@@ -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);
diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h
index 5a3b9df74ee..dde3ff53299 100644
--- a/source/gameengine/Ketsji/KX_GameObject.h
+++ b/source/gameengine/Ketsji/KX_GameObject.h
@@ -137,21 +137,22 @@ public:
#ifdef WITH_PYTHON
// Python attributes that wont convert into CValue
- //
+ //
// there are 2 places attributes can be stored, in the CValue,
// where attributes are converted into BGE's CValue types
// these can be used with property actuators
//
// For the python API, For types that cannot be converted into CValues (lists, dicts, GameObjects)
// these will be put into "m_attr_dict", logic bricks cannot access them.
- //
+ //
// rules for setting attributes.
- //
+ //
// * there should NEVER be a CValue and a m_attr_dict attribute with matching names. get/sets make sure of this.
// * if CValue conversion fails, use a PyObject in "m_attr_dict"
// * 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_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);
diff --git a/source/gameengine/Ketsji/KX_TouchEventManager.cpp b/source/gameengine/Ketsji/KX_TouchEventManager.cpp
index 96872f4e6fd..fcb09ebfec6 100644
--- a/source/gameengine/Ketsji/KX_TouchEventManager.cpp
+++ b/source/gameengine/Ketsji/KX_TouchEventManager.cpp
@@ -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);
}
-}