Browse Source

Python callback for collision filtering

enn0x 13 years ago
parent
commit
48b3b4dc55
3 changed files with 132 additions and 21 deletions
  1. 4 0
      panda/src/bullet/bulletWorld.I
  2. 102 21
      panda/src/bullet/bulletWorld.cxx
  3. 26 0
      panda/src/bullet/bulletWorld.h

+ 4 - 0
panda/src/bullet/bulletWorld.I

@@ -52,6 +52,10 @@ INLINE BulletWorld::
   delete _configuration;
   delete _configuration;
   delete _dispatcher;
   delete _dispatcher;
   delete _broadphase;
   delete _broadphase;
+
+#ifdef HAVE_PYTHON
+  Py_XDECREF(_filter_cb3._python_callback);
+#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 102 - 21
panda/src/bullet/bulletWorld.cxx

@@ -19,6 +19,14 @@
 
 
 #include "collideMask.h"
 #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)
 #define clamp(x, x_min, x_max) max(min(x, x_max), x_min)
 
 
 TypeHandle BulletWorld::_type_handle;
 TypeHandle BulletWorld::_type_handle;
@@ -105,6 +113,9 @@ BulletWorld() {
     case FA_groups_mask:
     case FA_groups_mask:
       _world->getPairCache()->setOverlapFilterCallback(&_filter_cb2);
       _world->getPairCache()->setOverlapFilterCallback(&_filter_cb2);
       break;
       break;
+    case FA_python_callback:
+      _world->getPairCache()->setOverlapFilterCallback(&_filter_cb3);
+      break;
     default:
     default:
       bullet_cat.error() << "no proper filter algorithm!" << endl;
       bullet_cat.error() << "no proper filter algorithm!" << endl;
   }
   }
@@ -748,27 +759,8 @@ get_collision_object(PandaNode *node) {
   return NULL;
   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
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -794,6 +786,31 @@ get_group_collision_flag(unsigned int group1, unsigned int group2) const {
   return _filter_cb2._collide[group1].get_bit(group2);
   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
 //     Function: BulletWorld::FilterCallback1::needBroadphaseCollision
 //       Access: Published
 //       Access: Published
@@ -853,6 +870,61 @@ needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) co
   return false;
   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
 //     Function: BulletWorld::BroadphaseAlgorithm ostream operator
 //  Description:
 //  Description:
@@ -908,8 +980,12 @@ operator << (ostream &out, BulletWorld::FilterAlgorithm algorithm) {
 
 
   case BulletWorld::FA_groups_mask:
   case BulletWorld::FA_groups_mask:
     return out << "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 << ")**";
   return out << "**invalid BulletWorld::FilterAlgorithm(" << (int)algorithm << ")**";
 }
 }
 
 
@@ -928,6 +1004,11 @@ operator >> (istream &in, BulletWorld::FilterAlgorithm &algorithm) {
   else if (word == "groups-mask") {
   else if (word == "groups-mask") {
     algorithm = BulletWorld::FA_groups_mask;
     algorithm = BulletWorld::FA_groups_mask;
   } 
   } 
+#ifdef HAVE_PYTHON
+  else if (word == "python-callback") {
+    algorithm = BulletWorld::FA_python_callback;
+  }
+#endif
   else {
   else {
     bullet_cat.error()
     bullet_cat.error()
       << "Invalid BulletWorld::FilterAlgorithm: " << word << "\n";
       << "Invalid BulletWorld::FilterAlgorithm: " << word << "\n";

+ 26 - 0
panda/src/bullet/bulletWorld.h

@@ -38,6 +38,11 @@
 #include "collideMask.h"
 #include "collideMask.h"
 #include "luse.h"
 #include "luse.h"
 
 
+#ifdef HAVE_PYTHON
+  #include "py_panda.h"
+  #include "Python.h"
+#endif
+
 class BulletPersistentManifold;
 class BulletPersistentManifold;
 class BulletShape;
 class BulletShape;
 class BulletSoftBodyWorldInfo;
 class BulletSoftBodyWorldInfo;
@@ -150,8 +155,15 @@ PUBLISHED:
   enum FilterAlgorithm {
   enum FilterAlgorithm {
     FA_mask,
     FA_mask,
     FA_groups_mask,
     FA_groups_mask,
+#ifdef HAVE_PYTHON
+    FA_python_callback,
+#endif
   };
   };
 
 
+#ifdef HAVE_PYTHON
+  void set_python_filter_callback(PyObject *callback);
+#endif
+
 public:
 public:
   static btCollisionObject *get_collision_object(PandaNode *node);
   static btCollisionObject *get_collision_object(PandaNode *node);
 
 
@@ -190,6 +202,16 @@ private:
     CollideMask _collide[32];
     CollideMask _collide[32];
   };
   };
 
 
+#ifdef HAVE_PYTHON
+  struct btFilterCallback3 : public btOverlapFilterCallback {
+    virtual bool needBroadphaseCollision(
+      btBroadphaseProxy* proxy0,
+      btBroadphaseProxy* proxy1) const;
+
+    PyObject *_python_callback;
+  };
+#endif
+
   btBroadphaseInterface *_broadphase;
   btBroadphaseInterface *_broadphase;
   btCollisionConfiguration *_configuration;
   btCollisionConfiguration *_configuration;
   btCollisionDispatcher *_dispatcher;
   btCollisionDispatcher *_dispatcher;
@@ -201,6 +223,10 @@ private:
   btFilterCallback1 _filter_cb1;
   btFilterCallback1 _filter_cb1;
   btFilterCallback2 _filter_cb2;
   btFilterCallback2 _filter_cb2;
 
 
+#ifdef HAVE_PYTHON
+  btFilterCallback3 _filter_cb3;
+#endif
+
   btSoftBodyWorldInfo _info;
   btSoftBodyWorldInfo _info;
 
 
   PT(BulletDebugNode) _debug;
   PT(BulletDebugNode) _debug;