diff --git a/.clang-format b/.clang-format index 790c24f..9e2410d 100644 --- a/.clang-format +++ b/.clang-format @@ -2,6 +2,7 @@ AllowShortFunctionsOnASingleLine: InlineOnly AllowShortIfStatementsOnASingleLine: Never BreakBeforeBraces: Mozilla +BreakConstructorInitializers: AfterColon BinPackParameters: 'false' BinPackArguments: 'false' ColumnLimit: '100' diff --git a/CMakePresets.json b/CMakePresets.json index da8f842..f5c887e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,42 +14,28 @@ "deprecated": true, "unusedCli": true, "systemVars": false - }, - "errors": { - "dev": true, - "deprecated": true } }, { - "name": "dev-mode", - "hidden": true, - "inherits": "cmake-pedantic", - "cacheVariables": { - "CMAKE_COLOR_DIAGNOSTICS": true - } - }, - { - "name": "ci-std", + "name": "std", "description": "This preset makes sure the project actually builds with at least the specified standard", "hidden": true, "cacheVariables": { - "CMAKE_CXX_EXTENSIONS": "OFF" + "CMAKE_CXX_EXTENSIONS": "OFF", + "CMAKE_CXX_STANDARD": "20", + "CMAKE_CXX_STANDARD_REQUIRED": "ON" } }, { - "name": "flags-unix", - "hidden": true, - "cacheVariables": { - "CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wshadow -Wformat=2 -Wundef" - } - }, - { - "name": "ci-unix", + "name": "dev", "generator": "Unix Makefiles", - "hidden": true, - "inherits": ["flags-unix", "ci-std"], + "binaryDir": "${sourceDir}/build", + "inherits": [ + "std" + ], "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release" + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic" } } ] diff --git a/data/shaders/skybox.frag b/data/shaders/skybox.frag deleted file mode 100644 index 99c85c6..0000000 --- a/data/shaders/skybox.frag +++ /dev/null @@ -1,15 +0,0 @@ -#version 330 core - -layout(location = 0) out vec4 f_color; - -in vec3 v_texCoord; - -uniform samplerCube u_skybox; - -void main() { - - vec3 fragmentColor = vec3(texture(u_skybox, v_texCoord)); - - f_color = vec4(fragmentColor, 1.0f); - -} diff --git a/data/shaders/skybox.vert b/data/shaders/skybox.vert deleted file mode 100644 index 176ed4c..0000000 --- a/data/shaders/skybox.vert +++ /dev/null @@ -1,17 +0,0 @@ -#version 330 core - -layout(location = 0) in vec3 a_position; - -out vec3 v_texCoord; - -uniform mat4 projection; -uniform mat4 view; -uniform mat4 u_viewProjectionMatrix; - -void main() { - - gl_Position = u_viewProjectionMatrix * vec4(a_position, 1.0); - - v_texCoord = a_position; - -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 24f390e..2fc8a9b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(fever_engine scene/gltf_loader.cpp scene/scene.cpp util/log.cpp - window/Window.cpp + window/window.cpp ) target_compile_features(fever_engine PUBLIC cxx_std_20) diff --git a/src/bin/Controller.cpp b/src/bin/Controller.cpp index 3793cef..7509e0d 100644 --- a/src/bin/Controller.cpp +++ b/src/bin/Controller.cpp @@ -4,8 +4,8 @@ #include "core/shader.h" #include "core/time.h" #include "input/input.h" -#include "scene/gltf_loader.h" -#include "window/Window.h" +#include "scene/scene.h" +#include "window/window.h" #include #include @@ -22,27 +22,34 @@ using namespace entt::literals; -static constexpr unsigned MAX_FPS = 60; - -Controller::Controller() - : m_gameWindow(std::make_shared()), - post_processing_framebuffer(m_gameWindow->physical_dimensions()), - m_gltf_loader{.image_cache = m_image_cache, - .material_cache = m_material_cache, - .mesh_cache = m_mesh_cache, - .shader_cache = m_shader_cache, - .scene_cache = m_scene_cache, - .gltf_mesh_cache = m_gltf_mesh_cache, - .gltf_node_cache = m_gltf_node_cache}, - m_gltf_cache(m_gltf_loader) +Controller::Controller() : + m_gameWindow(std::make_shared(event_dispatcher)), + post_processing_framebuffer(m_gameWindow->physical_dimensions()), + m_gltf_loader{.image_cache = m_image_cache, + .material_cache = m_material_cache, + .mesh_cache = m_mesh_cache, + .shader_cache = m_shader_cache, + .scene_cache = m_scene_cache, + .gltf_mesh_cache = m_gltf_mesh_cache, + .gltf_node_cache = m_gltf_node_cache, + .registry = registry}, + m_gltf_cache(m_gltf_loader), + key_listener{.registry = registry}, + cursor_listener{.registry = registry} { - std::filesystem::path document_path("ABeautifulGame.glb"); + std::filesystem::path document_path("WaterBottle/glTF-Binary/WaterBottle.glb"); entt::hashed_string document_hash(document_path.c_str()); entt::resource gltf_document = m_gltf_cache.load(document_hash, document_path).first->second; m_scene = gltf_document->default_scene.value_or(gltf_document->scenes.at(0)).handle(); + + Input::State::init_state(registry); + + event_dispatcher.sink().connect<&Controller::recreate_framebuffer>(this); + event_dispatcher.sink().connect<&Input::KeyListener::key_event>(key_listener); + event_dispatcher.sink().connect<&Input::CursorListener::cursor_event>(cursor_listener); } void Controller::run() @@ -54,41 +61,44 @@ void Controller::run() spdlog::info("Startup complete. Enter game loop."); // This is the game loop - while (glfwWindowShouldClose(&m_gameWindow->glfw_window()) == GLFW_FALSE) { + while (glfwWindowShouldClose(&m_gameWindow->handle()) == GLFW_FALSE) { // --- Timing --- - Time::update_delta_time(m_scene->registry()); + Time::update_delta_time(registry); // --- Check events, handle input --- - m_gameWindow->clear_mouse_cursor_input(); + // m_gameWindow->clear_mouse_cursor_input(); glfwPollEvents(); - Input::handle_keyboard_input(m_scene->registry(), m_gameWindow->key_input()); - Input::handle_mouse_cursor_input(m_scene->registry(), m_gameWindow->mouse_cursor_input()); - Input::handle_mouse_button_input(m_scene->registry(), m_gameWindow->mouse_button_input()); - m_gameWindow->update_catched_mouse(m_scene->registry()); - m_gameWindow->update_descriptor(m_scene->registry()); - // --- Update game state --- + event_dispatcher.update(); + + m_gameWindow->update_descriptor(registry); + m_gameWindow->mouse_catching(registry); + m_gameWindow->close_on_esc(registry); + m_scene->update(); + Input::State::update_state(registry); + Input::reset_mouse_motion(registry); + // --- Render and buffer swap --- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); post_processing_framebuffer.bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - Light::update_lights(m_scene->registry(), standard_material_shader); - Render::render(m_scene->registry()); + Light::update_lights(registry, standard_material_shader); + Render::render(registry); Framebuffer::unbind(); post_processing_framebuffer.draw(post_processing_shader); - glfwSwapBuffers(&m_gameWindow->glfw_window()); - - // Update window size - if (m_gameWindow->dimensions_changed()) { - auto dimensions = m_gameWindow->physical_dimensions(); - post_processing_framebuffer = Framebuffer(dimensions); - } + glfwSwapBuffers(&m_gameWindow->handle()); } } + +void Controller::recreate_framebuffer() +{ + auto dimensions = m_gameWindow->physical_dimensions(); + post_processing_framebuffer = Framebuffer(dimensions); +} diff --git a/src/bin/Controller.h b/src/bin/Controller.h index d1ee0d6..c58d808 100644 --- a/src/bin/Controller.h +++ b/src/bin/Controller.h @@ -2,8 +2,9 @@ #include "core/graphics/framebuffer.h" #include "core/shader.h" +#include "entt/entity/fwd.hpp" #include "scene/gltf_loader.h" -#include "scene/scene.h" +#include "input/input.h" #include #include @@ -11,9 +12,9 @@ #include #include -class Window; class Scene; class Camera; +class Window; class Framebuffer; class Controller @@ -24,14 +25,19 @@ public: void run(); private: + void recreate_framebuffer(); + std::shared_ptr m_gameWindow; std::shared_ptr m_scene; - Shader skybox_shader{"skybox", "data/shaders"}; Shader post_processing_shader{"post_processing", "data/shaders"}; Framebuffer post_processing_framebuffer; - double m_deltaTime{}; + entt::registry registry; + + entt::dispatcher event_dispatcher{}; + Input::KeyListener key_listener; + Input::CursorListener cursor_listener; // Resource caches entt::resource_cache m_image_cache; diff --git a/src/core/camera.cpp b/src/core/camera.cpp index 13ff6f3..b76d473 100644 --- a/src/core/camera.cpp +++ b/src/core/camera.cpp @@ -1,7 +1,7 @@ #include "camera.h" #include "core/time.h" #include "input/input.h" -#include "window/Window.h" +#include "window/window.h" #include #include @@ -40,7 +40,7 @@ void Camera::keyboard_movement(entt::registry& registry) }; auto& movement_context = registry.ctx().emplace(); - auto const& key_input = registry.ctx().get(); + auto const& key_state = registry.ctx().get>(); auto const& delta_time = registry.ctx().get(); auto camera_view = registry.view(); @@ -55,29 +55,28 @@ void Camera::keyboard_movement(entt::registry& registry) SPEED * delta_time.delta.count() * (movement_context.accelerate ? ACCELERATION : 1.0F); movement_context.accelerate = false; - for (auto const& [key, pressed] : key_input.key_map) { - if (key == GLFW_KEY_W && pressed) { - delta_pos += delta_factor * glm::normalize(front_vec); - } - if (key == GLFW_KEY_S && pressed) { - delta_pos -= delta_factor * glm::normalize(front_vec); - } - if (key == GLFW_KEY_A && pressed) { - delta_pos -= delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR)); - } - if (key == GLFW_KEY_D && pressed) { - delta_pos += delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR)); - } - if (key == GLFW_KEY_SPACE && pressed) { - delta_pos += delta_factor * UP_VECTOR; - } - if (key == GLFW_KEY_LEFT_SHIFT && pressed) { - delta_pos -= delta_factor * UP_VECTOR; - } - if (key == GLFW_KEY_LEFT_ALT && pressed) { - movement_context.accelerate = true; - } + if (key_state.pressed(Input::KeyCode{GLFW_KEY_W})) { + delta_pos += delta_factor * glm::normalize(front_vec); } + if (key_state.pressed(Input::KeyCode{GLFW_KEY_S})) { + delta_pos -= delta_factor * glm::normalize(front_vec); + } + if (key_state.pressed(Input::KeyCode{GLFW_KEY_A})) { + delta_pos -= delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR)); + } + if (key_state.pressed(Input::KeyCode{GLFW_KEY_D})) { + delta_pos += delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR)); + } + if (key_state.pressed(Input::KeyCode{GLFW_KEY_SPACE})) { + delta_pos += delta_factor * UP_VECTOR; + } + if (key_state.pressed(Input::KeyCode{GLFW_KEY_LEFT_SHIFT})) { + delta_pos -= delta_factor * UP_VECTOR; + } + if (key_state.pressed(Input::KeyCode{GLFW_KEY_LEFT_ALT})) { + movement_context.accelerate = true; + } + camera_transform.translation += delta_pos; } @@ -87,16 +86,12 @@ void Camera::mouse_orientation(entt::registry& registry) auto camera_entity = camera_view.front(); auto [camera, camera_transform] = camera_view.get(camera_entity); - auto const& mouse_cursor_input = registry.ctx().get(); - auto [deltaX, deltaY] = mouse_cursor_input.cursor_movement; + auto const& mouse_cursor_input = registry.ctx().get(); + auto delta_x = mouse_cursor_input.delta.x; + auto delta_y = mouse_cursor_input.delta.y; - if (std::abs(deltaX) < std::numeric_limits::epsilon() && - std::abs(deltaY) < std::numeric_limits::epsilon()) { - return; - } - - auto pitch = static_cast(deltaY); - auto yaw = static_cast(deltaX); + auto pitch = static_cast(-delta_y); + auto yaw = static_cast(delta_x); // Orthographic projection currently unsupported auto& camera_perspective = std::get(camera.projection); diff --git a/src/input/input.cpp b/src/input/input.cpp index db585bc..37350c3 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -1,21 +1,44 @@ #include "input.h" -void Input::handle_keyboard_input(entt::registry ®istry, Key const &key_input) +#include + +namespace Input { + +void KeyListener::key_event(KeyInput const& key_input_event) { - registry.ctx().erase(); - registry.ctx().emplace(key_input); + auto& key_state = registry.ctx().emplace>(); + + if (key_input_event.action == static_cast(GLFW_PRESS)) { + key_state.press(key_input_event.key_code); + } else if (key_input_event.action == static_cast(GLFW_RELEASE)) { + key_state.release(key_input_event.key_code); + } } -void Input::handle_mouse_button_input(entt::registry ®istry, - MouseButton const &mouse_button_input) +void CursorListener::cursor_event(MouseMotion const& mouse_motion_event) { - registry.ctx().erase(); - registry.ctx().emplace(mouse_button_input); + auto& mouse_motion = registry.ctx().emplace(); + mouse_motion.delta += mouse_motion_event.delta; } -void Input::handle_mouse_cursor_input(entt::registry ®istry, - MouseCursor const &mouse_cursor_input) +void reset_mouse_motion(entt::registry& registry) { - registry.ctx().erase(); - registry.ctx().emplace(mouse_cursor_input); + auto& mouse_motion = registry.ctx().emplace(); + mouse_motion = {}; } + +template void State::init_state(entt::registry& registry) +{ + registry.ctx().emplace>(); +} + +template void State::update_state(entt::registry& registry) +{ + auto& state = registry.ctx().get>(); + state.just_pressed_keys.clear(); + state.just_released_keys.clear(); +} + +template class State; + +} // namespace Input diff --git a/src/input/input.h b/src/input/input.h index f2e8e67..9b23bdc 100644 --- a/src/input/input.h +++ b/src/input/input.h @@ -1,27 +1,77 @@ #pragma once +#include "entt/entity/fwd.hpp" #include -#include +#include +#include #include namespace Input { -struct Key +enum class KeyCode : int { - std::unordered_map key_map; }; -struct MouseButton +enum class Action : int { - std::unordered_map button_map; -}; -struct MouseCursor -{ - std::pair cursor_movement; }; -void handle_keyboard_input(entt::registry ®istry, Key const &key_input); -void handle_mouse_button_input(entt::registry ®istry, MouseButton const &mouse_button_input); -void handle_mouse_cursor_input(entt::registry ®istry, MouseCursor const &mouse_cursor_input); +struct KeyInput +{ + KeyCode key_code; + Action action; +}; + +struct MouseMotion +{ + glm::vec2 delta{}; +}; + +template class State +{ +public: + auto pressed(T input) const -> bool { return pressed_keys.contains(input); } + auto just_pressed(T input) const -> bool { return just_pressed_keys.contains(input); } + auto just_released(T input) const -> bool { return just_pressed_keys.contains(input); } + + static void init_state(entt::registry& registry); + static void update_state(entt::registry& registry); + +private: + void press(T input) + { + if (pressed_keys.insert(input).second) { + just_pressed_keys.insert(input); + } + } + + void release(T input) + { + if (pressed_keys.erase(input) != 0) { + just_released_keys.insert(input); + } + } + + std::set pressed_keys; + std::set just_pressed_keys; + std::set just_released_keys; + + friend class KeyListener; + friend class CursorListener; +}; + +struct KeyListener +{ + entt::registry& registry; + void key_event(KeyInput const& key_input_event); +}; + +struct CursorListener +{ + entt::registry& registry; + void cursor_event(MouseMotion const& mouse_motion_event); +}; + +void reset_mouse_motion(entt::registry& registry); }; // namespace Input diff --git a/src/scene/gltf_loader.cpp b/src/scene/gltf_loader.cpp index 7a4b090..824ee51 100644 --- a/src/scene/gltf_loader.cpp +++ b/src/scene/gltf_loader.cpp @@ -405,13 +405,10 @@ auto GltfLoader::operator()(std::filesystem::path const& document_path) -> resul spdlog::warn("glTF scene has no name."); } - entt::registry registry; - // Spawn an entity for every node in scene for (auto const& node : nodes) { std::function)> spawn_node = - [this, &spawn_node, ®istry](GltfNode const& node, - std::optional parent) { + [this, &spawn_node](GltfNode const& node, std::optional parent) { auto entity = registry.create(); registry.emplace(entity, node.name); registry.emplace(entity, node.transform); @@ -475,7 +472,7 @@ auto GltfLoader::operator()(std::filesystem::path const& document_path) -> resul entt::hashed_string scene_hash(gltf_scene.name.c_str()); entt::resource scene_resource = - scene_cache.load(scene_hash, Scene{std::move(registry)}).first->second; + scene_cache.load(scene_hash, Scene{registry}).first->second; scenes.push_back(scene_resource); } diff --git a/src/scene/gltf_loader.h b/src/scene/gltf_loader.h index 23c663e..2759e95 100644 --- a/src/scene/gltf_loader.h +++ b/src/scene/gltf_loader.h @@ -1,7 +1,7 @@ #pragma once +#include "entt/entity/fwd.hpp" #include "gltf.h" -#include "components/transform.h" #include #include @@ -9,18 +9,20 @@ static constexpr auto MAX_SIZE = 512 * 1024 * 1024; -struct GltfLoader final +struct GltfLoader { using result_type = std::shared_ptr; - auto operator()(std::filesystem::path const &document_path) -> result_type; + auto operator()(std::filesystem::path const& document_path) -> result_type; - entt::resource_cache &image_cache; - entt::resource_cache &material_cache; - entt::resource_cache &mesh_cache; - entt::resource_cache &shader_cache; - entt::resource_cache &scene_cache; + entt::resource_cache& image_cache; + entt::resource_cache& material_cache; + entt::resource_cache& mesh_cache; + entt::resource_cache& shader_cache; + entt::resource_cache& scene_cache; - entt::resource_cache &gltf_mesh_cache; - entt::resource_cache &gltf_node_cache; + entt::resource_cache& gltf_mesh_cache; + entt::resource_cache& gltf_node_cache; + + entt::registry& registry; }; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 48c8ffa..6bd8c9b 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -2,55 +2,56 @@ #include "components/name.h" #include "components/transform.h" #include "core/camera.h" +#include "core/graphics/material.h" #include "core/graphics/mesh.h" #include "core/light.h" -#include "window/Window.h" +#include "window/window.h" -Scene::Scene(entt::registry registry) : m_registry(std::move(registry)) +Scene::Scene(entt::registry& registry) : registry(registry) { - auto mesh_view = m_registry.view>(); + auto mesh_view = registry.view>(); for (auto [entity, mesh] : mesh_view.each()) { - m_registry.emplace(entity, GpuMesh(mesh)); + registry.emplace(entity, GpuMesh(mesh)); // Remove Mesh resource as it is no longer needed. - m_registry.erase>(entity); + registry.erase>(entity); } - auto material_view = m_registry.view>(); + auto material_view = registry.view>(); for (auto [entity, material] : material_view.each()) { - m_registry.emplace(entity, GpuMaterial(material)); + registry.emplace(entity, GpuMaterial(material)); // Remove Material resource as it is no longer needed. - m_registry.erase>(entity); + registry.erase>(entity); } // Spawn default lights - auto directional_light = m_registry.create(); - m_registry.emplace(directional_light, "Directional Light"); - m_registry.emplace( + auto directional_light = registry.create(); + registry.emplace(directional_light, "Directional Light"); + registry.emplace( directional_light, Transform{.orientation = glm::toQuat( glm::lookAt({}, DirectionalLight::DEFAULT_DIRECTION, Camera::UP_VECTOR))}); - m_registry.emplace(directional_light, GlobalTransform{}); - m_registry.emplace( + registry.emplace(directional_light, GlobalTransform{}); + registry.emplace( directional_light, DirectionalLight{.illuminance = DirectionalLight::DEFAULT_ILLUMINANCE}); - auto point_light = m_registry.create(); - m_registry.emplace(point_light, "Point Light"); - m_registry.emplace(point_light, - Transform{.translation = PointLight::DEFAULT_POSITION}); - m_registry.emplace(point_light, GlobalTransform{}); - m_registry.emplace(point_light, - PointLight{.intensity = PointLight::DEFAULT_INTENSITY}); + auto point_light = registry.create(); + registry.emplace(point_light, "Point Light"); + registry.emplace(point_light, + Transform{.translation = PointLight::DEFAULT_POSITION}); + registry.emplace(point_light, GlobalTransform{}); + registry.emplace(point_light, + PointLight{.intensity = PointLight::DEFAULT_INTENSITY}); } void Scene::update() { - GlobalTransform::update(m_registry); - Camera::aspect_ratio_update(m_registry); - Camera::keyboard_movement(m_registry); + GlobalTransform::update(registry); + Camera::aspect_ratio_update(registry); + Camera::keyboard_movement(registry); - if (m_registry.ctx().get().catched) { - Camera::mouse_orientation(m_registry); + if (registry.ctx().get().catched) { + Camera::mouse_orientation(registry); } } diff --git a/src/scene/scene.h b/src/scene/scene.h index 4ba37a3..f198c93 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -1,18 +1,15 @@ #pragma once -#include "gltf.h" - #include #include class Scene { public: - Scene(entt::registry registry); + Scene(entt::registry& registry); void update(); - auto registry() -> auto & { return m_registry; } private: - entt::registry m_registry; + entt::registry& registry; }; diff --git a/src/window/Window.cpp b/src/window/Window.cpp deleted file mode 100644 index 47c4ce1..0000000 --- a/src/window/Window.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "Window.h" -#include "core/glad.h" - -#include -#include -#include - -static constexpr unsigned INIT_WINDOW_WIDTH = 1280; -static constexpr unsigned INIT_WINDOW_HEIGHT = 720; - -Window::Window() -{ - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - -#ifndef NDEBUG - glfwSetErrorCallback(glfw_error_callback); - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); -#else - // Maximize in release build - glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); -#endif - - m_glfw_window = std::shared_ptr( - glfwCreateWindow(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, "OpenGL", nullptr, nullptr), - [](GLFWwindow* window) { glfwDestroyWindow(window); }); - - if (!m_glfw_window) { - spdlog::critical("Failed to create window"); - } - - // Create OpenGL context - glfwMakeContextCurrent(m_glfw_window.get()); - -#ifndef NDEBUG - // Disable mouse cursor - m_mouse_catched.catched = false; -#endif - - set_catched_cursor(m_mouse_catched.catched); - - // Callbacks - glfwSetWindowUserPointer(m_glfw_window.get(), this); - glfwSetKeyCallback(m_glfw_window.get(), key_callback); - glfwSetCursorPosCallback(m_glfw_window.get(), mouse_cursor_callback); - glfwSetFramebufferSizeCallback(m_glfw_window.get(), framebuffer_size_callback); - - init_glad(); -} - -auto Window::dimensions_changed() -> bool -{ - bool temp = m_dimensions_changed; - m_dimensions_changed = false; - return temp; -} - -void Window::set_catched_cursor(bool value) -{ - glfwSetInputMode( - m_glfw_window.get(), GLFW_CURSOR, value ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); - - m_mouse_catched.catched = value; -} - -void Window::glfw_error_callback(int error, char const* description) -{ - spdlog::warn("GLFW [{:d}]: {:s}\n", error, description); -} - -void Window::framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height) -{ - auto& window = *static_cast(glfwGetWindowUserPointer(glfw_window)); - window.m_dimensions_changed = true; - glViewport(0, 0, width, height); -} - -void Window::key_callback(GLFWwindow* glfw_window, - int key, - [[maybe_unused]] int scancode, - int action, - [[maybe_unused]] int mods) -{ - auto& window = *static_cast(glfwGetWindowUserPointer(glfw_window)); - - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - glfwSetWindowShouldClose(glfw_window, GLFW_TRUE); - } else if (key == GLFW_KEY_LEFT_CONTROL && action == GLFW_PRESS) { - window.set_catched_cursor(!window.m_mouse_catched.catched); - } else if (key == GLFW_KEY_O && action == GLFW_PRESS) { - window.m_wire_frame_mode = !window.m_wire_frame_mode; - glPolygonMode(GL_FRONT_AND_BACK, window.m_wire_frame_mode ? GL_LINE : GL_FILL); - } else if (action == GLFW_PRESS || action == GLFW_RELEASE) { - window.m_key_input.key_map[key] = (action == GLFW_PRESS); - } -} - -void Window::mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos) -{ - auto& window = *static_cast(glfwGetWindowUserPointer(glfw_window)); - - double deltaCursorPosX = xpos - window.m_last_cursor_pos_x; - double deltaCursorPosY = -(ypos - window.m_last_cursor_pos_y); - - window.m_last_cursor_pos_x = xpos; - window.m_last_cursor_pos_y = ypos; - - // Check if this is the first VALID mouse event after window being resized - if (window.m_first_mouse_input && - (std::abs(deltaCursorPosX) >= std::numeric_limits::epsilon() || - std::abs(deltaCursorPosY) >= std::numeric_limits::epsilon())) { - window.m_first_mouse_input = false; - deltaCursorPosX = 0.0; - deltaCursorPosY = 0.0; - } - - deltaCursorPosX *= MOUSE_SENSITIVITY; - deltaCursorPosY *= MOUSE_SENSITIVITY; - - auto& [deltaX, deltaY] = window.m_mouse_cursor_input.cursor_movement; - deltaX += deltaCursorPosX; - deltaY += deltaCursorPosY; -} - -auto Window::logical_dimensions() const -> glm::u32vec2 -{ - int width{}; - int height{}; - glfwGetWindowSize(m_glfw_window.get(), &width, &height); - return {width, height}; -} - -auto Window::physical_dimensions() const -> glm::u32vec2 -{ - int width{}; - int height{}; - glfwGetFramebufferSize(m_glfw_window.get(), &width, &height); - return {width, height}; -} - -void Window::clear_mouse_cursor_input() -{ - m_mouse_cursor_input = {}; -}; - -void Window::update_catched_mouse(entt::registry& registry) const -{ - registry.ctx().erase(); - registry.ctx().emplace(m_mouse_catched); -} - -void Window::update_descriptor(entt::registry& registry) const -{ - auto dimensions = logical_dimensions(); - - registry.ctx().erase(); - registry.ctx().emplace(Descriptor{ - .logical_dimensions = dimensions, - .aspect_ratio = static_cast(dimensions.x) / static_cast(dimensions.y)}); -} diff --git a/src/window/Window.h b/src/window/Window.h deleted file mode 100644 index e1fff97..0000000 --- a/src/window/Window.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "input/input.h" - -#include -#include -#include -#include - -class GLFWwindow; - -class Window -{ -public: - struct MouseCatched - { - bool catched = true; - }; - - struct Descriptor - { - glm::u32vec2 logical_dimensions; - float aspect_ratio{}; - }; - - Window(); - - [[nodiscard]] auto glfw_window() -> GLFWwindow& { return *m_glfw_window; } - [[nodiscard]] auto physical_dimensions() const -> glm::u32vec2; - [[nodiscard]] auto logical_dimensions() const -> glm::u32vec2; - [[nodiscard]] auto dimensions_changed() -> bool; - [[nodiscard]] auto key_input() const -> auto const& { return m_key_input; } - [[nodiscard]] auto mouse_cursor_input() const -> auto const& { return m_mouse_cursor_input; } - [[nodiscard]] auto mouse_button_input() const -> auto const& { return m_mouse_button_input; } - [[nodiscard]] auto cursor_catched() const -> auto { return m_mouse_catched; } - - void clear_mouse_cursor_input(); - void update_catched_mouse(entt::registry& registry) const; - void update_descriptor(entt::registry& registry) const; - -private: - static void key_callback(GLFWwindow* glfw_window, int key, int scancode, int action, int mods); - static void mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos); - static void framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height); - static void glfw_error_callback(int error, char const* description); - - static constexpr float MOUSE_SENSITIVITY = 0.15F; - - void set_catched_cursor(bool value); - - std::shared_ptr m_glfw_window; - - // - Input::Key m_key_input; - Input::MouseButton m_mouse_button_input; - Input::MouseCursor m_mouse_cursor_input; - - bool m_dimensions_changed = false; - double m_last_cursor_pos_x = 0.0; - double m_last_cursor_pos_y = 0.0; - - bool m_first_mouse_input = false; - MouseCatched m_mouse_catched; - bool m_wire_frame_mode = false; -}; diff --git a/src/window/window.cpp b/src/window/window.cpp new file mode 100644 index 0000000..7c5ef2a --- /dev/null +++ b/src/window/window.cpp @@ -0,0 +1,149 @@ +#include "window.h" +#include "core/glad.h" +#include "input/input.h" + +#include + +#include +#include + +static constexpr unsigned INIT_WINDOW_WIDTH = 1280; +static constexpr unsigned INIT_WINDOW_HEIGHT = 720; + +Window::Window(entt::dispatcher& event_dispatcher) : event_dispatcher(event_dispatcher) +{ + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifndef NDEBUG + glfwSetErrorCallback(glfw_error_callback); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); +#else + // Maximize in release build + glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); +#endif + + glfw_window = std::shared_ptr( + glfwCreateWindow(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, "OpenGL", nullptr, nullptr), + [](GLFWwindow* window) { glfwDestroyWindow(window); }); + + if (!glfw_window) { + spdlog::critical("Failed to create window"); + } + + // Create OpenGL context + glfwMakeContextCurrent(glfw_window.get()); + + // Callbacks + glfwSetWindowUserPointer(glfw_window.get(), this); + glfwSetKeyCallback(glfw_window.get(), key_callback); + glfwSetCursorPosCallback(glfw_window.get(), mouse_cursor_callback); + glfwSetFramebufferSizeCallback(glfw_window.get(), framebuffer_size_callback); + + init_glad(); +} + +void Window::glfw_error_callback(int error, char const* description) +{ + spdlog::warn("GLFW [{:d}]: {:s}\n", error, description); +} + +void Window::framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height) +{ + auto& window = *static_cast(glfwGetWindowUserPointer(glfw_window)); + glViewport(0, 0, width, height); + + window.event_dispatcher.enqueue(); +} + +void Window::key_callback(GLFWwindow* glfw_window, + int key, + [[maybe_unused]] int scancode, + int action, + [[maybe_unused]] int mods) +{ + auto& window = *static_cast(glfwGetWindowUserPointer(glfw_window)); + + window.event_dispatcher.enqueue( + Input::KeyInput{.key_code = static_cast(key), + .action = static_cast(action)}); +} + +void Window::mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos) +{ + auto& window = *static_cast(glfwGetWindowUserPointer(glfw_window)); + + glm::vec2 delta{xpos - window.last_cursor_pos.x, ypos - window.last_cursor_pos.y}; + + window.last_cursor_pos = {xpos, ypos}; + delta *= MOUSE_SENSITIVITY; + + // Check if this is the first VALID mouse event after window being resized + if (window.first_mouse_input && (delta.x >= std::numeric_limits::epsilon() || + delta.y >= std::numeric_limits::epsilon())) { + window.first_mouse_input = false; + return; + } + + window.event_dispatcher.enqueue(Input::MouseMotion{.delta = delta}); +} + +auto Window::logical_dimensions() const -> glm::u32vec2 +{ + int width{}; + int height{}; + glfwGetWindowSize(glfw_window.get(), &width, &height); + return {width, height}; +} + +auto Window::physical_dimensions() const -> glm::u32vec2 +{ + int width{}; + int height{}; + glfwGetFramebufferSize(glfw_window.get(), &width, &height); + return {width, height}; +} + +void Window::mouse_catching(entt::registry& registry) const +{ + if (!registry.ctx().contains()) { + bool catched = true; + +#ifndef NDEBUG + catched = false; +#endif + + registry.ctx().emplace(MouseCatched{.catched = catched}); + } + + auto& mouse_catched = registry.ctx().get(); + auto const& key_state = registry.ctx().get>(); + + if (key_state.just_pressed(Input::KeyCode{GLFW_KEY_LEFT_CONTROL})) { + mouse_catched.catched = !mouse_catched.catched; + + glfwSetInputMode(glfw_window.get(), + GLFW_CURSOR, + mouse_catched.catched ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); + } +} + +void Window::close_on_esc(entt::registry& registry) const +{ + auto const& key_state = registry.ctx().get>(); + + if (key_state.just_pressed(Input::KeyCode{GLFW_KEY_ESCAPE})) { + glfwSetWindowShouldClose(glfw_window.get(), GLFW_TRUE); + } +} + +void Window::update_descriptor(entt::registry& registry) const +{ + auto dimensions = logical_dimensions(); + + registry.ctx().erase(); + registry.ctx().emplace(Descriptor{ + .logical_dimensions = dimensions, + .aspect_ratio = static_cast(dimensions.x) / static_cast(dimensions.y)}); +} diff --git a/src/window/window.h b/src/window/window.h new file mode 100644 index 0000000..86e8c67 --- /dev/null +++ b/src/window/window.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include + +class GLFWwindow; + +class Window +{ +public: + struct MouseCatched + { + bool catched = true; + }; + + struct Descriptor + { + glm::u32vec2 logical_dimensions; + float aspect_ratio{}; + }; + + struct ResizeEvent + {}; + + Window(entt::dispatcher& event_dispatcher); + + [[nodiscard]] auto handle() -> GLFWwindow& { return *glfw_window; } + [[nodiscard]] auto physical_dimensions() const -> glm::u32vec2; + [[nodiscard]] auto logical_dimensions() const -> glm::u32vec2; + + void update_descriptor(entt::registry& registry) const; + void mouse_catching(entt::registry& registry) const; + void close_on_esc(entt::registry& registry) const; + +private: + static void key_callback(GLFWwindow* glfw_window, int key, int scancode, int action, int mods); + static void mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos); + static void framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height); + static void glfw_error_callback(int error, char const* description); + + static constexpr float MOUSE_SENSITIVITY = 0.15F; + + std::shared_ptr glfw_window; + + entt::dispatcher& event_dispatcher; + + glm::vec2 last_cursor_pos{}; + bool first_mouse_input = true; +};