import math
from panda3d.core import BitMask32, Mat4, NodePath, Point3, VBase3, Vec3, Vec4, rad2Deg
from direct.showbase.DirectObject import DirectObject
from direct.showbase import ShowBaseGlobal
from .DirectUtil import CLAMP, useDirectRenderStyle
from .DirectGeometry import getCrankAngle, getScreenXY
from . import DirectGlobals as DG
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)
[docs]class DirectCameraControl(DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory('DirectCameraControl')
[docs] 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 = ShowBaseGlobal.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 = ShowBaseGlobal.direct.group.attachNewNode('coaMarkerRef')
self.camManipRef = ShowBaseGlobal.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
[docs] def toggleMarkerVis(self):
if self.coaMarker.isHidden():
self.coaMarker.show()
else:
self.coaMarker.hide()
[docs] 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()
[docs] 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
base = ShowBaseGlobal.base
self.startT = base.clock.getFrameTime()
self.startF = base.clock.getFrameCount()
# If the cam is orthogonal, spawn differentTask
direct = ShowBaseGlobal.direct
if hasattr(direct, "manipulationControl") and \
direct.manipulationControl.fMultiView and \
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
[docs] def mouseDollyStop(self):
self.__stopManipulateCamera()
[docs] def mouseFlyStart(self, modifiers):
# Record undo point
base = ShowBaseGlobal.base
direct = ShowBaseGlobal.direct
#direct.pushUndo([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 = base.clock.getFrameTime()
self.startF = base.clock.getFrameCount()
# Start manipulation
# If the cam is orthogonal, spawn differentTask
if hasattr(direct, "manipulationControl") and \
direct.manipulationControl.fMultiView and \
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(direct.dr.mouseX) < 0.9 and abs(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 = base.clock.getFrameTime()
self.startF = base.clock.getFrameCount()
# Start manipulation
self.spawnXZTranslateOrHPanYZoom()
# END MOUSE IN CENTRAL REGION
elif abs(direct.dr.mouseX) > 0.9 and abs(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
[docs] def mouseFlyStop(self):
self.__stopManipulateCamera()
base = ShowBaseGlobal.base
stopT = base.clock.getFrameTime()
deltaT = stopT - self.startT
stopF = base.clock.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
direct = ShowBaseGlobal.direct
if not self.altDown and len(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 = DG.SKIP_HIDDEN | DG.SKIP_BACKFACE
# Skip camera (and its children), unless control key is pressed
skipFlags |= DG.SKIP_CAMERA * (1 - base.getControl())
self.computeCOA(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(direct.iRay.getNumEntries()):
self.cqEntries.append(direct.iRay.getEntry(i))
# Show the marker
self.coaMarker.show()
# Resize it
self.updateCoaMarkerSize()
[docs] def mouseFlyStartTopWin(self):
print("Moving mouse 2 in new window")
#altIsDown = base.getAlt()
#if altIsDown:
# print "Alt is down"
[docs] def mouseFlyStopTopWin(self):
print("Stopping mouse 2 in new window")
[docs] 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(ShowBaseGlobal.direct.camera)).length()
self.__startManipulateCamera(task = t)
[docs] def spawnXZTranslateOrHPPan(self):
# Kill any existing tasks
self.__stopManipulateCamera()
# Spawn new task
self.__startManipulateCamera(func = self.XZTranslateOrHPPanTask)
[docs] def spawnXZTranslate(self):
# Kill any existing tasks
self.__stopManipulateCamera()
# Spawn new task
self.__startManipulateCamera(func = self.XZTranslateTask)
[docs] def spawnOrthoTranslate(self):
# Kill any existing tasks
self.__stopManipulateCamera()
# Spawn new task
self.__startManipulateCamera(func = self.OrthoTranslateTask)
[docs] def spawnHPanYZoom(self):
# Kill any existing tasks
self.__stopManipulateCamera()
# Spawn new task
t = Task.Task(self.HPanYZoomTask)
t.zoomSF = Vec3(self.coaMarker.getPos(ShowBaseGlobal.direct.camera)).length()
self.__startManipulateCamera(task = t)
[docs] def spawnOrthoZoom(self):
# Kill any existing tasks
self.__stopManipulateCamera()
# Spawn new task
t = Task.Task(self.OrthoZoomTask)
self.__startManipulateCamera(task = t)
[docs] def spawnHPPan(self):
# Kill any existing tasks
self.__stopManipulateCamera()
# Spawn new task
self.__startManipulateCamera(func = self.HPPanTask)
[docs] def XZTranslateOrHPanYZoomTask(self, state):
if ShowBaseGlobal.direct.fShift:
return self.XZTranslateTask(state)
else:
return self.HPanYZoomTask(state)
[docs] def XZTranslateOrHPPanTask(self, state):
if ShowBaseGlobal.direct.fShift:
# Panning action
return self.HPPanTask(state)
else:
# Translation action
return self.XZTranslateTask(state)
[docs] def XZTranslateTask(self, state):
direct = ShowBaseGlobal.direct
coaDist = Vec3(self.coaMarker.getPos(direct.camera)).length()
xlateSF = coaDist / direct.dr.near
direct.camera.setPos(direct.camera,
(-0.5 * direct.dr.mouseDeltaX *
direct.dr.nearWidth *
xlateSF),
0.0,
(-0.5 * direct.dr.mouseDeltaY *
direct.dr.nearHeight *
xlateSF))
return Task.cont
[docs] def OrthoTranslateTask(self, state):
# create ray from the camera to detect 3d position
direct = ShowBaseGlobal.direct
iRay = SelectionRay(direct.camera)
iRay.collider.setFromLens(direct.camNode, direct.dr.mouseX, direct.dr.mouseY)
#iRay.collideWithBitMask(1)
iRay.collideWithBitMask(BitMask32.bit(21))
iRay.ct.traverse(direct.grid)
entry = iRay.getEntry(0)
hitPt = entry.getSurfacePoint(entry.getFromNodePath())
iRay.collisionNodePath.removeNode()
del iRay
if hasattr(state, 'prevPt'):
direct.camera.setPos(direct.camera, (state.prevPt - hitPt))
state.prevPt = hitPt
return Task.cont
[docs] def HPanYZoomTask(self, state):
# If the cam is orthogonal, don't rotate or zoom.
direct = ShowBaseGlobal.direct
if (hasattr(direct.cam.node(), "getLens") and
direct.cam.node().getLens().__class__.__name__ == "OrthographicLens"):
return
if direct.fControl:
moveDir = Vec3(self.coaMarker.getPos(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 * ((direct.dr.mouseDeltaX -1.0 * direct.dr.mouseDeltaY)
* state.zoomSF))
hVal = 0.0
else:
moveDir.assign(moveDir * (-1.0 * direct.dr.mouseDeltaY *
state.zoomSF))
if direct.dr.mouseDeltaY > 0.0:
moveDir.setY(moveDir[1] * 1.0)
hVal = 0.5 * direct.dr.mouseDeltaX * direct.dr.fovH
direct.camera.setPosHpr(direct.camera,
moveDir[0],
moveDir[1],
moveDir[2],
hVal,
0.0, 0.0)
if self.lockRoll:
# flatten roll
direct.camera.setR(0)
return Task.cont
[docs] def OrthoZoomTask(self, state):
direct = ShowBaseGlobal.direct
filmSize = direct.camNode.getLens().getFilmSize()
factor = (direct.dr.mouseDeltaX -1.0 * direct.dr.mouseDeltaY) * 0.1
x = direct.dr.getWidth()
y = direct.dr.getHeight()
direct.dr.orthoFactor -= factor
if direct.dr.orthoFactor < 0:
direct.dr.orthoFactor = 0.0001
direct.dr.updateFilmSize(x, y)
return Task.cont
[docs] def HPPanTask(self, state):
direct = ShowBaseGlobal.direct
direct.camera.setHpr(direct.camera,
(0.5 * direct.dr.mouseDeltaX *
direct.dr.fovH),
(-0.5 * direct.dr.mouseDeltaY *
direct.dr.fovV),
0.0)
return Task.cont
[docs] def spawnMouseRotateTask(self):
# Kill any existing tasks
self.__stopManipulateCamera()
direct = ShowBaseGlobal.direct
if self.perspCollPlane:
iRay = SelectionRay(direct.camera)
iRay.collider.setFromLens(direct.camNode, 0.0, 0.0)
iRay.collideWithBitMask(1)
if 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(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(direct.camera, DG.ZERO_POINT)
t = Task.Task(self.mouseRotateTask)
if abs(direct.dr.mouseX) > 0.9:
t.constrainedDir = 'y'
else:
t.constrainedDir = 'x'
self.__startManipulateCamera(task = t)
[docs] def mouseRotateTask(self, state):
# If the cam is orthogonal, don't rotate.
direct = ShowBaseGlobal.direct
if (hasattr(direct.cam.node(), "getLens") and
direct.cam.node().getLens().__class__.__name__ == "OrthographicLens"):
return
# If moving outside of center, ignore motion perpendicular to edge
if ((state.constrainedDir == 'y') and (abs(direct.dr.mouseX) > 0.9)):
deltaX = 0
deltaY = direct.dr.mouseDeltaY
elif ((state.constrainedDir == 'x') and (abs(direct.dr.mouseY) > 0.9)):
deltaX = direct.dr.mouseDeltaX
deltaY = 0
else:
deltaX = direct.dr.mouseDeltaX
deltaY = direct.dr.mouseDeltaY
if direct.fShift:
direct.camera.setHpr(direct.camera,
(deltaX * direct.dr.fovH),
(-deltaY * direct.dr.fovV),
0.0)
if self.lockRoll:
# flatten roll
direct.camera.setR(0)
self.camManipRef.setPos(self.coaMarkerPos)
self.camManipRef.setHpr(direct.camera, DG.ZERO_POINT)
else:
if direct.camera.getPos().getZ() >=0 or not self.switchDirBelowZero:
dirX = -1
else:
dirX = 1
wrt = 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)
direct.camera.setTransform(self.camManipRef, wrt)
return Task.cont
[docs] def spawnMouseRollTask(self):
# Kill any existing tasks
self.__stopManipulateCamera()
# Set at markers position in render coordinates
direct = ShowBaseGlobal.direct
self.camManipRef.setPos(self.coaMarkerPos)
self.camManipRef.setHpr(direct.camera, DG.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 = direct.camera.getTransform(self.camManipRef)
self.__startManipulateCamera(task = t)
[docs] 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)
ShowBaseGlobal.direct.camera.setTransform(self.camManipRef, wrt)
return Task.cont
[docs] def lockCOA(self):
self.fLockCOA = 1
ShowBaseGlobal.direct.message('COA Lock On')
[docs] def unlockCOA(self):
self.fLockCOA = 0
ShowBaseGlobal.direct.message('COA Lock Off')
[docs] def toggleCOALock(self):
self.fLockCOA = 1 - self.fLockCOA
if self.fLockCOA:
ShowBaseGlobal.direct.message('COA Lock On')
else:
ShowBaseGlobal.direct.message('COA Lock Off')
[docs] 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 ShowBaseGlobal.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()
[docs] def computeCOA(self, entry):
coa = Point3(0)
dr = ShowBaseGlobal.direct.drList.getCurrentDr()
if self.fLockCOA:
# COA is locked, use existing point
# Use existing point
coa.assign(self.coaMarker.getPos(ShowBaseGlobal.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(ShowBaseGlobal.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)
ShowBaseGlobal.direct.message('COA Distance: ' + repr(dist))
coa.set(0, dist, 0)
# Compute COA Dist
coaDist = Vec3(coa - DG.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)
[docs] def updateCoa(self, ref2point, coaDist = None, ref = None):
self.coa.set(ref2point[0], ref2point[1], ref2point[2])
if not coaDist:
coaDist = Vec3(self.coa - DG.ZERO_POINT).length()
# Place the marker in render space
if ref is None:
# KEH: use the current display region
# ref = base.cam
ref = ShowBaseGlobal.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())
[docs] def updateCoaMarkerSizeOnDeath(self):
# Needed because tasks pass in state as first arg
self.updateCoaMarkerSize()
[docs] def updateCoaMarkerSize(self, coaDist = None):
if not coaDist:
coaDist = Vec3(self.coaMarker.getPos(ShowBaseGlobal.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 * (ShowBaseGlobal.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()
[docs] def homeCam(self):
# Record undo point
direct = ShowBaseGlobal.direct
direct.pushUndo([direct.camera])
direct.camera.reparentTo(ShowBaseGlobal.base.render)
direct.camera.clearMat()
# Resize coa marker
self.updateCoaMarkerSize()
[docs] def uprightCam(self):
self.__stopManipulateCamera()
# Record undo point
direct = ShowBaseGlobal.direct
direct.pushUndo([direct.camera])
# Pitch camera till upright
currH = direct.camera.getH()
ival = direct.camera.hprInterval(CAM_MOVE_DURATION,
(currH, 0, 0),
other=ShowBaseGlobal.base.render,
blendType='easeInOut',
name='manipulateCamera')
self.__startManipulateCamera(ival=ival)
[docs] def orbitUprightCam(self):
self.__stopManipulateCamera()
# Record undo point
direct = ShowBaseGlobal.direct
direct.pushUndo([direct.camera])
# Transform camera z axis to render space
render = ShowBaseGlobal.base.render
mCam2Render = Mat4(Mat4.identMat()) # [gjeon] fixed to give required argument
mCam2Render.assign(direct.camera.getMat(render))
zAxis = Vec3(mCam2Render.xformVec(DG.Z_AXIS))
zAxis.normalize()
# Compute rotation angle needed to upright cam
orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(DG.Z_AXIS), -1, 1)))
# Check angle
if orbitAngle < 0.1:
# Already upright
return
# Compute orthogonal axis of rotation
rotAxis = Vec3(zAxis.cross(DG.Z_AXIS))
rotAxis.normalize()
# Find angle between rot Axis and render X_AXIS
rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(DG.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 = direct.camera.getParent()
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)
[docs] def centerCam(self):
self.centerCamIn(1.0)
[docs] def centerCamNow(self):
self.centerCamIn(0.)
[docs] def centerCamIn(self, t):
self.__stopManipulateCamera()
# Record undo point
direct = ShowBaseGlobal.direct
direct.pushUndo([direct.camera])
# Determine marker location
markerToCam = self.coaMarker.getPos(direct.camera)
dist = Vec3(markerToCam - DG.ZERO_POINT).length()
scaledCenterVec = Y_AXIS * dist
delta = markerToCam - scaledCenterVec
self.camManipRef.setPosHpr(direct.camera, Point3(0), Point3(0))
ival = 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)
[docs] def zoomCam(self, zoomFactor, t):
self.__stopManipulateCamera()
# Record undo point
direct = ShowBaseGlobal.direct
direct.pushUndo([direct.camera])
# Find a point zoom factor times the current separation
# of the widget and cam
zoomPtToCam = self.coaMarker.getPos(direct.camera) * zoomFactor
# Put a target nodePath there
self.camManipRef.setPos(direct.camera, zoomPtToCam)
# Move to that point
ival = direct.camera.posInterval(CAM_MOVE_DURATION,
DG.ZERO_POINT,
other=self.camManipRef,
blendType='easeInOut')
ival = Sequence(ival, Func(self.updateCoaMarkerSizeOnDeath),
name = 'manipulateCamera')
self.__startManipulateCamera(ival = ival)
[docs] def spawnMoveToView(self, view):
# Kill any existing tasks
self.__stopManipulateCamera()
# Record undo point
direct = ShowBaseGlobal.direct
direct.pushUndo([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, DG.ZERO_VEC,
hprOffset)
# Scale center vec by current distance to target
offsetDistance = Vec3(direct.camera.getPos(self.camManipRef) -
DG.ZERO_POINT).length()
scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance)
# Now put the camManipRef at that point
self.camManipRef.setPosHpr(self.camManipRef,
scaledCenterVec,
DG.ZERO_VEC)
# Record view for next time around
self.lastView = view
ival = direct.camera.posHprInterval(CAM_MOVE_DURATION,
pos=DG.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)
[docs] def reparentCam(self, parent):
ShowBaseGlobal.direct.camera.wrtReparentTo(parent)
self.updateCoaMarkerSize()
[docs] def moveToFit(self):
# How big is the active widget?
direct = ShowBaseGlobal.direct
widgetScale = direct.widget.scalingNode.getScale(ShowBaseGlobal.base.render)
maxScale = max(widgetScale[0], widgetScale[1], widgetScale[2])
# At what distance does the widget fill 50% of the screen?
camY = ((2 * direct.dr.near * (1.5 * maxScale)) /
min(direct.dr.nearWidth, 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
direct.selected.getWrtAll()
# Push state onto undo stack
direct.pushUndo(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 = direct.widget.posInterval(CAM_MOVE_DURATION,
Point3(centerVec),
other=direct.camera,
blendType='easeInOut')
ival = Sequence(ival, Func(lambda: taskMgr.remove('stickToWidget')),
name = 'moveToFit')
ival.start()
[docs] def enableMouseFly(self, fKeyEvents = 1):
# disable C++ fly interface
base = ShowBaseGlobal.base
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(ShowBaseGlobal.direct.group)
[docs] def disableMouseFly(self):
# Hide the marker
self.coaMarker.reparentTo(ShowBaseGlobal.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')
ShowBaseGlobal.base.enableMouse()
[docs] def removeManipulateCameraTask(self):
self.__stopManipulateCamera()