Hi. I am new to Panda. I wish to achieve the select-drag-drop action for my project involving Chemical Reactions. The User should be able to drag an atom around the screen and drop it where he/she wishes to.
Referring to the Chessboard tutorial that comes along with Panda, Ive written a piece of code (thats a basic modification of the tutorial), but it has obvious logical gaps that I cant find my way around. I would be grateful if someone can help me with this.
import direct.directbase.DirectStart
from pandac.PandaModules import CollisionTraverser,CollisionNode
from pandac.PandaModules import CollisionHandlerQueue,CollisionRay
from pandac.PandaModules import AmbientLight,DirectionalLight,LightAttrib
from pandac.PandaModules import TextNode
from pandac.PandaModules import Point3,Vec3,Vec4,BitMask32
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
from direct.task.Task import Task
import sys
#First we define some contants for the colors
BLACK = Vec4(0,0,0,1)
WHITE = Vec4(1,1,1,1)
HIGHLIGHT = Vec4(0,1,1,1)
PIECEBLACK = Vec4(.15, .15, .15, 1)
#This function, given a line (vector plus origin point) and a desired z value,
#will give us the point on the line where the desired z value is what we want.
#This is how we know where to position an object in 3D space based on a 2D mouse
#position. It also assumes that we are dragging in the XY plane.
#
#This is derived from the mathmatical of a plane, solved for a given point
def PointAtZ(z, point, vec):
return point + vec * ((z-point.getZ()) / vec.getZ())
#A handy little function for getting the proper position for a point in space to place the atom at
def SquarePos(i):
return Point3((i%8) - 3.5, int(i/8) - 3.5, 0)
font = loader.loadFont("cmss12")
class World(DirectObject):
def __init__(self):
#This code puts the standard title and instruction text on screen
self.title = OnscreenText(text="Atom Drag Drop Test",
style=1, fg=(1,1,1,1), font = font,
pos=(0.8,-0.95), scale = .07)
self.escapeEvent = OnscreenText(
text="ESC: Quit", font = font,
style=1, fg=(1,1,1,1), pos=(-1.3, 0.95),
align=TextNode.ALeft, scale = .05)
self.mouse1Event = OnscreenText(
text="Left-click and drag: Pick up and drag piece",
style=1, fg=(1,1,1,1), pos=(-1.3, 0.90), font = font,
align=TextNode.ALeft, scale = .05)
self.accept('escape', sys.exit) #Escape quits
base.disableMouse() #Disble mouse camera control
camera.setPosHpr(0, -13.75, 6, 0, -25, 0) #Set the camera
self.setupLights() #Setup default lighting
#Since we are using collision detection to do picking, we set it up like
#any other collision detection system with a traverser and a handler
self.picker = CollisionTraverser() #Make a traverser
self.pq = CollisionHandlerQueue() #Make a handler
#Make a collision node for our picker ray
self.pickerNode = CollisionNode('mouseRay')
#Attach that node to the camera since the ray will need to be positioned
#relative to it
self.pickerNP = camera.attachNewNode(self.pickerNode)
#Everything to be picked will use bit 1. This way if we were doing other
#collision we could seperate it
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay() #Make our ray
self.pickerNode.addSolid(self.pickerRay) #Add it to the collision node
#Register the ray as something that can cause collisions
self.picker.addCollider(self.pickerNP, self.pq)
#self.picker.showCollisions(render)
#Start the task that handles the picking
self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
self.accept("mouse1", self.grabPiece) #left-click grabs a piece
self.accept("mouse1-up", self.releasePiece) #releasing places it
def mouseTask(self, task):
#Check to see if we can access the mouse. We need it to do anything else
if base.mouseWatcherNode.hasMouse():
#get the mouse position
mpos = base.mouseWatcherNode.getMouse()
#Set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
#If we are dragging something, set the position of the object
#to be at the appropriate point over the plane of the board
if self.dragging is not False:
#Gets the point described by pickerRay.getOrigin(), which is relative to
#camera, relative instead to render
nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
#Same thing with the direction of the ray
nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
self.pieces[self.dragging].obj.setPos(
PointAtZ(.5, nearPoint, nearVec))
#Do the actual collision pass
self.picker.traverse(self.object)
if self.pq.getNumEntries() > 0:
#if we have hit something, sort the hits so that the closest is first
self.pq.sortEntries()
i = int(self.pq.getEntry(0).getIntoNode())
flag=TRUE
return Task.cont
def grabPiece(self):
#If the flag set is true,set it to dragging mode
if (flag is not False):
self.dragging = TRUE
def releasePiece(self):
#Letting go of a piece.
if self.dragging is not False: #Make sure we really are dragging something
self.pieces[self.dragging].obj.setPos(Pos(self.dragging))
#We are no longer dragging anything
self.dragging = False
def setupLights(self): #This function sets up some default lighting
lAttrib = LightAttrib.makeAllOff()
ambientLight = AmbientLight( "ambientLight" )
ambientLight.setColor( Vec4(.8, .8, .8, 1) )
lAttrib = lAttrib.addLight( ambientLight )
directionalLight = DirectionalLight( "directionalLight" )
directionalLight.setDirection( Vec3( 0, 45, -45 ) )
directionalLight.setColor( Vec4( 0.2, 0.2, 0.2, 1 ) )
lAttrib = lAttrib.addLight( directionalLight )
render.attachNewNode( directionalLight.upcastToPandaNode() )
render.attachNewNode( ambientLight.upcastToPandaNode() )
render.node().setAttrib( lAttrib )
#Class to load the meshes
class Object:
def __init__(self, position):
self.obj = loader.loadModel(self.model)
self.obj.reparentTo(render)
self.obj.setColor(color)
#Do the main initialization and start 3D rendering
w = World()
run()
Thanks