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:
-rw-r--r--doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst41
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp46
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.h2
-rw-r--r--source/gameengine/Ketsji/KX_TouchEventManager.cpp24
-rw-r--r--source/gameengine/Ketsji/KX_TouchEventManager.h24
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp18
6 files changed, 143 insertions, 12 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 8b2edf183e4..4a9e5dabe1b 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
@@ -157,9 +157,46 @@ base class --- :class:`SCA_IObject`
.. attribute:: collisionCallbacks
- A list of callables to be run when a collision occurs.
+ A list of functions to be called when a collision occurs.
- :type: list
+ :type: list of functions and/or methods
+
+ Callbacks should either accept one argument `(object)`, or three
+ arguments `(object, point, normal)`. For simplicity, per
+ colliding object only the first collision point is reported.
+
+ .. code-block:: python
+
+ # Function form
+ def callback_three(object, point, normal):
+ print('Hit by %r at %s with normal %s' % (object.name, point, normal))
+
+ def callback_one(object):
+ print('Hit by %r' % object.name)
+
+ def register_callback(controller):
+ controller.owner.collisionCallbacks.append(callback_three)
+ controller.owner.collisionCallbacks.append(callback_one)
+
+
+ # Method form
+ class YourGameEntity(bge.types.KX_GameObject):
+ def __init__(self, old_owner):
+ self.collisionCallbacks.append(self.on_collision_three)
+ self.collisionCallbacks.append(self.on_collision_one)
+
+ def on_collision_three(self, object, point, normal):
+ print('Hit by %r at %s with normal %s' % (object.name, point, normal))
+
+ def on_collision_one(self, object):
+ print('Hit by %r' % object.name)
+
+ .. note::
+ For backward compatibility, a callback with variable number of
+ arguments (using `*args`) will be passed only the `object`
+ argument. Only when there is more than one fixed argument (not
+ counting `self` for methods) will the three-argument form be
+ used.
.. attribute:: scene
diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp
index e8b68d20e84..6d4b5564e19 100644
--- a/source/gameengine/Ketsji/KX_GameObject.cpp
+++ b/source/gameengine/Ketsji/KX_GameObject.cpp
@@ -77,6 +77,8 @@ typedef unsigned long uint_ptr;
#include "BL_Action.h"
#include "PyObjectPlus.h" /* python stuff */
+#include "BLI_utildefines.h"
+#include "python_utildefines.h"
// This file defines relationships between parents and children
// in the game engine.
@@ -1498,7 +1500,7 @@ void KX_GameObject::RegisterCollisionCallbacks()
pe->AddSensor(spc);
}
}
-void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider)
+void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider, const MT_Vector3 &point, const MT_Vector3 &normal)
{
#ifdef WITH_PYTHON
Py_ssize_t len;
@@ -1506,15 +1508,50 @@ void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider)
if (collision_callbacks && (len=PyList_GET_SIZE(collision_callbacks)))
{
- PyObject* args = Py_BuildValue("(O)", collider->GetProxy()); // save python creating each call
+ // Argument tuples are created lazily, only when they are needed.
+ PyObject *args_3 = NULL;
+ PyObject *args_1 = NULL; // Only for compatibility with pre-2.74 callbacks that take 1 argument.
+
PyObject *func;
PyObject *ret;
+ int co_argcount;
// 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);
+
+ // Get the number of arguments, supporting functions, methods and generic callables.
+ if (PyMethod_Check(func)) {
+ // Take away the 'self' argument for methods.
+ co_argcount = ((PyCodeObject *)PyFunction_GET_CODE(PyMethod_GET_FUNCTION(func)))->co_argcount - 1;
+ } else if (PyFunction_Check(func)) {
+ co_argcount = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_argcount;
+ } else {
+ // We'll just assume the callable takes the correct number of arguments.
+ co_argcount = 3;
+ }
+
+ // Check whether the function expects the colliding object only,
+ // or also the point and normal.
+ if (co_argcount <= 1) {
+ // One argument, or *args (which gives co_argcount == 0)
+ if (args_1 == NULL) {
+ args_1 = PyTuple_New(1);
+ PyTuple_SET_ITEMS(args_1, collider->GetProxy());
+ }
+ ret = PyObject_Call(func, args_1, NULL);
+ } else {
+ // More than one argument, assume we can give point & normal.
+ if (args_3 == NULL) {
+ args_3 = PyTuple_New(3);
+ PyTuple_SET_ITEMS(args_3,
+ collider->GetProxy(),
+ PyObjectFrom(point),
+ PyObjectFrom(normal));
+ }
+ ret = PyObject_Call(func, args_3, NULL);
+ }
if (ret == NULL) {
PyErr_Print();
@@ -1525,7 +1562,8 @@ void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider)
}
}
- Py_DECREF(args);
+ if (args_3) Py_DECREF(args_3);
+ if (args_1) Py_DECREF(args_1);
}
#endif
}
diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h
index 4538679303f..5800cbc6834 100644
--- a/source/gameengine/Ketsji/KX_GameObject.h
+++ b/source/gameengine/Ketsji/KX_GameObject.h
@@ -919,7 +919,7 @@ public:
void RegisterCollisionCallbacks();
void UnregisterCollisionCallbacks();
- void RunCollisionCallbacks(KX_GameObject *collider);
+ void RunCollisionCallbacks(KX_GameObject *collider, const MT_Vector3 &point, const MT_Vector3 &normal);
/**
* Stop making progress
*/
diff --git a/source/gameengine/Ketsji/KX_TouchEventManager.cpp b/source/gameengine/Ketsji/KX_TouchEventManager.cpp
index 7ec379eec26..eb774960d41 100644
--- a/source/gameengine/Ketsji/KX_TouchEventManager.cpp
+++ b/source/gameengine/Ketsji/KX_TouchEventManager.cpp
@@ -60,7 +60,7 @@ bool KX_TouchEventManager::NewHandleCollision(void* object1, void* object2, cons
PHY_IPhysicsController* obj1 = static_cast<PHY_IPhysicsController*>(object1);
PHY_IPhysicsController* obj2 = static_cast<PHY_IPhysicsController*>(object2);
- m_newCollisions.insert(std::pair<PHY_IPhysicsController*, PHY_IPhysicsController*>(obj1, obj2));
+ m_newCollisions.insert(NewCollision(obj1, obj2, coll_data));
return false;
}
@@ -209,9 +209,11 @@ void KX_TouchEventManager::NextFrame()
}
}
// Run python callbacks
- kxObj1->RunCollisionCallbacks(kxObj2);
- kxObj2->RunCollisionCallbacks(kxObj1);
+ PHY_CollData *colldata = cit->colldata;
+ kxObj1->RunCollisionCallbacks(kxObj2, colldata->m_point1, colldata->m_normal);
+ kxObj2->RunCollisionCallbacks(kxObj1, colldata->m_point2, -colldata->m_normal);
+ delete cit->colldata;
}
m_newCollisions.clear();
@@ -219,3 +221,19 @@ void KX_TouchEventManager::NextFrame()
for (it.begin();!it.end();++it)
(*it)->Activate(m_logicmgr);
}
+
+
+KX_TouchEventManager::NewCollision::NewCollision(PHY_IPhysicsController *first,
+ PHY_IPhysicsController *second,
+ const PHY_CollData *colldata)
+ : first(first), second(second), colldata(new PHY_CollData(*colldata))
+{}
+
+KX_TouchEventManager::NewCollision::NewCollision(const NewCollision &to_copy)
+ : first(to_copy.first), second(to_copy.second), colldata(to_copy.colldata)
+{}
+
+bool KX_TouchEventManager::NewCollision::operator<(const NewCollision &other) const
+{
+ return first < other.first || second < other.second || colldata < other.colldata;
+}
diff --git a/source/gameengine/Ketsji/KX_TouchEventManager.h b/source/gameengine/Ketsji/KX_TouchEventManager.h
index bd4903c1545..d9c6fdad307 100644
--- a/source/gameengine/Ketsji/KX_TouchEventManager.h
+++ b/source/gameengine/Ketsji/KX_TouchEventManager.h
@@ -45,7 +45,29 @@ class PHY_IPhysicsEnvironment;
class KX_TouchEventManager : public SCA_EventManager
{
- typedef std::pair<PHY_IPhysicsController*, PHY_IPhysicsController*> NewCollision;
+ /**
+ * Contains two colliding objects and the first contact point.
+ */
+ class NewCollision {
+ public:
+ PHY_IPhysicsController *first;
+ PHY_IPhysicsController *second;
+ PHY_CollData *colldata;
+
+ /**
+ * Creates a copy of the given PHY_CollData; freeing that copy should be done by the owner of
+ * the NewCollision object.
+ *
+ * This allows us to efficiently store NewCollision objects in a std::set without creating more
+ * copies of colldata, as the NewCollision copy constructor reuses the pointer and doesn't clone
+ * it again. */
+ NewCollision(PHY_IPhysicsController *first,
+ PHY_IPhysicsController *second,
+ const PHY_CollData *colldata);
+ NewCollision(const NewCollision &to_copy);
+ bool operator<(const NewCollision &other) const;
+ };
+
PHY_IPhysicsEnvironment* m_physEnv;
std::set<NewCollision> m_newCollisions;
diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp
index bcdc6f471ec..c43a3f782a7 100644
--- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp
+++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp
@@ -2261,6 +2261,7 @@ void CcdPhysicsEnvironment::CallbackTriggers()
int numManifolds = dispatcher->getNumManifolds();
for (int i=0;i<numManifolds;i++)
{
+ bool colliding_ctrl0 = true;
btPersistentManifold* manifold = dispatcher->getManifoldByIndexInternal(i);
int numContacts = manifold->getNumContacts();
if (!numContacts) continue;
@@ -2289,12 +2290,27 @@ void CcdPhysicsEnvironment::CallbackTriggers()
if (iter == m_triggerControllers.end())
{
iter = m_triggerControllers.find(ctrl1);
+ colliding_ctrl0 = false;
}
if (iter != m_triggerControllers.end())
{
+ static PHY_CollData coll_data;
+ const btManifoldPoint &cp = manifold->getContactPoint(0);
+
+ /* Make sure that "point1" is always on the object we report on, and
+ * "point2" on the other object. Also ensure the normal is oriented
+ * correctly. */
+ btVector3 point1 = colliding_ctrl0 ? cp.m_positionWorldOnA : cp.m_positionWorldOnB;
+ btVector3 point2 = colliding_ctrl0 ? cp.m_positionWorldOnB : cp.m_positionWorldOnA;
+ btVector3 normal = colliding_ctrl0 ? -cp.m_normalWorldOnB : cp.m_normalWorldOnB;
+
+ coll_data.m_point1 = MT_Vector3(point1.m_floats);
+ coll_data.m_point2 = MT_Vector3(point2.m_floats);
+ coll_data.m_normal = MT_Vector3(normal.m_floats);
+
m_triggerCallbacks[PHY_OBJECT_RESPONSE](m_triggerCallbacksUserPtrs[PHY_OBJECT_RESPONSE],
- ctrl0,ctrl1,0);
+ ctrl0, ctrl1, &coll_data);
}
// Bullet does not refresh the manifold contact point for object without contact response
// may need to remove this when a newer Bullet version is integrated