Bullet behaves strangely

I decided to try ghost objects to create triggers. However, I’m getting a few problems, let’s start with a strange message when running the simulation.

from direct.showbase.ShowBase import ShowBase
from panda3d.bullet import BulletRigidBodyNode, BulletBoxShape, BulletWorld, BulletGhostNode, BulletDebugNode, BulletPlaneShape
from panda3d.core import Vec3, TransformState, Point3, ClockObject, NodePath

class MyApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.bullet_world = BulletWorld()
        self.bullet_world.set_gravity(Vec3(0, 0, -9.8))

        debug_node = BulletDebugNode('debug')
        debug_bullet = NodePath(debug_node)
        debug_bullet.reparent_to(render)
        debug_bullet.show()
        self.bullet_world.set_debug_node(debug_node)

        body = BulletRigidBodyNode('body_1')
        body.add_shape(BulletBoxShape(Vec3(1, 1, 1)))
        body.set_mass(1)
        body.set_transform(TransformState.make_pos(Point3(0, 0, 20)))
        self.bullet_world.attach_rigid_body(body)

        self.trigger = BulletGhostNode('trigger')
        shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
        self.trigger.add_shape(shape)
        self.bullet_world.attach_ghost(self.trigger)

        taskMgr.add(self.update, "update")

    def update(self, task):
        dt = globalClock.get_dt()

        self.bullet_world.do_physics(dt)

        if self.trigger.get_num_overlapping_nodes() > 0:
            pass
            #return task.done

        return task.cont

app = MyApp()
app.run()

out:

D:\TestBullet>D:\Panda3D-1.10.12-x64\python\python.exe main.py
Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)
:bullet(error): Overflow in AABB, object removed from simulation
:bullet(error): If you can reproduce this, please email bugs@continuousphysics.com

:bullet(error): Please include above information, your Platform, version of OS.

:bullet(error): Thanks.

There is another problem, the ghostly object always shows a collision. Of course, this is not all, there are still surprises, but more about them later…

I think I found that this strange message is related to the debugging node.

I’m not sure about the error-message–that is an odd one–but if I recall correctly, detection of contact with ghost-nodes in Bullet requires not only checking the number of overlapping nodes, but also the checking of contact-points between the ghost and objects detected to be overlapping.

Looking at some old code of mine, what I had was essentially something like this:

for node in colliderNode.getOverlappingNodes():
    if bulletWorld.filterTest(colliderNode, node) and bulletWorld.contactTestPair(node, colliderNode).getNumContacts() > 0:
        # Treat as within the trigger

(You might not require the “filterTest”, depending on your situation.)

Thank you, it seems that an additional contact test is needed for concave forms.

Now we can go back to the main problem, since I now have a test example working.
I have set a limited mode for clock. The essence of the problem is that the bullet slows down when the frame rate drops, which logically should not happen.

from direct.showbase.ShowBase import ShowBase
from panda3d.bullet import BulletRigidBodyNode, BulletBoxShape, BulletWorld, BulletGhostNode, BulletDebugNode, BulletPlaneShape
from panda3d.core import Vec3, TransformState, Point3, NodePath, ClockObject

class MyApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.bullet_world = BulletWorld()
        self.bullet_world.set_gravity(Vec3(0, 0, -9.8))

        debug_node = BulletDebugNode('debug')
        debug_bullet = NodePath(debug_node)
        debug_bullet.reparent_to(render)
        debug_bullet.show()

        self.clock = ClockObject.get_global_clock()
        self.clock.set_mode(ClockObject.MLimited)
        self.clock.set_frame_rate(60)

        self.bullet_world.set_debug_node(debug_node)

        body = BulletRigidBodyNode('body_1')
        body.add_shape(BulletBoxShape(Vec3(1, 1, 1)))
        body.set_mass(1)
        body.set_transform(TransformState.make_pos(Point3(0, 0, 20)))
        self.bullet_world.attach_rigid_body(body)

        self.trigger = BulletGhostNode('trigger')
        shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
        self.trigger.add_shape(shape)
        self.bullet_world.attach_ghost(self.trigger)

        taskMgr.add(self.update, "update")

    def update(self, task):
        dt = globalClock.get_dt()

        self.bullet_world.do_physics(dt)

        if self.bullet_world.contact_test(self.trigger).get_num_contacts() > 0:
            print(task.time)
            return task.done

        return task.cont

app = MyApp()
app.run()

The time for which the cube falls from a height of 20 meters will be 1.9500038999999998 seconds at 60 frames.

If you limit the frame rate to 20 frames, then the time will already be 5.850002399999999. It’s counterintuitive, maybe I forgot something simple.