소스 검색

*** empty log message ***

Mark Mine 25 년 전
부모
커밋
79bc776db2

+ 14 - 3
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)

+ 15 - 1
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

+ 107 - 90
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')

+ 286 - 0
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)

+ 73 - 185
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,154 +57,68 @@ 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())
+
+            # 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])
 
-	# Do nothing if nothing selected
-        if not aNodePath:
-            print 'Nothing selected!!'
-            return 0
+    def followSelectedNodePathTask(self, state):
+        nodePath = state.nodePath
+        pos = nodePath.getPos(render)
+        self.objectHandles.setPos(pos)
+        return Task.cont
 
-	# Reset selected objects and highlight if multiSelect is false
-        if not multiSelect:
-            self.deselectAll()
+    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])
 
-	# 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 = '<noname>'
-        if issubclass(node.__class__, NamedNode):
-            namableName = node.getName()
-            if len(namableName) != 0:
-                name = namableName
-        return name
+    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 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')
-
-    def followSelectedNodePathTask(self, state):
-        aNodePath = state.aNodePath
-        pos = aNodePath.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 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 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 enable(self):
 	# Start all display region context tasks
         for context in self.contextList:
             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
+

+ 48 - 0
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 = '<noname>'
+        # 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):