diff --git a/FinalProject/camera.cpp b/FinalProject/camera.cpp index 9c0e83f..0951b2a 100644 --- a/FinalProject/camera.cpp +++ b/FinalProject/camera.cpp @@ -4,7 +4,7 @@ #include "camera.h" -// If no default input, set camera to (1, 1, 1) looking at (0, 0, 0) +// If no default input, set camera to (0, 0, 1) looking at (0, 0, 0) Camera::Camera() : Camera(defaultOrigin(), defaultTarget()) {} Camera::Camera(glm::vec3 position, glm::vec3 target) : _position(position) { @@ -46,4 +46,49 @@ void Camera::updateCameraState() { // Update up vector _up = glm::normalize(glm::cross(_right, _front)); -} \ No newline at end of file +} + +void Camera::rotate(glm::vec3 center, float deltaPitchAngle, float deltaYawAngle) { + if (deltaPitchAngle + _pitch > 89.0f) { + deltaPitchAngle = 89.0f - _pitch; + } + if (deltaPitchAngle + _pitch < -89.0f) { + deltaPitchAngle = -89.0f - _pitch; + } + // Get the normalized direction vector + glm::vec3 direction = glm::normalize(_position - center); + // Get the original distance from the center + float distance = glm::length(_position - center); + // Rotate the direction vector + glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), glm::radians(deltaPitchAngle), _right); + rotation = glm::rotate(rotation, glm::radians(deltaYawAngle), _worldUp); + direction = glm::normalize(glm::vec3(rotation * glm::vec4(direction, 1.0f))); + // Get new position + _position = center + direction * distance; + // Update the position + _pitch += deltaPitchAngle; + _yaw -= deltaYawAngle; + + updateCameraState(); +} + +void Camera::setRotation(glm::vec3 center, float pitchAngle, float yawAngle) { + if (pitchAngle > 89.0f) { + pitchAngle = 89.0f; + } + if (pitchAngle < -89.0f) { + pitchAngle = -89.0f; + } + // Get the direction vector + glm::vec3 direction = _position - center; + // Rotate the direction vector + glm::mat4 rotationMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(pitchAngle), _right); + rotationMatrix = glm::rotate(rotationMatrix, glm::radians(yawAngle), _worldUp); + direction = glm::vec3(rotationMatrix * glm::vec4(direction, 1.0f)); + // Update the position + _position = center + direction; + _pitch = pitchAngle; + _yaw = yawAngle; + + updateCameraState(); +} diff --git a/FinalProject/camera.h b/FinalProject/camera.h index 9ee0f87..e1b0ee5 100644 --- a/FinalProject/camera.h +++ b/FinalProject/camera.h @@ -3,9 +3,12 @@ #include #include +#define CAMERA_MAX_ZOOM 90.0f +#define CAMERA_MIN_ZOOM 1.0f + class Camera { public: - inline glm::vec3 defaultOrigin() { return glm::vec3(1.0f, 1.0f, 1.0f); } + inline glm::vec3 defaultOrigin() { return glm::vec3(0.0f, 0.0f, 1.0f); } inline glm::vec3 defaultTarget() { return glm::vec3(0.0f, 0.0f, 0.0f); } private: @@ -16,11 +19,7 @@ private: glm::vec3 _up; float _yaw = 0.0f; float _pitch = 0.0f; - float _zoom = 90.0f; - -private: - // Camera settings - float _moveStep = 1.0f; + float _zoom = 45.0f; private: // World settings @@ -33,27 +32,36 @@ public: Camera(glm::vec3 position, float yaw, float pitch); public: - inline float zoom() const { return _zoom; } + inline glm::vec3 front() const { return _front; } + inline float zoomVal() const { return _zoom; } inline glm::mat4 viewMatrix(); + + inline glm::vec3 position() const { return _position; } public: - inline void move(glm::vec3 direction, float step); + inline void move(glm::vec2 offset); inline void setPosition(glm::vec3 position); inline void pitch(float deltaAngle); inline void setPitch(float angle); inline void yaw(float deltaAngle); inline void setYaw(float angle); + inline void rotate(float deltaPitchAngle, float deltaYawAngle); + inline void setRotation(float pitchAngle, float yawAngle); + void rotate(glm::vec3 center, float deltaPitchAngle, float deltaYawAngle); + void setRotation(glm::vec3 center, float pitchAngle, float yawAngle); + inline void zoom(float deltaZoom); + inline void setZoom(float zoom); + inline void push(float distance); private: void updateCameraState(); }; -inline glm::mat4 Camera::viewMatrix() { - return glm::lookAt(_position, _position + _front, _up); -} - -inline void Camera::move(glm::vec3 direction, float step) { - _position += direction * step; +inline void Camera::move(glm::vec2 offset) { + // Offset describe the movement on the xy plane in the camera's coordinate system + // Should convert to the movement of position vector in world coordinate system + glm::vec3 worldSpaceOffset = offset.x * _right + offset.y * _up; + _position += worldSpaceOffset; updateCameraState(); } @@ -81,3 +89,58 @@ inline void Camera::setYaw(float angle) { _yaw = angle; updateCameraState(); } + +inline void Camera::rotate(float deltaPitchAngle, float deltaYawAngle) { + _pitch += deltaPitchAngle; + if (_pitch > 89.0f) { + _pitch = 89.0f; + } + if (_pitch < -89.0f) { + _pitch = -89.0f; + } + _yaw += deltaYawAngle; + if (_yaw > 360.0f) { + _yaw -= 360.0f; + } + if (_yaw < 0.0f) { + _yaw += 360.0f; + } + updateCameraState(); +} + +inline void Camera::setRotation(float pitchAngle, float yawAngle) { + _pitch = pitchAngle; + _yaw = yawAngle; + updateCameraState(); +} + +inline void Camera::zoom(float deltaZoom) { + _zoom += deltaZoom; + if (_zoom > CAMERA_MAX_ZOOM) { + _zoom = CAMERA_MAX_ZOOM; + } + else if (_zoom < CAMERA_MIN_ZOOM) { + _zoom = CAMERA_MIN_ZOOM; + } + updateCameraState(); +} + +inline void Camera::setZoom(float zoom) { + _zoom = zoom; + if (_zoom > CAMERA_MAX_ZOOM) { + _zoom = CAMERA_MAX_ZOOM; + } + else if (_zoom < CAMERA_MIN_ZOOM) { + _zoom = CAMERA_MIN_ZOOM; + } + updateCameraState(); +} + +inline void Camera::push(float distance) { + _position += distance * _front; + updateCameraState(); +} + +inline glm::mat4 Camera::viewMatrix() { + return glm::lookAt(_position, _position + _front, _up); +} diff --git a/FinalProject/sceneviewer.cpp b/FinalProject/sceneviewer.cpp index 772beae..6f60c35 100644 --- a/FinalProject/sceneviewer.cpp +++ b/FinalProject/sceneviewer.cpp @@ -55,22 +55,6 @@ void SceneViewer::initializeGL() { Logger::info("Currently running on OpenGL version: " + std::string((const char*)glGetString(GL_VERSION))); - _vao.ensureInitialized(); - Logger::info("Vertex Array Object initialized"); - - vector vertices = { - { { -0.5f, -0.5f, 0.0f } }, - { { 0.5f, -0.5f, 0.0f } }, - { { 0.0f, 0.5f, 0.0f } } - }; - VertexBufferObject vbo(vertices); - Logger::info("Vertex Buffer Object initialized"); - - _vao.bindVertexBufferObject(vbo); - _vao.setVertexAttributePointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0); - _vao.enableVertexAttribute(0); - Logger::info("Vertex Buffer Object bound to Vertex Array Object"); - _shaderProgram.ensureInitialized(); Logger::info("Shader Program initialized"); @@ -83,20 +67,11 @@ void SceneViewer::initializeGL() { Model* backpackModel = new Model("D:/code/ComputerGraphic/SceneEditor/obj/nanosuit/nanosuit.obj"); Logger::info("Model loaded"); - Renderable renderable(backpackModel); - _objects.push_back(backpackModel); + Renderable backpack(backpackModel); + _objects.push_back(backpack); - renderable.move(glm::vec3(100.0f, 0.0f, 0.0f)); - renderable.rotate(glm::vec3(0, 1, 0), 3.1415926f/2); - renderable.check_boundary(); - auto t1 = renderable.get_lower_boundary(); - auto t2 = renderable.get_upper_boundary(); - - _camera.setPosition(glm::vec3(0.0f, 15.0f, 8.0f)); - _camera.setYaw(-90.0f); - _camera.setPitch(-30.0f); - + _camera.setPosition(glm::vec3(0.0f, 0.0f, 5.0f)); } @@ -111,11 +86,86 @@ void SceneViewer::paintGL() { // Set view and projection matrices glm::mat4 view = _camera.viewMatrix(); - glm::mat4 projection = glm::perspective(glm::radians(_camera.zoom()), (float)width() / (float)height(), 0.1f, 100.0f); + glm::mat4 projection = glm::perspective(glm::radians(_camera.zoomVal()), (float)width() / (float)height(), 0.1f, 100.0f); _shaderProgram.setUniform("view", view); _shaderProgram.setUniform("projection", projection); for (auto object : _objects) { object.render(_shaderProgram); } -} \ No newline at end of file + + _shaderProgram.unbind(); +} + +void SceneViewer::mousePressEvent(QMouseEvent* event) { + Logger::debug("Mouse pressed at: " + std::to_string(event->x()) + ", " + std::to_string(event->y())); + if (event->button() == Qt::LeftButton) { + // TODO: Hit test on objects + } + else { + _lastMousePosition = event->pos(); + } +} + +void SceneViewer::mouseMoveEvent(QMouseEvent* event) { + Logger::debug("Mouse moved with offset: " + std::to_string(event->x() - _lastMousePosition.x()) + ", " + std::to_string(event->y() - _lastMousePosition.y())); + // Check the type of button pressed + switch (event->buttons()) { + case Qt::LeftButton: { + // Move the selected object + if (_selectedObject != nullptr) { + // TODO: move the selected object + } + break; + } + case Qt::RightButton: { + // Move the camera + float xoffset = event->x() - _lastMousePosition.x(); + float yoffset = _lastMousePosition.y() - event->y(); // reversed since y-coordinates go from bottom to top + float xmovement = xoffset * _cameraMovementSpeed; + float ymovement = yoffset * _cameraMovementSpeed; + glm::vec3 cameraPrevPos = _camera.position(); + _camera.move({ -xmovement, -ymovement }); + glm::vec3 cameraNewPos = _camera.position(); + _rotateCenter += cameraNewPos - cameraPrevPos; + Logger::debug("Camera moved to: " + std::to_string(_camera.position().x) + ", " + std::to_string(_camera.position().y) + ", " + std::to_string(_camera.position().z)); + Logger::debug("New center: " + std::to_string(_rotateCenter.x) + ", " + std::to_string(_rotateCenter.y) + ", " + std::to_string(_rotateCenter.z)); + break; + } + case Qt::MiddleButton: { + // Rotate the camera + float xoffset = event->x() - _lastMousePosition.x(); + float yoffset = _lastMousePosition.y() - event->y(); // reversed since y-coordinates go from bottom to top + // Calculate pitch angle + float pitch = yoffset * _cameraRotationSpeed; + // Calculate yaw angle + float yaw = xoffset * _cameraRotationSpeed; + _camera.rotate(_rotateCenter, pitch, -yaw); + Logger::debug("Camera rotated to: " + std::to_string(_camera.position().x) + ", " + std::to_string(_camera.position().y) + ", " + std::to_string(_camera.position().z)); + Logger::debug("Center at: " + std::to_string(_rotateCenter.x) + ", " + std::to_string(_rotateCenter.y) + ", " + std::to_string(_rotateCenter.z)); + break; + } + default: { + Logger::warning("Unknown mouse button input"); + Logger::warning("Mouse button: " + std::to_string(event->buttons())); + break; + } + } + // Update the last mouse position + _lastMousePosition = event->pos(); + // Update the view + update(); +} + +void SceneViewer::wheelEvent(QWheelEvent* event) { + // Zoom in or out + float wheelOffset = event->angleDelta().y(); + Logger::debug("Wheel offset: " + std::to_string(wheelOffset)); + _camera.push(wheelOffset * _cameraPushSpeed); + glm::vec3 cameraFront = _camera.front(); + _rotateCenter += wheelOffset * _cameraPushSpeed * cameraFront; + Logger::debug("New camera position: " + std::to_string(_camera.position().x) + ", " + std::to_string(_camera.position().y) + ", " + std::to_string(_camera.position().z)); + Logger::debug("New center position: " + std::to_string(_rotateCenter.x) + ", " + std::to_string(_rotateCenter.y) + ", " + std::to_string(_rotateCenter.z)); + // Update the view + update(); +} diff --git a/FinalProject/sceneviewer.h b/FinalProject/sceneviewer.h index 62b50bc..a854b32 100644 --- a/FinalProject/sceneviewer.h +++ b/FinalProject/sceneviewer.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -17,17 +18,36 @@ class SceneViewer : public QOpenGLWidget, protected QOpenGLFunctions Q_OBJECT private: + // OpenGL section------------------------------------- + // List of objects currently in the scene std::vector _objects; + // Shader program for objects ShaderProgram _shaderProgram = ShaderProgram::empty(); - VertexArrayObject _vao = VertexArrayObject::empty(); + // Main camera Camera _camera; + float _cameraMovementSpeed = 0.02f; + float _cameraRotationSpeed = 0.3f; + float _cameraPushSpeed = 0.02f; + // Rotate center + glm::vec3 _rotateCenter = glm::vec3(0.0f, 0.0f, 0.0f); + + // User Interaction flags section--------------------- + QPoint _lastMousePosition; + Renderable* _selectedObject = nullptr; public: SceneViewer(QWidget* parent = 0); ~SceneViewer(); protected: + // OpenGL functions virtual void initializeGL() override; virtual void paintGL() override; virtual void resizeGL(int w, int h) override; + + // Mouse events + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void wheelEvent(QWheelEvent* event) override; + }; \ No newline at end of file diff --git a/FinalProject/texture.cpp b/FinalProject/texture.cpp index 8bb6fc6..7fbc98e 100644 --- a/FinalProject/texture.cpp +++ b/FinalProject/texture.cpp @@ -25,6 +25,9 @@ Texture::Texture(TextureType type, std::string path) { OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // Set stbi to reverse the y-axis + stbi_set_flip_vertically_on_load(true); + // Load image, create texture and generate mipmaps int width, height, nrChannels; unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0); diff --git a/Models/backpack/specular.jpg b/Models/backpack/specular.jpg index 1fd675a..e80035a 100644 Binary files a/Models/backpack/specular.jpg and b/Models/backpack/specular.jpg differ