[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [60132] trunk/blender: BGE: Adding a Python collision API.

Mitchell Stokes mogurijin at gmail.com
Sat Sep 14 04:03:00 CEST 2013


Revision: 60132
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=60132
Author:   moguri
Date:     2013-09-14 02:02:58 +0000 (Sat, 14 Sep 2013)
Log Message:
-----------
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).

Modified Paths:
--------------
    trunk/blender/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst
    trunk/blender/source/gameengine/Ketsji/KX_GameObject.cpp
    trunk/blender/source/gameengine/Ketsji/KX_GameObject.h
    trunk/blender/source/gameengine/Ketsji/KX_TouchEventManager.cpp

Modified: trunk/blender/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst
===================================================================
--- trunk/blender/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst	2013-09-14 01:40:07 UTC (rev 60131)
+++ trunk/blender/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst	2013-09-14 02:02:58 UTC (rev 60132)
@@ -134,6 +134,12 @@
 
       :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).

Modified: trunk/blender/source/gameengine/Ketsji/KX_GameObject.cpp
===================================================================
--- trunk/blender/source/gameengine/Ketsji/KX_GameObject.cpp	2013-09-14 01:40:07 UTC (rev 60131)
+++ trunk/blender/source/gameengine/Ketsji/KX_GameObject.cpp	2013-09-14 02:02:58 UTC (rev 60132)
@@ -114,8 +114,10 @@
       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()
 {
+#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 @@
 	{
 		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 @@
 }
 
 
+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 @@
 	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 @@
 	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);

Modified: trunk/blender/source/gameengine/Ketsji/KX_GameObject.h
===================================================================
--- trunk/blender/source/gameengine/Ketsji/KX_GameObject.h	2013-09-14 01:40:07 UTC (rev 60131)
+++ trunk/blender/source/gameengine/Ketsji/KX_GameObject.h	2013-09-14 02:02:58 UTC (rev 60132)
@@ -137,21 +137,22 @@
 
 #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 @@
 	 * \section Logic bubbling methods.
 	 */
 
+	void RegisterCollisionCallbacks();
+	void UnregisterCollisionCallbacks();
+	void RunCollisionCallbacks(KX_GameObject *collider);
 	/**
 	 * Stop making progress
 	 */
@@ -1040,6 +1044,8 @@
 	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);

Modified: trunk/blender/source/gameengine/Ketsji/KX_TouchEventManager.cpp
===================================================================
--- trunk/blender/source/gameengine/Ketsji/KX_TouchEventManager.cpp	2013-09-14 01:40:07 UTC (rev 60131)
+++ trunk/blender/source/gameengine/Ketsji/KX_TouchEventManager.cpp	2013-09-14 02:02:58 UTC (rev 60132)
@@ -81,28 +81,42 @@
 							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;
+

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list