class ButtonWatcher(DirectObject): def __init__(self, keys): self.modKeys = ModifierButtons() for key in keys: self.modKeys.addButton(key) self.accept(key.getName(), self.modKeys.buttonDown, [key]) self.accept(key.getName()+'-up', self.modKeys.buttonUp, [key]) def getKeys(self): return self.modKeys def destroy(self): self.ignoreAll()
class ButtonWatcher(DirectObject): def __init__(self, keys): self.modKeys = ModifierButtons() for key in keys: self.modKeys.addButton(key) self.accept(key.getName(), self.modKeys.buttonDown, [key]) self.accept(key.getName() + '-up', self.modKeys.buttonUp, [key]) def getKeys(self): return self.modKeys def destroy(self): self.ignoreAll()
def setup_bindings_for_sensors(self): modifiers = ModifierButtons() modifiers.addButton(KeyboardButton.lshift()) modifiers.addButton(KeyboardButton.rshift()) modifiers.addButton(KeyboardButton.lcontrol()) modifiers.addButton(KeyboardButton.rcontrol()) modifiers.addButton(KeyboardButton.lalt()) modifiers.addButton(KeyboardButton.ralt()) modifiers.addButton(KeyboardButton.meta()) # For supporting "use_all_keys" and modifier keys button_node = base.buttonThrowers[0].node() button_node.setButtonDownEvent('buttonDown') button_node.setButtonUpEvent('buttonUp') button_node.setModifierButtons(modifiers)
class World(DirectObject): instance = None def __init__(self): render.setAntialias(AntialiasAttrib.MAuto) # Enable physics - perhaps this should go someplace else, but it must # be done before the Vehicle is initialized. base.enableParticles() aei = AngularEulerIntegrator() base.physicsMgr.attachAngularIntegrator(aei) SelectionEngine.getDefault().enable() SelectionManager.getDefault().enable() # Make the environment and the vehicle model. makeEnvironment() self.vehicle = Vehicle(render) MissionElement.loadElementConfig('mission_elements.plist') #layoutName = AppPreferences.get('last_layout', 'defaultLayout.plist') if len(sys.argv) == 2: layoutName = sys.argv[1] print "Using command line argument %s for layout" % layoutName else: print "Using default layout file" print "Use ./sim2.py [layout file] to use a different layout" print "Or press Ctrl+O to open a new layout in the simulator" layoutName = 'defaultLayout.plist' self.layout = MissionLayout.loadLayout(layoutName) render.attachNewNode(self.layout) self.vehicle.setLayout(self.layout) #Link the layout # Set up render buffer viewer, to aide debugging. self.accept("v", base.bufferViewer.toggleEnable) self.accept("V", base.bufferViewer.toggleEnable) base.bufferViewer.setPosition("llcorner") # Set up file saver self.accept('s', self._saveLayout) self.accept('o', self._openLayout) self.accept('f', self._setFreq) root = Tk() root.withdraw() self.modButtons = ModifierButtons() self.modButtons.addButton(KeyboardButton.control()) self.accept('control', self.modButtons.buttonDown, [KeyboardButton.control()]) self.accept('control-up', self.modButtons.buttonUp, [KeyboardButton.control()]) # Add GUI Controls ''' buttonReady = makeGeom('button_ready.png') b = DirectButton(geom = (buttonReady, makeGeom('button_click.png'), makeGeom('button_rollover.png'), buttonReady), relief = None) b.reparentTo(pixel2d) b.hide() b.setPos(base.win.getXSize()/2, 0, -base.win.getYSize()/2) b.setScale(1, 1, 1) b.bind(DirectGuiGlobals.ACCEPT, eventHandler, ['accept']) b.bind(DirectGuiGlobals.ACCEPTFAILED, eventHandler, ['accept failed']) b.bind(DirectGuiGlobals.ADJUST, eventHandler, ['adjust']) b.bind(DirectGuiGlobals.B1CLICK, eventHandler, ['b1click']) b.bind(DirectGuiGlobals.B1PRESS, eventHandler, ['b1press']) b.bind(DirectGuiGlobals.B1RELEASE, eventHandler, ['b1release']) b.bind(DirectGuiGlobals.B2CLICK, eventHandler, ['b2click']) b.bind(DirectGuiGlobals.B2PRESS, eventHandler, ['b2press']) b.bind(DirectGuiGlobals.B2RELEASE, eventHandler, ['b2release']) b.bind(DirectGuiGlobals.B3CLICK, eventHandler, ['b3click']) b.bind(DirectGuiGlobals.B3PRESS, eventHandler, ['b3press']) b.bind(DirectGuiGlobals.B3RELEASE, eventHandler, ['b3release']) b.bind(DirectGuiGlobals.ENTER, eventHandler, ['enter']) b.bind(DirectGuiGlobals.EXIT, eventHandler, ['exit']) b.bind(DirectGuiGlobals.WITHIN, eventHandler, ['within']) b.bind(DirectGuiGlobals.WITHOUT, eventHandler, ['without']) b.bind(DirectGuiGlobals.CURSORMOVE, eventHandler, ['cursormove']) # b['frameSize'] = (3, 3, 3, 3) ''' @classmethod def getInstance(cls): if cls.instance == None: cls.instance = World() return cls.instance def _saveLayout(self): if self.modButtons.isDown(KeyboardButton.control()): self.modButtons.buttonUp(KeyboardButton.control()) self.modButtons.buttonUp(KeyboardButton.asciiKey('s')) filename = asksaveasfilename(filetypes=[('plist files', '*.plist')]) if filename: self.layout.save(filename) AppPreferences.set('last_layout', filename) def _setFreq(self): #TODO: Check pinger selected pinger = None for element in self.layout.elements: if element.getTypeName() == "Pinger" and element.isSelected(): pinger = element break if pinger is None: return newfreq = askinteger("Set Pinger Frequency", "Set this pinger to which frequency? (in Hz)", initialvalue=pinger.pinger_frequency, minvalue=10000, maxvalue=50000) if newfreq is None: print "No frequency specified, aborting" return pinger.pinger_frequency = newfreq print "Frequency of pinger set to %d Hz" % newfreq def _openLayout(self): if self.modButtons.isDown(KeyboardButton.control()): self.modButtons.buttonUp(KeyboardButton.control()) self.modButtons.buttonUp(KeyboardButton.asciiKey('o')) filename = askopenfilename(filetypes=[('plist files', '*.plist')]) if filename: if self.layout: NodePath(self.layout).detachNode() self.layout = MissionLayout.loadLayout(filename) render.attachNewNode(self.layout) AppPreferences.set('last_layout', filename)
class World(DirectObject): instance = None def __init__(self): render.setAntialias(AntialiasAttrib.MAuto) # Enable physics - perhaps this should go someplace else, but it must # be done before the Vehicle is initialized. base.enableParticles() aei = AngularEulerIntegrator() base.physicsMgr.attachAngularIntegrator(aei) SelectionEngine.getDefault().enable() SelectionManager.getDefault().enable() # Make the environment and the vehicle model. makeEnvironment() self.vehicle = Vehicle(render) MissionElement.loadElementConfig('mission_elements.plist') #layoutName = AppPreferences.get('last_layout', 'defaultLayout.plist') if len(sys.argv) == 2: layoutName = sys.argv[1] print "Using command line argument %s for layout" %layoutName else: print "Using default layout file" print "Use ./sim2.py [layout file] to use a different layout" print "Or press Ctrl+O to open a new layout in the simulator" layoutName = 'defaultLayout.plist' self.layout = MissionLayout.loadLayout(layoutName) render.attachNewNode(self.layout) self.vehicle.setLayout(self.layout) #Link the layout # Set up render buffer viewer, to aide debugging. self.accept("v", base.bufferViewer.toggleEnable) self.accept("V", base.bufferViewer.toggleEnable) base.bufferViewer.setPosition("llcorner") # Set up file saver self.accept('s', self._saveLayout) self.accept('o', self._openLayout) self.accept('f', self._setFreq) root = Tk() root.withdraw() self.modButtons = ModifierButtons() self.modButtons.addButton(KeyboardButton.control()) self.accept('control', self.modButtons.buttonDown, [KeyboardButton.control()]) self.accept('control-up', self.modButtons.buttonUp, [KeyboardButton.control()]) # Add GUI Controls ''' buttonReady = makeGeom('button_ready.png') b = DirectButton(geom = (buttonReady, makeGeom('button_click.png'), makeGeom('button_rollover.png'), buttonReady), relief = None) b.reparentTo(pixel2d) b.hide() b.setPos(base.win.getXSize()/2, 0, -base.win.getYSize()/2) b.setScale(1, 1, 1) b.bind(DirectGuiGlobals.ACCEPT, eventHandler, ['accept']) b.bind(DirectGuiGlobals.ACCEPTFAILED, eventHandler, ['accept failed']) b.bind(DirectGuiGlobals.ADJUST, eventHandler, ['adjust']) b.bind(DirectGuiGlobals.B1CLICK, eventHandler, ['b1click']) b.bind(DirectGuiGlobals.B1PRESS, eventHandler, ['b1press']) b.bind(DirectGuiGlobals.B1RELEASE, eventHandler, ['b1release']) b.bind(DirectGuiGlobals.B2CLICK, eventHandler, ['b2click']) b.bind(DirectGuiGlobals.B2PRESS, eventHandler, ['b2press']) b.bind(DirectGuiGlobals.B2RELEASE, eventHandler, ['b2release']) b.bind(DirectGuiGlobals.B3CLICK, eventHandler, ['b3click']) b.bind(DirectGuiGlobals.B3PRESS, eventHandler, ['b3press']) b.bind(DirectGuiGlobals.B3RELEASE, eventHandler, ['b3release']) b.bind(DirectGuiGlobals.ENTER, eventHandler, ['enter']) b.bind(DirectGuiGlobals.EXIT, eventHandler, ['exit']) b.bind(DirectGuiGlobals.WITHIN, eventHandler, ['within']) b.bind(DirectGuiGlobals.WITHOUT, eventHandler, ['without']) b.bind(DirectGuiGlobals.CURSORMOVE, eventHandler, ['cursormove']) # b['frameSize'] = (3, 3, 3, 3) ''' @classmethod def getInstance(cls): if cls.instance == None: cls.instance = World() return cls.instance def _saveLayout(self): if self.modButtons.isDown(KeyboardButton.control()): self.modButtons.buttonUp(KeyboardButton.control()) self.modButtons.buttonUp(KeyboardButton.asciiKey('s')) filename = asksaveasfilename(filetypes=[('plist files', '*.plist')]) if filename: self.layout.save(filename) AppPreferences.set('last_layout', filename) def _setFreq(self): #TODO: Check pinger selected pinger = None for element in self.layout.elements: if element.getTypeName() == "Pinger" and element.isSelected(): pinger = element break if pinger is None: return newfreq = askinteger("Set Pinger Frequency", "Set this pinger to which frequency? (in Hz)", initialvalue=pinger.pinger_frequency, minvalue=10000, maxvalue=50000) if newfreq is None: print "No frequency specified, aborting" return pinger.pinger_frequency = newfreq print "Frequency of pinger set to %d Hz" % newfreq def _openLayout(self): if self.modButtons.isDown(KeyboardButton.control()): self.modButtons.buttonUp(KeyboardButton.control()) self.modButtons.buttonUp(KeyboardButton.asciiKey('o')) filename = askopenfilename(filetypes=[('plist files', '*.plist')]) if filename: if self.layout: NodePath(self.layout).detachNode() self.layout = MissionLayout.loadLayout(filename) render.attachNewNode(self.layout) AppPreferences.set('last_layout', filename)
def windowMaker(base, label): # find an open slot, if none, return false windict = dict() grid = None for i in range(grid_size[0]): for j in range(grid_size[1]): if occupied[i][j] == False: grid = (i, j) occupied[i][j] = True break else: continue break if not grid: return False windict['grid'] = grid position = grid_to_screen_pos(*grid) #open window newwin = base.openWindow(name=label) newwin.setWindowEvent(label + "-event") listener.accept(newwin.getWindowEvent(), windowEvent) windict['win'] = newwin #format window wp = WindowProperties() wp.setOrigin(*position) wp.setSize(*ws) newwin.requestProperties(wp) #make 3d-mousewatcher display region displayRegion = newwin.getDisplayRegion(0) displayRegion.setDimensions(.3, 1, 0, 1) mkNode = MouseAndKeyboard(newwin, 0, label + "_keyboard_mouse") mk = base.dataRoot.attachNewNode(mkNode) windict['mk'] = mk modis = ModifierButtons() modis.addButton(ButtonHandle('shift')) modis.addButton(ButtonHandle('control')) mwNode = MouseWatcher(label) mwNode.setModifierButtons(modis) mwNode.setDisplayRegion(displayRegion) mw = mk.attachNewNode(mwNode) windict['mw'] = mw bt = ButtonThrower(label + "_button_thrower") bt.setModifierButtons(modis) windict['bt'] = bt bt.setPrefix(label + "_") mw.attachNewNode(bt) #listen for default button events for button in buttons: listener.accept(bt.prefix + button, buttons[button]) #format render display region render_dr = newwin.getDisplayRegion(1) windict['render_dr'] = render_dr render_dr.setDimensions(.3, 1, 0, 1) #create a display region for Gui Elements gui_dr = newwin.makeDisplayRegion(0, .3, .3, 1) # gui_dr.setSort(20) mwNodegui = MouseWatcher(label + "gui") mwNodegui.setDisplayRegion(gui_dr) mwgui = mk.attachNewNode(mwNodegui) #create a 2d render/aspect for gui rendergui = NodePath('render2d') rendergui.setDepthWrite(0) rendergui.setMaterialOff(1) rendergui.setTwoSided(1) #set up aspect2d aspectgui = rendergui.attachNewNode(PGTop('aspectgui')) aspectgui.node().setMouseWatcher(mwgui.node()) #set up camera camNodegui = Camera("camNode2d") cameragui = rendergui.attachNewNode(camNodegui) cameragui.setPos(0, 0, 0) cameragui.setDepthTest(False) cameragui.setDepthWrite(False) lens = OrthographicLens() lens.setFilmSize(2, 2) lens.setNearFar(-1000, 1000) cameragui.node().setLens(lens) gui_dr.setCamera(cameragui) #make frame for gui frame = DirectFrame(frameSize=(-1, 1, -1, 1), frameColor=(.5, .5, .5, 1), relief='ridge') frame.reparentTo(aspectgui) frame.setTransparency(0) windict['gui_frame'] = frame #create Gui elements guibuttons = dict() # guibuttons['Create'] = label+"_mode_create" guibuttons['Preview'] = label + "_preview" # createButton(base, frame, .7, "Create", label+"_mode_create") createButton(base, frame, .7, "Preview", label + "_preview") windict['guibuttons'] = guibuttons #create a display region for math data preview preview_dr = newwin.makeDisplayRegion(0, .3, 0, .3) windict['preview_dr'] = preview_dr # preview_label = label+"_preview" # preview_mwNode = MouseWatcher(preview_label) # preview_mwNode.setDisplayRegion(preview_dr) # preview_mw = mk.attachNewNode(preview_mwNode) # preview_bt = ButtonThrower(preview_label+"_button_thrower") # preview_bt.setPrefix(preview_label+"_") # preview_mw.attachNewNode(preview_bt) # preview_dr.setSort(30) win_to_windict[newwin] = windict return windict
def initialize(self): self.lens = self.makeLens() self.camera = self.doc.render.attachNewNode( ModelNode("viewportCameraParent")) self.camNode = Camera("viewportCamera") self.camNode.setLens(self.lens) self.camNode.setCameraMask(self.getViewportMask()) self.cam = self.camera.attachNewNode(self.camNode) winprops = WindowProperties.getDefault() winprops.setParentWindow(int(self.winId())) winprops.setForeground(False) winprops.setUndecorated(True) gsg = self.doc.gsg output = base.graphicsEngine.makeOutput( base.pipe, "viewportOutput", 0, FrameBufferProperties.getDefault(), winprops, (GraphicsPipe.BFFbPropsOptional | GraphicsPipe.BFRequireWindow), gsg) self.qtWindow = QtGui.QWindow.fromWinId( output.getWindowHandle().getIntHandle()) self.qtWidget = QtWidgets.QWidget.createWindowContainer( self.qtWindow, self, QtCore.Qt.WindowDoesNotAcceptFocus | QtCore.Qt.WindowTransparentForInput | QtCore.Qt.WindowStaysOnBottomHint | QtCore.Qt.BypassWindowManagerHint | QtCore.Qt.SubWindow) #, #(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowDoesNotAcceptFocus #| QtCore.Qt.WindowTransparentForInput | QtCore.Qt.BypassWindowManagerHint #| QtCore.Qt.SubWindow | QtCore.Qt.WindowStaysOnBottomHint)) self.qtWidget.setFocusPolicy(QtCore.Qt.NoFocus) self.inputDevice = output.getInputDevice(0) assert output is not None, "Unable to create viewport output!" dr = output.makeDisplayRegion() dr.disableClears() dr.setCamera(self.cam) self.displayRegion = dr output.disableClears() output.setClearColor(Viewport.ClearColor) output.setClearColorActive(True) output.setClearDepthActive(True) output.setActive(True) self.win = output # keep track of the mouse in this viewport mak = MouseAndKeyboard(self.win, 0, "mouse") mouse = base.dataRoot.attachNewNode(mak) self.mouseAndKeyboard = mouse self.mouseWatcher = MouseWatcher() self.mouseWatcher.setDisplayRegion(self.displayRegion) mw = mouse.attachNewNode(self.mouseWatcher) self.mouseWatcherNp = mw # listen for keyboard and mouse events in this viewport bt = ButtonThrower("kbEvents") bt.setButtonDownEvent("btndown") bt.setButtonUpEvent("btnup") mods = ModifierButtons() mods.addButton(KeyboardButton.shift()) mods.addButton(KeyboardButton.control()) mods.addButton(KeyboardButton.alt()) mods.addButton(KeyboardButton.meta()) bt.setModifierButtons(mods) self.buttonThrower = mouse.attachNewNode(bt) # collision objects for clicking on objects from this viewport self.clickRay = CollisionRay() self.clickNode = CollisionNode("viewportClickRay") self.clickNode.addSolid(self.clickRay) self.clickNp = NodePath(self.clickNode) self.clickQueue = CollisionHandlerQueue() self.setupRender2d() self.setupCamera2d() self.gizmo = ViewportGizmo(self) self.doc.viewportMgr.addViewport(self) self.makeGrid()
class CameraController(DirectObject): instance = None def __init__(self): self.instructionText = addInstructions(0.95, '[ESC]: Leave Mouselook mode.') self.eventDispatcher = EventDispatcher() self.cameraMode = None self.clickPos = Vec2() self.lastMousePos = Vec2() self.focus = Vec3() self.mouseDown = False self.initialPos = Vec3() self.initialHpr = Vec3() self.initialMat = None # Disable the built-in mouse camera control (it sucks). base.disableMouse() self.setCameraMode(TRACKBALL) # Set the camera's initial position. base.camera.setPosHpr(0, 12, 30, 180, -70, 0) # Turn off events generated with modifier buttons, e.g. 'shift-a' # This is to keep keyboard control working after you alt-tab out # of the app. base.mouseWatcherNode.setModifierButtons(ModifierButtons()) base.buttonThrowers[0].node().setModifierButtons(ModifierButtons()) # This is a diagonal matrix that keeps track of movement key # state. The first three diagonal entries can be 1, 0, or -1. self.mouseLookTransMat = Mat4.scaleMat(Vec3(0.0, 0.0, 0.0)) # Keep track of how many movement keys are currently pressed. This # lets us short-circuit past a lot of math when no keys are held. self.keysHeld = 0 # Handle events for the movement keys. for key, value in key2MatArgs.items(): self.accept(key, self._moveKeyHandler, value) self.accept('escape', self._escKeyHandler) self.accept('m', self._mKeyHandler) self.accept('mouse1', self._mouseDownHandler, [1]) self.accept('mouse1-up', self._mouseUpHandler, [1]) self.accept('mouse2', self._mouseDownHandler, [2]) self.accept('mouse2-up', self._mouseUpHandler, [2]) self.accept('wheel_up', self._mouseWheelHandler, [1]) self.accept('wheel_down', self._mouseWheelHandler, [-1]) self.modButtons = ModifierButtons() self.modButtons.addButton(KeyboardButton.control()) self.accept('control', self.modButtons.buttonDown, [KeyboardButton.control()]) self.accept('control-up', self.modButtons.buttonUp, [KeyboardButton.control()]) self.accept(base.win.getWindowEvent(), self._windowHandler) @classmethod def getInstance(cls): if cls.instance == None: cls.instance = cls() return cls.instance def setFocus(self, position): self.focus = position base.camera.lookAt(render, position) def addEventHandler(self, evtType, handler): self.eventDispatcher.addHandler(evtType, handler) def getCameraMode(self): return self.cameraMode def setCameraMode(self, mode): if mode == self.cameraMode: return winProps = WindowProperties() # Tear-down code for the current mode. if self.cameraMode == MOUSELOOK: winProps.setCursorHidden(False) self.removeTask(self.viewTask) elif self.cameraMode == TRACKBALL: self.removeTask(self.viewTask) # Set-up code for the new mode. if mode == MOUSELOOK: winProps.setCursorHidden(True) # Mouse accumulator. Whenever the mouse moves, it is reset to the # center of the window. The accumulator keeps track of the total # movement self.mouseAccX = 0 self.mouseAccY = 0 self.recenterMouse = True # Add a task that moves the camera based on mouse position. self.viewTask = taskMgr.add(self._mlViewTask, 'MLViewTask') self.lastFrameTime = 0 # Used to calculate dt in mlViewTask. self.instructionText.setText('[ESC]: Leave Mouselook Mode') elif mode == TRACKBALL: self.instructionText.setText('[m]: Enter Mouselook Mode') self.viewTask = taskMgr.add(self._trackballTask, 'TrackballTask') self.cameraMode = mode self.eventDispatcher.dispatchEvent(EVT_CAMERA_MODE, self) base.win.requestProperties(winProps) def _windowHandler(self, window): pos = aspect2d.find('a2dTopLeft').getPos() self.instructionText.setPos(pos.getX() + 0.05, pos.getZ() - 0.05) hasFocus = window.getProperties().getForeground() if not hasFocus: self.setCameraMode(TRACKBALL) def _escKeyHandler(self): self.setCameraMode(TRACKBALL) def _mKeyHandler(self): if self.cameraMode == MOUSELOOK: self.setCameraMode(TRACKBALL) else: self.setCameraMode(MOUSELOOK) def _mouseWheelHandler(self, arg): deltY = base.camera.getPos().length() * arg * 0.15 transl = Mat4.translateMat(0, deltY, 0) base.camera.setMat(transl * base.camera.getMat()) self.eventDispatcher.dispatchEvent(EVT_CAMERA_MOVE, base.camera) def _mouseDownHandler(self, button): if base.mouseWatcherNode.hasMouse(): if button == 2 or self.modButtons.isDown(KeyboardButton.control()): self.clickPos = Vec2(base.mouseWatcherNode.getMouse()) self.mouseDown = True offset = self.focus - base.camera.getPos() self.initialTranslation = offset.length() self.initialHpr = base.camera.getHpr() def _mouseUpHandler(self, button): self.mouseDown = False def _moveKeyHandler(self, *matArgs): self.mouseLookTransMat.setCell(*matArgs) # matArgs[2] is 0 for key release events. if (matArgs[2]): self.keysHeld += 1 else: self.keysHeld -= 1 def _trackballTask(self, task): if not self.mouseDown: return Task.cont if (base.mouseWatcherNode.hasMouse()): mpos = base.mouseWatcherNode.getMouse() if mpos == self.lastMousePos: return Task.cont mDelta = mpos - self.clickPos heading = -mDelta.x * 100 + self.initialHpr.x pitch = mDelta.y * 100 + self.initialHpr.y if pitch > 90: pitch = 90 elif pitch < -90: pitch = -90 trans1 = Mat4.translateMat(self.focus) rotx = Mat4.rotateMat(heading, Vec3(0, 0, 1)) roty = Mat4.rotateMat(pitch, Vec3(1, 0, 0)) trans2 = Mat4.translateMat(0, -self.initialTranslation, 0) rotation = trans2 * roty * rotx * trans1 base.camera.setMat(rotation) self.eventDispatcher.dispatchEvent(EVT_CAMERA_MOVE, base.camera) self.lastMousePos = Vec2(mpos) return Task.cont def _mlViewTask(self, task): t = globalClock.getFrameTime() dt = t - self.lastFrameTime self.lastFrameTime = t if (self.keysHeld): # Update camera position based on keyboard controls. T = Mat4() T.invertAffineFrom(base.camera.getMat()) cameraMoveMat = T*self.mouseLookTransMat xDir = cameraMoveMat.getCol3(0) yDir = cameraMoveMat.getCol3(1) zDir = cameraMoveMat.getCol3(2) currentPos = base.camera.getPos() nextPos = currentPos + (xDir + yDir + zDir)*(dt*8) base.camera.setPos(nextPos) self.eventDispatcher.dispatchEvent(EVT_CAMERA_MOVE, base.camera) if (base.mouseWatcherNode.hasMouse()): halfWidth = base.win.getProperties().getXSize()/2 halfHeight = base.win.getProperties().getYSize()/2 if self.recenterMouse: # Don't move the camera, but center the pointer. This is # useful when entering mouselook mode, to prevent the camera # from jumping to the pointer. heading, pitch = base.camera.getH(), base.camera.getP() self.mouseAccX = int(heading / ML_MULT) self.mouseAccY = int(pitch / ML_MULT) self.recenterMouse = False # Only do this once. else: mpos = base.mouseWatcherNode.getMouse() x, y = mpos.getX(), mpos.getY() # short-circuit if mouse hasn't moved or is outside the window. if x == 0 and y == 0 or x > 1.0 or y > 1.0: return Task.cont # Convert x and y to pixels. self.mouseAccX -= int(round(x*halfWidth)) self.mouseAccY += int(round(y*halfHeight)) base.camera.setHpr(self.mouseAccX*ML_MULT, self.mouseAccY*ML_MULT, 0.0) base.win.movePointer(0, halfWidth, halfHeight) return Task.cont
class CameraController(DirectObject): instance = None def __init__(self): self.instructionText = addInstructions(0.95, '[ESC]: Leave Mouselook mode.') self.eventDispatcher = EventDispatcher() self.cameraMode = None self.clickPos = Vec2() self.lastMousePos = Vec2() self.focus = Vec3() self.mouseDown = False self.initialPos = Vec3() self.initialHpr = Vec3() self.initialMat = None # Disable the built-in mouse camera control (it sucks). base.disableMouse() self.setCameraMode(TRACKBALL) # Set the camera's initial position. base.camera.setPosHpr(0, 12, 30, 180, -70, 0) # Turn off events generated with modifier buttons, e.g. 'shift-a' # This is to keep keyboard control working after you alt-tab out # of the app. base.mouseWatcherNode.setModifierButtons(ModifierButtons()) base.buttonThrowers[0].node().setModifierButtons(ModifierButtons()) # This is a diagonal matrix that keeps track of movement key # state. The first three diagonal entries can be 1, 0, or -1. self.mouseLookTransMat = Mat4.scaleMat(Vec3(0.0, 0.0, 0.0)) # Keep track of how many movement keys are currently pressed. This # lets us short-circuit past a lot of math when no keys are held. self.keysHeld = 0 # Handle events for the movement keys. for key, value in key2MatArgs.items(): self.accept(key, self._moveKeyHandler, value) self.accept('escape', self._escKeyHandler) self.accept('m', self._mKeyHandler) self.accept('mouse1', self._mouseDownHandler, [1]) self.accept('mouse1-up', self._mouseUpHandler, [1]) self.accept('mouse2', self._mouseDownHandler, [2]) self.accept('mouse2-up', self._mouseUpHandler, [2]) self.accept('wheel_up', self._mouseWheelHandler, [1]) self.accept('wheel_down', self._mouseWheelHandler, [-1]) self.modButtons = ModifierButtons() self.modButtons.addButton(KeyboardButton.control()) self.accept('control', self.modButtons.buttonDown, [KeyboardButton.control()]) self.accept('control-up', self.modButtons.buttonUp, [KeyboardButton.control()]) self.accept(base.win.getWindowEvent(), self._windowHandler) @classmethod def getInstance(cls): if cls.instance == None: cls.instance = cls() return cls.instance def setFocus(self, position): self.focus = position base.camera.lookAt(render, position) def addEventHandler(self, evtType, handler): self.eventDispatcher.addHandler(evtType, handler) def getCameraMode(self): return self.cameraMode def setCameraMode(self, mode): if mode == self.cameraMode: return winProps = WindowProperties() # Tear-down code for the current mode. if self.cameraMode == MOUSELOOK: winProps.setCursorHidden(False) self.removeTask(self.viewTask) elif self.cameraMode == TRACKBALL: self.removeTask(self.viewTask) # Set-up code for the new mode. if mode == MOUSELOOK: winProps.setCursorHidden(True) # Mouse accumulator. Whenever the mouse moves, it is reset to the # center of the window. The accumulator keeps track of the total # movement self.mouseAccX = 0 self.mouseAccY = 0 self.recenterMouse = True # Add a task that moves the camera based on mouse position. self.viewTask = taskMgr.add(self._mlViewTask, 'MLViewTask') self.lastFrameTime = 0 # Used to calculate dt in mlViewTask. self.instructionText.setText('[ESC]: Leave Mouselook Mode') elif mode == TRACKBALL: self.instructionText.setText('[m]: Enter Mouselook Mode') self.viewTask = taskMgr.add(self._trackballTask, 'TrackballTask') self.cameraMode = mode self.eventDispatcher.dispatchEvent(EVT_CAMERA_MODE, self) base.win.requestProperties(winProps) def _windowHandler(self, window): pos = aspect2d.find('a2dTopLeft').getPos() self.instructionText.setPos(pos.getX() + 0.05, pos.getZ() - 0.05) hasFocus = window.getProperties().getForeground() if not hasFocus: self.setCameraMode(TRACKBALL) def _escKeyHandler(self): self.setCameraMode(TRACKBALL) def _mKeyHandler(self): if self.cameraMode == MOUSELOOK: self.setCameraMode(TRACKBALL) else: self.setCameraMode(MOUSELOOK) def _mouseWheelHandler(self, arg): deltY = base.camera.getPos().length() * arg * 0.15 transl = Mat4.translateMat(0, deltY, 0) base.camera.setMat(transl * base.camera.getMat()) self.eventDispatcher.dispatchEvent(EVT_CAMERA_MOVE, base.camera) def _mouseDownHandler(self, button): if base.mouseWatcherNode.hasMouse(): if button == 2 or self.modButtons.isDown(KeyboardButton.control()): self.clickPos = Vec2(base.mouseWatcherNode.getMouse()) self.mouseDown = True offset = self.focus - base.camera.getPos() self.initialTranslation = offset.length() self.initialHpr = base.camera.getHpr() def _mouseUpHandler(self, button): self.mouseDown = False def _moveKeyHandler(self, *matArgs): self.mouseLookTransMat.setCell(*matArgs) # matArgs[2] is 0 for key release events. if (matArgs[2]): self.keysHeld += 1 else: self.keysHeld -= 1 def _trackballTask(self, task): if not self.mouseDown: return Task.cont if (base.mouseWatcherNode.hasMouse()): mpos = base.mouseWatcherNode.getMouse() if mpos == self.lastMousePos: return Task.cont mDelta = mpos - self.clickPos heading = -mDelta.x * 100 + self.initialHpr.x pitch = mDelta.y * 100 + self.initialHpr.y if pitch > 90: pitch = 90 elif pitch < -90: pitch = -90 trans1 = Mat4.translateMat(self.focus) rotx = Mat4.rotateMat(heading, Vec3(0, 0, 1)) roty = Mat4.rotateMat(pitch, Vec3(1, 0, 0)) trans2 = Mat4.translateMat(0, -self.initialTranslation, 0) rotation = trans2 * roty * rotx * trans1 base.camera.setMat(rotation) self.eventDispatcher.dispatchEvent(EVT_CAMERA_MOVE, base.camera) self.lastMousePos = Vec2(mpos) return Task.cont def _mlViewTask(self, task): t = globalClock.getFrameTime() dt = t - self.lastFrameTime self.lastFrameTime = t if (self.keysHeld): # Update camera position based on keyboard controls. T = Mat4() T.invertAffineFrom(base.camera.getMat()) cameraMoveMat = T * self.mouseLookTransMat xDir = cameraMoveMat.getCol3(0) yDir = cameraMoveMat.getCol3(1) zDir = cameraMoveMat.getCol3(2) currentPos = base.camera.getPos() nextPos = currentPos + (xDir + yDir + zDir) * (dt * 8) base.camera.setPos(nextPos) self.eventDispatcher.dispatchEvent(EVT_CAMERA_MOVE, base.camera) if (base.mouseWatcherNode.hasMouse()): halfWidth = base.win.getProperties().getXSize() / 2 halfHeight = base.win.getProperties().getYSize() / 2 if self.recenterMouse: # Don't move the camera, but center the pointer. This is # useful when entering mouselook mode, to prevent the camera # from jumping to the pointer. heading, pitch = base.camera.getH(), base.camera.getP() self.mouseAccX = int(heading / ML_MULT) self.mouseAccY = int(pitch / ML_MULT) self.recenterMouse = False # Only do this once. else: mpos = base.mouseWatcherNode.getMouse() x, y = mpos.getX(), mpos.getY() # short-circuit if mouse hasn't moved or is outside the window. if x == 0 and y == 0 or x > 1.0 or y > 1.0: return Task.cont # Convert x and y to pixels. self.mouseAccX -= int(round(x * halfWidth)) self.mouseAccY += int(round(y * halfHeight)) base.camera.setHpr(self.mouseAccX * ML_MULT, self.mouseAccY * ML_MULT, 0.0) base.win.movePointer(0, halfWidth, halfHeight) return Task.cont