Example #1
0
class Camera:
    """class controlling a 3d camera for use with opengl"""
    def __init__(self,
                 mode=1,
                 position=glm.vec3(1, 0, 0),
                 target=glm.vec3(0),
                 worldUp=glm.vec3(0, 1, 0)):

        # settings
        self.fpsRotationSpeed = 0.005  # speed with which the camera rotates in fps mode
        self.moveSpeed = 0.125  # speed with which the camera moves in fps mode
        self.tbRotationSpeed = 0.015  # speed with which the camera rotates in trackball mode
        self.panSpeed = 0.006  # speed with which the camera pans in trackball mode
        self.zoomSpeed = 0.25  # speed with which the camera zooms (used for Z axis in trackball mode)
        self.enableAllControls = False  # enable movement while in trackball mode and pan/zoom while in fps mode
        self.movementSmoothing = 0.25  # higher numbers result in stronger smoothing and delayed movement
        self.rotationSmoothing = 0.3  # higher numbers result in stronger smoothing and delayed rotation
        self.worldUp = worldUp  # worlds up vector
        self.mode = mode  # 0 = trackball, 1 = first person
        self.movementSpeedMod = 1.0  # temporary modified movement speed, will be reset every frame

        # cameras internal state (DO NOT WRITE)
        self._movementInput = glm.vec3(0)
        self._rotationInput = glm.vec2(0)
        self._desiredTransform = Transform(
        )  # Transform(position)  # desired transform based on inputs
        self._desiredTargetDistance = 0  # desired distance along the front vector where the center for trackball mode lives
        self._currentTransform = Transform(
        )  #Transform(position)  # transform object describing orientation and position of camera
        self._currentTargetDistance = 0  # distance along the front vector where the center for trackball mode lives
        self.setPosition(position)
        self.setTarget(target)

        # variables depending on state, they are automatically updated when calling the Camera.update() (DO NOT WRITE, only read)
        self.modelMatrix = self._currentTransform.mat4()
        self.viewMatrix = glm.inverse(self.modelMatrix)

    def setTarget(self, target, interpolate=False):
        """Use to set a point where the camera should look. Target is expected to be a glm.vec3,
        interpolate is a bool. Set interpolate to true produces an animated camera movement"""
        self._desiredTransform.lookAt(target, self.worldUp)
        self._desiredTargetDistance = glm.length(
            target - self._desiredTransform.position)

        if not interpolate:
            self._currentTransform.orientation = self._desiredTransform.orientation
            self._currentTargetDistance = self._desiredTargetDistance

    def setPosition(self, position, interpolate=False):
        """Use to set the position of the camera. Position is expected to be a glm.vec3,
        interpolate is a bool. Set interpolate to true produces an animated camera movement"""
        newPos = glm.vec3(
            position.x, position.y,
            position.z)  # avoid python binding a reference to the position
        self._desiredTransform.position = newPos
        if not interpolate:
            self._currentTransform.position = newPos

    def rotateH(self, dPhi):
        self._rotationInput.x += dPhi * (self.fpsRotationSpeed if self.mode
                                         == 1 else self.tbRotationSpeed)

    def rotateV(self, dTheta):
        self._rotationInput.y += dTheta * (self.fpsRotationSpeed if self.mode
                                           == 1 else self.tbRotationSpeed)

    def moveX(self, dx):
        if self.mode == 1 or self.enableAllControls:
            self._movementInput.x += dx * self.moveSpeed

    def moveY(self, dy):
        if self.mode == 1 or self.enableAllControls:
            self._movementInput.y += dy * self.moveSpeed

    def moveZ(self, dz):
        if self.mode == 1 or self.enableAllControls:
            self._movementInput.z += dz * self.moveSpeed

    def panH(self, dx):
        if self.mode == 0 or self.enableAllControls:
            self._movementInput.x += dx * self.panSpeed

    def panV(self, dy):
        if self.mode == 0 or self.enableAllControls:
            self._movementInput.y += dy * self.panSpeed

    def zoom(self, dz):
        if self.mode == 0 or self.enableAllControls:
            self._movementInput.z -= dz * self.zoomSpeed

    def update(self, deltaTime):
        # movement in camera coordinates
        movement = self._currentTransform.orientation * (self._movementInput *
                                                         self.movementSpeedMod)

        if self.mode == 0:
            # rotate position around target
            # figure out where the old target is
            oldTarget = self._desiredTransform.position \
                        + self._desiredTransform.orientation * glm.vec3(0, 0, -1) * self._desiredTargetDistance

            # rotate the camera
            self._desiredTransform.orientation = glm.normalize(
                glm.angleAxis(self._rotationInput.x, self.worldUp) *
                self._desiredTransform.orientation *
                glm.angleAxis(self._rotationInput.y, glm.vec3(1, 0, 0)))

            # move so old target matches new target
            newTarget = self._desiredTransform.position \
                        + self._desiredTransform.orientation * glm.vec3(0, 0, -1) * self._desiredTargetDistance
            self._desiredTransform.position += oldTarget - newTarget

            # pan
            # zooming, make sure distance to the target does not become negative
            if self._desiredTargetDistance + self._movementInput.z > 0.01 * self.zoomSpeed:
                self._desiredTargetDistance += self._movementInput.z

            # now just apply  movement
            self._desiredTransform.position += movement

            # interpolate
            # interpolate distance to target
            self._currentTargetDistance = glm.mix(
                self._currentTargetDistance, self._desiredTargetDistance,
                glm.pow(deltaTime, self.movementSmoothing))

            # interpolate current target position
            desiredTarget = self._desiredTransform.position + self._desiredTransform.orientation * glm.vec3(
                0, 0, -1) * self._desiredTargetDistance
            oldActualTarget = self._currentTransform.position + self._currentTransform.orientation * glm.vec3(
                0, 0, -1) * self._currentTargetDistance
            oldActualTarget = glm.mix(
                oldActualTarget, desiredTarget,
                glm.pow(deltaTime, self.movementSmoothing))

            # interpolate orientation
            self._currentTransform.orientation = glm.slerp(
                self._currentTransform.orientation,
                self._desiredTransform.orientation,
                glm.pow(deltaTime, self.rotationSmoothing))

            # calculate proper position using difference that was created by rotation and moving the target
            newActualTarget = self._currentTransform.position + self._currentTransform.orientation * glm.vec3(
                0, 0, -1) * self._currentTargetDistance
            self._currentTransform.position += oldActualTarget - newActualTarget

        elif self.mode == 1:

            # movement
            self._desiredTransform.position += movement

            # rotation
            self._desiredTransform.orientation = glm.normalize(
                glm.angleAxis(self._rotationInput.x, self.worldUp) *
                self._desiredTransform.orientation *
                glm.angleAxis(self._rotationInput.y, glm.vec3(1, 0, 0)))

            # interpolate between transform and desired transform
            self._currentTransform.position = glm.mix(
                self._currentTransform.position,
                self._desiredTransform.position,
                glm.pow(deltaTime, self.movementSmoothing))
            self._currentTransform.orientation = glm.slerp(
                self._currentTransform.orientation,
                self._desiredTransform.orientation,
                glm.pow(deltaTime, self.rotationSmoothing))

        self.modelMatrix = self._currentTransform.mat4()
        self.viewMatrix = glm.inverse(self.modelMatrix)
        self._rotationInput.x = 0
        self._rotationInput.y = 0
        self._movementInput.x = 0
        self._movementInput.y = 0
        self._movementInput.z = 0
        self.movementSpeedMod = 1.0