Panda Bullet

Of course I understand your point of view. To be honoust I would stick with ODE if the Trianglemesh-Trianglemesh collision would not raise an strange ODE exception.
Btw thanks for the explanation between overlap and contact. I was just confused that a ghost could take a TriangleMesh as well, so i thought that in the case of tri-ghost vs tri-ghost check the overlap check is using a triangle-triangle test.

Just to clearify it:
You said that
“And no, if you check for overlaps OR contacts you won’t get duplicate results A,B and B,A”
but if do the following would not I get duplicated results?

for ghost in world.getGhosts():
   for res in ghost.getOverlappingNodes():
      pass

To the best of my knowledge I would do every node j <-> node i test which i.e. results into node 1 <-> node 2 and node 2 <-> node 1 but maybe I am wrong and Bullet works completely different internally. By the way a “get all contacts” or get all “get all overlaps” would ease the pain of iteration massivly because c++ is much faster than my python code.

The requested Debugging info:
dl.dropbox.com/u/6841319/render-ls.txt
dl.dropbox.com/u/6841319/screens … 11-525.jpg <- Ghosts are centered around 0,0,0 but the models can be found there the red and green symbols are located.

Triangle mesh vs. triangle mesh collisions are supported by Bullet, but they are heavily discouraged. Triangle meshes should be used for static geometry only (terrain, houses, trees etc.). For dynamic objects you should use either collision primitives (box, sphere, …) or convex hulls. If you have a dynamic object with a concave shape then decompose this shape into convex chunks. This is way faster than using a triangle mesh.

Um, my fault. The inner loop would not produces duplicates, but you might get duplicates for the outer loop.

How this? Such methods would return a list of overlaps or contact. What would you do with this list, if not iterate over the list, in Python? By the way, both methods exist.

I reproduced this part of your scene graph:

PandaNode render S:(CullFaceAttrib RescaleNormalAttrib)
  BulletDebugNode Debug (2 geoms)
  PandaNode body [pickable] T:(pos 127232 87808 4032)
    GeomNode rohr_005.mod (1 geoms: S:(TextureAttrib)) [name overwrite]
    BulletGhostNode Ghost

For me the debug geometry has been exactly where is should be, at (127232 87808 4032).
Your scene graph has hundreds of objects. I can reproduce all of it by hand, obviously. So please strip it down to a reasonable size, that is less than 5 objects.

dl.dropbox.com/u/6841319/screens … 11-602.jpg <- new image
dl.dropbox.com/u/6841319/render-ls.txt <- new log

Cannot tell you why the debug shape is placed there. But the ghost is at the correct place according to “Ghost Point3(125952, 80896, 3484.8)”. I only can tell you that im using the 245 run of the win32-buildbot.

I do not have “dynamic” objects in the sense of bullet. In my case bullet is needed for pure and fast collisiondetection.

To the “iteration” thing: I did not make myself clear. I mean a “world.get_all_overlaps” may be helpful so one could avoid iterating over all objects every frame in python and one would only have to process the listed “object-objects” collision which count should be much smaller than the object count.

The script below reproduces your scene graph (without the terrain, which is not related to this at all). Of course I am using my own models, a simple box.

I have the camera looking at the location of the ghost shape, and both the model and the debug show up for me. Please verify that you can see the ghost at the corrent position on your machine. Next, write a script which reproduces your problem.

PandaNode render S:(CullFaceAttrib RescaleNormalAttrib)
  ModelNode camera
    Camera cam ( PerspectiveLens ) T:q(pos 666.163 -4839.4 1796.76 hpr -55.6155 0.637059 0)
      PerspectiveLens fov = 39.3201 30
  BulletDebugNode Debug (2 geoms)
  PandaNode controller [pickable] T:(pos 125952 80896 3484.8)
    PandaNode part T:(scale 2)
      GeomNode mine1.mod (1 geoms: S:(TextureAttrib)) [name overwrite] T:(scale 500)
      BulletGhostNode Ghost (1 shapes)
import direct.directbase.DirectStart
from panda3d.core import Vec3
from panda3d.bullet import BulletDebugNode
from panda3d.bullet import BulletWorld
from panda3d.bullet import BulletPlaneShape
from panda3d.bullet import BulletRigidBodyNode
from panda3d.bullet import BulletBoxShape
from panda3d.bullet import BulletGhostNode
from panda3d.bullet import BulletConvexHullShape
from panda3d.bullet import BulletTriangleMesh
from panda3d.bullet import BulletTriangleMeshShape

base.cam.setPosHpr(666.163, -4839.4, 1796.76, 5.28297, -16.5635, -0.537553)
base.cam.lookAt(125952, 80896, 3484.8)
base.camLens.setFar(200000.0)

# --Debug--
debugNP = render.attachNewNode(BulletDebugNode('Debug'))
debugNP.show()
debugNP.node().setVerbose(True)
debugNP.reparentTo(render)
#debugNP.showTightBounds()
#debugNP.showBounds()

world = BulletWorld()
world.setGravity(Vec3(0, 0, -9.81))

# --World--
world.setDebugNode(debugNP.node())

boxNP = loader.loadModel('models/box.egg').findAllMatches('**/+GeomNode').getPath(0)
boxGeom = boxNP.node().getGeom(0)

# --NP:controller--
np1 = render.attachNewNode('controller')
np1.setPos(125952, 80896, 3484.8)
np1.setTag('pickable', '')

# -- NP:part--
np2 = np1.attachNewNode('part')
np2.setScale(2)

# --NP:mine1--
np3 = boxNP
np3.setName('mine1.mod')
np3.setTag('name', '')
np3.setTag('overwrite', '')
np3.setScale(500)
np3.reparentTo(np2)

# -- NP:ghost--
mesh = BulletTriangleMesh()
mesh.addGeom(boxGeom)
shape = BulletTriangleMeshShape(mesh, dynamic=False)
shape.setLocalScale(2000)
np4 = np2.attachNewNode(BulletGhostNode('Ghost'))
np4.node().addShape(shape)
np4.setPos(0, 0, 0)
np4.node().setDisableDeactivation(True)
world.attachGhost(np4.node())

render.ls()

# Update
def update(task):
  dt = globalClock.getDt()
  world.doPhysics(dt)
  return task.cont

taskMgr.add(update, 'update')
run()

Im still waiting for the answer to the last question

Please go to your last question and search downwards from there, looking for “@Anon”. I did reply on the next day already!

ok, missed it.
Still, I dont think you understand what Im asking

I know, i think i said the same. My point is, again, wouldn’t it be better to generate the BulletRigidBodyNode from a collisionNode, which is optimized for collisions, rather than using on geomNodes? I understand that collisionNodes are optimized for panda’s collision system, but at least the “not triangulating quads” thing might be an optimization for Bullet’s collision detection as well, thats why Im asking.

@ennox: just move
"np1.setPos(125952, 80896, 3484.8) " directly above the “render.ls()” and you will see.

@Nox_firegalaxy

Ok, I can see now what you mean. The good news is that there is no bug in the bullet debug renderer. The ghost (or rigid or soft) bodies are exactly where they are drawn by the debug renderer. What you see is a shortcoming of the Panda3D scene graph itself.

The class BulletBodyNodes are containers for the real Bullet bodies. To move a Bullet body you can use np.setPos() and other methods. The BulletBodyNodes implement a hook provided by Panda3D - the “transform_chaged” hook - which is called if the transform of a node changes, e. g. when a user calls np.setPos() etc.

In your case the transform never changes, because you call setPos on a PARENT of the body node. And the Panda3D scene graph doesn’t propagate this even down to the children of this node. So the body node doesn’t know that its ABSOLUTE transform has changed, and so the Bullet body stays where it is, at (0, 0, 0).

I have been aware of this problem since I started to work on Panda3D Bullet module, but forgot about it with time since there are ways to avoid this trap:

1.) Set up you scene graph top down. That is, first create the parent nodes and set their transforms, then the child nodes.

2.) Use another scene graph structure. The BulletBodyNodes are the root objects of a logical object, and GeomNodes or other PandaNodes belonging to this logical object are children of the BulletBodyNode. This way, if you mode a logical object, you will always call setPos etc. on the BulletBodyNode.

3.) Force an update of the BulletBodyNode’s transform after you have changed the transform of any parent. Unfortunatly np4.setPos(np4.getPos()) won’t work, since Panda3D is too clever and recognizes that the transform has not changed. You could use

np4.reparentTo(np3.getParent())

which always invokes the transform_changed hook.

Anyway, I should talk to drwr or rdb and ask if they have any other clever ideas.

No, it would not be better. I am repeating myself, and not the first time:

  • CollisionNode → optimised data structure for Panda3D’s built in collision system.
  • BulletShape → optimised data structure for Bullet collision system.
  • Bullet collision system can’t use CollisionNode.
  • Panda3D’s collision system can’t use BulletShape.

If you want to use Bullet you have to CREATE BulletShapes. You create BulletShapes by setting their geometry. Whatever source of the geometry you use is up to you.

You can use CollisionSolids as a source. But whatever optimisation CollisionSolids provide for Panda3D’s internal collision system is not important. You just read the Collisionsolid’s geometry and copy it to the BulletShape.

This is not more or less effective than copying a GeomNode’s geometry to a BulletShape. Or read it from an external file. It’s a matter of you personal preference and your personal needs.

By the way, there are some funny ideas in your last post.

  • You can render a quad or polygon, but you can not use it for collision detection because a quad is not an unambigiously defined surface. All collision systems work with triangles only (and not quads or polygons or triangle strips or fans). In fact when creating a BulletTriangleMesh from a Geom I first have to decompose the Geom into triangle primitives.

  • Bullet has shapes which Panda3D’s collision system does not know, for example cone or or heighfields. And Bullet (like PhysX too) distinguishes between convex and concave meshes: BulletTriangleMeshShape vs. BulletConvexHullShape. If you use CollisionSolids as only source of BulletShapes you won’t be able to optimally make use of Bullet, and soon run into performance issues.

Option one wont work because the positions are propagated. The second options feels a bit odd to be honoust so I guess I will stick with option 3.

Just to play it save:
If you had to find all convexobject-convexobject collisions (not collisionpoints, just collisions) per frame ; How would you implement it with the bullet binding if it should be most well performing and the collisioncount is much small than the object count.

EDIT: just tested np4.reparentTo(np3.getParent()) in the testsample but I do not work as expected. I have tried parenting the ghost to render +
np4.setTransform(np2.getTransform(render)) too. Even np4.setPos(np2.getPos(render)) seems to have no effect on the debug node. So what am I doing wrong?

EDIT2: is there a way to avoid the doPhysics(0) call if I only need the collisionstuff? I noticed that doPhysics seems to update the “debug” node, is that right?

EDIT3: okay the “not changed” positionstuff from EDIT is due to the fact, that it do not like doPhysics(0) at all. So a time different to 0 seems be required.

A bit of a feature request, is it possible for you to add support for different directions when setting damping?

Right now if I set linear damping if effects all axes, though I don’t want it to effect the z axes.

You didn’t need to,

I know, again, “generate the BulletRigidBodyNode from a collisionNode”, not use one in place of the other

I know, I posted a code where I generate them from geoms.

Woah, “funny ideas”? They are not ideas, it’s what I’ve learned from Panda’s documentations. Even if it was, it’s still rude.

From what I understand there isn’t a geomQuads panda3d.org/manual/index.php/GeomPrimitive
And um,

If Bullet only works on triangles, then its solved.

@Anon
I really meant well, but it seems I can’t find the right words to answer your questions. So I will stop answering them now. Maybe someone else is able to help you. Don’t forget that you always can look at the source code.

My fault. I should have been

np4.reparentTo(np4.getParent())

Effectively nothing changes in the scene graph. However, the transform_changed hook is called on the np4 node.
I currently evaluate ways to automatically handle such cases, so this workaround is not needed. But it probably will be some time before I can provide a solution.

Yes, doPhysics does a lot of stuff inside. Including stepping the simulation and updating the debug node.
Bullet itself can be used as a collision-only system, without physics. However, the Panda3D Bullet module does not support this.

Well, you can directly access the array of pairs which overlap, like this:

    self.world.doPhysics(dt)
    pairs = [(mf.getNode0().getName(),
              mf.getNode1().getName())
      for mf in self.world.getManifolds() if mf.getNumManifoldPoints() > 0]
    print pairs

For each overlapping pair one persistent manifold is created. Filtering this list for only those manifolds which have at least one manifold point (“contact point”) will give you a list of all object pairs which touch each other. The list does not contain any duplicates.

Do you mean friction and not damping? If so then you are looking for body.setAnisotropicFriction(Vec3).

It you mean damping then I don’t know any Bullet methods which support this. So it would be a feature request for Bullet itself and not the integration of Bullet in Panda3D.

I meant damping. I find it quite useful for creating custom character controllers, though it makes them fall much slower.

I did not know whether it was a Bullet problem or not.

Guess I will have to come up with a workaround.

@ennox is it possible that the debug render node is affected by the clipping? Because if 0,0,0 is in the “to-be-clipped” region all debug-shapes disappear.

EDIT: ye the debugnode ist clipped. Can be avoided by:

        debugNode.setBounds(OmniBoundingVolume()) 
        debugNode.setFinal(True)

This sounds like a use case for force fields. I keep an eye out for possible solutions, but I don’t know out of the box solutions.

It is just a GeomNode. Your solution is a good one - I didn’t think of this before. I will add this to the setup of the debug node this weekend. Thanks for the idea.