window management in its own files

This commit is contained in:
Mars 2024-11-18 13:56:19 -05:00
parent a6fa5b0570
commit 0f4d0d72d9
4 changed files with 234 additions and 98 deletions

View file

@ -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,

View file

@ -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 <imgui.h>
@ -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::InputMode::eCursor>(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::InputMode::eCursor>(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::InputMode::eCursor>(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::InputMode::eCursor>(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::InputMode::eCursor>(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<f32>(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 {

View file

@ -0,0 +1,78 @@
#include <vkfw.hpp>
#include "window_manager.hpp"
#include "../util/constants.hpp"
using namespace constants;
fn WindowManager::create(const char* title, const WindowCallbacks& callbacks)
-> std::tuple<vkfw::UniqueInstance, vkfw::UniqueWindow> {
// 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::InputMode::eCursor>(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<const char*> {
// Get the required extensions from GLFW
std::span<const char*> extensionsSpan = vkfw::getRequiredInstanceExtensions();
return { extensionsSpan.begin(), extensionsSpan.end() };
}
fn WindowManager::getFramebufferSize(const vkfw::Window& window) -> std::pair<i32, i32> {
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<vkfw::InputMode::eCursor>(mode);
}
fn WindowManager::setPosition(const vkfw::Window& window, i32 xpos, i32 ypos) -> void {
window.setPos(xpos, ypos);
}

View file

@ -0,0 +1,81 @@
#pragma once
#define VKFW_NO_STRUCT_CONSTRUCTORS // Use aggregate initialization for GLFW structs
#include <vkfw.hpp>
#include "../util/types.hpp"
struct WindowCallbacks {
std::function<void(const vkfw::Window&, f64, f64)> 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<void(const vkfw::Window&, usize, usize)> 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<vkfw::UniqueInstance, vkfw::UniqueWindow>;
/**
* @brief Gets the required instance extensions for window surface creation.
*
* @return std::vector<const char*> List of required extensions
*/
static fn getRequiredExtensions() -> std::vector<const char*>;
/**
* @brief Gets the framebuffer size of the window.
*
* @param window GLFW window
* @return std::pair<i32, i32> Width and height of the framebuffer
*/
static fn getFramebufferSize(const vkfw::Window& window) -> std::pair<i32, i32>;
/**
* @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;
};