Panda Bullet

hey, just in case.
I managed to get all this working properly by insuring bullet processing is allocated to a task_chain whose thread duration is less than the others. (ie simply put bullet alone in one task_chain)

here are the results… with no visible lag any longer (bullet task is #4)

## TASK 1 re-exerciced after 0.0794035 seconds
## TASK 3 re-exerciced after 0.020194 seconds
## TASK 4 re-exerciced after 0.0202004 seconds
## TASK 2 re-exerciced after 0.0201275 seconds
## TASK 4 re-exerciced after 0.0191932 seconds
## TASK 3 re-exerciced after 0.0191997 seconds
## TASK 2 re-exerciced after 0.0191966 seconds
## TASK 3 re-exerciced after 0.0226837 seconds
## TASK 4 re-exerciced after 0.0226882 seconds
## TASK 2 re-exerciced after 0.022687 seconds
## TASK 4 re-exerciced after 0.0195121 seconds
## TASK 3 re-exerciced after 0.0195166 seconds
## TASK 2 re-exerciced after 0.0195143 seconds
## TASK 1 re-exerciced after 0.0855763 seconds
## TASK 4 re-exerciced after 0.0227592 seconds
## TASK 3 re-exerciced after 0.0227775 seconds
## TASK 2 re-exerciced after 0.0227659 seconds
## TASK 3 re-exerciced after 0.0202487 seconds
## TASK 4 re-exerciced after 0.0202973 seconds
## TASK 2 re-exerciced after 0.020325 seconds
## TASK 3 re-exerciced after 0.0192568 seconds
## TASK 4 re-exerciced after 0.0192333 seconds
## TASK 2 re-exerciced after 0.0191966 seconds
## TASK 3 re-exerciced after 0.0184327 seconds
## TASK 4 re-exerciced after 0.0184335 seconds
## TASK 2 re-exerciced after 0.0184457 seconds
## TASK 1 re-exerciced after 0.0802387 seconds

giving something around 42fps…with spy print enabled, and roughly 60fps if no spy printing.

Hey, i am confused about something now, i wonder how can i make my bullets(projectiles) not able to collide with other bullets.

Bullet’s natural collision filtering would work great, its very simple and versatile.

bulletphysics.org/mediawiki- … _Filtering

Yes, but as far as I understand Python users will only interact with exactly one C++ thread directly. Or am I missing something?

Ok, makes sense to me. Synchronisation P2B and B2P happens inside BulletWorld::do_physics, and if this method is called only every few render frames then a certain lags will be observed. You should still be cautious. I expect any number of problems with using the Bullet module with multiple task chains.

Have a look at sample 04_Filtering.py.

The Bullet module uses the custom callback mechanism described in the links you have cited. It implements the custom callback by compaing the Panda3D “into” collision masks set for the two body nodes, i.e. np.setCollisionMask(BitMask32 mask)). Only the “into” collision masks are compared. The “from” collision masks are not used. Two bodies who should collide must have at least one bit in common.

Yeah i know how that works, i am pretty familiar with manual pages and samples, but problem is that i dont know how to prevent two bullets(as in projectiles) from colliding.

If i can set only their into collide masks, they will all have them same, and not one, but all of their positive bits will be same, so they will always collide.

Just in case, its not just two bullets, i want to prevent them all from colliding with other bullets.
Its easy to make situation where there are hundreds of bullets in simulation.

Well, you don’t have to use the same bitmask for every bullet instance. For example you can create 20 bullets, the first has a bitmask with bit 1 set, the second bullet has a bitmaks with bit 2 set and so on. This way you can create a limited number of bullets which don’t collide with each other. Objects which should collide with any bullet have to set bits 1 to 20.

If you need more bullets you could assign group them, and assign a different mask to each group. This way bullets from different groups won’t collide. Bullets from the same group will collide, but you can choose groups wise s that there are only few collisions, e. g. all bullets from the same volley are one group.

If you need hundreds of bullet wich all won’t collide with each other then you have a problem. How would you solve it if using C++? The way I would do it is to implement a custom (broadphase or narrowphase) callback.

However, if talking about Python this is not a solution, because you would run into performance problems. The reason is that the callback will be called by Bullet for EVERY collision check in the whole world, each frame. So hundreds of bullets mean ten-thousands of callback invocations, each frame. And callbacks to Python are expensive, even if you don’t do a lot of work within the callback.

If you really need this feature you should look at PhysX module. PhysX has methods to disable/enable collision for individual object pairs. But first you should reconsider if you really need to go this way.

How hard would be to make panda use bulletphysics native collision filtering? as described in first part on their wiki page about this topic?

I think such system is much more versatile than one that we use currently.

bulletphysics.org/mediawiki- … sing_masks

EDIT:
If bulletphysics native collision filtering is not disabled, maybe its only a matter of exposing second was of adding body to bullet world?

virtual void 	addRigidBody (btRigidBody *body)
virtual void 	addRigidBody (btRigidBody *body, short group, short mask)

From what i see now, in panda only first way is supported.

Hmmm… one idea would be to use both “into” and “from” collision masks, and check within the custom broadphase callback if the into-bitmask of the first object matches the from-bitmask of the second object.

But it is completely random which bject is first, and which second. What if o1.from → o2.into and o2.from → o1.into produce different results? It is difficult to set masks right. For example the sample setup within the link you cited is wrong (see the second note in the cited link).

Any ideas how to assert “symetric” group/mask settings?

Yes, i saw the note that example in bullet wiki will not work.
But i think it is not so bad to have to set up all filters and masks manually.

Here is rough sketch how i would setup my world
format is “group (bitmask)”

Collision bits:
	0  - Player ship
	1  - Enemy ships
	2  - Bullets
	3  - Stations
	30 - Picker Ray
	31 - Floor plane (zero plane)
	
0 (1,2,3,30)
1 (0,1,2,3,30)
2 (0,1,3)
30 (0,1,3,31)
31 (30)

So if i understand things right, with this setup everything would work correctly? Maybe it is bad that i would need to be careful to make everything symmetrical by hand, but i would welcome that given versatility that i would get this way.

About symmetry, that does not seem as an issue with a little bit of python code :slight_smile:

Lines 1-16 ==> sample of wanted output data
Lines 17-31==> App data setup
Lines 32-55==> Snippet to make bitmasks symmetrical, as they are not in “App data setup”, generation of ready to use int bitmasks.

pastie.org/2837975

Output is here pastie.org/2837998

With snippet like this, symmetry problem of “native” bullet physics bitmask filtering system is solved i think.

EDIT1:
Usage would be something like

#addRigidBody (btRigidBody *body, short group, short mask)
group = masks["Enemy"]["group"]
#or group = types["Enemy"]
mask = masks["Enemy"]["bitMask"]
addRigidBody(btNode,group,mask)

Not exactly what I have been suggesting.

Here is my proposal:
1.) A new config parameter “bullet-filter-algorith”, which lets you choose between the current, simple filtering (compare into-mask of two nodes) and the new filtering callback. So users who don’t need this extended filtering don’t need to bother with setting up.
2.) The new collision filtering checks collision between two nodes o1,o2 by comparing the from-collide mask of o1 with the into-collide-mask of o2.
3. I consider not only checking o1.from vs. o2.into, but also checking o1.into vs. o2.from. Only if both checks return true a collision will be created. The advantage would be that unsymmetrical setup still leads to a non-random behaviour.

Example usage, based on you setup:

# Setup:(group, collides with, int-value of mask)
# 0 ==> [1,2,3,4]    = 30
# 1 ==> [0,1,2,3,4]  = 31
# 2 ==> [0,1,3]      = 11
# 3 ==> [0,1,2,4]    = 23
# 4 ==> [0,1,3,5]    = 43
# 5 ==> [4]          = 16

# Groups and masks:
groups0 = BitMask32.bit(0)
groups1 = BitMask32.bit(1)
...

mask0 = BitMask32(30)
mask1 = BitMask32(31)
...

# Apply groups and masks to bodies
np0 = render.attachNewNode(BulletRigidBodyNode("0"))
np0.node().setFromCollideMask(groups0)
np0.node().setIntoCollideMask(mask0)
...

Please note that the following lines have the same effect:

np0.setCollideMask(groups0)
np0.node().setIntoCollideMask(mask0)

Here is an alternate way of setting up into-collision-masks using a small helper function:

bits = lambda *x: sum([1<<i for i in x])

mask0 = BitMask32(bits(1,2,3,4))
mask1 = BitMask32(bits(0,1,2,3,4))

I dont want to sound rude, so sorry if it sounds like that :slight_smile:

I fail to see how is that different from native (already implemented in bullet) bullet physics collision filtering.

Only difference that i see is in way how you assign bitmasks to nodes. IE in bullet its done when you add body to world, and with you proposal it would be possible to set bitmasks after object is added to world in easy way.

Do you plan to use already implemented filtering method or make new one?

That is same as bullet filtering works now? Sample on their wiki is invalid because before it acted as OR and now it is AND?

And apologies if I am forcing you to do work that you don’t think is needed, but i see this “lack” of collision filtering as major loss of flexibility.

Nope, it’s not the same. The collision filtering implemented using (short int) group/mask - as implemented in Bullet - is a subset what is possible with my proposal. First, you can have 32 groups and not just 15. And second, one object can be member of several groups, by setting more bits on the “from” bitmask.

I won’t use the group/mask filtering as rovided by Bullet, for several reasons:
1.) The Bullet API for setting mask/group is not consistent for rigid/ghost/soft body.
2.) We need to address “hidden” objects like in the character node too.
3.) I want an API where it is possible to choose between filter algorithms without having meaningless parameters in methods.
4.) My proposal can do more.
5.) My proposal is “Panda-ish”. Look at the manual pages for the built-in collision system of Panda3D, and you will see that the built-in system is very similar. Just that with the built-in system objects can be either “from” or “into” nodes, where here every node is both.
6.) My proposal extends the simple collision filtering API without need for modifications.

No, it bad because it is not symetrical. As I said, setting up right is a bit tricky. This is why I want to have the simple collision filtering as implemented in the Bullet module currently.

I consider an alternative approach too. I would keep a symetrical 32x32 matrix inside BulletWorld, which defines which group collides with which other groups. BulletWorld would have methods for enabeling/disableling collision between two groups, e. g. world.enableCollision(g1, g2, True). Default would be that every group collides with just itself, i. e. a identity-matrix. For the object nodes (rigid, soft, ghost, character) I would provide a method to set membership to zero, one or more groups (i. e. a groups-mask). And inside a custom callback, like in all the other cases too.

Oh, ok :slight_smile:

I get you now, and i really appreciate your efforts to make this wrapper so user friendly :slight_smile:

I have implemented a the new, alternate collision filtering. You can choose which collision filter algorithm you ant to use via the “bullet-filter-algorithm” config variable:

(1) “mask”
The old algorithm. It simply compares the collision masks of two nodes.

Consider the collision mask as membership with collision groups. There are 32 groups (0…31), and each bit set in the mask means you are member of the accoring group.

Two objects collide if they both belong to at least one group in common.

(2) “group-mask”
The new one. Again the collision mask can be considered as membership with collision groups. In addition to setting the group mask for each node you have to define which groups collide with which other group. You can setup this collision matrix with

world.setGroupCollisionFlag(g1, g2, flag)

By default each group collides only with objects from it’s own group. The matrix is an identity matrix.

You don’t have to pay attention to keep the matrix symetrical - I do it interal.

The samples.zip have been amended with a sample showing usage of the new collision algorithm. Please refer to the very first post in this thread.

For the use case of GrizzLyCRO the matrix setup should be:

# Setup: group ==> collides with groups
# 0 ==> [1,2,3,4]
# 1 ==> [0,1,2,3,4]
# 2 ==> [0,1,3]
# 3 ==> [0,1,2,4]
# 4 ==> [0,1,3,5]
# 5 ==> [4] 

world.setGroupCollisionFlag(0, 0, False)
world.setGroupCollisionFlag(0, 1, True)
world.setGroupCollisionFlag(0, 2, True)
world.setGroupCollisionFlag(0, 3, True)
world.setGroupCollisionFlag(0, 4, True)

world.setGroupCollisionFlag(1, 2, True)
world.setGroupCollisionFlag(1, 3, True)
world.setGroupCollisionFlag(1, 4, True)

world.setGroupCollisionFlag(2, 2, False)
world.setGroupCollisionFlag(2, 3, True)

world.setGroupCollisionFlag(3, 3, False)
world.setGroupCollisionFlag(3, 4, True)

world.setGroupCollisionFlag(4, 4, False)
world.setGroupCollisionFlag(4, 5, True)

I left away setting (j,i) if (i,j) is already set, since this is handled internal.

Objects of group 1 are the only ones which collide with objects of the same group. Self-collision is on by default, so we just have to set (i,i) if we want to disable it.

Small snippet to ease setup when you have many types of objects.

It would be needed only to alter “groups” and “colliders”

def setupCollisionMasks(btWorld):
    
    #first disable objects collision with objects from same group (ship-ship, bullet-bullet)
    for i in range(32):
        btWorld.setGroupCollisionFlag(i, i, False) 
    
    groups = {"Player":0,
             "Enemy" :1,
             "Bullet":2,
             "Station":3,
             "PickerRay":4,
             "FloorPlane":5
             }

    colliders = {
                 "Player":["Enemy","Bullet","Station","PickerRay"],
                 "Enemy":["Enemy","Bullet","Station","PickerRay"],
                 "PickerRay":["FloorPlane","Station"],
                 "Bullet":["Station"]
                 }

    for obj,into in colliders.iteritems():
        for val in into:
            btWorld.setGroupCollisionFlag(groups[obj], groups[val], True)

I have kind of a silly question, but how do I get Bullet to respect inertia? I apply a force to a box to get it moving. However, once I release the force the box stops moving after a few seconds. :stuck_out_tongue: Is there any hidden damping coefficient somewhere? (I’ve checked node.getLinearDamping(), and it returns 0.)

world = BulletWorld()
world.getWorldInfo().setAirDensity( 0 )

shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
node = BulletRigidBodyNode('Box')
node.setMass(1.0)
node.addShape(shape)
np = render.attachNewNode(node)
np.setPos(0, 0, 2)
world.attachRigidBody(node)
model = loader.loadModel('models/box.egg')
model.flattenLight()
model.reparentTo(np)
 
node.applyCentralForce( Vec3( -20, 0, 0) ) 
node.applyCentralForce( Vec3( 0, 0, 20) ) 

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

run()

My guess is that your body gets deactivated ecause it is too slow. If so then the observed velocity is not slowly getting smaller, but it suddenly stops, and the debug render turns from grey to green.

I am aware that the default linear threshold below which rigid bodies will be put to sleep (after some time) is rather high, but we are using the Bullet defaults here.

Here are the relevant methods

node.getLinearSleepThreshold()
node.getDeactivationTime()

node.setDeactivationEnabled(False)

node.setLinearSleepThreshold(0.1)
node.setAngularSleepThreshold(0.1)

You can either disable deactivation (“sleeping”) completely for this node, or set a lower threshold.

Please note that there has been a bug in setting the sleep thresholds: setLinearSleepThreshold() has been setting the angular threshold, and vice versa. I just commited a bug to fix this.