diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2023-11-15 13:03:48 -0500 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2023-11-15 13:03:48 -0500 |
| commit | 1513216876a0409f45d88cbad14ae8b48fca37e2 (patch) | |
| tree | 32b48d966faf36138dd985e4df739a83cddc5f48 | |
| parent | 6a30d21c5175c27aeccc8a6ae956c274f3ef9621 (diff) | |
major input refactor and add debug camera
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/camera.cpp | 13 | ||||
| -rw-r--r-- | src/camera.hpp | 33 | ||||
| -rw-r--r-- | src/game-settings.hpp | 1 | ||||
| -rw-r--r-- | src/game.cpp | 258 | ||||
| -rw-r--r-- | src/player-input.cpp | 762 | ||||
| -rw-r--r-- | src/player-input.hpp | 157 | ||||
| -rw-r--r-- | src/window.cpp | 33 |
8 files changed, 1076 insertions, 183 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6df5889..3b18024 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O3 -s DNDEBUG") set(PKE_SOURCE_FILES src/main.cpp src/macros.hpp + src/camera.hpp + src/camera.cpp src/components.hpp src/components.cpp src/ecs.hpp diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 0000000..d2c1b71 --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,13 @@ + +#include "camera.hpp" + +PkeCamera NullCamera { + .pos = glm::vec3(3.f, 3.f, 3.f), + .rot = glm::quat(1.f, 0.f, 0.f, 0.f), + .target = glm::vec3(0.f), + .type = PKE_CAMERA_TYPE_ORTHOGONAL, + .orientation = PKE_CAMERA_ORIENTATION_TARGET, + .stale = PKE_CAMERA_STALE_ALL, +}; +PkeCamera *ActiveCamera = &NullCamera; + diff --git a/src/camera.hpp b/src/camera.hpp new file mode 100644 index 0000000..c699767 --- /dev/null +++ b/src/camera.hpp @@ -0,0 +1,33 @@ + +#include "macros.hpp" +#include "vendor/glm_include.hpp" + +#include <cstdint> + +TypeSafeInt_Const_Expr(PkeCameraType, uint8_t, 0xFF); +TypeSafeInt_Const_Expr(PkeCameraOrientation, uint8_t, 0xFF); +TypeSafeInt_Const_Expr(PkeCameraStaleFlags, uint8_t, 0xFF); + +const PkeCameraType PKE_CAMERA_TYPE_PERSPECTIVE = PkeCameraType{1 << 0}; +const PkeCameraType PKE_CAMERA_TYPE_ORTHOGONAL = PkeCameraType{1 << 1}; + +const PkeCameraOrientation PKE_CAMERA_ORIENTATION_TARGET = PkeCameraOrientation{1 << 0}; +const PkeCameraOrientation PKE_CAMERA_ORIENTATION_FREE = PkeCameraOrientation{1 << 1}; + +const PkeCameraStaleFlags PKE_CAMERA_STALE_POS = PkeCameraStaleFlags{1 << 0}; +const PkeCameraStaleFlags PKE_CAMERA_STALE_ROT = PkeCameraStaleFlags{1 << 1}; +const PkeCameraStaleFlags PKE_CAMERA_STALE_ORIENTATION = PkeCameraStaleFlags{1 << 2}; +const PkeCameraStaleFlags PKE_CAMERA_STALE_ALL = PkeCameraStaleFlags{0xFF}; + +struct PkeCamera { + glm::vec3 pos; + glm::quat rot; + glm::vec3 target; + PkeCameraType type; + PkeCameraOrientation orientation; + PkeCameraStaleFlags stale; +}; +extern PkeCamera NullCamera; +extern PkeCamera *ActiveCamera; + + diff --git a/src/game-settings.hpp b/src/game-settings.hpp index cc20233..c42b917 100644 --- a/src/game-settings.hpp +++ b/src/game-settings.hpp @@ -15,6 +15,7 @@ struct GameSettings { double deltaPerFrame = 1.0 / double(targetFPS); double minimumDeltaPerFrame = 1.0 / double(minFPS); struct { + bool isUsingDebugCamera = false; bool isShowingConsole = true; bool isShowingEntityList = true; bool isShowingSceneEditor = true; diff --git a/src/game.cpp b/src/game.cpp index 69e728d..e921b2f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,6 +1,7 @@ #include "game.hpp" +#include "camera.hpp" #include "components.hpp" #include "dynamic-array.hpp" #include "entities.hpp" @@ -9,7 +10,9 @@ #include "imgui.h" #include "player-input.hpp" #include "vendor/glm_include.hpp" +#include "window.hpp" +#include <GLFW/glfw3.h> #include <cstring> #include <iomanip> @@ -20,6 +23,35 @@ char readLine[readLineLength]; const char *levelName = "demo-level"; +PkeCamera cameraDefault { + .pos = glm::vec3(-40.f, -40.f, -40.f), + .rot = glm::quat(1.f, 0.f, 0.f, 0.f), + .target = glm::vec3(0.f), + .type = PKE_CAMERA_TYPE_PERSPECTIVE, + .orientation = PKE_CAMERA_ORIENTATION_TARGET, + .stale = PKE_CAMERA_STALE_ALL, +}; +PkeCamera cameraDbg { + .pos = glm::vec3(4.f, 4.f, 4.f), + .rot = glm::quat(1.f, 0.f, 0.f, 0.f), + .target = glm::vec3(0.f), + .type = PKE_CAMERA_TYPE_PERSPECTIVE, + .orientation = PKE_CAMERA_ORIENTATION_FREE, + .stale = PKE_CAMERA_STALE_ALL, +}; + +const char *dbgCtrl_CameraLeft = "debug-camera-left"; +const char *dbgCtrl_CameraRight = "debug-camera-right"; +const char *dbgCtrl_CameraForward = "debug-camera-forward"; +const char *dbgCtrl_CameraBack = "debug-camera-back"; +const char *dbgCtrl_CameraUp = "debug-camera-up"; +const char *dbgCtrl_CameraDown = "debug-camera-down"; +const char *dbgCtrl_CameraRotCC = "debug-camera-rot-counter-clockwise"; +const char *dbgCtrl_CameraRotC = "debug-camera-rot-clockwise"; +const char *dbgCtrl_CameraRot = "debug-camera-rot"; +const char *dbgCtrl_UnlockCamera = "debug-camera-unlock"; +InputActionSetHandle debugControlsHandle = InputActionSetHandle_MAX; + struct EntityTypeInstanceCreateInfo { EntityHandle entityTypeEntityHandle; }; @@ -54,6 +86,11 @@ EntityHandle selectedEntity = EntityHandle_MAX; bool shouldCreateEntityType = false; EntityType *entityTypeToCreate = nullptr; +bool shouldInitEditor = true; +bool shouldLockCamera = false; +bool shouldUnlockCamera = false; +bool shouldTeardownEditor = false; + void SerializeEntityType(std::ofstream &stream, const EntityType &et) { char handleStr[19] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; snprintf(handleStr, 19, "0x%016lX",static_cast<EntityHandle_T>(et.entityHandle)); @@ -335,11 +372,36 @@ void LoadSceneFile(const char *sceneFilePath) { f.close(); } +void setupEditor() { + shouldInitEditor = false; + PkeInput_ActivateSet(debugControlsHandle); +} + +void teardownEditor() { + shouldTeardownEditor = false; + PkeInput_DeactivateSet(debugControlsHandle); +} + void Game_Tick(double delta) { /* * ECS_Tick() gets called first because it updates the public * `EntitiesToBeRemoved` for all other ticks to use. */ + if (shouldInitEditor) { + setupEditor(); + } + if (shouldUnlockCamera) { + shouldUnlockCamera = false; + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + cameraDbg.pos = ActiveCamera->pos; + if (ActiveCamera->orientation == cameraDbg.orientation) { + cameraDbg.rot = ActiveCamera->rot; + } else { + cameraDbg.rot = glm::quat(UBO.view); + } + cameraDbg.stale = PKE_CAMERA_STALE_ALL; + ActiveCamera = &cameraDbg; + } ECS_Tick_Early(delta); if (shouldCreateEntityType) { assert(entityTypeToCreate != nullptr); @@ -361,12 +423,133 @@ void Game_Tick(double delta) { ECS_CreateInstance(newEntity, createInfo.entityTypeEntityHandle); } PkeInput_Tick(delta); - static double accDelta = 0.0; - accDelta += delta; - UBO.model = glm::rotate(glm::mat4(1.0f), (float)accDelta * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); - UBO.view = glm::lookAt(glm::vec3(4.0f, 4.0f, 4.0f), glm::vec3(0), glm::vec3(0.0f, 0.0f, 1.0f)); + PkeInputEventHolder holder = PkeInput_Query(dbgCtrl_UnlockCamera); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyEsc; + keyEsc = static_cast<PkeKeyEvent *>(holder.ptr); + if (keyEsc->isPressed || keyEsc->pressCount > 0) { + pkeSettings.editorSettings.isUsingDebugCamera = false; + shouldLockCamera = true; + } + } + + if (shouldLockCamera) { + shouldLockCamera = false; + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + cameraDefault.stale = PKE_CAMERA_STALE_ALL; + ActiveCamera = &cameraDefault; + } + + if (pkeSettings.editorSettings.isUsingDebugCamera) { + holder = PkeInput_Query(dbgCtrl_CameraRot); + if (holder.type != InputEventHash{0}) { + const PkeCursorPosEvent *posEvent = static_cast<PkeCursorPosEvent *>(holder.ptr); + if (posEvent->xMotion || posEvent->yMotion) { + glm::vec3 axis1Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(1.f, 0.f, 0.f); + glm::vec3 axis2Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, 1.f, 0.f); + // glm::vec3 axis1Heading = cameraDbg.rot * glm::vec3(1.f, 0.f, 0.f); + // glm::vec3 axis2Heading = cameraDbg.rot * glm::vec3(0.f, 1.f, 0.f); + glm::quat pitch = glm::angleAxis(float(posEvent->yMotion) * 0.01f, glm::normalize(axis1Heading)); + glm::quat yaw = glm::angleAxis(float(posEvent->xMotion) * 0.01f, glm::normalize(axis2Heading)); + glm::quat rot = cameraDbg.rot * pitch * yaw; + // rot = glm::normalize(rot); + auto eul = glm::eulerAngles(rot); + cameraDbg.rot = glm::quat(eul); + cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_ROT; + } + } + + double leftCount = 0; + double rightCount = 0; + double forwardCount = 0; + double backCount = 0; + double upCount = 0; + double downCount = 0; + double rotCCCount = 0; + double rotCCount = 0; + + holder = PkeInput_Query(dbgCtrl_CameraLeft); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyLeft; + keyLeft = static_cast<PkeKeyEvent *>(holder.ptr); + leftCount = keyLeft->isPressed ? 1 : 0; + } + + holder = PkeInput_Query(dbgCtrl_CameraRight); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyRight; + keyRight = static_cast<PkeKeyEvent *>(holder.ptr); + rightCount = keyRight->isPressed ? 1 : 0; + } + + holder = PkeInput_Query(dbgCtrl_CameraForward); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyForward; + keyForward = static_cast<PkeKeyEvent *>(holder.ptr); + forwardCount = keyForward->isPressed ? 1 : 0; + } + + holder = PkeInput_Query(dbgCtrl_CameraBack); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyBack; + keyBack = static_cast<PkeKeyEvent *>(holder.ptr); + backCount = keyBack->isPressed ? 1 : 0; + } + + holder = PkeInput_Query(dbgCtrl_CameraUp); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyUp; + keyUp = static_cast<PkeKeyEvent *>(holder.ptr); + upCount = keyUp->isPressed ? 1 : 0; + } + + holder = PkeInput_Query(dbgCtrl_CameraDown); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyDown; + keyDown = static_cast<PkeKeyEvent *>(holder.ptr); + downCount = keyDown->isPressed ? 1 : 0; + } + + holder = PkeInput_Query(dbgCtrl_CameraRotCC); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyRotCC; + keyRotCC = static_cast<PkeKeyEvent *>(holder.ptr); + rotCCCount = keyRotCC->isPressed ? 1 : 0; + } + + holder = PkeInput_Query(dbgCtrl_CameraRotC); + if (holder.type != InputEventHash{0}) { + PkeKeyEvent *keyRotC; + keyRotC = static_cast<PkeKeyEvent *>(holder.ptr); + rotCCount = keyRotC->isPressed ? 1 : 0; + } + + double accelerated = delta * 10.f; + double axis1 = -(leftCount * accelerated) + (rightCount * accelerated); + double axis2 = (forwardCount * accelerated) + -(backCount * accelerated); + double axis3 = -(upCount * accelerated) + (downCount * accelerated); + if (axis1 != 0 || axis2 != 0 || axis3 != 0) { + glm::vec3 axis1Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(-axis1, 0.f, 0.f); + glm::vec3 axis2Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, 0.f, axis2); + glm::vec3 axis3Heading = glm::conjugate(cameraDbg.rot) * glm::vec3(0.f, axis3, 0.f); + cameraDbg.pos += glm::vec3(axis1Heading + axis2Heading + axis3Heading); + cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_POS; + } + + double axis4 = -(rotCCCount * delta) + (rotCCount * delta); + if (axis4 != 0.0) { + cameraDbg.rot = glm::quat(glm::vec3(0.f, 0.f, axis4)) * cameraDbg.rot; + cameraDbg.stale = cameraDbg.stale | PKE_CAMERA_STALE_ROT; + } + } else { + cameraDefault.target = glm::vec3(0.f, 0.f, 0.f); + cameraDefault.stale = cameraDefault.stale | PKE_CAMERA_STALE_ROT; + } EntityType_Tick_Late(delta); ECS_Tick_Late(delta); + if (shouldTeardownEditor) { + teardownEditor(); + } } void RecordImGui_GLM(const char *label, glm::mat4 &mat) { @@ -397,6 +580,13 @@ void RecordImGuiEditorWrapper() { shouldRecreateSwapchain = true; } // ImGui::Checkbox("Uncap Tickrate", &pkeSettings.isTickrateUnlocked); + if (ImGui::Checkbox("Use Debug Camera", &pkeSettings.editorSettings.isUsingDebugCamera)) { + if (pkeSettings.editorSettings.isUsingDebugCamera) { + shouldUnlockCamera = true; + } else { + shouldLockCamera = true; + } + } ImGuiIO &io = ImGui::GetIO(); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); @@ -678,8 +868,68 @@ void Game_Init() { memset(consoleBuffer[i], '\0', consoleLineLength); } Event_RegisterCallback("RenderImGui", RecordImGuiEditor); + + PkeInputSet debugControlsSet; + debugControlsSet.title = "debug-editor-controls"; + debugControlsSet.actionCount = 10; + debugControlsSet.actions = Pke_New<PkeInputAction>(debugControlsSet.actionCount); + + debugControlsSet.actions[0].name = dbgCtrl_CameraLeft; + debugControlsSet.actions[0].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_A, + }; + debugControlsSet.actions[1].name = dbgCtrl_CameraRight; + debugControlsSet.actions[1].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_D, + }; + debugControlsSet.actions[2].name = dbgCtrl_CameraForward; + debugControlsSet.actions[2].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_W, + }; + debugControlsSet.actions[3].name = dbgCtrl_CameraBack; + debugControlsSet.actions[3].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_S, + }; + debugControlsSet.actions[4].name = dbgCtrl_CameraUp; + debugControlsSet.actions[4].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_R, + }; + debugControlsSet.actions[5].name = dbgCtrl_CameraDown; + debugControlsSet.actions[5].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_F, + }; + debugControlsSet.actions[6].name = dbgCtrl_CameraRotCC; + debugControlsSet.actions[6].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_Q, + }; + debugControlsSet.actions[7].name = dbgCtrl_CameraRotC; + debugControlsSet.actions[7].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_E, + }; + debugControlsSet.actions[8].name = dbgCtrl_CameraRot; + debugControlsSet.actions[8].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS, + }; + debugControlsSet.actions[9].name = dbgCtrl_UnlockCamera; + debugControlsSet.actions[9].primaryHash = PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = GLFW_KEY_ESCAPE, + }; + + debugControlsHandle = PkeInput_RegisterSet(debugControlsSet); + + ActiveCamera = &cameraDefault; } void Game_Teardown() { + PkeInput_UnregisterSet(debugControlsHandle); entityInstancesToCreate.~DynArray(); } diff --git a/src/player-input.cpp b/src/player-input.cpp index 4b2b6be..9f50307 100644 --- a/src/player-input.cpp +++ b/src/player-input.cpp @@ -1,9 +1,38 @@ #include "player-input.hpp" -#include "window.hpp" + +#include "dynamic-array.hpp" #include "game-settings.hpp" +#include "window.hpp" + +#include <GLFW/glfw3.h> #include <chrono> +struct CursorEnterEvent { + bool entered; +}; +struct CursorPosEvent { + double x; + double y; +}; +struct KeyEvent { + int32_t button; + int32_t mods; + int8_t action; +}; +struct MouseButtonEvent { + int32_t button; + int32_t mods; + int8_t action; +}; +struct ScrollEvent { + double x; + double y; +}; +struct WindowFocusEvent { + bool focused; +}; + GLFWcursorenterfun prevCursorEnterCallback; GLFWcursorposfun prevCursorPosCallback; GLFWkeyfun prevKeyCallback; @@ -11,166 +40,500 @@ GLFWmousebuttonfun prevMouseButtonCallback; GLFWscrollfun prevScrollCallback; GLFWwindowfocusfun prevWindowFocusCallback; -TypeSafeInt_B(InputEventHash); -TypeSafeInt_B(InputEventType); -TypeSafeInt_B(InputCursorEntered); -TypeSafeInt_B(InputKeyboardKeyAction); -TypeSafeInt_B(InputMouseButtonAction); -TypeSafeInt_B(InputWindowFocused); - -DynArray<PkeInputEvent> UnhandledPkeInputEvents{0, nullptr}; - -bool PkeInput_Query(const PkeInputEvent &mask) { - auto count = UnhandledPkeInputEvents.Count(); - for (long i = 0; i < count; ++i) { - const auto &ev = UnhandledPkeInputEvents[i]; - // TODO this could probably be reduced to a single & op - // - if we exclude x, y, button, scanCode, and mods - // there are less than 64 options total - if (!(mask.type == ev.type)) continue; - if (!(mask.tickCount == ev.tickCount)) continue; - if (!(mask.cursorEntered == ev.cursorEntered)) continue; - if (!(mask.keyboardKeyAction == ev.keyboardKeyAction)) continue; - if (!(mask.mouseButtonAction == ev.mouseButtonAction)) continue; - if (!(mask.windowFocused == ev.windowFocused)) continue; - if (!(mask.x == ev.x)) continue; - if (!(mask.y == ev.y)) continue; - if (!(mask.button == ev.button)) continue; - if (!(mask.scanCode == ev.scanCode)) continue; - if (!(mask.mods == ev.mods)) continue; - UnhandledPkeInputEvents.Remove(i); - return true; - } - return false; +TypeSafeInt_B(InputActionSetHandle); + +DynArray<CursorPosEvent> unhandledCursorPosEvents{512, nullptr}; +DynArray<CursorEnterEvent> unhandledCursorEnterEvents{8, nullptr}; +DynArray<KeyEvent> unhandledKeyEvents{8, nullptr}; +DynArray<MouseButtonEvent> unhandledMouseButtonEvents{8, nullptr}; +DynArray<ScrollEvent> unhandledScrollEvents{8, nullptr}; +DynArray<WindowFocusEvent> unhandledWindowFocusEvents{8, nullptr}; + +DynArray<PkeCursorEnterEvent> registeredCursorEnterEvents{8, nullptr}; +bool lastCursorEntered = false; +DynArray<PkeCursorPosEvent> registeredCursorPosEvents{8, nullptr}; +struct { + double x = 0; + double y = 0; +} lastMousePos; +DynArray<PkeKeyEvent> registeredKeyEvents{8, nullptr}; +DynArray<PkeMouseButtonEvent> registeredMouseButtonEvents{8, nullptr}; +DynArray<PkeScrollEvent> registeredScrollEvents{8, nullptr}; +DynArray<PkeWindowFocusEvent> registeredWindowFocusEvents{8, nullptr}; +bool lastWindowFocus = false; + +// DynArray<PkeInputEvent> UnhandledPkeInputEvents{1024, nullptr}; + +DynArray<PkeInputSet> registeredInputSets{0, nullptr}; +DynArray<InputActionSetHandle> activeInputSetStack{0, nullptr}; + +PkeInputAction *FindActionByName(const char *actionName) { + int64_t count = 0; + count = activeInputSetStack.Count(); + for (int64_t i = count - 1; i >= 0; --i) { + InputActionSetHandle handle = activeInputSetStack[i]; + InputActionSetHandle_T index = static_cast<InputActionSetHandle_T>(handle); + PkeInputSet &set = registeredInputSets[index]; + for (int64_t k = 0; k < set.actionCount; ++k) { + PkeInputAction &inputAction = set.actions[k]; + if (strcmp(actionName, inputAction.name) != 0) { + continue; + }; + return &inputAction; + } + } + return nullptr; +} +template<InputEventHash T, typename S> InputEventHash FindActivePkeInputAction_ByAction(const PkeInputAction *inputAction, S *&activeEvent) { + activeEvent = nullptr; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + activeEvent = ®isteredCursorEnterEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + activeEvent = ®isteredCursorPosEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + activeEvent = ®isteredKeyEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_KEY; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + activeEvent = ®isteredMouseButtonEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + activeEvent = ®isteredScrollEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + activeEvent = ®isteredWindowFocusEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; + } + return InputEventHash{0}; } +template<InputEventHash T, typename S> InputEventHash FindActivePkeInputAction_ByName(const char *actionName, S *&activeEvent) { + activeEvent = nullptr; + bool any = false; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + any = any || bool(registeredCursorEnterEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + any = any || bool(registeredCursorPosEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + any = any || bool(registeredKeyEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + any = any || bool(registeredMouseButtonEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + any = any || bool(registeredScrollEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + any = any || bool(registeredWindowFocusEvents.Count()); + } + if (any == false) return InputEventHash{0}; + int64_t count = 0; + count = activeInputSetStack.Count(); + for (int64_t i = count - 1; i >= 0; --i) { + InputActionSetHandle handle = activeInputSetStack[i]; + InputActionSetHandle_T index = static_cast<InputActionSetHandle_T>(handle); + PkeInputSet &set = registeredInputSets[index]; + for (int64_t k = 0; k < set.actionCount; ++k) { + PkeInputAction &inputAction = set.actions[k]; + if (strcmp(actionName, inputAction.name) != 0) { + continue; + }; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + activeEvent = ®isteredCursorEnterEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + activeEvent = ®isteredCursorPosEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + activeEvent = ®isteredKeyEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_KEY; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + activeEvent = ®isteredMouseButtonEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + activeEvent = ®isteredScrollEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + activeEvent = ®isteredWindowFocusEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; + } + } + } + return InputEventHash{0}; +} +template<InputEventHash T, typename S> InputEventHash FindActivePkeInputAction_ByType(const PkeInputEventMask &mask, S *&activeEvent) { + activeEvent = nullptr; + bool any = false; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + any = any || bool(registeredCursorEnterEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + any = any || bool(registeredCursorPosEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + any = any || bool(registeredKeyEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + any = any || bool(registeredMouseButtonEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + any = any || bool(registeredScrollEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + any = any || bool(registeredWindowFocusEvents.Count()); + } + if (any == false) return InputEventHash{0}; + int64_t count = 0; + count = activeInputSetStack.Count(); + for (int64_t i = count - 1; i >= 0; --i) { + InputActionSetHandle handle = activeInputSetStack[i]; + InputActionSetHandle_T index = static_cast<InputActionSetHandle_T>(handle); + PkeInputSet &set = registeredInputSets[index]; + for (int64_t k = 0; k < set.actionCount; ++k) { + PkeInputAction &inputAction = set.actions[k]; + for (int64_t l = 0; l < PKE_INPUT_ACTION_MASK_COUNT; ++l) { + PkeInputEventMask &evMask = inputAction.masks[l]; + if ((evMask.computedHash & mask.computedHash) == InputEventHash{0}) { + continue; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + if (evMask.button != mask.button) { + continue; + } + if (evMask.mods != mask.mods) { + continue; + } + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + if (evMask.button != mask.button) { + continue; + } + if (evMask.mods != mask.mods) { + continue; + } + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + activeEvent = ®isteredCursorEnterEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + activeEvent = ®isteredCursorPosEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + activeEvent = ®isteredKeyEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_KEY; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + activeEvent = ®isteredMouseButtonEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + activeEvent = ®isteredScrollEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + activeEvent = ®isteredWindowFocusEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; + } + } + } + } + return InputEventHash{0}; +} +constexpr auto FindActivePkeInputAction_CursorEnter_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER, PkeCursorEnterEvent>; +constexpr auto FindActivePkeInputAction_CursorPos_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS, PkeCursorPosEvent>; +constexpr auto FindActivePkeInputAction_Key_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_KEY, PkeKeyEvent>; +constexpr auto FindActivePkeInputAction_MouseButton_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON, PkeMouseButtonEvent>; +constexpr auto FindActivePkeInputAction_Scroll_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_SCROLL, PkeScrollEvent>; +constexpr auto FindActivePkeInputAction_WindowFocus_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS, PkeWindowFocusEvent>; + +constexpr auto FindActivePkeInputAction_CursorEnter_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER, PkeCursorEnterEvent>; +constexpr auto FindActivePkeInputAction_CursorPos_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS, PkeCursorPosEvent>; +constexpr auto FindActivePkeInputAction_Key_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_KEY, PkeKeyEvent>; +constexpr auto FindActivePkeInputAction_MouseButton_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON, PkeMouseButtonEvent>; +constexpr auto FindActivePkeInputAction_Scroll_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_SCROLL, PkeScrollEvent>; +constexpr auto FindActivePkeInputAction_WindowFocus_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS, PkeWindowFocusEvent>; + void PkeInput_Tick(double delta) { - // If we haven't handled an event within two seconds, we probably don't need it - static GameTimeDuration eventExpiration(std::chrono::seconds(2)); - int64_t removeCount = 0; - int64_t unhandledCount = UnhandledPkeInputEvents.Count(); - auto expiredTime = pkeSettings.steadyClock.now() - eventExpiration; - for (int64_t i = 0; i < unhandledCount; ++i) { - auto &ev = UnhandledPkeInputEvents[i]; - ev.tickCount += 1; - if (ev.timePoint < expiredTime) { - removeCount += 1; + // reset everything + // @performance could happen concurrently + { + int64_t count = 0; + count = registeredCursorPosEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredCursorPosEvents[i]; + ev.xMotion = 0; + ev.yMotion = 0; + } + count = registeredKeyEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredKeyEvents[i]; + // TODO the idea here was right, but wrong place and too wide a swath + // if (i < count - 1) ev.isPressed = false; + ev.pressCount = 0; + } + count = registeredMouseButtonEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredMouseButtonEvents[i]; + // TODO the idea here was right, but wrong place and too wide a swath + // if (i < count - 1) ev.isPressed = false; + ev.pressCount = 0; + } + count = registeredScrollEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredScrollEvents[i]; + ev.xMotion = 0; + ev.yMotion = 0; + } + } + + // handle unhandled events + // @performance cache action->event results + { + do { // while (0) + PkeCursorEnterEvent *primaryEvent = nullptr; + FindActivePkeInputAction_CursorEnter_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_CURSOR_ENTER_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + const auto &ev = unhandledCursorEnterEvents[unhandledCursorEnterEvents.Count() - 1]; + primaryEvent->isEntered = ev.entered; + lastCursorEntered = ev.entered; + } while (0); + do { // while (0) + PkeCursorPosEvent *primaryEvent = nullptr; + FindActivePkeInputAction_CursorPos_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + int64_t count = unhandledCursorPosEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledCursorPosEvents[i]; + primaryEvent->xMotion += ev.x - lastMousePos.x; + primaryEvent->yMotion += ev.y - lastMousePos.y; + lastMousePos.x = ev.x; + lastMousePos.y = ev.y; + } + } while (0); + do { // while (0) + int64_t count = unhandledKeyEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledKeyEvents[i]; + PkeKeyEvent *primaryEvent = nullptr; + FindActivePkeInputAction_Key_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = ev.button, + .mods = ev.mods, + }, primaryEvent); + if (primaryEvent == nullptr) { + continue; + } + if (ev.action == GLFW_PRESS) { + primaryEvent->isPressed = true; + } else if (ev.action == GLFW_RELEASE) { + if (primaryEvent->isPressed == true) { + primaryEvent->pressCount += 1; + } + primaryEvent->isPressed = false; + } else { + // repeat + primaryEvent->isPressed = true; + primaryEvent->pressCount += 1; + } + } + } while (0); + do { // while (0) + int64_t count = unhandledMouseButtonEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledMouseButtonEvents[i]; + PkeMouseButtonEvent *primaryEvent = nullptr; + FindActivePkeInputAction_MouseButton_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, + .button = ev.button, + .mods = ev.mods, + }, primaryEvent); + if (primaryEvent == nullptr) { + continue; + } + if (ev.action == GLFW_PRESS) { + primaryEvent->isPressed = true; + } else if (ev.action == GLFW_RELEASE) { + if (primaryEvent->isPressed == true) { + primaryEvent->pressCount += 1; + } + primaryEvent->isPressed = false; + } + } + } while (0); + do { // while (0) + PkeScrollEvent *primaryEvent = nullptr; + FindActivePkeInputAction_Scroll_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_SCROLL_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + int64_t count = unhandledScrollEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledScrollEvents[i]; + primaryEvent->xMotion += ev.x; + primaryEvent->yMotion += ev.y; + } + } while (0); + do { // while (0) + PkeWindowFocusEvent *primaryEvent = nullptr; + FindActivePkeInputAction_WindowFocus_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_WINDOW_FOCUS_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + const auto &ev = unhandledWindowFocusEvents[unhandledWindowFocusEvents.Count() - 1]; + primaryEvent->isFocused = ev.focused; + lastWindowFocus = ev.focused; + } while (0); + } + unhandledCursorEnterEvents.Resize(0); + unhandledCursorPosEvents.Resize(0); + unhandledKeyEvents.Resize(0); + unhandledMouseButtonEvents.Resize(0); + unhandledScrollEvents.Resize(0); + unhandledWindowFocusEvents.Resize(0); + + // set boolean events with latest data + // - we do this after processing events to make sure that if a set is + // unregistered, the appropriate values are propagated + // - theoretically this scenario could be handled on unregister, + // but I'm not sure which would be more performant + { + int64_t count = 0; + count = registeredCursorEnterEvents.Count(); + for (int64_t i = 0; i < count - 1; ++i) { + auto &ev = registeredCursorEnterEvents[i]; + ev.isEntered = lastCursorEntered; + } + count = registeredWindowFocusEvents.Count(); + for (int64_t i = 0; i < count - 1; ++i) { + auto &ev = registeredWindowFocusEvents[i]; + ev.isFocused = lastWindowFocus; } } - if (removeCount > 0) { - UnhandledPkeInputEvents.Remove(0, removeCount); +} + +PkeInputEventHolder PkeInput_Query(const char *actionName) { + PkeInputEventBase *ev = nullptr; + auto *action = FindActionByName(actionName); + InputEventHash type = InputEventHash{0}; + if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER)) { + PkeCursorEnterEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER, PkeCursorEnterEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS)) { + PkeCursorPosEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS, PkeCursorPosEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY)) { + PkeKeyEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_KEY, PkeKeyEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON)) { + PkeMouseButtonEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON, PkeMouseButtonEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL)) { + PkeScrollEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_SCROLL, PkeScrollEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS)) { + PkeWindowFocusEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS, PkeWindowFocusEvent>(action, event); + ev = event; } + return PkeInputEventHolder { + .type = type, + .ptr = ev, + }; } void CursorEnterCallback(GLFWwindow *window, int entered) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_CURSOR_ENTER; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = InputCursorEntered{static_cast<InputCursorEntered_T>(entered)}; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = 0; - ev.y = 0; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredCursorEnterEvents.Count()) { + auto &ev = unhandledCursorEnterEvents.Push(); + ev.entered = bool(entered); + } if (prevCursorEnterCallback) { prevCursorEnterCallback(window, entered); } } void CursorPosCallback(GLFWwindow *window, double xPos, double yPos) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_CURSOR_POS; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = xPos; - ev.y = yPos; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredCursorPosEvents.Count()) { + auto &ev = unhandledCursorPosEvents.Push(); + ev.x = xPos; + ev.y = yPos; + } if (prevCursorPosCallback) { prevCursorPosCallback(window, xPos, yPos); } } void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_KEY; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = InputKeyboardKeyAction{static_cast<InputKeyboardKeyAction_T>(action)}; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = 0; - ev.y = 0; - ev.button = key; - ev.scanCode = scancode; - ev.mods = mods; - ev.tickCount = 0; + if (registeredKeyEvents.Count()) { + auto &ev = unhandledKeyEvents.Push(); + ev.button = key; + ev.mods = mods & 0x0F; + ev.action = action; + } if (prevKeyCallback) { prevKeyCallback(window, key, scancode, action, mods); } } void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_MOUSE_BUTTON; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = InputMouseButtonAction{static_cast<InputMouseButtonAction_T>(action)}; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = 0; - ev.y = 0; - ev.button = button; - ev.scanCode = 0; - ev.mods = mods; - ev.tickCount = 0; + if (registeredMouseButtonEvents.Count()) { + auto &ev = unhandledMouseButtonEvents.Push(); + ev.button = button; + ev.mods = mods; + ev.action = action; + } if (prevMouseButtonCallback) { prevMouseButtonCallback(window, button, action, mods); } } void ScrollCallback(GLFWwindow *window, double xOffset, double yOffset) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_SCROLL; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = xOffset; - ev.y = yOffset; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredScrollEvents.Count()) { + auto &ev = unhandledScrollEvents.Push(); + ev.x = xOffset; + ev.y = yOffset; + } if (prevScrollCallback) { prevScrollCallback(window, xOffset, yOffset); } } void WindowFocusCallback(GLFWwindow *window, int focused) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_WINDOW_FOCUS; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = InputWindowFocused{static_cast<InputWindowFocused_T>(focused)}; - ev.x = 0; - ev.y = 0; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredWindowFocusEvents.Count()) { + auto &ev = unhandledWindowFocusEvents.Push(); + ev.focused = focused; + } if (prevWindowFocusCallback) { prevWindowFocusCallback(window, focused); } @@ -185,13 +548,154 @@ void PkeInput_Init() { prevWindowFocusCallback = glfwSetWindowFocusCallback(window, WindowFocusCallback); } +InputActionSetHandle PkeInput_RegisterSet(const PkeInputSet &set) { + InputActionSetHandle returnValue{static_cast<InputActionSetHandle_T>(registeredInputSets.Count())}; + registeredInputSets.Push(set); + return returnValue; +} + +void PkeInput_ActivateSet(InputActionSetHandle handle) { + InputActionSetHandle_T index{static_cast<InputActionSetHandle_T>(handle)}; + activeInputSetStack.Push(handle); + auto &set = registeredInputSets[index]; + for (int64_t i = 0; i < set.actionCount; ++i) { + PkeInputAction &action = set.actions[i]; + // TODO doesn't support multiple masks - how do we align multiple event types? + // EX: strafe with keyboard + joystick + // creating multiple events doesn't make sense + // - have to keep in sync - isPressed ? + // - caller has to handle multiple scenarios + // Maybe we need a unifying event, but that defeats the purpose of this refactor and wastes memory + // Not to mention that the caller still has to know how to handle the unified response + // Some combinations of event types cannot be combined, so the caller has to know to ignore certain values + // Current design also has flaws + // The caller explicitly casts to the correct event type because we know the event type. + // If bindings were loaded dynamically, we would have to handle every scenario explicitly for every action + // Godot has some similarities - is_action_pressed(name) could hide the type, and Godot expects the + // programmer to know the input type in order to handle + // If we went for a more query-based approach - PkeInput_GetPressCount(actionTitle) for example + // there might be fewer oddities + // Maybe you just have to restrict certain actions to certain input types + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + action.eventIndex = registeredCursorEnterEvents.Count(); + registeredCursorEnterEvents.Push(PkeCursorEnterEvent { + .sourceSet = handle, + .isEntered = lastCursorEntered, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + action.eventIndex = registeredCursorPosEvents.Count(); + registeredCursorPosEvents.Push(PkeCursorPosEvent { + .sourceSet = handle, + .xMotion = 0, + .yMotion = 0, + }); + glfwGetCursorPos(window, &lastMousePos.x, &lastMousePos.y); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + action.eventIndex = registeredKeyEvents.Count(); + registeredKeyEvents.Push(PkeKeyEvent { + .sourceSet = handle, + .button = action.primaryHash.button, + .mods = action.primaryHash.mods, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + action.eventIndex = registeredMouseButtonEvents.Count(); + registeredMouseButtonEvents.Push(PkeMouseButtonEvent { + .sourceSet = handle, + .button = action.primaryHash.button, + .mods = action.primaryHash.mods, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + action.eventIndex = registeredScrollEvents.Count(); + registeredScrollEvents.Push(PkeScrollEvent { + .sourceSet = handle, + .xMotion = 0, + .yMotion = 0, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + action.eventIndex = registeredWindowFocusEvents.Count(); + registeredWindowFocusEvents.Push(PkeWindowFocusEvent { + .sourceSet = handle, + .isFocused = lastWindowFocus, + }); + } + } +} + +void PkeInput_DeactivateSet(InputActionSetHandle handle) { + int64_t index = -1; + auto count = activeInputSetStack.Count(); + for (int64_t i = 0; i < count; ++i) { + if (activeInputSetStack[i] == handle) { + index = i; + break; + } + } + assert(index >= 0 && "PkeInput_DeactivateSet - expected InputActionSet to be active"); + assert(index == activeInputSetStack.Count() - 1 && "PkeInput_UnregisterSet - expected InputActionSet to be the last set active"); + InputActionSetHandle_T handleIndex{static_cast<InputActionSetHandle_T>(handle)}; + auto &set = registeredInputSets[handleIndex]; + for (int64_t i = set.actionCount - 1; i >= 0; --i) { + PkeInputAction &action = set.actions[i]; + // TODO doesn't support multiple masks - how do we align scroll + button + // WITHOUT having two events allocated? + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + registeredCursorEnterEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + registeredCursorPosEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + registeredKeyEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + registeredMouseButtonEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + registeredScrollEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + registeredWindowFocusEvents.Remove(action.eventIndex); + } + } + activeInputSetStack.Pop(); +} + +void PkeInput_UnregisterSet(InputActionSetHandle handle) { + InputActionSetHandle_T index{static_cast<InputActionSetHandle_T>(handle)}; + assert(index == registeredInputSets.Count() - 1 && "PkeInput_UnregisterSet - expected InputActionSet to be the last set registered"); + const auto &set = registeredInputSets[index]; + if (activeInputSetStack.Has(handle)) { + PkeInput_DeactivateSet(handle); + } + Pke_Delete<PkeInputAction>(set.actions, set.actionCount); + registeredInputSets.Remove(index); +} + void PkeInput_Teardown() { - UnhandledPkeInputEvents.~DynArray(); - if (prevWindowFocusCallback) { glfwSetWindowFocusCallback(window, prevWindowFocusCallback); } - if (prevScrollCallback) { glfwSetScrollCallback(window, prevScrollCallback); } - if (prevMouseButtonCallback) { glfwSetMouseButtonCallback(window, prevMouseButtonCallback); } - if (prevKeyCallback) { glfwSetKeyCallback(window, prevKeyCallback); } - if (prevCursorPosCallback) { glfwSetCursorPosCallback(window, prevCursorPosCallback); } - if (prevCursorEnterCallback) { glfwSetCursorEnterCallback(window, prevCursorEnterCallback); } + glfwSetWindowFocusCallback(window, prevWindowFocusCallback); + glfwSetScrollCallback(window, prevScrollCallback); + glfwSetMouseButtonCallback(window, prevMouseButtonCallback); + glfwSetKeyCallback(window, prevKeyCallback); + glfwSetCursorPosCallback(window, prevCursorPosCallback); + glfwSetCursorEnterCallback(window, prevCursorEnterCallback); + unhandledCursorPosEvents.~DynArray(); + unhandledCursorEnterEvents.~DynArray(); + unhandledKeyEvents.~DynArray(); + unhandledMouseButtonEvents.~DynArray(); + unhandledScrollEvents.~DynArray(); + unhandledWindowFocusEvents.~DynArray(); + registeredCursorEnterEvents.~DynArray(); + registeredCursorPosEvents.~DynArray(); + registeredKeyEvents.~DynArray(); + registeredMouseButtonEvents.~DynArray(); + registeredScrollEvents.~DynArray(); + registeredWindowFocusEvents.~DynArray(); + activeInputSetStack.~DynArray(); + registeredInputSets.~DynArray(); } diff --git a/src/player-input.hpp b/src/player-input.hpp index a5e64be..a44ae42 100644 --- a/src/player-input.hpp +++ b/src/player-input.hpp @@ -1,59 +1,128 @@ #ifndef PKE_PLAYER_INPUT_HPP #define PKE_PLAYER_INPUT_HPP -#include "dynamic-array.hpp" #include "game-type-defs.hpp" #include "macros.hpp" -TypeSafeInt_H(InputEventHash, int16_t, 0x0FFF); -TypeSafeInt_H(InputEventType, int16_t, 0x0FFF); -TypeSafeInt_H(InputCursorEntered, int16_t, 0x0FFF); -TypeSafeInt_H(InputKeyboardKeyAction, int16_t, 0x0FFF); -TypeSafeInt_H(InputMouseButtonAction, int16_t, 0x0FFF); -TypeSafeInt_H(InputWindowFocused, int16_t, 0x0FFF); - -const InputEventType PKE_INPUT_EVENT_TYPE_CURSOR_ENTER = InputEventType{0}; -const InputEventType PKE_INPUT_EVENT_TYPE_CURSOR_POS = InputEventType{1}; -const InputEventType PKE_INPUT_EVENT_TYPE_KEY = InputEventType{2}; -const InputEventType PKE_INPUT_EVENT_TYPE_MOUSE_BUTTON = InputEventType{3}; -const InputEventType PKE_INPUT_EVENT_TYPE_SCROLL = InputEventType{4}; -const InputEventType PKE_INPUT_EVENT_TYPE_WINDOW_FOCUS = InputEventType{5}; - -const InputCursorEntered PKE_INPUT_CURSOR_ENTERED_UNSET = InputCursorEntered{-1}; -const InputCursorEntered PKE_INPUT_CURSOR_ENTERED_FALSE = InputCursorEntered{0}; -const InputCursorEntered PKE_INPUT_CURSOR_ENTERED_TRUE = InputCursorEntered{1}; - -const InputKeyboardKeyAction PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET = InputKeyboardKeyAction{-1}; -const InputKeyboardKeyAction PKE_INPUT_KEYBOARD_KEY_ACTION_RELEASE = InputKeyboardKeyAction{0}; -const InputKeyboardKeyAction PKE_INPUT_KEYBOARD_KEY_ACTION_PRESS = InputKeyboardKeyAction{1}; -const InputKeyboardKeyAction PKE_INPUT_KEYBOARD_KEY_ACTION_REPEAT = InputKeyboardKeyAction{2}; - -const InputMouseButtonAction PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET = InputMouseButtonAction{-1}; -const InputMouseButtonAction PKE_INPUT_MOUSE_BUTTON_ACTION_RELEASE = InputMouseButtonAction{0}; -const InputMouseButtonAction PKE_INPUT_MOUSE_BUTTON_ACTION_PRESS = InputMouseButtonAction{1}; - -const InputWindowFocused PKE_INPUT_WINDOW_FOCUSED_UNSET = InputWindowFocused{-1}; -const InputWindowFocused PKE_INPUT_WINDOW_FOCUSED_FALSE = InputWindowFocused{0}; -const InputWindowFocused PKE_INPUT_WINDOW_FOCUSED_TRUE = InputWindowFocused{1}; - -struct PkeInputEvent { - InputEventType type; - GameTimePoint timePoint; - InputCursorEntered cursorEntered; - InputKeyboardKeyAction keyboardKeyAction; - InputMouseButtonAction mouseButtonAction; - InputWindowFocused windowFocused; - double x; - double y; +#include <cstdint> + +TypeSafeInt_H(InputActionSetHandle, uint8_t, 0xFF); + +TypeSafeInt_Const_Expr(InputEventHash, uint16_t, 0xFFFF); + +const InputEventHash PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER = InputEventHash {0x0001}; +const InputEventHash PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS = InputEventHash {0x0002}; +const InputEventHash PKE_INPUT_HASH_EVENT_TYPE_KEY = InputEventHash {0x0004}; +const InputEventHash PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON = InputEventHash {0x0008}; +const InputEventHash PKE_INPUT_HASH_EVENT_TYPE_SCROLL = InputEventHash {0x0010}; +const InputEventHash PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS = InputEventHash {0x0020}; +const InputEventHash PKE_INPUT_HASH_CURSOR_ENTERED_FALSE = InputEventHash {0x0040}; +const InputEventHash PKE_INPUT_HASH_CURSOR_ENTERED_TRUE = InputEventHash {0x0080}; +const InputEventHash PKE_INPUT_HASH_KEYBOARD_KEY_ACTION_RELEASE = InputEventHash{0x0100}; +const InputEventHash PKE_INPUT_HASH_KEYBOARD_KEY_ACTION_PRESS = InputEventHash {0x0200}; +const InputEventHash PKE_INPUT_HASH_KEYBOARD_KEY_ACTION_REPEAT = InputEventHash {0x0400}; +const InputEventHash PKE_INPUT_HASH_MOUSE_BUTTON_ACTION_RELEASE = InputEventHash{0x0800}; +const InputEventHash PKE_INPUT_HASH_MOUSE_BUTTON_ACTION_PRESS = InputEventHash {0x1000}; +const InputEventHash PKE_INPUT_HASH_WINDOW_FOCUSED_FALSE = InputEventHash {0x2000}; +const InputEventHash PKE_INPUT_HASH_WINDOW_FOCUSED_TRUE = InputEventHash {0x4000}; + +const InputEventHash PKE_INPUT_HASH_ALL_EVENTS = + PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER + | PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS + | PKE_INPUT_HASH_EVENT_TYPE_KEY + | PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON + | PKE_INPUT_HASH_EVENT_TYPE_SCROLL + | PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; +const InputEventHash PKE_INPUT_HASH_ALL_CURSOR_ENTER_EVENTS = + PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER + | PKE_INPUT_HASH_CURSOR_ENTERED_FALSE + | PKE_INPUT_HASH_CURSOR_ENTERED_TRUE; +const InputEventHash PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS = + PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; +const InputEventHash PKE_INPUT_HASH_ALL_KEY_EVENTS = + PKE_INPUT_HASH_EVENT_TYPE_KEY + | PKE_INPUT_HASH_KEYBOARD_KEY_ACTION_RELEASE + | PKE_INPUT_HASH_KEYBOARD_KEY_ACTION_PRESS + | PKE_INPUT_HASH_KEYBOARD_KEY_ACTION_REPEAT; +const InputEventHash PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS = + PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON + | PKE_INPUT_HASH_MOUSE_BUTTON_ACTION_RELEASE + | PKE_INPUT_HASH_MOUSE_BUTTON_ACTION_PRESS; +const InputEventHash PKE_INPUT_HASH_ALL_SCROLL_EVENTS = + PKE_INPUT_HASH_EVENT_TYPE_SCROLL; +const InputEventHash PKE_INPUT_HASH_ALL_WINDOW_FOCUS_EVENTS = + PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS + | PKE_INPUT_HASH_WINDOW_FOCUSED_FALSE + | PKE_INPUT_HASH_WINDOW_FOCUSED_FALSE; + +struct PkeInputEventBase { }; +struct PkeInputEventHolder { + InputEventHash type = InputEventHash{0}; + PkeInputEventBase *ptr = nullptr; +}; +struct PkeCursorEnterEvent : public PkeInputEventBase { + InputActionSetHandle sourceSet; + bool isEntered; +}; +struct PkeCursorPosEvent : public PkeInputEventBase { + InputActionSetHandle sourceSet; + double xMotion; + double yMotion; +}; +struct PkeKeyEvent : public PkeInputEventBase { + InputActionSetHandle sourceSet; + bool isPressed; + int8_t pressCount; + int32_t button; + int32_t mods; +}; +struct PkeMouseButtonEvent : public PkeInputEventBase { + InputActionSetHandle sourceSet; + bool isPressed; + int8_t pressCount; int32_t button; - int32_t scanCode; int32_t mods; - uint64_t tickCount; +}; +struct PkeScrollEvent : public PkeInputEventBase { + InputActionSetHandle sourceSet; + double xMotion; + double yMotion; +}; +struct PkeWindowFocusEvent : public PkeInputEventBase { + InputActionSetHandle sourceSet; + bool isFocused; +}; + +struct PkeInputEventMask { + InputEventHash computedHash = InputEventHash{0}; + int32_t button = -1; + int32_t mods = 0; +}; + +constexpr uint64_t PKE_INPUT_ACTION_MASK_COUNT = 1; +struct PkeInputAction { + const char *name; + union { + PkeInputEventMask masks[PKE_INPUT_ACTION_MASK_COUNT] = {{}}; + struct { + PkeInputEventMask primaryHash; + }; + }; + int32_t eventIndex; +}; +struct PkeInputSet { + const char *title; + uint64_t actionCount; + PkeInputAction *actions; }; void PkeInput_Tick(double delta); -bool PkeInput_Query(const PkeInputEvent &mask); +PkeInputEventHolder PkeInput_Query(const char *actionName); void PkeInput_Init(); +InputActionSetHandle PkeInput_RegisterSet(const PkeInputSet &set); +void PkeInput_ActivateSet(InputActionSetHandle handle); +void PkeInput_DeactivateSet(InputActionSetHandle handle); +void PkeInput_UnregisterSet(InputActionSetHandle handle); void PkeInput_Teardown(); #endif /* PKE_PLAYER_INPUT_HPP */ diff --git a/src/window.cpp b/src/window.cpp index 0dbd415..4c6b598 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2,6 +2,7 @@ #define GLFW_INCLUDE_VULKAN #include "asset-manager.hpp" +#include "camera.hpp" #include "ecs.hpp" #include "entities.hpp" #include "event.hpp" @@ -89,7 +90,7 @@ VkSemaphore presentRenderFinishedSemaphores[MAX_FRAMES_IN_FLIGHT]; VkFence presentInFlightFences[MAX_FRAMES_IN_FLIGHT]; UniformBufferObject UBO{ .model = glm::mat4(1), - .view = glm::lookAt(glm::vec3(0), glm::vec3(2, 2, 0), glm::vec3(0, 0, 1)), + .view = glm::lookAt(glm::vec3(0), glm::vec3(2, 2, 0), glm::vec3(0, 1, 0)), .proj = glm::mat4(1), }; VkDeviceMemory uniformBufferMemory; @@ -814,9 +815,27 @@ void UpdatePresentDescriptorSets() { vkUpdateDescriptorSets(vkDevice, MAX_FRAMES_IN_FLIGHT, writeDescriptorSets, 0, nullptr); } -void UpdateCameraProjection() { - UBO.proj = glm::perspective(glm::radians(45.f), Extent.width / (float)Extent.height, 0.1f, 100.f); - UBO.proj[1][1] *= -1; +void UpdateCamera() { + if (bool(ActiveCamera->stale & PKE_CAMERA_STALE_POS)) { + UBO.model = glm::translate(glm::mat4(1.f), ActiveCamera->pos); + } + if (bool(ActiveCamera->stale & PKE_CAMERA_STALE_ROT)) { + if (bool(ActiveCamera->orientation == PKE_CAMERA_ORIENTATION_FREE)) { + UBO.view = glm::mat4_cast(ActiveCamera->rot); + } else if (bool(ActiveCamera->orientation == PKE_CAMERA_ORIENTATION_TARGET)) { + UBO.view = glm::lookAt(glm::vec3(0), ActiveCamera->pos - ActiveCamera->target, glm::vec3(0.f, 1.f, 0.f)); + } + } + if (bool(ActiveCamera->stale & PKE_CAMERA_STALE_ORIENTATION)) { + if (bool(ActiveCamera->type == PKE_CAMERA_TYPE_PERSPECTIVE)) { + UBO.proj = glm::perspective(glm::radians(45.f), Extent.width / (float)Extent.height, 0.1f, 100.f); + UBO.proj[1][1] *= -1; + } else if (bool(ActiveCamera->type == PKE_CAMERA_TYPE_ORTHOGONAL)) { + UBO.proj = glm::ortho(0.f, (float)Extent.width, 0.f, (float)Extent.height, 0.f, 100.f); + UBO.proj[1][1] *= -1; + } + } + ActiveCamera->stale = PkeCameraStaleFlags{0}; } void CreateRenderPass() { @@ -2405,12 +2424,13 @@ void RecreateSwapchain() { } Extent.width = width; Extent.height = height; + ActiveCamera->stale = ActiveCamera->stale | PKE_CAMERA_STALE_ORIENTATION; DetermineMonitor(); vkDeviceWaitIdle(vkDevice); DestroySwapchain(); CreateSwapchain(); UpdatePresentDescriptorSets(); - UpdateCameraProjection(); + UpdateCamera(); CreateFramebuffers(); shouldRecreateSwapchain = false; } @@ -2433,7 +2453,7 @@ void CreateWindow(PKEWindowProperties *wp) { CreateRenderPass(); CreatePresentPipeline(); UpdatePresentDescriptorSets(); - UpdateCameraProjection(); + UpdateCamera(); CreateFramebuffers(); CreateCommandPool(); CreateCommandBuffer(); @@ -2538,6 +2558,7 @@ void Render() { // update uniform buffer { + UpdateCamera(); void *mappedUboMemory; vkMapMemory(vkDevice, uniformBufferMemory, paddedUboBufferSize * imageIndex, sizeof(UniformBufferObject), 0, &mappedUboMemory); memcpy(mappedUboMemory, &UBO, sizeof(UniformBufferObject)); |
