I have added the feature to set a user-defined callback when a contact is added (Bullet’s gContactAddedCallback). Contact information can be modified from within the callback. In oder to use the callback you have to do the following:
1.) Call node.notifyCollisions(True) for every RigidBodyNode which should create callbacks.
2.) Create a callback object: cbo = PythonCallbackObject(func)
3.) Set the callback object: world.setContactAddedCallback(cbo)
A few things to notice:
- Callbacks ruin performance if overdone!
- There is only one global Bullet callback function. So you can not use different callbacks if you have multiple worlds.
- Callbacks can be created in addition to contact added events (both, only events, only callbacks, neither)
Here is a short sample for the conveyor belt:
import sys
import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from panda3d.core import Vec3
from panda3d.core import Point3
from panda3d.core import TransformState
from panda3d.core import BitMask32
from panda3d.core import PythonCallbackObject
from panda3d.bullet import BulletWorld
from panda3d.bullet import BulletPlaneShape
from panda3d.bullet import BulletBoxShape
from panda3d.bullet import BulletRigidBodyNode
from panda3d.bullet import BulletDebugNode
class Game(DirectObject):
def __init__(self):
base.setBackgroundColor(0.1, 0.1, 0.8, 1)
base.setFrameRateMeter(True)
base.cam.setPos(0, -20, 4)
base.cam.lookAt(0, 0, 0)
# Input
self.accept('escape', self.doExit)
self.accept('r', self.doReset)
# Task
taskMgr.add(self.update, 'updateWorld')
# Physics
self.setup()
# _____HANDLER_____
def doExit(self):
self.cleanup()
sys.exit(1)
def doReset(self):
self.cleanup()
self.setup()
# ____TASK___
def update(self, task):
dt = globalClock.getDt()
self.world.doPhysics(dt)
return task.cont
def cleanup(self):
self.world.clearContactAddedCallback()
self.world = None
self.worldNP.removeNode()
def setup(self):
self.worldNP = render.attachNewNode('World')
# World
self.debugNP = self.worldNP.attachNewNode(BulletDebugNode('Debug'))
self.debugNP.show()
self.debugNP.node().showWireframe(True)
self.debugNP.node().showConstraints(True)
self.debugNP.node().showBoundingBoxes(False)
self.debugNP.node().showNormals(True)
self.world = BulletWorld()
self.world.setGravity(Vec3(0, 0, -9.81))
self.world.setDebugNode(self.debugNP.node())
# Conveyor (static)
shape = BulletBoxShape(Vec3(4, 4, 0.4))
np = self.worldNP.attachNewNode(BulletRigidBodyNode('Conveyor'))
np.node().addShape(shape)
np.node().notifyCollisions(True)
np.setPos(0, 0, -1)
np.setCollideMask(BitMask32.allOn())
self.world.attachRigidBody(np.node())
# Crate (dynamic)
shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
np = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
np.node().setMass(1.0)
np.node().addShape(shape)
np.setPos(2, 0, 4)
np.setCollideMask(BitMask32.allOn())
self.world.attachRigidBody(np.node())
# Callback
self.world.setContactAddedCallback(PythonCallbackObject(self.added))
def added(self, data):
mp = data.getManifold()
v1 = Vec3(1, 0, 1)
v2 = v1.cross(mp.getNormalWorldOnB())
mp.setLateralFrictionInitialized(True)
mp.setLateralFrictionDir1(v1)
mp.setLateralFrictionDir2(v2)
mp.setContactMotion1(1.6)
#mp.setContactCfm2(0.6)
#mp.setCombinedFriction(0.2)
#mp.setCombinedRestitution(0.0)
game = Game()
run()