diff --git a/openVulkanoCpp.sln.DotSettings b/openVulkanoCpp.sln.DotSettings index 9ad4899..81d6aa3 100644 --- a/openVulkanoCpp.sln.DotSettings +++ b/openVulkanoCpp.sln.DotSettings @@ -4,11 +4,15 @@ <NamingElement Priority="9"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> <NamingElement Priority="11"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="PUBLIC"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement> <NamingElement Priority="13"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="scoped enumerator" /><type Name="unscoped enumerator" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></NamingElement> + <NamingElement Priority="9"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> + <NamingElement Priority="7"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement> <NamingElement Priority="16"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement> <NamingElement Priority="5"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement> True True + True True True + True True True \ No newline at end of file diff --git a/openVulkanoCpp/Base/Event.hpp b/openVulkanoCpp/Base/Event.hpp index 722e16d..59b586d 100644 --- a/openVulkanoCpp/Base/Event.hpp +++ b/openVulkanoCpp/Base/Event.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/openVulkanoCpp/Base/IPlatform.hpp b/openVulkanoCpp/Base/IPlatform.hpp new file mode 100644 index 0000000..94c6380 --- /dev/null +++ b/openVulkanoCpp/Base/IPlatform.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "IInitable.hpp" +#include "ITickable.hpp" +#include "ICloseable.hpp" +#include "UI/IWindow.hpp" + +namespace openVulkanoCpp +{ + class PlatformInitFailedException : public std::runtime_error + { + public: + PlatformInitFailedException(char const* const message) : runtime_error(message) {} + }; + + class IPlatform : public IInitable, public ITickable, public ICloseable + { + public: + virtual IWindow* MakeWindow() = 0; + }; +} diff --git a/openVulkanoCpp/Base/Logger.cpp b/openVulkanoCpp/Base/Logger.cpp index c362e0b..7cdd5b7 100644 --- a/openVulkanoCpp/Base/Logger.cpp +++ b/openVulkanoCpp/Base/Logger.cpp @@ -10,4 +10,5 @@ namespace openVulkanoCpp std::shared_ptr Logger::AUDIO = nullptr; std::shared_ptr Logger::DATA = nullptr; std::shared_ptr Logger::SCENE = nullptr; + std::shared_ptr Logger::INPUT = nullptr; } \ No newline at end of file diff --git a/openVulkanoCpp/Base/Logger.hpp b/openVulkanoCpp/Base/Logger.hpp index 26dbe31..e1d547b 100644 --- a/openVulkanoCpp/Base/Logger.hpp +++ b/openVulkanoCpp/Base/Logger.hpp @@ -28,6 +28,7 @@ namespace openVulkanoCpp static std::shared_ptr AUDIO; static std::shared_ptr DATA; static std::shared_ptr SCENE; + static std::shared_ptr INPUT; static void SetupLogger(std::string logFolder = "logs", std::string logFile = "openVulkano.log") { @@ -59,6 +60,7 @@ namespace openVulkanoCpp AUDIO = CreateLogger("audio"); DATA = CreateLogger("data"); SCENE = CreateLogger("scene"); + INPUT = CreateLogger("input"); spdlog::flush_every(std::chrono::seconds(5)); diff --git a/openVulkanoCpp/Base/PlatformEnums.hpp b/openVulkanoCpp/Base/PlatformEnums.hpp index a0588af..e74b3b7 100644 --- a/openVulkanoCpp/Base/PlatformEnums.hpp +++ b/openVulkanoCpp/Base/PlatformEnums.hpp @@ -10,6 +10,7 @@ namespace openVulkanoCpp //OpenGL, //DirectX11, //DirectX12, + //Metal, MAX_VALUE }; @@ -27,7 +28,7 @@ namespace openVulkanoCpp { enum Platform { - Windows = 0, MacOS, Linux, Android, MAX_VALUE + Windows = 0, MacOS, Linux, Android, iOS, MAX_VALUE }; inline std::string ToString(Platform os) @@ -35,9 +36,10 @@ namespace openVulkanoCpp switch (os) { case Windows: return "Windows"; - case MacOS: return "Windows"; - case Linux: return "Windows"; - case Android: return "Windows"; + case MacOS: return "MacOS"; + case Linux: return "Linux"; + case Android: return "Android"; + case iOS: return "iOS"; } return "Invalid"; } diff --git a/openVulkanoCpp/Base/UI/IWindow.hpp b/openVulkanoCpp/Base/UI/IWindow.hpp index 22a7181..c21e5df 100644 --- a/openVulkanoCpp/Base/UI/IWindow.hpp +++ b/openVulkanoCpp/Base/UI/IWindow.hpp @@ -4,7 +4,6 @@ #include #include #include "../PlatformEnums.hpp" -#include "../ITickable.hpp" #include "../ICloseable.hpp" namespace openVulkanoCpp @@ -29,7 +28,7 @@ namespace openVulkanoCpp WindowMode windowMode = WINDOWED; }; - class IWindow : public ITickable, public ICloseable + class IWindow : public ICloseable { public: virtual ~IWindow() = default; diff --git a/openVulkanoCpp/Host/GLFW/InputDeviceGLFW.hpp b/openVulkanoCpp/Host/GLFW/InputDeviceGLFW.hpp new file mode 100644 index 0000000..43be00e --- /dev/null +++ b/openVulkanoCpp/Host/GLFW/InputDeviceGLFW.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include "../../Base/ITickable.hpp" +#include "../../Input/InputDeviceKeyboard.hpp" +#include "../../Input/InputDeviceMouse.hpp" +#include "../../Input/InputDeviceController.hpp" +#include + +namespace openVulkanoCpp +{ + class KeyboardGLFW final : public Input::InputDeviceKeyboard + { + public: + KeyboardGLFW() = default; + + using Input::InputDeviceKeyboard::Init; + using Input::InputDeviceKeyboard::PreTick; + using Input::InputDeviceKeyboard::UpdateKey; + using Input::InputDeviceKeyboard::UpdateActiveWindow; + }; + + class MouseGLFW final : public Input::InputDeviceMouse + { + public: + MouseGLFW() = default; + + using Input::InputDeviceMouse::Init; + using Input::InputDeviceMouse::ClearAxes; + using Input::InputDeviceMouse::UpdateButtons; + using Input::InputDeviceMouse::UpdatePosition; + using Input::InputDeviceMouse::UpdateWheel; + using Input::InputDeviceMouse::UpdateActiveWindow; + }; + + class ControllerGLFW final : public Input::InputDeviceController, public ITickable + { + bool gamepad = false;; + public: + ControllerGLFW() = default; + + void Init(int joystickId) + { + if (GetIndex() != -1) return; + gamepad = glfwJoystickIsGamepad(joystickId); + std::string name = glfwGetJoystickName(joystickId); + if (gamepad) name += " - " + std::string(glfwGetGamepadName(joystickId)); + InputDeviceController::Init(joystickId, name); + if (!gamepad) Logger::INPUT->warn("Joysticks currently are not supported"); + } + + void Tick() override + { + if (GetIndex() == -1) return; + if (gamepad) + { + GLFWgamepadstate state; + glfwGetGamepadState(GetIndex(), &state); + for (int i = 0; i < 6; i++) + { + SetAxis(static_cast(i), state.axes[i]); + } + + uint32_t buttonStates = 0; + for (int i = 0; i < 15; i++) + { + buttonStates |= static_cast(state.buttons[i] != 0) << i; + } + SetButtons(buttonStates); + } + else + { + // Joysticks currently are not supported + } + } + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/GLFW/InputMappingGLFW.hpp b/openVulkanoCpp/Host/GLFW/InputMappingGLFW.hpp new file mode 100644 index 0000000..e62b4fa --- /dev/null +++ b/openVulkanoCpp/Host/GLFW/InputMappingGLFW.hpp @@ -0,0 +1,93 @@ +#pragma once +#include "../../Input/InputKey.hpp" +#include + +namespace openVulkanoCpp +{ + namespace GLFW + { + struct KeyboardInputMapping + { + int mapping[GLFW_KEY_LAST + 1]; + + constexpr KeyboardInputMapping() : mapping() + { + for (int i = 0; i <= GLFW_KEY_LAST; i++) + { + int remappedKey = -1; + + if (i == GLFW_KEY_SPACE) + { + remappedKey = i; + } + else if (i >= GLFW_KEY_COMMA && i <= GLFW_KEY_RIGHT_BRACKET) + { + if (i != 58 && i != 60 && i != 62 && i != 63 && i != 64) + { + remappedKey = i; + } + } + else if (i == GLFW_KEY_GRAVE_ACCENT) + { + remappedKey = 64; + } + else if (i == GLFW_KEY_WORLD_1) + { + remappedKey = 58; + } + else if (i == GLFW_KEY_WORLD_2) + { + remappedKey = 60; + } + else if (i >= GLFW_KEY_KP_0 && i <= GLFW_KEY_KP_EQUAL) + { + remappedKey = i - 320; + } + else if (i >= GLFW_KEY_NUM_LOCK) + { + remappedKey = 17; + } + else if (i == GLFW_KEY_SPACE) + { + remappedKey = 62; + } + else if (i == GLFW_KEY_APOSTROPHE) + { + remappedKey = 63; + } + else if (i >= GLFW_KEY_ESCAPE && i <= GLFW_KEY_END) + { + remappedKey = i - 238; + } + else if (i >= GLFW_KEY_CAPS_LOCK && i <= GLFW_KEY_SCROLL_LOCK) + { + remappedKey = i - 248; + } + else if (i >= GLFW_KEY_PRINT_SCREEN && i <= GLFW_KEY_PAUSE) + { + remappedKey = i - 249; + } + else if (i >= GLFW_KEY_LEFT_SHIFT && i <= GLFW_KEY_RIGHT_SUPER) + { + remappedKey = i - 304; + } + else if (i == GLFW_KEY_MENU) + { + remappedKey = 99; + } + else if (i >= GLFW_KEY_F1 && i <= GLFW_KEY_F25) + { + remappedKey = i - 189; + } + mapping[i] = remappedKey; + } + } + + constexpr int Map(int glfwKey) const + { + if (glfwKey < 0) return -1; + return mapping[glfwKey]; + } + }; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/GLFW/InputProviderGLFW.cpp b/openVulkanoCpp/Host/GLFW/InputProviderGLFW.cpp new file mode 100644 index 0000000..dc4695e --- /dev/null +++ b/openVulkanoCpp/Host/GLFW/InputProviderGLFW.cpp @@ -0,0 +1,61 @@ +#include "InputProviderGLFW.hpp" +#include "InputMappingGLFW.hpp" +#include "../../Input/InputManager.hpp" + +namespace openVulkanoCpp +{ + namespace GLFW + { + constexpr KeyboardInputMapping keyboardMapping = KeyboardInputMapping(); + + InputProviderGLFW* InputProviderGLFW::INSTANCE = nullptr; + + void InputProviderGLFW::Init() + { + if (INSTANCE) throw PlatformInitFailedException("An instance of the GLFW input provider has already been initialized!"); + INSTANCE = this; + glfwSetJoystickCallback(&JoystickCallback); + + // register already connected controller + for (int i = 0; i < GLFW_JOYSTICK_LAST; i++) + { + if (glfwJoystickPresent(i) == GLFW_TRUE) + { + OnJoystickConnect(i); + } + } + mouseButtons = 0; + + keyboard.Init(0, "Keyboard"); + mouse.Init(0, "Mouse"); + Input::InputManager::GetInstace()->RegisterInputDevice(&mouse); + Input::InputManager::GetInstace()->RegisterInputDevice(&keyboard); + } + + void InputProviderGLFW::OnJoystickConnect(int joystickId) + { + controllers[joystickId].Init(joystickId); + Input::InputManager::GetInstace()->RegisterInputDevice(&controllers[joystickId]); + } + + void InputProviderGLFW::OnJoystickDisconnect(int joystickId) + { + Input::InputManager::GetInstace()->UnregisterInputDevice(&controllers[joystickId]); + + controllers[joystickId].Close(); + } + + void InputProviderGLFW::SetKeyboardKey(IWindow* window, int key, int scanCode, int action, int mods) + { + if (action == GLFW_REPEAT) + { + //TODO handle repeat for text inputs + } + else + { + keyboard.UpdateKey(static_cast(keyboardMapping.Map(key)), action == GLFW_PRESS); + } + //TODO handle keyboard notifications + } + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/GLFW/InputProviderGLFW.hpp b/openVulkanoCpp/Host/GLFW/InputProviderGLFW.hpp new file mode 100644 index 0000000..3a80443 --- /dev/null +++ b/openVulkanoCpp/Host/GLFW/InputProviderGLFW.hpp @@ -0,0 +1,92 @@ +#pragma once +#include "../../Base/IInitable.hpp" +#include "../../Base/ITickable.hpp" +#include "../../Base/ICloseable.hpp" +#include "../../Base/IPlatform.hpp" +#include "InputDeviceGLFW.hpp" +#include +#include + +namespace openVulkanoCpp +{ + namespace GLFW + { + class WindowGLFW; + + class InputProviderGLFW final : public IInitable, public ITickable, public ICloseable + { + friend WindowGLFW; + static InputProviderGLFW* INSTANCE; + + std::array controllers = {}; + MouseGLFW mouse; + KeyboardGLFW keyboard; + uint8_t mouseButtons = 0; + + public: + void Init() override; + + void Close() override + { + glfwSetJoystickCallback(NULL); + INSTANCE = nullptr; + } + + void PreTick() + { + mouse.ClearAxes(); + keyboard.PreTick(); + } + + void Tick() override + { + for(ControllerGLFW& controller : controllers) + { + controller.Tick(); + } + mouse.UpdateButtons(mouseButtons); + } + + void SetMouseButton(int button, bool state) + { + uint8_t bit = 1 << button; + if (state) + { + mouseButtons |= bit; + } + else + { + mouseButtons ^= bit; + } + } + + void SetKeyboardKey(IWindow* window, int key, int scanCode, int action, int mods); + + void MouseEnterExitWindow(IWindow* window) + { + mouse.UpdateActiveWindow(window); + keyboard.UpdateActiveWindow(window); + } + + void OnJoystickConnect(int joystickId); + + void OnJoystickDisconnect(int joystickId); + + static void JoystickCallback(int joystickId, int joystickEvent) + { + if (joystickEvent == GLFW_CONNECTED) + { + INSTANCE->OnJoystickConnect(joystickId); + } + else if (joystickEvent == GLFW_DISCONNECTED) + { + INSTANCE->OnJoystickDisconnect(joystickId); + } + else + { + Logger::INPUT->warn("Unknown GLFW joystick event {0} for joystick {1}", joystickEvent, joystickId); + } + } + }; + } +} diff --git a/openVulkanoCpp/Host/GLFW/PlatformGLFW.hpp b/openVulkanoCpp/Host/GLFW/PlatformGLFW.hpp new file mode 100644 index 0000000..4d035ac --- /dev/null +++ b/openVulkanoCpp/Host/GLFW/PlatformGLFW.hpp @@ -0,0 +1,63 @@ +#pragma once +#include "../../Base/IPlatform.hpp" +#include "WindowGLFW.hpp" +#include "InputProviderGLFW.hpp" +#include + +namespace openVulkanoCpp +{ + namespace GLFW + { + class PlatformGLFW final : public IPlatform + { + std::vector windows; + InputProviderGLFW inputProvider; + bool initialized = false; + + public: + PlatformGLFW() = default; + + ~PlatformGLFW() override + { + if (initialized) + { + Close(); + } + } + + bool IsInitialized() const { return initialized; } + + void Init() override + { + if (!glfwInit()) throw PlatformInitFailedException("Failed to initialize glfw"); + inputProvider.Init(); + initialized = true; + } + + void Tick() override + { + inputProvider.PreTick(); + glfwPollEvents(); + inputProvider.Tick(); + } + + void Close() override + { + for(const IWindow* window : windows) + { + delete window; + } + windows.clear(); + inputProvider.Close(); + glfwTerminate(); + } + + IWindow* MakeWindow() override + { + WindowGLFW* window = new WindowGLFW(inputProvider); + windows.push_back(window); + return window; + } + }; + } +} diff --git a/openVulkanoCpp/Host/GLFW/WindowGLFW.hpp b/openVulkanoCpp/Host/GLFW/WindowGLFW.hpp new file mode 100644 index 0000000..492d5c6 --- /dev/null +++ b/openVulkanoCpp/Host/GLFW/WindowGLFW.hpp @@ -0,0 +1,456 @@ +#pragma once +#include +#include "../../Base/UI/BaseWindow.hpp" +#include "../../Base/Logger.hpp" +#include "../../Base/IPlatform.hpp" +#include "InputProviderGLFW.hpp" + +namespace openVulkanoCpp +{ + namespace GLFW + { + class WindowGLFW final : public BaseWindow, virtual public IVulkanWindow, virtual public IOpenGlWindow + { + private: + InputProviderGLFW& inputProvider; + GLFWwindow* window = nullptr; + IWindowHandler* handler = nullptr; + + public: + WindowGLFW(InputProviderGLFW& inputProvider): inputProvider(inputProvider) {} + + virtual ~WindowGLFW() override + { + if (window != nullptr) Close(); + } + + protected: + GLFWmonitor* GetCurrentMonitor() const + { + int monitorCount = 0; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + for (int i = 0; i < monitorCount; i++) + { + int posX, posY, sizeX, sizeY; + glfwGetMonitorWorkarea(monitors[i], &posX, &posY, &sizeX, &sizeY); + if (windowConfig.posX >= posX && windowConfig.posX < posX + sizeX && + windowConfig.posY >= posY && windowConfig.posY < posY + sizeY) + { + return monitors[i]; + } + } + return nullptr; + } + + GLFWmonitor* GetTargetMonitor() const + { + if (windowConfig.windowMode == FULLSCREEN) + { + // TODO add config to control used display + return GetPrimaryMonitor(); + } + return nullptr; + } + + void Create() + { + glfwWindowHint(GLFW_DECORATED, (~windowConfig.windowMode) & 1); + //TODO handle full screen resolutions + window = glfwCreateWindow(windowConfig.width, windowConfig.height, windowConfig.title.c_str(), GetTargetMonitor(), nullptr); + if (!window) return; + glfwSetWindowUserPointer(window, this); + RegisterCallbacks(); + } + + void RegisterCallbacks() const + { + glfwSetErrorCallback(ErrorCallback); + + glfwSetDropCallback(window, DropCallback); + glfwSetFramebufferSizeCallback(window, ResizeCallback); + glfwSetWindowFocusCallback(window, FocusCallback); + glfwSetWindowRefreshCallback(window, RefreshCallback); + glfwSetWindowIconifyCallback(window, MinimizeCallback); + glfwSetWindowPosCallback(window, WindowMoveCallback); + glfwSetWindowCloseCallback(window, CloseCallback); + + // Input Callbacks + glfwSetKeyCallback(window, KeyboardCallback); + glfwSetMouseButtonCallback(window, MouseButtonCallback); + glfwSetCursorPosCallback(window, MouseMoveCallback); + glfwSetScrollCallback(window, MouseScrollCallback); + glfwSetCursorEnterCallback(window, MouseEnterExitCallback); + //glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); + //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } + + static GLFWmonitor* GetPrimaryMonitor() + { + return glfwGetPrimaryMonitor(); + } + + static std::vector GetMonitors() + { + int count; + GLFWmonitor** monitorsArray = glfwGetMonitors(&count); + std::vector monitors; + monitors.reserve(count); + for (int i = 0; i < count; i++) + { + monitors[i] = monitorsArray[i]; + } + return monitors; + } + + public: // IWindow implementation + void Init(RenderAPI::RenderApi renderApi) override + { + if (renderApi == RenderAPI::VULKAN) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + Create(); + if (!window) + { + throw WindowInitFailedException("Failed to initialize window"); + } + if (renderApi != RenderAPI::VULKAN) MakeCurrentThread(); + Logger::WINDOW->info("GLFW Window created (id: {0})", GetWindowId()); + } + + void Close() override + { + glfwDestroyWindow(window); + window = nullptr; + Logger::WINDOW->info("GLFW Window destroyed (id: {0})", GetWindowId()); + } + + void Present() const override + { + glfwSwapBuffers(window); + } + + void Show() override + { + glfwShowWindow(window); + } + + void Hide() override + { + glfwHideWindow(window); + } + + void SetTitle(const std::string& title) override + { + windowConfig.title = title; + glfwSetWindowTitle(window, title.c_str()); + } + + void SetSize(uint32_t width, uint32_t height) override + { + windowConfig.width = width; + windowConfig.height = height; + if (window) + { + glfwSetWindowSize(window, width, height); + } + } + + void SetPosition(int posX, int posY) override + { + windowConfig.posX = posX; + windowConfig.posY = posY; + if (window) + { + glfwSetWindowPos(window, posX, posY); + } + } + + void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) override + { + minWidth = (minWidth < 0) ? GLFW_DONT_CARE : minWidth; + minHeight = (minHeight < 0) ? GLFW_DONT_CARE : minHeight; + maxWidth = (maxWidth < 0) ? GLFW_DONT_CARE : maxWidth; + maxHeight = (maxHeight < 0) ? GLFW_DONT_CARE : maxHeight; + glfwSetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight); + } + + void MakeCurrentThread() override + { + glfwMakeContextCurrent(window); + } + + void SetWindowMode(WindowMode windowMode) override + { + if (windowMode == this->windowConfig.windowMode) return; // Nothing to change here + if (!window) return; // Window not yet created + this->windowConfig.windowMode = windowMode; + uint32_t sizeX = 0, sizeY = 0; + int posX = 0, posY = 0; + GLFWmonitor* monitor = nullptr; + glfwWindowHint(GLFW_DECORATED, (~windowMode) & 1); + if (windowMode == WINDOWED || windowMode == BORDERLESS) + { + sizeX = windowConfig.width; + sizeY = windowConfig.height; + posX = windowConfig.posX; + posY = windowConfig.posY; + monitor = nullptr; + if (windowMode == WINDOWED) + { + Logger::WINDOW->info("Switching to Windowed mode"); + } + else + { + Logger::WINDOW->info("Switching to Borderless Windowed mode"); + } + } + else + { // Fullscreen + GetPosition(&windowConfig.posX, &windowConfig.posY); // Backup current window position + monitor = GetCurrentMonitor(); + if (!monitor) monitor = GetPrimaryMonitor(); + if (!monitor) return; // We don't have a monitor to set the fullscreen window to + const GLFWvidmode* videoMode = glfwGetVideoMode(monitor ? monitor : GetPrimaryMonitor()); + sizeX = videoMode->width; + sizeY = videoMode->height; + if (windowMode == FULLSCREEN) + { + // TODO find video mode that best matches user settings + Logger::WINDOW->info("Switching to Fullscreen mode"); + } + else if (windowMode == BORDERLESS_FULLSCREEN) + { + int sX, sY; + glfwGetMonitorWorkarea(monitor, &posX, &posY, &sX, &sY); + monitor = nullptr; + Logger::WINDOW->info("Switching to Borderless Fullscreen mode"); + } + } + glfwSetWindowMonitor(this->window, monitor, posX, posY, sizeX, sizeY, GLFW_DONT_CARE); + } + + void SetWindowHandler(IWindowHandler* handler) override + { + this->handler = handler; + } + + IVulkanWindow* GetVulkanWindow() override + { + return this; + } + + IOpenGlWindow* GetOpenGlWindow() override + { + return this; + } + + // Status getter + void GetSize(int* width, int* height) override + { + glfwGetWindowSize(window, width, height); + } + + void GetPosition(int* x, int* y) override + { + glfwGetWindowPos(window, x, y); + } + + IWindowHandler* GetWindowHandler() override + { + return handler; + } + + //IVulkanWindow stuff + vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator) override + { + VkSurfaceKHR rawSurface; + const auto result = static_cast(glfwCreateWindowSurface(static_cast(instance), window, reinterpret_cast(pAllocator), &rawSurface)); + return createResultValue(result, rawSurface, "vk::CommandBuffer::begin"); + } + + std::vector GetRequiredInstanceExtensions() override + { + return GetVulkanRequiredInstanceExtensions(); + } + + public: // Window events + void OnResize(const uint32_t newWidth, const uint32_t newHeight) + { + Logger::WINDOW->debug("Window (id: {0}) resized (width: {1}, height: {2})", GetWindowId(), newWidth, newHeight); + handler->OnWindowResize(this, newWidth, newHeight); + } + + void OnMinimize() + { + Logger::WINDOW->debug("Window (id: {0}) minimized", GetWindowId()); + handler->OnWindowMinimize(this); + } + + void OnRestore() + { + Logger::WINDOW->debug("Window (id: {0}) restored", GetWindowId()); + handler->OnWindowRestore(this); + } + + void OnFocusLost() + { + Logger::WINDOW->debug("Window (id: {0}) focus lost", GetWindowId()); + handler->OnWindowFocusLost(this); + } + + void OnFocusGained() + { + Logger::WINDOW->debug("Window (id: {0}) focus gained", GetWindowId()); + handler->OnWindowFocusGained(this); + } + + void OnMove(const int posX, const int posY) + { + Logger::WINDOW->debug("Window (id: {0}) moved (x: {1}, y: {2})", GetWindowId(), posX, posY); + if (windowConfig.windowMode == WINDOWED || windowConfig.windowMode == BORDERLESS) + { // Don't save window position for fullscreen + windowConfig.posX = posX; + windowConfig.posY = posY; + } + handler->OnWindowMove(this, posX, posY); + } + + void OnClose() + { + Logger::WINDOW->debug("Window (id: {0}) closed", GetWindowId()); + handler->OnWindowClose(this); + } + + protected: + virtual void OnKeyEvent(int key, int scanCode, int action, int mods) + { + if (key == GLFW_KEY_ENTER && action == GLFW_PRESS && mods == GLFW_MOD_ALT) + { + WindowMode newMode = FULLSCREEN; + switch (windowConfig.windowMode) + { + case WINDOWED: newMode = FULLSCREEN; break; + case BORDERLESS: newMode = BORDERLESS_FULLSCREEN; break; + case FULLSCREEN: newMode = WINDOWED; break; + case BORDERLESS_FULLSCREEN: newMode = BORDERLESS; break; + } + SetWindowMode(newMode); + } + switch (action) + { + case GLFW_PRESS:// OnKeyPressed(key, mods); + Logger::INPUT->info("Key Pressed: {0} Mod: {1} ScanCode: {2}", key, mods, scanCode); + break; + case GLFW_RELEASE:// OnKeyReleased(key, mods); + Logger::INPUT->info("Key Released: {0} Mod: {1} ScanCode: {2}", key, mods, scanCode); + break; + case GLFW_REPEAT: + Logger::INPUT->info("Key Repeat: {0} Mod: {1} ScanCode: {2}", key, mods, scanCode); + break; + + default: break; + } + } + + private: // Callbacks + static WindowGLFW* GetWindow(GLFWwindow* window) + { + return static_cast(glfwGetWindowUserPointer(window)); + } + + static void KeyboardCallback(GLFWwindow* window, int key, int scanCode, int action, int mods) + { + GetWindow(window)->OnKeyEvent(key, scanCode, action, mods); + const auto windowInstance = GetWindow(window); + windowInstance->inputProvider.SetKeyboardKey(windowInstance, key, scanCode, action, mods); + } + + static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) + { + GetWindow(window)->inputProvider.SetMouseButton(button, action != GLFW_RELEASE); + } + + static void MouseMoveCallback(GLFWwindow* window, double posX, double posY) + { + GetWindow(window)->inputProvider.mouse.UpdatePosition(posX, posY); + } + + static void MouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset) + { + GetWindow(window)->inputProvider.mouse.UpdateWheel(xOffset, yOffset); + } + + static void MouseEnterExitCallback(GLFWwindow* window, int entered) + { + const auto windowInstance = GetWindow(window); + windowInstance->inputProvider.MouseEnterExitWindow(windowInstance); + Logger::INPUT->info("Mouse enter/exit: {}", entered); + } + + static void ResizeCallback(GLFWwindow* window, int width, int height) + { + GetWindow(window)->OnResize(width, height); + } + + static void FocusCallback(GLFWwindow* window, const int focused) + { + if (focused == GLFW_TRUE) + GetWindow(window)->OnFocusGained(); + else + GetWindow(window)->OnFocusLost(); + } + + static void MinimizeCallback(GLFWwindow* window, const int minimized) + { + if (minimized == GLFW_TRUE) + GetWindow(window)->OnMinimize(); + else + GetWindow(window)->OnRestore(); + } + + static void RefreshCallback(GLFWwindow* window) + { + //TODO is there really anything to do? or is it ok if the window is only redrawn on the next frame? + } + + static void WindowMoveCallback(GLFWwindow* window, const int posX, const int posY) + { + GetWindow(window)->OnMove(posX, posY); + } + + static void CloseCallback(GLFWwindow* window) + { + GetWindow(window)->OnClose(); + } + + static void DropCallback(GLFWwindow* window, const int count, const char** paths) + { + //TODO something useful + } + + static void ErrorCallback(const int error, const char* description) + { + Logger::WINDOW->error("GLFW error (e{0}): {1}", error, description); + } + + + + + public: + static std::vector GetVulkanRequiredInstanceExtensions() + { + std::vector result; + uint32_t count = 0; + const char** names = glfwGetRequiredInstanceExtensions(&count); + if (names && count) + { + for (uint32_t i = 0; i < count; ++i) + { + result.emplace_back(names[i]); + } + } + return result; + } + + + }; + } +} diff --git a/openVulkanoCpp/Host/GraphicsAppManager.hpp b/openVulkanoCpp/Host/GraphicsAppManager.hpp index ad149e0..c3ea2c1 100644 --- a/openVulkanoCpp/Host/GraphicsAppManager.hpp +++ b/openVulkanoCpp/Host/GraphicsAppManager.hpp @@ -15,11 +15,12 @@ namespace openVulkanoCpp { /** - * \brief A simple GraphicsAppManager. It can only handle on window. + * \brief A simple GraphicsAppManager. It can only handle one window. */ - class GraphicsAppManager : virtual public IGraphicsAppManager, virtual public IWindowHandler + class GraphicsAppManager final : virtual public IGraphicsAppManager, virtual public IWindowHandler { private: + IPlatform* platform; IWindow* window; IGraphicsApp* app; IRenderer* renderer; @@ -27,7 +28,7 @@ namespace openVulkanoCpp bool paused = false, running = false; float fpsTimer = 0, avgFps = 0, avgFrameTime = 0; uint64_t frameCount = 0, lastFrameCount = 0; - Timer* frameTimer; + Timer frameTimer; std::string windowTitleFormat; public: @@ -37,22 +38,21 @@ namespace openVulkanoCpp Logger::SetupLogger(); if (!app) { - const auto msg = "The app must not be null!"; + constexpr auto msg = "The app must not be null!"; Logger::MANAGER->error(msg); throw std::runtime_error(msg); } - window = PlatformProducer::CreateBestWindow(renderApi); + platform = PlatformProducer::CreatePlatform(renderApi); + window = platform->MakeWindow(); renderer = PlatformProducer::CreateRenderManager(renderApi); app->SetGraphicsAppManager(this); window->SetWindowHandler(this); - frameTimer = new Timer(); } ~GraphicsAppManager() override { delete renderer; - delete window; - delete frameTimer; + delete platform; } public: // Getter @@ -91,14 +91,14 @@ namespace openVulkanoCpp void Pause() override { paused = true; - frameTimer->Stop(); + frameTimer.Stop(); Logger::MANAGER->info("Graphics application paused"); } void Resume() override { paused = false; - frameTimer->Start(); + frameTimer.Start(); Logger::MANAGER->info("Graphics application resumed"); } @@ -107,7 +107,7 @@ namespace openVulkanoCpp { running = true; StartUp(); - frameTimer->Reset(); + frameTimer.Reset(); Loop(); // Runs the rendering loop ShutDown(); } @@ -119,9 +119,10 @@ namespace openVulkanoCpp { Logger::MANAGER->info("Initializing ..."); app->Init(); + platform->Init(); window->Init(renderApi); //TODO restore window settings if there are any set - renderer->Init((IGraphicsAppManager*)this, window); + renderer->Init(static_cast(this), window); windowTitleFormat = app->GetAppName() + " " + app->GetAppVersion() + " - " + renderer->GetMainRenderDeviceName() + " - {:.1f} fps ({:.1f} ms)"; Logger::MANAGER->info("Initialized"); } @@ -139,7 +140,7 @@ namespace openVulkanoCpp { while (running) { - window->Tick(); + platform->Tick(); if (paused) { // The rendering is paused // No need to burn cpu time if the app is paused @@ -149,7 +150,7 @@ namespace openVulkanoCpp { app->Tick(); renderer->Tick(); - frameTimer->Tick(); + frameTimer.Tick(); UpdateFps(); } } @@ -160,6 +161,7 @@ namespace openVulkanoCpp Logger::MANAGER->info("Shutting down ..."); renderer->Close(); window->Close(); + platform->Close(); app->Close(); Logger::MANAGER->info("Shutdown complete"); } @@ -167,7 +169,7 @@ namespace openVulkanoCpp void UpdateFps() { frameCount++; - fpsTimer += frameTimer->GetTickSeconds(); + fpsTimer += frameTimer.GetTickSeconds(); if(fpsTimer > 1.0f) { diff --git a/openVulkanoCpp/Host/PlatformProducer.hpp b/openVulkanoCpp/Host/PlatformProducer.hpp index 539d6e8..382d549 100644 --- a/openVulkanoCpp/Host/PlatformProducer.hpp +++ b/openVulkanoCpp/Host/PlatformProducer.hpp @@ -1,9 +1,10 @@ #pragma once #include #include "../Base/Logger.hpp" -#include "../Vulkan/Renderer.hpp" #include "../Base/PlatformEnums.hpp" -#include "WindowGLFW.hpp" +#include "../Base/IPlatform.hpp" +#include "../Vulkan/Renderer.hpp" +#include "GLFW/PlatformGLFW.hpp" namespace openVulkanoCpp { @@ -32,18 +33,18 @@ namespace openVulkanoCpp } /** - * \brief Creates a window that fits best for the current environment - * \param renderApi The render api that should be used when searching for the best suited window - * \return The created window. nullptr if no window is supported on the current platform + * \brief Creates a platform that fits best for the current environment + * \param renderApi The render api that should be used when searching for the best suited platform provider + * \return The created platform * \throws std::runtime_error if the render api is not supported */ - static IWindow* CreateBestWindow(RenderAPI::RenderApi renderApi) - { //TODO add more windows to chose from - switch(renderApi) + static IPlatform* CreatePlatform(RenderAPI::RenderApi renderApi) + { + switch (renderApi) { - case RenderAPI::VULKAN: return new WindowGLFW(); + case RenderAPI::VULKAN: return new GLFW::PlatformGLFW(); default: - Logger::RENDER->error("Unsupported render api requested! Requested %d", static_cast(renderApi)); + Logger::MANAGER->error("Unsupported render api requested! Requested %d", static_cast(renderApi)); throw std::runtime_error("Unsupported render api requested!"); } } diff --git a/openVulkanoCpp/Host/WindowGLFW.hpp b/openVulkanoCpp/Host/WindowGLFW.hpp deleted file mode 100644 index c65d616..0000000 --- a/openVulkanoCpp/Host/WindowGLFW.hpp +++ /dev/null @@ -1,456 +0,0 @@ -#pragma once -#include -#include "../Base/UI/BaseWindow.hpp" -#include "../Base/Logger.hpp" - -namespace openVulkanoCpp -{ - class WindowGLFW : public BaseWindow, virtual public IVulkanWindow, virtual public IOpenGlWindow - { - private: - GLFWwindow* window = nullptr; - IWindowHandler* handler = nullptr; - - public: - WindowGLFW() = default; - - virtual ~WindowGLFW() override - { - if (window != nullptr) Close(); - } - - protected: - GLFWmonitor* GetCurrentMonitor() const - { - int monitorCount = 0; - GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); - for(int i = 0; i < monitorCount; i++) - { - int posX, posY, sizeX, sizeY; - glfwGetMonitorWorkarea(monitors[i], &posX, &posY, &sizeX, &sizeY); - if (windowConfig.posX >= posX && windowConfig.posX < posX + sizeX && - windowConfig.posY >= posY && windowConfig.posY < posY + sizeY) - { - return monitors[i]; - } - } - return nullptr; - } - - GLFWmonitor* GetTargetMonitor() const - { - if(windowConfig.windowMode == FULLSCREEN) - { - // TODO add config to control used display - return GetPrimaryMonitor(); - } - return nullptr; - } - - void Create() - { - glfwWindowHint(GLFW_DECORATED, (~windowConfig.windowMode) & 1); - //TODO handle full screen resolutions - window = glfwCreateWindow(windowConfig.width, windowConfig.height, windowConfig.title.c_str(), GetTargetMonitor(), nullptr); - if(!window) return; - glfwSetWindowUserPointer(window, this); - RegisterCallbacks(); - } - - void RegisterCallbacks() const - { - glfwSetErrorCallback(ErrorCallback); - - glfwSetDropCallback(window, DropCallback); - glfwSetFramebufferSizeCallback(window, ResizeCallback); - glfwSetWindowFocusCallback(window, FocusCallback); - glfwSetWindowRefreshCallback(window, RefreshCallback); - glfwSetWindowIconifyCallback(window, MinimizeCallback); - glfwSetWindowPosCallback(window, WindowMoveCallback); - glfwSetWindowCloseCallback(window, CloseCallback); - - // Input Callbacks - glfwSetKeyCallback(window, KeyboardCallback); - glfwSetMouseButtonCallback(window, MouseButtonCallback); - glfwSetCursorPosCallback(window, MouseMoveCallback); - glfwSetScrollCallback(window, MouseScrollCallback); - } - - static GLFWmonitor* GetPrimaryMonitor() - { - return glfwGetPrimaryMonitor(); - } - - static std::vector GetMonitors() - { - int count; - GLFWmonitor** monitorsArray = glfwGetMonitors(&count); - std::vector monitors; - monitors.reserve(count); - for (int i = 0; i < count; i++) - { - monitors[i] = monitorsArray[i]; - } - return monitors; - } - - public: // IWindow implementation - void Init(RenderAPI::RenderApi renderApi) override - { - if (!glfwInit()) throw WindowInitFailedException("Failed to initialize glfw"); - if(renderApi == RenderAPI::VULKAN) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - Create(); - if(!window) - { - glfwTerminate(); - throw WindowInitFailedException("Failed to initialize window"); - } - if (renderApi != RenderAPI::VULKAN) MakeCurrentThread(); - Logger::WINDOW->info("GLFW Window created (id: {0})", GetWindowId()); - } - - void Close() override - { - glfwDestroyWindow(window); - window = nullptr; - glfwTerminate(); - Logger::WINDOW->info("GLFW Window destroyed (id: {0})", GetWindowId()); - } - - void Present() const override - { - glfwSwapBuffers(window); - } - - void Show() override - { - glfwShowWindow(window); - } - - void Hide() override - { - glfwHideWindow(window); - } - - void Tick() override - { - glfwPollEvents(); - } - - void SetTitle(const std::string& title) override - { - windowConfig.title = title; - glfwSetWindowTitle(window, title.c_str()); - } - - void SetSize(uint32_t width, uint32_t height) override - { - windowConfig.width = width; - windowConfig.height = height; - if (window) - { - glfwSetWindowSize(window, width, height); - } - } - - void SetPosition(int posX, int posY) override - { - windowConfig.posX = posX; - windowConfig.posY = posY; - if(window) - { - glfwSetWindowPos(window, posX, posY); - } - } - - void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) override - { - minWidth = (minWidth < 0) ? GLFW_DONT_CARE : minWidth; - minHeight = (minHeight < 0) ? GLFW_DONT_CARE : minHeight; - maxWidth = (maxWidth < 0) ? GLFW_DONT_CARE : maxWidth; - maxHeight = (maxHeight < 0) ? GLFW_DONT_CARE : maxHeight; - glfwSetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight); - } - - void MakeCurrentThread() override - { - glfwMakeContextCurrent(window); - } - - void SetWindowMode(WindowMode windowMode) override - { - if (windowMode == this->windowConfig.windowMode) return; // Nothing to change here - if (!window) return; // Window not yet created - this->windowConfig.windowMode = windowMode; - uint32_t sizeX = 0, sizeY = 0; - int posX = 0, posY = 0; - GLFWmonitor* monitor = nullptr; - glfwWindowHint(GLFW_DECORATED, (~windowMode) & 1); - if(windowMode == WINDOWED || windowMode == BORDERLESS) - { - sizeX = windowConfig.width; - sizeY = windowConfig.height; - posX = windowConfig.posX; - posY = windowConfig.posY; - monitor = nullptr; - if(windowMode == WINDOWED) - { - Logger::WINDOW->info("Switching to Windowed mode"); - } - else - { - Logger::WINDOW->info("Switching to Borderless Windowed mode"); - } - } - else - { // Fullscreen - GetPosition(&windowConfig.posX, &windowConfig.posY); // Backup current window position - monitor = GetCurrentMonitor(); - if (!monitor) monitor = GetPrimaryMonitor(); - if (!monitor) return; // We don't have a monitor to set the fullscreen window to - const GLFWvidmode* videoMode = glfwGetVideoMode(monitor ? monitor : GetPrimaryMonitor()); - sizeX = videoMode->width; - sizeY = videoMode->height; - if (windowMode == FULLSCREEN) - { - // TODO find video mode that best matches user settings - Logger::WINDOW->info("Switching to Fullscreen mode"); - } - else if (windowMode == BORDERLESS_FULLSCREEN) - { - int sX, sY; - glfwGetMonitorWorkarea(monitor, &posX, &posY, &sX, &sY); - monitor = nullptr; - Logger::WINDOW->info("Switching to Borderless Fullscreen mode"); - } - } - glfwSetWindowMonitor(this->window, monitor, posX, posY, sizeX, sizeY, GLFW_DONT_CARE); - } - - void SetWindowHandler(IWindowHandler* handler) override - { - this->handler = handler; - } - - IVulkanWindow* GetVulkanWindow() override - { - return this; - } - - IOpenGlWindow* GetOpenGlWindow() override - { - return this; - } - - // Status getter - void GetSize(int* width, int* height) override - { - glfwGetWindowSize(window, width, height); - } - - void GetPosition(int* x, int* y) override - { - glfwGetWindowPos(window, x, y); - } - - IWindowHandler* GetWindowHandler() override - { - return handler; - } - - //IVulkanWindow stuff - vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator) override - { - VkSurfaceKHR rawSurface; - const auto result = static_cast(glfwCreateWindowSurface(static_cast(instance), window, reinterpret_cast(pAllocator), &rawSurface)); - return createResultValue(result, rawSurface, "vk::CommandBuffer::begin"); - } - - std::vector GetRequiredInstanceExtensions() override - { - return GetVulkanRequiredInstanceExtensions(); - } - - public: // Window events - void OnResize(const uint32_t newWidth, const uint32_t newHeight) - { - Logger::WINDOW->debug("Window (id: {0}) resized (width: {1}, height: {2})", GetWindowId(), newWidth, newHeight); - handler->OnWindowResize(this, newWidth, newHeight); - } - - void OnMinimize() - { - Logger::WINDOW->debug("Window (id: {0}) minimized", GetWindowId()); - handler->OnWindowMinimize(this); - } - - void OnRestore() - { - Logger::WINDOW->debug("Window (id: {0}) restored", GetWindowId()); - handler->OnWindowRestore(this); - } - - void OnFocusLost() - { - Logger::WINDOW->debug("Window (id: {0}) focus lost", GetWindowId()); - handler->OnWindowFocusLost(this); - } - - void OnFocusGained() - { - Logger::WINDOW->debug("Window (id: {0}) focus gained", GetWindowId()); - handler->OnWindowFocusGained(this); - } - - void OnMove(const int posX, const int posY) - { - Logger::WINDOW->debug("Window (id: {0}) moved (x: {1}, y: {2})", GetWindowId(), posX, posY); - if (windowConfig.windowMode == WINDOWED || windowConfig.windowMode == BORDERLESS) - { // Don't save window position for fullscreen - windowConfig.posX = posX; - windowConfig.posY = posY; - } - handler->OnWindowMove(this, posX, posY); - } - - void OnClose() - { - Logger::WINDOW->debug("Window (id: {0}) closed", GetWindowId()); - handler->OnWindowClose(this); - } - - public: // Input events TODO - virtual void OnKeyPressed(int key, int mods) {} - virtual void OnKeyReleased(int key, int mods) {} - virtual void OnMousePressed(int button, int mods) {} - virtual void OnMouseReleased(int button, int mods) {} - virtual void OnMouseMoved(double posX, double posY) {} - virtual void OnMouseScrolled(double delta) {} - - protected: - virtual void OnKeyEvent(int key, int scanCode, int action, int mods) - { - if (key == GLFW_KEY_ENTER && action == GLFW_PRESS && mods == GLFW_MOD_ALT) - { - WindowMode newMode = FULLSCREEN; - switch (windowConfig.windowMode) - { - case WINDOWED: newMode = FULLSCREEN; break; - case BORDERLESS: newMode = BORDERLESS_FULLSCREEN; break; - case FULLSCREEN: newMode = WINDOWED; break; - case BORDERLESS_FULLSCREEN: newMode = BORDERLESS; break; - } - SetWindowMode(newMode); - } - switch (action) - { - case GLFW_PRESS: OnKeyPressed(key, mods); break; - case GLFW_RELEASE: OnKeyReleased(key, mods); break; - default: break; - } - } - - virtual void OnMouseButtonEvent(int button, int action, int mods) - { - switch (action) - { - case GLFW_PRESS: OnMousePressed(button, mods); break; - case GLFW_RELEASE: OnMouseReleased(button, mods); break; - default: break; - } - } - - private: // Callbacks - static WindowGLFW* GetWindow(GLFWwindow* window) - { - return static_cast(glfwGetWindowUserPointer(window)); - } - - static void KeyboardCallback(GLFWwindow* window, int key, int scanCode, int action, int mods) - { - GetWindow(window)->OnKeyEvent(key, scanCode, action, mods); - } - - static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) - { - GetWindow(window)->OnMouseButtonEvent(button, action, mods); - } - - static void MouseMoveCallback(GLFWwindow* window, double posX, double posY) - { - GetWindow(window)->OnMouseMoved(posX, posY); - } - - static void MouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset) - { - GetWindow(window)->OnMouseScrolled(yOffset); - } - - static void ResizeCallback(GLFWwindow* window, int width, int height) - { - GetWindow(window)->OnResize(width, height); - } - - static void FocusCallback(GLFWwindow* window, const int focused) - { - if (focused == GLFW_TRUE) - GetWindow(window)->OnFocusGained(); - else - GetWindow(window)->OnFocusLost(); - } - - static void MinimizeCallback(GLFWwindow* window, const int minimized) - { - if(minimized == GLFW_TRUE) - GetWindow(window)->OnMinimize(); - else - GetWindow(window)->OnRestore(); - } - - static void RefreshCallback(GLFWwindow* window) - { - //TODO is there really anything to do? or is it ok if the window is only redrawn on the next frame? - } - - static void WindowMoveCallback(GLFWwindow* window, const int posX, const int posY) - { - GetWindow(window)->OnMove(posX, posY); - } - - static void CloseCallback(GLFWwindow* window) - { - GetWindow(window)->OnClose(); - } - - static void DropCallback(GLFWwindow* window, const int count, const char** paths) - { - //TODO something useful - } - - static void ErrorCallback(const int error, const char* description) - { - Logger::WINDOW->error("GLFW error (e{0}): {1}", error, description); - } - - - - - public: - static std::vector GetVulkanRequiredInstanceExtensions() - { - std::vector result; - uint32_t count = 0; - const char** names = glfwGetRequiredInstanceExtensions(&count); - if (names && count) - { - for (uint32_t i = 0; i < count; ++i) - { - result.emplace_back(names[i]); - } - } - return result; - } - - - }; -} diff --git a/openVulkanoCpp/Input/InputDevice.hpp b/openVulkanoCpp/Input/InputDevice.hpp new file mode 100644 index 0000000..0e4d7f6 --- /dev/null +++ b/openVulkanoCpp/Input/InputDevice.hpp @@ -0,0 +1,103 @@ +#pragma once +#include "../Base/ICloseable.hpp" +#include "InputKey.hpp" + +namespace openVulkanoCpp +{ + namespace Input + { + class InputDevice : public ICloseable + { + InputDeviceType deviceType = InputDeviceType::UNKNOWN; + int index = -1; + std::string name; + float axisAsButtonThreshold = 0.5f; + float buttonAsAxisValue = 1.0f; + + protected: + InputDevice() = default; + + void Init(InputDeviceType type, int index, const std::string& name) + { + this->deviceType = type; + this->index = index; + this->name = name; + } + + virtual float ReadAxis(int16_t key) const = 0; + + virtual bool ReadButton(int16_t key) const = 0; + + virtual bool ReadButtonUp(int16_t key) const = 0; + + virtual bool ReadButtonDown(int16_t key) const = 0; + + public: + virtual ~InputDevice() = default; + + virtual void Close() override + { + this->deviceType = InputDeviceType::UNKNOWN; + this->index = -1; + this->name = ""; + } + + InputDeviceType GetType() const { return deviceType; } + int GetIndex() const { return index; } + const std::string& GetName() const { return name; } + + float GetAxisAsButtonThreshold() const { return axisAsButtonThreshold; } + void SetAxisAsButtonThreshold(float value) { axisAsButtonThreshold = value; } + + float GetButtonAsAxisValue() const { return buttonAsAxisValue; } + void SetButtonAsAxisValue(float value) { buttonAsAxisValue = value; } + + bool GetButton(InputKey key) const + { + if (key.GetInputDeviceType() != deviceType) return false; + if (key.GetInputType() == InputKey::InputType::AXIS) + { + return ReadAxis(key.GetInputKey()) > axisAsButtonThreshold; + } + return ReadButton(key.GetInputKey()); + } + + bool GetButtonUp(InputKey key) const + { + if (key.GetInputDeviceType() != deviceType) return false; + if (key.GetInputType() == InputKey::InputType::AXIS) + { + //TODO handle + return ReadAxis(key.GetInputKey()) > axisAsButtonThreshold; + } + return ReadButtonUp(key.GetInputKey()); + } + + bool GetButtonDown(InputKey key) const + { + if (key.GetInputDeviceType() != deviceType) return false; + if (key.GetInputType() == InputKey::InputType::AXIS) + { + //TODO handle + return ReadAxis(key.GetInputKey()) > axisAsButtonThreshold; + } + return ReadButtonDown(key.GetInputKey()); + } + + float GetAxis(InputKey key) const + { + if (key.GetInputDeviceType() != deviceType) return 0; + if (key.GetInputType() == InputKey::InputType::BUTTON) + { + return ReadButton(key.GetInputKey()) ? buttonAsAxisValue : 0; + } + return ReadAxis(key.GetInputKey()); + } + + float GetAxis(InputKey keyPositive, InputKey keyNegative) const + { + return GetAxis(keyPositive) - GetAxis(keyNegative); + } + }; + } +} diff --git a/openVulkanoCpp/Input/InputDeviceController.hpp b/openVulkanoCpp/Input/InputDeviceController.hpp new file mode 100644 index 0000000..332e1d8 --- /dev/null +++ b/openVulkanoCpp/Input/InputDeviceController.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include "../Base/Logger.hpp" +#include "InputDevice.hpp" +#include + +namespace openVulkanoCpp +{ + namespace Input + { + class ControllerType + { + public: + enum Type { GENERIC_JOYSTICK, XBOX, DUAL_SHOCK, DUAL_SENSE }; + + private: + Type type; + + public: + ControllerType(Type type) : type(type) {} + }; + + class InputDeviceController : public InputDevice + { + float axes[InputKey::Controller::Axis::AXIS_LAST + 1] = {0}; + uint32_t pressedButtons = 0, lastPressedButtons = 0; + const ControllerType controllerType = ControllerType::GENERIC_JOYSTICK; + + bool GetLastButton(InputKey::Controller::Button button) const + { + return lastPressedButtons & (1 << button); + } + + protected: + InputDeviceController() = default; + + void Init(const int index, const std::string& name) + { + InputDevice::Init(InputDeviceType::CONTROLLER, index, name); + + pressedButtons = 0; + lastPressedButtons = 0; + for(float& axis : axes) + { + axis = 0; + } + + // TODO find controller type from name + Logger::INPUT->info("Initialized controller: id: {0}, name: {1}", index, name); + } + + void SetAxis(InputKey::Controller::Axis axisId, float value) + { + axes[axisId] = value; + } + + void SetButtons(uint32_t buttonStates) + { + lastPressedButtons = pressedButtons; + pressedButtons = buttonStates; + } + + float ReadAxis(int16_t key) const override final + { + return GetAxis(static_cast(key)); + } + + bool ReadButton(int16_t key) const override final + { + return GetButton(static_cast(key)); + } + + bool ReadButtonUp(int16_t key) const override final + { + return GetButtonUp(static_cast(key)); + } + + bool ReadButtonDown(int16_t key) const override final + { + return GetButtonDown(static_cast(key)); + } + + public: + bool GetButton(const InputKey::Controller::Button button) const + { + return pressedButtons & (1 << button); + } + + bool GetButtonUp(const InputKey::Controller::Button button) const + { + return !GetButton(button) && GetLastButton(button); + } + + bool GetButtonDown(const InputKey::Controller::Button button) const + { + return GetButton(button) && !GetLastButton(button); + } + + float GetAxis(const InputKey::Controller::Axis axis) const + { + return axes[axis]; + } + + ControllerType GetControllerType() const + { + return controllerType; + } + }; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Input/InputDeviceKeyboard.hpp b/openVulkanoCpp/Input/InputDeviceKeyboard.hpp new file mode 100644 index 0000000..34d3652 --- /dev/null +++ b/openVulkanoCpp/Input/InputDeviceKeyboard.hpp @@ -0,0 +1,87 @@ +#pragma once +#include "InputDevice.hpp" +#include + +namespace openVulkanoCpp +{ + class IWindow; + + namespace Input + { + class InputDeviceKeyboard : public InputDevice + { + std::bitset keyState; + std::bitset lastKeyState; + IWindow* lastWindow; + + protected: + InputDeviceKeyboard() = default; + + void Init(int index, const std::string& name) + { + InputDevice::Init(InputDeviceType::KEYBOARD, index, name); + } + + void PreTick() + { + lastKeyState = keyState; + } + + void UpdateKey(InputKey::Keyboard::Key key, bool state) + { + keyState[key] = state; + } + + void UpdateActiveWindow(IWindow* window) + { + lastWindow = window; + } + + float ReadAxis(int16_t key) const override final + { + return 0; + } + + bool ReadButton(int16_t key) const override final + { + return GetButton(static_cast(key)); + } + + bool ReadButtonUp(int16_t key) const override final + { + return GetButtonUp(static_cast(key)); + } + + bool ReadButtonDown(int16_t key) const override final + { + return GetButtonDown(static_cast(key)); + } + + public: + bool GetButton(const InputKey::Keyboard::Key button) const + { + return keyState[button] > 0; + } + + bool GetButtonUp(const InputKey::Keyboard::Key button) const + { + return !keyState[button] && lastKeyState[button]; + } + + bool GetButtonDown(const InputKey::Keyboard::Key button) const + { + return keyState[button] && !lastKeyState[button]; + } + + IWindow* GetActiveWindow() const + { + return lastWindow; + } + + bool IsInWindow(const IWindow* window) const + { + return window == lastWindow; + } + }; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Input/InputDeviceMouse.hpp b/openVulkanoCpp/Input/InputDeviceMouse.hpp new file mode 100644 index 0000000..64b464d --- /dev/null +++ b/openVulkanoCpp/Input/InputDeviceMouse.hpp @@ -0,0 +1,180 @@ +#pragma once +#include "InputDevice.hpp" +#include "../Base/Logger.hpp" + +namespace openVulkanoCpp +{ + class IWindow; + + namespace Input + { + class InputDeviceMouse : public InputDevice + { + float axes[InputKey::Mouse::Axis::AXIS_LAST + 2] = { 0 }; + uint8_t pressedButtons = 0, lastPressedButtons = 0; + double mousePosX = 0, mousePosY = 0; + IWindow* lastWindow = nullptr; + + protected: + InputDeviceMouse() = default; + + void Init(const int index, const std::string& name) + { + InputDevice::Init(InputDeviceType::MOUSE, index, name); + + ClearAxes(); + pressedButtons = 0; + lastPressedButtons = 0; + mousePosX = 0; + mousePosY = 0; + lastWindow = nullptr; + } + + void UpdateButtons(uint8_t newPressedButtons) + { + lastPressedButtons = pressedButtons; + pressedButtons = newPressedButtons; + if (lastPressedButtons != pressedButtons) + Logger::INPUT->info("Mouse button state changed {0:08b}", pressedButtons); + } + + void UpdatePosition(double posX, double posY) + { + axes[InputKey::Mouse::AXIS_X] = static_cast(posX - mousePosX); + axes[InputKey::Mouse::AXIS_Y] = static_cast(posY - mousePosY); + mousePosX = posX; + mousePosY = posY; + Logger::INPUT->info("Mouse moved posX: {0} posY: {1}, relativeX: {2}, relativeY: {3}", posX, posY, axes[InputKey::Mouse::AXIS_X], axes[InputKey::Mouse::AXIS_Y]); + } + + void UpdateWheel(float wheelX, float wheelY) + { + axes[InputKey::Mouse::AXIS_WHEEL_X] = wheelX; + axes[InputKey::Mouse::AXIS_WHEEL_Y] = wheelY; + Logger::INPUT->info("Mouse scrolled x: {0} y: {1}", wheelX, wheelY); + } + + void UpdateActiveWindow(IWindow* window) + { + axes[InputKey::Mouse::AXIS_X] = 0; + axes[InputKey::Mouse::AXIS_Y] = 0; + lastWindow = window; + } + + void ClearAxes() + { + for (float& axis : axes) + { + axis = 0; + } + } + + private: + bool GetLastButton(InputKey::Mouse::Button button) const + { + return lastPressedButtons & (1 << button); + } + + protected: + float ReadAxis(int16_t key) const override final + { + return GetAxis(static_cast(key)); + } + + bool ReadButton(int16_t key) const override final + { + return GetButton(static_cast(key)); + } + + bool ReadButtonUp(int16_t key) const override final + { + return GetButtonUp(static_cast(key)); + } + + bool ReadButtonDown(int16_t key) const override final + { + return GetButtonDown(static_cast(key)); + } + + public: + bool GetButton(const InputKey::Mouse::Button button) const + { + return pressedButtons & (1 << button); + } + + bool GetButtonUp(const InputKey::Mouse::Button button) const + { + return !GetButton(button) && GetLastButton(button); + } + + bool GetButtonDown(const InputKey::Mouse::Button button) const + { + return GetButton(button) && !GetLastButton(button); + } + + float GetAxis(const InputKey::Mouse::Axis axis) const + { + return axes[axis]; + } + + float GetWheel() const + { + return GetAxis(InputKey::Mouse::Axis::AXIS_WHEEL_Y); + } + + float GetWheelX() const + { + return GetAxis(InputKey::Mouse::Axis::AXIS_WHEEL_X); + } + + float GetWheelY() const + { + return GetAxis(InputKey::Mouse::Axis::AXIS_WHEEL_Y); + } + + glm::vec2 GetMousePosition() const + { + return { mousePosX, mousePosY }; + } + + void GetMousePosition(double& posX, double& posY) const + { + posX = mousePosX; + posY = mousePosY; + } + + glm::vec2 GetMousePosition(const IWindow* window) const + { + if (window == lastWindow) + { + return { mousePosX, mousePosY }; + } + return { std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() }; + } + + void GetMousePosition(const IWindow* window, double& posX, double& posY) const + { + if (window == lastWindow) + { + posX = mousePosX; + posY = mousePosY; + } + else + { + posX = std::numeric_limits::quiet_NaN(); + posY = std::numeric_limits::quiet_NaN(); + } + } + + IWindow* GetActiveWindow() const + { + return lastWindow; + } + + bool IsInWindow(const IWindow* window) const + { + return window == lastWindow; + } + }; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Input/InputDeviceType.hpp b/openVulkanoCpp/Input/InputDeviceType.hpp new file mode 100644 index 0000000..d2f8bd6 --- /dev/null +++ b/openVulkanoCpp/Input/InputDeviceType.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +namespace openVulkanoCpp +{ + namespace Input + { + enum class InputDeviceType : uint8_t + { + UNKNOWN, KEYBOARD, MOUSE, CONTROLLER + }; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Input/InputKey.hpp b/openVulkanoCpp/Input/InputKey.hpp new file mode 100644 index 0000000..713bf66 --- /dev/null +++ b/openVulkanoCpp/Input/InputKey.hpp @@ -0,0 +1,273 @@ +#pragma once +#include "InputDeviceType.hpp" + +namespace openVulkanoCpp +{ + namespace Input + { + class InputKey + { + public: + enum class InputType : uint8_t + { + BUTTON = 0, AXIS + }; + + class Keyboard + { + public: + enum Key : int16_t + { + UNKNOWN = -1, + // Numpad + KEY_NUMPAD_0 = 0, + KEY_NUMPAD_1 = 1, + KEY_NUMPAD_2 = 2, + KEY_NUMPAD_3 = 3, + KEY_NUMPAD_4 = 4, + KEY_NUMPAD_5 = 5, + KEY_NUMPAD_6 = 6, + KEY_NUMPAD_7 = 7, + KEY_NUMPAD_8 = 8, + KEY_NUMPAD_9 = 9, + KEY_NUMPAD_DECIMAL = 10, + KEY_NUMPAD_DIVIDE = 11, + KEY_NUMPAD_MULTIPLY = 12, + KEY_NUMPAD_SUBTRACT = 13, + KEY_NUMPAD_ADD = 14, + KEY_NUMPAD_ENTER = 15, + KEY_NUMPAD_EQUAL = 16, + KEY_NUM_LOCK = 17, + + KEY_ESCAPE = 18, + KEY_ENTER = 19, + KEY_TAB = 20, + KEY_BACKSPACE = 21, + KEY_INSERT = 22, + KEY_DELETE = 23, + KEY_RIGHT = 24, + KEY_LEFT = 25, + KEY_DOWN = 26, + KEY_UP = 27, + KEY_PAGE_UP = 28, + KEY_PAGE_DOWN = 29, + KEY_HOME = 30, + KEY_END = 31, + KEY_CAPS_LOCK = 32, + KEY_SCROLL_LOCK = 33, + KEY_PRINT_SCREEN = 34, + KEY_PAUSE = 35, + KEY_LEFT_SHIFT = 36, + KEY_LEFT_CONTROL = 37, + KEY_LEFT_ALT = 38, + KEY_LEFT_SUPER = 39, + KEY_RIGHT_SHIFT = 40, + KEY_RIGHT_CONTROL = 41, + KEY_RIGHT_ALT = 42, + KEY_RIGHT_SUPER = 43, + + // Printable keys, most of them are mapped to their ascii codes + KEY_COMMA = 44, + KEY_MINUS = 45, + KEY_PERIOD = 46, + KEY_SLASH = 47, + KEY_0 = 48, + KEY_1 = 49, + KEY_2 = 50, + KEY_3 = 51, + KEY_4 = 52, + KEY_5 = 53, + KEY_6 = 54, + KEY_7 = 55, + KEY_8 = 56, + KEY_9 = 57, + KEY_WORLD_1 = 58, + KEY_SEMICOLON = 59, + KEY_WORLD_2 = 60, + KEY_EQUAL = 61, + KEY_SPACE = 62, + KEY_APOSTROPHE = 63, //' + KEY_GRAVE_ACCENT = 64, // ` + KEY_A = 65, + KEY_B = 66, + KEY_C = 67, + KEY_D = 68, + KEY_E = 69, + KEY_F = 70, + KEY_G = 71, + KEY_H = 72, + KEY_I = 73, + KEY_J = 74, + KEY_K = 75, + KEY_L = 76, + KEY_M = 77, + KEY_N = 78, + KEY_O = 79, + KEY_P = 80, + KEY_Q = 81, + KEY_R = 82, + KEY_S = 83, + KEY_T = 84, + KEY_U = 85, + KEY_V = 86, + KEY_W = 87, + KEY_X = 88, + KEY_Y = 89, + KEY_Z = 90, + KEY_LEFT_BRACKET = 91, // [ + KEY_BACKSLASH = 92, + KEY_RIGHT_BRACKET = 93, // ] + + KEY_MENU = 99, + + // Function keys + KEY_F1 = 101, + KEY_F2 = 102, + KEY_F3 = 103, + KEY_F4 = 104, + KEY_F5 = 105, + KEY_F6 = 106, + KEY_F7 = 107, + KEY_F8 = 108, + KEY_F9 = 109, + KEY_F10 = 110, + KEY_F11 = 111, + KEY_F12 = 112, + KEY_F13 = 113, + KEY_F14 = 114, + KEY_F15 = 115, + KEY_F16 = 116, + KEY_F17 = 117, + KEY_F18 = 118, + KEY_F19 = 119, + KEY_F20 = 120, + KEY_F21 = 121, + KEY_F22 = 122, + KEY_F23 = 123, + KEY_F24 = 124, + KEY_F25 = 125, + + KEY_LAST = KEY_F25 + }; + }; + + class Mouse + { + public: + enum Button : int16_t + { + BUTTON_1 = 0, + BUTTON_2, + BUTTON_3, + BUTTON_4, + BUTTON_5, + BUTTON_6, + BUTTON_7, + BUTTON_8, + BUTTON_LAST = BUTTON_8, + BUTTON_LEFT = BUTTON_1, + BUTTON_RIGHT = BUTTON_2, + BUTTON_MIDDLE = BUTTON_3 + }; + + enum Axis : int16_t + { + AXIS_X = 0, + AXIS_Y, + AXIS_WHEEL_X, + AXIS_WHEEL_Y, + AXIS_LAST = AXIS_WHEEL_Y + }; + }; + + class Controller + { + public: + enum Button : int16_t + { + JOY_1 = 0, + JOY_2, + JOY_3, + JOY_4, + JOY_5, + JOY_6, + JOY_7, + JOY_8, + JOY_9, + JOY_10, + JOY_11, + JOY_12, + JOY_13, + JOY_14, + JOY_15, + JOY_16, + JOY_LAST = JOY_16, + + BUTTON_A = JOY_1, + BUTTON_B = JOY_2, + BUTTON_X = JOY_3, + BUTTON_Y = JOY_4, + BUTTON_LEFT_BUMPER = JOY_5, + BUTTON_RIGHT_BUMPER = JOY_6, + BUTTON_BACK = JOY_7, + BUTTON_START = JOY_8, + BUTTON_GUIDE = JOY_9, + BUTTON_LEFT_THUMB = JOY_10, + BUTTON_RIGHT_THUMB = JOY_11, + BUTTON_DPAD_UP = JOY_12, + BUTTON_DPAD_RIGHT = JOY_13, + BUTTON_DPAD_DOWN = JOY_14, + BUTTON_DPAD_LEFT = JOY_15, + + PS_BUTTON_CROSS = BUTTON_A, + PS_BUTTON_CIRCLE = BUTTON_B, + PS_BUTTON_SQUARE = BUTTON_X, + PS_BUTTON_TRIANGLE = BUTTON_Y + }; + + enum Axis : int16_t + { + AXIS_LEFT_X = 0, + AXIS_LEFT_Y, + AXIS_RIGHT_X, + AXIS_RIGHT_Y, + AXIS_LEFT_TRIGGER, + AXIS_RIGHT_TRIGGER, + AXIS_LAST = AXIS_RIGHT_TRIGGER + }; + }; + + private: + InputDeviceType deviceType; + InputType type; + int16_t key; + + public: + InputKey(Keyboard::Key keyboardKey) : + deviceType(InputDeviceType::KEYBOARD), type(InputType::BUTTON), key(keyboardKey) + {} + + InputKey(Mouse::Button mouseButton) : + deviceType(InputDeviceType::MOUSE), type(InputType::BUTTON), key(mouseButton) + {} + + InputKey(Mouse::Axis mouseAxis) : + deviceType(InputDeviceType::MOUSE), type(InputType::AXIS), key(mouseAxis) + {} + + InputKey(Controller::Button controllerButton) : + deviceType(InputDeviceType::CONTROLLER), type(InputType::BUTTON), key(controllerButton) + {} + + InputKey(Controller::Axis controllerAxis) : + deviceType(InputDeviceType::CONTROLLER), type(InputType::AXIS), key(controllerAxis) + {} + + InputDeviceType GetInputDeviceType() const { return deviceType; } + + InputType GetInputType() const { return type; } + + int16_t GetInputKey() const { return key; } + }; + } +} diff --git a/openVulkanoCpp/Input/InputManager.hpp b/openVulkanoCpp/Input/InputManager.hpp new file mode 100644 index 0000000..8bf08ba --- /dev/null +++ b/openVulkanoCpp/Input/InputManager.hpp @@ -0,0 +1,182 @@ +#pragma once + +#include "InputKey.hpp" +#include "InputDevice.hpp" +#include "../Base/Utils.hpp" + +#include +#include +#include + +namespace openVulkanoCpp +{ + namespace Input + { + class BaseInputAction + { + std::string name; + std::vector devices; + bool enabled; + + protected: + BaseInputAction(const std::string& name) : name(name), enabled(true) {} + + public: + virtual ~BaseInputAction() = default; + + const std::string& GetName() const + { + return name; + } + + const std::vector& GetDevices() const + { + return devices; + } + + bool IsEnabled() const + { + return enabled; + } + + void SetEnabled(bool enabled) + { + this->enabled = enabled; + } + }; + + class InputAction : public BaseInputAction + { + std::vector keys; + std::vector> axisButtons; + + public: + InputAction(const std::string& name) : BaseInputAction(name) + {} + + const std::vector& GetKeys() const + { + return keys; + } + + const std::vector>& GetAxisButtons() const + { + return axisButtons; + } + + void BindKey(InputKey key) + { + keys.push_back(key); + } + + void BindAxisButtons(InputKey keyPositive, InputKey keyNegative) + { + axisButtons.emplace_back(keyPositive, keyNegative); + } + }; + + class InputShortcut : public BaseInputAction + { + std::string name; + std::vector devices; + std::vector> buttonBindings; + + public: + InputShortcut(const std::string& name) : BaseInputAction(name) + {} + }; + + class InputManager + { + InputManager() = default; + public: + static InputManager* GetInstace() + { + static InputManager* instance = new InputManager(); + return instance; + } + + void RegisterInputDevice(InputDevice* device) + { + devices.push_back(device); + if (!lastActiveDevice) lastActiveDevice = device; + } + + void UnregisterInputDevice(InputDevice* device) + { + Utils::Remove(devices, device); + } + + InputAction* GetAction(const std::string& actionName) + { + InputAction*& action = actionNameMapping[actionName]; + if(!action) + { + action = new InputAction(actionName); + } + return action; + } + + float GetAxis(const InputAction* action) const + { + float value = 0; + const std::vector& testDevices = action->GetDevices().empty() ? devices : action->GetDevices(); + for (const InputDevice* device : testDevices) + { + for(InputKey key : action->GetKeys()) + { + value += device->GetAxis(key); + } + for(const auto& keys : action->GetAxisButtons()) + { + value += GetAxis(keys.first) - GetAxis(keys.second); + } + } + return value; + } + + float GetAxis(InputKey key) const + { + float value = 0; + for (const InputDevice* device : devices) + { + value += device->GetAxis(key); + } + return value; + } + + bool GetButton(InputAction* action) const + { + const std::vector& testDevices = action->GetDevices().empty() ? devices : action->GetDevices(); + for (const InputDevice* device : testDevices) + { + for (const InputKey key : action->GetKeys()) + { + if (device->GetButton(key)) return true; + } + } + return false; + } + + bool GetButton(InputKey key) const + { + for(const InputDevice* device : devices) + { + if (device->GetButton(key)) return true; + } + return false; + } + + InputDevice* GetLastActiveDevice() const + { + return lastActiveDevice; + } + + private: + //std::unordered_map> inputActionMapping; + std::unordered_map actionNameMapping; + std::vector devices; + InputDevice* lastActiveDevice = nullptr; + }; + } +} diff --git a/openVulkanoCpp/Scene/Camera.hpp b/openVulkanoCpp/Scene/Camera.hpp index e695f1c..d159454 100644 --- a/openVulkanoCpp/Scene/Camera.hpp +++ b/openVulkanoCpp/Scene/Camera.hpp @@ -3,6 +3,7 @@ #include #include #include "Node.hpp" +#define GLM_FORECE_DEPTH_ZERO_TO_ONE namespace openVulkanoCpp { @@ -17,7 +18,7 @@ namespace openVulkanoCpp glm::mat4x4 projection, view, viewProjection; Camera() = default; - virtual ~Camera() = default; + virtual ~Camera() override = default; public: void Init(float width, float height, float nearPlane, float farPlane) diff --git a/openVulkanoCpp/Scene/Geometry.hpp b/openVulkanoCpp/Scene/Geometry.hpp index 9020f2f..968f108 100644 --- a/openVulkanoCpp/Scene/Geometry.hpp +++ b/openVulkanoCpp/Scene/Geometry.hpp @@ -131,7 +131,7 @@ namespace openVulkanoCpp 20, 21, 22, 20, 22, 23 // right face index data }, indexCount); x *= 0.5f; y *= 0.5f; z *= 0.5f; - int i = 0; + uint32_t i = 0; // front face vertex data vertices[i++].Set(-x, +y, -z, +0, +0, -1, +0, +0); vertices[i++].Set(-x, -y, -z, +0, +0, -1, +0, +1); diff --git a/openVulkanoCpp/Vulkan/RenderPass.hpp b/openVulkanoCpp/Vulkan/RenderPass.hpp index 7003f71..10e6423 100644 --- a/openVulkanoCpp/Vulkan/RenderPass.hpp +++ b/openVulkanoCpp/Vulkan/RenderPass.hpp @@ -35,6 +35,7 @@ namespace openVulkanoCpp CreateRenderPass(); frameBuffer->InitRenderPass(this); SetClearColor(); + SetClearDepth(); } void Close() override diff --git a/openVulkanoCpp/Vulkan/Scene/VulkanShader.hpp b/openVulkanoCpp/Vulkan/Scene/VulkanShader.hpp index 2d7395c..46de025 100644 --- a/openVulkanoCpp/Vulkan/Scene/VulkanShader.hpp +++ b/openVulkanoCpp/Vulkan/Scene/VulkanShader.hpp @@ -63,7 +63,8 @@ namespace openVulkanoCpp vk::PipelineRasterizationStateCreateInfo rasterizer = {}; rasterizer.cullMode = vk::CullModeFlagBits::eBack; vk::PipelineMultisampleStateCreateInfo msaa = {}; - vk::PipelineDepthStencilStateCreateInfo depth = { {}, 1, 1, vk::CompareOp::eGreater }; + vk::PipelineDepthStencilStateCreateInfo depth = { {}, 1, 1, vk::CompareOp::eLess }; + depth.maxDepthBounds = 1; vk::PipelineColorBlendAttachmentState colorBlendAttachment = {}; colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eA | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eR; vk::PipelineColorBlendStateCreateInfo colorInfo = {}; @@ -74,7 +75,7 @@ namespace openVulkanoCpp vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { {}, static_cast(shaderStageCreateInfos.size()), shaderStageCreateInfos.data(), &pipelineVertexInputStateCreateInfo, &inputAssembly, nullptr, &viewportStateCreateInfo, &rasterizer, &msaa, &depth, &colorInfo, nullptr, context->pipeline.pipelineLayout, context->swapChainRenderPass.renderPass }; - pipeline = this->device.createGraphicsPipeline({}, pipelineCreateInfo).value; + pipeline = this->device.createGraphicsPipeline({}, pipelineCreateInfo); } diff --git a/openVulkanoCpp/main.cpp b/openVulkanoCpp/main.cpp index ebfbdf3..d23a6d2 100644 --- a/openVulkanoCpp/main.cpp +++ b/openVulkanoCpp/main.cpp @@ -2,8 +2,13 @@ #include "Scene/Scene.hpp" #include "Scene/Shader.hpp" #include "Base/EngineConfiguration.hpp" +#include "Input/InputManager.hpp" + +#include +#include using namespace openVulkanoCpp::Scene; +using namespace openVulkanoCpp::Input; uint32_t GEOS = 3000, OBJECTS = 10000, DYNAMIC = 1000; @@ -16,6 +21,11 @@ class ExampleApp : public openVulkanoCpp::IGraphicsApp std::vector drawablesPool; std::vector nodesPool; + InputAction* actionForward; + InputAction* actionSide; + InputAction* actionLookUp; + InputAction* actionLookSide; + public: std::string GetAppName() override { return "ExampleApp"; } std::string GetAppVersion() override { return "v1.0"; } @@ -51,14 +61,55 @@ public: scene.shader = &shader; GetGraphicsAppManager()->GetRenderer()->SetScene(&scene); + + auto input = InputManager::GetInstace(); + actionForward = input->GetAction("forward"); + actionSide = input->GetAction("side"); + actionLookUp = input->GetAction("look up"); + actionLookSide = input->GetAction("look side"); + + actionForward->BindKey(InputKey(InputKey::Controller::AXIS_LEFT_Y)); + actionForward->BindAxisButtons(InputKey(InputKey::Keyboard::KEY_S), InputKey(InputKey::Keyboard::KEY_W)); + actionSide->BindKey(InputKey(InputKey::Controller::AXIS_LEFT_X)); + actionSide->BindAxisButtons(InputKey(InputKey::Keyboard::KEY_D), InputKey(InputKey::Keyboard::KEY_A)); + actionLookUp->BindKey(InputKey(InputKey::Controller::AXIS_RIGHT_Y)); + actionLookSide->BindKey(InputKey(InputKey::Controller::AXIS_RIGHT_X)); + actionLookUp->BindAxisButtons(InputKey(InputKey::Keyboard::KEY_DOWN), InputKey(InputKey::Keyboard::KEY_UP)); + actionLookSide->BindAxisButtons(InputKey(InputKey::Keyboard::KEY_RIGHT), InputKey(InputKey::Keyboard::KEY_LEFT)); + actionLookUp->BindKey(InputKey(InputKey::Mouse::AXIS_Y)); + actionLookSide->BindKey(InputKey(InputKey::Mouse::AXIS_X)); } + float yaw = 0, pitch = 0; + glm::vec3 position = {0,0,10}; + void Tick() override { for(int i = 0; i < DYNAMIC; i++) { nodesPool[i]->SetMatrix(glm::translate(glm::mat4x4(1), glm::vec3((std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5))); } + + auto input = InputManager::GetInstace(); + glm::vec3 vec(input->GetAxis(actionSide), 0, -input->GetAxis(actionForward)); + if(glm::length2(vec) > 1) + { + vec = glm::normalize(vec); + } + + float tiemScale = 0.004f; + + vec = vec * tiemScale; // scale vector + + yaw += input->GetAxis(actionLookSide) * tiemScale * 0.5f; + pitch += input->GetAxis(actionLookUp) * tiemScale * 0.5f; + + glm::quat rot(glm::vec3(pitch, yaw, 0)); + glm::mat4 rotMat = glm::toMat4(rot); + + vec = glm::vec3(rot * glm::vec4(vec, 1)); + position += vec; + cam.SetMatrix(glm::translate(glm::mat4(1), position) * rotMat); } void Close() override{} diff --git a/openVulkanoCpp/openVulkanoCpp.vcxproj b/openVulkanoCpp/openVulkanoCpp.vcxproj index 2299cf0..c4d7e1d 100644 --- a/openVulkanoCpp/openVulkanoCpp.vcxproj +++ b/openVulkanoCpp/openVulkanoCpp.vcxproj @@ -51,6 +51,7 @@ $(VULKAN_SDK)\Include;$(SolutionDir)\external\spdlog\include;C:\Program Files\Assimp\include true _MBCS;%(PreprocessorDefinitions);DEBUG + stdcpp17 Console @@ -98,6 +99,7 @@ xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\" + @@ -110,6 +112,7 @@ xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\" + @@ -120,6 +123,17 @@ xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\" + + + + + + + + + + + @@ -129,7 +143,7 @@ xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\" - +