Panda Bullet


Here is a new version - it’s experimental and a complete rewrite with lots of API changes:

Summary of changes:

  • All physics objects are derived from PandaNode now (this is rigid body, soft body, ghost, character and vehicle).
  • Collision filtering is based on Panda3D’s CollideMask. Only the “from” collide mask is used.
  • Compound shapes are no longer required for bodies with several shapes. The btCompoundShape is created internally (I still have to optimize this, since currently a btCompoundShape is always internally created, no matter how many shapes are added to a rigid body).
  • Many objects and methods have been renamed, in order to fit better with Panda3D’s naming pattern.
  • Build system is now ‘waf’. Still Windows-only. Currently I am not using anything from Bullet’s “extra” directories.

I still have problems with synchronization of soft body transforms, so please don’t use soft bodies yet.

All the changes have been done in order to find a tighter integration with Panda3D. I’m not 100 percent convinced that this is the right way of providing Bullet physics in Panda3D, so please feel free to comment on the new version or - even bettere - suggest other ways.


And yet another version:

What’s new:

  • Built using Panda3D-2010.12.28-82 and Bullet r2260
  • Debug renderer: sphere shapes are now rendered as spheres, and not as diamonds like before.
  • Prevent nodes from getting flattened out.
  • BulletDebugNode: it’s no longer necessary to call hide/show after calling setVerbose.
  • Mass properties will be updated automatically after shapes are added/removed from a rigid body.
  • Classes derived from PandaNode have been renamed to reflect the inheritance, e. g. BulletGhost --> BulletGhostNode.
  • Optimized adding/removing of shape. No implicit btCompoundShape will be created if only one shape is added.
  • “Explosions” caused by “setPos” etc. being not applied right after creation of an object should no longer appear.


Id like to see your results, but i cant compile your project with waf

on win7 64bit


The important part of the console output, which gives information about what is going wrong, is ABOVE what you posted as screenshot. I guess you will see something like this:

Waf: Entering directory `C:\Development\BulletMk4\built'
[1/6] libbullet_igate.cxx:  -> built\libbullet_igate.cxx
    *** Error in bullet_utils.h near line 31, column 73:
    parse error
Error parsing file: 'bulletAllHitsRayResult.h'
Waf: Leaving directory `C:\Development\BulletMk4\built'
Build failed

The problem is that you probably didn’t set the path to Panda3D (current snapshot builds, 1.7.0 is not enough!). Check if those paths you see on your screenshot are right:

-S"C:\\Program Files/Panda3D-1.7.1/include" 
-S"C:\\Program Files/Panda3D-1.7.1/include/parser-inc"

Finally, if you have adjusted all paths, including the path to the compiled Bullet library, you should adjust the compiler flags and defines for 64bit Windows. I see in your screenshot that you are using 32bit defines.

I’m sorry, but I don’t have a Windows7/64bit machine, so I can’t test anything for you. Maybe someone can send me a full build log of Panda3D on Windows7 (with --verbose of course). I could try to make a best guess then.


Hi, your work is AMAZING! I really really enjoy it! Everything works really well (I had lots of fun with the car demo :wink:)

Keep up the great work,


Hi, I was wondering, are Character Controller Nodes supported on heightfields?

I ask because I tried to place the character demo into the height field demo, with the code looking like:

import sys
import direct.directbase.DirectStart

from import Actor
from direct.showbase.DirectObject import DirectObject
from direct.showbase.InputStateGlobal import inputState

from panda3d.core import AmbientLight
from panda3d.core import DirectionalLight
from panda3d.core import Vec3
from panda3d.core import Vec4
from panda3d.core import Point3
from panda3d.core import TransformState
from panda3d.core import BitMask32
from panda3d.core import Filename
from panda3d.core import PNMImage

from libpandabullet import BulletManager
from libpandabullet import BulletPlaneShape
from libpandabullet import BulletBoxShape
from libpandabullet import BulletRigidBodyNode
from libpandabullet import BulletDebugNode
from libpandabullet import BulletCapsuleShape
from libpandabullet import BulletCharacterControllerNode
from libpandabullet import BulletTriangleMesh
from libpandabullet import BulletTriangleMeshShape
from libpandabullet import BulletHeightfieldShape
from libpandabullet import ZUp

class World(DirectObject):
  def __init__(self):
    base.setBackgroundColor(0.0, 0.0, 0.6, 1)
    base.setFrameRateMeter(True), -20, 4), 0, 0)

    # Light
    alight = AmbientLight('ambientLight')
    alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
    alightNP = render.attachNewNode(alight)

    dlight = DirectionalLight('directionalLight')
    dlight.setDirection(Vec3(1, 1, -1))
    dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))

    dlightNP = render.attachNewNode(dlight)


    # Input
    self.accept('escape', self.doExit)
    self.accept('r', self.doReset)
    self.accept('f1', self.toggleWireframe)
    self.accept('f2', self.toggleTexture)
    self.accept('f3', self.toggleDebug)
    self.accept('f5', self.doScreenshot)

    self.accept('space', self.doJump)
    self.accept('c', self.doCrouch)

    inputState.watchWithModifiers('forward', 'w')
    inputState.watchWithModifiers('left', 'a')
    inputState.watchWithModifiers('reverse', 's')
    inputState.watchWithModifiers('right', 'd')
    inputState.watchWithModifiers('turnLeft', 'q')
    inputState.watchWithModifiers('turnRight', 'e')

    # Task
    taskMgr.add(self.update, 'updateWorld')

    # Physics

  # _____HANDLER_____

  def doExit(self):

  def doReset(self):

  def toggleWireframe(self):

  def toggleTexture(self):

  def toggleDebug(self):
    if self.debugNP.isHidden():

  def doScreenshot(self):

  def doJump(self):

  def doCrouch(self):
    self.crouching = not self.crouching
    sz = self.crouching and 0.6 or 1.0

    self.player.getShape().setLocalScale(Vec3(1, 1, sz))

    self.playerNP.setScale(Vec3(1, 1, sz) * 0.3048)
    self.playerNP.setPos(0, 0, -1 * sz)

  # ____TASK___

  def processInput(self, dt):
    speed = Vec3(0, 0, 0)
    omega = 0.0

    if inputState.isSet('forward'): speed.setY( 3.0)
    if inputState.isSet('reverse'): speed.setY(-3.0)
    if inputState.isSet('left'):    speed.setX(-3.0)
    if inputState.isSet('right'):   speed.setX( 3.0)
    if inputState.isSet('turnLeft'):  omega =  120.0
    if inputState.isSet('turnRight'): omega = -120.0

    self.player.setLinearVelocity(speed, True)

  def update(self, task):
    dt = globalClock.getDt()

    self.mgr.doPhysics(dt, 10, 0.008)

    return task.cont

  def cleanup(self):
    self.mgr = None

  def setup(self):
	self.worldNP = render.attachNewNode('World')

	# Manager
	self.debugNP = self.worldNP.attachNewNode(BulletDebugNode('Debug'))

	self.mgr = BulletManager()
	self.mgr.setGravity(Vec3(0, 0, -9.81))

	# Box (dynamic)
	shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

	np = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
	np.setPos(0, 0, 4)


	self.boxNP = np # For applying force & torque
	#- This heightfield code with a character is broken.. why?
	img = PNMImage(Filename('models/elevation2.png'))
	shape = BulletHeightfieldShape(img, 3.0, ZUp)
	np = self.worldNP.attachNewNode(BulletRigidBodyNode('Heightfield'))
	np.setPos(0, 0, 0)
	#- Comment out the code below for static planes and enable this code to see what I mean
	''' Heightfield is broken with character?
	#- Using static planes works fine with the character
	shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
	np = self.worldNP.attachNewNode(BulletRigidBodyNode('Ground'))
	np.setPos(0, 0, -1)
	#- Comment the static plane code out and uncomment the heightfield code
	#- It then crashes saying:
	#- Assertion failed: (_flags & F_lock_count) != 0 at line 68 of c:\buildslave\dev_sdk_win32\build\panda3d\built\include\mutexSimpleImpl.I
	# Character
	self.crouching = False

	h = 1.75
	w = 0.4
	shape = BulletCapsuleShape(w, h - 2 * w, ZUp)

	node = BulletCharacterControllerNode(shape, 0.4, 'Player')
	np = self.worldNP.attachNewNode(node)
	np.setPos(-2, 0, 4)


	self.player = node # For player control

	self.playerNP = Actor('models/ralph/ralph.egg', {
			              'run' : 'models/ralph/ralph-run.egg',
			              'walk' : 'models/ralph/ralph-walk.egg',
			              'jump' : 'models/ralph/ralph-jump.egg'})
	self.playerNP.setScale(0.3048) # 1ft = 0.3048m
	self.playerNP.setPos(0, 0, -1)

b = World()

that, and it error’s out (crashes python.exe)
here’s the only thing it prints to the terminal:

Assertion failed: (_flags & F_lock_count) != 0 at line 68 of c:\buildslave\dev_sdk_win32\build\panda3d\built\include\mutexSimpleImpl.I

was just curious if I had done something wrong or what, I know this is still uber beta, even if it is uber cool.



There is a bug in Bullet that does not allow the use of characters on height fields.


Right, but I’m using a patched version of Bullet which solves this bug.
See the above post for details:
[url]Panda Bullet]

What causes the crash here is some weirdness in BulletDebugNode, in particular the colors for lines. I will investigate the weekend.

Turning off the debug renderer should work as a temporary workaround.


I think I fixed the problem:


Hey enn0x, it works great!

Your patch fixed it and the code works good now, many thanks.


Hey enn0x, back again - I know what you’re thinking, “When will he leave? >.>” nah just kidding though if you want me to just shush and let you work let me know :wink:

(Does the BulletHeightFieldShape (or it’s node) provide a way to display a visual texture? otherwise I assume that I must build my visual heightmap using panda’s heightfield generator found here, in which case)

When matching a bullet heightfield with a panda visual heightfield, several inconvenient things happen (which I think might be easy-ish to include)

you have to turn the visual map using setH(90) because I guess panda or bullet disagree with the starting rotation or something
after that, the bullet map starts in the center point of the image (image width / 2 image height / 2, basically), the panda visual heightmap starts… in some corner, so you have to set it’s X and Y to offset half the image (which is weird), I wonder if either we could offset the bullet heightfield to match the panda3d one, OR add a way for a texture to be set on the bullet heightfield

these are just idea’s and I’m only hoping to give good feedback, what you’ve done so far is brilliant and I really want to see it go further :wink:
So it’s totally up to you if you think this is important or non important or anything


Here is my code, which will take a height map of any size, and set Pandas terrain to match that of Bullet.

#Set some var's:
        self.height = 250
        self.heightMap = "gfx/terrain/height.png"

        self.heightSize = PNMImage(self.heightMap).getXSize()

        self.terrain = GeoMipTerrain("terrain")


        self.root = self.terrain.getRoot()

        self.textureStage = TextureStage("Texture Stage")
        self.texture = loader.loadTexture('gfx/terrain/texture.jpg')
        self.detailMap = loader.loadTexture('gfx/terrain/detailmap.jpg')



        self.xform = TransformState.makePos((0,0,self.height/2))

        self.shape = BulletHeightfieldTerrainShape(self.heightMap,self.height,BulletHeightfieldTerrainShape.ZUp)

        self.body = BulletRigidBody(0,self.xform,self.shape)


No worries. I might be a bit sarcastic now and then, but I carefully read all suggestions, and I appreciate input.

No. The Bullet shapes are pure collision objects, without any visible geom. I want to keep it this way. Users should be able to choose whatever visible geometry they need (including whatever way of texturing/shaders they want to have.

The intended way is to create a BulletRigidBodyNode, reparent it to e. g. “render”, and then place nodes with visible geometry BELOW this node. If the body node’s transform changes then the visual geoms move together with the body node.

Hmmm… good point. Having the same conventions about orientation and origin in GeoMipTerrain and BulletTerrainShape would be convenient. However, there might be other ways of creating a terrain geometry, for example a simple static mesh, which won’t follow the GeoMipTerrain conventions. I have to thing about it, but I guess I will align both conventions.

Until then the best way would be to place the GeoMipTerrain below the BulletRigidBody, and just offset it’s transform and scale. For example Like Sothh has done (thanks for the code).


A mod needs to make this sticky!

Mod edit: your wish is granted.


Next version:

What’s new:

  • BulletHeightfieldShape is now by default oriented in the same way as GeoMipTerrain. However, the shape is still centered around the node’s position (unlike GeoMipTerrain).
  • Renamed the method BulletRigidBodyNode.attachShape to addShape.
  • Convenience functions for creating rigid bodies and ghosts from CollisionNodes, e. g. loaded from an .egg file (see maze sample).
  • Linux (32 bit) support. There are still some glitches - the slider and hinge constraints don’t work on Linux yet.


Hey enn0x,

Some useless random babbling here, I wanted to say that your Bullet implimentation in Panda is…
A. Amazing.
B. Has already surpassed the ODE implementation in panda IMO (excluding the whole platform dependence thing, which appear’s to be slowly waddling away… by that I mean I’ve no idea how to compile things or use the linux 32 version)
C. Should be included in panda ASAP

Obviously the platform dependency thing has to work out slowly, because it takes a while to support multiple OS’s, and I know people use Mac though it’s hard to compile on there when you have none (Idk if you do, I’ve never had one)

This work is simply amazing and I encourage people to look into using this in place of ODE or Physx, the wrappers are VERY nice and it seems like you’ve put alot of work into this enn0x

P.S. (the height field orientation correction I thank you for, it works wonders :smiley:)


I still dont know how to build this with waf. Ive never used waf. Installed it in Windows. Now what? This page says I shoud execute these 3 commands:

waf configure
waf build
waf install

Is that how I should do it? Or with the bat file in the libpandabullet folder?


Hi, I just built it on Linux (32) and I built it (after compile bullet) I ran:

python waf configure
python waf build

And then I just copied built/ (in your case .dll) to the test directory…

You may have to rename the dll to a .pyd on windows.

Enn0x, I dunno if it works for you, the (or whatever the character controller test is named) segmentation faults for me.
I used the latest bullet SVN revision… and it compiled without doubt… any luck for me?




Now you do exactly as this page says: you open a command shell, change directory to wherever you unpacked libpandabullet, and run the three commands.

I don’t know what you mean when you say you have installed waf. You don’t need to install waf! The complete build system is contained in one single file: “waf”.

The .bat file is there for just one purpose: because Windows doesn’t support the so called shebang. This is nothing obscene - look it up on wikipedia. The .bat file does nothing but call "python waf ".

Please not that you have to adjust the path setting withing the file “wscript”. You have to set the path to your Panda3d 1.7.1 installation, to your VS2008 & Windows SDK, and the path to where you have compiled Bullet libs.

Oh, and no grantee that it will compile or run on 64 bit Windows. I don’t have a 64 bit Windows, so I can’t test or fix anything.



Thank you very much, and glad you like it so far.
About integrating with Panda3D source code, well, original plans have been to introduce Bullet support with Panda3D 2.0. I am still not sure if the code is stable already, and there might be one more complete rewrite. so I’m afraid it will be too late for 1.7.1. The odd releases usually don’t introduce new features, so maybe 1.7.2 or 1.8.0.

About the issue with the character demo: This is a bug in Bullet itself. If you compile Bullet yourself you should patch the source code. See … 6858#66858 for a link to the Bullet forums (yes, it’s a know bug in Bullet).

Basically you edit one file, src/BulletDynamics/Character/btKinematicCharacterController.cpp. In line 87 you add “convexResult”, changing the line from

hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()...


hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()...

Then compile Bullet and libpandabullet, and the character demo should work well too. I hope this bug will be fixed soon within Bullet source code, since it is quite annoying to patch the code each time I get a new revision of the Bullet source code. But I’m afraid the character controller doesn’t have high priority for Bullet developers.