From 5b60668f71c2780f5c988e2caf18648de9fbb098 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 13 Apr 2024 12:11:36 +0100 Subject: [PATCH] Fix window resize in wayland --- dependencies/SDL | 2 +- dependencies/VulkanMemoryAllocator | 2 +- include/gfx_device.h | 4 +- include/renderer.h | 6 +- src/application.cpp | 3 +- src/gfx_device_vulkan.cpp | 27 +- src/renderer.cpp | 9 +- src/util/gltf_loader.cpp | 16 +- src/vulkan/gpu_allocator.cpp | 2 + src/vulkan/swapchain.cpp | 4 +- src/window.cpp | 865 +++++++++++++---------------- test/src/camera_controller.cpp | 2 +- test/src/camera_controller.hpp | 5 +- 13 files changed, 424 insertions(+), 523 deletions(-) diff --git a/dependencies/SDL b/dependencies/SDL index 2f4a7bb..859844e 160000 --- a/dependencies/SDL +++ b/dependencies/SDL @@ -1 +1 @@ -Subproject commit 2f4a7bbcedaaf49c8bf87e34081120a6a49a0d26 +Subproject commit 859844eae358447be8d66e6da59b6fb3df0ed778 diff --git a/dependencies/VulkanMemoryAllocator b/dependencies/VulkanMemoryAllocator index c351692..a6bfc23 160000 --- a/dependencies/VulkanMemoryAllocator +++ b/dependencies/VulkanMemoryAllocator @@ -1 +1 @@ -Subproject commit c351692490513cdb0e5a2c925aaf7ea4a9b672f4 +Subproject commit a6bfc237255a6bac1513f7c1ebde6d8aed6b5191 diff --git a/include/gfx_device.h b/include/gfx_device.h index 063810d..8a46b4b 100644 --- a/include/gfx_device.h +++ b/include/gfx_device.h @@ -23,7 +23,7 @@ class GFXDevice { void ShutdownImguiBackend(); void CmdRenderImguiDrawData(gfx::DrawBuffer* draw_buffer, ImDrawData* draw_data); - gfx::DrawBuffer* BeginRender(); + gfx::DrawBuffer* BeginRender(bool window_resized); /* - draw_buffer MUST be a valid pointer returned by BeginRender(). - draw_buffer is invalid after this function has been called. */ @@ -105,4 +105,4 @@ class GFXDevice { std::unique_ptr pimpl; }; -} // namespace engine \ No newline at end of file +} // namespace engine diff --git a/include/renderer.h b/include/renderer.h index ca97f6f..ca6a55e 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -35,10 +35,8 @@ class Renderer : private ApplicationComponent { ~Renderer(); - void PreRender(bool window_is_resized, glm::mat4 camera_transform); - // staticList can be nullptr to render nothing - void Render(const RenderList* static_list, const RenderList* dynamic_list, const std::vector& debug_lines); + void Render(bool window_is_resized, glm::mat4 camera_transform, const RenderList* static_list, const RenderList* dynamic_list, const std::vector& debug_lines); // getters @@ -118,4 +116,4 @@ class Renderer : private ApplicationComponent { void DrawRenderList(gfx::DrawBuffer* draw_buffer, const RenderList& render_list); }; -} // namespace engine \ No newline at end of file +} // namespace engine diff --git a/src/application.cpp b/src/application.cpp index 8d86148..6e8c9fe 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -413,8 +413,7 @@ void Application::GameLoop() static_list = mesh_render_system->GetStaticRenderList(); dynamic_list = mesh_render_system->GetDynamicRenderList(); } - renderer_->PreRender(window()->GetWindowResized(), camera_transform); - renderer_->Render(static_list, dynamic_list, debug_lines); + renderer_->Render(window()->GetWindowResized(), camera_transform, static_list, dynamic_list, debug_lines); debug_lines.clear(); // gets remade every frame :0 /* poll events */ diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 125af50..bc07692 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -67,7 +67,7 @@ static void check_vk_result(VkResult code) { checkVulkanError(code, -1); } namespace engine { -static constexpr uint32_t FRAMES_IN_FLIGHT = 2; // This improved FPS by 5x! (on Intel IGPU) +static constexpr uint32_t FRAMES_IN_FLIGHT = 2; // This improved FPS by 5x! (on Intel IGPU) static constexpr size_t PUSH_CONSTANT_MAX_SIZE = 128; // bytes static constexpr VkIndexType INDEX_TYPE = VK_INDEX_TYPE_UINT32; @@ -621,7 +621,7 @@ void GFXDevice::CmdRenderImguiDrawData(gfx::DrawBuffer* draw_buffer, ImDrawData* ImGui_ImplVulkan_RenderDrawData(draw_data, draw_buffer->frameData.drawBuf); } -gfx::DrawBuffer* GFXDevice::BeginRender() +gfx::DrawBuffer* GFXDevice::BeginRender(bool window_resized) { VkResult res; @@ -703,6 +703,12 @@ gfx::DrawBuffer* GFXDevice::BeginRender() uint32_t swapchainImageIndex; + if (window_resized && pimpl->FRAMECOUNT != 0) { // resize flag is true on first frame + pimpl->swapchainIsOutOfDate = true; + LOG_TRACE("Window resized, framecount == {}", pimpl->FRAMECOUNT); + } + + // THIS FUNCTION BLOCKS UNTIL AN IMAGE IS AVAILABLE (it waits for vsync) do { if (pimpl->swapchainIsOutOfDate) { // re-create swapchain @@ -714,7 +720,8 @@ gfx::DrawBuffer* GFXDevice::BeginRender() res = vkAcquireNextImageKHR(pimpl->device.device, pimpl->swapchain.swapchain, 1000000000LL, frameData.presentSemaphore, VK_NULL_HANDLE, &swapchainImageIndex); if (res != VK_SUBOPTIMAL_KHR && res != VK_ERROR_OUT_OF_DATE_KHR) VKCHECK(res); - if (res == VK_SUCCESS) pimpl->swapchainIsOutOfDate = false; + if (res == VK_ERROR_OUT_OF_DATE_KHR) pimpl->swapchainIsOutOfDate = true; + if (res == VK_SUCCESS || res == VK_SUBOPTIMAL_KHR) pimpl->swapchainIsOutOfDate = false; } while (pimpl->swapchainIsOutOfDate); /* record command buffer */ @@ -940,7 +947,7 @@ void GFXDevice::FinishRender(gfx::DrawBuffer* drawBuffer) std::vector waitSemaphores{}; std::vector waitDstStageMasks{}; - waitSemaphores.push_back(drawBuffer->frameData.presentSemaphore); // wait for image from 2nd last frame to be presented so it can be rendered to again + waitSemaphores.push_back(drawBuffer->frameData.presentSemaphore); // wait for image from 2nd last frame to be presented so it can be rendered to again waitDstStageMasks.push_back(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); waitSemaphores.push_back(drawBuffer->frameData.transferSemaphore); // wait for uniform buffer copies to complete waitDstStageMasks.push_back(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); @@ -970,8 +977,8 @@ void GFXDevice::FinishRender(gfx::DrawBuffer* drawBuffer) .pImageIndices = &swapchainImageIndex, .pResults = nullptr}; res = vkQueuePresentKHR(pimpl->device.queues.presentQueue, &presentInfo); - if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { - // flag to re-create the swapchain before next render + if (res == VK_ERROR_OUT_OF_DATE_KHR) { + // flag to re-create the swapchain next frame pimpl->swapchainIsOutOfDate = true; } else if (res != VK_SUCCESS) @@ -1351,9 +1358,9 @@ gfx::Pipeline* GFXDevice::CreatePipeline(const gfx::PipelineInfo& info) if (info.depth_attachment_only) { // use depth bias if only rendering to a depth attachment rasterizer.depthBiasEnable = VK_TRUE; - rasterizer.depthBiasConstantFactor = 2.0f;//1.25f; + rasterizer.depthBiasConstantFactor = 2.0f; // 1.25f; rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 3.5f;//1.75f; + rasterizer.depthBiasSlopeFactor = 3.5f; // 1.75f; } else { rasterizer.depthBiasEnable = VK_FALSE; @@ -1486,7 +1493,7 @@ gfx::DescriptorSetLayout* GFXDevice::CreateDescriptorSetLayout(const std::vector uint32_t i = 0; for (const auto& binding : bindings) { auto& vulkanBinding = vulkanBindings.emplace_back(); - vulkanBinding.binding = i; // This should be as low as possible to avoid wasting memory + vulkanBinding.binding = i; // This should be as low as possible to avoid wasting memory vulkanBinding.descriptorType = converters::getDescriptorType(binding.descriptor_type); vulkanBinding.descriptorCount = 1; // if > 1, accessible as an array in the shader vulkanBinding.stageFlags = converters::getShaderStageFlags(binding.stage_flags); @@ -1929,7 +1936,7 @@ gfx::Image* GFXDevice::CreateImage(uint32_t w, uint32_t h, gfx::ImageFormat inpu VkImageMemoryBarrier2 afterBlitBarrier{}; afterBlitBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; afterBlitBarrier.srcStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT; - afterBlitBarrier.srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT; // previous mip level was just READ from in a BLIT + afterBlitBarrier.srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT; // previous mip level was just READ from in a BLIT afterBlitBarrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; afterBlitBarrier.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_2_SHADER_READ_BIT; // it will next be sampled in a frag shader afterBlitBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; diff --git a/src/renderer.cpp b/src/renderer.cpp index c10a3e4..6df56c8 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -249,8 +249,9 @@ Renderer::~Renderer() device_->DestroyDescriptorSetLayout(global_uniform.layout); } -void Renderer::PreRender(bool window_is_resized, glm::mat4 camera_transform) +void Renderer::Render(bool window_is_resized, glm::mat4 camera_transform, const RenderList* static_list, const RenderList* dynamic_list, const std::vector& debug_lines) { + if (window_is_resized) { uint32_t w, h; device_->GetViewportSize(&w, &h); @@ -265,10 +266,6 @@ void Renderer::PreRender(bool window_is_resized, glm::mat4 camera_transform) const glm::mat4 view_matrix = glm::inverse(camera_transform); frame_uniform.uniform_buffer_data.data = view_matrix; device_->WriteUniformBuffer(frame_uniform.uniform_buffer, 0, sizeof(frame_uniform.uniform_buffer_data), &frame_uniform.uniform_buffer_data); -} - -void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_list, const std::vector& debug_lines) -{ if (rendering_started == false) { // render to shadow map @@ -292,7 +289,7 @@ void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_l last_bound_pipeline_ = nullptr; - gfx::DrawBuffer* draw_buffer = device_->BeginRender(); + gfx::DrawBuffer* draw_buffer = device_->BeginRender(window_is_resized); if (static_list) { if (!static_list->empty()) { diff --git a/src/util/gltf_loader.cpp b/src/util/gltf_loader.cpp index f06268c..74c98a9 100644 --- a/src/util/gltf_loader.cpp +++ b/src/util/gltf_loader.cpp @@ -239,7 +239,7 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } if (material.pbrMetallicRoughness.metallicFactor != 1.0 || material.pbrMetallicRoughness.roughnessFactor != 1.0) { if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != -1) { - LOG_WARN("Material {} contains a metallic and/or roughness multiplier which isn't supported yet.", material.name); + LOG_WARN("Material {} contains a metallic and/or roughness multiplier which isn't supported yet.", material.name); } } @@ -271,7 +271,8 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } // occlusion roughness metallic - materials.back()->SetOcclusionRoughnessMetallicTexture(scene.app()->GetResource("builtin.white")); // default ao = 1.0, rough = 1.0, metal = 1.0 + materials.back()->SetOcclusionRoughnessMetallicTexture( + scene.app()->GetResource("builtin.white")); // default ao = 1.0, rough = 1.0, metal = 1.0 if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != -1) { if (material.pbrMetallicRoughness.metallicRoughnessTexture.texCoord == 0) { LOG_DEBUG("Setting occlusion roughness metallic texture!"); @@ -283,7 +284,8 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } else { LOG_DEBUG("Creating occlusion roughness metallic texture..."); - const std::vector mr_values{1.0f /* no AO */, material.pbrMetallicRoughness.roughnessFactor, material.pbrMetallicRoughness.metallicFactor, 1.0f}; + const std::vector mr_values{1.0f /* no AO */, material.pbrMetallicRoughness.roughnessFactor, material.pbrMetallicRoughness.metallicFactor, + 1.0f}; Color mr(mr_values); if (metal_rough_textures.contains(mr) == false) { const uint8_t pixel[4] = {mr.r, mr.g, mr.b, mr.a}; @@ -302,7 +304,8 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) if (material.occlusionTexture.index != -1) { if (material.occlusionTexture.texCoord == 0) { if (material.occlusionTexture.index != material.pbrMetallicRoughness.metallicRoughnessTexture.index) { - throw std::runtime_error(std::string("Material ") + material.name + std::string(" has an ambient occlusion texture different to the metal-rough texture.")); + throw std::runtime_error(std::string("Material ") + material.name + + std::string(" has an ambient occlusion texture different to the metal-rough texture.")); } } else { @@ -518,8 +521,8 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) assert(num_unq_vertices >= 0); // get new vertices into the vector - vertices.resize(num_unq_vertices); - for (size_t i = 0; i < num_unq_vertices; ++i) { + vertices.resize(static_cast(num_unq_vertices)); + for (size_t i = 0; i < static_cast(num_unq_vertices); ++i) { vertices[i] = vertex_data_out[i]; } @@ -578,6 +581,7 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) // glTF uses the Y-up convention so the parent object must be rotated to Z-up const Entity parent = scene.CreateEntity(name, 0, glm::vec3{}, glm::quat{glm::one_over_root_two(), glm::one_over_root_two(), 0.0f, 0.0f}); + scene.GetTransform(parent)->is_static = isStatic; std::vector entities(model.nodes.size(), 0); std::function generateEntities = [&](Entity parent_entity, const tg::Node& node) -> void { diff --git a/src/vulkan/gpu_allocator.cpp b/src/vulkan/gpu_allocator.cpp index 8aaf6a6..190d61f 100644 --- a/src/vulkan/gpu_allocator.cpp +++ b/src/vulkan/gpu_allocator.cpp @@ -1,5 +1,7 @@ #include +#include // snprintf for vma + #include #define VMA_STATIC_VULKAN_FUNCTIONS 0 diff --git a/src/vulkan/swapchain.cpp b/src/vulkan/swapchain.cpp index 1c367b8..498d28e 100644 --- a/src/vulkan/swapchain.cpp +++ b/src/vulkan/swapchain.cpp @@ -19,8 +19,6 @@ namespace engine { sc->device = info.device; sc->allocator = info.allocator; - LOG_DEBUG("Recreating swapchain!\n"); - // get surface caps and features VkResult res; res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(info.physicalDevice, info.surface, &sc->surfaceCapabilities); @@ -249,6 +247,8 @@ namespace engine { } + LOG_INFO("Recreating swapchain! w: {} h: {}\n", sc->extent.width, sc->extent.height); + } void destroySwapchain(const Swapchain& sc) diff --git a/src/window.cpp b/src/window.cpp index 4a0c6c9..abfc64b 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -5,494 +5,387 @@ #include +#include "log.h" + static const uint64_t BILLION = 1000000000; namespace engine { - Window::Window(const std::string& title, bool resizable, bool fullscreen) - : title_(title), resizable_(resizable), fullscreen_(fullscreen) - { - - // init SDL - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - const std::string errMsg("Unable to initialise SDL: " + std::string(SDL_GetError())); - throw std::runtime_error(errMsg); - } - - counter_freq_ = SDL_GetPerformanceFrequency(); - start_time_ = GetNanos(); - last_frame_stamp_ = start_time_ - 1; - avg_fps_start_ = start_time_; - - Uint32 windowFlags = SDL_WINDOW_SHOWN; - - // use Vulkan 1.3 - windowFlags |= SDL_WINDOW_VULKAN; - - if (resizable_) { - windowFlags |= SDL_WINDOW_RESIZABLE; - } - - if (fullscreen_) { - windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - } - - // create the window - handle_ = SDL_CreateWindow( - title_.c_str(), - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - static_cast(win_size_.x), - static_cast(win_size_.y), - windowFlags); - if (handle_ == NULL) { - SDL_Quit(); - throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); - } - - const int WINDOWED_MIN_WIDTH = 640; - const int WINDOWED_MIN_HEIGHT = 480; - SDL_SetWindowMinimumSize(handle_, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT); - - // get window size - int winWidth, winHeight; - SDL_GetWindowSize(handle_, &winWidth, &winHeight); - - OnResize(winWidth, winHeight); - - } - - Window::~Window() - { - SDL_DestroyWindow(handle_); - SDL_Quit(); - } - - // private methods - - void Window::OnResize(Sint32 width, Sint32 height) - { - // get window size - win_size_.x = static_cast(width); - win_size_.y = static_cast(height); - - just_resized_ = true; - } - - void Window::ResetInputDeltas() - { - just_resized_ = false; - - keyboard_.deltas.fill(ButtonDelta::kSame); - - mouse_.deltas.fill(ButtonDelta::kSame); - mouse_.dx = 0; - mouse_.dy = 0; - mouse_.xscroll = 0.0f; - mouse_.yscroll = 0.0f; - } - - // TODO event methods (like callbacks) - - void Window::OnWindowEvent(SDL_WindowEvent& e) - { - - switch (e.event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - OnResize(e.data1, e.data2); - break; - case SDL_WINDOWEVENT_FOCUS_GAINED: - keyboard_focus_ = true; - break; - case SDL_WINDOWEVENT_FOCUS_LOST: - keyboard_focus_ = false; - break; - } - } - - void Window::OnKeyEvent(SDL_KeyboardEvent& e) - { - const ImGuiIO& io = ImGui::GetIO(); - if (io.WantCaptureKeyboard) { - keyboard_.deltas.fill(ButtonDelta::kSame); - } - else { - bool keyWasDown = keyboard_.keys[e.keysym.scancode]; - bool keyIsDown = (e.state == SDL_PRESSED); - keyboard_.keys[e.keysym.scancode] = keyIsDown; - if (keyIsDown != keyWasDown) { // (if key was pressed or released) - keyboard_.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::kPressed : ButtonDelta::kReleased; - } - } - } - - void Window::OnMouseButtonEvent(SDL_MouseButtonEvent& e) - { - const ImGuiIO& io = ImGui::GetIO(); - if (io.WantCaptureMouse) { - mouse_.deltas.fill(ButtonDelta::kSame); - } - else { - enum inputs::MouseButton button = inputs::MouseButton::M_INVALID; - switch (e.button) { - case SDL_BUTTON_LEFT: - button = inputs::MouseButton::M_LEFT; - break; - case SDL_BUTTON_MIDDLE: - button = inputs::MouseButton::M_MIDDLE; - break; - case SDL_BUTTON_RIGHT: - button = inputs::MouseButton::M_RIGHT; - break; - case SDL_BUTTON_X1: - button = inputs::MouseButton::M_X1; - break; - case SDL_BUTTON_X2: - button = inputs::MouseButton::M_X2; - break; - } - int buttonIndex = static_cast(button); - bool buttonWasDown = mouse_.buttons.at(buttonIndex); - bool buttonIsDown = (e.state == SDL_PRESSED); - mouse_.buttons.at(buttonIndex) = buttonIsDown; - if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) - // only sets delta if it hasn't already been set this frame (to detect very fast presses) - if (mouse_.deltas[buttonIndex] == ButtonDelta::kSame) { - mouse_.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::kPressed : ButtonDelta::kReleased; - } - } - } - } - - void Window::OnMouseMotionEvent(SDL_MouseMotionEvent& e) - { - const ImGuiIO& io = ImGui::GetIO(); - if (io.WantCaptureMouse) { - mouse_.dx = 0; - mouse_.dy = 0; - } - else { - mouse_.x = e.x; - mouse_.y = e.y; - mouse_.dx = e.xrel; - mouse_.dy = e.yrel; - } - } - - void Window::OnMouseWheelEvent(SDL_MouseWheelEvent& e) - { - const ImGuiIO& io = ImGui::GetIO(); - if (!io.WantCaptureMouse) { - if (e.direction == SDL_MOUSEWHEEL_NORMAL) { - mouse_.xscroll = e.preciseX; - mouse_.yscroll = e.preciseY; - } - else { // flipped - mouse_.xscroll = -e.preciseX; - mouse_.yscroll = -e.preciseY; - } - } - } - - // public methods - - SDL_Window* Window::GetHandle() const - { - return handle_; - } - - std::string Window::GetTitle() const - { - return title_; - } - - void Window::GetInputAndEvents() - { - - frames_++; - uint64_t currentFrameStamp = GetNanos(); - last_frame_time_ = currentFrameStamp - last_frame_stamp_; - last_frame_stamp_ = currentFrameStamp; - - ResetInputDeltas(); - - // loop through all available events - SDL_Event e; - while (SDL_PollEvent(&e)) { - ImGui_ImplSDL2_ProcessEvent(&e); - switch (e.type) { - - case SDL_QUIT: - SetCloseFlag(); - break; - - case SDL_WINDOWEVENT: - OnWindowEvent(e.window); - break; - - case SDL_KEYDOWN: // FALL THROUGH - case SDL_KEYUP: - OnKeyEvent(e.key); - break; - - case SDL_MOUSEBUTTONDOWN: // FALL THROUGH - case SDL_MOUSEBUTTONUP: - OnMouseButtonEvent(e.button); - break; - - case SDL_MOUSEMOTION: - OnMouseMotionEvent(e.motion); - break; - - case SDL_MOUSEWHEEL: - OnMouseWheelEvent(e.wheel); - break; - - } - } - - } - - void Window::SetTitle(std::string title) - { - SDL_SetWindowTitle(handle_, title.c_str()); - } - - bool Window::GetWindowResized() const - { - return just_resized_; - } - - void Window::SetResizedFlag() - { - just_resized_ = true; - } - - void Window::Show() - { - SDL_ShowWindow(handle_); - } - - void Window::Hide() - { - SDL_HideWindow(handle_); - } - - void Window::Focus() - { - SDL_RaiseWindow(handle_); - keyboard_focus_ = true; - } - - bool Window::HasFocus() const - { - return keyboard_focus_; - } - - void Window::SetCloseFlag() - { - should_close_ = true; - } - - bool Window::IsRunning() const - { - return !should_close_; - } - - void Window::SetFullscreen(bool fullscreen, bool exclusive) - { - - if (resizable_) { - - SDL_DisplayMode mode; - SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(handle_), &mode); - SDL_SetWindowDisplayMode(handle_, &mode); - - if (SDL_SetWindowFullscreen(handle_, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) { - throw std::runtime_error("Unable to set window to fullscreen/windowed"); - } - fullscreen_ = fullscreen; - if (fullscreen) { - - int width, height; - SDL_GetWindowSize(handle_, &width, &height); - OnResize(width, height); - } - } - } - - void Window::ToggleFullscreen() - { - SetFullscreen(!fullscreen_, true); - } - - bool Window::IsFullscreen() const - { - return fullscreen_; - } - - bool Window::SetRelativeMouseMode(bool enabled) - { - mouse_.captured = enabled; - int code = SDL_SetRelativeMouseMode(static_cast(enabled)); - if (code != 0) { - throw std::runtime_error("Unable to set relative mouse mode"); - } - else { - return true; - } - } - - bool Window::MouseCaptured() - { - return mouse_.captured; - } - - // getting input - - bool Window::GetKey(inputs::Key key) const - { - return keyboard_.keys[static_cast(key)]; - } - - bool Window::GetKeyPress(inputs::Key key) const - { - return keyboard_.deltas[static_cast(key)] == ButtonDelta::kPressed; - } - - bool Window::GetKeyRelease(inputs::Key key) const - { - return keyboard_.deltas[static_cast(key)] == ButtonDelta::kReleased; - } - - // TODO mouse input - - bool Window::GetButton(inputs::MouseButton button) const - { - return mouse_.buttons[static_cast(button)]; - } - - bool Window::GetButtonPress(inputs::MouseButton button) const - { - return mouse_.deltas[static_cast(button)] == ButtonDelta::kPressed; - } - - bool Window::GetButtonRelease(inputs::MouseButton button) const - { - return mouse_.deltas[static_cast(button)] == ButtonDelta::kReleased; - } - - int Window::GetMouseX() const - { - return static_cast(mouse_.x); - } - - int Window::GetMouseY() const - { - return static_cast(mouse_.y); - } - - float Window::GetMouseNormX() const - { - return ((float)mouse_.x * 2.0f / (float)win_size_.x) - 1.0f; - } - - float Window::GetMouseNormY() const - { - return ((float)mouse_.y * -2.0f / (float)win_size_.y) + 1.0f; - } - - int Window::GetMouseDX() const - { - return static_cast(mouse_.dx); - } - - int Window::GetMouseDY() const - { - return static_cast(mouse_.dy); - } - - float Window::GetMouseScrollX() const - { - return mouse_.xscroll; - } - - float Window::GetMouseScrollY() const - { - return mouse_.yscroll; - } - - // TODO game pad - - // get timer value - uint64_t Window::GetNanos() const - { - uint64_t count; - - count = SDL_GetPerformanceCounter(); - if (counter_freq_ == BILLION) { - return count; - } - else { - return count * (BILLION / counter_freq_); - } - } - - uint64_t Window::GetLastFrameStamp() const - { - return last_frame_stamp_; - } - - uint64_t Window::GetFrameCount() const - { - return frames_; - } - - uint64_t Window::GetStartTime() const - { - return start_time_; - } - - float Window::dt() const - { - return (float)last_frame_time_ / (float)BILLION; - } - - uint64_t Window::GetFPS() const - { - if (last_frame_time_ == 0) return 0; - return BILLION / last_frame_time_; - } - - uint64_t Window::GetAvgFPS() const - { - uint64_t delta_t = GetNanos() - avg_fps_start_; - if (delta_t == 0) return 0; - return BILLION * (frames_ - avg_fps_start_count_) / delta_t; - } - - void Window::ResetAvgFPS() - { - avg_fps_start_ = GetNanos(); - avg_fps_start_count_ = GetFrameCount(); - } - - bool Window::InfoBox(const std::string& title, const std::string& msg) - { - if (IsFullscreen() == false) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title.c_str(), msg.c_str(), handle_); - return true; - } - else { - return false; - } - } - - /* STATIC METHODS */ - - // Display an error message box - void Window::ErrorBox(const std::string& message) - { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Game Error", message.c_str(), NULL); - } - +Window::Window(const std::string& title, bool resizable, bool fullscreen) : title_(title), resizable_(resizable), fullscreen_(fullscreen) +{ + + // init SDL + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + const std::string errMsg("Unable to initialise SDL: " + std::string(SDL_GetError())); + throw std::runtime_error(errMsg); + } + + counter_freq_ = SDL_GetPerformanceFrequency(); + start_time_ = GetNanos(); + last_frame_stamp_ = start_time_ - 1; + avg_fps_start_ = start_time_; + + Uint32 windowFlags = SDL_WINDOW_SHOWN; + + // use Vulkan 1.3 + windowFlags |= SDL_WINDOW_VULKAN; + + if (resizable_) { + windowFlags |= SDL_WINDOW_RESIZABLE; + } + + if (fullscreen_) { + windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + + // create the window + handle_ = SDL_CreateWindow(title_.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, static_cast(win_size_.x), static_cast(win_size_.y), + windowFlags); + if (handle_ == NULL) { + SDL_Quit(); + throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); + } + + const int WINDOWED_MIN_WIDTH = 640; + const int WINDOWED_MIN_HEIGHT = 480; + SDL_SetWindowMinimumSize(handle_, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT); + + // get window size + int winWidth, winHeight; + SDL_GetWindowSize(handle_, &winWidth, &winHeight); + + OnResize(winWidth, winHeight); } + +Window::~Window() +{ + SDL_DestroyWindow(handle_); + SDL_Quit(); +} + +// private methods + +void Window::OnResize(Sint32 width, Sint32 height) +{ + // get window size + win_size_.x = static_cast(width); + win_size_.y = static_cast(height); + + just_resized_ = true; +} + +void Window::ResetInputDeltas() +{ + just_resized_ = false; + + keyboard_.deltas.fill(ButtonDelta::kSame); + + mouse_.deltas.fill(ButtonDelta::kSame); + mouse_.dx = 0; + mouse_.dy = 0; + mouse_.xscroll = 0.0f; + mouse_.yscroll = 0.0f; +} + +// TODO event methods (like callbacks) + +void Window::OnWindowEvent(SDL_WindowEvent& e) +{ + switch (e.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + LOG_TRACE("SDL size changed event!"); + OnResize(e.data1, e.data2); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + keyboard_focus_ = true; + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + keyboard_focus_ = false; + break; + } +} + +void Window::OnKeyEvent(SDL_KeyboardEvent& e) +{ + const ImGuiIO& io = ImGui::GetIO(); + if (io.WantCaptureKeyboard) { + keyboard_.deltas.fill(ButtonDelta::kSame); + } + else { + bool keyWasDown = keyboard_.keys[e.keysym.scancode]; + bool keyIsDown = (e.state == SDL_PRESSED); + keyboard_.keys[e.keysym.scancode] = keyIsDown; + if (keyIsDown != keyWasDown) { // (if key was pressed or released) + keyboard_.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::kPressed : ButtonDelta::kReleased; + } + } +} + +void Window::OnMouseButtonEvent(SDL_MouseButtonEvent& e) +{ + const ImGuiIO& io = ImGui::GetIO(); + if (io.WantCaptureMouse) { + mouse_.deltas.fill(ButtonDelta::kSame); + } + else { + enum inputs::MouseButton button = inputs::MouseButton::M_INVALID; + switch (e.button) { + case SDL_BUTTON_LEFT: + button = inputs::MouseButton::M_LEFT; + break; + case SDL_BUTTON_MIDDLE: + button = inputs::MouseButton::M_MIDDLE; + break; + case SDL_BUTTON_RIGHT: + button = inputs::MouseButton::M_RIGHT; + break; + case SDL_BUTTON_X1: + button = inputs::MouseButton::M_X1; + break; + case SDL_BUTTON_X2: + button = inputs::MouseButton::M_X2; + break; + } + int buttonIndex = static_cast(button); + bool buttonWasDown = mouse_.buttons.at(buttonIndex); + bool buttonIsDown = (e.state == SDL_PRESSED); + mouse_.buttons.at(buttonIndex) = buttonIsDown; + if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) + // only sets delta if it hasn't already been set this frame (to detect very fast presses) + if (mouse_.deltas[buttonIndex] == ButtonDelta::kSame) { + mouse_.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::kPressed : ButtonDelta::kReleased; + } + } + } +} + +void Window::OnMouseMotionEvent(SDL_MouseMotionEvent& e) +{ + const ImGuiIO& io = ImGui::GetIO(); + if (io.WantCaptureMouse) { + mouse_.dx = 0; + mouse_.dy = 0; + } + else { + mouse_.x = e.x; + mouse_.y = e.y; + mouse_.dx = e.xrel; + mouse_.dy = e.yrel; + } +} + +void Window::OnMouseWheelEvent(SDL_MouseWheelEvent& e) +{ + const ImGuiIO& io = ImGui::GetIO(); + if (!io.WantCaptureMouse) { + if (e.direction == SDL_MOUSEWHEEL_NORMAL) { + mouse_.xscroll = e.preciseX; + mouse_.yscroll = e.preciseY; + } + else { // flipped + mouse_.xscroll = -e.preciseX; + mouse_.yscroll = -e.preciseY; + } + } +} + +// public methods + +SDL_Window* Window::GetHandle() const { return handle_; } + +std::string Window::GetTitle() const { return title_; } + +void Window::GetInputAndEvents() +{ + + frames_++; + uint64_t currentFrameStamp = GetNanos(); + last_frame_time_ = currentFrameStamp - last_frame_stamp_; + last_frame_stamp_ = currentFrameStamp; + + ResetInputDeltas(); + + // loop through all available events + SDL_Event e; + while (SDL_PollEvent(&e)) { + ImGui_ImplSDL2_ProcessEvent(&e); + switch (e.type) { + + case SDL_QUIT: + SetCloseFlag(); + break; + + case SDL_WINDOWEVENT: + OnWindowEvent(e.window); + break; + + case SDL_KEYDOWN: // FALL THROUGH + case SDL_KEYUP: + OnKeyEvent(e.key); + break; + + case SDL_MOUSEBUTTONDOWN: // FALL THROUGH + case SDL_MOUSEBUTTONUP: + OnMouseButtonEvent(e.button); + break; + + case SDL_MOUSEMOTION: + OnMouseMotionEvent(e.motion); + break; + + case SDL_MOUSEWHEEL: + OnMouseWheelEvent(e.wheel); + break; + } + } +} + +void Window::SetTitle(std::string title) { SDL_SetWindowTitle(handle_, title.c_str()); } + +bool Window::GetWindowResized() const { return just_resized_; } + +void Window::SetResizedFlag() { just_resized_ = true; } + +void Window::Show() { SDL_ShowWindow(handle_); } + +void Window::Hide() { SDL_HideWindow(handle_); } + +void Window::Focus() +{ + SDL_RaiseWindow(handle_); + keyboard_focus_ = true; +} + +bool Window::HasFocus() const { return keyboard_focus_; } + +void Window::SetCloseFlag() { should_close_ = true; } + +bool Window::IsRunning() const { return !should_close_; } + +void Window::SetFullscreen(bool fullscreen, bool exclusive) +{ + + if (resizable_) { + + SDL_DisplayMode mode; + SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(handle_), &mode); + SDL_SetWindowDisplayMode(handle_, &mode); + + // this will only resize the window at the end of the frame + if (SDL_SetWindowFullscreen(handle_, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) { + throw std::runtime_error("Unable to set window to fullscreen/windowed"); + } + fullscreen_ = fullscreen; + } +} + +void Window::ToggleFullscreen() { SetFullscreen(!fullscreen_, true); } + +bool Window::IsFullscreen() const { return fullscreen_; } + +bool Window::SetRelativeMouseMode(bool enabled) +{ + mouse_.captured = enabled; + int code = SDL_SetRelativeMouseMode(static_cast(enabled)); + if (code != 0) { + throw std::runtime_error("Unable to set relative mouse mode"); + } + else { + return true; + } +} + +bool Window::MouseCaptured() { return mouse_.captured; } + +// getting input + +bool Window::GetKey(inputs::Key key) const { return keyboard_.keys[static_cast(key)]; } + +bool Window::GetKeyPress(inputs::Key key) const { return keyboard_.deltas[static_cast(key)] == ButtonDelta::kPressed; } + +bool Window::GetKeyRelease(inputs::Key key) const { return keyboard_.deltas[static_cast(key)] == ButtonDelta::kReleased; } + +// TODO mouse input + +bool Window::GetButton(inputs::MouseButton button) const { return mouse_.buttons[static_cast(button)]; } + +bool Window::GetButtonPress(inputs::MouseButton button) const { return mouse_.deltas[static_cast(button)] == ButtonDelta::kPressed; } + +bool Window::GetButtonRelease(inputs::MouseButton button) const { return mouse_.deltas[static_cast(button)] == ButtonDelta::kReleased; } + +int Window::GetMouseX() const { return static_cast(mouse_.x); } + +int Window::GetMouseY() const { return static_cast(mouse_.y); } + +float Window::GetMouseNormX() const { return ((float)mouse_.x * 2.0f / (float)win_size_.x) - 1.0f; } + +float Window::GetMouseNormY() const { return ((float)mouse_.y * -2.0f / (float)win_size_.y) + 1.0f; } + +int Window::GetMouseDX() const { return static_cast(mouse_.dx); } + +int Window::GetMouseDY() const { return static_cast(mouse_.dy); } + +float Window::GetMouseScrollX() const { return mouse_.xscroll; } + +float Window::GetMouseScrollY() const { return mouse_.yscroll; } + +// TODO game pad + +// get timer value +uint64_t Window::GetNanos() const +{ + uint64_t count; + + count = SDL_GetPerformanceCounter(); + if (counter_freq_ == BILLION) { + return count; + } + else { + return count * (BILLION / counter_freq_); + } +} + +uint64_t Window::GetLastFrameStamp() const { return last_frame_stamp_; } + +uint64_t Window::GetFrameCount() const { return frames_; } + +uint64_t Window::GetStartTime() const { return start_time_; } + +float Window::dt() const { return (float)last_frame_time_ / (float)BILLION; } + +uint64_t Window::GetFPS() const +{ + if (last_frame_time_ == 0) return 0; + return BILLION / last_frame_time_; +} + +uint64_t Window::GetAvgFPS() const +{ + uint64_t delta_t = GetNanos() - avg_fps_start_; + if (delta_t == 0) return 0; + return BILLION * (frames_ - avg_fps_start_count_) / delta_t; +} + +void Window::ResetAvgFPS() +{ + avg_fps_start_ = GetNanos(); + avg_fps_start_count_ = GetFrameCount(); +} + +bool Window::InfoBox(const std::string& title, const std::string& msg) +{ + if (IsFullscreen() == false) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title.c_str(), msg.c_str(), handle_); + return true; + } + else { + return false; + } +} + +/* STATIC METHODS */ + +// Display an error message box +void Window::ErrorBox(const std::string& message) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Game Error", message.c_str(), NULL); } + +} // namespace engine diff --git a/test/src/camera_controller.cpp b/test/src/camera_controller.cpp index 753c90d..ca70612 100644 --- a/test/src/camera_controller.cpp +++ b/test/src/camera_controller.cpp @@ -64,7 +64,7 @@ void CameraControllerSystem::OnUpdate(float ts) // jumping if (scene_->app()->input_manager()->GetButtonPress("jump") && (c->grounded || c->noclip)) { - c->vel.z += CameraControllerComponent::kJumpHeight; // m/s + c->vel.z += CameraControllerComponent::kJumpVelocity; // m/s } // update position with velocity: diff --git a/test/src/camera_controller.hpp b/test/src/camera_controller.hpp index ce78fe3..5b82096 100644 --- a/test/src/camera_controller.hpp +++ b/test/src/camera_controller.hpp @@ -16,7 +16,7 @@ struct CameraControllerComponent { static constexpr float kSpeedForwardBack = 4.0f; static constexpr float kSpeedStrafe = 4.0f; static constexpr float kSprintMultiplier = 2.0f; - static constexpr float kJumpHeight = /*4.4f*/ 8.8f; + static constexpr float kJumpVelocity = 4.4f; // collision static constexpr float kPlayerHeight = 2.0f; // 71.0f * 25.4f / 1000.0f; @@ -25,6 +25,7 @@ struct CameraControllerComponent { static constexpr size_t kNumHorizontalRays = 20; static constexpr float kGravAccel = -9.81f; + // static constexpr float kGravAccel = -1.625f; // moon gravity static constexpr float kMaxDistanceFromOrigin = 200.0f; bool noclip = false; @@ -48,4 +49,4 @@ class CameraControllerSystem : public engine::System { CameraControllerComponent* c = nullptr; engine::Scene* next_scene_ = nullptr; -}; \ No newline at end of file +};