import math from panda3d.core import ( CollisionNode, ConfigVariableBool, ConfigVariableString, CSDefault, GraphicsWindow, NodePath, Point3, TextNode, VBase3, VBase4, Vec3, Vec4, decomposeMatrix, deg2Rad, ) from .DirectUtil import useDirectRenderStyle from direct.showbase.DirectObject import DirectObject from direct.showbase.BulletinBoardGlobal import bulletinBoard as bboard from direct.task import Task from . import DirectGlobals as DG from .DirectCameraControl import DirectCameraControl from .DirectManipulation import DirectManipulationControl from .DirectSelection import SelectionRay, COA_ORIGIN, SelectedNodePaths from .DirectGrid import DirectGrid #from DirectGeometry import * from .DirectLights import DirectLights ## from direct.tkpanels import Placer ## from direct.tkwidgets import Slider ## from direct.tkwidgets import SceneGraphExplorer from direct.gui import OnscreenText from direct.interval.IntervalGlobal import Func, Sequence from direct.task.TaskManagerGlobal import taskMgr from direct.showbase.MessengerGlobal import messenger from direct.showbase import ShowBaseGlobal from direct.showbase.ShowBaseGlobal import ShowBase, hidden import builtins base: ShowBase class DirectSession(DirectObject): # post this to the bboard to make sure DIRECT doesn't turn on DIRECTdisablePost = 'disableDIRECT' cam: NodePath camera: NodePath oobeCamera: NodePath def __init__(self): # Establish a global pointer to the direct object early on # so dependant classes can access it in their code global direct, base base = ShowBaseGlobal.base base.direct = self setattr(builtins, 'direct', self) ShowBaseGlobal.direct = self # These come early since they are used later on self.group = base.render.attachNewNode('DIRECT') self.font = TextNode.getDefaultFont() self.fEnabled = 0 self.fEnabledLight = 0 self.fScaleWidgetByCam = 0 # [gjeon] flag for scaling widget by distance from the camera self.fIgnoreDirectOnlyKeyMap = 0 # [gjeon] to skip old direct controls in new LE self.drList = DisplayRegionList() self.iRayList = [x.iRay for x in self.drList] self.dr = self.drList[0] self.win: GraphicsWindow = base.win self.camera = base.camera self.cam = base.cam self.camNode = base.camNode self.trueCamera = self.camera self.iRay = self.dr.iRay self.coaMode = COA_ORIGIN self.cameraControl = DirectCameraControl() self.manipulationControl = DirectManipulationControl() self.useObjectHandles() self.grid = DirectGrid() self.grid.disable() self.lights = DirectLights(self.group) # Create some default lights self.lights.createDefaultLights() # But turn them off self.lights.allOff() # Initialize the collection of selected nodePaths self.selected = SelectedNodePaths() # Ancestry of currently selected object self.ancestry = [] self.ancestryIndex = 0 self.activeParent = None self.selectedNPReadout = OnscreenText.OnscreenText( pos = (0.1, 0.1), bg=Vec4(0, 0, 0, .2), style=3, fg = (1, 1, 1, 1), scale = 0.05, align = TextNode.ALeft, mayChange = 1, font = self.font) # Make sure readout is never lit or drawn in wireframe useDirectRenderStyle(self.selectedNPReadout) self.selectedNPReadout.reparentTo(hidden) self.activeParentReadout = OnscreenText.OnscreenText( pos = (0.1, 0.16), bg=Vec4(0, 0, 0, .2), style = 3, fg = (1, 1, 1, 1), scale = 0.05, align = TextNode.ALeft, mayChange = 1, font = self.font) # Make sure readout is never lit or drawn in wireframe useDirectRenderStyle(self.activeParentReadout) self.activeParentReadout.reparentTo(hidden) self.directMessageReadout = OnscreenText.OnscreenText( pos = (0.1, -0.1), bg=Vec4(0, 0, 0, .2), style = 3, fg = (1, 1, 1, 1), scale = 0.05, align = TextNode.ALeft, mayChange = 1, font = self.font) # Make sure readout is never lit or drawn in wireframe useDirectRenderStyle(self.directMessageReadout) self.directMessageReadout.reparentTo(hidden) # Create a vrpn client vrpn-server or default self.deviceManager = None self.joybox = None self.radamec = None self.fastrak = [] if ConfigVariableBool('want-vrpn', False): from direct.directdevices import DirectDeviceManager self.deviceManager = DirectDeviceManager.DirectDeviceManager() # Automatically create any devices specified in config file joybox = ConfigVariableString('vrpn-joybox-device', '').value radamec = ConfigVariableString('vrpn-radamec-device', '').value fastrak = ConfigVariableString('vrpn-fastrak-device', '').value if joybox: from direct.directdevices import DirectJoybox self.joybox = DirectJoybox.DirectJoybox(joybox) if radamec: from direct.directdevices import DirectRadamec self.radamec = DirectRadamec.DirectRadamec(radamec) if fastrak: from direct.directdevices import DirectFastrak # parse string into format device:N where N is the sensor name fastrak = fastrak.split() for i in range(len(fastrak))[1:]: self.fastrak.append(DirectFastrak.DirectFastrak(fastrak[0] + ':' + fastrak[i])) self.fControl = 0 self.fAlt = 0 self.fShift = 0 self.fMouse1 = 0 # [gjeon] to update alt key information while mouse1 is pressed self.fMouse2 = 0 self.fMouse3 = 0 self.pos = VBase3() self.hpr = VBase3() self.scale = VBase3() self.hitPt = Point3(0.0) # Lists for managing undo/redo operations self.undoList = [] self.redoList = [] # One run through the context task to init everything self.drList.updateContext() for dr in self.drList: dr.camUpdate() self.actionEvents = [ ['select', self.select], ['DIRECT-select', self.selectCB], ['deselect', self.deselect], ['deselectAll', self.deselectAll], ['DIRECT-preDeselectAll', self.deselectAllCB], ['highlightAll', self.selected.highlightAll], ['preRemoveNodePath', self.deselect], # Scene graph explorer functions ['SGE_Select', self.select], ['SGE_Deselect', self.deselect], ['SGE_Set Reparent Target', self.setActiveParent], ['SGE_Reparent', self.reparent], ['SGE_WRT Reparent', lambda np, s=self: s.reparent(np, fWrt = 1)], ['SGE_Flash', self.flash], ['SGE_Isolate', self.isolate], ['SGE_Toggle Vis', self.toggleVis], ['SGE_Show All', self.showAllDescendants], ['SGE_Fit', self.fitOnNodePath], ['SGE_Delete', self.removeNodePath], ['SGE_Set Name', self.getAndSetName], ['DIRECT-delete', self.removeAllSelected], ['DIRECT-Undo', self.undo], ['DIRECT-Redo', self.redo], ['DIRECT-OOBE', self.oobe], ['DIRECT-toggleWidgetVis', self.toggleWidgetVis], ['DIRECT-toggleWireframe', base.toggleWireframe], ['DIRECT-toggleVisAll', self.selected.toggleVisAll], ['DIRECT-toggleTexture', base.toggleTexture], ['DIRECT-upAncestry', self.upAncestry], ['DIRECT-downAncestry', self.downAncestry], ['DIRECT-toggleBackface', base.toggleBackface], ['DIRECT-flash', self.flash], ['DIRECT-toggleLigths', self.lights.toggle], ['DIRECT-toggleCOALock', self.cameraControl.toggleCOALock], ['DIRECT-setActiveParent', self.doSetActiveParent], ['DIRECT-doWrtReparent', self.doWrtReparent], ['DIRECT-doReparent', self.doReparent], ['DIRECT-doSelect', self.doSelect], ] if base.wantTk: from direct.tkpanels import Placer from direct.tkwidgets import Slider from direct.tkwidgets import SceneGraphExplorer self.actionEvents.extend([ ['SGE_Place', Placer.place], ['SGE_Set Color', Slider.rgbPanel], ['SGE_Explore', SceneGraphExplorer.explore],]) self.modifierEvents = ['control', 'control-up', 'control-repeat', 'shift', 'shift-up', 'shift-repeat', 'alt', 'alt-up', 'alt-repeat', ] keyList = [chr(i) for i in range(97, 123)] keyList.extend([chr(i) for i in range(48, 58)]) keyList.extend(["`", "-", "=", "[", "]", ";", "'", ",", ".", "/", "\\"]) self.specialKeys = ['escape', 'delete', 'page_up', 'page_down', 'enter'] def addCtrl(a): return "control-%s"%a def addShift(a): return "shift-%s"%a self.keyEvents = keyList[:] self.keyEvents.extend(list(map(addCtrl, keyList))) self.keyEvents.extend(list(map(addShift, keyList))) self.keyEvents.extend(self.specialKeys) self.mouseEvents = ['mouse1', 'mouse1-up', 'shift-mouse1', 'shift-mouse1-up', 'control-mouse1', 'control-mouse1-up', 'alt-mouse1', 'alt-mouse1-up', 'mouse2', 'mouse2-up', 'shift-mouse2', 'shift-mouse2-up', 'control-mouse2', 'control-mouse2-up', 'alt-mouse2', 'alt-mouse2-up', 'mouse3', 'mouse3-up', 'shift-mouse3', 'shift-mouse3-up', 'control-mouse3', 'control-mouse3-up', 'alt-mouse3', 'alt-mouse3-up', ] self.directOnlyKeyMap = { 'u': ('Orbit Upright Camera', 'DIRECT-orbitUprightCam'), 'shift-u': ('Upright Camera', 'DIRECT-uprightCam'), '1': ('Move Camera to View 1', 'DIRECT-spwanMoveToView-1'), '2': ('Move Camera to View 2', 'DIRECT-spwanMoveToView-2'), '3': ('Move Camera to View 3', 'DIRECT-spwanMoveToView-3'), '4': ('Move Camera to View 4', 'DIRECT-spwanMoveToView-4'), '5': ('Move Camera to View 5', 'DIRECT-spwanMoveToView-5'), '6': ('Move Camera to View 6', 'DIRECT-spwanMoveToView-6'), '7': ('Move Camera to View 7', 'DIRECT-spwanMoveToView-7'), '8': ('Move Camera to View 8', 'DIRECT-spwanMoveToView-8'), '9': ('Rotate Camera About widget 90 degrees Counterclockwise', 'DIRECT-swingCamAboutWidget-0'), '0': ('Rotate Camera About widget 90 degrees Clockwise', 'DIRECT-swingCamAboutWidget-1'), '`': ('Remove ManipulateCameraTask', 'DIRECT-removeManipulateCameraTask'), '=': ('Zoom In', 'DIRECT-zoomInCam'), 'shift-=': ('Zoom In', 'DIRECT-zoomInCam'), 'shift-_': ('Zoom Out', 'DIRECT-zoomOutCam'), '-': ('Zoom Out', 'DIRECT-zoomOutCam'), 'o': ('Toggle OOBE', 'DIRECT-OOBE'), '[': ('DIRECT-Undo', 'DIRECT-Undo'), 'shift-[': ('DIRECT-Undo', 'DIRECT-Undo'), ']': ('DIRECT-Redo', 'DIRECT-Redo'), 'shift-]': ('DIRECT-Redo', 'DIRECT-Redo'), } self.hotKeyMap = { 'c': ('Center Camera', 'DIRECT-centerCamIn'), 'f': ('Fit on Widget', 'DIRECT-fitOnWidget'), 'h': ('Move Camera to ', 'DIRECT-homeCam'), 'shift-v': ('Toggle Marker', 'DIRECT-toggleMarkerVis'), 'm': ('Move to fit', 'DIRECT-moveToFit'), 'n': ('Pick Next COA', 'DIRECT-pickNextCOA'), 'delete': ('Delete', 'DIRECT-delete'), '.': ('Scale Up Widget', 'DIRECT-widgetScaleUp'), ',': ('Scale Down Widget', 'DIRECT-widgetScaleDown'), 'page_up': ('Up Ancestry', 'DIRECT-upAncestry'), 'page_down': ('Down Ancestry', 'DIRECT-downAncestry'), 'escape': ('Deselect All', 'deselectAll'), 'v': ('Toggle Manipulating Widget', 'DIRECT-toggleWidgetVis'), 'b': ('Toggle Backface', 'DIRECT-toggleBackface'), 'control-f': ('Flash', 'DIRECT-flash'), 'l': ('Toggle lights', 'DIRECT-toggleLigths'), 'shift-l': ('Toggle COA Lock', 'DIRECT-toggleCOALock'), 'p': ('Set Active Parent', 'DIRECT-setActiveParent'), 'r': ('Wrt Reparent', 'DIRECT-doWrtReparent'), 'shift-r': ('Reparent', 'DIRECT-doReparent'), 's': ('Select', 'DIRECT-doSelect'), 't': ('Toggle Textures', 'DIRECT-toggleTexture'), 'shift-a': ('Toggle Vis all', 'DIRECT-toggleVisAll'), 'w': ('Toggle Wireframe', 'DIRECT-toggleWireframe'), 'control-z': ('Undo', 'LE-Undo'), 'shift-z': ('Redo', 'LE-Redo'), 'control-d': ('Duplicate', 'LE-Duplicate'), 'control-l': ('Make Live', 'LE-MakeLive'), 'control-n': ('New Scene', 'LE-NewScene'), 'control-s': ('Save Scene', 'LE-SaveScene'), 'control-o': ('Open Scene', 'LE-OpenScene'), 'control-q': ('Quit', 'LE-Quit'), } self.speicalKeyMap = { 'enter': 'DIRECT-enter', } self.passThroughKeys = ['v','b','l','p', 'r', 'shift-r', 's', 't','shift-a', 'w'] if base.wantTk: from direct.tkpanels import DirectSessionPanel self.panel = DirectSessionPanel.DirectSessionPanel(parent = base.tkRoot) clusterMode: str if hasattr(builtins, 'clusterMode'): # Has the clusterMode been set externally (i.e. via the # bootstrap application? clusterMode = builtins.clusterMode else: # Has the clusterMode been set via a config variable? clusterMode = ConfigVariableString("cluster-mode", '').value self.clusterMode = clusterMode if self.clusterMode == 'client': from direct.cluster.ClusterClient import createClusterClient self.cluster = createClusterClient() elif self.clusterMode == 'server': from direct.cluster.ClusterServer import ClusterServer self.cluster = ClusterServer(base.camera, base.cam) else: from direct.cluster.ClusterClient import DummyClusterClient self.cluster = DummyClusterClient() setattr(builtins, 'cluster', self.cluster) def addPassThroughKey(self,key): self.passThroughKeys.append(key) def enable(self): # don't enable DIRECT if someone has posted DIRECTdisablePost if bboard.has(DirectSession.DIRECTdisablePost): return if self.fEnabled: return # Make sure old tasks are shut down self.disable() # Start all display region context tasks self.drList.spawnContextTask() if not self.fEnabledLight: # Turn on mouse Flying self.cameraControl.enableMouseFly() # Turn on object manipulation self.manipulationControl.enableManipulation() # Make sure list of selected items is reset self.selected.reset() # Accept appropriate hooks if not self.fEnabledLight: self.enableKeyEvents() self.enableMouseEvents() self.enableActionEvents() self.enableModifierEvents() # Set flag self.fEnabled = 1 def enableLight(self): self.fEnabledLight = 1 self.enable() def disable(self): # Shut down all display region context tasks self.drList.removeContextTask() # Turn off camera fly self.cameraControl.disableMouseFly() # Turn off object manipulation self.deselectAll() self.manipulationControl.disableManipulation() self.disableKeyEvents() self.disableModifierEvents() self.disableMouseEvents() self.disableActionEvents() # Kill tasks taskMgr.remove('flashNodePath') taskMgr.remove('hideDirectMessage') taskMgr.remove('hideDirectMessageLater') # Set flag self.fEnabled = 0 def toggleDirect(self): if self.fEnabled: self.disable() else: self.enable() def minimumConfiguration(self): # Remove context task self.drList.removeContextTask() # Turn off camera fly self.cameraControl.disableMouseFly() # Ignore keyboard and action events self.disableKeyEvents() self.disableActionEvents() # But let mouse events pass through self.enableMouseEvents() self.enableModifierEvents() def oobe(self): # If oobeMode was never set, set it to false and create the # structures we need to implement OOBE. if not hasattr(self, 'oobeMode'): self.oobeMode = 0 self.oobeCamera = hidden.attachNewNode('oobeCamera') self.oobeVis = base.loader.loadModel('models/misc/camera') if self.oobeVis: self.oobeVis.node().setFinal(1) if self.oobeMode: # Position a target point to lerp the oobe camera to self.cameraControl.camManipRef.setPosHpr(self.trueCamera, 0, 0, 0, 0, 0, 0) ival = self.oobeCamera.posHprInterval( 2.0, pos = Point3(0), hpr = Vec3(0), other = self.cameraControl.camManipRef, blendType = 'easeInOut') ival = Sequence(ival, Func(self.endOOBE), name = 'oobeTransition') ival.start() else: # Place camera marker at true camera location self.oobeVis.reparentTo(self.trueCamera) # Remove any transformation on the models arc self.oobeVis.clearMat() # Make oobeCamera be a sibling of wherever camera is now. cameraParent = self.camera.getParent() # Prepare oobe camera self.oobeCamera.reparentTo(cameraParent) self.oobeCamera.setPosHpr(self.trueCamera, 0, 0, 0, 0, 0, 0) # Put camera under new oobe camera self.cam.reparentTo(self.oobeCamera) # Position a target point to lerp the oobe camera to self.cameraControl.camManipRef.setPos( self.trueCamera, Vec3(-2, -20, 5)) self.cameraControl.camManipRef.lookAt(self.trueCamera) ival = self.oobeCamera.posHprInterval( 2.0, pos = Point3(0), hpr = Vec3(0), other = self.cameraControl.camManipRef, blendType = 'easeInOut') ival = Sequence(ival, Func(self.beginOOBE), name = 'oobeTransition') ival.start() def beginOOBE(self): # Make sure we've reached our final destination self.oobeCamera.setPosHpr(self.cameraControl.camManipRef, 0, 0, 0, 0, 0, 0) self.camera = self.oobeCamera self.oobeMode = 1 def endOOBE(self): # Make sure we've reached our final destination self.oobeCamera.setPosHpr(self.trueCamera, 0, 0, 0, 0, 0, 0) # Disable OOBE mode. self.cam.reparentTo(self.trueCamera) self.camera = self.trueCamera # Get rid of ancillary node paths self.oobeVis.reparentTo(hidden) self.oobeCamera.reparentTo(hidden) self.oobeMode = 0 def destroy(self): self.disable() def reset(self): self.enable() # EVENT FUNCTIONS def enableActionEvents(self): for event in self.actionEvents: self.accept(event[0], event[1], extraArgs = event[2:]) def enableModifierEvents(self): for event in self.modifierEvents: self.accept(event, self.inputHandler, [event]) def enableKeyEvents(self): for event in self.keyEvents: self.accept(event, self.inputHandler, [event]) def enableMouseEvents(self): for event in self.mouseEvents: self.accept(event, self.inputHandler, [event]) def disableActionEvents(self): for event, method in self.actionEvents: self.ignore(event) def disableModifierEvents(self): for event in self.modifierEvents: self.ignore(event) def disableKeyEvents(self): for event in self.keyEvents: self.ignore(event) def disableMouseEvents(self): for event in self.mouseEvents: self.ignore(event) def inputHandler(self, input): if not hasattr(self, 'oobeMode') or self.oobeMode == 0: # [gjeon] change current camera dr, iRay, mouseWatcher accordingly to support multiple windows if self.manipulationControl.fMultiView: # handling orphan events if self.fMouse1 and 'mouse1' not in input or\ self.fMouse2 and 'mouse2' not in input or\ self.fMouse3 and 'mouse3' not in input: if input.endswith('-up') or\ input not in self.modifierEvents: # to handle orphan events return if (self.fMouse1 == 0 and 'mouse1-up' in input) or\ (self.fMouse2 == 0 and 'mouse2-up' in input) or\ (self.fMouse3 == 0 and 'mouse3-up' in input): # to handle orphan events return if (self.fMouse1 or self.fMouse2 or self.fMouse3) and\ input[4:7] != self.camera.getName()[:3] and\ input.endswith('-up'): # to handle orphan events return winCtrl = None possibleWinCtrls = [] for cWinCtrl in base.winControls: if cWinCtrl.mouseWatcher.node().hasMouse(): possibleWinCtrls.append(cWinCtrl) if len(possibleWinCtrls) == 1: winCtrl = possibleWinCtrls[0] elif len(possibleWinCtrls) > 1: for cWinCtrl in possibleWinCtrls: if (input.endswith('-up') and\ not input in self.modifierEvents and\ not input in self.mouseEvents) or\ (input in self.mouseEvents): if input[4:7] == cWinCtrl.camera.getName()[:3]: winCtrl = cWinCtrl else: if input[4:7] != cWinCtrl.camera.getName()[:3]: winCtrl = cWinCtrl if winCtrl is None: return if input not in self.modifierEvents: self.win = winCtrl.win self.camera = winCtrl.camera self.trueCamera = self.camera self.cam = NodePath(winCtrl.camNode) self.camNode = winCtrl.camNode if hasattr(winCtrl, 'grid'): self.grid = winCtrl.grid self.dr = self.drList[base.camList.index(NodePath(winCtrl.camNode))] self.iRay = self.dr.iRay base.mouseWatcher = winCtrl.mouseWatcher base.mouseWatcherNode = winCtrl.mouseWatcher.node() self.dr.mouseUpdate() DG.LE_showInOneCam(self.selectedNPReadout, self.camera.getName()) self.widget = self.manipulationControl.widgetList[base.camList.index(NodePath(winCtrl.camNode))] input = input[8:] # get rid of camera prefix if self.fAlt and 'alt' not in input and not input.endswith('-up'): input = 'alt-' + input if input.endswith('-repeat'): input = input[:-7] # Deal with keyboard and mouse input if input in self.hotKeyMap: keyDesc = self.hotKeyMap[input] messenger.send(keyDesc[1]) elif input in self.speicalKeyMap: messenger.send(self.speicalKeyMap[input]) elif input in self.directOnlyKeyMap: if self.fIgnoreDirectOnlyKeyMap: return keyDesc = self.directOnlyKeyMap[input] messenger.send(keyDesc[1]) elif input == 'mouse1-up': self.fMouse1 = 0 # [gjeon] to update alt key information while mouse1 is pressed messenger.send('DIRECT-mouse1Up') elif input.find('mouse1') != -1: self.fMouse1 = 1 # [gjeon] to update alt key information while mouse1 is pressed modifiers = self.getModifiers(input, 'mouse1') messenger.send('DIRECT-mouse1', sentArgs = [modifiers]) elif input == 'mouse2-up': self.fMouse2 = 0 messenger.send('DIRECT-mouse2Up') elif input.find('mouse2') != -1: self.fMouse2 = 1 modifiers = self.getModifiers(input, 'mouse2') messenger.send('DIRECT-mouse2', sentArgs = [modifiers]) elif input == 'mouse3-up': self.fMouse3 = 0 messenger.send('DIRECT-mouse3Up') elif input.find('mouse3') != -1: self.fMouse3 = 1 modifiers = self.getModifiers(input, 'mouse3') messenger.send('DIRECT-mouse3', sentArgs = [modifiers]) elif input == 'shift': self.fShift = 1 elif input == 'shift-up': self.fShift = 0 elif input == 'control': self.fControl = 1 # [gjeon] to update control key information while mouse1 is pressed if self.fMouse1: modifiers = DG.DIRECT_NO_MOD modifiers |= DG.DIRECT_CONTROL_MOD messenger.send('DIRECT-mouse1', sentArgs = [modifiers]) elif input == 'control-up': self.fControl = 0 elif input == 'alt': if self.fAlt: return self.fAlt = 1 # [gjeon] to update alt key information while mouse1 is pressed if self.fMouse1: modifiers = DG.DIRECT_NO_MOD modifiers |= DG.DIRECT_ALT_MOD messenger.send('DIRECT-mouse1', sentArgs = [modifiers]) elif self.fMouse2: modifiers = DG.DIRECT_NO_MOD modifiers |= DG.DIRECT_ALT_MOD messenger.send('DIRECT-mouse2', sentArgs = [modifiers]) elif self.fMouse3: modifiers = DG.DIRECT_NO_MOD modifiers |= DG.DIRECT_ALT_MOD messenger.send('DIRECT-mouse3', sentArgs = [modifiers]) elif input == 'alt-up': self.fAlt = 0 #Pass along certain events if this display is a cluster client if self.clusterMode == 'client': if input in self.passThroughKeys: self.cluster('messenger.send("%s")' % input, 0) def doSetActiveParent(self): if self.selected.last: self.setActiveParent(self.selected.last) def doReparent(self): if self.selected.last: self.reparent(self.selected.last) def doWrtReparent(self): if self.selected.last: self.reparent(self.selected.last, fWrt = 1) def doSelect(self): if self.selected.last: self.select(self.selected.last) def getModifiers(self, input, base): modifiers = DG.DIRECT_NO_MOD modifierString = input[: input.find(base)] if modifierString.find('shift') != -1: modifiers |= DG.DIRECT_SHIFT_MOD if modifierString.find('control') != -1: modifiers |= DG.DIRECT_CONTROL_MOD if modifierString.find('alt') != -1: modifiers |= DG.DIRECT_ALT_MOD return modifiers def gotShift(self, modifiers): return modifiers & DG.DIRECT_SHIFT_MOD def gotControl(self, modifiers): return modifiers & DG.DIRECT_CONTROL_MOD def gotAlt(self, modifiers): return modifiers & DG.DIRECT_ALT_MOD def setFScaleWidgetByCam(self, flag): self.fScaleWidgetByCam = flag if flag: taskMgr.add(self.widgetResizeTask, 'DIRECTWidgetResize') else: taskMgr.remove('DIRECTWidgetResize') def widgetResizeTask(self, state): if not taskMgr.hasTaskNamed('resizeObjectHandles'): dnp = self.selected.last if dnp: if self.manipulationControl.fMultiView: for i in range(3): sf = 30.0 * self.drList[i].orthoFactor self.manipulationControl.widgetList[i].setDirectScalingFactor(sf) nodeCamDist = Vec3(dnp.getPos(base.camList[3])).length() sf = 0.075 * nodeCamDist * math.tan(deg2Rad(self.drList[3].fovV)) self.manipulationControl.widgetList[3].setDirectScalingFactor(sf) else: nodeCamDist = Vec3(dnp.getPos(self.camera)).length() sf = 0.075 * nodeCamDist * math.tan(deg2Rad(self.drList.getCurrentDr().fovV)) self.widget.setDirectScalingFactor(sf) return Task.cont def select(self, nodePath, fMultiSelect = 0, fSelectTag = 1, fResetAncestry = 1, fLEPane=0, fUndo=1): messenger.send('DIRECT-select', [nodePath, fMultiSelect, fSelectTag, fResetAncestry, fLEPane, fUndo]) def selectCB(self, nodePath, fMultiSelect = 0, fSelectTag = 1, fResetAncestry = 1, fLEPane = 0, fUndo=1): dnp = self.selected.select(nodePath, fMultiSelect, fSelectTag) if dnp: messenger.send('DIRECT_preSelectNodePath', [dnp]) if fResetAncestry: # Update ancestry self.ancestry = dnp.getAncestors() self.ancestryIndex = 0 # Update the selectedNPReadout self.selectedNPReadout.reparentTo(base.a2dBottomLeft) self.selectedNPReadout.setText( 'Selected:' + dnp.getName()) # Show the manipulation widget if self.manipulationControl.fMultiView: for widget in self.manipulationControl.widgetList: widget.showWidget() else: self.widget.showWidget() editTypes = self.manipulationControl.getEditTypes([dnp]) if (editTypes & DG.EDIT_TYPE_UNEDITABLE) == DG.EDIT_TYPE_UNEDITABLE: self.manipulationControl.disableWidgetMove() else: self.manipulationControl.enableWidgetMove() # Update camera controls coa to this point # Coa2Camera = Coa2Dnp * Dnp2Camera mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(self.camera) row = mCoa2Camera.getRow(3) coa = Vec3(row[0], row[1], row[2]) self.cameraControl.updateCoa(coa) # Adjust widgets size # This uses the additional scaling factor used to grow and # shrink the widget if not self.fScaleWidgetByCam: # [gjeon] for not scaling widget by distance from camera if self.manipulationControl.fMultiView: for widget in self.manipulationControl.widgetList: widget.setScalingFactor(dnp.getRadius()) else: self.widget.setScalingFactor(dnp.getRadius()) # Spawn task to have object handles follow the selected object taskMgr.remove('followSelectedNodePath') t = Task.Task(self.followSelectedNodePathTask) t.dnp = dnp taskMgr.add(t, 'followSelectedNodePath') # Send an message marking the event messenger.send('DIRECT_selectedNodePath', [dnp]) messenger.send('DIRECT_selectedNodePath_fMulti_fTag', [dnp, fMultiSelect, fSelectTag]) messenger.send('DIRECT_selectedNodePath_fMulti_fTag_fLEPane', [dnp, fMultiSelect, fSelectTag, fLEPane]) def followSelectedNodePathTask(self, state): mCoa2Render = state.dnp.mCoa2Dnp * state.dnp.getMat(base.render) decomposeMatrix(mCoa2Render, self.scale, self.hpr, self.pos, CSDefault) self.widget.setPosHpr(self.pos, self.hpr) return Task.cont def deselect(self, nodePath): dnp = self.selected.deselect(nodePath) if dnp: # Hide the manipulation widget if self.manipulationControl.fMultiView: for widget in self.manipulationControl.widgetList: widget.hideWidget() else: self.widget.hideWidget() self.selectedNPReadout.reparentTo(hidden) self.selectedNPReadout.setText(' ') taskMgr.remove('followSelectedNodePath') self.ancestry = [] # Send an message marking the event messenger.send('DIRECT_deselectedNodePath', [dnp]) def deselectAll(self): messenger.send('DIRECT-preDeselectAll') def deselectAllCB(self): self.selected.deselectAll() # Hide the manipulation widget if self.manipulationControl.fMultiView: for widget in self.manipulationControl.widgetList: widget.hideWidget() else: self.widget.hideWidget() self.selectedNPReadout.reparentTo(hidden) self.selectedNPReadout.setText(' ') taskMgr.remove('followSelectedNodePath') messenger.send('DIRECT_deselectAll') def setActiveParent(self, nodePath = None): # Record new parent self.activeParent = nodePath # Update the activeParentReadout self.activeParentReadout.reparentTo(base.a2dBottomLeft) self.activeParentReadout.setText( 'Active Reparent Target:' + nodePath.getName()) # Alert everyone else messenger.send('DIRECT_activeParent', [self.activeParent]) def reparent(self, nodePath = None, fWrt = 0): if (nodePath and self.activeParent and self.isNotCycle(nodePath, self.activeParent)): oldParent = nodePath.getParent() if fWrt: nodePath.wrtReparentTo(self.activeParent) else: nodePath.reparentTo(self.activeParent) # Alert everyone else messenger.send('DIRECT_reparent', [nodePath, oldParent, self.activeParent]) # [gjeon] for others who needs fWrt information messenger.send('DIRECT_reparent_fWrt', [nodePath, oldParent, self.activeParent, fWrt]) def isNotCycle(self, nodePath, parent): if nodePath == parent: print('DIRECT.reparent: Invalid parent') return 0 elif parent.hasParent(): return self.isNotCycle(nodePath, parent.getParent()) else: return 1 def flash(self, nodePath = 'None Given'): """ Highlight an object by setting it red for a few seconds """ # Clean up any existing task taskMgr.remove('flashNodePath') # Spawn new task if appropriate if nodePath == 'None Given': # If nothing specified, try selected node path nodePath = self.selected.last if nodePath: if nodePath.hasColor(): doneColor = nodePath.getColor() flashColor = VBase4(1) - doneColor flashColor.setW(1) else: doneColor = None flashColor = VBase4(1, 0, 0, 1) # Temporarily set node path color nodePath.setColor(flashColor) # Clean up color in a few seconds t = taskMgr.doMethodLater(DG.DIRECT_FLASH_DURATION, # This is just a dummy task self.flashDummy, 'flashNodePath',) t.nodePath = nodePath t.doneColor = doneColor # This really does all the work t.setUponDeath(self.flashDone) def flashDummy(self, state): # Real work is done in upon death function return Task.done def flashDone(self, state): # Return node Path to original state if state.nodePath.isEmpty(): # Node path doesn't exist anymore, bail return if state.doneColor: state.nodePath.setColor(state.doneColor) else: state.nodePath.clearColor() def fitOnNodePath(self, nodePath = 'None Given'): if nodePath == 'None Given': # If nothing specified, try selected node path nodePath = self.selected.last self.select(nodePath) def fitTask(state, self = self): self.cameraControl.fitOnWidget() return Task.done taskMgr.doMethodLater(0.1, fitTask, 'manipulateCamera') def isolate(self, nodePath = 'None Given'): """ Show a node path and hide its siblings """ # First kill the flashing task to avoid complications taskMgr.remove('flashNodePath') # Use currently selected node path if node selected if nodePath == 'None Given': nodePath = self.selected.last # Do we have a node path? if nodePath: # Yes, show everything in level self.showAllDescendants(nodePath.getParent()) # Now hide all of this node path's siblings for sib in nodePath.getParent().getChildren(): if sib.node() != nodePath.node(): sib.hide() def toggleVis(self, nodePath = 'None Given'): """ Toggle visibility of node path """ # First kill the flashing task to avoid complications taskMgr.remove('flashNodePath') if nodePath == 'None Given': # If nothing specified, try selected node path nodePath = self.selected.last if nodePath: # Now toggle node path's visibility state if nodePath.isHidden(): nodePath.show() else: nodePath.hide() def removeNodePath(self, nodePath = 'None Given'): if nodePath == 'None Given': # If nothing specified, try selected node path nodePath = self.selected.last if nodePath: nodePath.removeNode() def removeAllSelected(self): self.selected.removeAll() def showAllDescendants(self, nodePath = None): """ Show the level and its descendants """ if nodePath is None: nodePath = base.render if not isinstance(nodePath, CollisionNode): nodePath.show() for child in nodePath.getChildren(): self.showAllDescendants(child) def upAncestry(self): if self.ancestry: l = len(self.ancestry) i = self.ancestryIndex + 1 if i < l: np = self.ancestry[i] name = np.getName() if (name != 'render') and (name != 'renderTop'): self.ancestryIndex = i self.select(np, 0, 0) self.flash(np) def downAncestry(self): if self.ancestry: l = len(self.ancestry) i = self.ancestryIndex - 1 if i >= 0: np = self.ancestry[i] name = np.getName() if (name != 'render') and (name != 'renderTop'): self.ancestryIndex = i self.select(np, 0, 0) self.flash(np) def getAndSetName(self, nodePath): """ Prompt user for new node path name """ from tkinter.simpledialog import askstring newName = askstring('Node Path: ' + nodePath.getName(), 'Enter new name:') if newName: nodePath.setName(newName) messenger.send('DIRECT_nodePathSetName', [nodePath, newName]) # UNDO REDO FUNCTIONS def pushUndo(self, nodePathList, fResetRedo = 1): # Assemble group of changes undoGroup = [] for nodePath in nodePathList: t = nodePath.getTransform() undoGroup.append([nodePath, t]) # Now record group self.undoList.append(undoGroup) # Truncate list self.undoList = self.undoList[-25:] # Alert anyone who cares messenger.send('DIRECT_pushUndo') if fResetRedo and (nodePathList != []): self.redoList = [] messenger.send('DIRECT_redoListEmpty') def popUndoGroup(self): # Get last item undoGroup = self.undoList[-1] # Strip last item off of undo list self.undoList = self.undoList[:-1] # Update state of undo button if not self.undoList: messenger.send('DIRECT_undoListEmpty') # Return last item return undoGroup def pushRedo(self, nodePathList): # Assemble group of changes redoGroup = [] for nodePath in nodePathList: t = nodePath.getTransform() redoGroup.append([nodePath, t]) # Now record redo group self.redoList.append(redoGroup) # Truncate list self.redoList = self.redoList[-25:] # Alert anyone who cares messenger.send('DIRECT_pushRedo') def popRedoGroup(self): # Get last item redoGroup = self.redoList[-1] # Strip last item off of redo list self.redoList = self.redoList[:-1] # Update state of redo button if not self.redoList: messenger.send('DIRECT_redoListEmpty') # Return last item return redoGroup def undo(self): if self.undoList: # Get last item off of redo list undoGroup = self.popUndoGroup() # Record redo information nodePathList = [x[0] for x in undoGroup] self.pushRedo(nodePathList) # Now undo xform for group for pose in undoGroup: # Undo xform pose[0].setTransform(pose[1]) # Alert anyone who cares messenger.send('DIRECT_undo', [nodePathList]) def redo(self): if self.redoList: # Get last item off of redo list redoGroup = self.popRedoGroup() # Record undo information nodePathList = [x[0] for x in redoGroup] self.pushUndo(nodePathList, fResetRedo = 0) # Redo xform for pose in redoGroup: pose[0].setTransform(pose[1]) # Alert anyone who cares messenger.send('DIRECT_redo', [nodePathList]) # UTILITY FUNCTIONS def message(self, text): taskMgr.remove('hideDirectMessage') taskMgr.remove('hideDirectMessageLater') self.directMessageReadout.reparentTo(base.a2dTopLeft) self.directMessageReadout.setText(text) self.hideDirectMessageLater() def hideDirectMessageLater(self): taskMgr.doMethodLater(3.0, self.hideDirectMessage, 'hideDirectMessage') def hideDirectMessage(self, state): self.directMessageReadout.reparentTo(hidden) return Task.done def useObjectHandles(self): self.widget = self.manipulationControl.objectHandles self.widget.reparentTo(self.group) def hideSelectedNPReadout(self): self.selectedNPReadout.reparentTo(hidden) def hideActiveParentReadout(self): self.activeParentReadout.reparentTo(hidden) def toggleWidgetVis(self): self.widget.toggleWidget() def setCOAMode(self, mode): self.coaMode = mode def isEnabled(self): return self.fEnabled def addUnpickable(self, item): for iRay in self.iRayList: iRay.addUnpickable(item) def removeUnpickable(self, item): for iRay in self.iRayList: iRay.removeUnpickable(item) class DisplayRegionContext(DirectObject): regionCount = 0 def __init__(self, cam): self.cam = cam self.camNode = self.cam.node() self.camLens = self.camNode.getLens() # set lens change callback changeEvent = 'dr%d-change-event' % DisplayRegionContext.regionCount DisplayRegionContext.regionCount += 1 self.camLens.setChangeEvent(changeEvent) self.accept(changeEvent, self.camUpdate) self.iRay = SelectionRay(self.cam) self.nearVec = Vec3(0) self.mouseX = 0.0 self.mouseY = 0.0 self.orthoFactor = 0.1 # A Camera node can have more than one display region # associated with it. Here I assume that there is only # one display region per camera, since we are defining a # display region on a per-camera basis. See note in # DisplayRegionList.__init__() self.dr = self.camNode.getDisplayRegion(0) left = self.dr.getLeft() right = self.dr.getRight() bottom = self.dr.getBottom() top = self.dr.getTop() self.originX = left+right-1 self.originY = top+bottom-1 self.scaleX = 1.0/(right-left) self.scaleY = 1.0/(top-bottom) self.setOrientation() self.camUpdate() def __getitem__(self, key): return self.__dict__[key] def setOrientation(self): # MRM This assumes orientation is set on transform above cam hpr = self.cam.getHpr() if hpr[2] < 135 and hpr[2]>45 or hpr[2]>225 and hpr[2]<315: self.isSideways = 1 elif hpr[2] > -135 and hpr[2] < -45 or hpr[2] < -225 and hpr[2] > -315: self.isSideways = 1 else: self.isSideways = 0 # The following take into consideration sideways displays def getHfov(self): if self.isSideways: return self.camLens.getVfov() else: return self.camLens.getHfov() def getVfov(self): if self.isSideways: return self.camLens.getHfov() else: return self.camLens.getVfov() def setHfov(self, hfov): if self.isSideways: self.camLens.setFov(self.camLens.getHfov(), hfov) else: self.camLens.setFov(hfov, self.camLens.getVfov()) def setVfov(self, vfov): if self.isSideways: self.camLens.setFov(vfov, self.camLens.getVfov()) else: self.camLens.setFov(self.camLens.getHfov(), vfov) def setFov(self, hfov, vfov): if self.isSideways: self.camLens.setFov(vfov, hfov) else: self.camLens.setFov(hfov, vfov) def getWidth(self): prop = ShowBaseGlobal.direct.win.getProperties() if prop.hasSize(): return prop.getXSize() else: return 640 def getHeight(self): prop = ShowBaseGlobal.direct.win.getProperties() if prop.hasSize(): return prop.getYSize() else: return 480 def updateFilmSize(self, width, height): if self.camLens.__class__.__name__ == "OrthographicLens": width *= self.orthoFactor height *= self.orthoFactor self.camLens.setFilmSize(width, height) def camUpdate(self, lens = None): # Window Data self.near = self.camLens.getNear() self.far = self.camLens.getFar() self.fovH = self.camLens.getHfov() self.fovV = self.camLens.getVfov() self.nearWidth = math.tan(deg2Rad(self.fovH * 0.5)) * self.near * 2.0 self.nearHeight = math.tan(deg2Rad(self.fovV * 0.5)) * self.near * 2.0 self.left = -self.nearWidth * 0.5 self.right = self.nearWidth * 0.5 self.top = self.nearHeight * 0.5 self.bottom = -self.nearHeight * 0.5 def mouseUpdate(self): # Mouse Data # Last frame self.mouseLastX = self.mouseX self.mouseLastY = self.mouseY # Values for this frame # This ranges from -1 to 1 mouseWatcherNode = base.mouseWatcherNode if mouseWatcherNode and mouseWatcherNode.hasMouse(): self.mouseX = mouseWatcherNode.getMouseX() self.mouseY = mouseWatcherNode.getMouseY() self.mouseX = (self.mouseX-self.originX)*self.scaleX self.mouseY = (self.mouseY-self.originY)*self.scaleY # Delta percent of window the mouse moved self.mouseDeltaX = self.mouseX - self.mouseLastX self.mouseDeltaY = self.mouseY - self.mouseLastY self.nearVec.set((self.nearWidth*0.5) * self.mouseX, self.near, (self.nearHeight*0.5) * self.mouseY) class DisplayRegionList(DirectObject): def __init__(self): self.displayRegionList = [] i = 0 # Things are funky if we are oobe if getattr(base, 'oobeMode', False): # assume we only have one cam at this point drc = DisplayRegionContext(base.cam) self.displayRegionList.append(drc) else: # MRM: Doesn't properly handle multiple camera groups anymore # Assumes everything is under main camera # This is following the old way of setting up # display regions. A display region is set up for # each camera node in the scene graph. This was done # so that only display regions in the scene graph are # considered. The right way to do this is to set up # a display region for each real display region, and then # keep track of which are currently active (e.g. use a flag) # processing only them. for camIndex, cam in enumerate(base.camList): if cam.name == '': cam.name = f'Camera{camIndex}' drc = DisplayRegionContext(cam) self.displayRegionList.append(drc) self.accept("DIRECT-mouse1", self.mouseUpdate) self.accept("DIRECT-mouse2", self.mouseUpdate) self.accept("DIRECT-mouse3", self.mouseUpdate) self.accept("DIRECT-mouse1Up", self.mouseUpdate) self.accept("DIRECT-mouse2Up", self.mouseUpdate) self.accept("DIRECT-mouse3Up", self.mouseUpdate) self.tryToGetCurrentDr = True def __getitem__(self, index): return self.displayRegionList[index] def __len__(self): return len(self.displayRegionList) def __iter__(self): return iter(self.displayRegionList) def updateContext(self): self.contextTask(None) def setNearFar(self, near, far): for dr in self.displayRegionList: dr.camLens.setNearFar(near, far) def setNear(self, near): for dr in self.displayRegionList: dr.camLens.setNear(near) def setFar(self, far): for dr in self.displayRegionList: dr.camLens.setFar(far) def setFov(self, hfov, vfov): for dr in self.displayRegionList: dr.setFov(hfov, vfov) def setHfov(self, fov): for dr in self.displayRegionList: dr.setHfov(fov) def setVfov(self, fov): for dr in self.displayRegionList: dr.setVfov(fov) def mouseUpdate(self, modifiers = DG.DIRECT_NO_MOD): for dr in self.displayRegionList: dr.mouseUpdate() #base.direct.dr = self.getCurrentDr() def getCurrentDr(self): if not self.tryToGetCurrentDr: return ShowBaseGlobal.direct.dr for dr in self.displayRegionList: if (dr.mouseX >= -1.0 and dr.mouseX <= 1.0 and dr.mouseY >= -1.0 and dr.mouseY <= 1.0): return dr return self.displayRegionList[0] def start(self): # First shutdown any existing task self.stop() # Start a new context task self.spawnContextTask() def stop(self): # Kill the existing context task taskMgr.remove('DIRECTContextTask') def spawnContextTask(self): taskMgr.add(self.contextTask, 'DIRECTContextTask') def removeContextTask(self): taskMgr.remove('DIRECTContextTask') def contextTask(self, state): # Window Data self.mouseUpdate() # hack to test movement return Task.cont def addDisplayRegionContext(self, cam): self.displayRegionList.append(DisplayRegionContext(cam)) def removeDisplayRegionContext(self, cam): for drc in self.displayRegionList: if drc.cam == cam: self.displayRegionList.remove(drc) break