based on a question in irc from chozabu (thanks for the hint) i updated the sample to include immovable’s (like a terrain using a trimesh):
# -----
# example for the integrated ode of panda3d
# -----
# by reto spoerri (Hypnos)
# rspoerri@@@nouser...org
# -----
# panda3d can be crashed using ode, this should hopefully be fixed some time
# -----
# license: you must! modify change and do with this script whatever you want.
# no requirement, but it would be nice if you have good idea's to put samples of it on the forums.
# -----
# possible sources of problems:
# - self.odeWorld.initSurfaceTable( x )
# i dont know what x should be, but if you dont call this function and use
# odeSpace.autoCollide() it will crash
# - self.odeContactGroup.empty()
# panda will add up collisions every frame, it will slow down until nothing
# works anymore
# - self.odeWorld.step( dt ) crashed for me, quickStep never did
# (might not be true accurate)
# -----
# usage:
# w/a/s/d/space: move to bottom block
# -----
import sys, random
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
Infinity = 1e10000
NaN = Infinity / Infinity
class odeObj( NodePath ):
# a ode controlled object
def __init__( self ):
NodePath.__init__( self, 'odeObj' )
def step( self ):
pos = self.odeBody.getPosition()
quat = Quat(self.odeBody.getQuaternion())
if pos.length() == Infinity or pos.length() == NaN:
# object got out of control, might happen if something goes wrong with
# the collision (or a bug)
self.stop()
# define a reset pos and rotation for the object
pos = Vec3(0,0,0)
quat = Quat(0,0,0,0)
self.setPos( render, pos )
self.setQuat( render, quat )
def stop( self ):
# stop a objects motion and rotation
# position
self.odeBody.setLinearVel( Vec3(0,0,0) )
self.odeBody.setForce( Vec3(0,0,0) )
# rotation
self.odeBody.setAngularVel( Vec3(0,0,0) )
self.odeBody.setTorque( Vec3(0,0,0) )
def setPos( self, *args ):
NodePath.setPos( self, *args )
self.updateOde()
def setHpr( self, *args ):
NodePath.setHpr( self, *args )
self.updateOde()
def updateOde( self ):
# moves the ode object, accordingly to the panda object
# usually the ode object moves the panda object
# this is not useful if you want to move a object which should collide
# during the moving time, you will need to apply forces to do that
self.odeBody.setPosition( self.getPos( render ) )
self.odeBody.setQuaternion( self.getQuat( render ) )
class odeGeom( odeObj ):
# a simple ode box object
def __init__( self, _odeSpace, _odeWorld ):
odeObj.__init__( self )
self.model = loader.loadModel( 'models/misc/rgbCube.bam' )
self.odeMass = OdeMass()
self.odeMass.setBox( 1, Vec3(1,1,1) )
self.odeBody = OdeBody( _odeWorld )
self.odeBody.setMass( self.odeMass )
# adding a geom to the space here _and_ later (using space.add(geom)) will crash panda
self.odeGeom = OdeBoxGeom( _odeSpace, Vec3(1,1,1) )
self.odeGeom.setBody( self.odeBody )
self.model.reparentTo( self )
class odeJointBox( odeObj ):
# a ode box conntected to attachTo with a joint
def __init__( self, _odeSpace, _odeWorld, attachTo ):
odeObj.__init__( self )
self.model = loader.loadModel( 'models/misc/rgbCube.bam' )
self.odeMass = OdeMass()
self.odeMass.setBox( 1, Vec3(1,1,1) )
self.odeBody = OdeBody( _odeWorld )
self.odeBody.setMass( self.odeMass )
# adding a geom to the space here _and_ later (using space.add(geom)) will crash panda
self.odeGeom = OdeBoxGeom( _odeSpace, Vec3(1,1,1) )
self.odeGeom.setBody( self.odeBody )
self.model.reparentTo( self )
# Connect body1 with the static environment
self.joint = OdeBallJoint( _odeWorld )
self.joint.attach( self.odeBody, attachTo )
self.joint.setAnchor( Vec3(2,2,0) )
class odeTrimesh( odeObj ):
def __init__( self, _odeSpace, _odeWorld ):
odeObj.__init__( self )
self.model = loader.loadModel( 'models/panda-model.egg' )
self.model.setScale( 0.01 )
self.odeMass = OdeMass()
self.odeMass.setBox( 1, Vec3(1,1,1) )
self.odeBody = OdeBody( _odeWorld )
self.odeBody.setMass( self.odeMass )
# if a model is scaled you need to call flattenstrong on the data
# else the collision object will stay the same size as before
# (at least that's what i suspect)
self.model.flattenStrong()
# get the models polygons
trimeshData = OdeTriMeshData( self.model, True )
# crete a trimesh object from the trimeshData
self.odeGeom = OdeTriMeshGeom( _odeSpace, trimeshData )
if False: # checking the data
print "self.odeGeom.getNumTriangles()", self.odeGeom.getNumTriangles()
for i in xrange(self.odeGeom.getNumTriangles()):
x = Point3(0)
y = Point3(0)
z = Point3(0)
self.odeGeom.getTriangle( i, x, y, z )
print x,y,z
self.odeGeom.setBody( self.odeBody )
self.model.reparentTo( self )
class odeTerrain( odeObj ):
# a object that does not move (like a terrain)
def __init__( self, _odeSpace, _odeWorld ):
odeObj.__init__( self )
self.model = loader.loadModel( 'models/panda-model.egg' )
self.model.setScale( 0.1 )
#self.model.setPos( Vec3(0,0,-20) )
self.model.flattenStrong()
# get the models polygons
trimeshData = OdeTriMeshData( self.model, True )
# crete a trimesh object from the trimeshData
self.odeGeom = OdeTriMeshGeom( _odeSpace, trimeshData )
self.model.reparentTo( self )
def updateOde( self ):
# moves the ode object, accordingly to the panda object
# usually the ode object moves the panda object
# this is not useful if you want to move a object which should collide
# during the moving time, you will need to apply forces to do that
self.odeGeom.setPosition( self.getPos( render ) )
self.odeGeom.setQuaternion( self.getQuat( render ) )
class mainClass( DirectObject ):
def __init__( self ):
self.odeWorld = OdeWorld()
self.odeWorld.setGravity( 0, 0, -9.81 )
self.odeWorld.setCfm(0.01)
self.odeWorld.setErp(0.01)
self.odeWorld.setContactSurfaceLayer(.0001)
self.odeSpace = OdeSimpleSpace()
self.odeSpace.enable()
self.floor = OdePlaneGeom(self.odeSpace, 0, 0, 1, 0)
# visual object for cardmaker
cm = CardMaker('card')
cm.setFrame( -1000, 1000, -1000, 1000 )
card = render.attachNewNode(cm.generate())
card.setHpr(0,-90,0)
self.objects = list()
for i in xrange(5):
obj = odeGeom( self.odeSpace, self.odeWorld )
obj.reparentTo( render )
obj.setPos( Vec3(0,5,i*2+2) )
self.objects.append( obj )
self.odeWorld.addBodyDampening( obj.odeBody, 0 )
# 2 objects connected by a joint
obj = odeGeom( self.odeSpace, self.odeWorld )
obj.reparentTo( render )
obj.setPos( Vec3(2,5,20))
self.objects.append( obj )
obj = odeJointBox( self.odeSpace, self.odeWorld, obj.odeBody )
obj.reparentTo( render )
obj.setPos( Vec3(4,5,20))
self.objects.append( obj )
# a trimesh object
obj = odeTrimesh( self.odeSpace, self.odeWorld )
obj.reparentTo( render )
obj.setPos( Vec3(20,20,10))
self.objects.append( obj )
# im not sure how to use this settings actually
# if initSurfaceTable is not defined panda will segfault
# but already a initSurfaceTable(0) will work
self.odeWorld.initSurfaceTable(self.odeSpace.getNumGeoms())
for i in xrange(self.odeSpace.getNumGeoms()):
for j in xrange(self.odeSpace.getNumGeoms()):
# pos1, pos2, mu, bounce, bounce_vel, soft_erp, soft_cfm, slip, dampen
self.odeWorld.setSurfaceEntry( i, j, 0.1, 0.1, 0, 0.01, 0.01, 0, 0 )
obj = odeTerrain( self.odeSpace, self.odeWorld )
obj.setPos( Vec3(20,20,0) )
obj.reparentTo( render )
# create a joint ground for the collision joints
self.odeSpace.setAutoCollideWorld( self.odeWorld )
self.odeContactGroup = OdeJointGroup()
self.odeSpace.setAutoCollideJointGroup( self.odeContactGroup )
self.accept( 'a', self.left )
self.accept( 'd', self.right )
self.accept( 'w', self.front )
self.accept( 's', self.back )
self.accept( 'e', self.up )
self.accept( 'u', self.push )
self.accept( 'j', self.pull )
self.accept( 'space', self.random )
self.accept( 'escape', sys.exit )
def step( self ):
# is required, else the collisions will increase until you have 0fps
self.odeContactGroup.empty()
# panda's internal method to handle collisions
contactCount = self.odeSpace.autoCollide()
dt = globalClock.getDt()
# the first frame of panda3d usually has a pretty large lag, causing objects
# to fall trough the ground
if dt > 0.2:
dt = 0.001
self.odeWorld.quickStep( dt )
for obj in self.objects:
obj.step()
def random( self ):
for obj in self.objects:
r = random.randint(0,1000)-500
obj.odeBody.addForce( Vec3(r,r,r) )
def pull( self ):
for obj in self.objects:
force = -obj.getPos( render ) * 50 + Vec3(0,0,50)
obj.odeBody.addForce( force )
def push( self ):
for obj in self.objects:
obj.odeBody.addForce( obj.getPos( render ) * 50 )
def up( self ):
obj = self.objects[-1]
obj.odeBody.addForce( Vec3(0,0,1000) )
def front( self ):
obj = self.objects[-1]
obj.odeBody.addForce( Vec3(0,500,0) )
def back( self ):
obj = self.objects[-1]
obj.odeBody.addForce( Vec3(0,-500,0) )
def left( self ):
obj = self.objects[-1]
obj.odeBody.addForce( Vec3(-500,0,0) )
def right( self ):
obj = self.objects[-1]
obj.odeBody.addForce( Vec3(500,0,0) )
if __name__ == '__main__':
import direct.directbase.DirectStart
main = mainClass()
while True:
main.step()
taskMgr.step()