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()