My Panda world has a bunch of different 2D-ish objects, some of which are rectangular and some of which are round.
I’d like to be able to drag-select multiple objects, and found the code ynjh_jo wrote to do just that.
My problem is that the code in that post surrounds each pickable object with a CollisionSphere. I would like to use a CollisionPlane instead of CollisionSphere if possible to match the usually rectangular shape of objects.
Class constructor:
self.nullClickPos = None
# credit: https://discourse.panda3d.org/t/drag-selecting-multiple-nodes/2967/1
self.collTrav = CollisionTraverser('selector')
self.selHandler = CollisionHandlerQueue()
CM = CardMaker('selectionRectangle')
CM.setFrame(0, 1, 0, 1)
self.rect = render2d.attachNewNode(CM.generate())
self.rect.setColor(0, 1, 1, 0.2)
self.rect.setTransparency(1)
self.rect.hide()
LS = LineSegs()
LS.moveTo(0, 0, 0)
LS.drawTo(1, 0, 0)
LS.drawTo(1, 0, 1)
LS.drawTo(0, 0, 1)
LS.drawTo(0, 0, 0)
self.rect.attachNewNode(LS.create())
self.rectTask = None
On object creation:
radius = newNodePath.getBounds().getRadius()
cnp = newNodePath.attachCollisionSphere('cplane', 0, 0, 0, radius, BitMask32.bit(1), BitMask32.allOff())
self.collTrav.addCollider(cnp, self.selHandler)
On mouse down:
def begin_rect_select(self):
self.unselect_all() # unhighlights all objs
self.nullClickPos = Point2(base.mouseWatcherNode.getMouse())
self.rect.setPos(self.nullClickPos[0], 1, self.nullClickPos[1])
self.rect.show()
self.rectTask = taskMgr.add(self.update_rect, 'update_rect')
def update_rect(self, tsk):
# credit: https://discourse.panda3d.org/t/drag-selecting-multiple-nodes/2967/1
if not self.panda.mouse.has(): # make sure mouse is in panda window
return Task.cont
d = Point2(base.mouseWatcherNode.getMouse()) - self.nullClickPos
self.rect.setScale(d[0] if d[0] else 1e-3, 1, d[1] if d[1] else 1e-3)
return Task.cont
On mouse up:
def end_rect_select(self):
if self.nullClickPos is None or self.rectTask is None:
return
taskMgr.remove(self.rectTask)
# credit: https://discourse.panda3d.org/t/drag-selecting-multiple-nodes/2967/1
bmin, bmax = self.rect.getTightBounds()
clickBL = Point2(bmin[0], bmin[2])
clickTR = Point2(bmax[0], bmax[2])
if clickTR == clickBL: # fudge the numbers a bit to avoid the degenerate case of no rectangle
clickTR = Point2(clickTR[0] + 0.00001, clickTR[1] + 0.00001)
pt = Point3()
llF = Point3()
urF = Point3()
base.camLens.extrude(clickBL, pt, llF)
base.camLens.extrude(clickTR, pt, urF)
ulF = Point3(llF[0], llF[1], urF[2])
brF = Point3(urF[0], urF[1], llF[2])
camOrigin = Point3(0)
left = CollisionPlane(Plane(camOrigin, ulF, llF)) # create the 4 sides with planes
right = CollisionPlane(Plane(camOrigin, brF, urF)) # they should all 'face' OUTward: i.e.
bot = CollisionPlane(Plane(camOrigin, llF, brF)) # collisions are INSIDE
top = CollisionPlane(Plane(camOrigin, urF, ulF))
pyramid = camera.attachNewNode(CollisionNode('pyramid'))
pyramid.node().addSolid(left)
pyramid.node().addSolid(top)
pyramid.node().addSolid(right)
pyramid.node().addSolid(bot)
# check for collisions
self.collTrav.traverse(render)
hits = []
for i in range(self.selHandler.getNumEntries()):
hitNp = self.selHandler.getEntry(i).getFromNodePath()
print 'testing', hitNp
hits.append(hitNp)
print hits
# if the object is within the rectangle, it collides with all 4 planes.
self.select(filter(lambda i: hits.count(i) == 4, hits))
self.selHandler.clearEntries()
pyramid.removeNode()
self.rect.hide()
self.nullClickPos = None
Thank you for any suggestions!