Panda Bullet

Ah, fair enough, and thank you for your explanation and thoughts.

As to what I’m going to do, I think that I might have a solution in mind–a partial version that I had previously constructed looks promising, and I have some ideas that might make it a viable approach–but I don’t know whether it’s going to work as I hope, or how it might scale given that I’m currently sticking to Python scripting.

(Without going into detail, it revolves around testing whether two lines have passed each other, and is, I believe, actually more stable at high speeds than low (I might even end up putting in a special case for low-speed situations).)

If that falls through I might fall back on a “game-logic” solution: testing for “hits” and “block” by virtue of whether or not the character initiated an appropriate block within the appropriate time-frame.

Your suggestion of several spheres sounds as though it should work.

I’ll hopefully post in either the Showcase or Code Snippets regarding whatever I come up with, presuming that I manage to get something sufficiently functional working, and I remember to post. :slight_smile:

One more thought: I think most games use a very simple logic to determine if a slash is a hit or a miss. Usually the player can not determine WHERE the sword should strike, i. e. in which direction (enemy head, arms, legs…). The player can only START the slash by pressing some attack button. Perhaps he can choose between different attack animations, but usually they only determin the amount of damage done (so called “power attacks”). I think the hit-or-fail logic is the same in most cases: the attack hits if the enemy is within sword range, maybe limited to the attacker’s front. So the check would be a simple overlap test with a sphere (radius = sword range). Many games, for example Oblivion, have a weapon property which holds the weapon range. This would be the perhaps the most simple implementation, and sufficient if you don’t want to give the player explicit control over how exactly to swing a weapon.

Indeed–but the reason that I’m looking into so complex a system is because I do indeed intend to give the player relatively explicit control over their character’s attacks, and view the action from a fairly close perspective. The combat mechanic that I have in mind is currently one-on-one and relatively sparsely encountered, not one-on-many and very frequent, and is focussed around attacks, parries and dodges.

Even so, as I mentioned, I may yet fall back on a “game-logic” solution. Even aside from the problems of collision, it may allow me to avoid the concern of things like arms blocking each other when attempting to swing.

(I have actually reduced the degree of control that the player has in my current iteration of the system, but that’s related to the input mechanism: the mouse is well-suited to pointing, but less so to swiping, I’ve found, or otherwise I’ve failed thus far to find a good method of providing the level of control that I had originally in mind.)

What if you used a collision shape exaggerated in the direction of the sword swing:

That is just a convex hull in the shape of a 2d trapezoid or shallow trapezoidal prism with a collision margin about as wide as the sword to make it 3d. It gets wider towards the tip of the sword since that part moves the fastest. The graphical representation of the sword is on the leading edge of the collision shape, so it won’t appear to hit anything before it gets there. The shape must always be reoriented to the sword’s current direction of travel though, and when it is not moving or moving slowly you may wish to switch to a thinner collision shape.

If you calibrated the shape correctly to the estimated speed of the sword (plus a little extra padding just to be sure) then you should get good contact:

Hmm… I have considered something much like that, I believe, but I’m not entirely sure of why I set it aside; I think that I may have been uncertain of how to go about it. It does seem as though it should work (albeit that my approach would be slightly different: I already have two points for each sword–one each at the base and tip, more or less–with which (if I keep the previous positions of those two points) I can define a quad or pair of triangles (perhaps the latter, to account for non-planar changes, since the sword moves in three dimensions), meaning that I shouldn’t need to rely on estimations of sword speed. I think that I should be able to get penetration depth, and from that an estimation of order of penetrations (allowing me to find the first contact and respond only to that).

I’ll think more on it, I intend, although I’ll likely try my “line-intersection” idea first–thank you. :slight_smile:

I may have discovered some obscure bugs in panda bullet:

#1

Python crashes (completely, no traceback) about 50% of the time after you remove the final child shape from the compound shape of a rigid body via a python callback. The crash doesn’t happen immediately after calling remove_shape() on the rigid body, but sometime before the in-progress BulletWorld.do_physics() call returns. It only seems to happen if the shape is removed during a callback. I have only tested rigid bodies, but maybe this happens for other body nodes as well.

Interestingly enough, I found a seemingly obsolete workaround on the bullet forums from years ago, which fixes this issue. That is, by adding and removing the rigid body from the world immediately after its final shape is removed, the possibility of a crash is avoided.

#2

BulletManifoldPoint.get_index0/1 returns a weird integer value (generally between 1 and 2 billion) for non-compound shapes. Especially if there isn’t a way to test for compound-ness from python, it may be best for it to return 0 (i.e. the smallest index value) since it has only one shape.

#3

It appears that BulletManifoldPoint.get_index0/1 and BulletRayHit.get_triangle_index both return the index of a compound shape child shape. I suggest giving these functions a consistent name and leave out “triangle” since this makes it seem exclusive to triangle mesh shapes. Maybe “get_shape_index”?

Similarly BulletManifoldPoint.get_part_id0/1 and BulletRayHit.get_shape_part, if indeed they return the same information, might use the same name. Is there any info on what these do, by the way? I can only ever seem to get a return value of -1 from them.

#4

As mentioned in #3 (first paragraph), there are python functions that return a child shape index for both collisions (via manifold points) and ray tests. Would it make sense to have similar functions for sweep tests and ghosts do you think?

#1
Yes, I have seen happen this myself before. The reason is that Bullet in some cases is not able to update the internal bounding box correctly.

The link you gave advises that after adding/removing shapes the mass properties have to be updated. We do this already, from the very first moment on: BulletBodyNode.add/remove_node both call the virtual function “shape_changed”, which is implemented in BulletbodyNode.shape_changed:

void BulletRigidBodyNode::
shape_changed() {

  set_mass(get_mass());
  transform_changed();
}

void BulletRigidBodyNode::
set_mass(PN_stdfloat mass) {

  btScalar bt_mass = mass;
  btVector3 bt_inertia(0.0, 0.0, 0.0);

  if (bt_mass > 0.0) {
    _rigid->getCollisionShape()->calculateLocalInertia(bt_mass, bt_inertia);
  }

  _rigid->setMassProps(bt_mass, bt_inertia);
  _rigid->updateInertiaTensor();
}

There is very little I can do here, beside what we already do.

Background information: native Bullet allows only one shape per body, and this shape can not have a local transform. If you want to have several shapes you have to add one btCompundShape, which in turn can have several child shape with local transforms.
for Panda3D I wanted to have a flat list of shapes (zero, one or more) where each shape can have a local transform. In order to emulate this I do quite a bit of logic in add/remove_shape methods:

  • Zero child shapes means I have one native btEmptyShape assigned to the rigid body.
  • One shape without local transform: the shape is added directly to the rigid body.
  • One shape with local transform: the body has one compound shape, which holds one child shape.
  • Two or more shapes: the body has one compund shape with several child shapes.
    So we never have the situation that the native Bullet body has no shape at all assigned; even if the list of shapes in Python is empty we have a special btEmptyShape shape assigned.

#2 & #3
I agree that the names are not very, well, intuitive. However, there has been a reason for choosing exactly these names: we (mostly me) don’t understand exactly what data the native Bullet member exactly hold, so chhosing our own names is likely to produce problems. In such a situation I think it is best to stick as close as possible to the native Bullet names. This has the advantage that users can look at the original Bullet documentation (incl. forums etc.) and have a chance to transfer knowledge they gain from these source to Panda3D Bullet. Here is an example of how closely we follow the native names:

////////////////////////////////////////////////////////////////////
//     Function: BulletManifoldPoint::get_index0
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
int BulletManifoldPoint::
get_index0() const {

  return _pt.m_index0;
}

index0 is a member in C++ Bullet, but since the Panda3D Python wrappers can’t create Python members I have to expose it as a getter method.

Of course this approach has disadvantages too. For example if the Bullet C++ API changes a tiny bit we have to adjust the Python API here too, and run into problems with ABI compatibiliy… hence it is always best to use Bullet development snapshots when dealing with Bullet.

#4
No, would not make sense, because sweep and ghost test are “intersections” of volumes, and don’t have exactly defined hit points, unlike raycasts or contact tests.
Or more practically: since native Bullet makes no such information available we can’t expose it from C++ to Python.

Sorry, I should have quoted the part of the link I was referring to–

Since he said this was fixed (six years ago) I imagine these fixes are already a part of the bullet version panda bullet is currently using (although it is interesting he mentions a bounding box issue as you have). But the workaround he gives of removing and re-attaching a rigid body after a shape has been altered, solves issue #1 as well. Specifically this is the relevant python code I used:

rigid_body.remove_shape(shape)
world.remove_rigid_body(rigid_body)
world.attach_rigid_body(rigid_body)

And the problem is eliminated.
(Although this code might be overkill, since it only needs to add/remove the rigid body after the last child shape has been removed during a python callback. The bug doesn’t seem to occur outside of these circumstances)

This is exactly the case where issue #2 occurs. If you call BulletManifoldPoint.get_index0/1 on this rigid body, the shape index you get back is a large somewhat random integer like 1,732,654,231. Naturally, when you call get_shape(index) on a body using an index value like this, it is out of range.

Good point; on closer examination this problem does seem to be with bullet itself.

Specifically, Bullet’s documentation says-
LocalShapeInfo gives extra information for complex shapes. Currently, only btTriangleMeshShape is available, so it just contains triangleIndex and subpart.
But I am finding (through panda bullet) that BulletRayHit.get_triangle_index() returns an accurate child shape index for compound shapes. So Bullet’s current documentation and naming convention appears to give a partly inaccurate description. I maybe should report this to the bullet developer(s).

So there is no practical way to determine which child shape(s) have been intersected?

I have seen this possible back then too, and I can confirm that it help. Like you I simply tested by using remove/attach functions calls from Python.

But there are two reasons why I didn’t want to change the code:
(1) In order to be able to remove from & add to a world automatically I would need a reference to the BulletWorld inside BulletBodyNode. I don’t have, and the native btCollisionObject doesn’t have too. Adding such a reference would introduce cyclic redundancy which I try to avoid. Well, I could use a WPT (weak pointer to)… so this is not a real showstopper.
(2) Adding and removing an object are heavy operation, which also reset various Bullet internal states of an object. So I didn’t want to call it in unnecessary situations. I didn’t know back then that it happen ONLY when removing the last shape. Are you certain that this is the only case when it happens?

Summary: it is possible to add a workaround on the C++ level, but given that there is a workaround on the Python level too I don’t see much need for making the code more complicated and less maintainable. Hmm, give me a few days. Maybe I add it anyway.

By the way: you can check the Bullet version using this utility function.

from panda3d.bullet import get_bullet_version

Well, yes, Bullet documentation is not the very best, for example compared with PhysX. But please consider that Bullet is an open sourc eproject, like Panda3D, and people don’t get payed for workin gon Bullet.

Since Bullet 3 is on its way and Bullet 2 won’t get much support during this phase I doubt Bullet developers will pay much attention to users complaining about naming issues in Bullet 2. I would wait until Bullet 3 is finally released.

I am not aware of a practical way. My hope is that in Bullet 3 things will be cleaned up.

Yeah, I am fairly certain. I added a “print rigid_body.get_shapes()” statement to the end of the function that removes shapes from a rigid body. After many tests I can only seem to get the python crash when the print out has reported an empty list (of shapes) for at least one of the colliding rigid bodies.

Also, forget what I said earlier about it happening during a python callback; that doesn’t matter I have now discovered. Instead, the crash only happens when the last shape is removed from a rigid body when its AABB is overlapping that of another rigid body.

Whatever you decide is best. I just wanted to report the issue and the extent to which I was able to isolate it.

It reports v2.81. Whenever I encounter a bug (that does not originate between keyboard and chair) I usually update to the latest nightly build of panda.

And you make a good point that many issues might already be gone in upcoming v3.x.

Hi guys,

I’m able to reproduce a segmentation fault when adding BulletTriangleMeshShapes to an active BulletRigidBodyNode, though only after doing a specific sequence of adding and removing shapes from the node.

I’m running this app using the Panda3D 1.8.1 SDK on Windows 8.

When the segfault happens I get some interesting :util(debug): log messages:

:util(debug): Making copy of GeomTriangles because it is shared by 2 pointers.
:util(debug): Making copy of GeomTriangles because it is shared by 2 pointers.
:util(debug): Making copy of GeomTriangles because it is shared by 2 pointers.
:util(debug): Making copy of GeomTriangles because it is shared by 2 pointers.
:util(debug): Making copy of GeomTriangles because it is shared by 2 pointers.
:util(debug): Making copy of GeomTriangles because it is shared by 2 pointers.
:util(debug): Making copy of GeomTriangles because it is shared by 2 pointers.
:util(debug): Making copy of GeomVertexArrayData because it is shared by 2 pointers.
:util(debug): Making copy of CopyOnWriteObj1<class ov_multiset<class PandaNode::DownConnection,struct std::less<class PandaNode::DownConnection> >> because it is locked in read mode.
:util(debug): Making copy of CopyOnWriteObj1<class ov_set<class PandaNode::UpConnection,struct std::less<class PandaNode::UpConnection> >> because it is locked in read mode.
:util(debug): Making copy of CopyOnWriteObj1<class ov_multiset<class PandaNode::DownConnection,struct std::less<class PandaNode::DownConnection> >> because it is locked in read mode.
:util(debug): Making copy of CopyOnWriteObj1<class ov_set<class PandaNode::UpConnection,struct std::less<class PandaNode::UpConnection> >> because it is locked in read mode.
:util(debug): Making copy of CopyOnWriteObj1<class ov_multiset<class PandaNode::DownConnection,struct std::less<class PandaNode::DownConnection> >> because it is locked in read mode.
:util(debug): Making copy of CopyOnWriteObj1<class ov_set<class PandaNode::UpConnection,struct std::less<class PandaNode::UpConnection> >> because it is locked in read mode.
:util(debug): Making copy of GeomTextGlyph because it is shared by 2 pointers.
:util(debug): Making copy of GeomVertexArrayData because it is shared by 2 pointers.

I have an example project that reproduces the segfault and a log file if you want to look at it. It’s basically a chopped up version of my minecraft-like game, Flying Islands. I’m creating BulletTriangleMeshShapes to make the physical geometry for floating groups of blocks.

[Link to example project removed, PM me if you want to take a look at it, I’m sort of nervous about sharing my source out in the open]
flyingislands.net/dumptruck/fig-dist/game.log

Sorry I couldn’t get a smaller script to reproduce it, but it’s a really finicky bug that requires you to break the blocks on one of the islands in a specific order. I guess the bug is dependent on the sequence of adding and removing shapes from the node. Check out IslandBulletShaper in island.py to see how I’m creating the BulletTriangleMeshShapes (at the heart of it I’m just basically using the example from the manual page: panda3d.org/manual/index.php … ion_Shapes)

My questions to you are:

  1. Am I allowed to change the geometry of a BulletRigidBodyNode while it’s active?
  2. What notify-level configs should I set to get more information about this?
  3. I’m not fully caught up on this thread, so apologies if this has already been caught and worked-around. I’m going to read through it right now…
  4. Adding and removing the BulletRigidBodyNode from the world right after updating its meshes suppresses the segfault (might be related to deus’ bug #1 from his post above?)

Would it be possible to expose bounding sphere data on a shape via btCollisionShape::getBoundingSphere?

This is useful information (radius and position) for AI obstacle awareness and aiming. Also useful for spawning objects at a safe distance from each other.

Sure. Give me a few days.
I’m a bit surprised we don’t already have it…

Well, I exposed this method. But the values you get in return are perhaps not what you might expect (or I would have expected). Seems to be the bounding sphere in the shape’s local coordinates.

Many thanks. In my particular use case I am transforming the geometry of convex hull shapes at the vertex level during creation, so that I can rotate as well as position them. As a side effect of this, shape coordinates do not differ from the local body coordinates, so everything works as expected anyway. :slight_smile:

Is there a way to directly access a compound shape so as to call get_bounds() on it?
I am looking to get the bounding spheres of both the child shapes and their parental compound shape.

Forgot to mention that I added such a method too. I also renamed the single-shape method, so now we have body.get_shape_bounds() and shape.get_shape_bounds().

Excellent!

Hi,

Sorry but this is probably a very n00b question. I’m trying to implement two Bullet objects in panda, one a character controller and the other an object the character controller interacts with. Initially I tried to use the CharacterController provided, but I had weird behaviour when trying to rotate the character controller using node.set_h() (huge jumps in rotation). So I created a very basic kinematic rigid body and just used set_pos() to move it. It actually works pretty well, but it’s not as nice as using linear_velocity.

So I thought I’d gotten away with that bodge, but of course when my character rotates in Panda (i.e. I walk past the object and then turn around to face it) and I apply a force to the other object, it goes the wrong way because the Y axis (for example) is reversed. In other words all my physics objects are out of sync with their rotation compared to Panda. I assumed Panda would handle that when I called set_h but obviously not. In pure bullet code I could just rotate the body by calling CentreOfMassTransform and setting rotation on a Quaternion, but I can’t access that through BulletRigidBodyNode.

I could start from scratch and do a transform something like enn0x does in the set_linear function of BulletCharacterControllerNode when set_linear=true, I just wondered if I was going about this the right way.

Cheers,
Zobbo

Well, apologies for that, I seem to have got it working using this:

   btRigidBody* rig = static_cast<btRigidBody*>(rigidNode_->get_object());
	btTransform trans = rig->getCenterOfMassTransform();
	btQuaternion transrot = trans.getRotation();
	btQuaternion rotquat;
	rotquat = rotquat.getIdentity();
	rotquat.setX(x);
	rotquat.setY(y);
	rotquat.setZ(z);
	transrot = rotquat * transrot;
	trans.setRotation(transrot);
	rig->setCenterOfMassTransform(trans);

Now I just have to make sure it’s in sync with Panda.

Cheers,
Zobbo

Hello Zobbo

Not sure if I understand your problem right, but here are a few general tips:

1.) The bullet character controller is just meant as a “default” or “example” implementation. If you have specific needs (which is very likely - most games will have) it might be a good idea to derive from BulletBaseCharacterControllerNode and implement zur own controller. It’s not that difficult. Of course this is only possible if using C++ (and not Python)

2.) When using the default character controller you should use the set_angular_movement method and not set_h. Setting the orientation should be done only one when positioning the controller in the worl, and from there on it should be controlled via angular velocity (degrees per second). The character controller will apply this angular velocity automatically during the next call to world.do_physics

3.) When using default character controller you should use a right hand Z-up coordinate system.

4.) Reparenting visual object below physics objects should (hopefully) be everything needed to keep them in sync with physics