CollisionHandlerGravity with multiple bitmasks

I’ve been struggling on this problem for a couple of hours. I have a world with a ground model with collision bitmask FLOORMASK, players/agents with AGENTMASK, and “static” objects with OBJMASK.

Using CollisionHandlerGravity, all three types of things land on the floor. But, I would also like objects to land on top of other objects. Can this be done with the CollisionHandlerGravity?

My code for setting up the handlers (called once in the main program):

        # Initialize the collision traverser.
        base.cTrav = CollisionTraverser()
        base.cTrav.setRespectPrevTransform(True)
        base.cTrav.showCollisions( render )

        # initialize 3 handlers: wall, gravity, and other events
        
        # the CollisionHandlerPusher for keeping items from going through walls
        base.cWall = CollisionHandlerPusher()
        
        # tracks the velocity of moving objects; CollisionHandlerFloor doesnt
        base.cFloor = CollisionHandlerGravity()
        # gravity should be -9.81m/s, but that doesn't quite work
        base.cFloor.setGravity(25)
        base.cFloor.setMaxVelocity(100)

        # Initialize the handler.
        base.cEvent = CollisionHandlerEvent()
        base.cEvent.addInPattern('%fn-into-%in')
        base.cEvent.addOutPattern('%fn-outof-%in')
       
        # initialize listeners
        base.accept('object-into-object', self._objCollisionHandlerIn)
        base.accept('object-outof-object', self._objCollisionHandlerOut)
        base.accept('agent-into-object', self._agentCollisionHandlerIn)
        base.accept('agent-into-agent', self._agentsCollisionIn)

I’m representing the objects as their top surfaces, since most of them are box-like. Here is the method, part of the class for the OBJMASK members, for determining their top surfaces and initializing their collision geometries.

    def _setupPhysics(self, model, collisionGeom='surface'):
        self.setCollideMask(OBJMASK)

        # when models are scaled down dramatically
        # collision handling is buggy.  flattenLight()  circumvents this.
        model.flattenLight()

        # setup floor collision ray for gravity
        cRay = CollisionRay()
        # set origin of ray slightly below obj
        lcorner, ucorner =model.getTightBounds()
        center = model.getBounds().getCenter()
        cRay.setOrigin(center[0],center[1],lcorner[2])
        # face it downward
        cRay.setDirection(0,0,-1)
        
        cRayNode = self.attachNewNode(CollisionNode('avatarRay'))
        cRayNode.node().addSolid(cRay)
        # nothing can collide INTO the ray
        cRayNode.node().setIntoCollideMask(BitMask32.allOff())
        # but the ray can collide INTO the FLOOR and other objects
        cRayNode.node().setFromCollideMask(FLOORMASK | OBJMASK)
     
        if collisionGeom == 'surface': 
            # find the surface of the model                    
            lcorner, ucorner =model.getTightBounds()
            center = model.getBounds().getCenter()
            left_front = Vec3(lcorner[0], lcorner[1], ucorner[2])
            left_back = Vec3(lcorner[0], ucorner[1], ucorner[2])
            right_front = Vec3(ucorner[0], lcorner[1], ucorner[2])
            right_back = ucorner
            # and make a Collision Polygon (ordering important)
            cGeom = CollisionPolygon(right_back, left_back, left_front, right_front)
        elif collisionGeom == 'sphere':
            # set up a collision sphere       
            bounds, offset = getOrientedBoundedBox(model)
            radius = bounds[0]/2.0
            cGeom = CollisionSphere(0.0, 0.0, 0.0, radius)
        
        # set so that is just considered a sensor.
        cGeom.setTangible(0)
        cNode.addSolid(cGeom)
        # it cannot collide into other objects
        cNode.setFromCollideMask(BitMask32.allOff())
        # but other objects and agents can collide INTO it
        cNode.setIntoCollideMask(OBJMASK | AGENTMASK)
        # attach to current node path
        cNodePath = self.attachNewNode(cNode)
        
        # register RayNode in GravityHandler and Traverser
        base.cFloor.addCollider(cRayNode,self)
        base.cTrav.addCollider(cRayNode, base.cFloor)

        # add this to the base collider, accessible through DirectStart
        base.cTrav.addCollider(cNodePath, base.cEvent)

If i set an object’s Z-pos/height several units above another object at the start of the simulator, it falls through other objects to the ground. In more words: the visible CollisionRay is a white line that goes through the lower objects (to the ground) and the object falls right through it – slowing down [but not stopping, as if it were actually being pushed back] as it approaches the other object and going through the ground.

I’ve tried the immediate options I could think of: tweaking velocity/gravity parameters, setting every combination of bitmasks, even turning on GravityHandler’s legacy mode! I’m really stuck - please help!

Thanks,
Dustin

The CollisionHandlerGravity and CollisionHandlerFloor are supposed to drop your object onto the highest of the things its ray collides with. So, in a perfect world, it should work correctly for objects on top of the floor, since the collision point with the objects would be the highest of the two collisions.

If you say it’s passing through your objects, perhaps this is because it’s failing to detect that collision for some reason, or something more subtle?

David

It must be something more subtle, because I’ve been able to get the Players to stand on objects, but objects still don’t gravitate to themselves. This is how I am initializing the agent’s ray tracer:

        # add collision ray to keep agent on the ground
        cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
        cRayNode = CollisionNode('actor-raynode')
        cRayNode.addSolid(cRay)
        cRayNode.setFromCollideMask(FLOORMASK) # FLOORMASK = bit(1)
        cRayNode.setIntoCollideMask(BitMask32.bit(0)) 
        self.cRayNodePath = self.actorNodePath.attachNewNode(cRayNode)
        # add colliders
        base.cFloor.addCollider(self.cRayNodePath, self.actorNodePath)
        base.cTrav.addCollider(self.cRayNodePath, base.cFloor)

And what I have for the objects class, which inherits NodePath.

 

class ObjectClass(NodePath)
...

def _setupPhysics(self, model, collisionGeom='surface'):
        self.setCollideMask(OBJMASK) # OBJMASK = Bit(5)

        model.flattenLight()

        # setup gravity pointer
        cRay = CollisionRay()
        # set origin of ray slightly below obj
        lcorner, ucorner =model.getTightBounds()
        center = model.getBounds().getCenter()
        cRay.setOrigin(center[0],center[1],lcorner[2])
        # face downward
        cRay.setDirection(0,0,-1)
        cRayNode = self.attachNewNode(CollisionNode('objectRay'))
        cRayNode.node().addSolid(cRay)
        # nothing can collide INTO the ray
        cRayNode.node().setIntoCollideMask(BitMask32.bit(0) )
        cRayNode.node().setFromCollideMask(BitMask32.allOn())
        cRayNode.show()
        # register RayNode in GravityHandler and Traverser
        base.cFloor.addCollider(cRayNode, self)
        base.cTrav.addCollider(cRayNode, base.cFloor)
        # TODO see if the collider geometry is defined in the model
        # ie. find it in the egg file:  cNodePath = model.find("**/collider")
        cNode = CollisionNode('object')
        if collisionGeom == 'surface':
            # find the surface of the model
            lcorner, ucorner =model.getTightBounds()
            center = model.getBounds().getCenter()
            left_front = Vec3(lcorner[0], lcorner[1], ucorner[2])
            left_back = Vec3(lcorner[0], ucorner[1], ucorner[2])
            right_front = Vec3(ucorner[0], lcorner[1], ucorner[2])
            right_back = ucorner
            # and make a Collision Polygon (ordering important)
            cGeom = CollisionPolygon(left_front, right_front, right_back, left_back)
        elif collisionGeom == 'sphere':
            # set up a collision sphere
            bounds, offset = getOrientedBoundedBox(model)
            radius = bounds[0]/2.0
            cGeom = CollisionSphere(0.0, 0.0, 0.0, radius)
        cGeom.setTangible(1)
        cNode.addSolid(cGeom)
        # objects (ray) and agents can collide INTO it
        cNode.setIntoCollideMask(OBJMASK | AGENTMASK |FLOORMASK)
        # AGENTMASK = bit(4)
        # but this surface/sphere cannot collide INTO other objects
        cNode.setFromCollideMask(BitMask32.bit(0))
        # attach to current node path
        cNodePath = self.attachNewNode(cNode)
        cNodePath.show()
        base.cTrav.addCollider(cNodePath, base.cEvent)

But, a thousand lines of code is worth a picture:

Actor can stand on the table, but the falling object’s ray goes right through it.

Interestingly, the Actor won’t stand on the table when the object’s sufrace geometry is intangible: i.e. cGeom.setTangible(0).

Thanks a million for the help.

Dustin

I don’t think that’s surprising, since the definition of intangible is that it shouldn’t affect things like the CollisionHandleFloor or CollisionHandlerGravity.

I don’t see anything obviously wrong in the code you posted, but it’s hard to tell just by looking at code. To solve your problem, we’ll need to determine what’s different between your actor and your falling object. This is just a basic debugging problem. I’d approach it by starting with your falling object and changing one thing at a time until it’s just like your actor; for instance, change it from a CollisionHandlerGravity to a CollisionHandlerFloor (assuming that’s a difference), change the bitmasks, and so on. Or, go the other way, and start with your actor and change one thing at a time until it’s just like your falling object.

Or, put just the falling object in a scene by itself, and see if it still demonstrates the problem; then enable collision visualization to see if it’s detecting the collision at all.

David

Thanks for the help, David. I’ve worked out the problem, which I believe was caused by interference between the collision geometry for the object and the ray tracer that was used by the gravity collision handler. Depending on the model, gravity collision ray needed to be launched from a different point.

If someone else wants to use this code, go here for the latest: github.com/dasmith/IsisWorld/blo … spatial.py