From 0f4d0d72d9ac717a66fe0952b812fea5a19b531c Mon Sep 17 00:00:00 2001 From: Mars Date: Mon, 18 Nov 2024 13:56:19 -0500 Subject: [PATCH] window management in its own files --- meson.build | 1 + src/main.cpp | 172 +++++++++++++++------------------- src/window/window_manager.cpp | 78 +++++++++++++++ src/window/window_manager.hpp | 81 ++++++++++++++++ 4 files changed, 234 insertions(+), 98 deletions(-) create mode 100644 src/window/window_manager.cpp create mode 100644 src/window/window_manager.hpp diff --git a/meson.build b/meson.build index 41d41fd..0dfd5b0 100644 --- a/meson.build +++ b/meson.build @@ -42,6 +42,7 @@ executable( 'src/camera/camera.cpp', 'src/init/vulkan_instance.cpp', 'src/init/debug_messenger.cpp', + 'src/window/window_manager.cpp', ], include_directories: include_directories('include', is_system: true), dependencies: deps, diff --git a/src/main.cpp b/src/main.cpp index 0e92c5f..f02a43f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,12 +28,13 @@ #include "structs/queue_family_indices.hpp" #include "structs/swap_chain_support_details.hpp" #include "structs/uniform_buffer_object.hpp" -#include "util/constants.hpp" // Constants definitions -#include "util/crosshair.hpp" // Crosshair definitions -#include "util/shaders.hpp" // Compiled shader code -#include "util/types.hpp" // Custom type definitions -#include "util/unique_image.hpp" // Custom image handling utilities -#include "util/vertex.hpp" // Custom vertex structure definition +#include "util/constants.hpp" // Constants definitions +#include "util/crosshair.hpp" // Crosshair definitions +#include "util/shaders.hpp" // Compiled shader code +#include "util/types.hpp" // Custom type definitions +#include "util/unique_image.hpp" // Custom image handling utilities +#include "util/vertex.hpp" // Custom vertex structure definition +#include "window/window_manager.hpp" // Window manager // ImGui headers for GUI #include @@ -213,100 +214,74 @@ class VulkanApp { * The window is created without a default OpenGL context, as we'll be using Vulkan. */ fn initWindow() -> void { - // Initialize GLFW - mVKFWInstance = vkfw::initUnique(); + // Initialize GLFW and create window + std::tie(mVKFWInstance, mWindow) = WindowManager::create( + "Vulkan", + { + .on_cursor_move = [this](const vkfw::Window& /*window*/, f64 mouseX, f64 mouseY) -> void { + if (!mCursorCaptured) + return; // Skip camera movement when cursor is not captured - // Set window creation hints - vkfw::WindowHints hints { .clientAPI = vkfw::ClientAPI::eNone }; // No OpenGL context + if (mFirstMouse) { + mLastX = mouseX; + mLastY = mouseY; + mFirstMouse = false; + return; + } - // Get the primary monitor and its resolution - vkfw::Monitor primaryMonitor = vkfw::getPrimaryMonitor(); - const GLFWvidmode* videoMode = primaryMonitor.getVideoMode(); + f64 xoffset = mouseX - mLastX; + f64 yoffset = mLastY - mouseY; // Reversed since y-coordinates range from bottom to top - // Calculate window position to center it - i32 xpos = (videoMode->width - WIDTH) / 2; - i32 ypos = (videoMode->height - HEIGHT) / 2; + mLastX = mouseX; + mLastY = mouseY; - // Create the window - mWindow = vkfw::createWindowUnique(WIDTH, HEIGHT, "Vulkan", hints); + mCamera.rotate(-xoffset, yoffset); // Invert xoffset for correct horizontal movement + }, + .on_key = [this]( + const vkfw::Window& window, + const vkfw::Key& key, + const i32& /*scancode*/, + const vkfw::KeyAction& action, + const vkfw::ModifierKeyFlags& /*mods*/ + ) -> void { + if (key == vkfw::Key::eEscape && action == vkfw::KeyAction::ePress) { + mCursorCaptured = false; + window.set(vkfw::CursorMode::eNormal); + } - // Set window position - mWindow->setPos(xpos, ypos); - - // Set the user pointer to this instance, allowing us to access it in callbacks - mWindow->setUserPointer(this); - - // Configure cursor for FPS-style camera control - mWindow->set(vkfw::CursorMode::eDisabled); - - // Set up mouse callback - mWindow->callbacks()->on_cursor_move = - [this](const vkfw::Window& /*window*/, f64 mouseX, f64 mouseY) -> void { - if (!mCursorCaptured) - return; // Skip camera movement when cursor is not captured - - if (mFirstMouse) { - mLastX = mouseX; - mLastY = mouseY; - mFirstMouse = false; - return; + if (key == vkfw::Key::eR && action == vkfw::KeyAction::ePress) { + try { + mDevice->waitIdle(); + createGraphicsPipeline(); + fmt::println("Shaders reloaded successfully!"); + } catch (const std::exception& e) { + fmt::println(stderr, "Failed to reload shaders: {}", e.what()); + } + } + }, + .on_mouse_button = [this]( + const vkfw::Window& window, + const vkfw::MouseButton& button, + const vkfw::MouseButtonAction& action, + const vkfw::ModifierKeyFlags& /*mods*/ + ) -> void { + if (button == vkfw::MouseButton::eLeft && action == vkfw::MouseButtonAction::ePress && + !mCursorCaptured) { + // Only capture cursor if click is not on ImGui window + if (!ImGui::GetIO().WantCaptureMouse) { + mCursorCaptured = true; + mFirstMouse = true; // Reset first mouse flag to avoid jumps + window.set(vkfw::CursorMode::eDisabled); + } + } + }, + .on_window_resize = + [this](const vkfw::Window& /*window*/, usize /*width*/, usize /*height*/) -> void { + // Set the framebuffer resized flag when the window is resized + mFramebufferResized = true; + }, } - - f64 xoffset = mouseX - mLastX; - f64 yoffset = mLastY - mouseY; // Reversed since y-coordinates range from bottom to top - - mLastX = mouseX; - mLastY = mouseY; - - mCamera.rotate(-xoffset, yoffset); // Invert xoffset for correct horizontal movement - }; - - // Set up key callback for escape - mWindow->callbacks()->on_key = [this]( - const vkfw::Window& window, - const vkfw::Key& key, - const i32& /*scancode*/, - const vkfw::KeyAction& action, - const vkfw::ModifierKeyFlags& /*mods*/ - ) -> void { - if (key == vkfw::Key::eEscape && action == vkfw::KeyAction::ePress) { - mCursorCaptured = false; - window.set(vkfw::CursorMode::eNormal); - } - - if (key == vkfw::Key::eR && action == vkfw::KeyAction::ePress) { - try { - mDevice->waitIdle(); - createGraphicsPipeline(); - fmt::println("Shaders reloaded successfully!"); - } catch (const std::exception& e) { fmt::println(stderr, "Failed to reload shaders: {}", e.what()); } - } - }; - - // Set up mouse button callback for re-capture - mWindow->callbacks()->on_mouse_button = [this]( - const vkfw::Window& window, - const vkfw::MouseButton& button, - const vkfw::MouseButtonAction& action, - const vkfw::ModifierKeyFlags& /*mods*/ - ) -> void { - if (button == vkfw::MouseButton::eLeft && action == vkfw::MouseButtonAction::ePress && - !mCursorCaptured) { - // Only capture cursor if click is not on ImGui window - if (!ImGui::GetIO().WantCaptureMouse) { - mCursorCaptured = true; - mFirstMouse = true; // Reset first mouse flag to avoid jumps - window.set(vkfw::CursorMode::eDisabled); - } - } - }; - - // Set up the window resize callback - mWindow->callbacks()->on_window_resize = - [this](const vkfw::Window& /*window*/, usize /*width*/, usize /*height*/) -> void { - // Set the framebuffer resized flag when the window is resized - mFramebufferResized = true; - }; + ); } /** @@ -437,7 +412,7 @@ class VulkanApp { f64 lastFpsUpdate = 0.0; i32 frameCounter = 0; - while (!mWindow->shouldClose()) { + while (!WindowManager::shouldClose(mWindow.get())) { f64 currentFrame = vkfw::getTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; @@ -561,7 +536,8 @@ class VulkanApp { fn updateFrameStats(f64& lastFpsUpdate, i32& frameCounter) -> void { f64 currentFrame = vkfw::getTime(); if (currentFrame - lastFpsUpdate > 1.0) { - mWindow->setTitle( + WindowManager::setTitle( + mWindow.get(), fmt::format("Vulkan - {:.0f}FPS", static_cast(frameCounter / (currentFrame - lastFpsUpdate))) ); lastFpsUpdate = currentFrame; @@ -608,7 +584,7 @@ class VulkanApp { fn recreateSwapChain() -> void { i32 width = 0, height = 0; while (width == 0 || height == 0) { - std::tie(width, height) = mWindow->getFramebufferSize(); + std::tie(width, height) = WindowManager::getFramebufferSize(mWindow.get()); vkfw::waitEvents(); } @@ -2688,7 +2664,7 @@ class VulkanApp { // Get the window's resolution u32 width = 0, height = 0; - std::tie(width, height) = mWindow->getFramebufferSize(); + std::tie(width, height) = WindowManager::getFramebufferSize(mWindow.get()); // Return the resolution clamped to the supported range return { diff --git a/src/window/window_manager.cpp b/src/window/window_manager.cpp new file mode 100644 index 0000000..29e2b7e --- /dev/null +++ b/src/window/window_manager.cpp @@ -0,0 +1,78 @@ +#include + +#include "window_manager.hpp" + +#include "../util/constants.hpp" + +using namespace constants; + +fn WindowManager::create(const char* title, const WindowCallbacks& callbacks) + -> std::tuple { + // Initialize GLFW + vkfw::UniqueInstance instance = vkfw::initUnique(); + + // Don't create an OpenGL context + vkfw::WindowHints hints; + hints.clientAPI = vkfw::ClientAPI::eNone; + +#ifdef __APPLE__ + // Required for macOS + hints.cocoaRetinaFramebuffer = true; +#endif + + // Create window + vkfw::UniqueWindow window = vkfw::createWindowUnique(WIDTH, HEIGHT, title, hints); + + // Get the primary monitor and its resolution + vkfw::Monitor primaryMonitor = vkfw::getPrimaryMonitor(); + const GLFWvidmode* videoMode = primaryMonitor.getVideoMode(); + + // Calculate window position to center it + i32 xpos = (videoMode->width - WIDTH) / 2; + i32 ypos = (videoMode->height - HEIGHT) / 2; + + // Set window position + window->setPos(xpos, ypos); + + // Configure cursor for FPS-style camera control + window->set(vkfw::CursorMode::eDisabled); + + // Set up callbacks + if (callbacks.on_cursor_move) + window->callbacks()->on_cursor_move = callbacks.on_cursor_move; + + if (callbacks.on_key) + window->callbacks()->on_key = callbacks.on_key; + + if (callbacks.on_mouse_button) + window->callbacks()->on_mouse_button = callbacks.on_mouse_button; + + if (callbacks.on_window_resize) + window->callbacks()->on_window_resize = callbacks.on_window_resize; + + return std::make_tuple(std::move(instance), std::move(window)); +} + +fn WindowManager::getRequiredExtensions() -> std::vector { + // Get the required extensions from GLFW + std::span extensionsSpan = vkfw::getRequiredInstanceExtensions(); + return { extensionsSpan.begin(), extensionsSpan.end() }; +} + +fn WindowManager::getFramebufferSize(const vkfw::Window& window) -> std::pair { + return window.getFramebufferSize(); +} + +fn WindowManager::shouldClose(const vkfw::Window& window) -> bool { return window.shouldClose(); } + +fn WindowManager::setTitle(const vkfw::Window& window, const std::string& title) -> void { + window.setTitle(title); +} + +fn WindowManager::setCursorMode(const vkfw::Window& window, vkfw::CursorMode mode) -> void { + window.set(mode); +} + +fn WindowManager::setPosition(const vkfw::Window& window, i32 xpos, i32 ypos) -> void { + window.setPos(xpos, ypos); +} diff --git a/src/window/window_manager.hpp b/src/window/window_manager.hpp new file mode 100644 index 0000000..9b1fcb7 --- /dev/null +++ b/src/window/window_manager.hpp @@ -0,0 +1,81 @@ +#pragma once + +#define VKFW_NO_STRUCT_CONSTRUCTORS // Use aggregate initialization for GLFW structs +#include + +#include "../util/types.hpp" + +struct WindowCallbacks { + std::function on_cursor_move; + std::function< + void(const vkfw::Window&, const vkfw::Key&, i32, const vkfw::KeyAction&, const vkfw::ModifierKeyFlags&)> + on_key; + std::function< + void(const vkfw::Window&, const vkfw::MouseButton&, const vkfw::MouseButtonAction&, const vkfw::ModifierKeyFlags&)> + on_mouse_button; + std::function on_window_resize; +}; + +class WindowManager { + public: + WindowManager() = default; + + /** + * @brief Creates a window with the specified dimensions and title. + * + * @param title Window title + * @param callbacks Window callbacks + * @return vkfw::UniqueWindow Unique pointer to the created window + */ + static fn create(const char* title, const WindowCallbacks& callbacks) + -> std::tuple; + + /** + * @brief Gets the required instance extensions for window surface creation. + * + * @return std::vector List of required extensions + */ + static fn getRequiredExtensions() -> std::vector; + + /** + * @brief Gets the framebuffer size of the window. + * + * @param window GLFW window + * @return std::pair Width and height of the framebuffer + */ + static fn getFramebufferSize(const vkfw::Window& window) -> std::pair; + + /** + * @brief Checks if the window should close. + * + * @param window GLFW window + * @return true Window should close + * @return false Window should stay open + */ + static fn shouldClose(const vkfw::Window& window) -> bool; + + /** + * @brief Sets the window title. + * + * @param window GLFW window + * @param title New window title + */ + static fn setTitle(const vkfw::Window& window, const std::string& title) -> void; + + /** + * @brief Sets the cursor mode. + * + * @param window GLFW window + * @param mode Cursor mode (Normal or Disabled) + */ + static fn setCursorMode(const vkfw::Window& window, vkfw::CursorMode mode) -> void; + + /** + * @brief Sets the window position. + * + * @param window GLFW window + * @param xpos X position + * @param ypos Y position + */ + static fn setPosition(const vkfw::Window& window, i32 xpos, i32 ypos) -> void; +};