Panda Bullet

Yes, this is still true. It has been temporarily been disabled, but it is enabled again. You might want to have a look at sample 08_Collisions (download links in the Bullet section of the manual).

I should mention that I do not compute anything myself. It is Bullet which generates the collision notifications. I just expose them to Panda3D/Python by creating an Panda3D event each time Bullet calls my callbacks.

Just out of curiousity: what would you consider “logical” events? I think the answer is tied to the game logic, and thus should not be hard coded in an underlying engine. But the engine should support being able to build logical events on top of what is provided.

OK. I guess I made a wrong assumption that the connection point is always the position of the “base” body.

Hm, you mean those are the transforms that would “move” the nodes to the connection point?
I’m not really sure why a rotation is needed.

Also there seems to be a “bug” for this constraint, the second node sometimes pans relative to itself. This is a terrible explanation, I’ll try to make an example video.

Maybe it is best to play around with the cone twist constraint yourself. Start with this setup:

    # Box B
    shape = BulletBoxShape(Vec3(0.1, 0.1, 0.1))
    bodyB = BulletRigidBodyNode('Box B')
    bodyNP = self.worldNP.attachNewNode(bodyB)
    bodyNP.node().addShape(shape)
    bodyNP.setPos(0, 0, 4)
    bodyNP.setHpr(0, 0, 0)
    self.world.attachRigidBody(bodyB)

    # Box A
    shape = BulletBoxShape(Vec3(0.4, 0.2, 0.1))
    bodyA = BulletRigidBodyNode('Box A')
    bodyNP = self.worldNP.attachNewNode(bodyA)
    bodyNP.node().addShape(shape)
    bodyNP.node().setMass(1.0)
    bodyNP.setPos(0, 0, 0)
    bodyNP.setHpr(0, 0, 0)
    self.world.attachRigidBody(bodyA)

    self.world.doPhysics(0.2)

    frameA = TransformState.makePosHpr(Point3(-2, 0, 0), Vec3(0, 0, 0))
    frameB = TransformState.makePosHpr(Point3(0, 0, -1), Vec3(0, 0, 90))
    cone = BulletConeTwistConstraint(bodyA, bodyB, frameA, frameB)
    cone.setDebugDrawSize(2.0)
    cone.setLimit(30, 30, 120, softness=1.0, bias=0.3, relaxation=1.0)
    self.world.attachConstraint(cone)

bodyB is static, e. g. the ceiling where the lamp is hanging, and it has no rotation, just an offset of +4 up. By the way: we don’t need a shape on bodyB - it is just for cheap visualisation of bodyB’s location/orientation.

frameB is the connection point in bodyB’s reference frame. Translation is 1 down, and rotation is 90 deg on Z. The default cone opening is along the +X axis, so we need to rotate +90 around Z-axis to make it point down (we want the lamp to hang DOWN after all).

Now, we want the lamp (bodyA) offsetted from the connection point, a bit below the connection point. This is where frameA kick is. try moving it up/down. Then left/right (!!!). And try to add a rotation on frameA. If you feel comfortable try moving/rotation frameB.

What I’m looking for is a way to process any collisions that occurred after the last doPhysics.

The bullet-contact-added method doesn’t do “logic” collisions, but all possible collisions, so that’s overkill.

And the contactTest(body).getContacts() wants a specific body to test against, but I’ve got a lot of bodies and want to process any collisions between them. Also doing lots of extra contactTest()s to recalculate the collisions looks like a huge resource drain, when a list of all collisions that happened in the last step should be easy for Bullet to store somewhere.

I think that’s what he meant by “logical events”. A “easy” way to get all the actual collisions that happened in the last doPhysics call.
At least that’s what I’m looking for :wink:

So your interpretation of “logical” collision is a list of all collisions which happened since the last frame? And it should be easy for Bullet (not the Bullet wrapper we provide?) to store thi slist somewhere? Hmm… this is easy. Actually it is so easy that I don’t see a reason why we should do it on the Bullet wrapper level: just start with an empty list each frame, and append whatever info you need to this list for each bullet-contact-added event. At the end of the simulation step you have your “logical collision” list. It’s really easy to do in Python. I doubt this is of any use to you.

Maybe I add my interpretation about what is meant with “logical collisions” by Hovis. But first we have to explain what a collision event created by Bullet is. Whenever two bodies touch each other at one point in space this is a collision. The problem is that two bodies can have multiple collision points. For example a box resting on a tringle mesh. Now, some games need to know as much information about each individual contact point, while other might just be interested in “do two bodies have at least one collision point”. The second question is what I think has been meant with “logical collision”. I put this term in quotation marks because I think it is a bad choice. For example such a “logical collision” does not have a location - it might be one point or several.

Again, I don’t see a reason to provide such information on the Bullet wrapper level, since again it is easy to get the information by a very thin layer on top of our Bullet wrapper: just keep a dictionary in python which has tuples of bodies as keys. Now register for the bullet-contact-added/removed events and update the dictionary with each event. Easy. Another way would be to store the list of currently colliding bodies as a set on the RigidBodyNode itself (PandaNode allows to store python tags!), and update these lists with each bullet-contact-added/removed event. Again easy. Choose the way which suits YO?UR game logic best. Other games will have other requirements.

I put logic in quotation marks for the same reason. Maybe realistic would be more accurate, but it still deserves quotation marks :wink:

The technical collisions that trigger an bullet-contact-added/removed event are not actual collisions in a “realistic” way, but in a “hitboxes have contact” way. These events also get created a lot when two objects rest on each other (yeah, technically they are not quite at rest). This of course is very much correct, as these are “contact” events. But this means all of these contacts need to be checked for collisions on top of the physics engine, while the engine has to do these checks in its main loop anyway.

But I have been looking for solutions in various places and found nothing “easy”. This should be addressed on the physics engine level, there is nothing that could be done on the wrapper level.

Can you be a bit more precise in what exactly should be done on the physics engine level? You say you found nothing “easy”. This implies that you have found solutions to some problem.

Just for the record: Bullet uses the very same manifold points which get exposed by our wrapper, in order to compute the forces which result from the contact/interpenetration.

And what is - in your optinion - the difference between collision and contact? The bullet-contact-added events are not bounding box checks.

The one solution already posted here and elsewhere is to go through all objects, get the contact data and recreate the collision information, but that’s already done in the engine itself. Wouldn’t it be better to have a log of collision data, than to go through some of the logic twice.

What I need, in the end, is a list of the collisions their 3d position and impact force data. Since this data must already exist inside the engine the easiest way to get it would be to have access to that data, not to recreate it.

You want to cycle through the list of all (persistent) manifold points each frame? No problem - get them with world.getManifolds(). This is exactly the list you are talking about. However, it is nothing “logical” or “high level”. It is just a list of data structure which Bullet internally keeps and updates when new contact get detected or existing contact are broken (hence persistent manifold point, because the data structure is not created on the fly by one-shot collision detection queries).

enn0x:

Thank you for responding to me, I wish Gmail hadn’t swallowed the notifications. You correctly interpreted what I meant by “logical” collisions. Your statement about not putting this game-logic oriented code in the Bullet wrapper is makes a lot of sense; I’m just afraid that managing it in python will be very slow. I was thinking that because the Bullet wrapper is already an intrinsic part of the engine, it could be used to generate these game logic events more efficiently. I’m afraid of the performance hit I might take while trying to manage big dicts or “touching” sets in python. Does that make sense?

Caveat: I don’t know anything about python tagging, I’ll be looking into that.

Hi ennox,
First thanks for the amazing bullet integration.

I have some questions (sorry for my bad english):

1) RESOLVED

2) When I change the scale of a RigidBodyNode, with the debug mode I can see that the shape is ok but the collisions are not good at all. Maybe it’s a problem with the internal setLocalScale method?

3) I wonder if the default characterController will be updated or if we have to use the new characterController made by coppertop?

Well, it’s not that intrinsic. Bullet is still (an will stay so) an add-on module for Panda3D, and Panda3D can be used and compiled without Bullet support. Otherwise it would be in panda3d.core, and not in panda3d.bullet.

Anyway, I agree that it might be faster if such bookkeeping is done in C++. However, the posts here in this thread and a few others did show that everybody seems to have a different opinion about what “logical” events should be.

My suggestion is to let users do their own implementations/bookeeping in Python, and once we have a couple of usable implementations sit back and analyze them, in oder to find out what the common sematic is. Then implement this is C++.

Maybe. The (user-friendly) synchronisation between Panda3D scene grahp and Bullet is not the best idea I ever had - it eats much performance and still seems to have errors. I would need a simple script to reproduce this and find out what is going wrong. Can you provide one?

The controller by Coppertop is certainly the better choice, because it is functional. Sooner or later we will try to port it from Python to C++.

I notice that there are no problems with scaled box shape, but collision problems occur when I scaled a capsule shape.
I don’t check every kind of shape but the simple following example show you a falling capsule shape scaled :

import direct.directbase.DirectStart
from panda3d.core import *
from panda3d.bullet import *
 
base.cam.setPos(0, -30, 0)
#base.cam.lookAt(0, 0, 5)
 
# World
world = BulletWorld()
world.setGravity(Vec3(0, 0, -9.81))
debugNode = BulletDebugNode('Debug')
debugNode.showWireframe(True)
debugNode.showConstraints(True)
debugNode.showBoundingBoxes(False)
debugNode.showNormals(False)
debugNP = render.attachNewNode(debugNode)
debugNP.show()
world.setDebugNode(debugNP.node())

# Plane
shape = BulletPlaneShape(Vec3(0, 0, 1), 1)
node = BulletRigidBodyNode('Ground')
node.addShape(shape)
np = render.attachNewNode(node)
np.setPos(0, 0, -2)
world.attachRigidBody(node)
 
# Boxes
model = loader.loadModel('models/box.egg')
model.setPos(-0.5, -0.5, -0.5)
model.flattenLight()
#shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
shape = BulletCapsuleShape(0.5, 1, ZUp)
node = BulletRigidBodyNode('Box')
node.setMass(1.0)
node.addShape(shape)
np = render.attachNewNode(node)
world.attachRigidBody(node)
model.copyTo(np)
np.setPos(0, 0, 10)
np.setScale(3)

def update(task):
  world.doPhysics(globalClock.getDt())
  return task.cont
 
taskMgr.add(update, 'update')
run()

enn0x: I think your plan to wait for the cream to rise to the top is a good one. I’ll do my best to provide the cream. :slight_smile:

A totally different question:

How do I apply a torque in a local reference frame?

Specifically: I’m trying to model a spaceship with one main forward-driving thruster, and also counter-balanced thrusters that JUST rotate the ship without adding any translational velocity. Expected behavior: Given that the ship is rolled 90deg about the Y axis (so that the ship’s -X axis corresponds with the world’s +Z axis), when the pilot pulls back on the stick to pitch up, then the thrusters will actually apply a torque around the world’s Z axis (ship’s X). (I’m not describing the direction of the torque vector, but the direction I expect the nose of the ship to go.)

I’ve looked at BulletRigidBodyNode.applyTorque(), and it only accepts a Vec3 in the world reference frame. I looked into the Bullet API itself, and it does the exact same thing. :neutral_face:

How would you feel about modifying BRBN.applyTorque() to take an optional first position NodePath parameter that would be used for the reference frame? This is a standard Panda-y way of specifying frames of references for lots of other operations. (See NP.setHpr at panda3d.org/reference/1.8.0/ … 2e9f1a6577)

Alternatively, can you give me some pointers on doing the math myself? I can’t figure out how to make it work. :blush: I found NP.getNetTransform(), but I’m just not sure how to use the returned TransformState to calculate my translated torque vector. I never got a chance to take a Linear Algebra course. :frowning:

Hmm… I didn’t notice anything weired. I get a capsule which is properly scale (tried scales 1.0, 2.0 and 3.0) and positioned. The capsule is stopped at exactly the right position by the ground plane.

enn0x.p3dp.com/hovis.jpg

Do you get different results?

Panda3D provides already all the math you need. In this case it is NodePath.getRelativeVector/Point.
Assuming that np is your rigid body node (of course this works with any PandaNode, not only BulletRigidBodyNodes)

v = Vec3(1, 0, 0) # local coordinates (reference frame: np):
v = render.getRelativeVector(np, v) # global coordinates (reference frame: render)

panda3d.org/reference/devel/ … ca851bc610

Convenience methods, like e. g. setHpr(other, hpr) which you mentioned, are usually found only in high-level facades. NodePath is such a facade. The underlying object (here: PandaNode) doesn’t even have setPos or setHpr. It just has the generic setTransform(TransformState ts) method. All of the NodePath.setHpr etc. methods just call the PandaNode.setTransform method.

That’s really strange, you can see different results on this pic:
imageshack.us/photo/my-images/507/bulletl.png

Scale > 1: Capsule stop above the ground
Scale < 1: Capsule stop below the ground
My OS is Win7.

This is strange. I’m on Win7 too, but I use a self-compiled Panda3D SDK. My first guess would be that you have a version of the Panda3D SDK which is compiled against an old version of the Bullet lib, since there have been changes in Bullet concerning the collision margin, and it seems to be a problem of margin and not scale. However, if I remember right then rdb confirmed that all build machines use Bullet-2.80-rev2531 now. Have you tried a recent buildbot version of Panda3D SDK?

I’m having an issue with Bullet that I posted here, but I am not sure if this is a python nuance I don’t know about or a Bullet one, so I am cross posting :slight_smile: