diff --git a/.gitignore b/.gitignore index 9785597..6fa9c1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build .cache +run diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 370c986..f6c5d5b 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -8,12 +8,12 @@ FetchContent_Declare( GIT_TAG 06fac4b ) -#set(GLFW_BUILD_WAYLAND OFF CACHE BOOL "WAYLAND GLFW" FORCE) -#FetchContent_Declare( -# glfw_fetch -# GIT_REPOSITORY https://github.com/glfw/glfw.git -# GIT_TAG 3.4 -#) +set(GLFW_BUILD_WAYLAND OFF CACHE BOOL "WAYLAND GLFW" FORCE) +FetchContent_Declare( + glfw_fetch + GIT_REPOSITORY https://github.com/glfw/glfw.git + GIT_TAG 3.4 +) FetchContent_Declare( glm_fetch @@ -63,4 +63,4 @@ FetchContent_Declare( GIT_TAG 35368f913b79acc112c05d67291681e7badea68b ) -FetchContent_MakeAvailable(vuk_fetch glm_fetch entt_fetch slang_fetch fastgltf_fetch) +FetchContent_MakeAvailable(vuk_fetch glfw_fetch glm_fetch entt_fetch slang_fetch fastgltf_fetch) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 520c432..f4fd334 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,6 +1,10 @@ add_library(rogue-lib STATIC cli/args.cpp cli/args.hpp + ui/display.cpp + ui/display.hpp + ui/user_input.cpp + ui/user_input.hpp ) target_include_directories(rogue-lib PUBLIC source .) target_compile_definitions(rogue-lib PUBLIC -DGLFW_INCLUDE_VULKAN -DGLM_ENABLE_EXPERIMENTAL) @@ -10,6 +14,7 @@ target_link_libraries(rogue-lib PUBLIC vk-bootstrap slang fastgltf + glfw ) add_executable(rogue diff --git a/source/main.cxx b/source/main.cxx index 1581625..920a39d 100644 --- a/source/main.cxx +++ b/source/main.cxx @@ -1,5 +1,6 @@ #include #include +#include #include auto main(int argc, char **argv) -> int { @@ -23,6 +24,15 @@ auto main(int argc, char **argv) -> int { std::println("{} does not exist", args.scene_path.generic_string()); return 2; } + + auto user_input = rog::ui::user_input(); + glfwInit(); + auto display = rog::ui::display(&user_input, {1920, 1080}, "Rogue PT"); + + while (!display.should_close()) { + display.poll_events(); + } + glfwTerminate(); } catch (const std::exception &ex) { log.error("{}", ex.what()); } diff --git a/source/ui/display.cpp b/source/ui/display.cpp new file mode 100644 index 0000000..959b7d4 --- /dev/null +++ b/source/ui/display.cpp @@ -0,0 +1,134 @@ +#include "display.hpp" + +#include +#include + +namespace rog::ui { + +GLFWwindow *display::create_window(glm::uvec2 extent, std::string_view title, + void *user_data) { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + const auto window = + glfwCreateWindow(extent.x, extent.y, title.data(), nullptr, nullptr); + if (window == nullptr) { + auto description = static_cast(nullptr); + glfwGetError(&description); + throw std::runtime_error(description); + } + glfwSetWindowUserPointer(window, user_data); + return window; +} + +display::display(user_input *input, glm::uvec2 extent, std::string_view title) + : handle(create_window(extent, title, this)), title(title), input(input) { + glfwSetWindowSizeCallback( + handle, [](GLFWwindow *window, int width, int height) { + auto self = static_cast(glfwGetWindowUserPointer(window)); + for (const auto &cb : self->resize_callbacks) { + cb(glm::uvec2(width, height)); + } + }); + glfwSetKeyCallback(handle, [](GLFWwindow *window, int key, + [[maybe_unused]] int scancode, int action, + [[maybe_unused]] int mods) { + auto self = static_cast(glfwGetWindowUserPointer(window)); + if (action == GLFW_RELEASE) { + self->input->set_key_state(static_cast(key), + key_state::released); + } else if (action == GLFW_REPEAT) { + self->input->set_key_state(static_cast(key), key_state::repeat); + } else if (action == GLFW_PRESS) { + self->input->set_key_state(static_cast(key), + key_state::pressed); + } + }); + glfwSetCursorPosCallback(handle, [](GLFWwindow *window, double x, double y) { + static_cast(glfwGetWindowUserPointer(window)) + ->input->set_mouse_pos(glm::vec2{x, y}); + }); + glfwSetMouseButtonCallback( + handle, [](GLFWwindow *window, int button, int action, int mods) { + auto self = static_cast(glfwGetWindowUserPointer(window)); + + auto mouse_button = ui::mouse_button::left; + switch (button) { + case GLFW_MOUSE_BUTTON_LEFT: + mouse_button = ui::mouse_button::left; + break; + case GLFW_MOUSE_BUTTON_RIGHT: + mouse_button = ui::mouse_button::right; + break; + case GLFW_MOUSE_BUTTON_MIDDLE: + mouse_button = ui::mouse_button::middle; + break; + default:; + } + + auto mouse_action = ui::button_state::down; + switch (action) { + case GLFW_PRESS: + mouse_action = ui::button_state::down; + break; + case GLFW_RELEASE: + mouse_action = ui::button_state::up; + break; + default:; + } + + self->input->set_mouse_button(mouse_button, mouse_action); + }); +} + +display::~display() { glfwDestroyWindow(handle); } + +display::display(const display &rhs) + : handle(create_window(rhs.extent(), rhs.title, this)), title(rhs.title) { + glfwSetWindowSizeCallback( + handle, [](GLFWwindow *window, int width, int height) { + auto self = static_cast(glfwGetWindowUserPointer(window)); + for (const auto &cb : self->resize_callbacks) { + cb(glm::uvec2(width, height)); + } + }); +} + +display::display(display &&rhs) noexcept + : handle(std::exchange(rhs.handle, nullptr)), title(rhs.title) {} + +display &display::operator=(const display &rhs) { return *this = display(rhs); } + +display &display::operator=(display &&rhs) noexcept { + std::swap(handle, rhs.handle); + std::swap(title, rhs.title); + return *this; +} + +bool display::should_close() const noexcept { + return glfwWindowShouldClose(handle); +} + +glm::uvec2 display::extent() const noexcept { + auto extent = glm::ivec2(); + glfwGetFramebufferSize(handle, &extent.x, &extent.y); + return extent; +} + +void display::poll_events() { glfwPollEvents(); } + +vk::raii::SurfaceKHR +display::create_surface(const vk::raii::Instance &instance) { + auto c_surface = VkSurfaceKHR(); + glfwCreateWindowSurface(*instance, handle, nullptr, &c_surface); + return vk::raii::SurfaceKHR(instance, c_surface); +} + +void display::resize_callback(std::function callback) { + resize_callbacks.push_back(callback); +} + +GLFWwindow *display::raw() const noexcept { return handle; } + +void display::request_close() noexcept { + glfwSetWindowShouldClose(handle, true); +} +} // namespace rog::ui diff --git a/source/ui/display.hpp b/source/ui/display.hpp new file mode 100644 index 0000000..d713c28 --- /dev/null +++ b/source/ui/display.hpp @@ -0,0 +1,57 @@ +#ifndef DISPLAY_HPP +#define DISPLAY_HPP + +#include +#include +#include + +#include "user_input.hpp" +#include +#include + +namespace rog::ui { + +class display { +private: + GLFWwindow *handle; + std::string_view title; + + user_input *input; + + [[nodiscard]] static GLFWwindow * + create_window(glm::uvec2 extent, std::string_view title, void *user_data); + + std::vector> resize_callbacks = {}; + +public: + display(user_input *input, glm::uvec2 extent, std::string_view title); + + ~display(); + + display(const display &rhs); + + display(display &&rhs) noexcept; + + display &operator=(const display &rhs); + + display &operator=(display &&rhs) noexcept; + + [[nodiscard]] bool should_close() const noexcept; + + [[nodiscard]] glm::uvec2 extent() const noexcept; + + void poll_events(); + + [[nodiscard]] vk::raii::SurfaceKHR + create_surface(const vk::raii::Instance &instance); + + void resize_callback(std::function callback); + + [[nodiscard]] GLFWwindow *raw() const noexcept; + + void request_close() noexcept; +}; + +} // namespace rog::ui + +#endif diff --git a/source/ui/user_input.cpp b/source/ui/user_input.cpp new file mode 100644 index 0000000..1cb8e62 --- /dev/null +++ b/source/ui/user_input.cpp @@ -0,0 +1,35 @@ +#include "user_input.hpp" + +namespace rog::ui { + +void user_input::set_key_state(key_code key, key_state state) { + const auto key_index = static_cast(key); + if (key_index < this->state.keys.size()) { + this->state.keys[key_index] = state; + } +} + +void user_input::set_mouse_pos(glm::vec2 pos) { state.mouse_pos = pos; } + +void user_input::set_mouse_button(mouse_button button, button_state state) { + this->state.mouse_buttons[static_cast(button)] = state; +} + +bool user_input::is_key_down(key_code code) const noexcept { + const auto idx = static_cast(code); + return state.keys[idx] == key_state::pressed || + state.keys[idx] == key_state::repeat || + state.keys[idx] == key_state::held; +} + +bool user_input::is_key_pressed(key_code code) const noexcept { + return state.keys[static_cast(code)] == key_state::pressed; +} + +glm::vec2 user_input::get_mouse_position() const noexcept { + return state.mouse_pos; +} + +input_state user_input::current_state() const noexcept { return state; } + +} // namespace rog::ui diff --git a/source/ui/user_input.hpp b/source/ui/user_input.hpp new file mode 100644 index 0000000..499a7fe --- /dev/null +++ b/source/ui/user_input.hpp @@ -0,0 +1,170 @@ +#ifndef USER_INPUT_HPP +#define USER_INPUT_HPP + +#include + +namespace rog::ui { + +enum class key_code { + space = 32, // I can't wait to write all of these out + apostrophe = 39, + comma = 44, + minus = 45, + period = 46, + slash = 47, + key_0 = 48, + key_1 = 49, + key_2 = 50, + key_3 = 51, + key_4 = 52, + key_5 = 53, + key_6 = 54, + key_7 = 55, + key_8 = 56, + key_9 = 57, // This is starting to get really annoying + semicolon = 59, + equal = 61, + key_a = 65, + key_b = 66, + key_c = 67, + key_d = 68, + key_e = 69, + key_f = 70, + key_g = 71, + key_h = 72, + key_i = 73, + key_j = 74, + key_k = 75, + key_l = 76, + key_m = 77, + key_n = 78, + key_o = 79, + key_p = 80, + key_q = 81, + key_r = 82, + key_s = 83, + key_t = 84, + key_u = 85, + key_v = 86, + key_w = 87, + key_x = 88, + key_y = 89, + key_z = 90, + key_left_bracket = 91, /* [ */ + key_backslash = 92, /* \ */ + key_right_bracket = 93, /* ] */ + key_grave_accent = 96, /* ` */ + key_world_1 = 161, /* non-US #1 */ + key_world_2 = 162, /* non-US #2 */ + key_escape = 256, + key_enter = 257, + key_tab = 258, + key_backspace = 259, + key_insert = 260, + key_delete = 261, + key_right = 262, + key_left = 263, + key_down = 264, + key_up = 265, + key_page_up = 266, + key_page_down = 267, + key_home = 268, + key_end = 269, + key_caps_lock = 280, + key_scroll_lock = 281, + key_num_lock = 282, + key_print_screen = 283, + key_pause = 284, + key_f1 = 290, + key_f2 = 291, + key_f3 = 292, + key_f4 = 293, + key_f5 = 294, + key_f6 = 295, + key_f7 = 296, + key_f8 = 297, + key_f9 = 298, + key_f10 = 299, + key_f11 = 300, + key_f12 = 301, + key_f13 = 302, + key_f14 = 303, + key_f15 = 304, + key_f16 = 305, + key_f17 = 306, + key_f18 = 307, + key_f19 = 308, + key_f20 = 309, + key_f21 = 310, + key_f22 = 311, + key_f23 = 312, + key_f24 = 313, + key_f25 = 314, + key_kp_0 = 320, + key_kp_1 = 321, + key_kp_2 = 322, + key_kp_3 = 323, + key_kp_4 = 324, + key_kp_5 = 325, + key_kp_6 = 326, + key_kp_7 = 327, + key_kp_8 = 328, + key_kp_9 = 329, + key_kp_decimal = 330, + key_kp_divide = 331, + key_kp_multiply = 332, + key_kp_subtract = 333, + key_kp_add = 334, + key_kp_enter = 335, + key_kp_equal = 336, + key_left_shift = 340, + key_left_control = 341, + key_left_alt = 342, + key_left_super = 343, + key_right_shift = 344, + key_right_control = 345, + key_right_alt = 346, + key_right_super = 347, + key_menu = 348, + max_key = 1024, +}; + +enum class key_state { none, pressed, held, repeat, released }; + +enum class button_state { down, up }; + +enum class mouse_button { + left, + right, + middle, +}; + +struct input_state { + std::array(key_code::max_key)> keys = {}; + std::array mouse_buttons = {}; + glm::vec2 mouse_pos = {}; +}; + +class user_input { +public: + void set_key_state(key_code key, key_state state); + + void set_mouse_pos(glm::vec2 pos); + + void set_mouse_button(mouse_button button, button_state state); + + [[nodiscard]] bool is_key_down(key_code code) const noexcept; + + [[nodiscard]] bool is_key_pressed(key_code code) const noexcept; + + [[nodiscard]] glm::vec2 get_mouse_position() const noexcept; + + [[nodiscard]] input_state current_state() const noexcept; + +private: + input_state state; +}; + +} // namespace rog::ui + +#endif