Panda Bullet

Oops! Sorry, the problem was really obvious :blush: All my models already have identity matrix, I just scale them in my program. However, since Iā€™m going to do a level editor, scaling collision geometry along with objects is a necessary feature. I guess that means that after any scale modification Iā€™ll have to create new collision shape?

Just do root.flattenLight() to bake any scales into the vertices.

David

Nope, that doesnā€™t work. Perhaps enn0x can override flattening methods to work on collision shapes, but itā€™s not there yet. Perhaps Iā€™ll just use an old build until scale synchronization will be fixed :slight_smile:

I have to wait until panda\src\display\graphicsEngine.cxx is fixed again in CVS HEAD before I can investiagate further.

FYI: Flattening is marked as unsafe for all PandaNodes in the Bullet module.

What, is graphicsEngine.cxx broken right now?

Edit: ah, so it was. My bad, I forgot to check in a dependent file. Fixed.

David

Thank you David.
I found the problem. Seem like I have been too eager with optimizing. I passed a CPT(TransformState) by reference, which actually allows the called method to alter the transform state. Fixed it in CVS. Scale should work again. Sorry.

Edit: the latest snapshot build contains the fix.

Thanks! Now static geometry is scaled appropriately.

However, now scaled dynamic objects behave weird. In my particular case, I have a code like this:

def LoadShip(self):
    _scl = 0.5
    scl = Vec3(_scl, _scl, _scl)
    
    ship = loader.loadModel("ship/ship")
    ship.setScale(scl)
    
    shape1 = BulletConvexHullShape()
    shape1.addGeom(GetCollider(ship, "**/coll_hull", True))
    shape1.setLocalScale(scl)
    
    shape2 = BulletConvexHullShape()
    shape2.addGeom(GetCollider(ship, "**/coll_top", True))
    shape2.setLocalScale(scl)
    
    self.ship = self.RigidBody([shape1, shape2], 1.0, False)
    
    ship.reparentTo(self.ship)
    
    self.ship.setScale(1.0 / _scl)

def RigidBody(self, shapes, mass=0, use_gravity=True, name="rigidbody"):
    node = BulletRigidBodyNode(name)
    node.setMass(mass)
    for shape in shapes:
        node.addShape(shape)
    if not use_gravity: node.setGravity(Vec3(0, 0, 0))
    np = render.attachNewNode(node)
    self.world.attachRigidBody(node)
    node.setPythonTag("NodePath", np)
    return np

This is a hacky solution to make my ship more sensitive to torque while preserving its size and mass (I couldnā€™t find the API to change moment of inertia).

In 295 Windows build this code makes resulting collision shape twice as big as visible ship, and it looks like the shape often ā€œnoticesā€ collisions when it already noticeably penetrated other object.
Of course, when I remove all scaling, the collisions are stable.

By the way, is there already a way to get/set moment of inertia?

Looking at your code you set scale at three places:
1.) The rigid body node
2.) The shapes
3.) The visual node, which is parented to the rigid body node!)

You should do it this way:
1.) set the scale on the rigid body node

The visual node is reparented to the rigid body node, so any scale set on the rigid body node is automatically set on all child nodes too.

The method shape.setLocalScale() is no longer required, since we internally call this method when the rigid bodyā€™s scale has changed. I will remove the method one of the next days.

Inertia is currently re-computed automatically every time a shape is added or removed, or if the mass is changed. So far I didnā€™t want to expose a manual way to set inertia. However, I am aware that there are use cases for this. I will think about this a little bit. Maybe we can support both, automatic and manual.

About the interpenetration: this is to be expected when using scale on physics/collision nodes. I did warn you guys about this, repeatedly!

Previously I had some experience with Newton physics libraryā€¦ It allowed to build a compound collision shape out of arbitrarily transformed primitive/convex shapes; I donā€™t remember exactly, but perhaps it was just converting them to convexes.
If I wanted to scale collision shape along with object, it was simply a matter of recreating the shape with different transformation matrices.

Iā€™m not sure about raw PhysX, but in Unity this is even more simple: a bodyā€™s shape is composed of its own collision component and all collision components of the children. If you move/rotate/scale these children, collision shape is updated accordingly.

To be honest, until recently I thought you were doing something similarā€¦ Could you shed some light on your current design?

When using bullet default character controller, i noticed that character is kind of warpingā€¦
It is evident in your(ennox) sample (sample 14) too.
Try walking in one direction, character will not move at
steady speed, itā€™s not smooth, it will kind of stutter.

I think it also happens with rigid body (kinematic), if i move nodepath with ā€œsetPosā€, but in that case it is much much less noticeable.
Maybe i am even imagining itā€¦ :stuck_out_tongue:

Is it(should it be) possible to move characterController np with setPos?

like:

node = BulletCharacterControllerNode(shape, 0.4, 'Player')
np = render.attachNewNode(node)
np.setY(np,1) #in real app, i would make it dependant on delta time

Platform win7 x64,panda build from september 6th.

Well, it is possible. But this is not the right way to move a character. The difference between setPos and moving the character with linear/angular speed is that setPos kinda teleports the character to the new location, without moving it along the space in between. At first this might not seem like a bad thing, if setPos is called every frame. But the poble is that setPos might place the character right inside the middle of some other object, and in the following frame huge forces will be applied. Moving the character with linear/angular speed checks for obstacles, and uses a ā€œcollide and slideā€ algorithm.

We have to expose both ways of moving a character. setPos internally calls the Bullet btKinematicCharacterController method ā€œwarpā€. What I have done is to exchange the method called when moving with linear/angular speed. Previously it has been setVelocityForTimeInterval(v, dt). Now I used setWalkDirection(v * dt). Maybe the characters now move a bit more fluent.

Similar to the Newton (2.x) way, or similar to the Unity way? Those are different concepts, and also different from the way PhysX. I donā€™t understand what you want to tell me here. Should I try to mimic on of these concepts? I try to stick as close as possible to the Panda3D scene graph concept. Maybe you can phrase some concrete questions.

@enn0x
Just tried new panda build, it is still very ā€œlaggyā€.
I am using .setLinearVelocity()

It happens even when character is in air.
(I played with friction in fear that it may cause that warping effect, so tried jumping, but it is still very visible)

EDIT1: i took a look at source of bullet character controller, and i donā€™t have any idea why that would happen.

That last change should not be relevant, both end up calling stepForwardAndStrafe().

Could this be one way or another related to the same issue I flagged in June, ie movement lagging ? (btw. as of today Iā€™ve not found a clear explaination)

This lag phenomenon is really a riddleā€¦

I donā€™t think that is relevant, because issue is not visible if you use setPos on NP to rigidBodyNode.

And whole NP is lagging, its not just debug node.

Well the whole NP is lagging for me too and not only in debug mode!

It is just that by displaying the debug bound geometries attached to kinematics nodes, the phenomenom is even more apparent since the Bullet nodes appear to be left behind and every couple of frame are pulled back to the actual node positionā€¦

I sincerely apologize :unamused:
I meant ā€œsimilar to Unityā€, since Newtonā€™s way is manual and as so doesnā€™t have any scale synchronization.

Here are the more (I hope) concrete questions:

  1. If a rigidbody is parented to some node, what are your plans to handle this? I can see here the following options:
    a) Rigidbody ingnores its parent (behaves as if parent was absent).
    b) Rigidbody is excluded from the simulation until it gets rid of the parent.
    c) If top-level parent is a body itself, the child bodyā€™s shape is added to parentā€™s shape. When the child becomes free, the shape is removed from parent.
    d) Same as previous + childā€™s mass is also added to/subtracted from parentā€™s mass (and center of mass & inertia recalculated).
    Depending on the situation, any of these options might be desirable, but (d) seems to be the most intuitive (aka ā€œfastened bodiesā€). So, the question: which behavior should we expect in PandaBullet, or will we be able to select the one we need?

  2. Even if (c) or (d) will not be the default behavior, itā€™s still possible to add arbitrarily transformed collision shapes (like in Newton). However, current BulletBodyNode API seems to lack methods to remove some/all shapes. Recreating the whole BulletBodyNode seems like overkillā€¦ I suppose shape removal methods will be added at some point?

  3. With (c) and (d) thereā€™s also the issue of synchronization. Any modification to childā€™s matrix/shape/mass/etc. would require recalculating shape/mass/etc. for top-level parent body. This is doable (though noticeably more cumbersome if done through python-level API)ā€¦ But I guess such deep synchronization is not in your plans. Or is it? :astonished:

Well, there are several places which might be the origin for this, not only the PandaBullet module: your python code (or my python samples), Panda itself, or Bullet itself. The character controller is not really a figurehead of Bullet - currently there are still lots of features missing, like for example proper collision response.

Anyway, a perceived lag is not enough. If we want to find the cause we first have to create a (1) reproducable and (2) measurable effect. And this is where you could help.

A starting point would be to write a python sample in which a character controller is moving at a constant speed in one direction. The lag should be perceivable here too. Next we need to quantify the lag. This means to observe the amount of movement each frame (read the nodeā€™s position, calculate delta, and convert it to speed using the time elapsed between two frames). Log all these values to a text file. If there is a measurable lag then there should be a one or more frames where the speed drops significantly (drop to zero, or just get a few percent slower?). Oh, and it might be worth to also add not only the elapsed time provided by Panda3D, but also the real time. Iā€™m not talking about secure time like in SRTP - the Python time.time should be enough.

Once we have measure the lag we can go on and try to locate the cause.

Here is basic sample, going to write some statistics gathering code next.
pastie.org/2505119

EDIT1:
Here is basic snippet for viewing speed
pastie.org/2505153

try to change sync video to true to fix not moved bug.
I left unlimited FPS, because those 0.0 while moving look bad.

And here is text data from sample run (with sync video on). Moving only in y-positive direction, removed rotation (setH on character) from code.
pastie.org/2505179

(a) is the option which is currently realized, and there are no plans to change this behavious. Each rigid body is a single node. A rigid body can have more than one shape, but it still is one rigid nody. For example a table could be made from five boxes, one for the table plate and four for the table legs.

If there are ā€œremovableā€ parts, for example an aircraft carrying a bomb, then the aircraft and the bomb would be two bodies, and the bomb should be fixed to the aircraft using a joint. With PhysX it is possible to define how much force has to be applied until the joint breaks. With Bullet Iā€™m not yet sure how to realize this.

Option (d) would mean that internally there is only one Bullet body, and I have to add up mass and inertia internally. This is something you could do one the Python side yourself, and then just create one BulletRigidBodyNode.

Regarding such thing my strategy is to keep as close to the original physics engines as possible.

For certain combinations of objects I want to add meaningfull parent/child relationships:
ā†’ a ghost obects being the child of a rigid/soft/character, i.e. example a sensor zone which moves with a body.
ā†’ a character controller being the child of a rigid body, i.e. a character on a moving platform. Because Bulletā€™s character controllers are kinematic and donā€™t care about friction between the charcter and the ground on which it moves.

Actually, the first case has been implemented until I redesigned the internal synchronisation mechanism two weeks ago. Currently it is disabled, but will be back.

Current BulletBodyNode API has both addShape and removeShape methods, so it is possible to remove some/all shapes.
https://www.panda3d.org/reference/devel/python/classpanda3d.bullet.BulletBodyNode.php

It wonā€™t be possible to add arbitrarily transformed collision shapes. It is possible to add shapes with offset and rotation. And it is possible to apply a scale, but the scale will be applied to all shapes. It wonā€™t be possible to apply shear.

None of the big commercial physics engines support transforms beyond translation + orientation, and this has a reason. Iā€™m in doubt if non-uniform scale and shear can be supported properly. It is not only the effect on collision detection, but also on inertia and contact response. If Newton 2.x handles these things corretly then I have highest respect for Julio Jerez. Anyway, Bullet donā€™t support arbitrary transforms, and I canā€™t add this feature on the wrapper/integration layer. You might want to talk to the Bullet developers here.

Of course. Any change of transforms within the subtree lead to modified mass properties and some other effects. Since the Panda3D scene graph doesnā€™t provide the necessary hook I would have to check all physics nodes each frame, calculate their delta transforms, and so onā€¦ costs some performance.

What I currently do is to loop over all physics nodes each frame twice, once before stepping the simulation and once after stepping the simulation. Before I do the Panda3Dā€“>Bullet sync, and afterwards the Bulletā€“>Panda3D sync.