Esempio n. 1
0
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)
Esempio n. 2
0
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
Esempio n. 3
0
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)
Esempio n. 4
0
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