| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- from PandaObject import *
- from DirectGeometry import *
- CAM_MOVE_DURATION = 1.0
- COA_MARKER_SF = 0.0075
- Y_AXIS = Vec3(0,1,0)
- class DirectCameraControl(PandaObject):
- def __init__(self):
- # Create the grid
- self.orthoViewRoll = 0.0
- self.lastView = 0
- self.coa = Point3(0,100,0)
- self.coaDist = 100
- self.coaMarker = loader.loadModel('models/misc/sphere')
- self.coaMarker.setName('DirectCameraCOAMarker')
- self.coaMarker.setColor(1,0,0)
- self.coaMarker.setPos(0,0,0)
- useDirectRenderStyle(self.coaMarker)
- self.coaMarkerPos = Point3(0)
- self.camManipRef = direct.group.attachNewNode('camManipRef')
- t = CAM_MOVE_DURATION
- self.actionEvents = [
- ['handleMouse2', self.mouseFlyStart],
- ['handleMouse2Up', self.mouseFlyStop],
- ['c', self.centerCamIn, 0.5],
- ['f', self.fitOnWidget],
- ['h', self.homeCam],
- ['m', self.moveToFit],
- ['u', self.orbitUprightCam],
- ['U', self.uprightCam],
- [`1`, self.spawnMoveToView, 1],
- [`2`, self.spawnMoveToView, 2],
- [`3`, self.spawnMoveToView, 3],
- [`4`, self.spawnMoveToView, 4],
- [`5`, self.spawnMoveToView, 5],
- [`6`, self.spawnMoveToView, 6],
- [`7`, self.spawnMoveToView, 7],
- [`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],
- ]
- def mouseFlyStart(self):
- # Record undo point
- direct.pushUndo([direct.camera])
- # Where are we in the display region?
- if ((abs(direct.dr.mouseX) < 0.9) & (abs(direct.dr.mouseY) < 0.9)):
- # MOUSE IS IN CENTRAL REGION
- # Hide the marker for this kind of motion
- self.coaMarker.hide()
- # Check for a hit point based on
- # current mouse position
- # And then spawn task to determine mouse mode
- numEntries = direct.iRay.pickGeom(
- render,direct.dr.mouseX,direct.dr.mouseY)
- # Then filter out hidden nodes from entry list
- indexList = []
- for i in range(0,numEntries):
- entry = direct.iRay.cq.getEntry(i)
- node = entry.getIntoNode()
- if node.isHidden():
- pass
- else:
- # Not one of the widgets, use it
- indexList.append(i)
- coa = Point3(0)
- if(indexList):
- # Grab first point (it should be the closest)
- minPt = indexList[0]
- # Find hit point in camera's space
- hitPt = direct.iRay.camToHitPt(minPt)
- coa.set(hitPt[0],hitPt[1],hitPt[2])
- coaDist = Vec3(coa - ZERO_POINT).length()
- # Handle case of bad coa point (too close or too far)
- if ((coaDist < (1.1 * direct.dr.near)) |
- (coaDist > direct.dr.far)):
- # Just use existing point
- coa.assign(self.coaMarker.getPos(direct.camera))
- coaDist = Vec3(coa - ZERO_POINT).length()
- if coaDist < (1.1 * direct.dr.near):
- coa.set(0,100,0)
- coaDist = 100
- else:
- # If no intersection point:
- # Use existing point
- coa.assign(self.coaMarker.getPos(direct.camera))
- coaDist = Vec3(coa - ZERO_POINT).length()
- # Check again its not to close
- if coaDist < (1.1 * direct.dr.near):
- coa.set(0,100,0)
- coaDist = 100
- # Update coa and marker
- self.updateCoa(coa, coaDist)
- # Start manipulation
- self.spawnXZTranslateOrHPanYZoom()
- # END MOUSE IN CENTRAL REGION
- else:
- if ((abs(direct.dr.mouseX) > 0.9) & (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()
- def mouseFlyStop(self):
- taskMgr.removeTasksNamed('manipulateCamera')
- # Show the marker
- self.coaMarker.show()
- # Resize it
- self.updateCoaMarkerSize()
- def spawnXZTranslateOrHPanYZoom(self):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Spawn the new task
- t = Task.Task(self.XZTranslateOrHPanYZoomTask)
- # For HPanYZoom
- t.zoomSF = Vec3(self.coa).length()
- taskMgr.spawnTaskNamed(t, 'manipulateCamera')
- def spawnXZTranslateOrHPPan(self):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Spawn new task
- taskMgr.spawnMethodNamed(self.XZTranslateOrHPPanTask,
- 'manipulateCamera')
- def spawnXZTranslate(self):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Spawn new task
- taskMgr.spawnMethodNamed(self.XZTranslateTask, 'manipulateCamera')
- def spawnHPanYZoom(self):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Spawn new task
- t = Task.Task(self.HPanYZoomTask)
- t.zoomSF = Vec3(self.coa).length()
- taskMgr.spawnTaskNamed(t, 'manipulateCamera')
- def spawnHPPan(self):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Spawn new task
- taskMgr.spawnMethodNamed(self.HPPanTask, 'manipulateCamera')
- def XZTranslateOrHPanYZoomTask(self, state):
- if direct.fShift | direct.fControl:
- return self.HPanYZoomTask(state)
- else:
- return self.XZTranslateTask(state)
- def XZTranslateOrHPPanTask(self, state):
- if direct.fShift:
- # Panning action
- return self.HPPanTask(state)
- else:
- # Translation action
- return self.XZTranslateTask(state)
- def XZTranslateTask(self,state):
- 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
- def HPanYZoomTask(self,state):
- if direct.fShift:
- 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)
- moveDir.assign(moveDir * (-2.0 * direct.dr.mouseDeltaY *
- state.zoomSF))
- direct.camera.setPosHpr(direct.camera,
- moveDir[0],
- moveDir[1],
- moveDir[2],
- (0.5 * direct.dr.mouseDeltaX *
- direct.dr.fovH),
- 0.0, 0.0)
- return Task.cont
- def HPPanTask(self, state):
- 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
- def spawnMouseRotateTask(self):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Set at markers position in render coordinates
- self.camManipRef.setPos(self.coaMarkerPos)
- self.camManipRef.setHpr(direct.camera, ZERO_POINT)
- t = Task.Task(self.mouseRotateTask)
- if abs(direct.dr.mouseX) > 0.9:
- t.constrainedDir = 'y'
- else:
- t.constrainedDir = 'x'
- taskMgr.spawnTaskNamed(t, 'manipulateCamera')
- def mouseRotateTask(self, state):
- # If moving outside of center, ignore motion perpendicular to edge
- if ((state.constrainedDir == 'y') & (abs(direct.dr.mouseX) > 0.9)):
- deltaX = 0
- deltaY = direct.dr.mouseDeltaY
- elif ((state.constrainedDir == 'x') & (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)
- self.camManipRef.setPos(self.coaMarkerPos)
- self.camManipRef.setHpr(direct.camera, ZERO_POINT)
- else:
- wrtMat = direct.camera.getMat( self.camManipRef )
- self.camManipRef.setHpr(self.camManipRef,
- (-1 * deltaX * 180.0),
- (deltaY * 180.0),
- 0.0)
- direct.camera.setMat(self.camManipRef, wrtMat)
- return Task.cont
- def spawnMouseRollTask(self):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Set at markers position in render coordinates
- self.camManipRef.setPos(self.coaMarkerPos)
- self.camManipRef.setHpr(direct.camera, ZERO_POINT)
- t = Task.Task(self.mouseRollTask)
- t.coaCenter = getScreenXY(self.coaMarker)
- t.lastAngle = getCrankAngle(t.coaCenter)
- t.wrtMat = direct.camera.getMat( self.camManipRef )
- taskMgr.spawnTaskNamed(t, 'manipulateCamera')
- def mouseRollTask(self, state):
- wrtMat = state.wrtMat
- angle = getCrankAngle(state.coaCenter)
- deltaAngle = angle - state.lastAngle
- state.lastAngle = angle
- self.camManipRef.setHpr(self.camManipRef, 0, 0, -deltaAngle)
- direct.camera.setMat(self.camManipRef, wrtMat)
- return Task.cont
- def updateCoa(self, cam2point, coaDist = None):
- self.coa.set(cam2point[0], cam2point[1], cam2point[2])
- if coaDist:
- self.coaDist = coaDist
- else:
- self.coaDist = Vec3(self.coa - ZERO_POINT).length()
- # Place the marker in render space
- self.coaMarker.setPos(direct.camera,self.coa)
- # Resize it
- self.updateCoaMarkerSize(coaDist)
- # Record marker pos in render space
- self.coaMarkerPos.assign(self.coaMarker.getPos())
- def updateCoaMarkerSizeOnDeath(self, state):
- # Needed because tasks pass in state as first arg
- self.updateCoaMarkerSize()
- def updateCoaMarkerSize(self, coaDist = None):
- if not coaDist:
- coaDist = Vec3(self.coaMarker.getPos( direct.camera )).length()
- self.coaMarker.setScale(COA_MARKER_SF * coaDist *
- math.tan(deg2Rad(direct.dr.fovV)))
- def homeCam(self):
- # Record undo point
- direct.pushUndo([direct.camera])
- direct.camera.setMat(Mat4.identMat())
- # Resize coa marker
- self.updateCoaMarkerSize()
- def uprightCam(self):
- taskMgr.removeTasksNamed('manipulateCamera')
- # Record undo point
- direct.pushUndo([direct.camera])
- # Pitch camera till upright
- currH = direct.camera.getH()
- direct.camera.lerpHpr(currH, 0, 0,
- CAM_MOVE_DURATION,
- other = render,
- blendType = 'easeInOut',
- task = 'manipulateCamera')
- def orbitUprightCam(self):
- taskMgr.removeTasksNamed('manipulateCamera')
- # Record undo point
- direct.pushUndo([direct.camera])
- # Transform camera z axis to render space
- mCam2Render = 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 = direct.camera.getParent()
- direct.camera.wrtReparentTo(self.camManipRef)
- # Rotate ref CS to final orientation
- t = self.camManipRef.lerpHpr(rotAngle, orbitAngle, 0,
- CAM_MOVE_DURATION,
- other = render,
- blendType = 'easeInOut',
- task = 'manipulateCamera')
- # Upon death, reparent Cam to parent
- t.parent = parent
- t.uponDeath = self.reparentCam
- def centerCam(self):
- self.centerCamIn(1.0)
-
- def centerCamNow(self):
- self.centerCamIn(0.)
- def centerCamIn(self, t):
- taskMgr.removeTasksNamed('manipulateCamera')
- # Record undo point
- direct.pushUndo([direct.camera])
- # Determine marker location
- markerToCam = self.coaMarker.getPos( direct.camera )
- dist = Vec3(markerToCam - ZERO_POINT).length()
- scaledCenterVec = Y_AXIS * dist
- delta = markerToCam - scaledCenterVec
- self.camManipRef.setPosHpr(direct.camera, Point3(0), Point3(0))
- t = direct.camera.lerpPos(Point3(delta),
- CAM_MOVE_DURATION,
- other = self.camManipRef,
- blendType = 'easeInOut',
- task = 'manipulateCamera')
- t.uponDeath = self.updateCoaMarkerSizeOnDeath
- def zoomCam(self, zoomFactor, t):
- taskMgr.removeTasksNamed('manipulateCamera')
- # Record undo point
- 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
- t = direct.camera.lerpPos(ZERO_POINT,
- CAM_MOVE_DURATION,
- other = self.camManipRef,
- blendType = 'easeInOut',
- task = 'manipulateCamera')
- t.uponDeath = self.updateCoaMarkerSizeOnDeath
-
- def spawnMoveToView(self, view):
- # Kill any existing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # Record undo point
- 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, ZERO_VEC,
- hprOffset)
- # Scale center vec by current distance to target
- offsetDistance = Vec3(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
- t = direct.camera.lerpPosHpr(ZERO_POINT,
- VBase3(0,0,self.orthoViewRoll),
- CAM_MOVE_DURATION,
- other = self.camManipRef,
- blendType = 'easeInOut',
- task = 'manipulateCamera')
- t.uponDeath = self.updateCoaMarkerSizeOnDeath
-
-
- def swingCamAboutWidget(self, degrees, t):
- # Remove existing camera manipulation task
- taskMgr.removeTasksNamed('manipulateCamera')
-
- # Record undo point
- direct.pushUndo([direct.camera])
-
- # Coincident with widget
- self.camManipRef.setPos(self.coaMarker, ZERO_POINT)
- # But aligned with render space
- self.camManipRef.setHpr(ZERO_POINT)
- parent = direct.camera.getParent()
- direct.camera.wrtReparentTo(self.camManipRef)
- manipTask = self.camManipRef.lerpHpr(VBase3(degrees,0,0),
- CAM_MOVE_DURATION,
- blendType = 'easeInOut',
- task = 'manipulateCamera')
- # Upon death, reparent Cam to parent
- manipTask.parent = parent
- manipTask.uponDeath = self.reparentCam
- def reparentCam(self, state):
- direct.camera.wrtReparentTo(state.parent)
- self.updateCoaMarkerSize()
- def fitOnWidget(self):
- # Fit the node on the screen
-
- # stop any ongoing tasks
- taskMgr.removeTasksNamed('manipulateCamera')
- # How big is the node?
- nodeScale = direct.widget.scalingNode.getScale(render)
- maxScale = max(nodeScale[0],nodeScale[1],nodeScale[2])
- maxDim = min(direct.dr.nearWidth, direct.dr.nearHeight)
- # At what distance does the object fill 30% of the screen?
- # Assuming radius of 1 on widget
- camY = 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 = direct.widget.getPos(direct.camera)
-
- # How far do you move the camera to be this distance from the node?
- deltaMove = vWidget2Camera - centerVec
-
- # Move a target there
- self.camManipRef.setPos(direct.camera, deltaMove)
- parent = direct.camera.getParent()
- direct.camera.wrtReparentTo(self.camManipRef)
- fitTask = direct.camera.lerpPos(Point3(0,0,0),
- CAM_MOVE_DURATION,
- blendType = 'easeInOut',
- task = 'manipulateCamera')
- # Upon death, reparent Cam to parent
- fitTask.parent = parent
- fitTask.uponDeath = self.reparentCam
- def moveToFit(self):
- # How bit is the active widget?
- widgetScale = 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 * 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.removeTasksNamed('followSelectedNodePath')
- # Spawn a task to keep the selected objects with the widget
- taskMgr.spawnMethodNamed(self.stickToWidgetTask, 'stickToWidget')
- # Spawn a task to move the widget
- t = direct.widget.lerpPos(Point3(centerVec),
- CAM_MOVE_DURATION,
- other = direct.camera,
- blendType = 'easeInOut',
- task = 'moveToFitTask')
- t.uponDeath = lambda state: taskMgr.removeTasksNamed('stickToWidget')
- def stickToWidgetTask(self, state):
- # Move the objects with the widget
- direct.selected.moveWrtWidgetAll()
- # Continue
- return Task.cont
- def enableMouseFly(self):
- # disable C++ fly interface
- base.disableMouse()
- # Enable events
- for event in self.actionEvents:
- self.accept(event[0], event[1], extraArgs = event[2:])
- # Show marker
- self.coaMarker.reparentTo(direct.group)
- def disableMouseFly(self):
- # Hide the marker
- self.coaMarker.reparentTo(hidden)
- # Ignore events
- for event in self.actionEvents:
- self.ignore(event[0])
- def removeManipulateCameraTask(self):
- taskMgr.removeTasksNamed('manipulateCamera')
|