Bitmask Example

Here is a short example of using bitmasks to selectively test collisions against different object in the scene:

  1from direct.showbase.ShowBase import ShowBase
  2from direct.showbase.DirectObject import DirectObject
  3from direct.gui.OnscreenText import OnscreenText, TextNode
  4from panda3d.core import CollisionTraverser
  5from panda3d.core import CollisionHandlerQueue, CollisionNode, BitMask32
  6from panda3d.core import CollisionPlane, CollisionSphere, CollisionRay
  7from panda3d.core import Plane, Vec3, Point3
  8
  9
 10class World(DirectObject):
 11
 12    def __init__(self):
 13        # Create a traverser that Panda3D will automatically use every frame.
 14        base.cTrav = CollisionTraverser()
 15        # Create a handler for the events.
 16        self.collHandler = CollisionHandlerQueue()
 17
 18        # Define a few bitmasks for use.
 19        # Teaching the concepts of bitmasks is out of the scope of this sample.
 20        # This just shows a practical application of bitmasks.
 21        goodMask = BitMask32(0x1)
 22        badMask = BitMask32(0x2)
 23        floorMask = BitMask32(0x4)
 24
 25        # Make a list of different combinations of the masks for later use.
 26        # We will switch between these masks later on.
 27        self.maskList = [
 28            ["floor", floorMask],
 29            ["smiley", goodMask],
 30            ["frowney", badMask],
 31            ["characters", goodMask | badMask],
 32            ["smiley and floor", goodMask | floorMask],
 33            ["frowney and floor", badMask | floorMask],
 34            ["all", floorMask | goodMask | badMask]
 35        ]
 36        # This keeps track of where we are in the dictionary.
 37        self.maskPos = 0
 38
 39        # First we create a floor collision plane.
 40        floorNode = base.render.attachNewNode("Floor NodePath")
 41        # Create a collision plane solid.
 42        collPlane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0)))
 43        # Call our function that creates a nodepath with a collision node.
 44        floorCollisionNP = self.makeCollisionNodePath(floorNode, collPlane)
 45        # Get the collision node the Nodepath is referring to.
 46        floorCollisionNode = floorCollisionNP.node()
 47        # The floor is only an into object, so just need to set its into mask.
 48        floorCollisionNode.setIntoCollideMask(floorMask)
 49
 50        # Create a collision sphere. Since the models we'll be colliding
 51        # are basically the same we can get away with just creating one
 52        # collision solid and adding the same solid to both collision nodes.
 53        collSphere = CollisionSphere(0, 0, 0, 1.5)
 54
 55        # Make a smiley.
 56        smiley = base.loader.loadModel('smiley')
 57        smiley.reparentTo(base.render)
 58        smiley.setPos(-3, 3, 3)
 59        smiley.setName("Smiley")
 60        smileyCollisionNP = self.makeCollisionNodePath(smiley, collSphere)
 61        # Like with the floor plane we need to set the into mask.
 62        # Here we shortcut getting the actual collision node.
 63        smileyCollisionNP.node().setIntoCollideMask(goodMask)
 64
 65        # Make a frowney.
 66        frowney = base.loader.loadModel('frowney')
 67        frowney.reparentTo(base.render)
 68        frowney.setPos(-3, 3, 7)
 69        frowney.setName("Frowney")
 70        frowneyCollisionNP = self.makeCollisionNodePath(frowney, collSphere)
 71        # Use the the Nodepath.setCollideMask() function to set the into mask.
 72        # setCollideMask() sets the into mask of all child nodes to the given
 73        # mask.
 74        frowneyCollisionNP.setCollideMask(badMask)
 75        # Note that we don't call setCollideMask() from frowney because this
 76        # will turn the frowney mesh into a collision mesh which is unwanted.
 77
 78        # Note that we didn't set a from collide mask for previous objects
 79        # since we're not adding them to the traverser as from objects.
 80
 81        # Make a collision ray that passes through all of the objects.
 82        self.pointerNode = base.render.attachNewNode("Main Collider")
 83        self.pointerNode.setPos(-3, 3, 10)
 84        # Create a ray collision solid that points downwards.
 85        raySolid = CollisionRay(0, 0, 0, 0, 0, -1)
 86        mainColNP = self.makeCollisionNodePath(self.pointerNode, raySolid)
 87        self.mainColNode = mainColNP.node()
 88        # Set a from collide mask for this ray so that we can selectively
 89        # collide against the other objects.
 90        self.mainColNode.setFromCollideMask(self.maskList[self.maskPos][1])
 91        base.cTrav.addCollider(mainColNP, self.collHandler)
 92
 93        # Set up the camera.
 94        base.disableMouse()
 95        base.camera.setPos(20, -20, 5)
 96        base.camera.lookAt(0, 0, 5)
 97        # Debug mode for collision traversers; shows collisions visually.
 98        base.cTrav.showCollisions(base.render)
 99
100        # Setup the title text.
101        collideText = self.maskList[self.maskPos][0]
102        self.title = OnscreenText(text="Colliding with %s" % (collideText),
103                                  mayChange=True,
104                                  pos=(0.3, 0),
105                                  align=TextNode.ALeft,
106                                  fg=(1, 1, 1, 1))
107        OnscreenText(text="Press space to change collision mask",
108                     pos=(0, 0.8),
109                     fg=(1, 1, 1, 1))
110
111        # Set space to change the from collision mask of the collision ray.
112        base.accept("space", self.switchCollisionMask)
113
114    def makeCollisionNodePath(self, nodepath, solid):
115        '''
116        Creates a collision node and attaches the collision solid to the
117        supplied NodePath. Returns the nodepath of the collision node.
118
119        '''
120        # Creates a collision node named after the name of the NodePath.
121        collNode = CollisionNode("%s c_node" % nodepath.getName())
122        collNode.addSolid(solid)
123        collisionNodepath = nodepath.attachNewNode(collNode)
124        # Show the collision node, which makes the solids show up.
125        collisionNodepath.show()
126
127        return collisionNodepath
128
129    def switchCollisionMask(self):
130        if self.maskPos == len(self.maskList) - 1:
131            self.maskPos = 0
132        else:
133            self.maskPos += 1
134
135        # Changing the from collide mask of objects allows you to selectively
136        # test collisions against different objects.
137        name, mask = self.maskList[self.maskPos]
138        self.mainColNode.setFromCollideMask(mask)
139        self.title.setText("Colliding with %s" % (name))
140
141
142base = ShowBase()
143world = World()
144base.run()
../../../_images/collisionbitmasks.png