From 1bd7ed1e249435c0211f34d010f3570ee9901f22 Mon Sep 17 00:00:00 2001
From: Vanessa Karolek <vaka1997@uni-koblenz.de>
Date: Mon, 14 Jun 2021 00:41:32 +0200
Subject: [PATCH] [#42][Refactor] huge update - see body text

1) Now there is only one camera class (no inheritance anymore). Furthermore, the camera object is separated from camera controls and only manages camera specific information, such as the view matrix, projection matrix, the position, etc. Camera controls are now handled via camera controller objects.

2) The CameraManager now manages an arbitrary number of camera controllers, such as PilotCameraController or TrackballCameraController (TraceCameraController will be implemented soon!). Before you can use a camera object in your render loop, you need to add at least one camera. You do not have to use a camera controller to render something, but you need to implement camera movements yourself... If you want to CONTROL a camera object, you can make use of the existing camera controller objects. To create a controller, you need to add a camera controller object to the camera manager and specify which camera should be used for this controller. You can change the bound camera whenever you like to. To switch between all existing camera controllers, you can use the active controller in the render loop and change the cycle between all camera controllers via Tab.
See the example projects for an examplary application.

3) Currently, events are only handled for the active camera controller.

4) DISCUSSION: We use the 'new' keyword within the camera manager class. Unfortunately, we did not find any other way to create an object (such as the camera controller) inside the scope of one function and make use of the inheritance (only pointers seem to support this) or to refer to an object that updates everywhere where it is refered. To not lose the object at specified memory location, we need to create it via 'new' and ensure that all allocated objects are deleted within the destructor of the camera manager. Find this lines at the 'TODO' marked locations. If you have a better idea to solve this problem, please let us know. :)
---
 modules/camera/CMakeLists.txt                 |   8 +-
 modules/camera/include/vkcv/camera/Camera.hpp | 140 +++++---------
 .../include/vkcv/camera/CameraController.hpp  |  83 +++++++++
 .../include/vkcv/camera/CameraManager.hpp     | 162 +++++++++++-----
 .../vkcv/camera/PilotCameraController.hpp     | 152 +++++++++++++++
 .../include/vkcv/camera/TrackballCamera.hpp   |  88 ---------
 .../vkcv/camera/TrackballCameraController.hpp | 106 +++++++++++
 modules/camera/src/vkcv/camera/Camera.cpp     | 120 +++---------
 .../src/vkcv/camera/CameraController.cpp      |  29 +++
 .../camera/src/vkcv/camera/CameraManager.cpp  | 160 +++++++++-------
 .../src/vkcv/camera/PilotCameraController.cpp | 176 ++++++++++++++++++
 .../src/vkcv/camera/TrackballCamera.cpp       |  78 --------
 .../vkcv/camera/TrackballCameraController.cpp | 111 +++++++++++
 projects/first_mesh/src/main.cpp              |  19 +-
 projects/first_triangle/src/main.cpp          |  22 ++-
 15 files changed, 980 insertions(+), 474 deletions(-)
 create mode 100644 modules/camera/include/vkcv/camera/CameraController.hpp
 create mode 100644 modules/camera/include/vkcv/camera/PilotCameraController.hpp
 delete mode 100644 modules/camera/include/vkcv/camera/TrackballCamera.hpp
 create mode 100644 modules/camera/include/vkcv/camera/TrackballCameraController.hpp
 create mode 100644 modules/camera/src/vkcv/camera/CameraController.cpp
 create mode 100644 modules/camera/src/vkcv/camera/PilotCameraController.cpp
 delete mode 100644 modules/camera/src/vkcv/camera/TrackballCamera.cpp
 create mode 100644 modules/camera/src/vkcv/camera/TrackballCameraController.cpp

diff --git a/modules/camera/CMakeLists.txt b/modules/camera/CMakeLists.txt
index 28080bf2..5c17d8d3 100644
--- a/modules/camera/CMakeLists.txt
+++ b/modules/camera/CMakeLists.txt
@@ -11,10 +11,14 @@ set(vkcv_camera_include ${PROJECT_SOURCE_DIR}/include)
 set(vkcv_camera_sources
 		${vkcv_camera_include}/vkcv/camera/Camera.hpp
 		${vkcv_camera_source}/vkcv/camera/Camera.cpp
-		${vkcv_camera_include}/vkcv/camera/TrackballCamera.hpp
-		${vkcv_camera_source}/vkcv/camera/TrackballCamera.cpp
 		${vkcv_camera_include}/vkcv/camera/CameraManager.hpp
 		${vkcv_camera_source}/vkcv/camera/CameraManager.cpp
+		${vkcv_camera_include}/vkcv/camera/CameraController.hpp
+		${vkcv_camera_source}/vkcv/camera/CameraController.cpp
+		${vkcv_camera_include}/vkcv/camera/PilotCameraController.hpp
+		${vkcv_camera_source}/vkcv/camera/PilotCameraController.cpp
+		${vkcv_camera_include}/vkcv/camera/TrackballCameraController.hpp
+		${vkcv_camera_source}/vkcv/camera/TrackballCameraController.cpp
 )
 
 # adding source files to the project
diff --git a/modules/camera/include/vkcv/camera/Camera.hpp b/modules/camera/include/vkcv/camera/Camera.hpp
index aa0ec519..d9eacbee 100644
--- a/modules/camera/include/vkcv/camera/Camera.hpp
+++ b/modules/camera/include/vkcv/camera/Camera.hpp
@@ -9,38 +9,25 @@
 namespace vkcv {
 
     /**
-     * @brief Used to create a camera whose position can be changed.
+     * @brief Used to create a camera which governs the view and projection matrices.
      */
-    class Camera {
+    class Camera final {
     protected:
 		glm::mat4 m_view;
 		glm::mat4 m_projection;
 
-		int m_width;
-		int m_height;
-
 		float m_near;
 		float m_far;
 		float m_fov;
 		float m_ratio;
 
-        glm::vec3 m_up;
+		glm::vec3 m_up;
         glm::vec3 m_position;
-        float m_cameraSpeed;
+        glm::vec3 m_center;
+
         float m_pitch;
         float m_yaw;
 
-        int m_fov_nsteps;
-        float m_fov_min;
-        float m_fov_max;
-
-        bool m_forward;
-        bool m_backward;
-        bool m_left;
-        bool m_right;
-        bool m_top;
-        bool m_bottom;
-
     public:
 
         /**
@@ -51,7 +38,7 @@ namespace vkcv {
         /**
          * @brief The destructor of the camera (default behavior)
          */
-        virtual ~Camera();
+        ~Camera();
         
         /**
          * @brief Sets the perspective object according to @p fov, @p ratio, @p near and @p far. This leads to changes in the projection matrix of the camera
@@ -66,14 +53,7 @@ namespace vkcv {
          * @brief Gets the view matrix of the camera
          * @return The view matrix of the camera
          */
-        const glm::mat4& getView();
-
-        /**
-         * @brief Updates the view matrix of the camera with respect to @p deltaTime
-         * @param deltaTime The time that has passed since last update
-         * @return The updated view matrix of the camera
-         */
-        glm::mat4 updateView(double deltaTime);
+        glm::mat4& getView();
 
         /**
          * @brief Sets the view matrix of the camera according to @p position, @p center and @p up
@@ -87,7 +67,7 @@ namespace vkcv {
          * @brief Gets the current projection of the camera
          * @return The current projection matrix
          */
-        const glm::mat4& getProjection();
+        glm::mat4& getProjection();
 
         /**
          * @brief Sets the projection matrix of the camera to @p projection
@@ -95,6 +75,12 @@ namespace vkcv {
          */
         void setProjection(const glm::mat4 projection);
 
+        /**
+         * @brief Gets the model-view-projection matrix of the camera
+         * @return The model-view-projection matrix
+         */
+        glm::mat4 getMVP() const;
+
         /**
          * @brief Gets the near and far bounds of the view frustum of the camera.
          * @param[out] near The near bound of the view frustum
@@ -102,41 +88,29 @@ namespace vkcv {
          */
         void getNearFar(float &near, float &far) const;
 
-        /**
-         * @brief Sets the up vector of the camera to @p up
-         * @param[in] up The new up vector of the camera
-         */
-        void setUp(const glm::vec3 &up);
-
         /**
          * @brief Gets the current field of view of the camera in radians
          * @return[in] The current field of view in radians
          */
-        float getFov() const;
+        const float getFov() const;
 
         /**
          * @brief Sets the field of view of the camera to @p fov in radians
          * @param[in] fov The new field of view in radians
          */
         void setFov(float fov);
-        
+
         /**
-         * @brief Changes the field of view of the camera with an @p offset in degrees
-         * @param[in] offset The offset in degrees
+         * @brief Gets the current aspect ratio of the camera
+         * @return The current aspect ratio of the camera
          */
-        void changeFov(double offset);
+        float getRatio() const;
 
         /**
          * @brief Updates the aspect ratio of the camera with @p ratio and, thus, changes the projection matrix
          * @param[in] ratio The new aspect ratio of the camera
          */
-        void updateRatio(float ratio);
-
-        /**
-         * @brief Gets the current aspect ratio of the camera
-         * @return The current aspect ratio of the camera
-         */
-        float getRatio() const;
+        void setRatio(float ratio);
 
         /**
          * @brief Sets @p near and @p far as new values for the view frustum of the camera. This leads to changes in the projection matrix according to these two values.
@@ -164,78 +138,52 @@ namespace vkcv {
         void setPosition( glm::vec3 position );
 
         /**
-         * @brief Gets the pitch value of the camera in degrees
-         * @return The pitch value in degrees
-         */
-        float getPitch() const;
-
-        /**
-         * @brief Sets the pitch value of the camera to @p pitch in degrees
-         * @param[in] pitch The new pitch value in degrees
-         */
-        void setPitch(float pitch);
-
-        /**
-         * @brief Gets the yaw value of the camera in degrees
-         * @return The yaw value in degrees
+         * @brief Gets the center point.
+         * @return The center point.
          */
-        float getYaw() const;
+        glm::vec3 getCenter() const;
 
         /**
-         * @brief Sets the yaw value of the camera to @p yaw
-         * @param[in] yaw The new yaw value in degrees
+         * @brief Sets @p center as the new center point.
+         * @param center The new center point.
          */
-        void setYaw(float yaw);
+        void setCenter(glm::vec3 center);
 
         /**
-         * @brief Pans the view of the camera according to the pitch and yaw values and additional offsets @p xOffset and @p yOffset (e.g. taken from mouse movement)
-         * @param[in] xOffset The offset added to the yaw value
-         * @param[in] yOffset The offset added to the pitch value
+         * @brief Gets the pitch value of the camera in degrees.
+         * @return The pitch value in degrees.
          */
-        void panView( double xOffset, double yOffset );
-
-        /**
-         * @brief Updates the position of the camera with respect to @p deltaTime
-         * @param[in] deltaTime The time that has passed since last update
-         */
-        void updatePosition(double deltaTime);
-
-        /**
-         * @brief Indicates forward movement of the camera depending on the performed @p action
-         * @param[in] action The performed action
-         */
-        void moveForward(int action);
+        float getPitch() const;
 
         /**
-         * @brief Indicates backward movement of the camera depending on the performed @p action
-         * @param[in] action The performed action
+         * @brief Sets the pitch value of the camera to @p pitch in degrees.
+         * @param[in] pitch The new pitch value in degrees.
          */
-        void moveBackward(int action);
+        void setPitch(float pitch);
 
         /**
-         * @brief Indicates left movement of the camera depending on the performed @p action
-         * @param[in] action The performed action
+         * @brief Gets the yaw value of the camera in degrees.
+         * @return The yaw value in degrees.
          */
-        void moveLeft(int action);
+        float getYaw() const;
 
         /**
-         * @brief Indicates right movement of the camera depending on the performed @p action
-         * @param[in] action The performed action
+         * @brief Sets the yaw value of the camera to @p yaw.
+         * @param[in] yaw The new yaw value in degrees.
          */
-        void moveRight(int action);
+        void setYaw(float yaw);
 
         /**
-         * @brief Indicates top movement of the camera depending on the performed @p action
-         * @param[in] action The performed action
+         * @brief Gets the up vector.
+         * @return The up vector.
          */
-        void moveTop(int action);
+        glm::vec3 getUp() const;
 
         /**
-         * @brief Indicates bottom movement of the camera depending on the performed @p action
-         * @param[in] action The performed action
+         * @brief Sets @p up as the new up vector.
+         * @param up The new up vector.
          */
-        void moveBottom(int action);
-
+        void setUp(const glm::vec3 &up);
     };
 
 }
diff --git a/modules/camera/include/vkcv/camera/CameraController.hpp b/modules/camera/include/vkcv/camera/CameraController.hpp
new file mode 100644
index 00000000..0bdcf788
--- /dev/null
+++ b/modules/camera/include/vkcv/camera/CameraController.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "Camera.hpp"
+#include "vkcv/Window.hpp"
+#include <glfw/glfw3.h>
+
+namespace vkcv {
+
+    /**
+     * @brief Used as a base class for defining camera controller classes with different behaviors, e.g. the
+     * #PilotCameraController.
+     */
+    class CameraController {
+    protected:
+        Camera* m_camera;
+        Window* m_window;
+        double m_lastX;
+        double m_lastY;
+
+    public:
+
+        /**
+         * @brief The constructor of the #CameraController (default behavior).
+         */
+        CameraController() = default;
+
+        /**
+         * @brief Gets the camera object.
+         * @return The camera object.
+         */
+        Camera& getCamera();
+
+        /**
+         * @brief Sets @p camera as the new camera object.
+         * @param camera The new camera object.
+         */
+        virtual void setCamera(Camera &camera);
+
+        /**
+         * @brief Sets @p window as the new window object.
+         * @param window The new window object.
+         */
+        void setWindow(Window &window);
+
+        /**
+         * @brief Updates the camera object in respect to @p deltaTime.
+         * @param deltaTime The time that has passed since last update.
+         */
+        virtual void updateCamera(double deltaTime);
+
+        /**
+         * @brief A callback function for key events.
+         * @param[in] key The keyboard key.
+         * @param[in] scancode The platform-specific scancode.
+         * @param[in] action The key action.
+         * @param[in] mods The modifier bits.
+         */
+        virtual void keyCallback(int key, int scancode, int action, int mods);
+
+        /**
+         * @brief A callback function for mouse scrolling events.
+         * @param[in] offsetX The offset in horizontal direction.
+         * @param[in] offsetY The offset in vertical direction.
+         */
+        virtual void scrollCallback( double offsetX, double offsetY);
+
+        /**
+         * @brief A callback function for mouse movement events.
+         * @param[in] x The horizontal mouse position.
+         * @param[in] y The vertical mouse position.
+         */
+        virtual void mouseMoveCallback(double offsetX, double offsetY);
+
+        /**
+         * @brief A callback function for mouse button events.
+         * @param[in] button The mouse button.
+         * @param[in] action The button action.
+         * @param[in] mods The modifier bits.
+         */
+        virtual void mouseButtonCallback(int button, int action, int mods);
+    };
+
+}
\ No newline at end of file
diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp
index 917d1915..400a4c45 100644
--- a/modules/camera/include/vkcv/camera/CameraManager.hpp
+++ b/modules/camera/include/vkcv/camera/CameraManager.hpp
@@ -1,87 +1,165 @@
 #pragma once
 
-#include "TrackballCamera.hpp"
+#include "PilotCameraController.hpp"
+#include "TrackballCameraController.hpp"
+#include "CameraController.hpp"
 #include "vkcv/Window.hpp"
 #include <GLFW/glfw3.h>
 #include <functional>
 
-namespace vkcv{
+namespace vkcv {
 
+    /**
+     * @brief Used for specifying existing types of camera controllers when adding a new controller object to the
+     * #CameraManager.
+     */
+    enum class ControllerType {
+        PILOT,
+        TRACKBALL,
+        TRACE
+    };
+
+    /**
+     * @brief Used for managing an arbitrary amount of camera controllers.
+     */
     class CameraManager{
     private:
         std::function<void(int, int, int, int)> m_keyHandle;
         std::function<void(double, double)> m_mouseMoveHandle;
         std::function<void(double, double)> m_mouseScrollHandle;
         std::function<void(int, int, int)> m_mouseButtonHandle;
+        std::function<void(int, int)> m_resizeHandle;
 
-        Window &m_window;
-        Camera m_camera;
-        TrackballCamera m_trackball;    // TODO: maybe there is a better way for switching between cameras?
-        float m_width;
-        float m_height;
+        Window& m_window;
+        std::vector<Camera*> m_cameras;
+        std::vector<CameraController*> m_controllers;
+        uint32_t m_activeControllerIndex;
 
-        bool m_rotationActive = false;
-        double m_lastX ;
-        double m_lastY ;
 
         /**
-         * @brief Binds the camera object to the window event handles
+         * @brief Binds the camera object to the window event handles.
          */
-        void bindCamera();
+        void bindCameraToEvents();
 
         /**
-         * @brief A callback function for key events. Currently, 3D camera movement via W, A, S, D, E, Q and window closure via Escape are supported
-         * @param[in] key The keyboard key
-         * @param[in] scancode The platform-specific scancode
-         * @param[in] action The key action
-         * @param[in] mods The modifier bits
+         * @brief A callback function for key events. Currently, cycling between all existing camera controllers via Tab,
+         * window closure via Esc and polling key events from the active camera controller are supported.
+         * @param[in] key The keyboard key.
+         * @param[in] scancode The platform-specific scancode.
+         * @param[in] action The key action.
+         * @param[in] mods The modifier bits.
          */
         void keyCallback(int key, int scancode, int action, int mods);
 
         /**
-         * @brief A callback function for mouse scrolling events. Currently, this leads to changes in the field of view of the camera object
-         * @param[in] offsetX The offset in horizontal direction
-         * @param[in] offsetY The offset in vertical direction
+         * @brief A callback function for mouse scrolling events.
+         * @param[in] offsetX The offset in horizontal direction.
+         * @param[in] offsetY The offset in vertical direction.
          */
-        void scrollCallback( double offsetX, double offsetY);
+        void scrollCallback(double offsetX, double offsetY);
 
         /**
-         * @brief A callback function for mouse movement events. Currently, this leads to panning the view of the camera, if @fn mouseButtonCallback(int button, int action, int mods) enabled panning.
-         * @param[in] offsetX The offset in horizontal direction
-         * @param[in] offsetY The offset in vertical direction
+         * @brief A callback function for mouse movement events.
+         * @param[in] x The horizontal mouse position.
+         * @param[in] y The vertical mouse position.
          */
-        void mouseMoveCallback( double offsetX, double offsetY);
+        void mouseMoveCallback(double x, double y);
 
         /**
-         * @brief A callback function for mouse button events. Currently, the right mouse button enables panning the view of the camera as long as it is pressed.
-         * @param[in] button The mouse button
-         * @param[in] action The button action
-         * @param[in] mods The modifier bits
+         * @brief A callback function for mouse button events.
+         * @param[in] button The mouse button.
+         * @param[in] action The button action.
+         * @param[in] mods The modifier bits.
          */
         void mouseButtonCallback(int button, int action, int mods);
 
+        /**
+         * @brief A callback function for handling the window resizing event. Each existing camera is resized in respect
+         * of the window size.
+         * @param[in] width The new width of the window.
+         * @param[in] height The new height of the window.
+         */
+        void resizeCallback(int width, int height);
+
+
     public:
 
         /**
-         * @brief The constructor
-         * @param[in] window The window
-         * @param[in] width The width of the window
-         * @param[in] height The height of the window
-         * @param[in] up The up vector of the camera. Per default: @code{.cpp} up = glm::vec3(0.0f, 1.0f, 0.0f) @endcode
-         * @param[in] position The position of the camera. Per default: @code{.cpp} position = glm::vec3(0.0f,0.0f,0.0f) @endcode
+         * @brief The constructor of the #CameraManager.
+         * @param[in] window The window.
+         * @param[in] width The width of the window.
+         * @param[in] height The height of the window.
+         */
+        CameraManager(Window &window, float width, float height);
+
+        /**
+         * @brief The destructor of the #CameraManager. Destroying the #CameraManager leads to deletion of all stored
+         * camera objects and camera controller objects.
+         */
+        ~CameraManager();
+
+        /**
+         * @brief Adds a new camera object to the #CameraManager.
+         * @return The index of the newly created camera object.
          */
-        CameraManager(Window &window, float width, float height, glm::vec3 up = glm::vec3(0.0f,1.0f,0.0f), glm::vec3 position = glm::vec3(0.0f,0.0f,0.0f));
+        int addCamera();
 
         /**
-         * @brief Gets the camera object
-         * @return The camera object
+         * @brief Gets the stored camera object located at @p cameraIndex.
+         * @param cameraIndex The index of the stored camera object.
+         * @return The camera object at @p cameraIndex.
          */
-        Camera& getCamera();
+        Camera& getCamera(uint32_t cameraIndex);
 
         /**
-         * @brief Gets the trackball camera object
-         * @return The trackball camera object
+         * @brief Adds a new camera controller object of @p controllerType to the #CameraManager. If the newly created
+         * camera controller object is the first controller object that has been added, it becomes the active
+         * controllerType.
+         * @param[in] controllerType The controllerType type that should be used.
+         * @param[in] cameraIndex The camera index.
+         * @return The index of the camera controllerType.
          */
-        TrackballCamera& getTrackballCamera();
+        int addController(ControllerType controllerType, uint32_t cameraIndex);
+
+        /**
+         * @brief Gets the stored camera controller object located at @p controllerIndex.
+         * @param controllerIndex The controller index.
+         * @return The camera controller object at @p controllerIndex.
+         */
+        CameraController& getController(uint32_t controllerIndex);
+
+        /**
+         * @brief Binds the stored camera object located at @p cameraIndex to the stored camera controller object
+         * located at @p controllerIndex.
+         * @param cameraIndex The camera index.
+         * @param controllerIndex The controller index.
+         */
+        void bindCameraToController(uint32_t cameraIndex, uint32_t controllerIndex);
+
+        /**
+         * @brief Gets the currently active camera controller that is bound to the window.
+         * @return The currently active camera controller object.
+         */
+        CameraController& getActiveController();
+
+        /**
+         * @brief Sets the camera controller located at @p controllerIndex as the active camera controller.
+         * @param controllerIndex The controller index.
+         */
+        void setActiveController(uint32_t controllerIndex);
+
+        /**
+         * @brief Updates all stored camera controllers in respect to @p deltaTime.
+         * @param deltaTime The time that has passed since last update.
+         */
+        void update(double deltaTime);
+
+        /**
+         * @brief Updates the stored camera controller located at @p controllerIndex in respect to @p deltaTime.
+         * @param deltaTime The time that has passed since last update.
+         * @param controllerIndex The controller index.
+         */
+        void update(double deltaTime, uint32_t controllerIndex);
+
     };
 }
diff --git a/modules/camera/include/vkcv/camera/PilotCameraController.hpp b/modules/camera/include/vkcv/camera/PilotCameraController.hpp
new file mode 100644
index 00000000..c164710f
--- /dev/null
+++ b/modules/camera/include/vkcv/camera/PilotCameraController.hpp
@@ -0,0 +1,152 @@
+#pragma once
+
+#include <vkcv/camera/CameraController.hpp>
+
+namespace vkcv {
+
+    /**
+     * @brief Used to move around a camera object in world space.
+     */
+    class PilotCameraController final : public CameraController {
+    private:
+        // camera movement indicators
+        bool m_forward;
+        bool m_backward;
+        bool m_upward;
+        bool m_downward;
+        bool m_left;
+        bool m_right;
+
+        bool m_rotationActive;
+
+        float m_cameraSpeed;
+
+        int m_fov_nsteps;
+        float m_fov_min;
+        float m_fov_max;
+
+
+        /**
+         * @brief Updates the position of the camera with respect to @p deltaTime.
+         * @param[in] deltaTime The time that has passed since last update.
+         * @return The updated camera position.
+         */
+        glm::vec3 updatePosition(double deltaTime);
+
+        /**
+         * @brief Updates the view matrix of the camera with respect to @p deltaTime.
+         * @param deltaTime The time that has passed since last update.
+         * @return The updated view matrix of the camera.
+         */
+        glm::mat4 updateView(double deltaTime);
+
+        /**
+         * @brief Indicates forward movement of the camera depending on the performed @p action.
+         * @param[in] action The performed action.
+         */
+        void moveForward(int action);
+
+        /**
+         * @brief Indicates backward movement of the camera depending on the performed @p action.
+         * @param[in] action The performed action.
+         */
+        void moveBackward(int action);
+
+        /**
+         * @brief Indicates left movement of the camera depending on the performed @p action.
+         * @param[in] action The performed action.
+         */
+        void moveLeft(int action);
+
+        /**
+         * @brief Indicates right movement of the camera depending on the performed @p action.
+         * @param[in] action The performed action.
+         */
+        void moveRight(int action);
+
+        /**
+         * @brief Indicates upward movement of the camera depending on the performed @p action.
+         * @param[in] action The performed action.
+         */
+        void moveUpward(int action);
+
+        /**
+         * @brief Indicates downward movement of the camera depending on the performed @p action.
+         * @param[in] action The performed action.
+         */
+        void moveDownward(int action);
+
+    public:
+
+        /**
+         * @brief The default constructor of the #PilotCameraController.
+         */
+        PilotCameraController();
+
+        /**
+         * @brief The destructor of the #PilotCameraController (default behavior).
+         */
+        ~PilotCameraController() = default;
+
+        /**
+         * @brief Changes the field of view of the camera with an @p offset in degrees.
+         * @param[in] offset The offset in degrees.
+         */
+        void changeFov(double offset);
+
+        /**
+         * @brief Pans the view of the camera according to the pitch and yaw values and additional offsets @p xOffset
+         * and @p yOffset.
+         * @param[in] xOffset The offset added to the yaw value.
+         * @param[in] yOffset The offset added to the pitch value.
+         */
+        void panView(double xOffset, double yOffset);
+
+        /**
+         * @brief Sets @p camera as the new camera object.
+         * @param camera The new camera object.
+         */
+        virtual void setCamera(Camera &camera);
+
+        /**
+         * @brief Updates the camera object in respect to @p deltaTime.
+         * @param deltaTime The time that has passed since last update.
+         */
+        void updateCamera(double deltaTime);
+
+        /**
+         * @brief A callback function for key events. Currently, 3D camera movement via W, A, S, D, E, Q are supported.
+         * @param[in] key The keyboard key.
+         * @param[in] scancode The platform-specific scancode.
+         * @param[in] action The key action.
+         * @param[in] mods The modifier bits.
+         */
+        void keyCallback(int key, int scancode, int action, int mods);
+
+        /**
+         * @brief A callback function for mouse scrolling events. Currently, this leads to changes in the field of view
+         * of the camera object.
+         * @param[in] offsetX The offset in horizontal direction.
+         * @param[in] offsetY The offset in vertical direction.
+         */
+        void scrollCallback( double offsetX, double offsetY);
+
+        /**
+         * @brief A callback function for mouse movement events. Currently, this leads to panning the view of the camera,
+         * if #mouseButtonCallback(int button, int action, int mods) enabled panning.
+         * @param[in] x The horizontal mouse position
+         * @param[in] y The vertical mouse position
+         */
+        void mouseMoveCallback(double x, double y);
+
+        /**
+         * @brief A callback function for mouse button events. Currently, the right mouse button enables panning the
+         * view of the camera as long as it is pressed.
+         * @param[in] button The mouse button
+         * @param[in] action The button action
+         * @param[in] mods The modifier bits
+         */
+        void mouseButtonCallback(int button, int action, int mods);
+    };
+
+}
\ No newline at end of file
diff --git a/modules/camera/include/vkcv/camera/TrackballCamera.hpp b/modules/camera/include/vkcv/camera/TrackballCamera.hpp
deleted file mode 100644
index 21f79f9d..00000000
--- a/modules/camera/include/vkcv/camera/TrackballCamera.hpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#pragma once
-
-#include "Camera.hpp"
-
-namespace vkcv {
-
-    class TrackballCamera : public vkcv::Camera {
-    protected:
-        glm::vec3 m_center;
-        float m_radius;
-        float m_scrollSensitivity;
-
-    public:
-
-        /**
-         * @brief The default constructor of the trackball camera
-         */
-        TrackballCamera();
-
-        /**
-         * @brief The destructor of the trackball camera (default behavior)
-         */
-        ~TrackballCamera();
-
-        /**
-         * @brief Gets the radius of the trackball camera that specifies the distance of the trackball camera to the center point
-         * @return The radius of the trackball camera
-         */
-        float getRadius() const;
-
-        /**
-         * @brief Sets the current radius of the trackball camera to @p radius
-         * @param[in] radius The new radius of the trackball camera
-         */
-        void setRadius( const float radius);
-
-        /**
-         * @brief Gets the center point the trackball camera is looking at
-         * @return The center point of the trackball camera
-         */
-        const glm::vec3& getCenter();
-
-        /**
-         * @brief Sets the current center point of the trackball camera to @p center
-         * @param[in] center The new center point of the trackball camera
-         */
-        void setCenter(const glm::vec3 &center);
-
-        /**
-         * @brief Sets the pitch value of the trackball camera to @p pitch
-         * @param[in] pitch The new pitch value of the trackball camera
-         */
-        void setPitch(float pitch);
-
-        /**
-         * @brief Sets the yaw value of the trackball camera to @p yaw
-         * @param[in] yaw The new yaw value of the trackball camera
-         */
-        void setYaw(float yaw);
-
-        /**
-         * @brief Changes the field of view of the trackball camera with an @p offset in degrees
-         * @param[in] offset The offset in degrees
-         */
-        void changeFov(double offset);
-
-        /**
-         * @brief Pans the view of the trackball camera according to the pitch and yaw values and additional offsets @p xOffset and @p yOffset (e.g. taken from mouse movement)
-         * @param[in] xOffset The offset added to the yaw value
-         * @param[in] yOffset The offset added to the pitch value
-         */
-        void panView(double xOffset, double yOffset);
-
-        /**
-         * @brief Updates the view matrix of the trackball camera with respect to @p deltatime
-         * @param deltaTime The time that has passed since last update
-         * @return The updated view matrix of the trackball camera
-         */
-        glm::mat4 updateView(double deltaTime);
-
-        /**
-         * @brief Updates the position of the trackball camera with respect to @p deltaTime
-         * @param[in] deltaTime The time that has passed since last update
-         */
-        void updatePosition(double deltaTime);
-    };
-
-}
\ No newline at end of file
diff --git a/modules/camera/include/vkcv/camera/TrackballCameraController.hpp b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp
new file mode 100644
index 00000000..31aecf13
--- /dev/null
+++ b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp
@@ -0,0 +1,106 @@
+#pragma once
+
+#include "CameraController.hpp"
+
+namespace vkcv {
+
+    /**
+     * @brief Used to orbit a camera around its center point.
+     */
+    class TrackballCameraController final : public CameraController {
+    private:
+        bool m_rotationActive;
+
+        float m_cameraSpeed;
+        float m_scrollSensitivity;
+        float m_radius;
+
+
+        /**
+         * @brief Updates the position of the camera.
+         * @return The updated camera position.
+         */
+        glm::vec3 updatePosition();
+
+        /**
+         * @brief Updates the view matrix of the camera.
+         * @return The updated view matrix of the camera.
+         */
+        glm::mat4 updateView();
+
+        /**
+         * @brief Updates the current radius in respect to the @p offset.
+         * @param offset The offset between the old and new radius.
+         */
+        void updateRadius(double offset);
+
+    public:
+
+        /**
+         * @brief The default constructor of the #TrackballCameraController.
+         */
+        TrackballCameraController();
+
+        /**
+         * @brief The destructor of the #TrackballCameraController (default behavior).
+         */
+        ~TrackballCameraController() = default;
+
+        /**
+         * @brief Sets @p radius as the new radius for orbiting around the camera's center point.
+         * @param radius The new radius.
+         */
+        void setRadius(const float radius);
+
+        /**
+         * @brief Pans the view of the camera according to the pitch and yaw values and additional offsets @p xOffset
+         * and @p yOffset.
+         * @param[in] xOffset The offset added to the yaw value.
+         * @param[in] yOffset The offset added to the pitch value.
+         */
+        void panView(double xOffset, double yOffset);
+
+        /**
+        * @brief Updates the camera object in respect to @p deltaTime.
+        * @param deltaTime The time that has passed since last update.
+        */
+        void updateCamera(double deltaTime);
+
+        /**
+         * @brief A callback function for key events. Currently, the trackball camera does not support camera movement.
+         * It can only orbit around its center point.
+         * @param[in] key The keyboard key.
+         * @param[in] scancode The platform-specific scancode.
+         * @param[in] action The key action.
+         * @param[in] mods The modifier bits.
+         */
+        void keyCallback(int key, int scancode, int action, int mods);
+
+        /**
+         * @brief A callback function for mouse scrolling events. Currently, this leads to changes in the field of view
+         * of the camera object.
+         * @param[in] offsetX The offset in horizontal direction.
+         * @param[in] offsetY The offset in vertical direction.
+         */
+        void scrollCallback(double offsetX, double offsetY);
+
+        /**
+         * @brief A callback function for mouse movement events. Currently, this leads to panning the view of the
+         * camera, if #mouseButtonCallback(int button, int action, int mods) enabled panning.
+         * @param[in] x The horizontal mouse position.
+         * @param[in] y The vertical mouse position.
+         */
+        void mouseMoveCallback(double x, double y);
+
+        /**
+         * @brief A callback function for mouse button events. Currently, the right mouse button enables panning the
+         * view of the camera as long as it is pressed.
+         * @param[in] button The mouse button.
+         * @param[in] action The button action.
+         * @param[in] mods The modifier bits.
+         */
+        void mouseButtonCallback(int button, int action, int mods);
+
+    };
+
+}
\ No newline at end of file
diff --git a/modules/camera/src/vkcv/camera/Camera.cpp b/modules/camera/src/vkcv/camera/Camera.cpp
index dfda62db..f721b7e2 100644
--- a/modules/camera/src/vkcv/camera/Camera.cpp
+++ b/modules/camera/src/vkcv/camera/Camera.cpp
@@ -3,35 +3,23 @@
 
 namespace vkcv {
 
-    Camera::Camera(){
+    Camera::Camera() {
+        m_position = glm::vec3(0.0f, 0.0f, -1.0f);
         m_up = glm::vec3(0.0f, 1.0f, 0.0f);
-        m_position = glm::vec3(0.0f, 0.0f, 0.0f);
-        m_cameraSpeed = 2.f;
-
-        m_pitch = 0.0;
-        m_yaw = 180.0;
-
-        m_fov_nsteps = 100;
-        m_fov_min = 10;
-        m_fov_max = 120;
-
-        m_forward = false;
-        m_backward = false;
-        m_left = false;
-        m_right = false;
-        m_top = false;
-        m_bottom = false;
+        m_center = glm::vec3(0.0f, 0.0f, 0.0f);
+        lookAt(m_position, m_center, m_up);
+        glm::vec3 front = glm::normalize(m_center - m_position);
+        m_pitch = atan2(front.y, sqrt(front.x * front.x + front.z * front.z));
+        m_yaw = atan2(front.x, front.z);
     }
 
     Camera::~Camera() = default;
 
     void Camera::lookAt(glm::vec3 position, glm::vec3 center, glm::vec3 up){
         m_view = glm::lookAt(position, center, up);
-    }
-
-    glm::mat4 Camera::updateView(double deltaTime){
-        updatePosition(deltaTime);
-        return m_view = glm::lookAt(m_position, m_position + getFront() , m_up);
+        m_position = position;
+        m_up = up;
+        m_center = center;
     }
 
     void Camera::getNearFar( float &near, float &far) const {
@@ -39,12 +27,11 @@ namespace vkcv {
         far = m_far;
     }
 
-
-    const glm::mat4& Camera::getView() {
+    glm::mat4& Camera::getView() {
         return m_view;
     }
 
-    const glm::mat4& Camera::getProjection() {
+    glm::mat4& Camera::getProjection() {
         return m_projection;
     }
 
@@ -52,7 +39,11 @@ namespace vkcv {
         m_projection = projection;
     }
 
-    float Camera::getFov() const {
+    glm::mat4 Camera::getMVP() const {
+        return m_projection * m_view;
+    }
+
+    const float Camera::getFov() const {
         return m_fov;
     }
 
@@ -61,29 +52,15 @@ namespace vkcv {
         setPerspective( m_fov, m_ratio, m_near, m_far);
     }
 
-    void Camera::changeFov(double offset){
-        float fov = m_fov;
-        float fov_range = m_fov_max - m_fov_min;
-        float fov_stepsize = glm::radians(fov_range)/m_fov_nsteps;
-        fov -= (float) offset*fov_stepsize;
-        if (fov < glm::radians(m_fov_min)) {
-            fov = glm::radians(m_fov_min);
-        }
-        if (fov > glm::radians(m_fov_max)) {
-            fov = glm::radians(m_fov_max);
-        }
-        setFov(fov);
+    float Camera::getRatio() const {
+        return m_ratio;
     }
 
-    void Camera::updateRatio( float ratio){
+    void Camera::setRatio(float ratio){
         m_ratio = ratio;
         setPerspective( m_fov, m_ratio, m_near, m_far);
     }
 
-    float Camera::getRatio() const {
-        return 0.0f;
-    }
-
     void Camera::setNearFar( float near, float far){
         m_near = near;
         m_far = far;
@@ -114,8 +91,12 @@ namespace vkcv {
         m_position = position;
     }
 
-    void Camera::setUp(const glm::vec3 &up) {
-        m_up = up;
+    glm::vec3 Camera::getCenter() const {
+        return m_center;
+    }
+
+    void Camera::setCenter(glm::vec3 center) {
+        m_center = center;
     }
 
     float Camera::getPitch() const {
@@ -123,12 +104,6 @@ namespace vkcv {
     }
 
     void Camera::setPitch(float pitch) {
-        if (pitch > 89.0f) {
-            pitch = 89.0f;
-        }
-        if (pitch < -89.0f) {
-            pitch = -89.0f;
-        }
         m_pitch = pitch;
     }
 
@@ -137,50 +112,15 @@ namespace vkcv {
     }
 
     void Camera::setYaw(float yaw) {
-        if (yaw < 0.0f) {
-            yaw += 360.0f;
-        }
-        else if (yaw > 360.0f) {
-            yaw -= 360.0f;
-        }
         m_yaw = yaw;
     }
 
-    void Camera::panView(double xOffset, double yOffset) {
-        setYaw(m_yaw + xOffset);
-        setPitch(m_pitch + yOffset);
-    }
-
-    void Camera::updatePosition(double deltaTime ){
-        m_position += (m_cameraSpeed * getFront() * static_cast<float> (m_forward) * static_cast<float>(deltaTime));
-        m_position -= (m_cameraSpeed * getFront() * static_cast<float> (m_backward) * static_cast<float>(deltaTime));
-        m_position += (glm::normalize(glm::cross(getFront(), m_up)) * m_cameraSpeed * static_cast<float> (m_left) * static_cast<float>(deltaTime));
-        m_position -= (glm::normalize(glm::cross(getFront(), m_up)) * m_cameraSpeed * static_cast<float> (m_right) * static_cast<float>(deltaTime));
-        m_position -= m_up * m_cameraSpeed * static_cast<float> (m_top) * static_cast<float>(deltaTime);
-        m_position += m_up * m_cameraSpeed * static_cast<float> (m_bottom) * static_cast<float>(deltaTime);
-    }
-
-    void Camera::moveForward(int action){
-        m_forward = static_cast<bool>(action);
-    }
-
-    void Camera::moveBackward(int action){
-        m_backward = static_cast<bool>(action);
+    glm::vec3 Camera::getUp() const {
+        return m_up;
     }
 
-    void Camera::moveLeft(int action){
-        m_left = static_cast<bool>(action);
-    }
-
-    void Camera::moveRight(int action){
-        m_right = static_cast<bool>(action);
-    }
-
-    void Camera::moveTop(int action){
-        m_top = static_cast<bool>(action);
+    void Camera::setUp(const glm::vec3 &up) {
+        m_up = up;
     }
 
-    void Camera::moveBottom(int action){
-        m_bottom = static_cast<bool>(action);
-    }
 }
\ No newline at end of file
diff --git a/modules/camera/src/vkcv/camera/CameraController.cpp b/modules/camera/src/vkcv/camera/CameraController.cpp
new file mode 100644
index 00000000..477d9380
--- /dev/null
+++ b/modules/camera/src/vkcv/camera/CameraController.cpp
@@ -0,0 +1,29 @@
+#include "vkcv/camera/CameraController.hpp"
+
+namespace vkcv {
+
+    Camera& CameraController::getCamera() {
+        return *m_camera;
+    }
+
+    void CameraController::setCamera(Camera &camera) {
+        m_camera = &camera;
+    }
+
+    void CameraController::setWindow(Window &window) {
+        m_window = &window;
+        m_lastX = m_window->getWidth() / 2.0;
+        m_lastY = m_window->getHeight() / 2.0;
+    }
+
+    void CameraController::updateCamera(double deltaTime) {}
+
+    void CameraController::keyCallback(int key, int scancode, int action, int mods) {}
+
+    void CameraController::scrollCallback( double offsetX, double offsetY) {}
+
+    void CameraController::mouseMoveCallback(double offsetX, double offsetY) {}
+
+    void CameraController::mouseButtonCallback(int button, int action, int mods) {}
+
+}
\ No newline at end of file
diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp
index 1358d39f..35556abb 100644
--- a/modules/camera/src/vkcv/camera/CameraManager.cpp
+++ b/modules/camera/src/vkcv/camera/CameraManager.cpp
@@ -3,95 +3,129 @@
 
 namespace vkcv{
 
-    CameraManager::CameraManager(Window &window, float width, float height, glm::vec3 up, glm::vec3 position):
-    m_window(window), m_width(width), m_height(height)
+    CameraManager::CameraManager(Window &window, float width, float height)
+    : m_window(window)
     {
-        m_camera.setUp(up);
-        m_camera.setPosition(position);
-        m_camera.setPerspective( glm::radians(60.0f), m_width / m_height, 0.1f, 10.f);
-        m_trackball.setUp(up);
-        m_trackball.setPosition(position);
-        m_trackball.setPerspective( glm::radians(60.0f), m_width / m_height, 0.1f, 10.f);
-        m_lastX = width/2.0;
-        m_lastY = height/2.0;
-        bindCamera();
-    }
-
-    void CameraManager::bindCamera(){
+        bindCameraToEvents();
+    }
+
+    CameraManager::~CameraManager() {
+        // free memory of allocated pointers (specified with 'new')
+        for (auto controller : m_controllers) {
+            delete controller;
+        }
+
+        for (auto camera : m_cameras) {
+            delete camera;
+        }
+    }
+
+    void CameraManager::bindCameraToEvents() {
         m_keyHandle = m_window.e_key.add( [&](int key, int scancode, int action, int mods) { this->keyCallback(key, scancode, action, mods); });
         m_mouseMoveHandle = m_window.e_mouseMove.add( [&]( double offsetX, double offsetY) {this->mouseMoveCallback( offsetX, offsetY);} );
         m_mouseScrollHandle =  m_window.e_mouseScroll.add([&](double offsetX, double offsetY) {this->scrollCallback( offsetX, offsetY);} );
         m_mouseButtonHandle = m_window.e_mouseButton.add([&] (int button, int action, int mods) {this->mouseButtonCallback( button,  action,  mods);});
+        m_resizeHandle = m_window.e_resize.add([&](int width, int height) {this->resizeCallback(width, height);});
     }
 
-    void CameraManager::mouseButtonCallback(int button, int action, int mods){
-        if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == false && action == GLFW_PRESS){
-            glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED);
-            m_rotationActive = true;
-        }else if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == true && action == GLFW_RELEASE){
-            glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL);
-            m_rotationActive = false;
+    void CameraManager::resizeCallback(int width, int height) {
+        for (size_t i = 0; i < m_cameras.size(); i++) {
+            getCamera(i).setRatio(static_cast<float>(width) / static_cast<float>(height));;
         }
     }
 
+    void CameraManager::mouseButtonCallback(int button, int action, int mods){
+        m_controllers[m_activeControllerIndex]->mouseButtonCallback(button, action, mods);
+    }
+
     void CameraManager::mouseMoveCallback(double x, double y){
-        float xoffset = x - m_lastX;
-        float yoffset = m_lastY - y;
-        m_lastX = x;
-        m_lastY = y;
+        m_controllers[m_activeControllerIndex]->mouseMoveCallback(x, y);
+    }
 
-        if(!m_rotationActive){
-            return;
-        }
+    void CameraManager::scrollCallback(double offsetX, double offsetY) {
+        m_controllers[m_activeControllerIndex]->scrollCallback(offsetX, offsetY);
+    }
 
-        float sensitivity = 0.05f;
-        xoffset *= sensitivity;
-        yoffset *= sensitivity;
+    void CameraManager::keyCallback(int key, int scancode, int action, int mods)  {
+        switch (action) {
+            case GLFW_RELEASE:
+                switch (key) {
+                    case GLFW_KEY_TAB:
+                        if (m_activeControllerIndex + 1 == m_controllers.size()) {
+                            m_activeControllerIndex = 0;
+                        }
+                        else {
+                            m_activeControllerIndex++;
+                        }
+                        return;
+                    case GLFW_KEY_ESCAPE:
+                        glfwSetWindowShouldClose(m_window.getWindow(), 1);
+                        return;
+                }
+            default:
+                m_controllers[m_activeControllerIndex]->keyCallback(key, scancode, action, mods);
+        }
+    }
 
-        m_camera.panView( xoffset , yoffset );
-        m_trackball.panView( xoffset , yoffset );
+    int CameraManager::addCamera() {
+        m_cameras.push_back(new Camera());  // TODO: is there another way we can do this?
+        m_cameras.back()->setPerspective(glm::radians(60.0f), m_window.getWidth() / m_window.getHeight(), 0.1f, 10.0f);
+        return m_cameras.size() - 1;
     }
 
-    void CameraManager::scrollCallback(double offsetX, double offsetY) {
-        m_camera.changeFov(offsetY);
-        m_trackball.changeFov(offsetY);
+    Camera& CameraManager::getCamera(uint32_t cameraIndex) {
+        return *m_cameras[cameraIndex];
     }
 
-    void CameraManager::keyCallback(int key, int scancode, int action, int mods) {
-        switch (key) {
-            case GLFW_KEY_W:
-                m_camera.moveForward(action);
-                break;
-            case GLFW_KEY_S:
-                m_camera.moveBackward(action);
-                break;
-            case GLFW_KEY_A:
-                m_camera.moveLeft(action);
-                break;
-            case GLFW_KEY_D:
-                m_camera.moveRight(action);
-                break;
-            case GLFW_KEY_E:
-                m_camera.moveTop(action);
-                break;
-            case GLFW_KEY_Q:
-                m_camera.moveBottom(action);
-                break;
-            case GLFW_KEY_ESCAPE:
-                glfwSetWindowShouldClose(m_window.getWindow(), 1);
+
+    int CameraManager::addController(ControllerType controllerType, uint32_t cameraIndex) {
+        switch(controllerType) {
+            case ControllerType::PILOT: {
+                m_controllers.push_back(new PilotCameraController());   // TODO: is there another way we can do this?
                 break;
-            default:
+            }
+            case ControllerType::TRACKBALL: {
+                m_controllers.push_back(new TrackballCameraController());
                 break;
+            }
+            case ControllerType::TRACE: {
+                // TODO: implement (Josch)
+            }
         }
+
+        m_controllers.back()->setWindow(m_window);
+
+        int controllerIndex = m_controllers.size() - 1;
+        bindCameraToController(cameraIndex, controllerIndex);
+
+        if (controllerIndex == 0) {
+            setActiveController(0);
+        }
+
+        return controllerIndex;
     }
 
-    Camera& CameraManager::getCamera(){
-        return m_camera;
+    CameraController& CameraManager::getController(uint32_t controllerIndex) {
+        return *m_controllers[controllerIndex];
     }
 
-    TrackballCamera& CameraManager::getTrackballCamera() {
-        return m_trackball;
+    void CameraManager::bindCameraToController(uint32_t cameraIndex, uint32_t controllerIndex) {
+        m_controllers[controllerIndex]->setCamera(*m_cameras[cameraIndex]);
     }
 
+    CameraController& CameraManager::getActiveController() {
+        return *m_controllers[m_activeControllerIndex];
+    }
 
+    void CameraManager::setActiveController(uint32_t controllerIndex) {
+        m_activeControllerIndex = controllerIndex;
+    }
+
+    void CameraManager::update(double deltaTime) {
+        m_controllers[m_activeControllerIndex]->updateCamera(deltaTime);
+    }
+
+    void CameraManager::update(double deltaTime, uint32_t controllerIndex) {
+        m_controllers[controllerIndex]->updateCamera(deltaTime);
+    }
 }
\ No newline at end of file
diff --git a/modules/camera/src/vkcv/camera/PilotCameraController.cpp b/modules/camera/src/vkcv/camera/PilotCameraController.cpp
new file mode 100644
index 00000000..05bb6276
--- /dev/null
+++ b/modules/camera/src/vkcv/camera/PilotCameraController.cpp
@@ -0,0 +1,176 @@
+#include "vkcv/camera/PilotCameraController.hpp"
+#include <iostream>
+
+namespace vkcv {
+
+    PilotCameraController::PilotCameraController() {
+        m_forward = false;
+        m_backward = false;
+        m_upward = false;
+        m_downward = false;
+        m_left = false;
+        m_right = false;
+
+        m_rotationActive = false;
+
+        m_cameraSpeed = 2.0f;
+
+        m_fov_nsteps = 100;
+        m_fov_min = 10;
+        m_fov_max = 120;
+
+        m_lastX = 0.0;
+        m_lastY = 0.0;
+    }
+
+    void PilotCameraController::changeFov(double offset){
+        float fov = m_camera->getFov();
+        float fov_range = m_fov_max - m_fov_min;
+        float fov_stepsize = glm::radians(fov_range)/m_fov_nsteps;
+        fov -= (float) offset*fov_stepsize;
+        if (fov < glm::radians(m_fov_min)) {
+            fov = glm::radians(m_fov_min);
+        }
+        if (fov > glm::radians(m_fov_max)) {
+            fov = glm::radians(m_fov_max);
+        }
+        m_camera->setFov(fov);
+    }
+
+    void PilotCameraController::panView(double xOffset, double yOffset) {
+        // handle yaw rotation
+        float yaw = m_camera->getYaw() + xOffset;
+        if (yaw < -180.0f) {
+            yaw += 360.0f;
+        }
+        else if (yaw > 180.0f) {
+            yaw -= 360.0f;
+        }
+        m_camera->setYaw(yaw);
+
+        // handle pitch rotation
+        float pitch = m_camera->getPitch() - yOffset;
+        if (pitch > 89.0f) {
+            pitch = 89.0f;
+        }
+        if (pitch < -89.0f) {
+            pitch = -89.0f;
+        }
+        m_camera->setPitch(pitch);
+    }
+
+    glm::mat4 PilotCameraController::updateView(double deltaTime){
+        updatePosition(deltaTime);
+        glm::vec3 position = m_camera->getPosition();
+        glm::vec3 front = m_camera->getFront();
+        glm::vec3 up = m_camera->getUp();
+        m_camera->lookAt(position, position + front, up);
+        return m_camera->getView();
+    }
+
+    glm::vec3 PilotCameraController::updatePosition(double deltaTime ){
+        glm::vec3 position = m_camera->getPosition();
+        glm::vec3 front = m_camera->getFront();
+        glm::vec3 up = m_camera->getUp();
+        position += (m_cameraSpeed * front * static_cast<float> (m_forward) * static_cast<float>(deltaTime));
+        position -= (m_cameraSpeed * front * static_cast<float> (m_backward) * static_cast<float>(deltaTime));
+        position += (glm::normalize(glm::cross(front, up)) * m_cameraSpeed * static_cast<float> (m_left) * static_cast<float>(deltaTime));
+        position -= (glm::normalize(glm::cross(front, up)) * m_cameraSpeed * static_cast<float> (m_right) * static_cast<float>(deltaTime));
+        position -= up * m_cameraSpeed * static_cast<float> (m_upward) * static_cast<float>(deltaTime);
+        position += up * m_cameraSpeed * static_cast<float> (m_downward) * static_cast<float>(deltaTime);
+        m_camera->setPosition(position);
+        return position;
+    }
+
+    void PilotCameraController::setCamera(Camera &camera) {
+        m_camera = &camera;
+        m_camera->setYaw(180.0f);
+    }
+
+    void PilotCameraController::updateCamera(double deltaTime) {
+        updateView(deltaTime);
+    }
+
+    void PilotCameraController::keyCallback(int key, int scancode, int action, int mods) {
+        switch (key) {
+            case GLFW_KEY_W:
+                moveForward(action);
+                break;
+            case GLFW_KEY_S:
+                moveBackward(action);
+                break;
+            case GLFW_KEY_A:
+                moveLeft(action);
+                break;
+            case GLFW_KEY_D:
+                moveRight(action);
+                break;
+            case GLFW_KEY_E:
+                moveUpward(action);
+                break;
+            case GLFW_KEY_Q:
+                moveDownward(action);
+                break;
+            default:
+                break;
+        }
+    }
+
+    void PilotCameraController::scrollCallback(double offsetX, double offsetY) {
+        changeFov(offsetY);
+    }
+
+    void PilotCameraController::mouseMoveCallback(double x, double y) {
+        float xoffset = x - m_lastX;
+        float yoffset = m_lastY - y;
+        m_lastX = x;
+        m_lastY = y;
+
+        if(!m_rotationActive){
+            return;
+        }
+
+        float sensitivity = 0.05f;
+        xoffset *= sensitivity;
+        yoffset *= sensitivity;
+
+        panView(xoffset , yoffset);
+    }
+
+    void PilotCameraController::mouseButtonCallback(int button, int action, int mods) {
+        if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == false && action == GLFW_PRESS){
+            glfwSetInputMode(m_window->getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+            m_rotationActive = true;
+        }
+        else if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == true && action == GLFW_RELEASE){
+            glfwSetInputMode(m_window->getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+            m_rotationActive = false;
+        }
+    }
+
+
+    void PilotCameraController::moveForward(int action){
+        m_forward = static_cast<bool>(action);
+    }
+
+    void PilotCameraController::moveBackward(int action){
+        m_backward = static_cast<bool>(action);
+    }
+
+    void PilotCameraController::moveLeft(int action){
+        m_left = static_cast<bool>(action);
+    }
+
+    void PilotCameraController::moveRight(int action){
+        m_right = static_cast<bool>(action);
+    }
+
+    void PilotCameraController::moveUpward(int action){
+        m_upward = static_cast<bool>(action);
+    }
+
+    void PilotCameraController::moveDownward(int action){
+        m_downward = static_cast<bool>(action);
+    }
+
+}
\ No newline at end of file
diff --git a/modules/camera/src/vkcv/camera/TrackballCamera.cpp b/modules/camera/src/vkcv/camera/TrackballCamera.cpp
deleted file mode 100644
index e99540e9..00000000
--- a/modules/camera/src/vkcv/camera/TrackballCamera.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "vkcv/camera/TrackballCamera.hpp"
-#include <iostream>
-
-namespace vkcv {
-
-    TrackballCamera::TrackballCamera() {
-        m_pitch = 0.0f;
-        m_yaw = 0.0f;
-        m_radius = 1.5f;
-        m_center = glm::vec3(0.0f,0.0f,0.0f);
-        m_cameraSpeed = 5.0f;
-        m_scrollSensitivity = 0.05f;
-    }
-
-    TrackballCamera::~TrackballCamera() = default;
-
-    float TrackballCamera::getRadius() const{
-        return m_radius;
-    }
-
-    void TrackballCamera::setRadius( const float radius) {
-        if (m_radius < 0.1f) {
-            m_radius = 0.1f;
-        }
-        m_radius = radius;
-    }
-
-    const glm::vec3& TrackballCamera::getCenter() {
-        return m_center;
-    }
-
-    void TrackballCamera::setCenter(const glm::vec3 &center) {
-        m_center = center;
-    }
-
-    void TrackballCamera::setPitch(float pitch) {
-        if (pitch < 0.0f) {
-            pitch += 360.0f;
-        }
-        else if (pitch > 360.0f) {
-            pitch -= 360.0f;
-        }
-        m_pitch = pitch;
-    }
-
-    void TrackballCamera::setYaw(float yaw) {
-        if (yaw < 0.0f) {
-            yaw += 360.0f;
-        }
-        else if (yaw > 360.0f) {
-            yaw -= 360.0f;
-        }
-        m_yaw = yaw;
-    }
-
-    void TrackballCamera::changeFov(double offset) {
-        setRadius(m_radius - offset * m_scrollSensitivity);
-    }
-
-    void TrackballCamera::panView(double xOffset, double yOffset) {
-        setYaw(m_yaw + xOffset * m_cameraSpeed);
-        setPitch(m_pitch + yOffset * m_cameraSpeed);
-    }
-
-    glm::mat4 TrackballCamera::updateView(double deltaTime)  {
-        updatePosition(deltaTime);
-        return m_view = glm::lookAt(m_position, m_center, m_up);
-    }
-
-    void TrackballCamera::updatePosition(double deltaTime) {
-        glm::mat4 rotationY = glm::rotate(glm::mat4(1.0f), glm::radians(m_yaw), glm::vec3(0.0f, 1.0f, 0.0f));
-        glm::mat4 rotationX = glm::rotate(rotationY, glm::radians(m_pitch), glm::vec3(1.0f, 0.0f, 0.0f));
-        glm::vec3 translate = glm::vec3(0.0f,0.0f,m_radius);
-        translate = glm::vec3(rotationX * glm::vec4(translate, 0.0f));
-        m_position = m_center + translate;
-        m_up = glm::vec3(rotationX * glm::vec4(glm::vec3(0.0f, 1.0f, 0.0f), 0.0f));
-    }
-}
\ No newline at end of file
diff --git a/modules/camera/src/vkcv/camera/TrackballCameraController.cpp b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp
new file mode 100644
index 00000000..90673de3
--- /dev/null
+++ b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp
@@ -0,0 +1,111 @@
+#include "vkcv/camera/TrackballCameraController.hpp"
+
+namespace vkcv {
+
+    TrackballCameraController::TrackballCameraController() {
+        m_rotationActive = false;
+        m_radius = 3.0f;
+        m_cameraSpeed = 2.5f;
+        m_scrollSensitivity = 0.05f;
+    }
+
+    void TrackballCameraController::setRadius(const float radius) {
+        if (radius < 0.1f) {
+            m_radius = 0.1f;
+        }
+        else {
+            m_radius = radius;
+        }
+    }
+
+    void TrackballCameraController::panView(double xOffset, double yOffset) {
+        // handle yaw rotation
+        float yaw = m_camera->getYaw() + xOffset * m_cameraSpeed;
+        if (yaw < 0.0f) {
+            yaw += 360.0f;
+        }
+        else if (yaw > 360.0f) {
+            yaw -= 360.0f;
+        }
+        m_camera->setYaw(yaw);
+
+        // handle pitch rotation
+        float pitch = m_camera->getPitch() + yOffset * m_cameraSpeed;
+        if (pitch < 0.0f) {
+            pitch += 360.0f;
+        }
+        else if (pitch > 360.0f) {
+            pitch -= 360.0f;
+        }
+        m_camera->setPitch(pitch);
+    }
+
+    glm::vec3 TrackballCameraController::updatePosition() {
+        float yaw = m_camera->getYaw();
+        float pitch = m_camera->getPitch();
+        glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f);
+        glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f);
+
+        glm::mat4 rotationY = glm::rotate(glm::mat4(1.0f), glm::radians(yaw), yAxis);
+        glm::mat4 rotationX = glm::rotate(rotationY, glm::radians(pitch), xAxis);
+        glm::vec3 translate = glm::vec3(0.0f, 0.0f, m_radius);
+        translate = glm::vec3(rotationX * glm::vec4(translate, 0.0f));
+        glm::vec3 center = m_camera->getCenter();
+        glm::vec3 position = center +translate;
+        m_camera->setPosition(position);
+        glm::vec3 up = glm::vec3(rotationX * glm::vec4(glm::vec3(0.0f, 1.0f, 0.0f), 0.0f));
+        m_camera->setUp(up);
+        return position;
+    }
+
+    glm::mat4 TrackballCameraController::updateView() {
+        updatePosition();
+        glm::vec3 position = m_camera->getPosition();
+        glm::vec3 center = m_camera->getCenter();
+        glm::vec3 up = m_camera->getUp();
+        m_camera->lookAt(position, center, up);
+        return m_camera->getView();
+    }
+
+    void TrackballCameraController::updateRadius(double offset) {
+        setRadius(m_radius - offset * m_scrollSensitivity);
+    }
+
+    void TrackballCameraController::updateCamera(double deltaTime) {
+        updateView();
+    }
+
+    void TrackballCameraController::keyCallback(int key, int scancode, int action, int mods) {}
+
+    void TrackballCameraController::scrollCallback(double offsetX, double offsetY) {
+        updateRadius(offsetY);
+    }
+
+    void TrackballCameraController::mouseMoveCallback(double x, double y) {
+        float xoffset = x - m_lastX;
+        float yoffset = m_lastY - y;
+        m_lastX = x;
+        m_lastY = y;
+
+        if(!m_rotationActive){
+            return;
+        }
+
+        float sensitivity = 0.05f;
+        xoffset *= sensitivity;
+        yoffset *= sensitivity;
+
+        panView(xoffset , yoffset);
+    }
+
+    void TrackballCameraController::mouseButtonCallback(int button, int action, int mods) {
+        if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == false && action == GLFW_PRESS){
+            glfwSetInputMode(m_window->getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+            m_rotationActive = true;
+        }
+        else if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == true && action == GLFW_RELEASE){
+            glfwSetInputMode(m_window->getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+            m_rotationActive = false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 93d8103d..0fb352ac 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -17,8 +17,6 @@ int main(int argc, const char** argv) {
 		false
 	);
 
-	vkcv::CameraManager cameraManager(window, windowWidth, windowHeight);
-
 	window.initEvents();
 
 	vkcv::Core core = vkcv::Core::create(
@@ -140,17 +138,24 @@ int main(int argc, const char** argv) {
 	setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(1, sampler) };
 	core.writeResourceDescription(set, 0, setWrites);
 
-	auto start = std::chrono::system_clock::now();
+    vkcv::CameraManager cameraManager(window, windowWidth, windowHeight);
+    uint32_t camIndex = cameraManager.addCamera();
+    uint32_t controllerIndex = cameraManager.addController(vkcv::ControllerType::PILOT, camIndex);
+
+    uint32_t camIndex2 = cameraManager.addCamera();
+    uint32_t controllerIndex2 = cameraManager.addController(vkcv::ControllerType::TRACKBALL, camIndex2);
+
+
+    auto start = std::chrono::system_clock::now();
 	while (window.isWindowOpen()) {
 		core.beginFrame();
 		window.pollEvents();
 		auto end = std::chrono::system_clock::now();
 		auto deltatime = end - start;
 		start = end;
-//		cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count());
-//		const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView();
-        cameraManager.getTrackballCamera().updateView(std::chrono::duration<double>(deltatime).count());
-        const glm::mat4 mvp = cameraManager.getTrackballCamera().getProjection() * cameraManager.getTrackballCamera().getView();
+		cameraManager.update(std::chrono::duration<double>(deltatime).count());
+        glm::mat4 mvp = cameraManager.getActiveController().getCamera().getMVP();
+
 
 		core.renderMesh(
 			trianglePass,
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 5568a497..1e8ae53c 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -16,10 +16,6 @@ int main(int argc, const char** argv) {
             false
     );
 
-    vkcv::CameraManager cameraManager(window, windowWidth, windowHeight);
-    cameraManager.getTrackballCamera().setPosition(glm::vec3(0.0f,0.5f,0.0f));
-    cameraManager.getTrackballCamera().setCenter(glm::vec3(0.0f,0.0f,-1.0f));
-
     window.initEvents();
 
     vkcv::Core core = vkcv::Core::create(
@@ -135,6 +131,18 @@ int main(int argc, const char** argv) {
      *
      * PipelineHandle trianglePipeline = core.CreatePipeline(trianglePipeline);
      */
+
+    vkcv::CameraManager cameraManager(window, windowWidth, windowHeight);
+    uint32_t camIndex = cameraManager.addCamera();
+    cameraManager.getCamera(camIndex).setPosition(glm::vec3(0.0f, 0.0f, 0.0f));
+    cameraManager.getCamera(camIndex).setCenter(glm::vec3(0.0f, 0.0f, -1.0f));
+    uint32_t controllerIndex = cameraManager.addController(vkcv::ControllerType::PILOT, camIndex);
+
+    uint32_t camIndex2 = cameraManager.addCamera();
+    cameraManager.getCamera(camIndex2).setPosition(glm::vec3(0.0f, 0.0f, 0.0f));
+    cameraManager.getCamera(camIndex2).setCenter(glm::vec3(0.0f, 0.0f, -1.0f));
+    uint32_t controllerIndex2 = cameraManager.addController(vkcv::ControllerType::TRACKBALL, camIndex2);
+
     auto start = std::chrono::system_clock::now();
     while (window.isWindowOpen())
     {
@@ -143,10 +151,8 @@ int main(int argc, const char** argv) {
         auto end = std::chrono::system_clock::now();
         auto deltatime = end - start;
         start = end;
-//        cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count());
-//        const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView();
-        cameraManager.getTrackballCamera().updateView(std::chrono::duration<double>(deltatime).count());
-        const glm::mat4 mvp = cameraManager.getTrackballCamera().getProjection() * cameraManager.getTrackballCamera().getView();
+        cameraManager.update(std::chrono::duration<double>(deltatime).count());
+        glm::mat4 mvp = cameraManager.getActiveController().getCamera().getMVP();
 
         core.renderMesh(
                 trianglePass,
-- 
GitLab