From 8ecb2f612cd190fe6872939d2f91e7ccb13ba869 Mon Sep 17 00:00:00 2001 From: Mark Mine Date: Mon, 15 Jan 2001 04:21:16 +0000 Subject: [PATCH] *** empty log message *** --- direct/src/directutil/DirectCameraControl.py | 558 +++++++++++-------- direct/src/directutil/DirectGeometry.py | 39 ++ direct/src/directutil/DirectManipulation.py | 62 +-- direct/src/directutil/DirectSession.py | 45 +- direct/src/showbase/Messenger.py | 38 ++ 5 files changed, 455 insertions(+), 287 deletions(-) diff --git a/direct/src/directutil/DirectCameraControl.py b/direct/src/directutil/DirectCameraControl.py index 47ba50f1ea..99c82ff115 100644 --- a/direct/src/directutil/DirectCameraControl.py +++ b/direct/src/directutil/DirectCameraControl.py @@ -1,5 +1,5 @@ from PandaObject import * -from DirectGeometry import useDirectRenderStyle +from DirectGeometry import * CAM_MOVE_DURATION = 1.0 COA_MARKER_SF = 0.0075 @@ -19,26 +19,24 @@ class DirectCameraControl(PandaObject): useDirectRenderStyle(self.coaMarker) self.coaMarkerPos = Point3(0) self.camManipRef = direct.group.attachNewNode('camManipRef') - self.zeroBaseVec = VBase3(0) - self.zeroVector = Vec3(0) - self.centerVec = Vec3(0, 1, 0) - self.zeroPoint = Point3(0) t = CAM_MOVE_DURATION self.actionEvents = [ ['handleMouse2', self.mouseFlyStart], ['handleMouse2Up', self.mouseFlyStop], - ['u', self.uprightCam], ['c', self.centerCamIn, 0.5], - ['h', self.homeCam], ['f', self.fitOnWidget], - [`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], + ['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], @@ -59,102 +57,241 @@ class DirectCameraControl(PandaObject): # MOUSE IS IN CENTRAL REGION # Hide the marker for this kind of motion self.coaMarker.hide() - # See if the shift key is pressed - if (direct.fShift): - # If shift key is pressed, just perform horiz and vert pan: - self.spawnHPPan() - else: - # Otherwise, 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) - # 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): - # Start off with first point - 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 - self.zeroPoint).length() - # Check other intersection points, sorting them - # TBD: Use TBS C++ function to do this - if len(indexList) > 1: - for i in range(1,len(indexList)): - entryNum = indexList[i] - hitPt = direct.iRay.camToHitPt(entryNum) - dist = Vec3(hitPt - self.zeroPoint).length() - if (dist < coaDist): - coaDist = dist - coa.set(hitPt[0],hitPt[1],hitPt[2]) - minPt = i - # 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 - self.zeroPoint).length() - if coaDist < (1.1 * direct.dr.near): - coa.set(0,100,0) - coaDist = 100 + # 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) + # Sort intersection points + direct.iRay.cq.sortEntries() + # 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: - # If no intersection point: - # Use existing point + # 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() + """ + # Check other intersection points, sorting them + # TBD: Use TBS C++ function to do this + if len(indexList) > 1: + for i in range(1,len(indexList)): + entryNum = indexList[i] + hitPt = direct.iRay.camToHitPt(entryNum) + dist = Vec3(hitPt - ZERO_POINT).length() + if (dist < coaDist): + coaDist = dist + coa.set(hitPt[0],hitPt[1],hitPt[2]) + minPt = i + """ + # 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 - self.zeroPoint).length() + coaDist = Vec3(coa - ZERO_POINT).length() if coaDist < (1.1 * direct.dr.near): coa.set(0,100,0) coaDist = 100 - # Update coa and marker - self.updateCoa(coa, coaDist) - # Now spawn task to determine mouse fly mode - self.determineMouseFlyMode() + 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: - # Mouse is in outer frame, spawn mouseRotateTask - self.spawnMouseRotateTask() + if ((abs(self.initMouseX) > 0.9) & (abs(self.initMouseY) > 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('determineMouseFlyMode') taskMgr.removeTasksNamed('manipulateCamera') # Show the marker self.coaMarker.show() # Resize it self.updateCoaMarkerSize() - def determineMouseFlyMode(self): - # Otherwise, determine mouse fly mode - t = Task.Task(self.determineMouseFlyModeTask) - taskMgr.spawnTaskNamed(t, 'determineMouseFlyMode') + 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 determineMouseFlyModeTask(self, state): - deltaX = direct.dr.mouseX - self.initMouseX - deltaY = direct.dr.mouseY - self.initMouseY - if ((abs(deltaX) < 0.1) & (abs(deltaY) < 0.1)): - return Task.cont + 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: + return self.XZTranslateTask(state) else: - if (abs(deltaY) > 0.1): - self.spawnHPanYZoom() - else: - self.spawnXZTranslate() - return Task.done + return self.HPanYZoomTask(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.fControl: + moveDir = Vec3(Y_AXIS) + else: + 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() + 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 within frame, 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 - self.zeroPoint).length() + self.coaDist = Vec3(self.coa - ZERO_POINT).length() # Place the marker in render space self.coaMarker.setPos(direct.camera,self.coa) # Resize it @@ -162,6 +299,10 @@ class DirectCameraControl(PandaObject): # 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() @@ -172,18 +313,59 @@ class DirectCameraControl(PandaObject): # Record undo point direct.pushUndo([direct.camera]) direct.camera.setMat(Mat4.identMat()) + # Resize coa marker + self.updateCoaMarkerSize() def uprightCam(self): taskMgr.removeTasksNamed('manipulateCamera') - currH = direct.camera.getH() # 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) @@ -196,15 +378,16 @@ class DirectCameraControl(PandaObject): direct.pushUndo([direct.camera]) # Determine marker location markerToCam = self.coaMarker.getPos( direct.camera ) - dist = Vec3(markerToCam - self.zeroPoint).length() - scaledCenterVec = self.centerVec * dist + dist = Vec3(markerToCam - ZERO_POINT).length() + scaledCenterVec = Y_AXIS * dist delta = markerToCam - scaledCenterVec self.camManipRef.setPosHpr(direct.camera, Point3(0), Point3(0)) - direct.camera.lerpPos(Point3(delta), - CAM_MOVE_DURATION, - other = self.camManipRef, - blendType = 'easeInOut', - task = 'manipulateCamera') + 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') @@ -216,13 +399,14 @@ class DirectCameraControl(PandaObject): # Put a target nodePath there self.camManipRef.setPos(direct.camera, zoomPtToCam) # Move to that point - direct.camera.lerpPos(self.zeroPoint, - CAM_MOVE_DURATION, - other = self.camManipRef, - blendType = 'easeInOut', - task = 'manipulateCamera') + t = direct.camera.lerpPos(ZERO_POINT, + CAM_MOVE_DURATION, + other = self.camManipRef, + blendType = 'easeInOut', + task = 'manipulateCamera') + t.uponDeath = self.updateCoaMarkerSizeOnDeath - def SpawnMoveToView(self, view): + def spawnMoveToView(self, view): # Kill any existing tasks taskMgr.removeTasksNamed('manipulateCamera') # Record undo point @@ -252,25 +436,26 @@ class DirectCameraControl(PandaObject): elif view == 7: hprOffset.set(135., -35.264, 0.) # Position target - self.camManipRef.setPosHpr(self.coaMarker, self.zeroBaseVec, + self.camManipRef.setPosHpr(self.coaMarker, ZERO_VEC, hprOffset) # Scale center vec by current distance to target offsetDistance = Vec3(direct.camera.getPos(self.camManipRef) - - self.zeroPoint).length() - scaledCenterVec = self.centerVec * (-1.0 * offsetDistance) + ZERO_POINT).length() + scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance) # Now put the camManipRef at that point self.camManipRef.setPosHpr(self.camManipRef, scaledCenterVec, - self.zeroBaseVec) + ZERO_VEC) # Record view for next time around self.lastView = view - direct.camera.lerpPosHpr(self.zeroPoint, - VBase3(0,0,self.orthoViewRoll), - CAM_MOVE_DURATION, - other = self.camManipRef, - blendType = 'easeInOut', - task = 'manipulateCamera') - + 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 @@ -280,9 +465,9 @@ class DirectCameraControl(PandaObject): direct.pushUndo([direct.camera]) # Coincident with widget - self.camManipRef.setPos(self.coaMarker, self.zeroPoint) + self.camManipRef.setPos(self.coaMarker, ZERO_POINT) # But aligned with render space - self.camManipRef.setHpr(self.zeroPoint) + self.camManipRef.setHpr(ZERO_POINT) parent = direct.camera.getParent() direct.camera.wrtReparentTo(self.camManipRef) @@ -297,115 +482,7 @@ class DirectCameraControl(PandaObject): def reparentCam(self, state): direct.camera.wrtReparentTo(state.parent) - - def spawnHPanYZoom(self): - # Kill any existing tasks - taskMgr.removeTasksNamed('manipulateCamera') - # hide the marker - self.coaMarker.hide() - # Negate vec to give it the correct sense for mouse motion below - targetVector = self.coa * -1 - t = Task.Task(self.HPanYZoomTask) - t.targetVector = targetVector - taskMgr.spawnTaskNamed(t, 'manipulateCamera') - - def HPanYZoomTask(self,state): - targetVector = state.targetVector - # Can bring object to you by dragging across half the screen - distToMove = targetVector * (2.0 * direct.dr.mouseDeltaY) - direct.camera.setPosHpr(direct.camera, - distToMove[0], - distToMove[1], - distToMove[2], - (0.5 * direct.dr.mouseDeltaX * - direct.dr.fovH), - 0.0, 0.0) - return Task.cont - - - def spawnXZTranslateOrHPPan(self): - # Kill any existing tasks - taskMgr.removeTasksNamed('manipulateCamera') - # Hide the marker - self.coaMarker.hide() - t = Task.Task(self.XZTranslateOrHPPanTask) - t.scaleFactor = (self.coaDist / direct.dr.near) - taskMgr.spawnTaskNamed(t, 'manipulateCamera') - - def XZTranslateOrHPPanTask(self, state): - if direct.fShift: - direct.camera.setHpr(direct.camera, - (0.5 * direct.dr.mouseDeltaX * - direct.dr.fovH), - (-0.5 * direct.dr.mouseDeltaY * - direct.dr.fovV), - 0.0) - else: - direct.camera.setPos(direct.camera, - (-0.5 * direct.dr.mouseDeltaX * - direct.dr.nearWidth * - state.scaleFactor), - 0.0, - (-0.5 * direct.dr.mouseDeltaY * - direct.dr.nearHeight * - state.scaleFactor)) - return Task.cont - - def spawnXZTranslate(self): - # Kill any existing tasks - taskMgr.removeTasksNamed('manipulateCamera') - # Hide the marker - self.coaMarker.hide() - t = Task.Task(self.XZTranslateTask) - t.scaleFactor = (self.coaDist / direct.dr.near) - taskMgr.spawnTaskNamed(t, 'manipulateCamera') - - def XZTranslateTask(self,state): - direct.camera.setPos(direct.camera, - (-0.5 * direct.dr.mouseDeltaX * - direct.dr.nearWidth * - state.scaleFactor), - 0.0, - (-0.5 * direct.dr.mouseDeltaY * - direct.dr.nearHeight * - state.scaleFactor)) - 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, self.zeroPoint) - t = Task.Task(self.mouseRotateTask) - t.wrtMat = direct.camera.getMat( self.camManipRef ) - taskMgr.spawnTaskNamed(t, 'manipulateCamera') - - def mouseRotateTask(self, state): - wrtMat = state.wrtMat - self.camManipRef.setHpr(self.camManipRef, - (-0.5 * direct.dr.mouseDeltaX * 180.0), - (0.5 * direct.dr.mouseDeltaY * 180.0), - 0.0) - direct.camera.setMat(self.camManipRef, wrtMat) - return Task.cont - - def spawnHPPan(self): - # Kill any existing tasks - taskMgr.removeTasksNamed('manipulateCamera') - # Hide the marker - self.coaMarker.hide() - t = Task.Task(self.HPPanTask) - taskMgr.spawnTaskNamed(t, 'manipulateCamera') - - 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 + self.updateCoaMarkerSize() def fitOnWidget(self): # Fit the node on the screen @@ -444,6 +521,39 @@ class DirectCameraControl(PandaObject): 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() diff --git a/direct/src/directutil/DirectGeometry.py b/direct/src/directutil/DirectGeometry.py index 5c9b73d9cf..41ef5706a4 100644 --- a/direct/src/directutil/DirectGeometry.py +++ b/direct/src/directutil/DirectGeometry.py @@ -86,6 +86,45 @@ def ROUND_TO(value, divisor): return round(value/float(divisor)) * divisor def ROUND_INT(val): return int(round(val)) +def CLAMP(val, min, max): + if val < min: + return min + elif val > max: + return max + else: + return val + +def getNearProjectionPoint(nodePath): + # Find the position of the projection of the specified node path + # on the near plane + origin = nodePath.getPos(direct.camera) + # project this onto near plane + if origin[1] != 0.0: + return origin * (direct.dr.near / origin[1]) + else: + # Object is coplaner with camera, just return something reasonable + return Point3(0, direct.dr.near, 0) + +def getScreenXY(nodePath): + # Where does the node path's projection fall on the near plane + nearVec = getNearProjectionPoint(nodePath) + # Clamp these coordinates to visible screen + nearX = CLAMP(nearVec[0], direct.dr.left, direct.dr.right) + nearY = CLAMP(nearVec[2], direct.dr.bottom, direct.dr.top) + # What percentage of the distance across the screen is this? + percentX = (nearX - direct.dr.left)/direct.dr.nearWidth + percentY = (nearY - direct.dr.bottom)/direct.dr.nearHeight + # Map this percentage to the same -1 to 1 space as the mouse + screenXY = Vec3((2 * percentX) - 1.0,nearVec[1],(2 * percentY) - 1.0) + # Return the resulting value + return screenXY + +def getCrankAngle(center): + # Used to compute current angle of mouse (relative to the coa's + # origin) in screen space + x = direct.dr.mouseX - center[0] + y = direct.dr.mouseY - center[2] + return (180 + rad2Deg(math.atan2(y,x))) # Set direct drawing style for an object # Never light object or draw in wireframe diff --git a/direct/src/directutil/DirectManipulation.py b/direct/src/directutil/DirectManipulation.py index 62a8f42ae1..f987ba5255 100644 --- a/direct/src/directutil/DirectManipulation.py +++ b/direct/src/directutil/DirectManipulation.py @@ -28,6 +28,7 @@ class DirectManipulationControl(PandaObject): self.actionEvents = [ ['handleMouse1', self.manipulationStart], ['handleMouse1Up', self.manipulationStop], + ['space', self.toggleObjectHandlesMode], ['.', self.objectHandles.multiplyScalingFactorBy, 2.0], ['>', self.objectHandles.multiplyScalingFactorBy, 2.0], [',', self.objectHandles.multiplyScalingFactorBy, 0.5], @@ -137,8 +138,6 @@ class DirectManipulationControl(PandaObject): # We had been scaling, need to reset object handles self.objectHandles.transferObjectHandlesScale() self.fScaling = 0 - if self.fSetCoa: - self.objectHandles.manipModeColor() direct.selected.highlightAll() self.objectHandles.showAllHandles() self.objectHandles.hideGuides() @@ -188,6 +187,13 @@ class DirectManipulationControl(PandaObject): if item in self.unpickable: self.unpickable.remove(item) + def toggleObjectHandlesMode(self): + self.fSetCoa = 1 - self.fSetCoa + if self.fSetCoa: + self.objectHandles.coaModeColor() + else: + self.objectHandles.manipModeColor() + def removeManipulateObjectTask(self): taskMgr.removeTasksNamed('manipulateObject') @@ -206,20 +212,10 @@ class DirectManipulationControl(PandaObject): self.objectHandles.showGuides() self.objectHandles.hideAllHandles() self.objectHandles.showHandle(self.constraint) - if self.fSetCoa: - self.objectHandles.coaModeColor() - # Record relationship between selected nodes and widget direct.selected.getWrtAll() - # hide the bbox of the selected objects during interaction direct.selected.dehighlightAll() - - """ - # Push the undo dcs for the selected objects - direct.undo.push( - (direct.selected, 'dcs')) - """ # Manipulate the real object with the constraint # The constraint is passed as the name of the node self.spawnManipulateObjectTask() @@ -350,13 +346,6 @@ class DirectManipulationControl(PandaObject): y + self.initY * dr.mouseDeltaY, z) - def getCrankAngle(self): - # Used to compute current angle of mouse (relative to the widget's - # origin) in screen space - x = direct.dr.mouseX - self.rotationCenter[0] - y = direct.dr.mouseY - self.rotationCenter[2] - return (180 + rad2Deg(math.atan2(y,x))) - def widgetCheck(self,type): # Utility to see if we are looking at the top or bottom of # a 2D planar widget or if we are looking at a 2D planar widget @@ -388,27 +377,6 @@ class DirectManipulationControl(PandaObject): # Check angle between two vectors return(abs(widgetDir.dot(widgetAxis)) < .2) - def getWidgetsNearProjectionPoint(self): - # Find the position of the projection of the specified node path - # on the near plane - widgetOrigin = direct.widget.getPos(direct.camera) - # project this onto near plane - return widgetOrigin * (direct.dr.near / widgetOrigin[1]) - - def getScreenXY(self): - # Where does the widget's projection fall on the near plane - nearVec = self.getWidgetsNearProjectionPoint() - # Clamp these coordinates to visible screen - nearX = self.clamp(nearVec[0], direct.dr.left, direct.dr.right) - nearY = self.clamp(nearVec[2], direct.dr.bottom, direct.dr.top) - # What percentage of the distance across the screen is this? - percentX = (nearX - direct.dr.left)/direct.dr.nearWidth - percentY = (nearY - direct.dr.bottom)/direct.dr.nearHeight - # Map this percentage to the same -1 to 1 space as the mouse - screenXY = Vec3((2 * percentX) - 1.0,nearVec[1],(2 * percentY) - 1.0) - # Return the resulting value - return screenXY - def rotate1D(self): # Constrained 1D rotation about the widget's main axis (X,Y, or Z) # Rotation depends upon circular motion of the mouse about the @@ -421,11 +389,11 @@ class DirectManipulationControl(PandaObject): self.fHitInit = 0 self.rotateAxis = self.constraint[:1] self.fWidgetTop = self.widgetCheck('top?') - self.rotationCenter = self.getScreenXY() - self.lastCrankAngle = self.getCrankAngle() + self.rotationCenter = getScreenXY(direct.widget) + self.lastCrankAngle = getCrankAngle(self.rotationCenter) # Rotate widget based on how far cursor has swung around origin - newAngle = self.getCrankAngle() + newAngle = getCrankAngle(self.rotationCenter) deltaAngle = self.lastCrankAngle - newAngle if self.fWidgetTop: deltaAngle = -1 * deltaAngle @@ -491,14 +459,6 @@ class DirectManipulationControl(PandaObject): ) direct.widget.setScale(currScale) - def clamp(self, val, min, max): - if val < min: - return min - elif val > max: - return max - else: - return val - class ObjectHandles(NodePath,PandaObject): def __init__(self): diff --git a/direct/src/directutil/DirectSession.py b/direct/src/directutil/DirectSession.py index d6a3b1c9eb..c4e735e917 100644 --- a/direct/src/directutil/DirectSession.py +++ b/direct/src/directutil/DirectSession.py @@ -82,7 +82,8 @@ class DirectSession(PandaObject): 'escape', 'space', 'delete', 'shift', 'shift-up', 'alt', 'alt-up', 'control', 'control-up', - 'page_up', 'page_down', + 'page_up', 'page_down', 'tab', + '[', '{', ']', '}', 'b', 'c', 'f', 'l', 's', 't', 'v', 'w'] self.mouseEvents = ['mouse1', 'mouse1-up', 'mouse2', 'mouse2-up', @@ -190,21 +191,27 @@ class DirectSession(PandaObject): self.downAncestry() elif input == 'escape': self.deselectAll() + elif input == 'delete': + self.removeAllSelected() + elif input == 'tab': + self.toggleWidgetVis() + elif input == 'b': + base.toggleBackface() elif input == 'l': self.lights.toggle() elif input == 's': if self.selected.last: self.select(self.selected.last) - elif input == 'delete': - self.removeAllSelected() - elif input == 'v': - self.selected.toggleVisAll() - elif input == 'b': - base.toggleBackface() elif input == 't': base.toggleTexture() + elif input == 'v': + self.selected.toggleVisAll() elif input == 'w': base.toggleWireframe() + elif (input == '[') | (input == '{'): + self.undo() + elif (input == ']') | (input == '}'): + self.redo() def select(self, nodePath, fMultiselect = 0, fResetAncestry = 1): dnp = self.selected.select(nodePath, fMultiselect) @@ -219,7 +226,7 @@ class DirectSession(PandaObject): self.readout.reparentTo(render2d) self.readout.setText(dnp.name) # Show the manipulation widget - self.widget.reparentTo(direct.group) + self.reparentWidgetTo('direct') # Update camera controls coa to this point # Coa2Camera = Coa2Dnp * Dnp2Camera mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(self.camera) @@ -250,7 +257,7 @@ class DirectSession(PandaObject): dnp = self.selected.deselect(nodePath) if dnp: # Hide the manipulation widget - self.widget.reparentTo(hidden) + self.reparentWidgetTo('hidden') self.readout.reparentTo(hidden) self.readout.setText(' ') taskMgr.removeTasksNamed('followSelectedNodePath') @@ -261,7 +268,7 @@ class DirectSession(PandaObject): def deselectAll(self): self.selected.deselectAll() # Hide the manipulation widget - self.widget.reparentTo(hidden) + self.reparentWidgetTo('hidden') self.readout.reparentTo(hidden) self.readout.setText(' ') taskMgr.removeTasksNamed('followSelectedNodePath') @@ -384,7 +391,7 @@ class DirectSession(PandaObject): # Now record group self.undoList.append(undoGroup) # Truncate list - self.undoList = self.undoList[-5:] + self.undoList = self.undoList[-25:] # Alert anyone who cares messenger.send('pushUndo') if fResetRedo & (nodePathList != []): @@ -413,7 +420,7 @@ class DirectSession(PandaObject): # Now record redo group self.redoList.append(redoGroup) # Truncate list - self.redoList = self.redoList[-5:] + self.redoList = self.redoList[-25:] # Alert anyone who cares messenger.send('pushRedo') @@ -462,6 +469,20 @@ class DirectSession(PandaObject): def hideReadout(self): self.readout.reparentTo(hidden) + def reparentWidgetTo(self, parent): + if parent == 'direct': + self.widget.reparentTo(direct.group) + self.widgetParent = 'direct' + else: + self.widget.reparentTo(hidden) + self.widgetParent = 'hidden' + + def toggleWidgetVis(self): + if self.widgetParent == 'direct': + self.reparentWidgetTo('hidden') + else: + self.reparentWidgetTo('direct') + class DisplayRegionList: def __init__(self): self.displayRegionList = [] diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index a47c48846c..4e84975401 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -113,12 +113,46 @@ class Messenger: """ self.dict.clear() + def listAllEvents(self): + str = 'Messenger\n' + str = str + '='*50 + '\n' + keys = self.dict.keys() + keys.sort() + for event in keys: + str = str + 'Event: ' + event + '\n' + str = str + '='*50 + '\n' + print str + def __repr__(self): """__repr__(self) Print out the table in a readable format """ str = 'Messenger\n' str = str + '='*50 + '\n' + keys = self.dict.keys() + keys.sort() + for event in keys: + acceptorDict = self.dict[event] + str = str + 'Event: ' + event + '\n' + for object in acceptorDict.keys(): + method, extraArgs, persistent = acceptorDict[object] + className = object.__class__.__name__ + methodName = method.__name__ + str = (str + '\t' + + 'Acceptor: ' + className + ' instance' + '\n\t' + + 'Method: ' + methodName + '\n\t' + + 'Extra Args: ' + `extraArgs` + '\n\t' + + 'Persistent: ' + `persistent` + '\n\n' + ) + str = str + '='*50 + '\n' + return str + + def __reprehensible__(self): + """__repr__(self) + Old way to print out the table in a readable format + """ + str = 'Messenger\n' + str = str + '='*50 + '\n' for event in self.dict.keys(): acceptorDict = self.dict[event] str = str + event + '\n' @@ -129,3 +163,7 @@ class Messenger: return str + + + +