Browse Source

Enhancements to DirectSelection classes

Mark Mine 22 years ago
parent
commit
d5b915eaad

+ 5 - 5
direct/src/directtools/DirectCameraControl.py

@@ -101,9 +101,7 @@ class DirectCameraControl(PandaObject):
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE
             # Skip camera (and its children), unless control key is pressed
             # Skip camera (and its children), unless control key is pressed
             skipFlags |= SKIP_CAMERA * (1 - base.getControl())
             skipFlags |= SKIP_CAMERA * (1 - base.getControl())
-            nodePath, hitPt, hitPtDist = direct.iRay.pickGeom(
-                skipFlags = skipFlags)
-            self.computeCOA(nodePath, hitPt, hitPtDist)
+            self.computeCOA(direct.iRay.pickGeom(skipFlags = skipFlags))
             # Record reference point
             # Record reference point
             self.coaMarkerRef.iPosHprScale(base.cam)
             self.coaMarkerRef.iPosHprScale(base.cam)
             # Record entries
             # Record entries
@@ -307,7 +305,7 @@ class DirectCameraControl(PandaObject):
                 self.cqEntries = self.cqEntries[:-1]
                 self.cqEntries = self.cqEntries[:-1]
                 self.pickNextCOA()
                 self.pickNextCOA()
 
 
-    def computeCOA(self, nodePath, hitPt, hitPtDist):
+    def computeCOA(self, entry):
         coa = Point3(0)
         coa = Point3(0)
         dr = direct.drList.getCurrentDr()
         dr = direct.drList.getCurrentDr()
         if self.fLockCOA:
         if self.fLockCOA:
@@ -316,9 +314,11 @@ class DirectCameraControl(PandaObject):
             coa.assign(self.coaMarker.getPos(direct.camera))
             coa.assign(self.coaMarker.getPos(direct.camera))
             # Reset hit point count
             # Reset hit point count
             self.nullHitPointCount = 0
             self.nullHitPointCount = 0
-        elif nodePath:
+        elif entry:
             # Got a hit point (hit point is in camera coordinates)
             # Got a hit point (hit point is in camera coordinates)
             # Set center of action
             # Set center of action
+            hitPt = entry.getFromIntersectionPoint()
+            hitPtDist = Vec3(hitPt).length()
             coa.assign(hitPt)
             coa.assign(hitPt)
             # Handle case of bad coa point (too close or too far)
             # Handle case of bad coa point (too close or too far)
             if ((hitPtDist < (1.1 * dr.near)) or
             if ((hitPtDist < (1.1 * dr.near)) or

+ 14 - 14
direct/src/directtools/DirectManipulation.py

@@ -40,14 +40,14 @@ class DirectManipulationControl(PandaObject):
         # Start out in select mode
         # Start out in select mode
         self.mode = 'select'
         self.mode = 'select'
         # Check for a widget hit point
         # Check for a widget hit point
-        nodePath, hitPt, hitPtDist = direct.iRay.pickWidget()
+        entry = direct.iRay.pickWidget()
         # Did we hit a widget?
         # Did we hit a widget?
-        if nodePath:
+        if entry:
             # Yes!
             # Yes!
-            self.hitPt.assign(hitPt)
-            self.hitPtDist = hitPtDist
+            self.hitPt.assign(entry.getFromIntersectionPoint())
+            self.hitPtDist = Vec3(self.hitPt).length()
             # Constraint determined by nodes name
             # Constraint determined by nodes name
-            self.constraint = nodePath.getName()
+            self.constraint = entry.getIntoNodePath().getName()
         else:
         else:
             # Nope, off the widget, no constraint
             # Nope, off the widget, no constraint
             self.constraint = None
             self.constraint = None
@@ -90,14 +90,13 @@ class DirectManipulationControl(PandaObject):
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE
             # Skip camera (and its children), unless control key is pressed
             # Skip camera (and its children), unless control key is pressed
             skipFlags |= SKIP_CAMERA * (1 - base.getControl())
             skipFlags |= SKIP_CAMERA * (1 - base.getControl())
-            nodePath, hitPt, hitPtDist = direct.iRay.pickGeom(
-                skipFlags = skipFlags)
-            if nodePath:
+            entry = direct.iRay.pickGeom(skipFlags = skipFlags)
+            if entry:
                 # Record hit point information
                 # Record hit point information
-                self.hitPt.assign(hitPt)
-                self.hitPtDist = hitPtDist
+                self.hitPt.assign(entry.getFromIntersectionPoint())
+                self.hitPtDist = Vec3(self.hitPt).length()
                 # Select it
                 # Select it
-                direct.select(nodePath, direct.fShift)
+                direct.select(entry.getIntoNodePath(), direct.fShift)
             else:
             else:
                 direct.deselectAll()
                 direct.deselectAll()
         else:
         else:
@@ -484,16 +483,17 @@ class DirectManipulationControl(PandaObject):
     def plantSelectedNodePath(self):
     def plantSelectedNodePath(self):
         """ Move selected object to intersection point of cursor on scene """
         """ Move selected object to intersection point of cursor on scene """
         # Check for intersection
         # Check for intersection
-        nodePath, hitPt, hitPtDist = direct.iRay.pickGeom(
+        entry = direct.iRay.pickGeom(
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA)
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA)
         # MRM: Need to handle moving COA
         # MRM: Need to handle moving COA
-        if (nodePath != None) and (direct.selected.last != None):
+        if (entry != None) and (direct.selected.last != None):
             # Record undo point
             # Record undo point
             direct.pushUndo(direct.selected)
             direct.pushUndo(direct.selected)
             # Record wrt matrix
             # Record wrt matrix
             direct.selected.getWrtAll()
             direct.selected.getWrtAll()
             # Move selected
             # Move selected
-            direct.widget.setPos(direct.camera, hitPt)
+            direct.widget.setPos(
+                direct.camera,entry.getFromIntersectionPoint())
             # Move all the selected objects with widget
             # Move all the selected objects with widget
             # Move the objects with the widget
             # Move the objects with the widget
             direct.selected.moveWrtWidgetAll()
             direct.selected.moveWrtWidgetAll()

+ 85 - 45
direct/src/directtools/DirectSelection.py

@@ -379,13 +379,16 @@ class DirectBoundingBox:
 
 
 
 
 class SelectionQueue(CollisionHandlerQueue):
 class SelectionQueue(CollisionHandlerQueue):
-    def __init__(self, parent):
+    def __init__(self, fromNP = render):
         # Initialize the superclass
         # Initialize the superclass
         CollisionHandlerQueue.__init__(self)
         CollisionHandlerQueue.__init__(self)
-        # Current entry in collision queue
+        # Current index and entry in collision queue
         self.index = -1
         self.index = -1
-        # Create a collision node path attached to the given parent
-        self.collisionNodePath = parent.attachNewNode( CollisionNode("ray") )
+        self.entry = None
+        self.skipFlags = SKIP_NONE
+        # Create a collision node path attached to the given NP
+        self.collisionNodePath = NodePath(CollisionNode("collisionNP"))
+        self.setFromNP(fromNP)
         # Don't pay the penalty of drawing this collision ray
         # Don't pay the penalty of drawing this collision ray
         self.collisionNodePath.hide()
         self.collisionNodePath.hide()
         self.collisionNode = self.collisionNodePath.node()
         self.collisionNode = self.collisionNodePath.node()
@@ -399,6 +402,10 @@ class SelectionQueue(CollisionHandlerQueue):
         self.unpickable = UNPICKABLE
         self.unpickable = UNPICKABLE
         # Derived class must add Collider to complete initialization
         # Derived class must add Collider to complete initialization
 
 
+    def setFromNP(self, fromNP):
+        # Update fromNP
+        self.collisionNodePath.reparentTo(fromNP)
+
     def addCollider(self, collider):
     def addCollider(self, collider):
         # Inherited class must call this function to specify collider object
         # Inherited class must call this function to specify collider object
         # Record collision object
         # Record collision object
@@ -426,12 +433,18 @@ class SelectionQueue(CollisionHandlerQueue):
         if item in self.unpickable:
         if item in self.unpickable:
             self.unpickable.remove(item)
             self.unpickable.remove(item)
 
 
-    def getCurrentEntry(self):
-        if self.index < 0:
-            return None
+    def setCurrentIndex(self, index):
+        if (index < 0) or (index >= self.getNumEntries()):
+            self.index = -1
         else:
         else:
-            return self.getEntry(self.index)
-        
+            self.index = index
+
+    def setCurrentEntry(self, entry):
+        self.entry = entry
+
+    def getCurrentEntry(self):
+        return self.entry
+
     def isEntryBackfacing(self, entry):
     def isEntryBackfacing(self, entry):
         # If dot product of collision point surface normal and
         # If dot product of collision point surface normal and
         # ray from camera to collision point is positive, we are
         # ray from camera to collision point is positive, we are
@@ -440,10 +453,39 @@ class SelectionQueue(CollisionHandlerQueue):
         v.normalize()
         v.normalize()
         return v.dot(entry.getFromSurfaceNormal()) >= 0
         return v.dot(entry.getFromSurfaceNormal()) >= 0
 
 
-class NewSelectionRay(SelectionQueue):
-    def __init__(self,parent):
+    def findCollisionEntry(self, skipFlags = SKIP_NONE, startIndex = 0 ):
+        # Init self.index and self.entry
+        self.setCurrentIndex(-1)
+        self.setCurrentEntry(None)
+        # Pick out the closest object that isn't a widget
+        for i in range(startIndex,self.getNumEntries()):
+            entry = self.getEntry(i)
+            nodePath = entry.getIntoNodePath()
+            if (skipFlags & SKIP_HIDDEN) and nodePath.isHidden():
+                # Skip if hidden node
+                pass
+            elif (skipFlags & SKIP_BACKFACE) and self.isEntryBackfacing(entry):
+                # Skip, if backfacing poly
+                pass
+            elif ((skipFlags & SKIP_CAMERA) and
+                  (camera in nodePath.getAncestry())):
+                # Skip if parented to a camera.
+                pass
+            # Can pick unpickable, use the first visible node
+            elif ((skipFlags & SKIP_UNPICKABLE) and
+                  (nodePath.getName() in self.unpickable)):
+                # Skip if in unpickable list
+                pass
+            else:
+                self.setCurrentIndex(i)
+                self.setCurrentEntry(entry)
+                break
+        return self.getCurrentEntry()
+
+class SelectionRay(SelectionQueue):
+    def __init__(self, fromNP = render):
         # Initialize the superclass
         # Initialize the superclass
-        SelectionQueue.__init__(self, parent)
+        SelectionQueue.__init__(self, fromNP)
         self.addCollider(CollisionRay())
         self.addCollider(CollisionRay())
     
     
     def pick(self, targetNodePath):
     def pick(self, targetNodePath):
@@ -485,37 +527,35 @@ class NewSelectionRay(SelectionQueue):
         # Determine collision entry
         # Determine collision entry
         return self.findCollisionEntry(skipFlags)
         return self.findCollisionEntry(skipFlags)
 
 
-    def findCollisionEntry(self, skipFlags = SKIP_NONE ):
-        # Init self.index
-        self.index = -1
-        # Pick out the closest object that isn't a widget
-        for i in range(0,self.getNumEntries()):
-            entry = self.getEntry(i)
-            nodePath = entry.getIntoNodePath()
-            if (skipFlags & SKIP_HIDDEN) and nodePath.isHidden():
-                # Skip if hidden node
-                pass
-            elif (skipFlags & SKIP_BACKFACE) and self.isEntryBackfacing(entry):
-                # Skip, if backfacing poly
-                pass
-            elif ((skipFlags & SKIP_CAMERA) and
-                  (camera in nodePath.getAncestry())):
-                # Skip if parented to a camera.
-                pass
-            # Can pick unpickable, use the first visible node
-            elif ((skipFlags & SKIP_UNPICKABLE) and
-                  (nodePath.getName() in self.unpickable)):
-                # Skip if in unpickable list
-                pass
-            else:
-                self.index = i
-                break
-        # Did we hit an object?
-        if(self.index >= 0):
-            # Yes! Find hit point in parent's space
-            hitPt = entry.getFromIntersectionPoint()
-            hitPtDist = Vec3(hitPt).length()
-            return (nodePath, hitPt, hitPtDist)
-        else:
-            return (None, ZERO_POINT, 0)
+
+class SelectionSegment(SelectionQueue):
+    # Like a selection ray but with two endpoints instead of an endpoint
+    # and a direction
+    def __init__(self, fromNP = render, numSegments = 1):
+        # Initialize the superclass
+        SelectionQueue.__init__(self, fromNP)
+        self.colliders = []
+        self.numColliders = 0
+        for i in range(numSegments):
+            self.addCollider(CollisionSegment())
+    
+    def addCollider(self, collider):
+        # Record new collision object
+        self.colliders.append(collider)
+        # Add the collider to the collision Node
+        self.collisionNode.addSolid( collider )
+        self.numColliders += 1
+
+    def pickGeom(self, targetNodePath = render, endPointList = [],
+                 skipFlags = SKIP_HIDDEN | SKIP_CAMERA ):
+        self.collideWithGeom()
+        for i in range(min(len(endPointList), self.numColliders)):
+            pointA, pointB = endPointList[i]
+            collider = self.colliders[i]
+            collider.setPointA( pointA )
+            collider.setPointB( pointB )
+        self.ct.traverse( targetNodePath )
+        # self.sortEntries()
+        # Determine collision entry
+        return self.findCollisionEntry(skipFlags)
 
 

+ 1 - 1
direct/src/directtools/DirectSession.py

@@ -784,7 +784,7 @@ class DisplayRegionContext(PandaObject):
         DisplayRegionContext.regionCount += 1
         DisplayRegionContext.regionCount += 1
         self.camLens.setChangeEvent(changeEvent)
         self.camLens.setChangeEvent(changeEvent)
         self.accept(changeEvent, self.camUpdate)
         self.accept(changeEvent, self.camUpdate)
-        self.iRay = NewSelectionRay(self.cam)
+        self.iRay = SelectionRay(self.cam)
         self.nearVec = Vec3(0)
         self.nearVec = Vec3(0)
         self.mouseX = 0.0
         self.mouseX = 0.0
         self.mouseY = 0.0
         self.mouseY = 0.0

+ 3 - 2
direct/src/tkpanels/MopathRecorder.py

@@ -1675,8 +1675,9 @@ class MopathRecorder(AppShell, PandaObject):
 
 
     def followTerrain(self, height = 1.0):
     def followTerrain(self, height = 1.0):
         self.iRay.rayCollisionNodePath.reparentTo(self.nodePath)
         self.iRay.rayCollisionNodePath.reparentTo(self.nodePath)
-        nodePath, hitPt, hitPtDist = self.iRay.pickGeom3D()
-        if nodePath:
+        entry = self.iRay.pickGeom3D()
+        if entry:
+            hitPtDist = Vec3(entry.getFromIntersectionPoint()).length()
             self.nodePath.setZ(self.nodePath, height - hitPtDist)
             self.nodePath.setZ(self.nodePath, height - hitPtDist)
         self.iRay.rayCollisionNodePath.reparentTo(self.recorderNodePath)
         self.iRay.rayCollisionNodePath.reparentTo(self.recorderNodePath)