From 4b3eb84627fa3313250f285556622320de7c8d55 Mon Sep 17 00:00:00 2001 From: Ruixiang Du Date: Wed, 6 Nov 2024 00:17:20 +0800 Subject: [PATCH] opengl: added camera controller, to be tweaked --- src/app/panels/scene_panel.cpp | 17 ++--- src/app/panels/scene_panel.hpp | 4 + .../imview/component/opengl/camera.hpp | 24 ++++-- .../component/opengl/camera_controller.hpp | 23 +++++- .../include/imview/component/opengl/grid.hpp | 2 + .../imview/component/opengl/triangle.hpp | 2 + .../imview/interface/opengl_drawable.hpp | 2 + src/imview/src/component/opengl/camera.cpp | 27 +++++++ .../component/opengl/camera_controller.cpp | 74 ++++++++++++++++++- .../src/component/opengl/frame_buffer.cpp | 1 - src/imview/src/component/opengl/grid.cpp | 4 + src/imview/src/component/opengl/triangle.cpp | 4 + src/imview/test/test_camera.cpp | 49 +++++++++--- 13 files changed, 202 insertions(+), 31 deletions(-) diff --git a/src/app/panels/scene_panel.cpp b/src/app/panels/scene_panel.cpp index 85fffc5..8a5682c 100644 --- a/src/app/panels/scene_panel.cpp +++ b/src/app/panels/scene_panel.cpp @@ -20,6 +20,9 @@ ScenePanel::ScenePanel(const std::string& panel_name) : GlWidget(panel_name) { this->SetNoTitleBar(true); this->SetNoBackground(true); + camera_ = + std::make_unique(glm::vec3(0.0f, 3.0f, 8.0f), -90.0f, -25.0f); + auto grid = std::make_unique(10.0f, 1.0f, glm::vec3(0.7f, 0.7f, 0.7f)); this->AddOpenGLObject("grid", std::move(grid)); } @@ -27,19 +30,11 @@ ScenePanel::ScenePanel(const std::string& panel_name) : GlWidget(panel_name) { void ScenePanel::Draw() { ImVec2 content_size = ImGui::GetContentRegionAvail(); - // Orthographic projection for a top-down view + // get view matrices from camera float aspect_ratio = static_cast(content_size.x) / static_cast(content_size.y); - // std::cout << "aspect ratio: " << aspect_ratio << std::endl; - glm::mat4 projection = - glm::perspective(glm::radians(45.0f), aspect_ratio, 0.1f, 100.0f); - - // Simple view matrix looking at an angle - glm::mat4 view = glm::lookAt( - glm::vec3(10.0f, 10.0f, 10.0f), // Camera positioned at an angle - glm::vec3(0.0f, 0.0f, 0.0f), // Looking at the origin - glm::vec3(0.0f, 1.0f, 0.0f) // Up vector pointing along the Y-axis - ); + glm::mat4 projection = camera_->GetProjectionMatrix(aspect_ratio); + glm::mat4 view = camera_->GetViewMatrix(); UpdateView(projection, view); diff --git a/src/app/panels/scene_panel.hpp b/src/app/panels/scene_panel.hpp index b0b5ed0..e6f8f9c 100644 --- a/src/app/panels/scene_panel.hpp +++ b/src/app/panels/scene_panel.hpp @@ -13,6 +13,7 @@ #include "imview/widget/gl_widget.hpp" #include "imview/component/opengl/grid.hpp" +#include "imview/component/opengl/camera.hpp" namespace quickviz { class ScenePanel : public GlWidget { @@ -20,6 +21,9 @@ class ScenePanel : public GlWidget { ScenePanel(const std::string& panel_name); void Draw() override; + + private: + std::unique_ptr camera_; }; } // namespace quickviz diff --git a/src/imview/include/imview/component/opengl/camera.hpp b/src/imview/include/imview/component/opengl/camera.hpp index acdacb0..48fd82d 100644 --- a/src/imview/include/imview/component/opengl/camera.hpp +++ b/src/imview/include/imview/component/opengl/camera.hpp @@ -40,26 +40,36 @@ class Camera { }; public: - enum class Movement { kForward, kBackward, kLeft, kRight }; + enum class Movement { kForward, kBackward, kLeft, kRight, kUp, kDown }; public: Camera(glm::vec3 position, float yaw, float pitch, float fov = default_fov); - void SetWorldUpVector(glm::vec3 up) { world_up_ = up; } - - void Reset(); + // public methods glm::mat4 GetViewMatrix() const; glm::mat4 GetProjectionMatrix(float aspect_ratio, float z_near = 0.1f, float z_far = 100.0f) const; + void Reset(); + void SetWorldUpVector(glm::vec3 up); + void LookAt(const glm::vec3& target); + glm::vec3 GetFront() const { return current_state_.front; } + + void SetPosition(const glm::vec3& position); + glm::vec3 GetPosition() const { return current_state_.position; } + void SetYaw(float yaw); + float GetYaw() const { return current_state_.yaw; } + void SetPitch(float pitch); + float GetPitch() const { return current_state_.pitch; } + void SetFOV(float fov) { fov_ = fov; } + float GetFOV() const { return fov_; } + float GetMovementSpeed() const { return movement_speed_; } + void ProcessKeyboard(Movement direction, float dt); void ProcessMouseMovement(float x_offset, float y_offset, bool constrain_pitch = true); void ProcessMouseScroll(float y_offset); - glm::vec3 GetPosition() const { return current_state_.position; } - glm::vec3 GetFront() const { return current_state_.front; } - private: void UpdateCameraVectors(); diff --git a/src/imview/include/imview/component/opengl/camera_controller.hpp b/src/imview/include/imview/component/opengl/camera_controller.hpp index 6cfeb93..d0573f8 100644 --- a/src/imview/include/imview/component/opengl/camera_controller.hpp +++ b/src/imview/include/imview/component/opengl/camera_controller.hpp @@ -12,7 +12,28 @@ #include "imview/component/opengl/camera.hpp" namespace quickviz { -class CameraController {}; +class CameraController { + public: + enum class Mode { kFirstPerson, kOrbit, kTopDown, kFreeLook }; + using CameraMovement = Camera::Movement; + + public: + CameraController(Camera& camera); + + void SetMode(Mode mode); + void ProcessKeyboard(CameraMovement direction, float delta_time); + void ProcessMouseMovement(float x_offset, float y_offset); + void ProcessMouseScroll(float y_offset); + + private: + void UpdateOrbitPosition(); + + Camera& camera_; + Mode mode_ = Mode::kOrbit; + glm::vec3 orbit_target_ = glm::vec3(0.0f, 0.0f, 0.0f); + float orbit_distance_ = 10.0f; + float top_down_height_ = 10.0f; +}; } // namespace quickviz #endif // QUICKVIZ_CAMERA_CONTROLLER_HPP \ No newline at end of file diff --git a/src/imview/include/imview/component/opengl/grid.hpp b/src/imview/include/imview/component/opengl/grid.hpp index f3a3940..181528b 100644 --- a/src/imview/include/imview/component/opengl/grid.hpp +++ b/src/imview/include/imview/component/opengl/grid.hpp @@ -25,6 +25,8 @@ class Grid : public OpenGLDrawable { void SetLineColor(const glm::vec3& color, float alpha = 0.5f); + void InitGraphicsResources() override; + void DeinitGraphicsResources() override; void OnDraw(const glm::mat4& projection, const glm::mat4& view) override; private: diff --git a/src/imview/include/imview/component/opengl/triangle.hpp b/src/imview/include/imview/component/opengl/triangle.hpp index 6464603..74dea8c 100644 --- a/src/imview/include/imview/component/opengl/triangle.hpp +++ b/src/imview/include/imview/component/opengl/triangle.hpp @@ -24,6 +24,8 @@ class Triangle : public OpenGLDrawable { void SetColor(const glm::vec3& color, float alpha = 0.5f); + void InitGraphicsResources() override; + void DeinitGraphicsResources() override; void OnDraw(const glm::mat4& projection, const glm::mat4& view) override; private: diff --git a/src/imview/include/imview/interface/opengl_drawable.hpp b/src/imview/include/imview/interface/opengl_drawable.hpp index af4c107..7eeec67 100644 --- a/src/imview/include/imview/interface/opengl_drawable.hpp +++ b/src/imview/include/imview/interface/opengl_drawable.hpp @@ -17,6 +17,8 @@ class OpenGLDrawable { virtual ~OpenGLDrawable() = default; /****** public methods ******/ + virtual void InitGraphicsResources() = 0; + virtual void DeinitGraphicsResources() = 0; virtual void OnDraw(const glm::mat4& projection, const glm::mat4& view) = 0; }; } // namespace quickviz diff --git a/src/imview/src/component/opengl/camera.cpp b/src/imview/src/component/opengl/camera.cpp index e71a255..7fc4243 100644 --- a/src/imview/src/component/opengl/camera.cpp +++ b/src/imview/src/component/opengl/camera.cpp @@ -33,6 +33,11 @@ void Camera::Reset() { UpdateCameraVectors(); } +void Camera::SetWorldUpVector(glm::vec3 up) { + world_up_ = up; + UpdateCameraVectors(); +} + glm::mat4 Camera::GetViewMatrix() const { return glm::lookAt(current_state_.position, current_state_.position + current_state_.front, @@ -44,6 +49,28 @@ glm::mat4 Camera::GetProjectionMatrix(float aspect_ratio, float z_near, return glm::perspective(glm::radians(fov_), aspect_ratio, z_near, z_far); } +void Camera::SetPosition(const glm::vec3& position) { + current_state_.position = position; +} + +void Camera::SetYaw(float yaw) { + current_state_.yaw = yaw; + UpdateCameraVectors(); +} + +void Camera::SetPitch(float pitch) { + current_state_.pitch = pitch; + UpdateCameraVectors(); +} + +void Camera::LookAt(const glm::vec3& target) { + current_state_.front = glm::normalize(target - current_state_.position); + current_state_.right = + glm::normalize(glm::cross(current_state_.front, world_up_)); + current_state_.up = + glm::normalize(glm::cross(current_state_.right, current_state_.front)); +} + void Camera::UpdateCameraVectors() { glm::vec3 front; front.x = std::cos(glm::radians(current_state_.yaw)) * diff --git a/src/imview/src/component/opengl/camera_controller.cpp b/src/imview/src/component/opengl/camera_controller.cpp index 1ca98bb..cddf8e2 100644 --- a/src/imview/src/component/opengl/camera_controller.cpp +++ b/src/imview/src/component/opengl/camera_controller.cpp @@ -8,4 +8,76 @@ #include "imview/component/opengl/camera_controller.hpp" -namespace quickviz {} // namespace quickviz \ No newline at end of file +namespace quickviz { +CameraController::CameraController(Camera& camera) : camera_(camera) {} + +void CameraController::SetMode(CameraController::Mode mode) { + mode_ = mode; + if (mode == Mode::kTopDown) { + camera_.SetPosition(glm::vec3(0.0f, top_down_height_, 0.0f)); + camera_.SetPitch(-90.0f); + camera_.SetYaw(0.0f); + } +} + +void CameraController::ProcessKeyboard( + CameraController::CameraMovement direction, float delta_time) { + if (mode_ == Mode::kOrbit) return; + if (mode_ == Mode::kTopDown) { + float velocity = camera_.GetMovementSpeed() * delta_time; + glm::vec3 position = camera_.GetPosition(); + // Move only along X and Z axes + if (direction == CameraMovement::kForward) position.z -= velocity; + if (direction == CameraMovement::kBackward) position.z += velocity; + if (direction == CameraMovement::kLeft) position.x -= velocity; + if (direction == CameraMovement::kRight) position.x += velocity; + + camera_.SetPosition(position); // Update position without changing height + } + camera_.ProcessKeyboard(direction, delta_time); +} + +void CameraController::ProcessMouseMovement(float x_offset, float y_offset) { + switch (mode_) { + case Mode::kFirstPerson: + case Mode::kFreeLook: + camera_.ProcessMouseMovement(x_offset, y_offset); + break; + case Mode::kOrbit: + camera_.ProcessMouseMovement(x_offset, y_offset); + UpdateOrbitPosition(); + break; + case Mode::kTopDown: + // Ignore mouse movement for top-down view + break; + } +} + +void CameraController::ProcessMouseScroll(float y_offset) { + if (mode_ == Mode::kOrbit) { + orbit_distance_ -= y_offset; + if (orbit_distance_ < 1.0f) orbit_distance_ = 1.0f; + UpdateOrbitPosition(); + } else if (mode_ == Mode::kTopDown) { + glm::vec3 position = camera_.GetPosition(); + position.y -= y_offset; // Adjust height (Y position) with scroll + if (position.y < 1.0f) position.y = 1.0f; // Set a minimum height + camera_.SetPosition(position); + } else { + camera_.ProcessMouseScroll(y_offset); + } +} + +void CameraController::UpdateOrbitPosition() { + float cam_x = orbit_target_.x + orbit_distance_ * + cos(glm::radians(camera_.GetYaw())) * + cos(glm::radians(camera_.GetPitch())); + float cam_y = + orbit_target_.y + orbit_distance_ * sin(glm::radians(camera_.GetPitch())); + float cam_z = orbit_target_.z + orbit_distance_ * + sin(glm::radians(camera_.GetYaw())) * + cos(glm::radians(camera_.GetPitch())); + camera_.SetPosition(glm::vec3(cam_x, cam_y, cam_z)); + camera_.LookAt(orbit_target_); +} +} // namespace quickviz \ No newline at end of file diff --git a/src/imview/src/component/opengl/frame_buffer.cpp b/src/imview/src/component/opengl/frame_buffer.cpp index aceae2c..ebd94e9 100644 --- a/src/imview/src/component/opengl/frame_buffer.cpp +++ b/src/imview/src/component/opengl/frame_buffer.cpp @@ -66,7 +66,6 @@ void FrameBuffer::DestroyBuffers() { void FrameBuffer::Bind(bool lock_aspect_ratio) const { glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer_); - if (lock_aspect_ratio) { float expected_width = height_ * aspect_ratio_; if (expected_width > width_) { diff --git a/src/imview/src/component/opengl/grid.cpp b/src/imview/src/component/opengl/grid.cpp index 7b4d952..59ae56f 100644 --- a/src/imview/src/component/opengl/grid.cpp +++ b/src/imview/src/component/opengl/grid.cpp @@ -64,6 +64,10 @@ void Grid::SetLineColor(const glm::vec3& color, float alpha) { alpha_ = alpha; } +void Grid::InitGraphicsResources() {} + +void Grid::DeinitGraphicsResources() {} + void Grid::OnDraw(const glm::mat4& projection, const glm::mat4& view) { shader_.Use(); shader_.SetUniform("projection", projection); diff --git a/src/imview/src/component/opengl/triangle.cpp b/src/imview/src/component/opengl/triangle.cpp index a23f1ab..2a8f817 100644 --- a/src/imview/src/component/opengl/triangle.cpp +++ b/src/imview/src/component/opengl/triangle.cpp @@ -65,6 +65,10 @@ void Triangle::SetColor(const glm::vec3& color, float alpha) { alpha_ = alpha; } +void Triangle::InitGraphicsResources() {} + +void Triangle::DeinitGraphicsResources() {} + void Triangle::OnDraw(const glm::mat4& projection, const glm::mat4& view) { shader_.Use(); shader_.SetUniform("projection", projection); diff --git a/src/imview/test/test_camera.cpp b/src/imview/test/test_camera.cpp index 82623eb..c815137 100644 --- a/src/imview/test/test_camera.cpp +++ b/src/imview/test/test_camera.cpp @@ -16,6 +16,7 @@ #include "imview/window.hpp" #include "imview/component/opengl/grid.hpp" #include "imview/component/opengl/camera.hpp" +#include "imview/component/opengl/camera_controller.hpp" using namespace quickviz; @@ -27,23 +28,48 @@ float lastX = 1920 / 2.0f; float lastY = 1080 / 2.0f; Camera camera(glm::vec3(0.0f, 3.0f, 8.0f), -90.0f, -25.0f); +CameraController camera_controller(camera); void ProcessInput(GLFWwindow* window) { + // if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { + // std::cout << "W key pressed" << std::endl; + // camera.ProcessKeyboard(Camera::Movement::kForward, deltaTime); + // } + // if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { + // std::cout << "S key pressed" << std::endl; + // camera.ProcessKeyboard(Camera::Movement::kBackward, deltaTime); + // } + // if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { + // std::cout << "A key pressed" << std::endl; + // camera.ProcessKeyboard(Camera::Movement::kLeft, deltaTime); + // } + // if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { + // std::cout << "D key pressed" << std::endl; + // camera.ProcessKeyboard(Camera::Movement::kRight, deltaTime); + // } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { - std::cout << "W key pressed" << std::endl; - camera.ProcessKeyboard(Camera::Movement::kForward, deltaTime); + camera_controller.ProcessKeyboard( + CameraController::CameraMovement::kForward, deltaTime); } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { - std::cout << "S key pressed" << std::endl; - camera.ProcessKeyboard(Camera::Movement::kBackward, deltaTime); + camera_controller.ProcessKeyboard( + CameraController::CameraMovement::kBackward, deltaTime); } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { - std::cout << "A key pressed" << std::endl; - camera.ProcessKeyboard(Camera::Movement::kLeft, deltaTime); + camera_controller.ProcessKeyboard(CameraController::CameraMovement::kLeft, + deltaTime); } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { - std::cout << "D key pressed" << std::endl; - camera.ProcessKeyboard(Camera::Movement::kRight, deltaTime); + camera_controller.ProcessKeyboard(CameraController::CameraMovement::kRight, + deltaTime); + } + if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) { + camera_controller.ProcessKeyboard(CameraController::CameraMovement::kUp, + deltaTime); + } + if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) { + camera_controller.ProcessKeyboard(CameraController::CameraMovement::kDown, + deltaTime); } if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) { camera.Reset(); @@ -63,11 +89,13 @@ void MouseCallback(GLFWwindow* window, double xpos, double ypos) { lastX = xpos; lastY = ypos; - camera.ProcessMouseMovement(xoffset, yoffset); + // camera.ProcessMouseMovement(xoffset, yoffset); + camera_controller.ProcessMouseMovement(xoffset, yoffset); } void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { - camera.ProcessMouseScroll(yoffset); + // camera.ProcessMouseScroll(yoffset); + camera_controller.ProcessMouseScroll(yoffset); } int main(int argc, char* argv[]) { @@ -75,6 +103,7 @@ int main(int argc, char* argv[]) { int height = 1080; Window win("Test Window", width, height); + camera_controller.SetMode(CameraController::Mode::kTopDown); glfwSetCursorPosCallback(win.GetWindowObject(), MouseCallback); glfwSetScrollCallback(win.GetWindowObject(), ScrollCallback); glfwSetInputMode(win.GetWindowObject(), GLFW_CURSOR, GLFW_CURSOR_DISABLED);