# HG changeset patch # User sam # Date 1715777483 -25200 # Node ID f888ac4825b8f3b7d3320ff0b26e63ea6a513da5 # Parent 73b572f82a1f811c8904b09462190b7959c8681c did: refactor input system, did some renaming, add quering of keys in key-value-store diff -r 73b572f82a1f -r f888ac4825b8 semicongine/core/buildconfig.nim --- a/semicongine/core/buildconfig.nim Thu May 09 23:02:35 2024 +0700 +++ b/semicongine/core/buildconfig.nim Wed May 15 19:51:23 2024 +0700 @@ -29,7 +29,7 @@ # root of where settings files will be searched # must be relative (to the directory of the binary) -const DEBUG* = not defined(release) +const DEBUG* {.booldefine.} = not defined(release) const CONFIGROOT* {.strdefine.}: string = "." assert not isAbsolute(CONFIGROOT) diff -r 73b572f82a1f -r f888ac4825b8 semicongine/engine.nim --- a/semicongine/engine.nim Thu May 09 23:02:35 2024 +0700 +++ b/semicongine/engine.nim Wed May 15 19:51:23 2024 +0700 @@ -34,7 +34,6 @@ Shutdown Engine* = object applicationName: string - debug: bool showFps: bool device: Device debugger: Debugger @@ -46,7 +45,7 @@ currentRenderTimeI: int = 0 # forward declarations -func getAspectRatio*(engine: Engine): float32 +func GetAspectRatio*(engine: Engine): float32 proc destroy*(engine: var Engine) = checkVkResult engine.device.vk.vkDeviceWaitIdle() @@ -63,7 +62,6 @@ proc initEngine*( applicationName = querySetting(projectName), - debug = DEBUG, showFps = DEBUG, vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), vulkanLayers: openArray[string] = [], @@ -78,7 +76,6 @@ echo "Starting without Steam" result.applicationName = applicationName - result.debug = debug result.showFps = showFps result.window = createWindow(result.applicationName) @@ -86,7 +83,7 @@ layers = @vulkanLayers instanceExtensions: seq[string] - if result.debug: + if DEBUG: instanceExtensions.add "VK_EXT_debug_utils" layers.add "VK_LAYER_KHRONOS_validation" # This stuff might be usefull if we one day to smart GPU memory allocation, @@ -99,7 +96,7 @@ instanceExtensions = instanceExtensions, layers = layers.deduplicate(), ) - if result.debug: + if DEBUG: result.debugger = result.instance.createDebugMessenger() # create devices let selectedPhysicalDevice = result.instance.getPhysicalDevices().filterBestGraphics() @@ -145,7 +142,7 @@ assert engine.renderer.isSome assert not scene.loaded checkVkResult engine.device.vk.vkDeviceWaitIdle() - scene.addShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.getAspectRatio) + scene.addShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.GetAspectRatio) engine.renderer.get.setupDrawableBuffers(scene) engine.renderer.get.updateMeshData(scene, forceAll = true) engine.renderer.get.updateUniformData(scene, forceAll = true) @@ -162,7 +159,7 @@ let t0 = getMonoTime() engine.renderer.get.startNewFrame() - scene.setShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.getAspectRatio) + scene.setShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.GetAspectRatio) engine.renderer.get.updateMeshData(scene) engine.renderer.get.updateUniformData(scene) engine.renderer.get.render(scene) @@ -182,25 +179,25 @@ # wrappers for internal things -func gpuDevice*(engine: Engine): Device = engine.device -func getWindow*(engine: Engine): auto = engine.window -func getAspectRatio*(engine: Engine): float32 = engine.getWindow().size[0] / engine.getWindow().size[1] -func showSystemCursor*(engine: Engine) = engine.window.showSystemCursor() -func hideSystemCursor*(engine: Engine) = engine.window.hideSystemCursor() -func fullscreen*(engine: Engine): bool = engine.fullscreen -proc `fullscreen=`*(engine: var Engine, enable: bool) = +func GpuDevice*(engine: Engine): Device = engine.device +func GetWindow*(engine: Engine): auto = engine.window +func GetAspectRatio*(engine: Engine): float32 = engine.GetWindow().size[0] / engine.GetWindow().size[1] +func ShowSystemCursor*(engine: Engine) = engine.window.showSystemCursor() +func HideSystemCursor*(engine: Engine) = engine.window.hideSystemCursor() +func Fullscreen*(engine: Engine): bool = engine.fullscreen +proc `Fullscreen=`*(engine: var Engine, enable: bool) = if enable != engine.fullscreen: engine.fullscreen = enable engine.window.fullscreen(engine.fullscreen) -func limits*(engine: Engine): VkPhysicalDeviceLimits = - engine.gpuDevice().physicalDevice.properties.limits +func Limits*(engine: Engine): VkPhysicalDeviceLimits = + engine.device.physicalDevice.properties.limits -proc updateInputs*(engine: Engine): bool = - updateInputs(engine.window.pendingEvents()) +proc UpdateInputs*(engine: Engine): bool = + UpdateInputs(engine.window.pendingEvents()) -proc processEvents*(engine: Engine, panel: var Panel) = - let hasMouseNow = panel.contains(mousePositionNormalized(engine.window.size), engine.getAspectRatio) +proc ProcessEvents*(engine: Engine, panel: var Panel) = + let hasMouseNow = panel.contains(MousePositionNormalized(engine.window.size), engine.GetAspectRatio) # enter/leave events if hasMouseNow: @@ -214,9 +211,9 @@ # button events if hasMouseNow: - if input.mouseWasPressed(): - if panel.onMouseDown != nil: panel.onMouseDown(panel, input.mousePressedButtons()) - if input.mouseWasReleased(): - if panel.onMouseUp != nil: panel.onMouseUp(panel, input.mouseReleasedButtons()) + if MouseWasPressed(): + if panel.onMouseDown != nil: panel.onMouseDown(panel, MousePressedButtons()) + if MouseWasReleased(): + if panel.onMouseUp != nil: panel.onMouseUp(panel, MouseReleasedButtons()) panel.hasMouse = hasMouseNow diff -r 73b572f82a1f -r f888ac4825b8 semicongine/input.nim --- a/semicongine/input.nim Thu May 09 23:02:35 2024 +0700 +++ b/semicongine/input.nim Wed May 15 19:51:23 2024 +0700 @@ -1,33 +1,13 @@ # Linux joystick: https://www.kernel.org/doc/Documentation/input/joystick-api.txt # Windows joystick: https://learn.microsoft.com/en-us/windows/win32/xinput/getting-started-with-xinput -# -# API to define actions that are connected to user inputs -# -# Example: -# -# type -# Action = enum -# Jump -# Left -# Right -# -# AddAction(Jump, SpaceDown, Pressed) # trigger action -# AddAction(Left, Arrow, Down) # boolean action -# AddAction(Left, Joystick_Left, Axis) # axis action -# -# -# -# -# if Action(Jump).Triggered: -# accel_y = 1 -# if Action(Left).Active: -# accel_y = 1 -# if Action(Left).Value: -# accel_y = 1 + +import std/tables +import std/strutils import ./core/vector import ./events +import ./storage type Input = object @@ -42,9 +22,10 @@ mouseWheel: float32 windowWasResized: bool = true -var input*: Input +# warning, shit is not thread safe +var input: Input -proc updateInputs*(events: seq[Event]): bool = +proc UpdateInputs*(events: seq[Event]): bool = # reset input states input.keyWasPressed = {} input.keyWasReleased = {} @@ -81,21 +62,115 @@ input.mouseWheel = event.amount return not killed -proc keyIsDown*(key: Key): bool = key in input.keyIsDown -proc keyWasPressed*(key: Key): bool = key in input.keyWasPressed -proc keyWasPressed*(): bool = input.keyWasPressed.len > 0 -proc keyWasReleased*(key: Key): bool = key in input.keyWasReleased -proc mouseIsDown*(button: MouseButton): bool = button in input.mouseIsDown -proc mouseWasPressed*(): bool = input.mouseWasPressed.len > 0 -proc mouseWasPressed*(button: MouseButton): bool = button in input.mouseWasPressed -proc mousePressedButtons*(): set[MouseButton] = input.mouseWasPressed -proc mouseWasReleased*(): bool = input.mouseWasReleased.len > 0 -proc mouseWasReleased*(button: MouseButton): bool = button in input.mouseWasReleased -proc mouseReleasedButtons*(): set[MouseButton] = input.mouseWasReleased -proc mousePosition*(): Vec2f = input.mousePosition -proc mousePositionNormalized*(size: (int, int)): Vec2f = +proc KeyIsDown*(key: Key): bool = key in input.keyIsDown +proc KeyWasPressed*(key: Key): bool = key in input.keyWasPressed +proc KeyWasPressed*(): bool = input.keyWasPressed.len > 0 +proc KeyWasReleased*(key: Key): bool = key in input.keyWasReleased +proc MouseIsDown*(button: MouseButton): bool = button in input.mouseIsDown +proc MouseWasPressed*(): bool = input.mouseWasPressed.len > 0 +proc MouseWasPressed*(button: MouseButton): bool = button in input.mouseWasPressed +proc MousePressedButtons*(): set[MouseButton] = input.mouseWasPressed +proc MouseWasReleased*(): bool = input.mouseWasReleased.len > 0 +proc MouseWasReleased*(button: MouseButton): bool = button in input.mouseWasReleased +proc MouseReleasedButtons*(): set[MouseButton] = input.mouseWasReleased +proc MousePosition*(): Vec2f = input.mousePosition +proc MousePositionNormalized*(size: (int, int)): Vec2f = result.x = (input.mousePosition.x / float32(size[0])) * 2.0 - 1.0 result.y = (input.mousePosition.y / float32(size[1])) * 2.0 - 1.0 -proc mouseMove*(): auto = input.mouseMove -proc mouseWheel*(): auto = input.mouseWheel -proc windowWasResized*(): auto = input.windowWasResized +proc MouseMove*(): auto = input.mouseMove +proc MouseWheel*(): auto = input.mouseWheel +proc WindowWasResized*(): auto = input.windowWasResized + +# actions as a slight abstraction over raw input + +type + ActionMap = object + keyActions: Table[string, set[Key]] + mouseActions: Table[string, set[MouseButton]] + +# warning, shit is not thread safe +var actionMap: ActionMap + +proc MapAction*[T: enum](action: T, key: Key) = + if not actionMap.keyActions.contains($action): + actionMap.keyActions[$action] = {} + actionMap.keyActions[$action].incl key + +proc MapAction*[T: enum](action: T, button: MouseButton) = + if not actionMap.mouseActions.contains($action): + actionMap.mouseActions[$action] = {} + actionMap.mouseActions[$action].incl button + +proc MapAction*[T: enum](action: T, keys: openArray[Key|MouseButton]) = + for key in keys: + MapAction(action, key) + +proc UnmapAction*[T: enum](action: T, key: Key) = + if actionMap.keyActions.contains($action): + actionMap.keyActions[$action].excl(key) + +proc UnmapAction*[T: enum](action: T, button: MouseButton) = + if actionMap.mouseActions.contains($action): + actionMap.mouseActions[$action].excl(button) + +proc UnmapAction*[T: enum](action: T) = + if actionMap.keyActions.contains($action): + actionMap.keyActions[$action] = {} + if actionMap.mouseActions.contains($action): + actionMap.mouseActions[$action] = {} + +proc SaveCurrentActionMapping*() = + for name, keys in actionMap.keyActions.pairs: + SystemStorage.store(name, keys, table = "input_mapping_key") + for name, buttons in actionMap.mouseActions.pairs: + SystemStorage.store(name, buttons, table = "input_mapping_mouse") + +proc LoadActionMapping*[T]() = + reset(actionMap) + for name in SystemStorage.list(table = "input_mapping_key"): + let action = parseEnum[T](name) + let keys = SystemStorage.load(name, set[Key](), table = "input_mapping_key") + for key in keys: + MapAction(action, key) + +proc ActionDown*[T](action: T): bool = + if actionMap.keyActions.contains($action): + for key in actionMap.keyActions[$action]: + if key in input.keyIsDown: + return true + return false + if actionMap.mouseActions.contains($action): + for button in actionMap.mouseActions[$action]: + if button in input.mouseIsDown: + return true + return false + +proc ActionPressed*[T](action: T): bool = + if actionMap.keyActions.contains($action): + for key in actionMap.keyActions[$action]: + if key in input.keyWasPressed: + return true + elif actionMap.mouseActions.contains($action): + for button in actionMap.mouseActions[$action]: + if button in input.mouseWasPressed: + return true + +proc ActionReleased*[T](action: T): bool = + if actionMap.keyActions.contains($action): + for key in actionMap.keyActions[$action]: + if key in input.keyWasReleased: + return true + elif actionMap.mouseActions.contains($action): + for button in actionMap.mouseActions[$action]: + if button in input.mouseWasReleased: + return true + +proc ActionValue*[T](action: T): float32 = + if actionMap.keyActions.contains($action): + for key in actionMap.keyActions[$action]: + if key in input.keyIsDown: + return 1 + elif actionMap.mouseActions.contains($action): + for button in actionMap.mouseActions[$action]: + if button in input.mouseIsDown: + return 1 diff -r 73b572f82a1f -r f888ac4825b8 semicongine/storage.nim --- a/semicongine/storage.nim Thu May 09 23:02:35 2024 +0700 +++ b/semicongine/storage.nim Wed May 15 19:51:23 2024 +0700 @@ -9,7 +9,7 @@ import ./core const STORAGE_NAME = Path("storage.db") -const KEY_VALUE_TABLE_NAME = "shelf" +const DEFAULT_KEY_VALUE_TABLE_NAME = "shelf" type StorageType* = enum @@ -27,29 +27,35 @@ string(Path(getDataDir()) / Path(AppName())).createDir() Path(getDataDir()) / Path(AppName()) / STORAGE_NAME -proc setup(storageType: StorageType) = +proc ensureExists(storageType: StorageType) = if storageType in db: return db[storageType] = open(string(storageType.path), "", "", "") - db[storageType].exec(sql(&"""CREATE TABLE IF NOT EXISTS {KEY_VALUE_TABLE_NAME} ( + +proc ensureExists(storageType: StorageType, table: string) = + storageType.ensureExists() + db[storageType].exec(sql(&"""CREATE TABLE IF NOT EXISTS {table} ( key TEXT NOT NULL UNIQUE, value TEXT NOT NULL )""")) -proc store*[T](storageType: StorageType, key: string, value: T) = - storageType.setup() - const KEY_VALUE_TABLE_NAME = "shelf" - db[storageType].exec(sql(&"""INSERT INTO {KEY_VALUE_TABLE_NAME} VALUES(?, ?) +proc store*[T](storageType: StorageType, key: string, value: T, table = DEFAULT_KEY_VALUE_TABLE_NAME) = + storageType.ensureExists(table) + db[storageType].exec(sql(&"""INSERT INTO {table} VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value """), key, $$value) -proc load*[T](storageType: StorageType, key: string, default: T): T = - storageType.setup() - const KEY_VALUE_TABLE_NAME = "shelf" - let dbResult = db[storageType].getValue(sql(&"""SELECT value FROM {KEY_VALUE_TABLE_NAME} WHERE key = ? """), key) +proc load*[T](storageType: StorageType, key: string, default: T, table = DEFAULT_KEY_VALUE_TABLE_NAME): T = + storageType.ensureExists(table) + let dbResult = db[storageType].getValue(sql(&"""SELECT value FROM {table} WHERE key = ? """), key) if dbResult == "": return default return to[T](dbResult) +proc list*[T](storageType: StorageType, table = DEFAULT_KEY_VALUE_TABLE_NAME): seq[string] = + storageType.ensureExists(table) + for row in db[storageType].fastRows(sql(&"""SELECT key FROM {table}""")): + result.add row[0] + proc purge*(storageType: StorageType) = storageType.path().string.removeFile()