diff --git a/.gitignore b/.gitignore index d2bf98a016f588760241f9dc7f90f6197c458404..7ee4ff1903e902c4715c6e2b0c3e784ed5755aaf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -<<<<<<< HEAD -======= - ->>>>>>> develop # IDE specific files .project .cproject @@ -19,3 +15,6 @@ cmake-build-release/ *.exe *.ilk *.pdb + +# GUI configuration files +imgui.ini diff --git a/.gitmodules b/.gitmodules index 62938a4b1ff2c6787b619cc2c18ef91cb0f0f679..e0aaf2d17c340f98ae875f7e0f1238bfe04f7e5d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "modules/shader_compiler/lib/glslang"] path = modules/shader_compiler/lib/glslang url = https://github.com/KhronosGroup/glslang.git +[submodule "modules/gui/lib/imgui"] + path = modules/gui/lib/imgui + url = https://github.com/ocornut/imgui.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a7f978f81313ecc3a657dd670d2a16db3cd4e8d..bff486150f082c2e96e543436d977cf3112403ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,15 +41,15 @@ if (vkcv_build_debug) endif() endif() +# configure everything to use the required dependencies +include(${vkcv_config}/Libraries.cmake) + # add modules as targets add_subdirectory(modules) # add source files for compilation include(${vkcv_config}/Sources.cmake) -# configure everything to use the required dependencies -include(${vkcv_config}/Libraries.cmake) - message("-- Libraries: [ ${vkcv_libraries} ]") message("-- Flags: [ ${vkcv_flags} ]") diff --git a/config/Sources.cmake b/config/Sources.cmake index 62cec249367995db0217c71455cfcee982c65af3..4397e4978eb022d267571d185a1f122d053a5ea1 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -32,8 +32,8 @@ set(vkcv_sources ${vkcv_include}/vkcv/Logger.hpp - ${vkcv_include}/vkcv/SwapChain.hpp - ${vkcv_source}/vkcv/SwapChain.cpp + ${vkcv_include}/vkcv/Swapchain.hpp + ${vkcv_source}/vkcv/Swapchain.cpp ${vkcv_include}/vkcv/ShaderStage.hpp diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 4a51b24f5c978daebc5116e20b527252c8063d61..bf9514d5ba4c9d5dbf8d41be2a489dae826886a8 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -8,7 +8,7 @@ #include <vulkan/vulkan.hpp> #include "vkcv/Context.hpp" -#include "vkcv/SwapChain.hpp" +#include "vkcv/Swapchain.hpp" #include "vkcv/Window.hpp" #include "vkcv/PassConfig.hpp" #include "vkcv/Handles.hpp" @@ -41,6 +41,7 @@ namespace vkcv QueueType queueType; std::vector<vk::Semaphore> waitSemaphores; std::vector<vk::Semaphore> signalSemaphores; + vk::Fence fence; }; class Core final @@ -52,7 +53,7 @@ namespace vkcv * * @param context encapsulates various Vulkan objects */ - Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> imageViews, + Core(Context &&context, Window &window, const Swapchain& swapChain, std::vector<vk::ImageView> imageViews, const CommandResources& commandResources, const SyncResources& syncResources) noexcept; // explicit destruction of default constructor Core() = delete; @@ -61,11 +62,11 @@ namespace vkcv Context m_Context; - SwapChain m_swapchain; + Swapchain m_swapchain; std::vector<vk::ImageView> m_swapchainImageViews; std::vector<vk::Image> m_swapchainImages; std::vector<vk::ImageLayout> m_swapchainImageLayouts; - const Window& m_window; + Window& m_window; std::unique_ptr<PassManager> m_PassManager; std::unique_ptr<PipelineManager> m_PipelineManager; @@ -78,10 +79,10 @@ namespace vkcv CommandResources m_CommandResources; SyncResources m_SyncResources; uint32_t m_currentSwapchainImageIndex; + + event_handle<int,int> e_resizeHandle; - std::function<void(int, int)> e_resizeHandle; - - static std::vector<vk::ImageView> createImageViews( Context &context, SwapChain& swapChain); + static std::vector<vk::ImageView> createImageViews( Context &context, Swapchain& swapChain); void recordSwapchainImageLayoutTransition(vk::CommandBuffer cmdBuffer, vk::ImageLayout newLayout); @@ -123,6 +124,9 @@ namespace vkcv [[nodiscard]] const Context &getContext() const; + + [[nodiscard]] + const Swapchain& getSwapchain() const; /** * Creates a #Core with given @p applicationName and @p applicationVersion for your application. @@ -253,8 +257,6 @@ namespace vkcv */ void endFrame(); - vk::Format getSwapchainImageFormat(); - /** * Submit a command buffer to any queue of selected type. The recording can be customized by a * custom record-command-function. If the command submission has finished, an optional finish-function @@ -279,5 +281,8 @@ namespace vkcv void submitCommandStream(const CommandStreamHandle handle); void prepareSwapchainImageForPresent(const CommandStreamHandle handle); void prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image); + + const vk::ImageView& getSwapchainImageView() const; + }; } diff --git a/include/vkcv/Event.hpp b/include/vkcv/Event.hpp index 0836e836e84ff7dfc4931a7cedd65497bf9a89cf..e324917674cd2e1773ee23a9411ab28f6eb0d684 100644 --- a/include/vkcv/Event.hpp +++ b/include/vkcv/Event.hpp @@ -3,10 +3,18 @@ #include <functional> namespace vkcv { + + template<typename... T> + struct event_handle { + uint32_t id; + }; template<typename... T> struct event_function { typedef std::function<void(T...)> type; + + event_handle<T...> handle; + type callback; }; /** @@ -16,7 +24,8 @@ namespace vkcv { template<typename... T> struct event { private: - std::vector<typename event_function<T...>::type> m_handles; + std::vector< event_function<T...> > m_functions; + uint32_t m_id_counter; public: @@ -25,28 +34,34 @@ namespace vkcv { * @param arguments of the given function */ void operator()(T... arguments) { - for (auto &handle : this->m_handles) { - handle(arguments...); + for (auto &function : this->m_functions) { + function.callback(arguments...); } } /** * adds a function handle to the event to be called - * @param handle of the function + * @param callback of the function + * @return handle of the function */ - typename event_function<T...>::type add(typename event_function<T...>::type handle) { - this->m_handles.push_back(handle); - return handle; + event_handle<T...> add(typename event_function<T...>::type callback) { + event_function<T...> function; + function.handle = { m_id_counter++ }; + function.callback = callback; + this->m_functions.push_back(function); + return function.handle; } /** * removes a function handle of the event * @param handle of the function */ - void remove(typename event_function<T...>::type handle) { - this->m_handles.erase( - remove(this->m_handles.begin(), this->m_handles.end(), handle), - this->m_handles.end() + void remove(event_handle<T...> handle) { + this->m_functions.erase( + std::remove_if(this->m_functions.begin(), this->m_functions.end(), [&handle](auto function){ + return (handle.id == function.handle.id); + }), + this->m_functions.end() ); } diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp index a1219ce4147ebbb8ae0650da8a87766f8967874b..840650a1d6288922eceff7ba10ee7e71bf88dc22 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -29,9 +29,6 @@ namespace vkcv { [[nodiscard]] uint32_t getDepth() const; - - [[nodiscard]] - vk::ImageLayout getLayout() const; [[nodiscard]] vkcv::ImageHandle getHandle() const; diff --git a/include/vkcv/SwapChain.hpp b/include/vkcv/Swapchain.hpp similarity index 83% rename from include/vkcv/SwapChain.hpp rename to include/vkcv/Swapchain.hpp index 089205d1633551b4ad9f11d0bdd5540b2bb61bbb..b75fc5a87156ea56061e41b4b0974928c83ffa28 100644 --- a/include/vkcv/SwapChain.hpp +++ b/include/vkcv/Swapchain.hpp @@ -7,8 +7,9 @@ namespace vkcv { - class SwapChain final { + class Swapchain final { private: + friend class Core; struct Surface { @@ -21,10 +22,10 @@ namespace vkcv Surface m_Surface; vk::SwapchainKHR m_Swapchain; - vk::Format m_SwapchainFormat; - vk::ColorSpaceKHR m_SwapchainColorSpace; - vk::PresentModeKHR m_SwapchainPresentMode; - uint32_t m_SwapchainImageCount; + vk::Format m_Format; + vk::ColorSpaceKHR m_ColorSpace; + vk::PresentModeKHR m_PresentMode; + uint32_t m_ImageCount; vk::Extent2D m_Extent; @@ -39,16 +40,36 @@ namespace vkcv * @param format */ // TODO: - SwapChain(const Surface &surface, + Swapchain(const Surface &surface, vk::SwapchainKHR swapchain, vk::Format format, vk::ColorSpaceKHR colorSpace, vk::PresentModeKHR presentMode, uint32_t imageCount, vk::Extent2D extent) noexcept; + + /** + * TODO + * + * @return + */ + bool shouldUpdateSwapchain() const; + + /** + * TODO + * + * context + * window + */ + void updateSwapchain(const Context &context, const Window &window); + + /** + * + */ + void signalSwapchainRecreation(); public: - SwapChain(const SwapChain& other); + Swapchain(const Swapchain& other); /** * @return The swapchain linked with the #SwapChain class @@ -69,7 +90,7 @@ namespace vkcv * @return gets the chosen swapchain format */ [[nodiscard]] - vk::Format getSwapchainFormat() const; + vk::Format getFormat() const; /** * creates a swap chain object out of the given window and the given context @@ -77,37 +98,17 @@ namespace vkcv * @param context of the application * @return returns an object of swapChain */ - static SwapChain create(const Window &window, const Context &context); + static Swapchain create(const Window &window, const Context &context); /** * Destructor of SwapChain */ - virtual ~SwapChain(); + virtual ~Swapchain(); /** * @return number of images in swapchain */ - uint32_t getImageCount(); - - /** - * TODO - * - * @return - */ - bool shouldUpdateSwapchain() const; - - /** - * TODO - * - * context - * window - */ - void updateSwapchain(const Context &context, const Window &window); - - /** - * - */ - void signalSwapchainRecreation(); + uint32_t getImageCount() const; /** * TODO diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp index f71671c935a0a5e17bb517c726d75ffff2973532..51d6e2245a8b588334b38254c05276ee0eb10150 100644 --- a/include/vkcv/Window.hpp +++ b/include/vkcv/Window.hpp @@ -13,16 +13,19 @@ struct GLFWwindow; namespace vkcv { - class Window final { - private: - GLFWwindow *m_window; - - /** + class Window { + protected: + GLFWwindow *m_window; + + /** * * @param GLFWwindow of the class */ - explicit Window(GLFWwindow *window); - + explicit Window(GLFWwindow *window); + + static GLFWwindow* createGLFWWindow(const char *windowTitle, int width, int height, bool resizable); + + private: /** * mouse callback for moving the mouse on the screen * @param[in] window The window that received the event. @@ -58,6 +61,13 @@ namespace vkcv { * @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down. */ static void onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods); + + /** + * char callback for any typed character + * @param[in] window The window that received the event + * @param[in] c The character that got typed + */ + static void onCharEvent(GLFWwindow *callbackWindow, unsigned int c); public: /** @@ -95,6 +105,7 @@ namespace vkcv { event< double, double > e_mouseScroll; event< int, int > e_resize; event< int, int, int, int > e_key; + event< unsigned int > e_char; /** * returns the current window diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index e8efea4981da3ffb338d508431ed4f92805ed5cd..5edb802b3adf16878c2dec4050d8444278739026 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -2,5 +2,6 @@ # Add new modules here: add_subdirectory(asset_loader) add_subdirectory(camera) +add_subdirectory(gui) add_subdirectory(shader_compiler) add_subdirectory(testing) diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp index 0c5041795ece9cd84e740a04c0d64e4964d0f4c8..5755d6cdc20f0321197b7755e459725eb363fc90 100644 --- a/modules/camera/include/vkcv/camera/CameraManager.hpp +++ b/modules/camera/include/vkcv/camera/CameraManager.hpp @@ -25,11 +25,11 @@ namespace vkcv::camera { */ class CameraManager{ private: - std::function<void(int, int, int, int)> m_keyHandle; - std::function<void(double, double)> m_mouseMoveHandle; - std::function<void(double, double)> m_mouseScrollHandle; - std::function<void(int, int, int)> m_mouseButtonHandle; - std::function<void(int, int)> m_resizeHandle; + event_handle<int, int, int, int> m_keyHandle; + event_handle<double, double> m_mouseMoveHandle; + event_handle<double, double> m_mouseScrollHandle; + event_handle<int, int, int> m_mouseButtonHandle; + event_handle<int, int> m_resizeHandle; Window& m_window; std::vector<Camera> m_cameras; diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp index 561596c25a66334b56b3253b998c8c63428ef121..84a0d7ca3049846c4fbb234bab02b5f4d3c7ffd5 100644 --- a/modules/camera/src/vkcv/camera/CameraManager.cpp +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -14,7 +14,13 @@ namespace vkcv::camera { m_lastY = static_cast<float>(window.getHeight()) / 2.0f; } - CameraManager::~CameraManager() {} + CameraManager::~CameraManager() { + m_window.e_key.remove(m_keyHandle); + m_window.e_mouseMove.remove(m_mouseMoveHandle); + m_window.e_mouseScroll.remove(m_mouseScrollHandle); + m_window.e_mouseButton.remove(m_mouseButtonHandle); + m_window.e_resize.remove(m_resizeHandle); + } void CameraManager::bindCameraToEvents() { m_keyHandle = m_window.e_key.add( [&](int key, int scancode, int action, int mods) { this->keyCallback(key, scancode, action, mods); }); diff --git a/modules/gui/CMakeLists.txt b/modules/gui/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce03f16e1f8d421f5b8e6c2fe913c0da04d34598 --- /dev/null +++ b/modules/gui/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_gui) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_gui_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_gui_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_gui_sources + ${vkcv_gui_include}/vkcv/gui/GUI.hpp + ${vkcv_gui_source}/vkcv/gui/GUI.cpp + ) + +# Setup some path variables to load libraries +set(vkcv_gui_lib lib) +set(vkcv_gui_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_gui_lib}) + +# Check and load IMGUI +include(config/ImGui.cmake) + +# adding source files to the module +add_library(vkcv_gui STATIC ${vkcv_gui_sources} ${vkcv_imgui_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_gui ${vkcv_gui_libraries} vkcv ${vkcv_libraries}) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_gui SYSTEM BEFORE PRIVATE ${vkcv_gui_includes} ${vkcv_include} ${vkcv_includes}) + +# add the own include directory for public headers +target_include_directories(vkcv_gui BEFORE PUBLIC ${vkcv_gui_include} ${vkcv_imgui_includes}) diff --git a/modules/gui/config/ImGui.cmake b/modules/gui/config/ImGui.cmake new file mode 100644 index 0000000000000000000000000000000000000000..3f55ad05c34783ba0e82c41d2cbc4e5b204d60e7 --- /dev/null +++ b/modules/gui/config/ImGui.cmake @@ -0,0 +1,17 @@ + +if (EXISTS "${vkcv_gui_lib_path}/imgui") + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_glfw.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_vulkan.cpp ) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_draw.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_demo.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_tables.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_widgets.cpp) + + list(APPEND vkcv_imgui_includes ${vkcv_gui_lib}/imgui) + list(APPEND vkcv_imgui_includes ${vkcv_gui_lib}/imgui/backend) + + list(APPEND vkcv_gui_include ${vkcv_gui_lib}) +else() + message(WARNING "IMGUI is required..! Update the submodules!") +endif () diff --git a/modules/gui/include/vkcv/gui/GUI.hpp b/modules/gui/include/vkcv/gui/GUI.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d1a9986c5f69bfd9d4392bd5ae50f0b1f8b60642 --- /dev/null +++ b/modules/gui/include/vkcv/gui/GUI.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include "imgui/imgui.h" +#include "imgui/backends/imgui_impl_glfw.h" +#include "imgui/backends/imgui_impl_vulkan.h" + +#include <vkcv/Core.hpp> +#include <vkcv/Window.hpp> + +namespace vkcv::gui { + + class GUI final { + private: + Window& m_window; + Core& m_core; + + const Context& m_context; + + ImGuiContext* m_gui_context; + + vk::DescriptorPool m_descriptor_pool; + vk::RenderPass m_render_pass; + + event_handle<int,int,int> f_mouseButton; + event_handle<double,double> f_mouseScroll; + event_handle<int,int,int,int> f_key; + event_handle<unsigned int> f_char; + + public: + /** + * Constructor of a new instance of ImGui management + * + * @param core Valid #Core instance of the framework + * @param window Valid #Window instance of the framework + */ + GUI(Core& core, Window& window); + + GUI(const GUI& other) = delete; + GUI(GUI&& other) = delete; + + GUI& operator=(const GUI& other) = delete; + GUI& operator=(GUI&& other) = delete; + + /** + * Destructor of a #GUI instance + */ + virtual ~GUI(); + + /** + * Sets up a new frame for ImGui to draw + */ + void beginGUI(); + + /** + * Ends a frame for ImGui, renders it and draws it onto + * the currently active swapchain image of the core (ready to present). + */ + void endGUI(); + + }; + +} diff --git a/modules/gui/lib/imgui b/modules/gui/lib/imgui new file mode 160000 index 0000000000000000000000000000000000000000..d5828cd988db525f27128edeadb1a689cd2d7461 --- /dev/null +++ b/modules/gui/lib/imgui @@ -0,0 +1 @@ +Subproject commit d5828cd988db525f27128edeadb1a689cd2d7461 diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp new file mode 100644 index 0000000000000000000000000000000000000000..096a857a13f01840d8a3a7e2bf74ba571bd2c249 --- /dev/null +++ b/modules/gui/src/vkcv/gui/GUI.cpp @@ -0,0 +1,239 @@ + +#include "vkcv/gui/GUI.hpp" + +#include <GLFW/glfw3.h> +#include <vkcv/Logger.hpp> + +namespace vkcv::gui { + + static void checkVulkanResult(VkResult resultCode) { + if (resultCode == 0) + return; + + const auto result = vk::Result(resultCode); + + vkcv_log(LogLevel::ERROR, "ImGui has a problem with Vulkan! (%s)", vk::to_string(result).c_str()); + } + + GUI::GUI(Core& core, Window& window) : + m_window(window), + m_core(core), + m_context(m_core.getContext()), + m_gui_context(nullptr) { + IMGUI_CHECKVERSION(); + + m_gui_context = ImGui::CreateContext(); + + ImGui_ImplGlfw_InitForVulkan(m_window.getWindow(), false); + + f_mouseButton = m_window.e_mouseButton.add([&](int button, int action, int mods) { + ImGui_ImplGlfw_MouseButtonCallback(m_window.getWindow(), button, action, mods); + }); + + f_mouseScroll = m_window.e_mouseScroll.add([&](double xoffset, double yoffset) { + ImGui_ImplGlfw_ScrollCallback(m_window.getWindow(), xoffset, yoffset); + }); + + f_key = m_window.e_key.add([&](int key, int scancode, int action, int mods) { + ImGui_ImplGlfw_KeyCallback(m_window.getWindow(), key, scancode, action, mods); + }); + + f_char = m_window.e_char.add([&](unsigned int c) { + ImGui_ImplGlfw_CharCallback(m_window.getWindow(), c); + }); + + vk::DescriptorPoolSize pool_sizes[] = { + vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eCombinedImageSampler, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageImage, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformTexelBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageTexelBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eInputAttachment, 1000) + }; + + const vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo ( + vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, + static_cast<uint32_t>(1000 * IM_ARRAYSIZE(pool_sizes)), + static_cast<uint32_t>(IM_ARRAYSIZE(pool_sizes)), + pool_sizes + ); + + m_descriptor_pool = m_context.getDevice().createDescriptorPool(descriptorPoolCreateInfo); + + const vk::PhysicalDevice& physicalDevice = m_context.getPhysicalDevice(); + const Swapchain& swapchain = m_core.getSwapchain(); + + const uint32_t graphicsQueueFamilyIndex = ( + m_context.getQueueManager().getGraphicsQueues()[0].familyIndex + ); + + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = m_context.getInstance(); + init_info.PhysicalDevice = m_context.getPhysicalDevice(); + init_info.Device = m_context.getDevice(); + init_info.QueueFamily = graphicsQueueFamilyIndex; + init_info.Queue = m_context.getQueueManager().getGraphicsQueues()[0].handle; + init_info.PipelineCache = nullptr; + init_info.DescriptorPool = m_descriptor_pool; + init_info.Allocator = nullptr; + init_info.MinImageCount = swapchain.getImageCount(); + init_info.ImageCount = swapchain.getImageCount(); + init_info.CheckVkResultFn = checkVulkanResult; + + const vk::AttachmentDescription attachment ( + vk::AttachmentDescriptionFlags(), + swapchain.getFormat(), + vk::SampleCountFlagBits::e1, + vk::AttachmentLoadOp::eLoad, + vk::AttachmentStoreOp::eStore, + vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, + vk::ImageLayout::eUndefined, + vk::ImageLayout::ePresentSrcKHR + ); + + const vk::AttachmentReference attachmentReference ( + 0, + vk::ImageLayout::eColorAttachmentOptimal + ); + + const vk::SubpassDescription subpass ( + vk::SubpassDescriptionFlags(), + vk::PipelineBindPoint::eGraphics, + 0, + nullptr, + 1, + &attachmentReference, + nullptr, + nullptr, + 0, + nullptr + ); + + const vk::SubpassDependency dependency ( + VK_SUBPASS_EXTERNAL, + 0, + vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::AccessFlags(), + vk::AccessFlagBits::eColorAttachmentWrite, + vk::DependencyFlags() + ); + + const vk::RenderPassCreateInfo passCreateInfo ( + vk::RenderPassCreateFlags(), + 1, + &attachment, + 1, + &subpass, + 1, + &dependency + ); + + m_render_pass = m_context.getDevice().createRenderPass(passCreateInfo); + + ImGui_ImplVulkan_Init(&init_info, m_render_pass); + + const SubmitInfo submitInfo { QueueType::Graphics, {}, {} }; + + m_core.recordAndSubmitCommands(submitInfo, [](const vk::CommandBuffer& commandBuffer) { + ImGui_ImplVulkan_CreateFontsTexture(commandBuffer); + }, []() { + ImGui_ImplVulkan_DestroyFontUploadObjects(); + }); + + m_context.getDevice().waitIdle(); + } + + GUI::~GUI() { + m_context.getDevice().waitIdle(); + + ImGui_ImplVulkan_Shutdown(); + + m_context.getDevice().destroyRenderPass(m_render_pass); + m_context.getDevice().destroyDescriptorPool(m_descriptor_pool); + + ImGui_ImplGlfw_Shutdown(); + + m_window.e_mouseButton.remove(f_mouseButton); + m_window.e_mouseScroll.remove(f_mouseScroll); + m_window.e_key.remove(f_key); + m_window.e_char.remove(f_char); + + if (m_gui_context) { + ImGui::DestroyContext(m_gui_context); + } + } + + void GUI::beginGUI() { + const Swapchain& swapchain = m_core.getSwapchain(); + const auto extent = swapchain.getExtent(); + + if ((extent.width > 0) && (extent.height > 0)) { + ImGui_ImplVulkan_SetMinImageCount(swapchain.getImageCount()); + } + + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + } + + void GUI::endGUI() { + ImGui::Render(); + + ImDrawData* drawData = ImGui::GetDrawData(); + + if ((!drawData) || + (drawData->DisplaySize.x <= 0.0f) || + (drawData->DisplaySize.y <= 0.0f)) { + return; + } + + const Swapchain& swapchain = m_core.getSwapchain(); + const auto extent = swapchain.getExtent(); + + const vk::FramebufferCreateInfo framebufferCreateInfo ( + vk::FramebufferCreateFlags(), + m_render_pass, + 1, + &m_core.getSwapchainImageView(), + extent.width, + extent.height, + 1 + ); + + const vk::Framebuffer framebuffer = m_context.getDevice().createFramebuffer(framebufferCreateInfo); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Graphics; + + m_core.recordAndSubmitCommands(submitInfo, [&](const vk::CommandBuffer& commandBuffer) { + const vk::Rect2D renderArea ( + vk::Offset2D(0, 0), + extent + ); + + const vk::RenderPassBeginInfo beginInfo ( + m_render_pass, + framebuffer, + renderArea, + 0, + nullptr + ); + + commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eInline); + + ImGui_ImplVulkan_RenderDrawData(drawData, commandBuffer); + + commandBuffer.endRenderPass(); + }, [&]() { + m_context.getDevice().destroyFramebuffer(framebuffer); + }); + } + +} diff --git a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp index 7214e208b068ba7b9002363e594f76d66123bffd..9e07dec255d283b4b8a8d4fcc769083498c10264 100644 --- a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp +++ b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp @@ -167,12 +167,13 @@ namespace vkcv::shader { } std::streamsize fileSize = file.tellg(); - std::vector<char> buffer (fileSize); + std::vector<char> buffer (fileSize + 1); file.seekg(0); file.read(buffer.data(), fileSize); file.close(); + buffer[fileSize] = '\0'; return buffer; } diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp index c33f1adc985b40566aa25c5f51e2a1c622dfa280..7ec54582aac6b16a484b74183036539e91cfe731 100644 --- a/projects/cmd_sync_test/src/main.cpp +++ b/projects/cmd_sync_test/src/main.cpp @@ -86,7 +86,7 @@ int main(int argc, const char** argv) { const vkcv::AttachmentDescription present_color_attachment( vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, - core.getSwapchainImageFormat() + core.getSwapchain().getFormat() ); const vkcv::AttachmentDescription depth_attachment( diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index 8ffe501747fe516de9ab10834de788f110ad8722..6377a6726d3e9d1830518206133dd7780593d576 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -63,7 +63,7 @@ int main(int argc, const char** argv) { const vkcv::AttachmentDescription present_color_attachment( vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, - core.getSwapchainImageFormat() + core.getSwapchain().getFormat() ); const vkcv::AttachmentDescription depth_attachment( diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp index 5a59edf7549dfd50877e78e3ca5071bba72098b3..00a862cfd77b522e9d83b51e703ea48ce45e5d5c 100644 --- a/projects/first_scene/src/main.cpp +++ b/projects/first_scene/src/main.cpp @@ -112,7 +112,7 @@ int main(int argc, const char** argv) { const vkcv::AttachmentDescription present_color_attachment( vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, - core.getSwapchainImageFormat() + core.getSwapchain().getFormat() ); const vkcv::AttachmentDescription depth_attachment( diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt index 7e606b2348ea82486c2a57ee1062ef34150e46a0..ba8c83c06fc804082e6a0a14c3c0414899ef3057 100644 --- a/projects/first_triangle/CMakeLists.txt +++ b/projects/first_triangle/CMakeLists.txt @@ -22,7 +22,7 @@ if(MSVC) endif() # including headers of dependencies and the VkCV framework -target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) +target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(first_triangle vkcv vkcv_testing vkcv_camera vkcv_shader_compiler) +target_link_libraries(first_triangle vkcv vkcv_testing vkcv_camera vkcv_shader_compiler vkcv_gui) diff --git a/projects/first_triangle/shaders/shader.frag b/projects/first_triangle/shaders/shader.frag index d26446a73020111695aa2c86166205796dfa5e44..080678beb011afe4b03aed3bf7ae7148b77932dc 100644 --- a/projects/first_triangle/shaders/shader.frag +++ b/projects/first_triangle/shaders/shader.frag @@ -4,6 +4,6 @@ layout(location = 0) in vec3 fragColor; layout(location = 0) out vec4 outColor; -void main() { +void main() { outColor = vec4(fragColor, 1.0); } \ No newline at end of file diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 1c6f96041874a262b481727caf41e0b1142f5570..5a962b8983f6735530b38de5be679096fa997bd5 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -5,6 +5,7 @@ #include <chrono> #include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/gui/GUI.hpp> int main(int argc, const char** argv) { const char* applicationName = "First Triangle"; @@ -28,6 +29,8 @@ int main(int argc, const char** argv) { {}, { "VK_KHR_swapchain" } ); + + vkcv::gui::GUI gui (core, window); const auto& context = core.getContext(); const vk::Instance& instance = context.getInstance(); @@ -81,7 +84,7 @@ int main(int argc, const char** argv) { const vkcv::AttachmentDescription present_color_attachment( vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, - core.getSwapchainImageFormat()); + core.getSwapchain().getFormat()); vkcv::PassConfig trianglePassDefinition({ present_color_attachment }); vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); @@ -215,6 +218,14 @@ int main(int argc, const char** argv) { core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); + + gui.beginGUI(); + + ImGui::Begin("Hello world"); + ImGui::Text("This is a test!"); + ImGui::End(); + + gui.endGUI(); core.endFrame(); } diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 44e7111e1f4941ef2f0f8114ac788d7db4a13b5a..49707d4cffc18719d8fbb18a9e632e12ba679c2e 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -35,7 +35,7 @@ namespace vkcv deviceExtensions ); - SwapChain swapChain = SwapChain::create(window, context); + Swapchain swapChain = Swapchain::create(window, context); std::vector<vk::ImageView> imageViews; imageViews = createImageViews( context, swapChain); @@ -54,8 +54,12 @@ namespace vkcv { return m_Context; } + + const Swapchain& Core::getSwapchain() const { + return m_swapchain; + } - Core::Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> imageViews, + Core::Core(Context &&context, Window &window, const Swapchain& swapChain, std::vector<vk::ImageView> imageViews, const CommandResources& commandResources, const SyncResources& syncResources) noexcept : m_Context(std::move(context)), m_window(window), @@ -76,8 +80,8 @@ namespace vkcv m_CommandStreamManager->init(this); m_ImageManager->m_core = this; - - e_resizeHandle = window.e_resize.add( [&](int width, int height) { + + e_resizeHandle = m_window.e_resize.add( [&](int width, int height) { m_swapchain.signalSwapchainRecreation(); }); @@ -86,6 +90,8 @@ namespace vkcv } Core::~Core() noexcept { + m_window.e_resize.remove(e_resizeHandle); + m_Context.getDevice().waitIdle(); for (auto image : m_swapchainImageViews) { m_Context.m_Device.destroyImageView(image); @@ -232,7 +238,6 @@ namespace vkcv attachmentsViews.push_back(targetHandle); } - vk::Framebuffer framebuffer = nullptr; const vk::FramebufferCreateInfo createInfo( {}, renderpass, @@ -240,16 +245,21 @@ namespace vkcv attachmentsViews.data(), width, height, - 1); - if(m_Context.m_Device.createFramebuffer(&createInfo, nullptr, &framebuffer) != vk::Result::eSuccess) - { + 1 + ); + + vk::Framebuffer framebuffer = m_Context.m_Device.createFramebuffer(createInfo); + + if (!framebuffer) { vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer"); return; } - vk::Viewport dynamicViewport(0.0f, 0.0f, - static_cast<float>(width), static_cast<float>(height), - 0.0f, 1.0f); + vk::Viewport dynamicViewport( + 0.0f, 0.0f, + static_cast<float>(width), static_cast<float>(height), + 0.0f, 1.0f + ); vk::Rect2D dynamicScissor({0, 0}, {width, height}); @@ -349,15 +359,17 @@ namespace vkcv const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); const auto& queueManager = m_Context.getQueueManager(); - std::array<vk::Semaphore, 2> waitSemaphores{ - m_SyncResources.renderFinished, - m_SyncResources.swapchainImageAcquired }; + std::array<vk::Semaphore, 2> waitSemaphores{ + m_SyncResources.renderFinished, + m_SyncResources.swapchainImageAcquired + }; const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain(); const vk::PresentInfoKHR presentInfo( waitSemaphores, swapchain, - m_currentSwapchainImageIndex); + m_currentSwapchainImageIndex + ); vk::Result result; @@ -371,10 +383,6 @@ namespace vkcv vkcv_log(LogLevel::ERROR, "Swapchain present failed (%s)", vk::to_string(result).c_str()); } } - - vk::Format Core::getSwapchainImageFormat() { - return m_swapchain.getSwapchainFormat(); - } void Core::recordAndSubmitCommands( const SubmitInfo &submitInfo, @@ -390,11 +398,19 @@ namespace vkcv beginCommandBuffer(cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit); record(cmdBuffer); cmdBuffer.end(); - - const vk::Fence waitFence = createFence(device); + + vk::Fence waitFence; + + if (!submitInfo.fence) { + waitFence = createFence(device); + } + submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores); waitForFence(device, waitFence); - device.destroyFence(waitFence); + + if (!submitInfo.fence) { + device.destroyFence(waitFence); + } device.freeCommandBuffers(cmdPool, cmdBuffer); @@ -458,7 +474,7 @@ namespace vkcv return m_DescriptorManager->getDescriptorSet(handle); } - std::vector<vk::ImageView> Core::createImageViews( Context &context, SwapChain& swapChain){ + std::vector<vk::ImageView> Core::createImageViews( Context &context, Swapchain& swapChain){ std::vector<vk::ImageView> imageViews; std::vector<vk::Image> swapChainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain()); imageViews.reserve( swapChainImages.size() ); @@ -477,7 +493,7 @@ namespace vkcv vk::ImageViewCreateFlags(), image, vk::ImageViewType::e2D, - swapChain.getSwapchainFormat(), + swapChain.getFormat(), componentMapping, subResourceRange); @@ -507,4 +523,9 @@ namespace vkcv m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eShaderReadOnlyOptimal, cmdBuffer); }, nullptr); } + + const vk::ImageView& Core::getSwapchainImageView() const { + return m_swapchainImageViews[m_currentSwapchainImageIndex]; + } + } diff --git a/src/vkcv/SwapChain.cpp b/src/vkcv/Swapchain.cpp similarity index 88% rename from src/vkcv/SwapChain.cpp rename to src/vkcv/Swapchain.cpp index b787536b66cfd802dfd435a773a584c875eeb391..639e949bc442588ece4e13b92bd032ecbd513352 100644 --- a/src/vkcv/SwapChain.cpp +++ b/src/vkcv/Swapchain.cpp @@ -1,4 +1,4 @@ -#include <vkcv/SwapChain.hpp> +#include <vkcv/Swapchain.hpp> #include <utility> #define GLFW_INCLUDE_VULKAN @@ -27,44 +27,44 @@ namespace vkcv return vk::SurfaceKHR(surface); } - SwapChain::SwapChain(const Surface &surface, + Swapchain::Swapchain(const Surface &surface, vk::SwapchainKHR swapchain, vk::Format format, vk::ColorSpaceKHR colorSpace, vk::PresentModeKHR presentMode, uint32_t imageCount, vk::Extent2D extent) noexcept : - m_Surface(surface), - m_Swapchain(swapchain), - m_SwapchainFormat(format), - m_SwapchainColorSpace(colorSpace), - m_SwapchainPresentMode(presentMode), - m_SwapchainImageCount(imageCount), - m_Extent(extent), - m_RecreationRequired(false) + m_Surface(surface), + m_Swapchain(swapchain), + m_Format(format), + m_ColorSpace(colorSpace), + m_PresentMode(presentMode), + m_ImageCount(imageCount), + m_Extent(extent), + m_RecreationRequired(false) {} - SwapChain::SwapChain(const SwapChain &other) : + Swapchain::Swapchain(const Swapchain &other) : m_Surface(other.m_Surface), m_Swapchain(other.m_Swapchain), - m_SwapchainFormat(other.m_SwapchainFormat), - m_SwapchainColorSpace(other.m_SwapchainColorSpace), - m_SwapchainPresentMode(other.m_SwapchainPresentMode), - m_SwapchainImageCount(other.m_SwapchainImageCount), + m_Format(other.m_Format), + m_ColorSpace(other.m_ColorSpace), + m_PresentMode(other.m_PresentMode), + m_ImageCount(other.m_ImageCount), m_Extent(other.m_Extent), m_RecreationRequired(other.m_RecreationRequired.load()) {} - const vk::SwapchainKHR& SwapChain::getSwapchain() const { + const vk::SwapchainKHR& Swapchain::getSwapchain() const { return m_Swapchain; } - vk::SurfaceKHR SwapChain::getSurface() const { + vk::SurfaceKHR Swapchain::getSurface() const { return m_Surface.handle; } - vk::Format SwapChain::getSwapchainFormat() const{ - return m_SwapchainFormat; + vk::Format Swapchain::getFormat() const{ + return m_Format; } /** @@ -162,7 +162,7 @@ namespace vkcv * @param context that keeps instance, physicalDevice and a device. * @return swapchain */ - SwapChain SwapChain::create(const Window &window, const Context &context) { + Swapchain Swapchain::create(const Window &window, const Context &context) { const vk::Instance& instance = context.getInstance(); const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); const vk::Device& device = context.getDevice(); @@ -199,7 +199,7 @@ namespace vkcv vk::SwapchainKHR swapchain = device.createSwapchainKHR(swapchainCreateInfo); - return SwapChain(surface, + return Swapchain(surface, swapchain, chosenSurfaceFormat.format, chosenSurfaceFormat.colorSpace, @@ -208,11 +208,11 @@ namespace vkcv chosenExtent); } - bool SwapChain::shouldUpdateSwapchain() const { + bool Swapchain::shouldUpdateSwapchain() const { return m_RecreationRequired; } - void SwapChain::updateSwapchain(const Context &context, const Window &window) { + void Swapchain::updateSwapchain(const Context &context, const Window &window) { if (!m_RecreationRequired.exchange(false)) return; @@ -222,9 +222,9 @@ namespace vkcv vk::SwapchainCreateInfoKHR swapchainCreateInfo( vk::SwapchainCreateFlagsKHR(), m_Surface.handle, - m_SwapchainImageCount, - m_SwapchainFormat, - m_SwapchainColorSpace, + m_ImageCount, + m_Format, + m_ColorSpace, extent2D, 1, vk::ImageUsageFlagBits::eColorAttachment, @@ -233,7 +233,7 @@ namespace vkcv nullptr, vk::SurfaceTransformFlagBitsKHR::eIdentity, vk::CompositeAlphaFlagBitsKHR::eOpaque, - m_SwapchainPresentMode, + m_PresentMode, true, oldSwapchain ); @@ -244,19 +244,19 @@ namespace vkcv m_Extent = extent2D; } - void SwapChain::signalSwapchainRecreation() { + void Swapchain::signalSwapchainRecreation() { m_RecreationRequired = true; } - const vk::Extent2D& SwapChain::getExtent() const { + const vk::Extent2D& Swapchain::getExtent() const { return m_Extent; } - SwapChain::~SwapChain() { + Swapchain::~Swapchain() { // needs to be destroyed by creator } - uint32_t SwapChain::getImageCount() { - return m_SwapchainImageCount; + uint32_t Swapchain::getImageCount() const { + return m_ImageCount; } } diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp index c21271b78f7501721d5c0496d0344dd68e2e7e52..2436619300c24f035cba727481dfce8e1b397c9b 100644 --- a/src/vkcv/Window.cpp +++ b/src/vkcv/Window.cpp @@ -24,22 +24,25 @@ namespace vkcv { glfwTerminate(); } } + + GLFWwindow* Window::createGLFWWindow(const char *windowTitle, int width, int height, bool resizable) { + if(s_WindowCount == 0) { + glfwInit(); + } + + s_WindowCount++; + + width = std::max(width, 1); + height = std::max(height, 1); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); + + return glfwCreateWindow(width, height, windowTitle, nullptr, nullptr); + } Window Window::create( const char *windowTitle, int width, int height, bool resizable) { - if(s_WindowCount == 0) { - glfwInit(); - } - s_WindowCount++; - - width = std::max(width, 1); - height = std::max(height, 1); - - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); - GLFWwindow *window; - window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr); - - return Window(window); + return Window(createGLFWWindow(windowTitle, width, height, resizable)); } void Window::initEvents() { @@ -55,6 +58,8 @@ namespace vkcv { glfwSetKeyCallback(m_window, Window::onKeyEvent); glfwSetScrollCallback(m_window, Window::onMouseScrollEvent); + + glfwSetCharCallback(m_window, Window::onCharEvent); } void Window::pollEvents() { @@ -62,7 +67,6 @@ namespace vkcv { } void Window::onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { @@ -71,7 +75,6 @@ namespace vkcv { } void Window::onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { @@ -88,7 +91,6 @@ namespace vkcv { } void Window::onResize(GLFWwindow *callbackWindow, int width, int height) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { @@ -97,13 +99,20 @@ namespace vkcv { } void Window::onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { window->e_key(key, scancode, action, mods); } } + + void Window::onCharEvent(GLFWwindow *callbackWindow, unsigned int c) { + auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_char(c); + } + } bool Window::isWindowOpen() const { return !glfwWindowShouldClose(m_window);