diff --git a/direct/src/cluster/ClusterConfig.py b/direct/src/cluster/ClusterConfig.py index 03733502b1..30bee5d44a 100644 --- a/direct/src/cluster/ClusterConfig.py +++ b/direct/src/cluster/ClusterConfig.py @@ -25,14 +25,22 @@ ClientConfigs = { 'pos' : Vec3(0), 'hpr' : Vec3(0)} ], - 'two-server' : [{'display name' : 'display0', + 'two-server' : [{'display name' : 'master', 'display mode' : 'client', 'pos' : Vec3(0), 'hpr' : Vec3(30,0,0)}, - {'display name' : 'display1', + {'display name' : 'la', 'pos' : Vec3(0), 'hpr' : Vec3(-30,0,0)} ], + 'parallax-two-server' : [{'display name' : 'master', + 'display mode' : 'client', + 'pos' : Vec3(0), + 'hpr' : Vec3(0)}, + {'display name' : 'la', + 'pos' : Vec3(0), + 'hpr' : Vec3(0)} + ], 'mono-modelcave-pipe0': [{'display name' : 'display0', 'display mode' : 'client', 'pos' : Vec3(0), diff --git a/direct/src/directtools/DirectCameraControl.py b/direct/src/directtools/DirectCameraControl.py index fd329c44e5..53c066ece6 100644 --- a/direct/src/directtools/DirectCameraControl.py +++ b/direct/src/directtools/DirectCameraControl.py @@ -1,6 +1,7 @@ from PandaObject import * from DirectUtil import * from DirectGeometry import * +from DirectGlobals import * import Task CAM_MOVE_DURATION = 1.2 @@ -96,16 +97,19 @@ class DirectCameraControl(PandaObject): # current mouse position # Allow intersection with unpickable objects # And then spawn task to determine mouse mode - node, hitPt, hitPtDist = direct.iRay.pickGeom( - fIntersectUnpickable = 1, - fIgnoreCamera = 1 - base.getControl()) - self.computeCOA(node, hitPt, hitPtDist) + # Don't intersect with hidden or backfacing objects + 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) # Record reference point - self.coaMarkerRef.iPosHprScale(direct.iRay.collisionRef) + self.coaMarkerRef.iPosHprScale(base.cam) # Record entries self.cqEntries = [] - for i in range(direct.iRay.cq.getNumEntries()): - self.cqEntries.append(direct.iRay.cq.getEntry(i)) + for i in range(direct.iRay.getNumEntries()): + self.cqEntries.append(direct.iRay.getEntry(i)) # Show the marker self.coaMarker.show() # Resize it @@ -292,13 +296,10 @@ class DirectCameraControl(PandaObject): entry = self.cqEntries[0] self.cqEntries = self.cqEntries[1:] + self.cqEntries[:1] # Filter out object's under camera - node = entry.getIntoNode() - nodePath = render.findPathTo(node) + nodePath = entry.getIntoNodePath() if direct.camera not in nodePath.getAncestry(): - # Compute hit point - # KEH: use current display region ray - # hitPt = direct.iRay.parentToHitPt(entry) - hitPt = direct.drList.getCurrentDr().iRay.parentToHitPt(entry) + # Compute new hit point + hitPt = entry.getFromIntersectionPoint() # Move coa marker to new point self.updateCoa(hitPt, ref = self.coaMarkerRef) else: @@ -306,7 +307,7 @@ class DirectCameraControl(PandaObject): self.cqEntries = self.cqEntries[:-1] self.pickNextCOA() - def computeCOA(self, node, hitPt, hitPtDist): + def computeCOA(self, nodePath, hitPt, hitPtDist): coa = Point3(0) dr = direct.drList.getCurrentDr() if self.fLockCOA: @@ -315,7 +316,7 @@ class DirectCameraControl(PandaObject): coa.assign(self.coaMarker.getPos(direct.camera)) # Reset hit point count self.nullHitPointCount = 0 - elif node: + elif nodePath: # Got a hit point (hit point is in camera coordinates) # Set center of action coa.assign(hitPt) @@ -639,6 +640,9 @@ class DirectCameraControl(PandaObject): # Ignore events for event in self.actionEvents: self.ignore(event[0]) + # Kill tasks + self.removeManipulateCameraTask() + taskMgr.remove('stickToWidget') base.enableMouse() def removeManipulateCameraTask(self): diff --git a/direct/src/directtools/DirectGlobals.py b/direct/src/directtools/DirectGlobals.py index dce94179cf..bf3962a7aa 100644 --- a/direct/src/directtools/DirectGlobals.py +++ b/direct/src/directtools/DirectGlobals.py @@ -24,3 +24,11 @@ DIRECT_NO_MOD = 0 DIRECT_SHIFT_MOD = 1 DIRECT_CONTROL_MOD = 2 DIRECT_ALT_MOD = 4 + +SKIP_NONE = 0 +SKIP_HIDDEN = 1 +SKIP_BACKFACE = 2 +SKIP_CAMERA = 4 +SKIP_UNPICKABLE = 8 +SKIP_ALL = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE + diff --git a/direct/src/directtools/DirectManipulation.py b/direct/src/directtools/DirectManipulation.py index 2d1efeb85b..5a77ecd07d 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 - node, hitPt, hitPtDist = direct.iRay.pickWidget() + nodePath, hitPt, hitPtDist = direct.iRay.pickWidget() # Did we hit a widget? - if node: + if nodePath: # Yes! self.hitPt.assign(hitPt) self.hitPtDist = hitPtDist # Constraint determined by nodes name - self.constraint = node.getName() + self.constraint = nodePath.getName() else: # Nope, off the widget, no constraint self.constraint = None @@ -86,18 +86,16 @@ class DirectManipulationControl(PandaObject): # depending on flag..... if self.mode == 'select': # Check for object under mouse - if direct.fControl: - node, hitPt, hitPtDist = direct.iRay.pickGeom( - fIgnoreCamera = 0) - else: - node, hitPt, hitPtDist = direct.iRay.pickGeom( - fIgnoreCamera = 1) - if node: + # Don't intersect with hidden or backfacing objects + 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: # Record hit point information self.hitPt.assign(hitPt) self.hitPtDist = hitPtDist - # Find the node path from the node found above - nodePath = render.findPathTo(node) # Select it direct.select(nodePath, direct.fShift) else: @@ -153,6 +151,12 @@ class DirectManipulationControl(PandaObject): # Ignore events for event in self.actionEvents: self.ignore(event[0]) + self.removeManipulateObjectTask() + taskMgr.remove('manipulateObject') + taskMgr.remove('manip-move-wait') + taskMgr.remove('manip-watch-mouse') + taskMgr.remove('highlightWidgetTask') + taskMgr.remove('resizeObjectHandles') def toggleObjectHandlesMode(self): self.fSetCoa = 1 - self.fSetCoa @@ -480,10 +484,10 @@ class DirectManipulationControl(PandaObject): def plantSelectedNodePath(self): """ Move selected object to intersection point of cursor on scene """ # Check for intersection - node, hitPt, hitPtDist = direct.iRay.pickGeom( - fIntersectUnpickable = 1) + nodePath, hitPt, hitPtDist = direct.iRay.pickGeom( + skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA) # MRM: Need to handle moving COA - if (node != None) and (direct.selected.last != None): + if (nodePath != None) and (direct.selected.last != None): # Record undo point direct.pushUndo(direct.selected) # Record wrt matrix @@ -503,9 +507,9 @@ class ObjectHandles(NodePath,PandaObject): # Load up object handles model and assign it to self self.assign(loader.loadModel('models/misc/objectHandles')) - self.node().setName('objectHandles') + self.setName('objectHandles') self.scalingNode = self.getChild(0) - self.scalingNode.node().setName('ohScalingNode') + self.scalingNode.setName('ohScalingNode') self.ohScalingFactor = 1.0 # To avoid recreating a vec every frame self.hitPt = Vec3(0) @@ -847,7 +851,7 @@ class ObjectHandles(NodePath,PandaObject): lines.moveTo(-500,0,0) lines.drawTo(500,0,0) lines.create() - lines.node().setName('x-guide') + lines.setName('x-guide') # Y guide lines lines = LineNodePath(self.guideLines) @@ -856,7 +860,7 @@ class ObjectHandles(NodePath,PandaObject): lines.moveTo(0,-500,0) lines.drawTo(0,500,0) lines.create() - lines.node().setName('y-guide') + lines.setName('y-guide') # Z guide lines lines = LineNodePath(self.guideLines) @@ -865,7 +869,7 @@ class ObjectHandles(NodePath,PandaObject): lines.moveTo(0,0,-500) lines.drawTo(0,0,500) lines.create() - lines.node().setName('z-guide') + lines.setName('z-guide') def getAxisIntersectPt(self, axis): # Calc the xfrom from camera to widget diff --git a/direct/src/directtools/DirectSelection.py b/direct/src/directtools/DirectSelection.py index f2666372a4..cc7417b4f4 100644 --- a/direct/src/directtools/DirectSelection.py +++ b/direct/src/directtools/DirectSelection.py @@ -378,33 +378,45 @@ class DirectBoundingBox: ) -class SelectionRay: +class SelectionQueue(CollisionHandlerQueue): def __init__(self, parent): + # Initialize the superclass + CollisionHandlerQueue.__init__(self) + # Current entry in collision queue + self.index = -1 # Create a collision node path attached to the given parent - self.rayCollisionNodePath = parent.attachNewNode( CollisionNode("ray") ) + self.collisionNodePath = parent.attachNewNode( CollisionNode("ray") ) # Don't pay the penalty of drawing this collision ray - self.rayCollisionNodePath.hide() - self.rayCollisionNode = self.rayCollisionNodePath.node() + self.collisionNodePath.hide() + self.collisionNode = self.collisionNodePath.node() # Intersect with geometry to begin with self.collideWithGeom() - # Create a collision ray - self.ray = CollisionRay() - # Add the ray to the collision Node - self.rayCollisionNode.addSolid( self.ray ) - # Create a queue to hold the collision results - self.cq = CollisionHandlerQueue() - # Number of entries in CollisionHandlerQueue - self.numEntries = 0 - # Current entry in collision queue - self.cqIndex = 0 # And a traverser to do the actual collision tests self.ct = CollisionTraverser() - # Let the traverser know about the queue and the collision node - self.ct.addCollider(self.rayCollisionNode, self.cq ) - # Reference node path (for picking next) - self.collisionRef = direct.group.attachNewNode('collisionRef') + # Let the traverser know about the collision node and the queue + self.ct.addCollider(self.collisionNode, self) # List of objects that can't be selected self.unpickable = UNPICKABLE + # Derived class must add Collider to complete initialization + + def addCollider(self, collider): + # Inherited class must call this function to specify collider object + # Record collision object + self.collider = collider + # Add the collider to the collision Node + self.collisionNode.addSolid( self.collider ) + + def collideWithGeom(self): + self.collisionNode.setIntoCollideMask(BitMask32().allOff()) + self.collisionNode.setFromCollideMask(BitMask32().allOff()) + self.collisionNode.setCollideGeom(1) + + def collideWithWidget(self): + self.collisionNode.setIntoCollideMask(BitMask32().allOff()) + mask = BitMask32() + mask.setWord(0x80000000) + self.collisionNode.setFromCollideMask(mask) + self.collisionNode.setCollideGeom(0) def addUnpickable(self, item): if item not in self.unpickable: @@ -414,168 +426,96 @@ class SelectionRay: if item in self.unpickable: self.unpickable.remove(item) - def pickGeom(self, targetNodePath = render, fIntersectUnpickable = 0, - fIgnoreCamera = 0): - self.collideWithGeom() - self.pick(targetNodePath, - direct.dr.mouseX, - direct.dr.mouseY) - # Init self.cqIndex - self.cqIndex = -1 - # Pick out the closest object that isn't a widget - for i in range(0,self.numEntries): - entry = self.cq.getEntry(i) - node = entry.getIntoNode() - nodePath = targetNodePath.findPathTo(node) - # Don't pick hidden nodes - if nodePath.isHidden(): - pass - if fIgnoreCamera and (direct.camera in nodePath.getAncestry()): - # This avoids things parented to a camera. Good idea? - pass - # Can pick unpickable, use the first visible node - elif fIntersectUnpickable: - self.cqIndex = i - break - # Is it a named node?, If so, see if it has a name - elif issubclass(node.__class__, PandaNode): - name = node.getName() - if name in self.unpickable: - pass - else: - self.cqIndex = i - break - # Not hidden and not one of the widgets, use it - else: - self.cqIndex = i - break - # Did we hit an object? - if(self.cqIndex >= 0): - # Yes! - # Find hit point in parent's space - entry = self.cq.getEntry(self.cqIndex) - hitPt = self.parentToHitPt(entry) - hitPtDist = Vec3(hitPt - ZERO_POINT).length() - return (node, hitPt, hitPtDist) + def getCurrentEntry(self): + if self.index < 0: + return None else: - return (None, ZERO_POINT, 0) + return self.getEntry(self.index) + + def isEntryBackfacing(self, entry): + # If dot product of collision point surface normal and + # ray from camera to collision point is positive, we are + # looking at the backface of the polygon + v = Vec3(entry.getFromIntersectionPoint()) + v.normalize() + return v.dot(entry.getFromSurfaceNormal()) >= 0 - def pickWidget(self, targetNodePath = render): - self.collideWithWidget() - self.pick(targetNodePath, - direct.dr.mouseX, - direct.dr.mouseY) - # Did we hit a widget? - if self.numEntries: - # Yes! - # Entry 0 is the closest hit point if multiple hits - minPt = 0 - # Find hit point in parent's space - entry = self.cq.getEntry(minPt) - hitPt = self.parentToHitPt(entry) - hitPtDist = Vec3(hitPt).length() - # Get the associated collision queue object - entry = self.cq.getEntry(minPt) - # Extract the node - node = entry.getIntoNode() - # Return info - return (node, hitPt, hitPtDist) - else: - return (None, ZERO_POINT, 0) - - def pick(self, targetNodePath, mouseX, mouseY): +class NewSelectionRay(SelectionQueue): + def __init__(self,parent): + # Initialize the superclass + SelectionQueue.__init__(self, parent) + self.addCollider(CollisionRay()) + + def pick(self, targetNodePath): # Determine ray direction based upon the mouse coordinates - # Note! This has to be a cam object (of type LensNode) - self.ray.setFromLens( base.camNode, mouseX, mouseY ) + if direct: + mx = direct.dr.mouseX + my = direct.dr.mouseY + else: + mx = base.mouseWatcherNode.getMouseX() + my = base.mouseWatcherNode.getMouseY() + self.collider.setFromLens( base.camNode, mx, my ) self.ct.traverse( targetNodePath ) - self.numEntries = self.cq.getNumEntries() - self.cq.sortEntries() - # Record cam's current position (used for cycling through - # other hit points) - self.collisionRef.iPosHprScale(base.cam) - return self.numEntries + self.sortEntries() - def getHitPt(self, entry): - if self.cqIndex >= 0: - self.cqIndex = (self.cqIndex + 1) % self.numEntries - entry = self.cq.getEntry(self.cqIndex) - node = entry.getIntoNode() - # Find hit point in parent's space - hitPt = self.parentToHitPt(entry) - hitPtDist = Vec3(hitPt - ZERO_POINT).length() - return (node, hitPt, hitPtDist) - else: - return (None, ZERO_POINT, 0) - - def pickGeom3D(self, targetNodePath = render, origin = Point3(0), - dir = Vec3(0,0,-1), fIntersectUnpickable = 0): + def pickGeom(self, targetNodePath = render, skipFlags = SKIP_ALL ): self.collideWithGeom() - numEntries = self.pick3D(targetNodePath, origin, dir) - # Init self.cqIndex - self.cqIndex = -1 - # Pick out the closest object that isn't a widget - for i in range(0,numEntries): - entry = self.cq.getEntry(i) - node = entry.getIntoNode() - nodePath = targetNodePath.findPathTo(node) - # Don't pick hidden nodes - if nodePath.isHidden(): - pass - # Can pick unpickable, use the first visible node - if fIntersectUnpickable: - self.cqIndex = i - break - # Is it a named node?, If so, see if it has a name - elif issubclass(node.__class__, PandaNode): - name = node.getName() - if name in self.unpickable: - pass - else: - self.cqIndex = i - break - # Not hidden and not one of the widgets, use it - else: - self.cqIndex = i - break - # Did we hit an object? - if(self.cqIndex >= 0): - # Yes! - # Find hit point in parent's space - entry = self.cq.getEntry(self.cqIndex) - hitPt = self.parentToHitPt(entry) - hitPtDist = Vec3(hitPt - ZERO_POINT).length() - return (node, hitPt, hitPtDist) - else: - return (None, ZERO_POINT, 0) + self.pick(targetNodePath) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + def pickWidget(self, targetNodePath = render, skipFlags = SKIP_NONE ): + self.collideWithWidget() + self.pick(targetNodePath) + # Determine collision entry + return self.findCollisionEntry(skipFlags) def pick3D(self, targetNodePath, origin, dir): # Determine ray direction based upon the mouse coordinates - # Note! This has to be a cam object (of type ProjectionNode) - self.ray.setOrigin( origin ) - self.ray.setDirection( dir ) + self.collider.setOrigin( origin ) + self.collider.setDirection( dir ) self.ct.traverse( targetNodePath ) - self.numEntries = self.cq.getNumEntries() - self.cq.sortEntries() - return self.numEntries + self.sortEntries() - def collideWithGeom(self): - self.rayCollisionNode.setIntoCollideMask(BitMask32().allOff()) - self.rayCollisionNode.setFromCollideMask(BitMask32().allOff()) - self.rayCollisionNode.setCollideGeom(1) + def pickGeom3D(self, targetNodePath = render, + origin = Point3(0), dir = Vec3(0,0,-1), + skipFlags = SKIP_HIDDEN | SKIP_CAMERA ): + self.collideWithGeom() + self.pick3D(targetNodePath, origin, dir) + # Determine collision entry + return self.findCollisionEntry(skipFlags) - def collideWithWidget(self): - self.rayCollisionNode.setIntoCollideMask(BitMask32().allOff()) - mask = BitMask32() - mask.setWord(0x80000000) - self.rayCollisionNode.setFromCollideMask(mask) - self.rayCollisionNode.setCollideGeom(0) - - def objectToHitPt(self, index): - return self.cq.getEntry(index).getIntoIntersectionPoint() - - def parentToHitPt(self, entry): - # Get hit point - hitPt = entry.getIntoIntersectionPoint() - # Convert point from object local space to parent's space - return entry.getInvWrtSpace().xformPoint(hitPt) + 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) diff --git a/direct/src/directtools/DirectSession.py b/direct/src/directtools/DirectSession.py index a3ed905037..2f7a9aa7a1 100644 --- a/direct/src/directtools/DirectSession.py +++ b/direct/src/directtools/DirectSession.py @@ -216,6 +216,10 @@ class DirectSession(PandaObject): self.disableKeyEvents() self.disableMouseEvents() self.disableActionEvents() + # Kill tasks + taskMgr.remove('flashNodePath') + taskMgr.remove('hideDirectMessage') + taskMgr.remove('hideDirectMessageLater') # Set flag self.fEnabled = 0 @@ -558,6 +562,9 @@ class DirectSession(PandaObject): def flashDone(self,state): # Return node Path to original state + if state.nodePath.isEmpty(): + # Node path doesn't exist anymore, bail + return if state.doneColor: state.nodePath.setColor(state.doneColor) else: @@ -777,7 +784,7 @@ class DisplayRegionContext(PandaObject): DisplayRegionContext.regionCount += 1 self.camLens.setChangeEvent(changeEvent) self.accept(changeEvent, self.camUpdate) - self.iRay = SelectionRay(self.cam) + self.iRay = NewSelectionRay(self.cam) self.nearVec = Vec3(0) self.mouseX = 0.0 self.mouseY = 0.0 diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index 0475c2fd37..78dd259012 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -388,7 +388,7 @@ class TaskManager: # Alert the world, a new task is born! index = len(self.doLaterList) messenger.send('TaskManager-spawnDoLater', - sentArgs = [task, task.name, index]) + sentArgs = [task, task.name, task.id]) return task def doLater(self, delayTime, task, taskName): diff --git a/direct/src/tkpanels/MopathRecorder.py b/direct/src/tkpanels/MopathRecorder.py index 7773889049..3ef128a9c3 100644 --- a/direct/src/tkpanels/MopathRecorder.py +++ b/direct/src/tkpanels/MopathRecorder.py @@ -1675,8 +1675,8 @@ class MopathRecorder(AppShell, PandaObject): def followTerrain(self, height = 1.0): self.iRay.rayCollisionNodePath.reparentTo(self.nodePath) - node, hitPt, hitPtDist = self.iRay.pickGeom3D() - if node: + nodePath, hitPt, hitPtDist = self.iRay.pickGeom3D() + if nodePath: self.nodePath.setZ(self.nodePath, height - hitPtDist) self.iRay.rayCollisionNodePath.reparentTo(self.recorderNodePath) diff --git a/direct/src/tkpanels/NotifyPanel.py b/direct/src/tkpanels/NotifyPanel.py index eb4bf0454e..8cab6f162e 100644 --- a/direct/src/tkpanels/NotifyPanel.py +++ b/direct/src/tkpanels/NotifyPanel.py @@ -58,12 +58,7 @@ class NotifyPanel: # Severity frame Label(severityFrame, text = 'Severity:', font=('MSSansSerif', 10, 'bold'), -<<<<<<< NotifyPanel.py - justify = 'right', anchor = 'w').pack(fill = X, padx = 5) -======= - justify = RIGHT, anchor = W).pack( - fill = X, padx = 5) ->>>>>>> 1.2 + justify = RIGHT, anchor = W).pack(fill = X, padx = 5) self.severity = IntVar() self.severity.set(0) self.fatalSeverity = Radiobutton(severityFrame, text = 'Fatal',