Bullet causing python.exe crash

forbjok

01-12-2009 23:06:52

I'm new to both OGRE and Bullet, and while testing I encountered a strange error.

When running the following code, it causes python.exe to "encounter a problem" (crash) when it gets to the line that runs stepSimulation() on the bullet dynamics world.

import ogre.renderer.OGRE as ogre
import SampleFramework as sf
import ogre.io.OIS as OIS

from ogre.physics import bullet

class TutorialApplication(sf.Application):
def _createScene(self):
sm = self.sceneManager
sm.ambientLight = ogre.ColourValue(1, 1, 1)

# R1
e1 = sm.createEntity("Robot", "robot.mesh")
n1 = sm.getRootSceneNode().createChildSceneNode("RobotNode")
n2 = sm.getRootSceneNode().createChildSceneNode("CameraNode")

n1.attachObject(e1)
n1.attachObject(self.camera)
self.camera.position = (0, 10, -500)
self.camera.lookAt((0,0,0))
self.camera.setProjectionType(ogre.PT_ORTHOGRAPHIC)

self.pcnode = n1
self.pcent = e1

# Create plane
plane = ogre.Plane((0, 1, 0), 0)
mm = ogre.MeshManager.getSingleton()
mm.createPlane("Ground", "General", plane
, 1500, 100, 20, 20
, True, 1, 15, 1
, (0, 0, 1)
)

e = sm.createEntity("GroundEntity", "Ground")
sm.getRootSceneNode().createChildSceneNode().attachObject(e)
e.setMaterialName("Examples/Rockwall")
e.castShadows = False

# Set up bullet
collisionConfiguration = bullet.get_btDefaultCollisionConfiguration()
dispatcher = bullet.get_btCollisionDispatcher1 (collisionConfiguration)
broadphase = bullet.btDbvtBroadphase()
solver = bullet.btSequentialImpulseConstraintSolver()

world = bullet.btDiscreteDynamicsWorld(dispatcher, broadphase , solver, collisionConfiguration)
world.setGravity(bullet.btVector3(0,-10,0))
world.getDispatchInfo().m_enableSPU = True

#ps = bullet.btStaticPlaneShape(bullet.btVector3(0,1,0), 50)

self.dynworld = world

# aDd things
self.things = []
self.AddThingy((100, 100, 0))
self.AddThingy((200, 200, 0))
self.AddThingy((300, 300, 0))
self.AddThingy((400, 400, 0))

def AddThingy(self, pos):
sm = self.sceneManager

# Create a thingy and a scene node, and attach them
e = sm.createEntity("Thingy" + str(len(self.things)), "ninja.mesh")
n = sm.getRootSceneNode().createChildSceneNode("ThingyNode" + str(len(self.things)))
n.attachObject(e)
n.position = pos

bt = self.BulletThingy(n)

# Add thingy to list of thingies
self.things += [[e, n, bt]]

def BulletThingy(self, node):
mass = 10
shape = bullet.btBoxShape(bullet.btVector3(10, 10, 10))
mots = ThingyMotionState(node)
obj = bullet.btRigidBody(mass, mots, shape)
print "OBJE ", obj
self.dynworld.addRigidBody(obj)

return obj

def _createCamera(self):
self.camera = self.sceneManager.createCamera("PlayerCam")
self.camera.nearClipDistance = 5

def _createFrameListener(self):
"""Creates the FrameListener."""
#,self.frameListener, self.frameListener.Mouse
self.frameListener = FrameListener(self.renderWindow, self, self.pcnode, self.pcent)
self.root.addFrameListener(self.frameListener)

class ThingyMotionState(bullet.btMotionState):
def __init__(self, node):
bullet.btMotionState.__init__(self)

self.node = node

def getWorldTransform(self, WorldTrans):
#org = WorldTrans.getOrigin()
#self.node.translate((org.x(), org.y(), org.z()))
pass

def setWorldTransform(self, WorldTrans):
#org = WorldTrans.getOrigin()
#print (org.x(), org.y(), org.z())
#self.node.translate((org.x(), org.y(), org.z()))
pass

class FrameListener(ogre.FrameListener, ogre.WindowEventListener):
def __init__(self, renderwindow, scenemanager, pcnode, pcent):
ogre.FrameListener.__init__(self)
ogre.WindowEventListener.__init__(self)

self.renderWindow = renderwindow
self.scenemanager = scenemanager
self.pcnode = pcnode
self.pcent = pcent

self._setupInput()

def frameEnded(self, ev):
#print self.scenemanager.dynworld
print "test1"
print "the following line causes python.exe to suddenly 'encounter a problem'..."
self.scenemanager.dynworld.stepSimulation(ev.timeSinceLastFrame)
print "test2"

return ogre.FrameListener.frameEnded(self, ev)

def _setupInput(self):
self.InputManager = OIS.createPythonInputSystem([("WINDOW", str(self.renderWindow.getCustomAttributeInt("WINDOW")))])
self.keyboard = self.InputManager.createInputObjectKeyboard( OIS.OISKeyboard, False )

def _processUnbufferedKeyInput(self, frameEvent):
kb = self.keyboard

if kb.isKeyDown(OIS.KC_ESCAPE) or kb.isKeyDown(OIS.KC_Q):
return False

pc = self.pcnode
pe = self.pcent
if kb.isKeyDown(OIS.KC_A):
pc.translate(0.2, 0, 0)

if kb.isKeyDown(OIS.KC_D):
pc.translate(-0.2, 0, 0)

return True
def frameRenderingQueued(self, evt):
if self.renderWindow.isClosed():
return False

self.keyboard.capture()

return self._processUnbufferedKeyInput(evt)

if __name__ == '__main__':
ta = TutorialApplication()
ta.go()


If I comment out the offending line, the application runs, but of couse, Bullet does nothing.
A lot of the bullet-code is copied directly from the tests in "Demos\bullet", which do run without crashing Python.

Since the tests under "Demos\bullet" run without crashing, I thought I'd try to strip down test01.py to see if I could get that to crash in the same way, by making it as similar as possible to what I'm doing in my test program without the OGRE stuff.
This is what I came up with:

#
# Updated to calculate a falling sphere:
# http://www.bulletphysics.com/mediawiki-1.5.8/index.php?title=Hello_World
#

import sys
sys.path.insert(0,'..')
import PythonOgreConfig

import ogre.renderer.OGRE as ogre
from ogre.physics import bullet
#m_clock=bullet.btClock()

#def getDeltaTimeMicroseconds() :
# dt = m_clock.getTimeMicroseconds()
# m_clock.reset()
# return dt

collisionConfiguration = bullet.get_btDefaultCollisionConfiguration()
dispatcher = bullet.get_btCollisionDispatcher1 (collisionConfiguration)
broadphase = bullet.btDbvtBroadphase()
solver = bullet.btSequentialImpulseConstraintSolver()

world = bullet.btDiscreteDynamicsWorld(dispatcher, broadphase , solver, collisionConfiguration)
world.setGravity(bullet.btVector3(0,-10,0))
world.getDispatchInfo().m_enableSPU = True

#for x in range (30):
# world.stepSimulation( x * 1/30)
#
#world.stepSimulation(0)
#world.stepSimulation(-0.333)

mass = 10
shape=bullet.btBoxShape(bullet.btVector3(10, 10, 10))
motionState=bullet.btDefaultMotionState()
Object = bullet.btRigidBody(mass, motionState, shape) # ...this should work in my eyes
world.addRigidBody(Object)

for x in range (90):
world.stepSimulation( 0.0001 )#x * 1/30)
print("1")

# note we need to delete the world before the Object as there are issues with the destructor on Objects causing
# virtual functions being called errors
del world


... but that still does not crash.

I have no idea if this problem is due to something I'm doing wrong in my test application, or an error in the bullet code in python-ogre.
Anyone have any idea what might be causing this?

forbjok

02-12-2009 00:57:06

I found a way to reproduce the problem without the OGRE stuff.
Apparently, if more than one btRigidBody object is added to the world, executing stepSimulation() will consistently crash python.
The code below, for example will always cause python to crash. Changing the "2" to a "1" in the range(), will make it run without crashing.

#
# Updated to calculate a falling sphere:
# http://www.bulletphysics.com/mediawiki-1.5.8/index.php?title=Hello_World
#

import sys
sys.path.insert(0,'..')
import PythonOgreConfig

import ogre.renderer.OGRE as ogre
from ogre.physics import bullet

# Set up bullet
collisionConfiguration = bullet.get_btDefaultCollisionConfiguration()
dispatcher = bullet.get_btCollisionDispatcher1 (collisionConfiguration)
broadphase = bullet.btDbvtBroadphase()
solver = bullet.btSequentialImpulseConstraintSolver()

world = bullet.btDiscreteDynamicsWorld(dispatcher, broadphase , solver, collisionConfiguration)
world.setGravity(bullet.btVector3(0,-10,0))
world.getDispatchInfo().m_enableSPU = True

for i in range(2):
print(i)
mass = 10
shape = bullet.btBoxShape(bullet.btVector3(10, 10, 10))
mots = bullet.btDefaultMotionState()
o = bullet.btRigidBody(mass, mots, shape)
world.addRigidBody(o)

for i in range(90):
world.stepSimulation(0.0001)

# note we need to delete the world before the Object as there are issues with the destructor on Objects causing
# virtual functions being called errors
del world


Am I just doing something wrong, or is this a bug?

andy

02-12-2009 23:04:01

Problem is caused by the fact you are creating objects that need to remain in scope (in C++ they could have been created with 'new') -- what's happening is that you only have 'one' (the last one in the loop) copy of the body, shape and motion and when it is accessed from C++ multiple times things get ugly..

So you need to do something like:
store=[]
for i in range(200):
print(i)
mass = 10
shape = bullet.btBoxShape(bullet.btVector3(10, 10, 10))
mots = bullet.btDefaultMotionState()
o = bullet.btRigidBody(mass, mots, shape)
world.addRigidBody(o)
store.append(shape)
store.append(o)
store.append(mots)


Regards
Andy

forbjok

02-12-2009 23:20:30

So, what's happening is basically that when they go out of scope in Python, they get garbage-collected, even though the C++ libraries are still using them?
If so, isn't this something the wrapper should be taking care of automatically, so you don't have to keep a Python list of a million objects you'll never be referring to in Python again?

andy

03-12-2009 00:03:30

There isn't anything the wrapper can really do about this (in an automatic fashion).. It is 'possible' to force boost to own the returned 'object' however by doing this you lose the ability to control object life time yourself..

For example if the object returned by bullet.btBoxShape was 'kept' by the wrapper then you would never be able to delete it or free memory associated with it until the program exited which isn't good behavior either.

This is a quirk associated with mixing languages... Oh you could always write an extension wrapper in python that made it work the way you wanted/expected...

regards

Andy

magcius

05-12-2009 19:32:27

Why can't you store a reference to mots and shape in o?