diff --git a/direct/src/directtools/DirectCameraControl.py b/direct/src/directtools/DirectCameraControl.py index 53c066ece6..a474a7099c 100644 --- a/direct/src/directtools/DirectCameraControl.py +++ b/direct/src/directtools/DirectCameraControl.py @@ -101,9 +101,7 @@ class DirectCameraControl(PandaObject): skipFlags = SKIP_HIDDEN | SKIP_BACKFACE # Skip camera (and its children), unless control key is pressed 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 self.coaMarkerRef.iPosHprScale(base.cam) # Record entries @@ -307,7 +305,7 @@ class DirectCameraControl(PandaObject): self.cqEntries = self.cqEntries[:-1] self.pickNextCOA() - def computeCOA(self, nodePath, hitPt, hitPtDist): + def computeCOA(self, entry): coa = Point3(0) dr = direct.drList.getCurrentDr() if self.fLockCOA: @@ -316,9 +314,11 @@ class DirectCameraControl(PandaObject): coa.assign(self.coaMarker.getPos(direct.camera)) # Reset hit point count self.nullHitPointCount = 0 - elif nodePath: + elif entry: # Got a hit point (hit point is in camera coordinates) # Set center of action + hitPt = entry.getFromIntersectionPoint() + hitPtDist = Vec3(hitPt).length() coa.assign(hitPt) # Handle case of bad coa point (too close or too far) if ((hitPtDist < (1.1 * dr.near)) or diff --git a/direct/src/directtools/DirectManipulation.py b/direct/src/directtools/DirectManipulation.py index 5a77ecd07d..aa7c917349 100644 --- a/direct/src/directtools/DirectManipulation.py +++ b/direct/src/directtools/DirectManipulation.py @@ -40,14 +40,14 @@ class DirectManipulationControl(PandaObject): # Start out in select mode self.mode = 'select' # Check for a widget hit point - nodePath, hitPt, hitPtDist = direct.iRay.pickWidget() + entry = direct.iRay.pickWidget() # Did we hit a widget? - if nodePath: + if entry: # Yes! - self.hitPt.assign(hitPt) - self.hitPtDist = hitPtDist + self.hitPt.assign(entry.getFromIntersectionPoint()) + self.hitPtDist = Vec3(self.hitPt).length() # Constraint determined by nodes name - self.constraint = nodePath.getName() + self.constraint = entry.getIntoNodePath().getName() else: # Nope, off the widget, no constraint self.constraint = None @@ -90,14 +90,13 @@ class DirectManipulationControl(PandaObject): skipFlags = SKIP_HIDDEN | SKIP_BACKFACE # Skip camera (and its children), unless control key is pressed 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 - self.hitPt.assign(hitPt) - self.hitPtDist = hitPtDist + self.hitPt.assign(entry.getFromIntersectionPoint()) + self.hitPtDist = Vec3(self.hitPt).length() # Select it - direct.select(nodePath, direct.fShift) + direct.select(entry.getIntoNodePath(), direct.fShift) else: direct.deselectAll() else: @@ -484,16 +483,17 @@ class DirectManipulationControl(PandaObject): def plantSelectedNodePath(self): """ Move selected object to intersection point of cursor on scene """ # Check for intersection - nodePath, hitPt, hitPtDist = direct.iRay.pickGeom( + entry = direct.iRay.pickGeom( skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA) # 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 direct.pushUndo(direct.selected) # Record wrt matrix direct.selected.getWrtAll() # Move selected - direct.widget.setPos(direct.camera, hitPt) + direct.widget.setPos( + direct.camera,entry.getFromIntersectionPoint()) # Move all the selected objects with widget # Move the objects with the widget direct.selected.moveWrtWidgetAll() diff --git a/direct/src/directtools/DirectSelection.py b/direct/src/directtools/DirectSelection.py index cc7417b4f4..65ced5b3ec 100644 --- a/direct/src/directtools/DirectSelection.py +++ b/direct/src/directtools/DirectSelection.py @@ -379,13 +379,16 @@ class DirectBoundingBox: class SelectionQueue(CollisionHandlerQueue): - def __init__(self, parent): + def __init__(self, fromNP = render): # Initialize the superclass CollisionHandlerQueue.__init__(self) - # Current entry in collision queue + # Current index and entry in collision queue 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 self.collisionNodePath.hide() self.collisionNode = self.collisionNodePath.node() @@ -399,6 +402,10 @@ class SelectionQueue(CollisionHandlerQueue): self.unpickable = UNPICKABLE # Derived class must add Collider to complete initialization + def setFromNP(self, fromNP): + # Update fromNP + self.collisionNodePath.reparentTo(fromNP) + def addCollider(self, collider): # Inherited class must call this function to specify collider object # Record collision object @@ -426,12 +433,18 @@ class SelectionQueue(CollisionHandlerQueue): if item in self.unpickable: 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: - 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): # If dot product of collision point surface normal and # ray from camera to collision point is positive, we are @@ -440,10 +453,39 @@ class SelectionQueue(CollisionHandlerQueue): v.normalize() 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 - SelectionQueue.__init__(self, parent) + SelectionQueue.__init__(self, fromNP) self.addCollider(CollisionRay()) def pick(self, targetNodePath): @@ -485,37 +527,35 @@ class NewSelectionRay(SelectionQueue): # Determine collision entry 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) diff --git a/direct/src/directtools/DirectSession.py b/direct/src/directtools/DirectSession.py index 2f7a9aa7a1..5342f31f4f 100644 --- a/direct/src/directtools/DirectSession.py +++ b/direct/src/directtools/DirectSession.py @@ -784,7 +784,7 @@ class DisplayRegionContext(PandaObject): DisplayRegionContext.regionCount += 1 self.camLens.setChangeEvent(changeEvent) self.accept(changeEvent, self.camUpdate) - self.iRay = NewSelectionRay(self.cam) + self.iRay = SelectionRay(self.cam) self.nearVec = Vec3(0) self.mouseX = 0.0 self.mouseY = 0.0 diff --git a/direct/src/tkpanels/MopathRecorder.py b/direct/src/tkpanels/MopathRecorder.py index 3ef128a9c3..1547b92bc0 100644 --- a/direct/src/tkpanels/MopathRecorder.py +++ b/direct/src/tkpanels/MopathRecorder.py @@ -1675,8 +1675,9 @@ class MopathRecorder(AppShell, PandaObject): def followTerrain(self, height = 1.0): 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.iRay.rayCollisionNodePath.reparentTo(self.recorderNodePath)