Panda Bullet

Actually, Im not sure what that means, the z position?

Another question: I know for Panda’s collision system you’re better off with models exported specifically as collision geometry, optimized for collision. What if you use Bullet? Will those collsion eggs work with it?
I can do something like this, but not sure how efficient it is:

def createMesh(nodepath):
	mesh = BulletTriangleMesh()
	
	# create collision solid from the model geometry
	geomNodeCollection = nodepath.findAllMatches('**/+GeomNode')
	for nodePath in geomNodeCollection.asList():
		geomNode = nodePath.node()
		for i in range(geomNode.getNumGeoms()):
			geom = geomNode.getGeom(i)
			mesh.addGeom(geom)
	shape = BulletTriangleMeshShape(mesh, dynamic=False)
	node = BulletRigidBodyNode('bulletnode')
	node.addShape(shape)
	return node

No, not the z-position, but the DISTANCE between the plan and the origin of the coordinate system. The origin is defined by the node knows as “render”, and the origin has coordinates (0, 0, 0).

What is a “collision egg”? I only know the egg file syntax. Are you perhaps talking about collision nodes? Then this question has been answered on the very first page of the manual. Please read it again.

I still dont get it, as I understand it’s an infinite plane, how does it know the angle of the distance?

I dont see it
panda3d.org/manual/index.php … th_Panda3D
panda3d.org/manual/index.php … ello_World

Well, why working on the code or the manual if I can answer forum question. Here you go. Have a look at http://www.panda3d.org/manual/index.php/Using_Bullet_with_Panda3D. There you will find the following paragraph:

So to answer your question even more explicitly:

  • No, you can not use CollisionSolids or CollisionNodes when using Bullet (or ODE or PhysX).
  • If you use Bullet you have to use the BulletCollisionShapes.
  • If you use ODE you have to use the OdeGeoms.
  • If you use PhysX you have to use PhysxShapes.

You seem a bit confused, or missing basic skills in geometry. A distance is always a skalar measurement, that is a “number”. In particular a distance does not have an angle. Angles are only defined for two directions (“vectors”).

A plane can be defined by the following equation:

a * x + b * y + c * z + d = 0

The above equation must be valid for every point (x,y,z) which is on the plane.
(a,b,c) is the normal vector of the plane.
If (a,b,c) has unit length then d is the shortest distance between the plane and the origin (0,0,0).

You might want to google for “plane equation” if this seems too complicated. It often helps to see illustrations.

It might be useful to read up about planes. It’s a concept in linear algebra, not specific to Panda3D or Bullet.
en.wikipedia.org/wiki/Plane_(geometry
A plane is usually expressed as the following formula:
Ax + By + Cz + D = 0
(Or the following, which is the same but D inverted):
A
x + By + Cz = D
Then A, B, and C would be the normalised coordinates of the normal vector of the plane, and D would be the shortest distance from the plane to the origin (note that this goes along the normal vector).

If you don’t want to have to deal with this, use Panda’s Plane class, which also supports passing three points in the constructor (keep in mind that the winding order matters for the orientation of the plane), or a point and a normal vector (probably easiest to imagine).

In fact, if you have a point and a normal vector, all you need to do is project that point onto the normal vector, and the length of that would be the D parameter.

from what I know when you set an object in your 3d modeller to be used for collisions before exporting, its optimized for collisions, but I think you can still use it like a geomNode. Although I understand what youre saying, my point is isn’t there a need to use geometry which was optimized to work fast for collisions like for Panda’s collision system? Should I just generate a Bullet mesh like in the example I posted and not worry about this?

Your viewpoint is, well, wrong. Your 3d modeller doesn’t create anything optimized for collision detection. Optimized data is managed by dedicated classes within the collision system, i. e. BulletShapes or CollisionSolids. What you are probably talking about is called “content pipeline”.

The Panda3D Bullet module has no “one single true” content pipeline, at least not yet. It support different ways. Whatever is best for you? Who knows, if not you yourself. Here are some ideas:

1.)
Create egg files which contain dedicated collision groups. Load the egg file, and you will get CollisionNodes & CollisionSolids. write code which finds these CollisionSolids and transform them into BulletShapes yourself.

2.)
Create egg files which have no collision geoemtry at all, just those things to be rendered. Find these things and feed them to the BulletTriangleMesh (or others) like in your example above, to get BulletShapes.

3.)
Create egg files which contain rendering geometry and other, simple geometry to be used for collision. Use labels to mark these simple objects. After loading find and remove the marked objects, and feed them to the BulletTriangleMesh (or others). Same from here.

4.)
Have a look at the Bullet port of the “ball and maze” sample in the last libpandabullet zip-file. It shows a way to automatically convert CollisionNodes/Solids into ridig bodies or ghost objects, including triggers. but be aware that there is no telepathy built in. This will work only in simple cases.

Whatever way you choose or invent yourself, always keep in mind the gold rule that the more “out-of-the-box” features you use the less fine-grained control you will have. Set up Bullet collision geometry yourself triangle by triangle from wherever you want is most most flexible way, but also the way where you have to spend most effort.

uh, not the modeller, the exporter

Bullet doesn’t have a representation for visible geometry, as far as I know, so there is no distinction between the two approaches on the Bullet side.

It might be splitting hairs, but still wrong.

The exporter just marks the collision geoemtry. It does not optimize it. Optimization is achived by the way the data is stored in memory and the way it is accessible by the collision detection algorithm. Optimisation is always tied to the actual collision algorithm(s). One example for optimization is the BVH (Bounding Volume Hierarchy) trees which Bullet internally builds for complex triangle meshes.

If you read the text by drwr which you quoted, then you will see that drwr also talks about the data structures used for storing the geometry.

This is in on way related to file formats. File formats, like egg, hold just the geometry. It is up to the classes managing the information loaded from files to store and provide the data in optimized ways.

@rdb:
Seem like the confusion originates from blurry distinction between “rendering geometry” and “collision geometry”, caused by Panda3D supporting collision detection against visible geoemetry. Which is, imo, a very nice feature for rapid prototyping. Do you have a suggestion on how to best summarize this? I obviously don’t find the right words to explain it. Maybe we can add a paragraph in the manual page panda3d.org/manual/index.php/Physics, since this is not related to Bullet only.

x_x

hmm… okay, Ill try to rethink all this. If a geometry is set for collisions in the exporter and so in the egg, doesnt that mean when you use loadModel and when the data types are created, the nodes are optimized for collisions and for rendering if not specified in the egg automatically, and there’s nothing you can do anymore? Thats what I understood from drwr’s posts and the suggestions to not use the geoms for collisions.
I dont know what kind of optimizations there are, but one Ive heard is rendered geometry is triangulated. So if you havent specified the geometry to be used for collisions in the egg, the loadModel will generate geoms, and optimize the geometry for rendering, like triangulate it, and so it wont be very efficient for using in collisions. I might have this all wrong, please tell me how it really is.

Hi, enn0x

Sorry for late reply I’ve been out for most of the week, and just taking advantage of the week-end to get “Pandaifyed” a little bit!

Well, tried the updated bulletDebugNode.cpp from the CVS… still the same issues.
The node seems to be the good one (as per the code). BTW I don’t know what tight bounds or bounds would look like if displayed!

I’ll have a look at the possibility to put together a small code example that illustrate both issues, as soon as possible (I can’t really commit when… sorry i’m quite busy this month)

Kind regards

Hi, am I blind or is there no way to get an event if two objects collided? Im used to the ODE setCollisionEvent but to the best of my knowledge i would have to call getOverlappingNodes on ghosts and contactTest for rigid/dynamics. Is that correct? Is not there a way to get an event per collision if a doPhysics or a informAboutCollisions is called?

P.S: Something similiar to
OdeTriMeshGeom(self.space,OdeTriMeshData(obj.node))
would be neat. I saw some “for GeomNode in my node” stuff but this does not look really efficient (at least it is python and iterative=> not that fast).

P.P.S:
the DebugNode renders the debugstuff but all are placed around 0,0,0. I tried

ghostNP = render.attachNewNode(ghost)ghostNP.reparentTo(obj.node)

and

obj.node.attachNewNode(ghost)

Maybe there is a getPos() and not a getPos(render) equivalent in the code? Btw I am using the devel version 245 for windows.

@Nox_firegalaxy

There is, but it is currently disabled. However, the API for this is still present (body.noftiyCollisions, then accept the “bullet-contact-added” etc. events).

I have decided to add triangle mesh data at Geom level. I think this is a good tradeoff between speed and flexibility.

Come on, how many Geoms are inside a GeomNode usually? One, two, five, a dozen? So this is a handful of loops in Python code. How much time is this? Nothing in relation to the code on the C++ side which iterates over hundresd or thousands of vertices and triangles.

And you have the possibility that you can select which Geom you want to use for collision geometry, and which ones you want to leave away.

Sorry, but I can’t make sense of this. Mind to explain what you want to ask actually?

@jean-claude

I think there has not been a Panda3D snapshot build after I checked in the fix for recomputing bounds. Anyway, a small sample would be great.

@Anon
You are thinking way too complicated. The Panda3D scene graph knows different types of nodes, among them:

  • GeomNode, for rendering visible geometry
  • CollisionNode, for collision detection using Panda3D’s internal collision detection system.
  • BulletRigidBodyNode (+BulletShape), for collision detection & physics using the Bullet physics engine.

Within an egg file you have groups of geometry data. A group can be marked as “collision geometry”.

When loading an egg file the loader created scene graph nodes from the groups inside an egg file. For normal groups he created GeomNodes, and for collision groups he creates CollisionNodes. This is fine if you want to use the Panda3D internal collision system.

If you want to use Bullet you somehow have to get BulletRigidBodyNodes. How you do this is up to you, and there are different ways.

It’s not related to any physics engine, just to Panda’s built-in collision system. The difference between the methods is the way it is stored in memory. For visible geometry, it’s stored in a format that can be passed to the graphics card rapidly. For collision geometry, it’s stored in a more mathematical description so that intersection tests can be performed rapidly. For instance, convex polygons are stored instead of triangles, and every polygon also stores extra data such as the mathematical plane to minimise the amount of calculations that need to be performed every frame.
Panda’s collision system has a feature to collide against visible geometry, which seems only useful for prototyping, but I personally wouldn’t mind if this functionality was missing. It should be avoided.
That said, I would word it in a way that makes it seem like a little side-feature, rather than “one of two ways” to perform collision detection.

(As for ODE, the trimesh class converts visible geometry into an ODE trimesh representation. I don’t know how the Bullet or PhysX layers handle this, if they support arbitrary meshes at all. Personally, I think there are many types of collision volumes that are vastly superior to meshes, such as convex volumes.)

Both (Bullet and PhysX) layers support this. The difference is that the ODE module allows to pass a NodePath, and then automatically collects all Geoms below this node. Bullet doesn’t do this automatically for you - you have to find the GeomNodes and Geoms yourself. For example like this:

mesh = BulletTriangleMesh()
geomNodeCollection = nodepath.findAllMatches('**/+GeomNode')
for nodePath in geomNodeCollection.asList():
  geomNode = nodePath.node()
  for i in range(geomNode.getNumGeoms()):
    geom = geomNode.getGeom(i)
    mesh.addGeom(geom)

Nox_firegalaxy has considered this a problem with regard to performance, since loops in Python are slower than loops in C++. My opinion is that the tiny loss in performance is made up by the gain in flexibility.

Bullet also has the capability to create shapes from collison solids, not only from GeomNode/Geoms:

BulletBodyNode.addShapesFromCollisionSolids(CollisionNode cnode)

By the way, both Bullet and PhysX support arbitrary convex collision geometry (“convex hulls”).

I should mention how to enable this feature. But I think this is not exactly what you are looking for. A event is created each time a contact point is created.

# enable contact events
from pandac.PandaModules import loadPrcFileData
loadPrcFileData('', 'bullet-enable-contact-events true')

# activate generation of contact events for one body
body.notifyCollisions(True)

# listen for events
self.accept('bullet-contact-added', self.doAdded)
self.accept('bullet-contact-destroyed', self.doDestroyed)

# finally the event handlers
def doAdded(self, node1, node2):
  print 'added:', node1.getName(), node2.getName()

def doDestroyed(self, node1, node2):
  print 'destroyed:', node1.getName(), node2.getName()

Please keep in mind that:
1.) More than one contact point can be created for two bodies touching each other.
2.) Contact points can get created and removed again and again while the bodies “slide” against each other.

I guess notifyCollisions does not work with Ghosts right? Of course I could iterate over all ghosts and check for overlapping ghosts but this way I would have to deal with duplicated “events” (If A overlaps with B, B overlaps with A too) and I would have to do it every frame (which is really has a massive impact on the performance if done with python). Furthermore a “space partition” would allow to only tests ghosts/bodys in the same grid node against eachother but placing the node-node iteration in pure C++ would be fast enough I guess.
I just want to have a similar behaviour like the collisionsystem of ODE and panda. There a “doCollisionCheck” like call started a collision test over all registrated elements and issues one event per object-object overlapping.

Is there any reason against adding your codesnippet for creating the TriangleMeshShape to the bulletbinding as a shortcut (i.e. as BulletTriangleMeshShape(PandaNode) )? Because I think most people will just need this simple implementation.

To the “Debugging Node Issue”:
I have multiple mainnodes with many subnodes and each of this subnodes has a BulletGhostNode attached to them. But if I enable the Debugging Node feature all of the Shapes are rendered at 0,0,0 and not at the position of the subnodes.

Not right. notifyCollisions is a method of BulletBodyNode, and thus available on all types derived from this class, i. e. rigid, soft and ghost.

Please note that checking for overlaps is not related to contacts. The Bullet overlap checks return bodies which have overlaps of their AABBs (axis aligned bounding boxes). This does not mean that they actually touch or intersect.

And no, if you check for overlaps OR contacts you won’t get duplicate results A,B and B,A. You might get more than one contact point for two bodies A,B though.

About performance, well, of course iterations done every timeframe impacts performance. But flooding the event queue with hundreds of contact events has impact too, and you have to process all of the events in Python event handlers, since there is currently no filter to selectively create/accept contact events. This is why I added a configuration parameter which enables or disables creation of contact events. Usually you don’t want to resort to this feature.

Bullet does not support subspaces, like ODE does. Sorry, but this is not a problem of the way we integrate Bullet, but this is Bullet itself. However, ghost objects can be used to find all bodies within a particular region (an arbitrary region, this is, since you can define the region of interest with one or more arbitrary shapes!).

Bullet is Bullet, and ODE is ODE. They do things differently. With time we will add convenience functions, like the one you described above. But first we want to have the original Bullet features exposed and STABLE, before we reach out for more and more add-on features (like moving platform support, better character controllers, etc.). There is lots of work to do.

No, there is no particular reason. I will add it eventually. This is very low on the priority list, since this is a pure convenience method. There is no actual functional gain or relevant performance gain.

Um, you mean you have more than one root node of the scene graph (“render”, “render2”, “render3”…)? Can you please PM me a printout of the scene graph(s) (render.ls()), and a screenshot of this scene? Maybe there is a bug with absolute/relative placement of ghost objects.