Hi,
I’m implementing a picker by following the former Demos/OpenGL/DemoApplication.cpp code in bullet 2.83. But I’m having trouble with BulletWorld.clearTickCallback(), that is when I try to clear a previously set tick callback (with BulletWorld.setTickCallback()) I get this error:
...
Assertion failed: world->getWorldUserInfo() at line 932 of panda/src/bullet/bulletWorld.cxx
Traceback (most recent call last):
File "/Projects/physics_bullet_tes2.py", line 173, in simulationTask
bulletWorld.doPhysics(globalClock.getDt())
AssertionError: world->getWorldUserInfo() at line 932 of panda/src/bullet/bulletWorld.cxx
:task(error): Exception occurred in PythonTask Physics Simulation
Traceback (most recent call last):
File "/Projects/physics_bullet_tes2.py", line 222, in <module>
app.run()
File "/usr/share/panda3d/direct/showbase/ShowBase.py", line 2984, in run
self.taskMgr.run()
File "/usr/share/panda3d/direct/task/Task.py", line 509, in run
self.step()
File "/usr/share/panda3d/direct/task/Task.py", line 466, in step
self.mgr.poll()
File "/Projects/physics_bullet_tes2.py", line 173, in simulationTask
bulletWorld.doPhysics(globalClock.getDt())
AssertionError: world->getWorldUserInfo() at line 932 of panda/src/bullet/bulletWorld.cxx
Here is the example code:
from panda3d.core import Vec3, NodePath, CardMaker, BitMask32, LMatrix4f, \
LPoint3f, LVector3f, TransformState, PythonCallbackObject
from panda3d.bullet import BulletSphereShape, BulletConstraint, \
BulletRigidBodyNode, BulletSphericalConstraint, BulletWorld, \
BulletPlaneShape, BulletGenericConstraint
from direct.showbase.ShowBase import ShowBase
class Picker:
def __init__(self, app, world, pickKeyOn, pickKeyOff,
csIsSpherical=True, cfm=0.5, erp=0.5, debug=False):
# get bullet world reference
self.mWorld = world
# get render, camera node paths
self.app = app
self.mRender = self.app.render
self.mCamera = self.app.camera
self.mCamLens = self.app.camLens
# (re)set picking logic data
self.mCsPick = None
self.mPivotCamDist = 0.0
self.mPickedBody = None
self.mCsIsSpherical = csIsSpherical
self.mCfm = cfm
self.mErp = erp
# setup event callback for picking body
self.mPickKeyOn = pickKeyOn
self.mPickKeyOff = pickKeyOff
self.app.accept(self.mPickKeyOn, self.pickBody, [self.mPickKeyOn])
self.app.accept(self.mPickKeyOff, self.pickBody, [self.mPickKeyOff])
# debug
self.debug = debug
def pickBody(self, event):
# handle body picking
if event == self.mPickKeyOn:
# get the mouse watcher
mwatcher = self.app.mouseWatcherNode
if mwatcher.hasMouse():
# Get to and from pos in camera coordinates
pMouse = mwatcher.getMouse()
#
pFrom, pTo = (LPoint3f(), LPoint3f())
if self.mCamLens.extrude(pMouse, pFrom, pTo):
# Transform to global coordinates
pFrom = self.mRender.getRelativePoint(self.mCamera, pFrom)
pTo = self.mRender.getRelativePoint(self.mCamera, pTo)
# cast a ray to detect a body
result = self.mWorld.rayTestClosest(pFrom, pTo, BitMask32.allOn())
#
if result.hasHit():
# possible hit objects:
# - BulletRigidBodyNode
# - BulletCharacterControllerNode
# - BulletVehicle
# - BulletConstraint
# - BulletSoftBodyNode
# - BulletGhostNode
if result.getNode().isOfType(BulletRigidBodyNode.getClassType()):
self.mPickedBody = result.getNode()
if not (self.mPickedBody.isStatic() or self.mPickedBody.isKinematic()):
# set body as active and not deactivable
self.mPickedBody.setDeactivationEnabled(False)
# get global pivot pos
pivotPos = result.getHitPos()
# get the initial distance from camera
self.mPivotCamDist = (pivotPos - self.mCamera.getPos(self.mRender)).length()
# compute pivot pos relative to body
bodyNP = NodePath(self.mPickedBody)
pivotLocalPos = bodyNP.getRelativePoint(self.mRender, pivotPos)
# create constraint
if self.mCsIsSpherical:
# spherical
self.mCsPick = BulletSphericalConstraint(self.mPickedBody, pivotLocalPos)
else:
# generic
self.mCsPick = BulletGenericConstraint(self.mPickedBody,
TransformState.makePos(pivotLocalPos), True)
# set parameters (in Bullet environment)
dof6 = self.mCsPick
dof6.setAngularLimit(0, 0.0, 0.0)
# define the 'strength' of our constraint (each axis)
dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 0)
dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 1)
dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 2)
dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 3)
dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 4)
dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 5)
# define the 'error reduction' of our constraint (each axis)
dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 0)
dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 1)
dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 2)
dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 3)
dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 4)
dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 5)
# and attach it to the world
self.mWorld.attach(self.mCsPick);
# set callback
self.mWorld.setTickCallback(PythonCallbackObject(self.movePicked), True);
# print hit body
self.PRINT_DEBUG_HIT(result)
else:
print("Nothing hit!")
else:
if self.mCsPick is not None:
# unset callback
# ##ERROR
self.mWorld.clearTickCallback()
# remove constraint from world
self.mWorld.remove(self.mCsPick)
# delete constraint
self.mCsPick = None
# set body as inactive and deactivable
self.mPickedBody.setDeactivationEnabled(True);
def movePicked(self, data):
# handle picked body if any
if self.mCsPick is not None:
# get the mouse watcher
mwatcher = self.app.mouseWatcherNode
if mwatcher.hasMouse():
# Get to and from pos in camera coordinates
pMouse = mwatcher.getMouse()
#
pNear, pFar = (LPoint3f(), LPoint3f())
if self.mCamLens.extrude(pMouse, pNear, pFar):
# Transform to global coordinates
pNear = self.mRender.getRelativePoint(self.mCamera, pNear)
pFar = self.mRender.getRelativePoint(self.mCamera, pFar)
# new pivot (b) pos
vecFarNear = pFar - pNear
vecFarNear.normalize()
pivotPos = self.mCamera.getPos(self.mRender) + vecFarNear * self.mPivotCamDist;
if self.mCsIsSpherical:
self.mCsPick.setPivotB(pivotPos)
else:
# (in Bullet environment)
dof6 = self.mCsPick
dof6.setFrames(dof6.getFrameA().setPos(pivotPos), dof6.getFrameB())
def PRINT_DEBUG_HIT(self, result):
if self.debug:
print(result.getNode().getType().getName() +
'\n\t| panda node: ' + str(result.getNode().getName()) +
'\n\t| hit pos: (' +
str(result.getHitPos().getX()) + ', ' +
str(result.getHitPos().getY()) + ', ' +
str(result.getHitPos().getZ()) + ')'
'\n\t| hit normal: (' +
str(result.getHitNormal().getX()) + ', ' +
str(result.getHitNormal().getY()) + ', ' +
str(result.getHitNormal().getZ()) + ')'
'\n\t| hit fraction: "' +
str(result.getHitFraction()) + '"'
'\n\t| from pos: (' +
str(result.getFromPos().getX()) + ', ' +
str(result.getFromPos().getY()) + ', ' +
str(result.getFromPos().getZ()) + ')'
'\n\t| to pos: (' +
str(result.getToPos().getX()) + ', ' +
str(result.getToPos().getY()) + ', ' +
str(result.getToPos().getZ()) + ')')
if __name__ == '__main__':
app = ShowBase()
# Setup our physics world
bulletWorld = BulletWorld()
bulletWorld.setGravity(0, 0, -9.81)
# add the simulation task
def simulationTask(task):
global globalClock, bulletWorld
# do physics step
bulletWorld.doPhysics(globalClock.getDt())
#
return task.cont
#
app.taskMgr.add(simulationTask, "Physics Simulation")
# Add a plane
cm = CardMaker("plane")
cm.setFrame(-10000, 10000, -10000, 10000)
plane = NodePath(cm.generate())
plane.setP(-90.0)
plane.setZ(1.0)
plane.setColor(LVector3f(0.0, 0.5, 0.5))
# Add a plane to collide with
planeShape = BulletPlaneShape(Vec3(0, 0, 1), 1)
planeBody = BulletRigidBodyNode('Plane')
planeBody.setRestitution(0.1)
planeBody.setFriction(0.8)
planeBody.addShape(planeShape)
planeBody.setStatic(True) # static
bulletWorld.attachRigidBody(planeBody)
planeBodyNP = app.render.attachNewNode(planeBody)
planeBodyNP.setPos(0, 0, 0)
plane.reparentTo(planeBodyNP)
# add a body
pandaActor = app.loader.loadModel("smiley")
pandaActor.setScale(2.0)
# set rigid body
pandaShape = BulletSphereShape(2.0)
pandaBody = BulletRigidBodyNode('smiley body')
pandaBody.setMass(1.0)
pandaBody.setRestitution(0.99)
pandaBody.setFriction(0.8)
pandaBody.addShape(pandaShape)
bulletWorld.attachRigidBody(pandaBody)
pandaBodyNP = app.render.attachNewNode(pandaBody)
pandaBodyNP.setPos(0.0, 0.0, 90.0)
pandaActor.reparentTo(pandaBodyNP)
#set camera
app.camera.setPos(0.0, -250, 75.0)
app.camera.lookAt(0.0, 200.0, 0.0)
mat = LMatrix4f(app.camera.getTransform().getMat())
mat.invertInPlace()
app.mouseInterfaceNode.setMat(mat)
#add a picker
picker = Picker(app, bulletWorld, "p", "p-up",
csIsSpherical=True, cfm=0.5, erp=0.5, debug=True)
app.run()
here the picker will pick (in this case by typing “p”) a (not static) rigid body and move it with the mouse by setting a constraint and a tick callback ; when unpicking it (in this case by releasing the p key, i.e. “p-up”) it destroy the constraint and clear the tick callback, but the above error is produced.