diff --git a/direct/src/directscripts/python-mode.el b/direct/src/directscripts/python-mode.el index 3e6b243d65..283430274a 100644 --- a/direct/src/directscripts/python-mode.el +++ b/direct/src/directscripts/python-mode.el @@ -101,12 +101,12 @@ See the Python Mode home page for details: :group 'languages :prefix "py-") -(defcustom py-python-command "ppython" +(defcustom py-python-command "python" "*Shell command used to start Python interpreter." :type 'string :group 'python) -(defcustom pyd-python-command "ppython" +(defcustom pyd-python-command "python_d" "*Shell command used to start Python interpreter." :type 'string :group 'python) @@ -142,7 +142,7 @@ mode buffer is visited during an Emacs session. After that, use :group 'python) -(defcustom pyd-python-command-args '("-d -i") +(defcustom pyd-python-command-args '("-i") "*List of string arguments to be used when starting a Python shell." :type '(repeat string) :group 'python) @@ -562,6 +562,8 @@ Currently-active file is at the head of the list.") (define-key py-shell-map "\C-c=" 'py-down-exception) ;; VR STUDIO ENHANCEMENTS (define-key py-shell-map "\C-d" 'comint-delchar-or-maybe-python-resume) + (define-key py-shell-map [return] 'comint-interrupt-subjob-or-maybe-return) + (define-key py-shell-map [C-return] 'comint-send-input) (define-key py-shell-map "\C-c\C-r" 'python-resume) (define-key py-shell-map "\C-c\C-s" 'pyd-shell) ) @@ -3165,6 +3167,15 @@ These are Python temporary files awaiting execution." (python-resume) (delete-char arg)))) +(defun comint-interrupt-subjob-or-maybe-return (arg) + "Enter a return (comint-send-input) or send a comint-interrupt-subjob + if point is at the end of the buffer and there is no input" + (interactive "p") + (let ((proc (get-buffer-process (current-buffer)))) + (if (and (eobp) proc (= (point) (marker-position (process-mark proc)))) + (comint-interrupt-subjob) + (comint-send-input)))) + ;; Function to try to resume panda mainloop (defun python-resume () (interactive) diff --git a/direct/src/directscripts/runPythonEmacs b/direct/src/directscripts/runPythonEmacs index e78668f71c..7777fa972e 100755 --- a/direct/src/directscripts/runPythonEmacs +++ b/direct/src/directscripts/runPythonEmacs @@ -1,6 +1,20 @@ #! /bin/sh -# Note: De-cygwin-ifying the PYTHONPATH is now the job of ppython +# Under Windows/Cygwin, we have to de-cygwinify the semicolon +# separated PYTHONPATH + +# First, initialize the new path +NEWPYTHONPATH= + +# To iterate, temporarily change the semicolons into spaces +for path in `echo $PYTHONPATH | sed 'y/;/ /'`; do + # Then for each entry run it through the cygwin filter + NEWPYTHONPATH=$NEWPYTHONPATH\;`cygpath -w $path` +done + +# Export the new PYTHONPATH +PYTHONPATH=$NEWPYTHONPATH +export PYTHONPATH # Lets also de-cygwinify the Project variables (so you can use file name # completion) This is hardcoded for the most popular trees diff --git a/direct/src/directutil/DirectCameraControl.py b/direct/src/directutil/DirectCameraControl.py index 577fd2509f..2e195b4890 100644 --- a/direct/src/directutil/DirectCameraControl.py +++ b/direct/src/directutil/DirectCameraControl.py @@ -6,8 +6,8 @@ class DirectCameraControl(PandaObject): def __init__(self, direct): # Create the grid self.direct = direct - self.defChan = direct.chanCenter - self.camera = self.defChan.camera + self.chan = direct.chanCenter + self.camera = self.chan.camera self.orthoViewRoll = 0.0 self.lastView = 0 self.coa = Point3(0) @@ -34,39 +34,39 @@ class DirectCameraControl(PandaObject): # Otherwise, check for a hit point based on current mouse position # And then spawn task to determine mouse mode numEntries = self.direct.iRay.pick(render,chan.mouseX,chan.mouseY) + coa = Point3(0) if(numEntries): # Start off with first point minPt = 0 # Find hit point in camera's space - self.coa = self.direct.iRay.camToHitPt(minPt) - self.coaDist = Vec3(self.coa - self.zeroPoint).length() + hitPt = self.direct.iRay.camToHitPt(minPt) + coa.set(hitPt[0],hitPt[1],hitPt[2]) + coaDist = Vec3(coa - self.zeroPoint).length() # Check other intersection points, sorting them # TBD: Use TBS C++ function to do this if numEntries > 1: for i in range(1,numEntries): hitPt = self.direct.iRay.camToHitPt(i) dist = Vec3(hitPt - self.zeroPoint).length() - if (dist < self.coaDist): - self.coaDist = dist - self.coa = hitPt + if (dist < coaDist): + coaDist = dist + coa.set(hitPt[0],hitPt[1],hitPt[2]) minPt = i # Handle case of bad coa point (too close or too far) - if ((self.coaDist < (1.1 * self.defChan.near)) | - (self.coaDist > self.defChan.far)): + if ((coaDist < (1.1 * self.chan.near)) | + (coaDist > self.chan.far)): # Put it out in front of the camera - self.coa.set(0,10,0) - self.coaDist = 10 + coa.set(0,100,0) + coaDist = 100 else: # If no intersection point: # Put coa out in front of the camera - self.coa.set(0,10,0) - self.coaDist = 10 + coa.set(0,100,0) + coaDist = 100 - # Place the marker in render space - self.coaMarker.setPos(self.camera,self.coa) - # Record this point for later use - self.coaMarkerPos = self.coaMarker.getPos() + # Update coa and marker + self.updateCoa(coa, coaDist) # Now spawn task to determine mouse fly mode self.determineMouseFlyMode() # END MOUSE IN CENTRAL REGION @@ -79,17 +79,13 @@ class DirectCameraControl(PandaObject): taskMgr.removeTasksNamed('manipulateCamera') def determineMouseFlyMode(self): - if (self.direct.fShift): - # If shift key is pressed: - self.spawnHPPan() - else: - # Otherwise, determine mouse fly mode - t = Task.Task(self.determineMouseFlyModeTask) - taskMgr.spawnTaskNamed(t, 'determineMouseFlyMode') + # Otherwise, determine mouse fly mode + t = Task.Task(self.determineMouseFlyModeTask) + taskMgr.spawnTaskNamed(t, 'determineMouseFlyMode') def determineMouseFlyModeTask(self, state): - deltaX = self.defChan.mouseX - self.initMouseX - deltaY = self.defChan.mouseY - self.initMouseY + deltaX = self.chan.mouseX - self.initMouseX + deltaY = self.chan.mouseY - self.initMouseY if ((abs(deltaX) < 0.1) & (abs(deltaY) < 0.1)): return Task.cont else: @@ -99,6 +95,17 @@ class DirectCameraControl(PandaObject): self.spawnXZTranslate() return Task.done + def updateCoa(self, cam2point, coaDist = None): + self.coa.set(cam2point[0], cam2point[1], cam2point[2]) + if coaDist: + self.coaDist = coaDist + else: + self.coaDist = Vec3(self.coa - self.zeroPoint).length() + # Place the marker in render space + self.coaMarker.setPos(self.camera,self.coa) + # Record marker pos in render space + self.coaMarkerPos = self.coaMarker.getPos() + def homeCam(self, chan): chan.camera.setMat(Mat4.identMat()) @@ -121,7 +128,7 @@ class DirectCameraControl(PandaObject): def centerCamIn(self, chan,t): # Chan is a display region context taskMgr.removeTasksNamed('manipulateCamera') - markerToCam = self.coaMarker.getPos( chan.camera ) + markerToCam = self.coaMarker.getPos( chan.camera ) dist = Vec3(markerToCam - self.zeroPoint).length() scaledCenterVec = self.centerVec * dist delta = markerToCam - scaledCenterVec @@ -207,8 +214,8 @@ class DirectCameraControl(PandaObject): # But aligned with render space self.relNodePath.setHpr(self.zeroPoint) - parent = self.defChan.camera.getParent() - self.defChan.camera.wrtReparentTo(self.relNodePath) + parent = self.camera.getParent() + self.camera.wrtReparentTo(self.relNodePath) manipTask = self.relNodePath.lerpHpr(VBase3(degrees,0,0), CAM_MOVE_DURATION, @@ -219,11 +226,12 @@ class DirectCameraControl(PandaObject): manipTask.uponDeath = self.reparentCam def reparentCam(self, state): - self.defChan.camera.wrtReparentTo(state.parent) + self.camera.wrtReparentTo(state.parent) def spawnHPanYZoom(self): + # Kill any existing tasks + taskMgr.removeTasksNamed('manipulateCamera') # Negate vec to give it the correct sense for mouse motion below - # targetVector = self.coa * -1 targetVector = self.coa * -1 t = Task.Task(self.HPanYZoomTask) t.targetVector = targetVector @@ -231,84 +239,93 @@ class DirectCameraControl(PandaObject): def HPanYZoomTask(self,state): targetVector = state.targetVector - distToMove = targetVector * self.defChan.mouseDeltaY - self.defChan.camera.setPosHpr(self.defChan.camera, - distToMove[0], - distToMove[1], - distToMove[2], - (0.5 * self.defChan.mouseDeltaX * - self.defChan.fovH), - 0.0, 0.0) + distToMove = targetVector * self.chan.mouseDeltaY + self.camera.setPosHpr(self.camera, + distToMove[0], + distToMove[1], + distToMove[2], + (0.5 * self.chan.mouseDeltaX * + self.chan.fovH), + 0.0, 0.0) return Task.cont def spawnXZTranslateOrHPPan(self): + # Kill any existing tasks + taskMgr.removeTasksNamed('manipulateCamera') t = Task.Task(self.XZTranslateOrHPPanTask) - t.scaleFactor = (self.coaDist / self.defChan.near) + t.scaleFactor = (self.coaDist / self.chan.near) taskMgr.spawnTaskNamed(t, 'manipulateCamera') def XZTranslateOrHPPanTask(self, state): if self.direct.fShift: - self.defChan.camera.setHpr(self.defChan.camera, - (0.5 * self.defChan.mouseDeltaX * - self.defChan.fovH), - (-0.5 * self.defChan.mouseDeltaY * - self.defChan.fovV), - 0.0) + self.camera.setHpr(self.camera, + (0.5 * self.chan.mouseDeltaX * + self.chan.fovH), + (-0.5 * self.chan.mouseDeltaY * + self.chan.fovV), + 0.0) else: - self.defChan.camera.setPos(self.defChan.camera, - (-0.5 * self.defChan.mouseDeltaX * - self.defChan.nearWidth * - state.scaleFactor), - 0.0, - (-0.5 * self.defChan.mouseDeltaY * - self.defChan.nearHeight * - state.scaleFactor)) + self.camera.setPos(self.camera, + (-0.5 * self.chan.mouseDeltaX * + self.chan.nearWidth * + state.scaleFactor), + 0.0, + (-0.5 * self.chan.mouseDeltaY * + self.chan.nearHeight * + state.scaleFactor)) return Task.cont def spawnXZTranslate(self): + # Kill any existing tasks + taskMgr.removeTasksNamed('manipulateCamera') t = Task.Task(self.XZTranslateTask) - t.scaleFactor = (self.coaDist / self.defChan.near) + t.scaleFactor = (self.coaDist / self.chan.near) taskMgr.spawnTaskNamed(t, 'manipulateCamera') def XZTranslateTask(self,state): - self.defChan.camera.setPos(self.defChan.camera, - (-0.5 * self.defChan.mouseDeltaX * - self.defChan.nearWidth * - state.scaleFactor), - 0.0, - (-0.5 * self.defChan.mouseDeltaY * - self.defChan.nearHeight * - state.scaleFactor)) + self.camera.setPos(self.camera, + (-0.5 * self.chan.mouseDeltaX * + self.chan.nearWidth * + state.scaleFactor), + 0.0, + (-0.5 * self.chan.mouseDeltaY * + self.chan.nearHeight * + state.scaleFactor)) return Task.cont def spawnMouseRotateTask(self): - self.relNodePath.setPos(render, self.coaMarkerPos) - self.relNodePath.setHpr(self.defChan.camera, self.zeroPoint) + # Kill any existing tasks + taskMgr.removeTasksNamed('manipulateCamera') + # Set at markers position in render coordinates + self.relNodePath.setPos(self.coaMarkerPos) + self.relNodePath.setHpr(self.camera, self.zeroPoint) t = Task.Task(self.mouseRotateTask) - t.wrtMat = self.defChan.camera.getMat( self.relNodePath ) + t.wrtMat = self.camera.getMat( self.relNodePath ) taskMgr.spawnTaskNamed(t, 'manipulateCamera') def mouseRotateTask(self, state): wrtMat = state.wrtMat self.relNodePath.setHpr(self.relNodePath, - (-0.5 * self.defChan.mouseDeltaX * 180.0), - (0.5 * self.defChan.mouseDeltaY * 180.0), + (-0.5 * self.chan.mouseDeltaX * 180.0), + (0.5 * self.chan.mouseDeltaY * 180.0), 0.0) - self.defChan.camera.setMat(self.relNodePath, wrtMat) + self.camera.setMat(self.relNodePath, wrtMat) return Task.cont def spawnHPPan(self): + # Kill any existing tasks + taskMgr.removeTasksNamed('manipulateCamera') t = Task.Task(self.HPPanTask) taskMgr.spawnTaskNamed(t, 'manipulateCamera') def HPPanTask(self, state): - self.defChan.camera.setHpr(self.defChan.camera, - (0.5 * self.defChan.mouseDeltaX * - self.defChan.fovH), - (-0.5 * self.defChan.mouseDeltaY * - self.defChan.fovV), - 0.0) + self.camera.setHpr(self.camera, + (0.5 * self.chan.mouseDeltaX * + self.chan.fovH), + (-0.5 * self.chan.mouseDeltaY * + self.chan.fovV), + 0.0) return Task.cont def enableMouseFly(self): @@ -320,30 +337,30 @@ class DirectCameraControl(PandaObject): # disable C++ fly interface base.disableMouse() # Accept middle mouse events - self.accept('mouse2', self.mouseFlyStart, [self.defChan]) - self.accept('mouse2-up', self.mouseFlyStop) + self.accept('handleMouse2', self.mouseFlyStart, [self.chan]) + self.accept('handleMouse2Up', self.mouseFlyStop) def enableHotKeys(self): t = CAM_MOVE_DURATION - self.accept('u', self.uprightCam, [self.defChan]) - self.accept('c', self.centerCamIn, [self.defChan, 0.5]) - self.accept('h', self.homeCam, [self.defChan]) + self.accept('u', self.uprightCam, [self.chan]) + self.accept('c', self.centerCamIn, [self.chan, 0.5]) + self.accept('h', self.homeCam, [self.chan]) for i in range(1,9): - self.accept(`i`, self.SpawnMoveToView, [self.defChan, i]) - self.accept('9', self.swingCamAboutWidget, [self.defChan, -90.0, t]) - self.accept('0', self.swingCamAboutWidget, [self.defChan, 90.0, t]) + self.accept(`i`, self.SpawnMoveToView, [self.chan, i]) + self.accept('9', self.swingCamAboutWidget, [self.chan, -90.0, t]) + self.accept('0', self.swingCamAboutWidget, [self.chan, 90.0, t]) self.accept('`', self.removeManipulateCameraTask) - self.accept('=', self.zoomCam, [self.defChan, 0.5, t]) - self.accept('+', self.zoomCam, [self.defChan, 0.5, t]) - self.accept('-', self.zoomCam, [self.defChan, -2.0, t]) - self.accept('_', self.zoomCam, [self.defChan, -2.0, t]) + self.accept('=', self.zoomCam, [self.chan, 0.5, t]) + self.accept('+', self.zoomCam, [self.chan, 0.5, t]) + self.accept('-', self.zoomCam, [self.chan, -2.0, t]) + self.accept('_', self.zoomCam, [self.chan, -2.0, t]) def disableMouseFly(self): # Hide the marker self.coaMarker.reparentTo(hidden) - # Accept middle mouse events - self.ignore('mouse2') - self.ignore('mouse2-up') + # Ignore middle mouse events + self.ignore('handleMouse2') + self.ignore('handleMouse2Up') self.ignore('u') self.ignore('c') self.ignore('h') diff --git a/direct/src/directutil/DirectSelection.py b/direct/src/directutil/DirectSelection.py index 359b521423..713a9250a0 100644 --- a/direct/src/directutil/DirectSelection.py +++ b/direct/src/directutil/DirectSelection.py @@ -1,4 +1,7 @@ from PandaObject import * +from DirectGeometry import * +from DirectSelection import * + class SelectionRay: def __init__(self, camera, fGeom = 1): @@ -41,6 +44,289 @@ class SelectionRay: # Convert point from object local space to camera space return entry.getInvWrtSpace().xformPoint(hitPt) + +class DirectBoundingBox: + def __init__(self, nodePath): + # Record the node path + self.nodePath = nodePath + # Compute bounds, min, max, etc. + self.computeBounds() + # Generate the bounding box + self.lines = self.createBBoxLines() + + def computeBounds(self): + self.bounds = self.nodePath.getBounds() + self.center = self.bounds.getCenter() + self.radius = self.bounds.getRadius() + self.min = Point3(self.center - Point3(self.radius)) + self.max = Point3(self.center + Point3(self.radius)) + + def createBBoxLines(self): + # Create a line segments object for the bbox + lines = LineNodePath(hidden) + lines.node().setName('bboxLines') + lines.setColor( VBase4( 1., 0., 0., 1. ) ) + lines.setThickness( 0.5 ) + + minX = self.min[0] + minY = self.min[1] + minZ = self.min[2] + maxX = self.max[0] + maxY = self.max[1] + maxZ = self.max[2] + + # Bottom face + lines.moveTo( minX, minY, minZ ) + lines.drawTo( maxX, minY, minZ ) + lines.drawTo( maxX, maxY, minZ ) + lines.drawTo( minX, maxY, minZ ) + lines.drawTo( minX, minY, minZ ) + + # Front Edge/Top face + lines.drawTo( minX, minY, maxZ ) + lines.drawTo( maxX, minY, maxZ ) + lines.drawTo( maxX, maxY, maxZ ) + lines.drawTo( minX, maxY, maxZ ) + lines.drawTo( minX, minY, maxZ ) + + # Three remaining edges + lines.moveTo( maxX, minY, minZ ) + lines.drawTo( maxX, minY, maxZ ) + lines.moveTo( maxX, maxY, minZ ) + lines.drawTo( maxX, maxY, maxZ ) + lines.moveTo( minX, maxY, minZ ) + lines.drawTo( minX, maxY, maxZ ) + + # Create and return bbox lines + lines.create() + return lines + + def updateBBoxLines(self): + ls = self.lines.lineSegs + + minX = self.min[0] + minY = self.min[1] + minZ = self.min[2] + maxX = self.max[0] + maxY = self.max[1] + maxZ = self.max[2] + + # Bottom face + ls.setVertex( 0, minX, minY, minZ ) + ls.setVertex( 1, maxX, minY, minZ ) + ls.setVertex( 2, maxX, maxY, minZ ) + ls.setVertex( 3, minX, maxY, minZ ) + ls.setVertex( 4, minX, minY, minZ ) + + # Front Edge/Top face + ls.setVertex( 5, minX, minY, maxZ ) + ls.setVertex( 6, maxX, minY, maxZ ) + ls.setVertex( 7, maxX, maxY, maxZ ) + ls.setVertex( 8, minX, maxY, maxZ ) + ls.setVertex( 9, minX, minY, maxZ ) + + # Three remaining edges + ls.setVertex( 10, maxX, minY, minZ ) + ls.setVertex( 11, maxX, minY, maxZ ) + ls.setVertex( 12, maxX, maxY, minZ ) + ls.setVertex( 13, maxX, maxY, maxZ ) + ls.setVertex( 14, minX, maxY, minZ ) + ls.setVertex( 15, minX, maxY, maxZ ) + + def getBounds(self): + # Get a node path's bounds + nodeBounds = self.nodePath.node().getBound() + for child in self.nodePath.getChildrenAsList(): + nodeBounds.extendBy(child.getBottomArc().getBound()) + return nodeBounds.makeCopy() + + def show(self): + self.lines.reparentTo(self.nodePath) + + def hide(self): + self.lines.reparentTo(hidden) + + def getCenter(self): + return self.center + + def getRadius(self): + return self.radius + + def getMin(self): + return self.min + + def getMax(self): + return self.max + + def vecAsString(self, vec): + return '%.2f %.2f %.2f' % (vec[0], vec[1], vec[2]) + + def __repr__(self): + return (`self.__class__` + + '\nNodePath:\t%s\n' % self.name + + 'Min:\t\t%s\n' % self.vecAsString(self.min) + + 'Max:\t\t%s\n' % self.vecAsString(self.max) + + 'Center:\t\t%s\n' % self.vecAsString(self.center) + + 'Radius:\t\t%.2f' % self.radius + ) + + +class DirectNodePath(NodePath): + # A node path augmented with info, bounding box, and utility methods + def __init__(self, nodePath): + # Initialize the superclass + NodePath.__init__(self) + self.assign(nodePath) + # Get a reasonable name + self.name = self.getNodePathName() + # Create a bounding box + self.bbox = DirectBoundingBox(self) + # Use value of this pointer as unique ID + self.id = self.node().this + # Show bounding box + self.highlight() + + def highlight(self): + self.bbox.show() + + def dehighlight(self): + self.bbox.hide() + + def getCenter(self): + return self.bbox.getCenter() + + def getRadius(self): + return self.bbox.getRadius() + + def getMin(self): + return self.bbox.getMin() + + def getMax(self): + return self.bbox.getMax() + + def __repr__(self): + return ('NodePath:\t%s\n' % self.name) + +class SelectedNodePaths(PandaObject): + def __init__(self): + self.selectedDict = {} + self.deselectedDict = {} + self.last = None + + def select(self, nodePath, fMultiSelect = 0): + # Do nothing if nothing selected + if not nodePath: + print 'Nothing selected!!' + return None + + # Reset selected objects and highlight if multiSelect is false + if not fMultiSelect: + self.deselectAll() + + # Get this pointer + id = nodePath.node().this + # First see if its already in the selected dictionary + dnp = self.selectedDict.get(id, None) + # If so, we're done + if not dnp: + # See if it is in the deselected dictionary + dnp = self.deselectedDict.get(id, None) + if dnp: + # It has been previously selected: + # Show its bounding box + dnp.highlight() + # Remove it from the deselected dictionary + del(self.deselectedDict[id]) + else: + # Didn't find it, create a new selectedNodePath instance + dnp = DirectNodePath(nodePath) + # Add it to the selected dictionary + self.selectedDict[dnp.id] = dnp + # And update last + self.last = dnp + return dnp + + def deselect(self, nodePath): + # Get this pointer + id = nodePath.node().this + # See if it is in the selected dictionary + dnp = self.selectedDict.get(id, None) + if dnp: + # It was selected: + # Hide its bounding box + dnp.dehighlight() + # Remove it from the selected dictionary + del(self.selectedDict[id]) + # And keep track of it in the deselected dictionary + self.deselectedDict[id] = dnp + return dnp + + def selectedAsList(self): + list = [] + for key in self.selectedDict.keys(): + list.append(self.selectedDict[key]) + + def deselectedAsList(self): + list = [] + for key in self.deselectedDict.keys(): + list.append(self.deselectedDict[key]) + + def forEachSelectedNodePathDo(self, func): + duplicateKeys = self.selectedDict.keys()[:] + for key in duplicateKeys: + func(self.selectedDict[key]) + + def forEachDeselectedNodePathDo(self, func): + duplicateKeys = self.deselectedDict.keys()[:] + for key in duplicateKeys: + func(self.deselectedDict[key]) + + def deselectAll(self): + self.forEachSelectedNodePathDo(self.deselect) + + def highlightAll(self): + self.forEachSelectedNodePathDo(DirectNodePath.highlight) + + def dehighlightAll(self): + self.forEachSelectedNodePathDo(DirectNodePath.dehighlight) + + def removeSelected(self): + selected = self.dnp.last + if selected: + selected.remove() + + def removeAll(self): + # Remove all selected nodePaths from the Scene Graph + self.forEachSelectedNodePathDo(NodePath.remove) + + def toggleVizSelected(self): + selected = self.dnp.last + # Toggle visibility of selected node paths + if selected: + selected.toggleViz() + + def toggleVizAll(self): + # Toggle viz for all selected node paths + self.forEachSelectedNodePathDo(NodePath.toggleViz) + + def isolateSelected(self): + selected = self.dnp.last + if selected: + selected.isolate() + + def getDirectNodePath(self, nodePath): + # Get this pointer + id = nodePath.node().this + # First check selected dict + dnp = self.selectedDict.get(id, None) + if dnp: + return dnp + # Otherwise return result of deselected search + return self.selectedDict.get(id, None) + + def getNumSelected(self): + return len(self.selectedDict.keys()) + """ dd = loader.loadModel(r"I:\beta\toons\install\neighborhoods\donalds_dock") dd.reparentTo(render) diff --git a/direct/src/directutil/DirectSession.py b/direct/src/directutil/DirectSession.py index 77f3db6631..22a8381cb8 100644 --- a/direct/src/directutil/DirectSession.py +++ b/direct/src/directutil/DirectSession.py @@ -1,11 +1,13 @@ from PandaObject import * from DirectCameraControl import * +from DirectManipulation import * from DirectSelection import * from DirectGeometry import * import OnscreenText + class DirectSession(PandaObject): - + def __init__(self): self.contextList = [] self.iRayList = [] @@ -15,11 +17,10 @@ class DirectSession(PandaObject): self.chanCenter = self.getChanData(0) self.cameraControls = DirectCameraControl(self) + self.manipulationControls = DirectManipulationControl(self) # Initialize the collection of selected nodePaths - self.selectedNodePaths = {} - self.selectedNodePath = None - self.lastSelected = None + self.selected = SelectedNodePaths() self.readout = OnscreenText.OnscreenText( '', 0.1, -0.95 ) # self.readout.textNode.setCardColor(0.5, 0.5, 0.5, 0.5) @@ -28,24 +29,23 @@ class DirectSession(PandaObject): self.createObjectHandles() self.useObjectHandles() - self.createBBox() - self.bboxList = [] - self.fControl = 0 self.fAlt = 0 self.fShift = 0 self.in2DWidget = 0 self.iRay = self.iRayList[0] - self.iRay.rayCollisionNodePath.node().setFromCollideMask(BitMask32().allOff()) - self.iRay.rayCollisionNodePath.node().setIntoCollideMask(BitMask32().allOff()) + self.iRay.rayCollisionNodePath.node().setFromCollideMask( + BitMask32().allOff()) + self.iRay.rayCollisionNodePath.node().setIntoCollideMask( + BitMask32().allOff()) self.hitPt = Point3(0.0) - self.actionEvents = [('selectNodePath', self.selectNodePath), - ('deselectNodePath', self.deselectNodePath), + self.actionEvents = [('select', self.select), + ('deselect', self.deselect), ('deselectAll', self.deselectAll), - ('highlightNodePath', self.highlightNodePath), - ('removeNodePath', self.removeNodePath), + ('highlightAll', self.selected.highlightAll), + ('preRemoveNodePath', self.deselect), ('in2DWidget', self.in2DWidget)] self.keyEvents = ['left', 'right', 'up', 'down', 'escape', 'space', 'delete', @@ -57,147 +57,59 @@ class DirectSession(PandaObject): 'mouse2', 'mouse2-up', 'mouse3', 'mouse3-up'] - def selectNodePath(self, aNodePath, multiSelect = 0): - self.lastSelected = aNodePath + def select(self, nodePath): + dnp = self.selected.select(nodePath) + if dnp: + # Update the readout + self.readout.reparentTo(render2d) + self.readout.setText(dnp.name) + # Show the manipulation widget + self.objectHandles.reparentTo(render) + # Adjust its size + self.objectHandles.setScale(dnp.getRadius()) - # Do nothing if nothing selected - if not aNodePath: - print 'Nothing selected!!' - return 0 - - # Reset selected objects and highlight if multiSelect is false - if not multiSelect: - self.deselectAll() - - # Record newly selected object - # Use this pointer as an index - self.selectedNodePaths[aNodePath.this] = aNodePath - self.highlightNodePath(aNodePath) - self.readout.reparentTo(render2d) - self.readout.setText(self.getNodeName(aNodePath)) - - def getNodeName(self, aNodePath): - node = aNodePath.node() - name = '' - if issubclass(node.__class__, NamedNode): - namableName = node.getName() - if len(namableName) != 0: - name = namableName - return name - - def in2DWidget(self): - self.in2DWidget = 1 - - def deselectNodePath(self, aNodePath): - # remove nodePath from selectedNodePaths dictionary if it exists - key = aNodePath.this - if self.selectedNodePaths.has_key(key): - del self.selectedNodePaths[key] - # Hide the manipulation widget - self.objectHandles.reparentTo(hidden) - self.readout.reparentTo(hidden) - self.readout.setText(' ') - taskMgr.removeTasksNamed('followSelectedNodePath') - - def deselectAll(self): - self.selectedNodePaths = {} - # Hide the manipulation widget - self.objectHandles.reparentTo(hidden) - self.readout.reparentTo(hidden) - self.readout.setText(' ') - taskMgr.removeTasksNamed('followSelectedNodePath') - - def highlightNodePath(self, aNodePath): - selectedBounds = self.getBounds(aNodePath) - # Does this work? - radius = selectedBounds.getRadius() - # radius = 5.0. - # Place the manipulation widget on the object too - self.objectHandles.reparentTo(render) - self.objectHandles.setScale(radius) - # Spawn task to have object handles follow the selected object - taskMgr.removeTasksNamed('followSelectedNodePath') - t = Task.Task(self.followSelectedNodePathTask) - t.aNodePath = aNodePath - taskMgr.spawnTaskNamed(t, 'followSelectedNodePath') + # TBD Compute widget COA + + # Update camera controls coa to this point + wrtMat = dnp.getMat(base.camera) + self.cameraControls.updateCoa( + wrtMat.xformPoint(dnp.getCenter())) + + # Spawn task to have object handles follow the selected object + taskMgr.removeTasksNamed('followSelectedNodePath') + t = Task.Task(self.followSelectedNodePathTask) + t.nodePath = dnp + taskMgr.spawnTaskNamed(t, 'followSelectedNodePath') + # Send an message marking the event + messenger.send('selectedNodePath', [dnp]) def followSelectedNodePathTask(self, state): - aNodePath = state.aNodePath - pos = aNodePath.getPos(render) + nodePath = state.nodePath + pos = nodePath.getPos(render) self.objectHandles.setPos(pos) return Task.cont - def isolateSelected(self): - selected = self.selectedNodePath - if selected: - self.showAllDescendants(selected.getParent()) - self.hideSiblings(selected) + def deselect(self, nodePath): + dnp = self.snp.deselect(nodePath) + if dnp: + # Hide the manipulation widget + self.objectHandles.reparentTo(hidden) + self.readout.reparentTo(hidden) + self.readout.setText(' ') + taskMgr.removeTasksNamed('followSelectedNodePath') + # Send an message marking the event + messenger.send('deselectedNodePath', [dnp]) - def removeNodePath(self, aNodePath): - # Method to handle the remove event sent by the Scene Graph Explorer - # Remove highlight and deselect nodePath - self.deselectNodePath(aNodePath) - # Send message in case anyone needs to do something - # before node is deleted - messenger.send('preRemoveNodePath', [aNodePath]) - # Remove nodePath - aNodePath.reparentTo(hidden) - aNodePath.removeNode() + def deselectAll(self): + self.selected.deselectAll() + # Hide the manipulation widget + self.objectHandles.reparentTo(hidden) + self.readout.reparentTo(hidden) + self.readout.setText(' ') + taskMgr.removeTasksNamed('followSelectedNodePath') - def removeSelectedNodePaths(self): - # Remove all selected nodePaths from the Scene Graph - for key in self.selectedNodePaths.keys(): - np = self.selectedNodePaths[key] - self.removeNodePath(np) - - def toggleVizSelectedNodePaths(self): - # Toggle visibility of selected node paths - for key in self.selectedNodePaths.keys(): - path = self.selectedNodePaths[key] - if path.isHidden(): - path.show() - else: - path.hide() - - def getBounds(self, aNodePath): - # Get a node path's bounds - nodeBounds = aNodePath.node().getBound() - for kid in aNodePath.getChildrenAsList(): - nodeBounds.extendBy(kid.getBottomArc().getBound()) - return nodeBounds.makeCopy() - - def showAllDescendantsSelectedNodePath(self): - # Show the descendants of the selectedNodePath - selected = self.selectedNodePath - if selected: - self.showAllDescendants(selected) - - def showAllDescendants(self, aNodePath): - aNodePath.show() - for child in aNodePath.getChildrenAsList(): - self.showAllDescendants(child) - - def showSelectedNodePathSiblings(self): - selected = self.selectedNodePath - if selected: - self.showSiblings(selected) - - def showSiblings(self, aNodePath): - aNodePath.show() - for sib in aNodePath.getParent().getChildrenAsList(): - if sib != aNodePath: - sib.hide() - - def hideSelectedNodePathSiblings(self): - selected = self.selectedNodePath - if selected: - self.hideSiblings(selected) - - def hideSiblings(self, aNodePath): - aNodePath.show() - for sib in aNodePath.getParent().getChildrenAsList(): - if sib != aNodePath: - sib.hide() + def in2DWidget(self): + self.in2DWidget = 1 def enable(self): # Start all display region context tasks @@ -205,6 +117,8 @@ class DirectSession(PandaObject): context.spawnContextTask() # Turn on mouse Flying self.cameraControls.enableMouseFly() + # Turn on object manipulation + self.manipulationControls.enableManipulation() # Accept appropriate hooks self.enableKeyEvents() self.enableMouseEvents() @@ -216,6 +130,8 @@ class DirectSession(PandaObject): context.removeContextTask() # Turn off camera fly self.cameraControls.disableMouseFly() + # Turn off object manipulation + self.manipulationControls.disableManipulation() self.disableKeyEvents() self.disableMouseEvents() self.disableActionEvents() @@ -285,6 +201,10 @@ class DirectSession(PandaObject): messenger.send('handle2DMouse1Up') if not self.in2DWidget: messenger.send('handleMouse1Up') + elif input == 'mouse2': + messenger.send('handleMouse2') + elif input == 'mouse2-up': + messenger.send('handleMouse2Up') elif input == 'mouse3': messenger.send('handleMouse3') elif input == 'mouse3-up': @@ -304,12 +224,12 @@ class DirectSession(PandaObject): elif input == 'escape': self.deselectAll() elif input == 'l': - if not self.lastSelected: - self.selectNodePath(self.lastSelected) + if self.selected.last: + self.select(self.selected.last) elif input == 'delete': - self.removeSelectedNodePaths() + self.selected.removeAll() elif input == 'v': - self.toggleVizSelectedNodePaths() + self.selected.toggleVizAll() elif input == 'b': base.toggleBackface() elif input == 't': @@ -317,39 +237,6 @@ class DirectSession(PandaObject): elif input == 'w': base.toggleWireframe() - def createBBox(self, parent = None): - # Create a line segments object for the bbox - if parent is None: - parent = hidden - bbox = self.bbox = LineNodePath(parent) - #bbox.setName('bbox') - bbox.setColor( VBase4( 1., 0., 0., 1. ) ) - bbox.setThickness( 0.5 ) - - # Bottom face - bbox.drawTo( 0.0, 0.0, 0.0 ) - bbox.drawTo( 1.0, 0.0, 0.0 ) - bbox.drawTo( 1.0, 1.0, 0.0 ) - bbox.drawTo( 0.0, 1.0, 0.0 ) - bbox.drawTo( 0.0, 0.0, 0.0 ) - - # Front Edge/Top face - bbox.drawTo( 0.0, 0.0, 1.0 ) - bbox.drawTo( 1.0, 0.0, 1.0 ) - bbox.drawTo( 1.0, 1.0, 1.0 ) - bbox.drawTo( 0.0, 1.0, 1.0 ) - bbox.drawTo( 0.0, 0.0, 1.0 ) - - # Three remaining edges - bbox.moveTo( Point3( 1.0, 0.0, 0.0 ) ) - bbox.drawTo( 1.0, 0.0, 1.0 ) - bbox.moveTo( Point3( 1.0, 1.0, 0.0 ) ) - bbox.drawTo( 1.0, 1.0, 1.0 ) - bbox.moveTo( Point3( 0.0, 1.0, 0.0 ) ) - bbox.drawTo( 0.0, 1.0, 1.0 ) - - bbox.create() - def createObjectHandles(self): oh = self.objectHandles = hidden.attachNewNode( NamedNode('objectHandles') ) @@ -429,3 +316,4 @@ class DisplayRegionContext(PandaObject): self.mouseDeltaY = self.mouseY - self.mouseLastY # Continue the task return Task.cont + diff --git a/direct/src/extensions/NodePath-extensions.py b/direct/src/extensions/NodePath-extensions.py index 80fcf3c924..4fa2b20381 100644 --- a/direct/src/extensions/NodePath-extensions.py +++ b/direct/src/extensions/NodePath-extensions.py @@ -1,8 +1,23 @@ + """ NodePath-extensions module: contains methods to extend functionality of the NodePath class """ + def getNodePathName(self): + from PandaModules import * + # Initialize to a default value + name = '' + # Get the bottom node + node = self.node() + # Is it a named node?, If so, see if it has a name + if issubclass(node.__class__, NamedNode): + namedNodeName = node.getName() + # Is it not zero length? + if len(namedNodeName) != 0: + name = namedNodeName + return name + # For iterating over children def getChildrenAsList(self): childrenList = [] @@ -10,6 +25,39 @@ childrenList.append(self.getChild(childNum)) return childrenList + def toggleViz(self): + if self.isHidden(): + self.show() + else: + self.hide() + + def showSiblings(self): + for sib in self.getParent().getChildrenAsList(): + if sib != self: + sib.show() + + def hideSiblings(self): + for sib in self.getParent().getChildrenAsList(): + if sib != aNodePath: + sib.hide() + + def showAllDescendants(self): + self.show() + for child in self.getChildrenAsList(): + self.showAllDescendants(child) + + def isolate(self): + self.showAllDescendants() + self.hideSiblings() + + def remove(self): + # Send message in case anyone needs to do something + # before node is deleted + messenger.send('preRemoveNodePath', [self]) + # Remove nodePath + self.reparentTo(hidden) + self.removeNode() + # private methods def __getBlend(self, blendType):