Ejemplo n.º 1
0
    def __init__(self, *args, **kwargs):
        NodePath.__init__(self, *args, **kwargs)
        DirectObject.__init__(self)

        self.scale_signal = Signal()

        self.reset()
Ejemplo n.º 2
0
class Camera(NodePath, DirectObject):
    """
    The world map camera for Orbital Industry. Replaces base.camera.
    """

    CAMERA_DISTANCE = 1000
    SCALE_THRESHOLD = 1000.0

    def __init__(self, *args, **kwargs):
        NodePath.__init__(self, *args, **kwargs)
        DirectObject.__init__(self)

        self.scale_signal = Signal()

        self.reset()

    def display(self, display=True):
        """
        Displays/hides the camera.

        @type display: boolean
        @param display: True to display the camera.
        """

        if display:
            self.reparentTo(render)
            self.listen(True)
        else:
            self.detachNode()
            self.listen(False)

    def listen(self, listen=True):
        """
        Causes the camera to associate/disassociate with user input.

        @type listen: boolean
        @param listen: True to active camera controls.
        """
        
        if listen:
            self.addTask(self.updateTask, 'camera.updateTask')

            self.accept("mouse1", self.startPan)
            self.accept("mouse1-up", self.stopPan)
            self.accept("mouse3", self.startZoom)
            self.accept("mouse3-up", self.stopZoom)
        else:
            self.removeAllTasks()
            self.ignoreAll()

    def reset(self):
        """
        Reset the camera to factory defaults.
        """

        # State variables
        self.leftDown = False
        self.rightDown = False

        self.panning = False
        self.zooming = False

        self.scale = 0.00003
        self.scale_level = 0

        self.rotation = 0.0
        self.inclination = omath.PI / 4.0
        self.mouseX = 0.0
        self.mouseY = 0.0

        self._updatePosition(0, 0, True)

    def startPan(self):
        """
        Set panning variable.
        """

        self.panning = True
        self._updateMouse()

    def stopPan(self):
        """
        Unset panning variable.
        """

        self.panning = False
        self._updateMouse()

    def startZoom(self):
        """
        Set zooming variable.
        """

        self.zooming = True
        self._updateMouse()

    def stopZoom(self):
        """
        Unset zooming variable.
        """

        self.zooming = False
        self._updateMouse()

    def _getMouse(self):
        """
        Return the current positoin of the mouse.

        @rtype: (number, number)
        @return: Tuple with mouse x/y coordinates. Returns (None, None) if
        mouse is off-screen.
        """

        if base.mouseWatcherNode.hasMouse():
            return (base.mouseWatcherNode.getMouseX(),
                    base.mouseWatcherNode.getMouseY())
        else:
            return (None, None)

    def _updateMouse(self):
        """
        Updates the self.mouseX and self.mouseY "last known position"
        coordinates.
        """
        self.mouseX, self.mouseY = self._getMouse()

    def _updatePosition(self, mouseX, mouseY, force=False):
        """
        Update the position of the camera from mouseX and mouseY.

        @type mouseX: number
        @param mouseX: X-delta of the mouse.

        @type mouseY: number
        @param mouseY: Y-delta of the mouse.

        @type force: boolean
        @param force: True to force camera update regardless of mouse state.
        """

        # Calculate pan
        if force or self.panning:
            self.rotation = self.rotation + (self.mouseX - mouseX) * 5
            self.rotation %= omath.TWOPI

            self.inclination = self.inclination + (self.mouseY - mouseY) * 2
            self.inclination = min(omath.PI/2.0,
                                   max(-omath.PI/2.0, self.inclination))
            
            # Calculate inclination limiter (pulls in horizontal displacement
            # as vertical displacement increases). This confines our camera to
            # a spherical path.
            limiter = math.cos(self.inclination)

            cosRot = math.cos(self.rotation)
            sinRot = math.sin(self.rotation)
            
            self.setPos(cosRot * limiter * Camera.CAMERA_DISTANCE,
                        sinRot * limiter * Camera.CAMERA_DISTANCE,
                        math.sin(self.inclination) * Camera.CAMERA_DISTANCE)

            # Camera "up" vector is just the camera vector inclined by 90 deg
            upInclination = self.inclination + (omath.PI / 2.0)
            upLimiter = math.cos(upInclination)

            self.lookAt((0,0,0), Vec3(cosRot * upLimiter,
                                      sinRot * upLimiter,
                                      math.sin(upInclination)))

        # Calculate zoom
        if force or self.zooming:
            self.scale *= ((mouseY - self.mouseY) + 1) ** 4

            prev_level = self.scale_level
            
            while self.scale > Camera.SCALE_THRESHOLD:
                self.scale_level += 1
                self.scale /= Camera.SCALE_THRESHOLD

            while self.scale < 1:
                self.scale_level -= 1
                self.scale *= Camera.SCALE_THRESHOLD

            # emit signals if we changed scale_level
            if self.scale_level != prev_level:
                multiplier = 1
                if 0 > self.scale_level:
                    for i in range(-self.scale_level):
                        multiplier /= Camera.SCALE_THRESHOLD
                elif 0 < scale_level:
                    for i in range(self.scale_level):
                        multiplier *= Camera.SCALE_THRESHOLD

                self.scale_signal.emit(self.scale_level, multiplier)

    def updateTask(self, task):
        """
        Gets the current mouse offsets and updates the camera accordingly.
        """

        mouseX,mouseY = self._getMouse()
        
        # If the mouse is just reentering the frame, overwrite the None values
        # with the current ones. This will cause the first mousedelta to be
        # zero upon entering the frame.
        if (self.mouseX == None or self.mouseY == None):
            self.mouseX = mouseX
            self.mouseY = mouseY

        self._updatePosition(mouseX, mouseY)

        # Store away current mouse values as last known values
        self.mouseX = mouseX
        self.mouseY = mouseY

        return task.cont