| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895 |
- 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
- from direct.task.TaskManagerGlobal import taskMgr
- 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 = base.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.setPosHprScale(base.cam, 0, 0, 0, 0, 0, 0, 1, 1, 1)
- # 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:
- # 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.removeNode()
- 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:
- # 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:
- # 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:
- # 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 is 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()
|