Starting the Loop

Now we’re at the beginning of the main loop. First, we handle the movement, then update the FPS counter, and reset the camera image.

In this module we are dealing with the following code snippet of the Engine Loop:

main method
1def main(self):
2
3    ...
4
5        self.window.handle_movement()
6        self.fps_counter.update()
7        self.camera_model.reset_camera_image()
8
9        ...

handle_movement()

The handle_movement method manages the camera’s movement in response to keyboard inputs.

def handle_movement(self):
    camera_speed = 100
    current_time = time.time()
    if current_time - self.last_update_time >= self.update_interval:
        self.last_update_time = current_time

        key = cv.waitKey(30) & 0xFF

        if key == ord('d'):
            self.move_camera('forward', camera_speed)
        if key == ord('a'):
            self.move_camera('backward', camera_speed)
        if key == ord('w'):
            self.move_camera('left', camera_speed)
        if key == ord('s'):
            self.move_camera('right', camera_speed)
        if key == ord('q'):
            self.move_camera('down', camera_speed)
        if key == ord('e'):
            self.move_camera('up', camera_speed)

move_camera()

The move_camera method calculates the direction vectors based on the camera’s current yaw and pitch and updates the camera’s position accordingly.

Note

This way, the camera movement using W, A, S, and D is not influenced by the direction you’re looking at.

def move_camera(self, direction, speed):
    # Calculate vectors
    yaw = np.deg2rad(self.camera_system_rotation_yaw / 10.0)
    pitch = np.deg2rad(self.camera_system_rotation_pitch / 10.0)

    forward_x = np.cos(pitch) * np.cos(yaw)
    forward_y = np.cos(pitch) * np.sin(yaw)
    forward_z = np.sin(pitch)

    right_x = np.sin(yaw)
    right_y = -np.cos(yaw)
    right_z = 0

    up_x = 0
    up_y = 0
    up_z = 1

    if direction == 'forward':
        self.camera_system_translation_x += int(forward_x * speed)
        self.camera_system_translation_y += int(forward_y * speed)
        self.camera_system_translation_z += int(forward_z * speed)
    elif direction == 'backward':
        self.camera_system_translation_x -= int(forward_x * speed)
        self.camera_system_translation_y -= int(forward_y * speed)
        self.camera_system_translation_z -= int(forward_z * speed)
    elif direction == 'left':
        self.camera_system_translation_x -= int(right_x * speed)
        self.camera_system_translation_y -= int(right_y * speed)
    elif direction == 'right':
        self.camera_system_translation_x += int(right_x * speed)
        self.camera_system_translation_y += int(right_y * speed)
    elif direction == 'up':
        self.camera_system_translation_z += int(up_z * speed)
    elif direction == 'down':
        self.camera_system_translation_z -= int(up_z * speed)

    self.camera_system_translation_x = np.clip(self.camera_system_translation_x, 0, 20000)
    self.camera_system_translation_y = np.clip(self.camera_system_translation_y, 0, 20000)
    self.camera_system_translation_z = np.clip(self.camera_system_translation_z, 0, 20000)
    cv.setTrackbarPos("X", self.camera_window_name, self.camera_system_translation_x)
    cv.setTrackbarPos("Y", self.camera_window_name, self.camera_system_translation_y)
    cv.setTrackbarPos("Z", self.camera_window_name, self.camera_system_translation_z)

mouse_event_handler()

The mouse_event_handler method handles the mouse interactions with the window, allowing users to rotate the camera view by dragging or clicking inside the window by right-click.

Note

With the left click, you can drag your view like on Google Maps. With the right click, your mouse movement will control the camera, and a double right-click will exit this mode.

def mouse_event_handler(self, event, x, y, flags, param):
    if event == cv.EVENT_LBUTTONDOWN:
        self.mouse_is_pressed = True
        self.last_mouse_position = (x, y)
    elif event == cv.EVENT_LBUTTONUP:
        self.mouse_is_pressed = False
    elif event == cv.EVENT_RBUTTONDOWN:
        self.right_button_mode = True
    elif event == cv.EVENT_RBUTTONDBLCLK:
        self.right_button_mode = False
        self.last_mouse_position = (x, y)
    elif event == cv.EVENT_MOUSEMOVE:
        if self.mouse_is_pressed or self.right_button_mode:
            dx = x - self.last_mouse_position[0]
            dy = y - self.last_mouse_position[1]
            self.camera_system_rotation_yaw += dx
            self.camera_system_rotation_roll += dy
            if self.camera_system_rotation_yaw > 3600:
                self.camera_system_rotation_yaw -= 3599
            if self.camera_system_rotation_roll > 3600:
                self.camera_system_rotation_roll -= 3599
            if self.camera_system_rotation_yaw < 0:
                self.camera_system_rotation_yaw += 3599
            if self.camera_system_rotation_roll < 0:
                self.camera_system_rotation_roll += 3599
            cv.setTrackbarPos("Yaw", self.camera_window_name, self.camera_system_rotation_yaw)
            cv.setTrackbarPos("Roll", self.camera_window_name, self.camera_system_rotation_roll)
            self.last_mouse_position = (x, y)

fps_counter.update()
  • The method updates the current FPS value. It calculates the time difference between the last update and the current one to determine the FPS:

    • delta_time: Time difference between the current and the last frame.

    • fps: Calculated as 1.0 / delta_time, representing the frames per second.

def update(self) -> None:
    timestamp = time.time()
    delta_time = timestamp - self.last_timestamp
    self.last_timestamp = timestamp
    try:
        self.fps = 1.0 / delta_time
    except:
        self.fps = 0
    self.fps_history.append(self.fps)
    if len(self.fps_history) > self.filter_window_size:
        self.fps_history.pop(0)

Lastly, in this module, we resets the camera image to a blank (white) image.

reset_camera_image()
def reset_camera_image(self) -> None:
    self.camera_image.fill(255)