summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2023-11-15 13:03:48 -0500
committerJonathan Bradley <jcb@pikum.xyz>2023-11-15 13:03:48 -0500
commit1513216876a0409f45d88cbad14ae8b48fca37e2 (patch)
tree32b48d966faf36138dd985e4df739a83cddc5f48
parent6a30d21c5175c27aeccc8a6ae956c274f3ef9621 (diff)
major input refactor and add debug camera
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/camera.cpp13
-rw-r--r--src/camera.hpp33
-rw-r--r--src/game-settings.hpp1
-rw-r--r--src/game.cpp258
-rw-r--r--src/player-input.cpp762
-rw-r--r--src/player-input.hpp157
-rw-r--r--src/window.cpp33
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 = &registeredCursorEnterEvents[inputAction->eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) {
+ activeEvent = &registeredCursorPosEvents[inputAction->eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) {
+ activeEvent = &registeredKeyEvents[inputAction->eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_KEY;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) {
+ activeEvent = &registeredMouseButtonEvents[inputAction->eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) {
+ activeEvent = &registeredScrollEvents[inputAction->eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_SCROLL;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) {
+ activeEvent = &registeredWindowFocusEvents[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 = &registeredCursorEnterEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) {
+ activeEvent = &registeredCursorPosEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) {
+ activeEvent = &registeredKeyEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_KEY;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) {
+ activeEvent = &registeredMouseButtonEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) {
+ activeEvent = &registeredScrollEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_SCROLL;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) {
+ activeEvent = &registeredWindowFocusEvents[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 = &registeredCursorEnterEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) {
+ activeEvent = &registeredCursorPosEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) {
+ activeEvent = &registeredKeyEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_KEY;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) {
+ activeEvent = &registeredMouseButtonEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) {
+ activeEvent = &registeredScrollEvents[inputAction.eventIndex];
+ return PKE_INPUT_HASH_EVENT_TYPE_SCROLL;
+ }
+ if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) {
+ activeEvent = &registeredWindowFocusEvents[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));