from direct.showbase.DirectObject import DirectObject from DirectUtil import * from DirectGeometry import * from DirectGlobals import * from DirectSelection import SelectionRay from direct.interval.IntervalGlobal import Sequence, Func from direct.directnotify import DirectNotifyGlobal from direct.task import Task CAM_MOVE_DURATION = 1.2 COA_MARKER_SF = 0.0075 Y_AXIS = Vec3(0, 1, 0) class DirectCameraControl(DirectObject): notify = DirectNotifyGlobal.directNotify.newCategory('DirectCameraControl') def __init__(self): # Create the grid self.startT = 0.0 self.startF = 0 self.orthoViewRoll = 0.0 self.lastView = 0 self.coa = Point3(0, 100, 0) self.coaMarker = loader.loadModel('models/misc/sphere') self.coaMarker.setName('DirectCameraCOAMarker') self.coaMarker.setTransparency(1) self.coaMarker.setColor(1, 0, 0, 0) self.coaMarker.setPos(0, 100, 0) useDirectRenderStyle(self.coaMarker) self.coaMarkerPos = Point3(0) self.coaMarkerColorIval = None self.fLockCOA = 0 self.nullHitPointCount = 0 self.cqEntries = [] self.coaMarkerRef = base.direct.group.attachNewNode('coaMarkerRef') self.camManipRef = base.direct.group.attachNewNode('camManipRef') self.switchDirBelowZero = True self.manipulateCameraTask = None self.manipulateCameraInterval = None t = CAM_MOVE_DURATION self.actionEvents = [ ['DIRECT-mouse1', self.mouseRotateStart], ['DIRECT-mouse1Up', self.mouseDollyStop], ['DIRECT-mouse2', self.mouseFlyStart], ['DIRECT-mouse2Up', self.mouseFlyStop], ['DIRECT-mouse3', self.mouseDollyStart], ['DIRECT-mouse3Up', self.mouseDollyStop], ] # [gjeon] moved all of the hotkeys to single place for easy remapping ## self.keyEvents = [ ## ['c', self.centerCamIn, 0.5], ## ['f', self.fitOnWidget], # Note: This function doesn't work as intended ## ['h', self.homeCam], ## ['shift-v', self.toggleMarkerVis], ## ['m', self.moveToFit], # Note: This function doesn't work as intended; the object dissappears and screen flashes ## ['n', self.pickNextCOA], ## ['u', self.orbitUprightCam], ## ['shift-u', self.uprightCam], ## [repr(1), self.spawnMoveToView, 1], ## [repr(2), self.spawnMoveToView, 2], ## [repr(3), self.spawnMoveToView, 3], ## [repr(4), self.spawnMoveToView, 4], ## [repr(5), self.spawnMoveToView, 5], ## [repr(6), self.spawnMoveToView, 6], ## [repr(7), self.spawnMoveToView, 7], ## [repr(8), self.spawnMoveToView, 8], ## ['9', self.swingCamAboutWidget, -90.0, t], ## ['0', self.swingCamAboutWidget, 90.0, t], ## ['`', self.removeManipulateCameraTask], ## ['=', self.zoomCam, 0.5, t], ## ['+', self.zoomCam, 0.5, t], ## ['-', self.zoomCam, -2.0, t], ## ['_', self.zoomCam, -2.0, t], ## ] self.keyEvents = [ ['DIRECT-centerCamIn', self.centerCamIn, 0.5], ['DIRECT-fitOnWidget', self.fitOnWidget], # Note: This function doesn't work as intended ['DIRECT-homeCam', self.homeCam], ['DIRECT-toggleMarkerVis', self.toggleMarkerVis], ['DIRECT-moveToFit', self.moveToFit], # Note: This function doesn't work as intended; the object dissappears and screen flashes ['DIRECT-pickNextCOA', self.pickNextCOA], ['DIRECT-orbitUprightCam', self.orbitUprightCam], ['DIRECT-uprightCam', self.uprightCam], ['DIRECT-spwanMoveToView-1', self.spawnMoveToView, 1], ['DIRECT-spwanMoveToView-2', self.spawnMoveToView, 2], ['DIRECT-spwanMoveToView-3', self.spawnMoveToView, 3], ['DIRECT-spwanMoveToView-4', self.spawnMoveToView, 4], ['DIRECT-spwanMoveToView-5', self.spawnMoveToView, 5], ['DIRECT-spwanMoveToView-6', self.spawnMoveToView, 6], ['DIRECT-spwanMoveToView-7', self.spawnMoveToView, 7], ['DIRECT-spwanMoveToView-8', self.spawnMoveToView, 8], ['DIRECT-swingCamAboutWidget-0', self.swingCamAboutWidget, -90.0, t], ['DIRECT-swingCamAboutWidget-1', self.swingCamAboutWidget, 90.0, t], ['DIRECT-removeManipulateCameraTask', self.removeManipulateCameraTask], ['DIRECT-zoomInCam', self.zoomCam, 0.5, t], ['DIRECT-zoomOutCam', self.zoomCam, -2.0, t], ] # set this to true to prevent the camera from rolling self.lockRoll = False # NIK - flag to determine whether to use maya camera controls self.useMayaCamControls = 0 self.altDown = 0 self.perspCollPlane = None # [gjeon] used for new LE self.perspCollPlane2 = None # [gjeon] used for new LE def toggleMarkerVis(self): ## if base.direct.cameraControl.coaMarker.isHidden(): ## base.direct.cameraControl.coaMarker.show() ## else: ## base.direct.cameraControl.coaMarker.hide() if self.coaMarker.isHidden(): self.coaMarker.show() else: self.coaMarker.hide() def mouseRotateStart(self, modifiers): if self.useMayaCamControls and modifiers == 4: # alt is pressed - use maya controls # base.direct.pushUndo([base.direct.camera]) # Wasteful use of undo self.spawnMouseRotateTask() def mouseDollyStart(self, modifiers): if self.useMayaCamControls and modifiers == 4: # alt is pressed - use maya controls # Hide the marker for this kind of motion self.coaMarker.hide() # Record time of start of mouse interaction self.startT= globalClock.getFrameTime() self.startF = globalClock.getFrameCount() # If the cam is orthogonal, spawn differentTask if hasattr(base.direct, "manipulationControl") and base.direct.manipulationControl.fMultiView and\ base.direct.camera.getName() != 'persp': self.spawnOrthoZoom() else: # Start manipulation self.spawnHPanYZoom() def __stopManipulateCamera(self): if self.manipulateCameraTask: taskMgr.remove(self.manipulateCameraTask) self.manipulateCameraTask = None if self.manipulateCameraInterval: self.manipulateCameraInterval.finish() self.manipulateCameraInterval = None def __startManipulateCamera(self, func = None, task = None, ival = None): self.__stopManipulateCamera() if func: assert(task is None) task = Task.Task(func) if task: self.manipulateCameraTask = taskMgr.add(task, 'manipulateCamera') if ival: ival.start() self.manipulateCameraInterval = ival def mouseDollyStop(self): self.__stopManipulateCamera() def mouseFlyStart(self, modifiers): # Record undo point # base.direct.pushUndo([base.direct.camera]) # Wasteful use of undo if self.useMayaCamControls and modifiers == 4: # alt is down, use maya controls # Hide the marker for this kind of motion self.coaMarker.hide() # Record time of start of mouse interaction self.startT= globalClock.getFrameTime() self.startF = globalClock.getFrameCount() # Start manipulation # If the cam is orthogonal, spawn differentTask if hasattr(base.direct, "manipulationControl") and base.direct.manipulationControl.fMultiView and\ base.direct.camera.getName() != 'persp': self.spawnOrthoTranslate() else: self.spawnXZTranslate() self.altDown = 1 elif not self.useMayaCamControls: # Where are we in the display region? if ((abs(base.direct.dr.mouseX) < 0.9) and (abs(base.direct.dr.mouseY) < 0.9)): # MOUSE IS IN CENTRAL REGION # Hide the marker for this kind of motion self.coaMarker.hide() # Record time of start of mouse interaction self.startT= globalClock.getFrameTime() self.startF = globalClock.getFrameCount() # Start manipulation self.spawnXZTranslateOrHPanYZoom() # END MOUSE IN CENTRAL REGION else: if ((abs(base.direct.dr.mouseX) > 0.9) and (abs(base.direct.dr.mouseY) > 0.9)): # Mouse is in corners, spawn roll task self.spawnMouseRollTask() else: # Mouse is in outer frame, spawn mouseRotateTask self.spawnMouseRotateTask() if not modifiers == 4: self.altDown = 0 def mouseFlyStop(self): self.__stopManipulateCamera() stopT = globalClock.getFrameTime() deltaT = stopT - self.startT stopF = globalClock.getFrameCount() deltaF = stopF - self.startF ## No reason this shouldn't work with Maya cam on # if not self.useMayaCamControls and (deltaT <= 0.25) or (deltaF <= 1): # Do this when not trying to manipulate camera if not self.altDown and len(base.direct.selected.getSelectedAsList()) == 0: # Check for a hit point based on # current mouse position # Allow intersection with unpickable objects # And then spawn task to determine mouse mode # 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()) self.computeCOA(base.direct.iRay.pickGeom(skipFlags = skipFlags)) # Record reference point self.coaMarkerRef.iPosHprScale(base.cam) # Record entries self.cqEntries = [] for i in range(base.direct.iRay.getNumEntries()): self.cqEntries.append(base.direct.iRay.getEntry(i)) # Show the marker self.coaMarker.show() # Resize it self.updateCoaMarkerSize() def mouseFlyStartTopWin(self): print "Moving mouse 2 in new window" #altIsDown = base.getAlt() #if altIsDown: # print "Alt is down" def mouseFlyStopTopWin(self): print "Stopping mouse 2 in new window" def spawnXZTranslateOrHPanYZoom(self): # Kill any existing tasks self.__stopManipulateCamera() # Spawn the new task t = Task.Task(self.XZTranslateOrHPanYZoomTask) # For HPanYZoom t.zoomSF = Vec3(self.coaMarker.getPos(base.direct.camera)).length() self.__startManipulateCamera(task = t) def spawnXZTranslateOrHPPan(self): # Kill any existing tasks self.__stopManipulateCamera() # Spawn new task self.__startManipulateCamera(func = self.XZTranslateOrHPPanTask) def spawnXZTranslate(self): # Kill any existing tasks self.__stopManipulateCamera() # Spawn new task self.__startManipulateCamera(func = self.XZTranslateTask) def spawnOrthoTranslate(self): # Kill any existing tasks self.__stopManipulateCamera() # Spawn new task self.__startManipulateCamera(func = self.OrthoTranslateTask) def spawnHPanYZoom(self): # Kill any existing tasks self.__stopManipulateCamera() # Spawn new task t = Task.Task(self.HPanYZoomTask) t.zoomSF = Vec3(self.coaMarker.getPos(base.direct.camera)).length() self.__startManipulateCamera(task = t) def spawnOrthoZoom(self): # Kill any existing tasks self.__stopManipulateCamera() # Spawn new task t = Task.Task(self.OrthoZoomTask) self.__startManipulateCamera(task = t) def spawnHPPan(self): # Kill any existing tasks self.__stopManipulateCamera() # Spawn new task self.__startManipulateCamera(func = self.HPPanTask) def XZTranslateOrHPanYZoomTask(self, state): if base.direct.fShift: return self.XZTranslateTask(state) else: return self.HPanYZoomTask(state) def XZTranslateOrHPPanTask(self, state): if base.direct.fShift: # Panning action return self.HPPanTask(state) else: # Translation action return self.XZTranslateTask(state) def XZTranslateTask(self, state): coaDist = Vec3(self.coaMarker.getPos(base.direct.camera)).length() xlateSF = (coaDist / base.direct.dr.near) base.direct.camera.setPos(base.direct.camera, (-0.5 * base.direct.dr.mouseDeltaX * base.direct.dr.nearWidth * xlateSF), 0.0, (-0.5 * base.direct.dr.mouseDeltaY * base.direct.dr.nearHeight * xlateSF)) return Task.cont def OrthoTranslateTask(self, state): # create ray from the camera to detect 3d position iRay = SelectionRay(base.direct.camera) iRay.collider.setFromLens(base.direct.camNode, base.direct.dr.mouseX, base.direct.dr.mouseY) #iRay.collideWithBitMask(1) iRay.collideWithBitMask(BitMask32.bit(21)) iRay.ct.traverse(base.direct.grid) entry = iRay.getEntry(0) hitPt = entry.getSurfacePoint(entry.getFromNodePath()) iRay.collisionNodePath.removeNode() del iRay if hasattr(state, 'prevPt'): base.direct.camera.setPos(base.direct.camera, (state.prevPt - hitPt)) state.prevPt = hitPt return Task.cont def HPanYZoomTask(self, state): # If the cam is orthogonal, don't rotate or zoom. if (hasattr(base.direct.cam.node(), "getLens") and base.direct.cam.node().getLens().__class__.__name__ == "OrthographicLens"): return if base.direct.fControl: moveDir = Vec3(self.coaMarker.getPos(base.direct.camera)) # If marker is behind camera invert vector if moveDir[1] < 0.0: moveDir.assign(moveDir * -1) moveDir.normalize() else: moveDir = Vec3(Y_AXIS) if self.useMayaCamControls : # use maya controls moveDir.assign(moveDir * ((base.direct.dr.mouseDeltaX -1.0 * base.direct.dr.mouseDeltaY) * state.zoomSF)) hVal = 0.0 else: moveDir.assign(moveDir * (-1.0 * base.direct.dr.mouseDeltaY * state.zoomSF)) if base.direct.dr.mouseDeltaY > 0.0: moveDir.setY(moveDir[1] * 1.0) hVal = 0.5 * base.direct.dr.mouseDeltaX * base.direct.dr.fovH base.direct.camera.setPosHpr(base.direct.camera, moveDir[0], moveDir[1], moveDir[2], hVal, 0.0, 0.0) if (self.lockRoll == True): # flatten roll base.direct.camera.setR(0) return Task.cont def OrthoZoomTask(self, state): filmSize = base.direct.camNode.getLens().getFilmSize() factor = (base.direct.dr.mouseDeltaX -1.0 * base.direct.dr.mouseDeltaY) * 0.1 x = base.direct.dr.getWidth() y = base.direct.dr.getHeight() base.direct.dr.orthoFactor -= factor if base.direct.dr.orthoFactor < 0: base.direct.dr.orthoFactor = 0.0001 base.direct.dr.updateFilmSize(x, y) return Task.cont def HPPanTask(self, state): base.direct.camera.setHpr(base.direct.camera, (0.5 * base.direct.dr.mouseDeltaX * base.direct.dr.fovH), (-0.5 * base.direct.dr.mouseDeltaY * base.direct.dr.fovV), 0.0) return Task.cont def spawnMouseRotateTask(self): # Kill any existing tasks self.__stopManipulateCamera() if self.perspCollPlane: iRay = SelectionRay(base.direct.camera) iRay.collider.setFromLens(base.direct.camNode, 0.0, 0.0) iRay.collideWithBitMask(1) if base.direct.camera.getPos().getZ() >=0: iRay.ct.traverse(self.perspCollPlane) else: iRay.ct.traverse(self.perspCollPlane2) if iRay.getNumEntries() > 0: entry = iRay.getEntry(0) hitPt = entry.getSurfacePoint(entry.getFromNodePath()) # create a temp nodePath to get the position np = NodePath('temp') np.setPos(base.direct.camera, hitPt) self.coaMarkerPos = np.getPos() np.remove() self.coaMarker.setPos(self.coaMarkerPos) iRay.collisionNodePath.removeNode() del iRay # Set at markers position in render coordinates self.camManipRef.setPos(self.coaMarkerPos) self.camManipRef.setHpr(base.direct.camera, ZERO_POINT) t = Task.Task(self.mouseRotateTask) if abs(base.direct.dr.mouseX) > 0.9: t.constrainedDir = 'y' else: t.constrainedDir = 'x' self.__startManipulateCamera(task = t) def mouseRotateTask(self, state): # If the cam is orthogonal, don't rotate. if (hasattr(base.direct.cam.node(), "getLens") and base.direct.cam.node().getLens().__class__.__name__ == "OrthographicLens"): return # If moving outside of center, ignore motion perpendicular to edge if ((state.constrainedDir == 'y') and (abs(base.direct.dr.mouseX) > 0.9)): deltaX = 0 deltaY = base.direct.dr.mouseDeltaY elif ((state.constrainedDir == 'x') and (abs(base.direct.dr.mouseY) > 0.9)): deltaX = base.direct.dr.mouseDeltaX deltaY = 0 else: deltaX = base.direct.dr.mouseDeltaX deltaY = base.direct.dr.mouseDeltaY if base.direct.fShift: base.direct.camera.setHpr(base.direct.camera, (deltaX * base.direct.dr.fovH), (-deltaY * base.direct.dr.fovV), 0.0) if (self.lockRoll == True): # flatten roll base.direct.camera.setR(0) self.camManipRef.setPos(self.coaMarkerPos) self.camManipRef.setHpr(base.direct.camera, ZERO_POINT) else: if base.direct.camera.getPos().getZ() >=0 or not self.switchDirBelowZero: dirX = -1 else: dirX = 1 wrt = base.direct.camera.getTransform(self.camManipRef) self.camManipRef.setHpr(self.camManipRef, (dirX * deltaX * 180.0), (deltaY * 180.0), 0.0) if (self.lockRoll == True): # flatten roll self.camManipRef.setR(0) base.direct.camera.setTransform(self.camManipRef, wrt) return Task.cont def spawnMouseRollTask(self): # Kill any existing tasks self.__stopManipulateCamera() # Set at markers position in render coordinates self.camManipRef.setPos(self.coaMarkerPos) self.camManipRef.setHpr(base.direct.camera, ZERO_POINT) t = Task.Task(self.mouseRollTask) t.coaCenter = getScreenXY(self.coaMarker) t.lastAngle = getCrankAngle(t.coaCenter) # Store the camera/manipRef offset transform t.wrt = base.direct.camera.getTransform(self.camManipRef) self.__startManipulateCamera(task = t) def mouseRollTask(self, state): wrt = state.wrt angle = getCrankAngle(state.coaCenter) deltaAngle = angle - state.lastAngle state.lastAngle = angle self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle) if (self.lockRoll == True): # flatten roll self.camManipRef.setR(0) base.direct.camera.setTransform(self.camManipRef, wrt) return Task.cont def lockCOA(self): self.fLockCOA = 1 base.direct.message('COA Lock On') def unlockCOA(self): self.fLockCOA = 0 base.direct.message('COA Lock Off') def toggleCOALock(self): self.fLockCOA = 1 - self.fLockCOA if self.fLockCOA: base.direct.message('COA Lock On') else: base.direct.message('COA Lock Off') def pickNextCOA(self): """ Cycle through collision handler entries """ if self.cqEntries: # Get next entry and rotate entries entry = self.cqEntries[0] self.cqEntries = self.cqEntries[1:] + self.cqEntries[:1] # Filter out object's under camera nodePath = entry.getIntoNodePath() if base.direct.camera not in nodePath.getAncestors(): # Compute new hit point hitPt = entry.getSurfacePoint(entry.getFromNodePath()) # Move coa marker to new point self.updateCoa(hitPt, ref = self.coaMarkerRef) else: # Remove offending entry self.cqEntries = self.cqEntries[:-1] self.pickNextCOA() def computeCOA(self, entry): coa = Point3(0) dr = base.direct.drList.getCurrentDr() if self.fLockCOA: # COA is locked, use existing point # Use existing point coa.assign(self.coaMarker.getPos(base.direct.camera)) # Reset hit point count self.nullHitPointCount = 0 elif entry: # Got a hit point (hit point is in camera coordinates) # Set center of action hitPt = entry.getSurfacePoint(entry.getFromNodePath()) 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 (hitPtDist > dr.far)): # Just use existing point coa.assign(self.coaMarker.getPos(base.direct.camera)) # Reset hit point count self.nullHitPointCount = 0 else: # Increment null hit point count self.nullHitPointCount = (self.nullHitPointCount + 1) % 7 # No COA lock and no intersection point # Use a point out in front of camera # Distance to point increases on multiple null hit points # MRM: Would be nice to be able to control this # At least display it dist = pow(10.0, self.nullHitPointCount) base.direct.message('COA Distance: ' + repr(dist)) coa.set(0, dist, 0) # Compute COA Dist coaDist = Vec3(coa - ZERO_POINT).length() if coaDist < (1.1 * dr.near): coa.set(0, 100, 0) coaDist = 100 # Update coa and marker self.updateCoa(coa, coaDist = coaDist) def updateCoa(self, ref2point, coaDist = None, ref = None): self.coa.set(ref2point[0], ref2point[1], ref2point[2]) if not coaDist: coaDist = Vec3(self.coa - ZERO_POINT).length() # Place the marker in render space if ref == None: # KEH: use the current display region # ref = base.cam ref = base.direct.drList.getCurrentDr().cam self.coaMarker.setPos(ref, self.coa) pos = self.coaMarker.getPos() self.coaMarker.setPosHprScale(pos, Vec3(0), Vec3(1)) # Resize it self.updateCoaMarkerSize(coaDist) # Record marker pos in render space self.coaMarkerPos.assign(self.coaMarker.getPos()) def updateCoaMarkerSizeOnDeath(self): # Needed because tasks pass in state as first arg self.updateCoaMarkerSize() def updateCoaMarkerSize(self, coaDist = None): if not coaDist: coaDist = Vec3(self.coaMarker.getPos(base.direct.camera)).length() # Nominal size based on default 30 degree vertical FOV # Need to adjust size based on distance and current FOV sf = COA_MARKER_SF * coaDist * (base.direct.drList.getCurrentDr().fovV/30.0) if sf == 0.0: sf = 0.1 self.coaMarker.setScale(sf) # Lerp color to fade out if self.coaMarkerColorIval: self.coaMarkerColorIval.finish() self.coaMarkerColorIval = Sequence( Func(self.coaMarker.unstash), self.coaMarker.colorInterval(1.5, Vec4(1, 0, 0, 0), startColor = Vec4(1, 0, 0, 1), blendType = 'easeInOut'), Func(self.coaMarker.stash) ) self.coaMarkerColorIval.start() def homeCam(self): # Record undo point base.direct.pushUndo([base.direct.camera]) base.direct.camera.reparentTo(render) base.direct.camera.clearMat() # Resize coa marker self.updateCoaMarkerSize() def uprightCam(self): self.__stopManipulateCamera() # Record undo point base.direct.pushUndo([base.direct.camera]) # Pitch camera till upright currH = base.direct.camera.getH() ival = base.direct.camera.hprInterval(CAM_MOVE_DURATION, (currH, 0, 0), other = render, blendType = 'easeInOut', name = 'manipulateCamera') self.__startManipulateCamera(ival = ival) def orbitUprightCam(self): self.__stopManipulateCamera() # Record undo point base.direct.pushUndo([base.direct.camera]) # Transform camera z axis to render space mCam2Render = Mat4(Mat4.identMat()) # [gjeon] fixed to give required argument mCam2Render.assign(base.direct.camera.getMat(render)) zAxis = Vec3(mCam2Render.xformVec(Z_AXIS)) zAxis.normalize() # Compute rotation angle needed to upright cam orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(Z_AXIS), -1, 1))) # Check angle if orbitAngle < 0.1: # Already upright return # Compute orthogonal axis of rotation rotAxis = Vec3(zAxis.cross(Z_AXIS)) rotAxis.normalize() # Find angle between rot Axis and render X_AXIS rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(X_AXIS), -1, 1))) # Determine sign or rotation angle if rotAxis[1] < 0: rotAngle *= -1 # Position ref CS at coa marker with xaxis aligned with rot axis self.camManipRef.setPos(self.coaMarker, Vec3(0)) self.camManipRef.setHpr(render, rotAngle, 0, 0) # Reparent Cam to ref Coordinate system parent = base.direct.camera.getParent() base.direct.camera.wrtReparentTo(self.camManipRef) # Rotate ref CS to final orientation ival = self.camManipRef.hprInterval(CAM_MOVE_DURATION, (rotAngle, orbitAngle, 0), other = render, blendType = 'easeInOut') ival = Sequence(ival, Func(self.reparentCam, parent), name = 'manipulateCamera') self.__startManipulateCamera(ival = ival) def centerCam(self): self.centerCamIn(1.0) def centerCamNow(self): self.centerCamIn(0.) def centerCamIn(self, t): self.__stopManipulateCamera() # Record undo point base.direct.pushUndo([base.direct.camera]) # Determine marker location markerToCam = self.coaMarker.getPos(base.direct.camera) dist = Vec3(markerToCam - ZERO_POINT).length() scaledCenterVec = Y_AXIS * dist delta = markerToCam - scaledCenterVec self.camManipRef.setPosHpr(base.direct.camera, Point3(0), Point3(0)) ival = base.direct.camera.posInterval(CAM_MOVE_DURATION, Point3(delta), other = self.camManipRef, blendType = 'easeInOut') ival = Sequence(ival, Func(self.updateCoaMarkerSizeOnDeath), name = 'manipulateCamera') self.__startManipulateCamera(ival = ival) def zoomCam(self, zoomFactor, t): self.__stopManipulateCamera() # Record undo point base.direct.pushUndo([base.direct.camera]) # Find a point zoom factor times the current separation # of the widget and cam zoomPtToCam = self.coaMarker.getPos(base.direct.camera) * zoomFactor # Put a target nodePath there self.camManipRef.setPos(base.direct.camera, zoomPtToCam) # Move to that point ival = base.direct.camera.posInterval(CAM_MOVE_DURATION, ZERO_POINT, other = self.camManipRef, blendType = 'easeInOut') ival = Sequence(ival, Func(self.updateCoaMarkerSizeOnDeath), name = 'manipulateCamera') self.__startManipulateCamera(ival = ival) def spawnMoveToView(self, view): # Kill any existing tasks self.__stopManipulateCamera() # Record undo point base.direct.pushUndo([base.direct.camera]) # Calc hprOffset hprOffset = VBase3() if view == 8: # Try the next roll angle self.orthoViewRoll = (self.orthoViewRoll + 90.0) % 360.0 # but use the last view view = self.lastView else: self.orthoViewRoll = 0.0 # Adjust offset based on specified view if view == 1: hprOffset.set(180., 0., 0.) elif view == 2: hprOffset.set(0., 0., 0.) elif view == 3: hprOffset.set(90., 0., 0.) elif view == 4: hprOffset.set(-90., 0., 0.) elif view == 5: hprOffset.set(0., -90., 0.) elif view == 6: hprOffset.set(0., 90., 0.) elif view == 7: hprOffset.set(135., -35.264, 0.) # Position target self.camManipRef.setPosHpr(self.coaMarker, ZERO_VEC, hprOffset) # Scale center vec by current distance to target offsetDistance = Vec3(base.direct.camera.getPos(self.camManipRef) - ZERO_POINT).length() scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance) # Now put the camManipRef at that point self.camManipRef.setPosHpr(self.camManipRef, scaledCenterVec, ZERO_VEC) # Record view for next time around self.lastView = view ival = base.direct.camera.posHprInterval(CAM_MOVE_DURATION, pos = ZERO_POINT, hpr = VBase3(0, 0, self.orthoViewRoll), other = self.camManipRef, blendType = 'easeInOut') ival = Sequence(ival, Func(self.updateCoaMarkerSizeOnDeath), name = 'manipulateCamera') self.__startManipulateCamera(ival = ival) def swingCamAboutWidget(self, degrees, t): # Remove existing camera manipulation task self.__stopManipulateCamera() # Record undo point base.direct.pushUndo([base.direct.camera]) # Coincident with widget self.camManipRef.setPos(self.coaMarker, ZERO_POINT) # But aligned with render space self.camManipRef.setHpr(ZERO_POINT) parent = base.direct.camera.getParent() base.direct.camera.wrtReparentTo(self.camManipRef) ival = self.camManipRef.hprInterval(CAM_MOVE_DURATION, VBase3(degrees, 0, 0), blendType = 'easeInOut') ival = Sequence(ival, Func(self.reparentCam, parent), name = 'manipulateCamera') self.__startManipulateCamera(ival = ival) def reparentCam(self, parent): base.direct.camera.wrtReparentTo(parent) self.updateCoaMarkerSize() def fitOnWidget(self, nodePath = 'None Given'): # Fit the node on the screen # stop any ongoing tasks self.__stopManipulateCamera() # How big is the node? nodeScale = base.direct.widget.scalingNode.getScale(render) maxScale = max(nodeScale[0], nodeScale[1], nodeScale[2]) maxDim = min(base.direct.dr.nearWidth, base.direct.dr.nearHeight) # At what distance does the object fill 30% of the screen? # Assuming radius of 1 on widget camY = base.direct.dr.near * (2.0 * maxScale)/(0.3 * maxDim) # What is the vector through the center of the screen? centerVec = Y_AXIS * camY # Where is the node relative to the viewpoint vWidget2Camera = base.direct.widget.getPos(base.direct.camera) # How far do you move the camera to be this distance from the node? deltaMove = vWidget2Camera - centerVec # Move a target there try: self.camManipRef.setPos(base.direct.camera, deltaMove) except Exception: self.notify.debug parent = base.direct.camera.getParent() base.direct.camera.wrtReparentTo(self.camManipRef) ival = base.direct.camera.posInterval(CAM_MOVE_DURATION, Point3(0, 0, 0), blendType = 'easeInOut') ival = Sequence(ival, Func(self.reparentCam, parent), name = 'manipulateCamera') self.__startManipulateCamera(ival = ival) def moveToFit(self): # How big is the active widget? widgetScale = base.direct.widget.scalingNode.getScale(render) maxScale = max(widgetScale[0], widgetScale[1], widgetScale[2]) # At what distance does the widget fill 50% of the screen? camY = ((2 * base.direct.dr.near * (1.5 * maxScale)) / min(base.direct.dr.nearWidth, base.direct.dr.nearHeight)) # Find a point this distance along the Y axis # MRM: This needs to be generalized to support non uniform frusta centerVec = Y_AXIS * camY # Before moving, record the relationship between the selected nodes # and the widget, so that this can be maintained base.direct.selected.getWrtAll() # Push state onto undo stack base.direct.pushUndo(base.direct.selected) # Remove the task to keep the widget attached to the object taskMgr.remove('followSelectedNodePath') # Spawn a task to keep the selected objects with the widget taskMgr.add(self.stickToWidgetTask, 'stickToWidget') # Spawn a task to move the widget ival = base.direct.widget.posInterval(CAM_MOVE_DURATION, Point3(centerVec), other = base.direct.camera, blendType = 'easeInOut') ival = Sequence(ival, Func(lambda: taskMgr.remove('stickToWidget')), name = 'moveToFit') ival.start() def stickToWidgetTask(self, state): # Move the objects with the widget base.direct.selected.moveWrtWidgetAll() # Continue return Task.cont def enableMouseFly(self, fKeyEvents = 1): # disable C++ fly interface base.disableMouse() # Enable events for event in self.actionEvents: self.accept(event[0], event[1], extraArgs = event[2:]) if fKeyEvents: for event in self.keyEvents: self.accept(event[0], event[1], extraArgs = event[2:]) # Show marker self.coaMarker.reparentTo(base.direct.group) def disableMouseFly(self): # Hide the marker self.coaMarker.reparentTo(hidden) # Ignore events for event in self.actionEvents: self.ignore(event[0]) for event in self.keyEvents: self.ignore(event[0]) # Kill tasks self.removeManipulateCameraTask() taskMgr.remove('stickToWidget') base.enableMouse() def removeManipulateCameraTask(self): self.__stopManipulateCamera()