From dd65a44c9a192d62f7661090682ee0dc99fb0491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 5 Feb 2015 09:39:53 +0100 Subject: BGE physics: When colliding, report first contact point to Python This patch adds two parameters to the functions in the collisionCallbacks list. The callback function should thus be like this: ``` def on_colliding(other, point, normal): print("Colliding with %s at %s with normal %s" % (other, point, normal)) game_ob.collisionCallbacks.append(on_colliding) ``` The `point` parameter will contain the collision point in world coordinates on the current object, and the `normal` contains the surface normal at the collision point. The callback functions are checked for the number of arguments `co_argcount`. The new `point` and `normal` arguments are only passed when `co_argcount > 1` or when `co_argcount` cannot be determined. Reviewers: brita_, campbellbarton Subscribers: sergey, sybren, agoose77 Projects: #game_physics Differential Revision: https://developer.blender.org/D926 --- source/gameengine/Ketsji/KX_GameObject.cpp | 46 +++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'source/gameengine/Ketsji/KX_GameObject.cpp') 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 } -- cgit v1.2.3