From 48b3b4dc55e7b05b7331ba33dcbfb0af0b095148 Mon Sep 17 00:00:00 2001 From: enn0x Date: Mon, 26 Mar 2012 20:06:10 +0000 Subject: [PATCH] Python callback for collision filtering --- panda/src/bullet/bulletWorld.I | 4 + panda/src/bullet/bulletWorld.cxx | 123 +++++++++++++++++++++++++------ panda/src/bullet/bulletWorld.h | 26 +++++++ 3 files changed, 132 insertions(+), 21 deletions(-) diff --git a/panda/src/bullet/bulletWorld.I b/panda/src/bullet/bulletWorld.I index 5a85cab4b6..bcf9acaae7 100644 --- a/panda/src/bullet/bulletWorld.I +++ b/panda/src/bullet/bulletWorld.I @@ -52,6 +52,10 @@ INLINE BulletWorld:: delete _configuration; delete _dispatcher; delete _broadphase; + +#ifdef HAVE_PYTHON + Py_XDECREF(_filter_cb3._python_callback); +#endif } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index 732e65ec98..c7e98914fb 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -19,6 +19,14 @@ #include "collideMask.h" +#ifdef HAVE_PYTHON + #include "py_panda.h" + #include "typedReferenceCount.h" + #ifndef CPPPARSER + extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_PandaNode; + #endif +#endif + #define clamp(x, x_min, x_max) max(min(x, x_max), x_min) TypeHandle BulletWorld::_type_handle; @@ -105,6 +113,9 @@ BulletWorld() { case FA_groups_mask: _world->getPairCache()->setOverlapFilterCallback(&_filter_cb2); break; + case FA_python_callback: + _world->getPairCache()->setOverlapFilterCallback(&_filter_cb3); + break; default: bullet_cat.error() << "no proper filter algorithm!" << endl; } @@ -748,27 +759,8 @@ get_collision_object(PandaNode *node) { return NULL; } -/* //////////////////////////////////////////////////////////////////// -// Function: BulletWorld::clean_manifolds_for_node -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -void BulletWorld:: -clean_manifolds_for_node(PandaNode *node) { - - btCollisionObject *object = get_collision_object(node); - if (object) { - btBroadphaseProxy* proxy = object->getBroadphaseHandle(); - if (proxy) { - _world->getPairCache()->cleanProxyFromPairs(proxy, _dispatcher); - } - } -} -*/ - -//////////////////////////////////////////////////////////////////// -// Function: BulletWorld::get_collision_object +// Function: BulletWorld::set_group_collision_flag // Access: Public // Description: //////////////////////////////////////////////////////////////////// @@ -794,6 +786,31 @@ get_group_collision_flag(unsigned int group1, unsigned int group2) const { return _filter_cb2._collide[group1].get_bit(group2); } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: BulletWorld::set_python_filter_callback +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void BulletWorld:: +set_python_filter_callback(PyObject *callback) { + + nassertv(callback != NULL); + + if (!PyCallable_Check(callback)) { + PyErr_Format(PyExc_TypeError, "'%s' object is not callable", callback->ob_type->tp_name); + return; + } + + if (bullet_filter_algorithm != FA_python_callback) { + bullet_cat.warning() << "filter algorithm is not 'python-callback'" << endl; + } + + _filter_cb3._python_callback = callback; + Py_XINCREF(_filter_cb3._python_callback); +} +#endif + //////////////////////////////////////////////////////////////////// // Function: BulletWorld::FilterCallback1::needBroadphaseCollision // Access: Published @@ -853,6 +870,61 @@ needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) co return false; } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: BulletWorld::FilterCallback3::needBroadphaseCollision +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +bool BulletWorld::btFilterCallback3:: +needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const { + + nassertr(_python_callback, false); + + btCollisionObject *obj0 = (btCollisionObject *) proxy0->m_clientObject; + btCollisionObject *obj1 = (btCollisionObject *) proxy1->m_clientObject; + + nassertr(obj0, false); + nassertr(obj1, false); + + PandaNode *node0 = (PandaNode *) obj0->getUserPointer(); + PandaNode *node1 = (PandaNode *) obj1->getUserPointer(); + + nassertr(node0, false); + nassertr(node1, false); + + PyObject *p0 = DTool_CreatePyInstanceTyped(node0, Dtool_PandaNode, true, false, node0->get_type_index()); + PyObject *p1 = DTool_CreatePyInstanceTyped(node1, Dtool_PandaNode, true, false, node1->get_type_index()); + + PyObject *result = PyEval_CallFunction(_python_callback, "OO", p0, p1); + + bool collide = false; + + if (!result) { + bullet_cat.error() << "An error occurred while calling python function!" << endl; + PyErr_Print(); + } + else { + int v = PyObject_IsTrue(result); + if (v == 1) { + collide = true; + } + else if (v == 0) { + collide = false; + } + else { + bullet_cat.error() << "Python callback function must return a bool object" << endl; + } + Py_DECREF(result); + } + + //Py_XDECREF(p0); + //Py_XDECREF(p1); + + return collide; +} +#endif + //////////////////////////////////////////////////////////////////// // Function: BulletWorld::BroadphaseAlgorithm ostream operator // Description: @@ -908,8 +980,12 @@ operator << (ostream &out, BulletWorld::FilterAlgorithm algorithm) { case BulletWorld::FA_groups_mask: return out << "groups-mask"; - }; +#ifdef HAVE_PYTHON + case BulletWorld::FA_python_callback: + return out << "python-callback"; +#endif + }; return out << "**invalid BulletWorld::FilterAlgorithm(" << (int)algorithm << ")**"; } @@ -928,6 +1004,11 @@ operator >> (istream &in, BulletWorld::FilterAlgorithm &algorithm) { else if (word == "groups-mask") { algorithm = BulletWorld::FA_groups_mask; } +#ifdef HAVE_PYTHON + else if (word == "python-callback") { + algorithm = BulletWorld::FA_python_callback; + } +#endif else { bullet_cat.error() << "Invalid BulletWorld::FilterAlgorithm: " << word << "\n"; diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index 5581a308a7..1675ebcb3f 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -38,6 +38,11 @@ #include "collideMask.h" #include "luse.h" +#ifdef HAVE_PYTHON + #include "py_panda.h" + #include "Python.h" +#endif + class BulletPersistentManifold; class BulletShape; class BulletSoftBodyWorldInfo; @@ -150,8 +155,15 @@ PUBLISHED: enum FilterAlgorithm { FA_mask, FA_groups_mask, +#ifdef HAVE_PYTHON + FA_python_callback, +#endif }; +#ifdef HAVE_PYTHON + void set_python_filter_callback(PyObject *callback); +#endif + public: static btCollisionObject *get_collision_object(PandaNode *node); @@ -190,6 +202,16 @@ private: CollideMask _collide[32]; }; +#ifdef HAVE_PYTHON + struct btFilterCallback3 : public btOverlapFilterCallback { + virtual bool needBroadphaseCollision( + btBroadphaseProxy* proxy0, + btBroadphaseProxy* proxy1) const; + + PyObject *_python_callback; + }; +#endif + btBroadphaseInterface *_broadphase; btCollisionConfiguration *_configuration; btCollisionDispatcher *_dispatcher; @@ -201,6 +223,10 @@ private: btFilterCallback1 _filter_cb1; btFilterCallback2 _filter_cb2; +#ifdef HAVE_PYTHON + btFilterCallback3 _filter_cb3; +#endif + btSoftBodyWorldInfo _info; PT(BulletDebugNode) _debug;