From 3f07c03fa51162cee4dbe53abf4a958bb6c35cda Mon Sep 17 00:00:00 2001 From: Noel Berry Date: Sat, 20 Mar 2021 19:23:20 -0700 Subject: [PATCH] new input binding/mapping implementation --- CMakeLists.txt | 8 +- include/blah.h | 5 +- include/blah/input/binding.h | 261 ++++++++++++++ include/blah/input/binding_registry.h | 37 ++ include/blah/input/virtual_axis.h | 86 ----- include/blah/input/virtual_button.h | 83 ----- include/blah/input/virtual_stick.h | 97 ----- src/input/binding.cpp | 492 ++++++++++++++++++++++++++ src/input/binding_registry.cpp | 70 ++++ src/input/input.cpp | 4 + src/input/virtual_axis.cpp | 166 --------- src/input/virtual_button.cpp | 174 --------- src/input/virtual_stick.cpp | 191 ---------- 13 files changed, 869 insertions(+), 805 deletions(-) create mode 100644 include/blah/input/binding.h create mode 100644 include/blah/input/binding_registry.h delete mode 100644 include/blah/input/virtual_axis.h delete mode 100644 include/blah/input/virtual_button.h delete mode 100644 include/blah/input/virtual_stick.h create mode 100644 src/input/binding.cpp create mode 100644 src/input/binding_registry.cpp delete mode 100644 src/input/virtual_axis.cpp delete mode 100644 src/input/virtual_button.cpp delete mode 100644 src/input/virtual_stick.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e7239a2..85821ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ add_library(blah src/core/app.cpp src/core/filesystem.cpp - src/core/log.cpp + src/core/common.cpp src/core/time.cpp src/graphics/blend.cpp @@ -21,9 +21,8 @@ add_library(blah src/graphics/texture.cpp src/input/input.cpp - src/input/virtual_stick.cpp - src/input/virtual_button.cpp - src/input/virtual_axis.cpp + src/input/binding.cpp + src/input/binding_registry.cpp src/containers/str.cpp @@ -54,7 +53,6 @@ add_library(blah src/streams/memorystream.cpp src/streams/stream.cpp - src/internal/graphics_backend_gl.cpp src/internal/graphics_backend_d3d11.cpp src/internal/graphics_backend_dummy.cpp diff --git a/include/blah.h b/include/blah.h index b44430f..d2084cd 100644 --- a/include/blah.h +++ b/include/blah.h @@ -28,9 +28,8 @@ #include "blah/images/packer.h" #include "blah/input/input.h" -#include "blah/input/virtual_stick.h" -#include "blah/input/virtual_button.h" -#include "blah/input/virtual_axis.h" +#include "blah/input/binding.h" +#include "blah/input/binding_registry.h" #include "blah/math/calc.h" #include "blah/math/circle.h" diff --git a/include/blah/input/binding.h b/include/blah/input/binding.h new file mode 100644 index 0000000..f657506 --- /dev/null +++ b/include/blah/input/binding.h @@ -0,0 +1,261 @@ +#pragma once +#include +#include +#include +#include + +namespace Blah +{ + // Represents a Controller Trigger or a single direction of a Controller Axis. + // Used in the Binding implementation. + struct BoundTrigger + { + // Controller Index we're bound to + int controller = 0; + + // The Axis we're bound to + Axis axis = Axis::None; + + // Minimum value of the axis + float threshold = 0.01f; + + // requires a positive value + // otherwise requires a negative value + bool positive = true; + + BoundTrigger() = default; + BoundTrigger(Axis axis); + BoundTrigger(int controller, Axis axis, float threshold, bool positive); + + bool is_down(float axis_value) const; + + // helper functions for frequent use cases + static BoundTrigger left_stick_left(int controller, float threshold); + static BoundTrigger left_stick_right(int controller, float threshold); + static BoundTrigger left_stick_up(int controller, float threshold); + static BoundTrigger left_stick_down(int controller, float threshold); + static BoundTrigger right_stick_left(int controller, float threshold); + static BoundTrigger right_stick_right(int controller, float threshold); + static BoundTrigger right_stick_up(int controller, float threshold); + static BoundTrigger right_stick_down(int controller, float threshold); + static BoundTrigger left_trigger(int controller, float threshold); + static BoundTrigger right_trigger(int controller, float threshold); + }; + + struct BoundButton + { + // Controller Index we're bound to + int controller = 0; + + // Button we're bound to + Button button = Button::None; + + BoundButton() = default; + BoundButton(Button button); + BoundButton(int controller, Button button); + }; + + // Single input Binding + class Binding + { + public: + + // Input Buffer for press events + float press_buffer = 0; + + // Input Buffer for release events + float release_buffer = 0; + + // List of bound Keys + StackVector keys; + + // List of bound Buttons + StackVector buttons; + + // List of bound Triggers / Axis + StackVector triggers; + + // List of bound Mouse buttons + StackVector mouse; + + // if the binding has been pressed + bool pressed() const; + + // if the binding has been released + bool released() const; + + // if the binding is currently held + bool down() const; + + // returns the binding's value from 0-1 + float value() const; + + // returns the bindings signed value (0 or 1) + int sign() const; + + // returns the timestamp of the last time the binding was pressed + double timestamp() const; + + // updates the binding state + void update(); + + // consumes the current press, and pressed() will return false until the next press + void consume_press(); + + // consumes the current release, and released() will return false until the next release + void consume_release(); + + // adds a key to the binding + void add(Key key); + + // adds a button to the binding + void add(BoundButton button); + + // adds an trigger to the binding + void add(BoundTrigger trigger); + + // adds a mouse button to the binding + void add(MouseButton mouse); + + // adds an input to the binding + template + void add(T first, T2 second, const Args&... args) + { + add(first); + add(second, args...); + } + + // assigns all the bindings to the specific controller + void set_controller(int index); + + // removes all bindings + void clear(); + + private: + double m_last_timestamp = 0; + double m_last_press_time = -1; + double m_last_release_time = -1; + float m_value = 0.0f; + bool m_pressed = false; + bool m_released = false; + bool m_down = false; + bool m_press_consumed = false; + bool m_release_consumed = false; + + bool get_pressed() const; + bool get_released() const; + bool get_down() const; + float get_value() const; + }; + + // Represents a Bound Axis (ex. Left/Right movement, or a Trigger) + class AxisBinding + { + public: + + enum class Overlap + { + Newer, + Older, + Cancel + }; + + // Negative Value Binding + Binding negative; + + // Positive Value Binding + Binding positive; + + // How to handle overlaps (ex. Left and Right are both held) + Overlap overlap = Overlap::Newer; + + // Current Value from -1 to 1 + float value() const; + + // Current value, either -1, 0, or 1 + int sign() const; + + // updates the Binding + void update(); + + // consumes the press buffer + void consume_press(); + + // consumes the release buffer + void consume_release(); + + // Adds a negative & positive binding pair + template + void add(NegativeT negative, PositiveT positive) + { + this->negative.add(negative); + this->positive.add(positive); + } + + // Adds a Stick binding + void add_left_stick_x(int controller, float threshold); + void add_left_stick_y(int controller, float threshold); + void add_right_stick_x(int controller, float threshold); + void add_right_stick_y(int controller, float threshold); + + // assigns all the bindings to the specific controller + void set_controller(int index); + + // Clears all Bindings + void clear(); + }; + + class StickBinding + { + public: + + // An optional threshold for circular thresholds + float round_threshold = 0.0f; + + // X Axis Binding + AxisBinding x; + + // Y Axis Binding + AxisBinding y; + + // Current Value, -1 to 1 + Vec2 value() const; + + // Current value, either -1, 0, or 1 + Point sign() const; + + // Updates the Binding + void update(); + + // Consumes the Press Buffer + void consume_press(); + + // Consumes the Release Buffer + void consume_release(); + + // Adds directional bindings + template + void add(LeftT left, RightT right, UpT up, DownT down) + { + x.negative.add(left); + x.positive.add(right); + y.negative.add(up); + y.positive.add(down); + } + + // Adds the dpad binding + void add_dpad(int controller); + + // Adds the left stick binding + void add_left_stick(int controller, float threshold); + + // Adds the right stick binding + void add_right_stick(int controller, float threshold); + + // assigns all the bindings to the specific controller + void set_controller(int index); + + // Clears all the bindings + void clear(); + }; +} \ No newline at end of file diff --git a/include/blah/input/binding_registry.h b/include/blah/input/binding_registry.h new file mode 100644 index 0000000..73c7660 --- /dev/null +++ b/include/blah/input/binding_registry.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +namespace Blah +{ + using BindingRef = std::shared_ptr; + using AxisBindingRef = std::shared_ptr; + using StickBindingRef = std::shared_ptr; + + // Optional registry to automatically update bindings. + // You can register different types of bindings here and until they are + // no longer used, they will be updated without having to explicitely call + // their update methods + class BindingRegistry + { + public: + // registers a new binding + static BindingRef register_binding(); + + // registers a new axis binding + static AxisBindingRef register_axis(); + + // registers a new stick binding + static StickBindingRef register_stick(); + + // updates all the bindings. This is called + // automatically by the App loop. + static void update(); + + private: + static Vector> bindings; + static Vector> axes; + static Vector> sticks; + }; +} \ No newline at end of file diff --git a/include/blah/input/virtual_axis.h b/include/blah/input/virtual_axis.h deleted file mode 100644 index 4a026ae..0000000 --- a/include/blah/input/virtual_axis.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once -#include - -namespace Blah -{ - // A virtual controller axis, which can be used to map multiple - // inputs to an axis. Note that you must call `update` every frame! - class VirtualAxis - { - private: - struct KeysNode - { - Key positive = Key::Unknown; - Key negative = Key::Unknown; - - int value = 0; - - void init(Key negative, Key positive); - void update(); - }; - - struct ButtonsNode - { - int gamepad_id = 0; - Button positive = Button::None; - Button negative = Button::None; - - int value = 0; - - void init(int gamepad_id, Button negative, Button positive); - void update(); - }; - - struct AxisNode - { - int gamepad_id = 0; - Axis axis = Axis::None; - float deadzone = 0; - - float value = 0; - - void init(int gamepad_id, Axis axis, float deadzone); - void update(); - }; - - KeysNode m_keys[Input::max_virtual_nodes]; - ButtonsNode m_buttons[Input::max_virtual_nodes]; - AxisNode m_axes[Input::max_virtual_nodes]; - int m_keys_len = 0; - int m_buttons_len = 0; - int m_axes_len = 0; - - float m_press_buffer = 0; - float m_release_buffer = 0; - float m_repeat_delay = 0; - float m_repeat_interval = 0; - - float m_value = 0; - int m_value_i = 0; - float m_last_value = 0; - int m_last_value_i = 0; - bool m_pressed = false; - bool m_released = false; - double m_last_press_time = -1; - double m_last_release_time = -1; - double m_repeat_press_time = -1; - - public: - VirtualAxis& add_keys(Key negative, Key positive); - VirtualAxis& add_buttons(int gamepad_id, Button negative, Button positive); - VirtualAxis& add_axis(int gamepad_id, Axis axis, float deadzone); - VirtualAxis& repeat(float m_repeat_delay, float m_repeat_interval); - VirtualAxis& press_buffer(float duration); - VirtualAxis& release_buffer(float duration); - - void update(); - float value() const { return m_value; } - int value_i() const { return m_value_i; } - float last_value() const { return m_last_value; } - int last_value_i() const { return m_last_value_i; } - bool pressed() const { return m_pressed; } - bool released() const { return m_released; } - void clear_press_buffer() { m_last_press_time = -1; m_pressed = false; } - void clear_release_buffer() { m_last_release_time = -1; m_released = false; } - }; -} \ No newline at end of file diff --git a/include/blah/input/virtual_button.h b/include/blah/input/virtual_button.h deleted file mode 100644 index f0f8ac0..0000000 --- a/include/blah/input/virtual_button.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once -#include - -namespace Blah -{ - class VirtualButton - { - private: - struct KeyNode - { - Key key = Key::Unknown; - - bool down = false; - bool pressed = false; - bool released = false; - - void init(Key key); - void update(); - }; - - struct ButtonNode - { - int gamepad_id = 0; - Button button = Button::None; - - bool down = false; - bool pressed = false; - bool released = false; - - void init(int gamepad_id, Button button); - void update(); - }; - - struct AxisNode - { - int gamepad_id = 0; - Axis axis = Axis::None; - float threshold = 0; - bool greater_than = false; - - bool down = false; - bool pressed = false; - bool released = false; - - void init(int gamepad_id, Axis axis, float threshold, bool greater_than); - void update(); - }; - - KeyNode m_keys[Input::max_virtual_nodes]; - ButtonNode m_buttons[Input::max_virtual_nodes]; - AxisNode m_axes[Input::max_virtual_nodes]; - int m_keys_len = 0; - int m_buttons_len = 0; - int m_axes_len = 0; - - float m_press_buffer = 0; - float m_release_buffer = 0; - float m_repeat_delay = 0; - float m_repeat_interval = 0; - - bool m_down = false; - bool m_pressed = false; - bool m_released = false; - double m_last_press_time = -1; - double m_last_release_time = -1; - double m_repeat_press_time = -1; - - public: - VirtualButton& add_key(Key key); - VirtualButton& add_button(int gamepad_id, Button button); - VirtualButton& add_axis(int gamepad_id, Axis axis, float threshold, bool greater_than); - VirtualButton& repeat(float m_repeat_delay, float m_repeat_interval); - VirtualButton& press_buffer(float duration); - VirtualButton& release_buffer(float duration); - - void update(); - bool down() const { return m_down; } - bool pressed() const { return m_pressed; } - bool released() const { return m_released; } - void clear_press_buffer() { m_last_press_time = -1; m_pressed = false; } - void clear_release_buffer() { m_last_release_time = -1; m_released = false; } - }; -} \ No newline at end of file diff --git a/include/blah/input/virtual_stick.h b/include/blah/input/virtual_stick.h deleted file mode 100644 index a0bb0a8..0000000 --- a/include/blah/input/virtual_stick.h +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once -#include -#include -#include - -namespace Blah -{ - // A virtual controller stick, which can be used to map multiple - // inputs to a stick. Note that you must call `update` every frame! - class VirtualStick - { - private: - struct KeysNode - { - Key left; - Key right; - Key up; - Key down; - - Point value; - - void init(Key left, Key right, Key up, Key down); - void update(); - }; - - struct ButtonsNode - { - int gamepad_id; - Button left; - Button right; - Button up; - Button down; - - Point value; - - void init(int gamepad_id, Button left, Button right, Button up, Button down); - void update(); - }; - - struct AxesNode - { - int gamepad_id; - Axis horizontal; - Axis vertical; - float deadzone; - - Vec2 value; - - void init(int gamepad_id, Axis horizontal, Axis vertical, float deadzone); - void update(); - }; - - KeysNode m_keys[Input::max_virtual_nodes]; - ButtonsNode m_buttons[Input::max_virtual_nodes]; - AxesNode m_axes[Input::max_virtual_nodes]; - int m_keys_len = 0; - int m_buttons_len = 0; - int m_axes_len = 0; - - float m_press_buffer = 0; - float m_release_buffer = 0; - float m_repeat_delay = 0; - float m_repeat_interval = 0; - - Vec2 m_value = Vec2(); - Point m_value_i = Point(); - Vec2 m_last_value = Vec2(); - Point m_last_value_i = Point(); - bool m_pressed = false; - bool m_released = false; - - float m_i_deadzone; - double m_last_press_time = -1; - double m_last_release_time = -1; - double m_repeat_press_time = -1; - - public: - VirtualStick(); - VirtualStick(float iDeadzone); - VirtualStick& add_keys(Key left, Key right, Key up, Key down); - VirtualStick& add_buttons(int gamepad_id, Button left, Button right, Button up, Button down); - VirtualStick& add_axes(int gamepad_id, Axis horizontal, Axis vertical, float deadzone); - VirtualStick& repeat(float m_repeat_delay, float m_repeat_interval); - VirtualStick& press_buffer(float duration); - VirtualStick& release_buffer(float duration); - - void update(); - const Vec2& value() const { return m_value; } - const Point& value_i() const { return m_value_i; } - const Vec2& last_value() const { return m_last_value; } - const Point& last_value_i() const { return m_last_value_i; } - bool pressed() const { return m_pressed; } - bool released() const { return m_released; } - void clear_press_buffer() { m_last_press_time = 0; } - void clear_release_buffer() { m_last_release_time = 0; } - }; -} \ No newline at end of file diff --git a/src/input/binding.cpp b/src/input/binding.cpp new file mode 100644 index 0000000..dbb4385 --- /dev/null +++ b/src/input/binding.cpp @@ -0,0 +1,492 @@ +#include +#include +#include + +using namespace Blah; + +BoundTrigger::BoundTrigger(Axis axis) + : axis(axis) +{ + +} + +BoundTrigger::BoundTrigger(int controller, Axis axis, float threshold, bool positive) + : controller(controller), axis(axis), threshold(threshold), positive(positive) +{ + +} + +bool BoundTrigger::is_down(float axis_value) const +{ + if ((axis_value > 0 && positive) || (axis_value < 0 && !positive)) + { + if (Calc::abs(axis_value) >= threshold) + return true; + } + + return false; +} + +BoundTrigger BoundTrigger::left_stick_left(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::LeftX, threshold, false); +} + +BoundTrigger BoundTrigger::left_stick_right(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::LeftX, threshold, true); +} + +BoundTrigger BoundTrigger::left_stick_up(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::LeftY, threshold, false); +} + +BoundTrigger BoundTrigger::left_stick_down(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::LeftY, threshold, true); +} + +BoundTrigger BoundTrigger::right_stick_left(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::RightX, threshold, false); +} + +BoundTrigger BoundTrigger::right_stick_right(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::RightX, threshold, true); +} + +BoundTrigger BoundTrigger::right_stick_up(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::RightY, threshold, false); +} + +BoundTrigger BoundTrigger::right_stick_down(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::RightY, threshold, true); +} + +BoundTrigger BoundTrigger::left_trigger(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::LeftTrigger, threshold, true); +} + +BoundTrigger BoundTrigger::right_trigger(int controller, float threshold) +{ + return BoundTrigger(controller, Axis::RightTrigger, threshold, true); +} + +BoundButton::BoundButton(Button button) + : button(button) {} + +BoundButton::BoundButton(int controller, Button button) + : controller(controller), button(button) {} + +bool Binding::pressed() const +{ + if (m_press_consumed) + return false; + + if (m_last_press_time >= 0 && (Time::seconds - m_last_press_time) <= press_buffer) + return true; + + return m_pressed; +} + +bool Binding::released() const +{ + if (m_release_consumed) + return false; + + if (m_last_release_time >= 0 && (Time::seconds - m_last_release_time) <= release_buffer) + return true; + + return m_released; +} + +bool Binding::down() const +{ + return m_down; +} + +float Binding::value() const +{ + return m_value; +} + +int Binding::sign() const +{ + return (int)Calc::sign(m_value); +} + +double Binding::timestamp() const +{ + return m_last_timestamp; +} + +void Binding::update() +{ + m_press_consumed = false; + m_release_consumed = false; + + if (get_pressed()) + { + m_last_timestamp = Time::seconds; + m_last_press_time = Time::seconds; + m_pressed = true; + } + else + { + m_pressed = false; + } + + if (get_released()) + { + m_last_release_time = Time::seconds; + m_released = true; + } + else + { + m_released = false; + } + + m_down = get_down(); + m_value = get_value(); +} + +void Binding::consume_press() +{ + m_press_consumed = true; + m_last_press_time = -1; +} + +void Binding::consume_release() +{ + m_release_consumed = true; + m_last_release_time = -1; +} + +void Binding::add(Key key) +{ + keys.push_back(key); +} + +void Binding::add(BoundButton button) +{ + buttons.push_back(button); +} + +void Binding::add(BoundTrigger trigger) +{ + triggers.push_back(trigger); +} + +void Binding::add(MouseButton button) +{ + mouse.push_back(button); +} + +void Binding::set_controller(int index) +{ + for (auto& it : buttons) + it.controller = index; + for (auto& it : triggers) + it.controller = index; +} + +void Binding::clear() +{ + keys.clear(); + buttons.clear(); + triggers.clear(); + mouse.clear(); +} + +bool Binding::get_pressed() const +{ + for (auto& it : keys) + if (Input::pressed(it)) + return true; + + for (auto& it : mouse) + if (Input::pressed(it)) + return true; + + for (auto& it : buttons) + if (Input::pressed(it.controller, it.button)) + return true; + + for (auto& it : triggers) + { + if (it.controller < 0 || it.controller >= Input::max_controllers) + continue; + + if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis) + continue; + + if (it.is_down(Input::state()->controllers[it.controller].axis[(int)it.axis]) && + !it.is_down(Input::last_state()->controllers[it.controller].axis[(int)it.axis])) + return true; + } + + return false; +} + +bool Binding::get_released() const +{ + for (auto& it : keys) + if (Input::released(it)) + return true; + + for (auto& it : mouse) + if (Input::released(it)) + return true; + + for (auto& it : buttons) + if (Input::released(it.controller, it.button)) + return true; + + for (auto& it : triggers) + { + if (it.controller < 0 || it.controller >= Input::max_controllers) + continue; + + if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis) + continue; + + if (!it.is_down(Input::state()->controllers[it.controller].axis[(int)it.axis]) && + it.is_down(Input::last_state()->controllers[it.controller].axis[(int)it.axis])) + return true; + } + + return false; +} + +bool Binding::get_down() const +{ + for (auto& it : keys) + if (Input::down(it)) + return true; + + for (auto& it : mouse) + if (Input::down(it)) + return true; + + for (auto& it : buttons) + if (Input::down(it.controller, it.button)) + return true; + + for (auto& it : triggers) + { + if (it.controller < 0 || it.controller >= Input::max_controllers) + continue; + + if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis) + continue; + + if (it.is_down(Input::state()->controllers[it.controller].axis[(int)it.axis])) + return true; + } + + return false; +} + +float Binding::get_value() const +{ + for (auto& it : keys) + if (Input::down(it)) + return 1.0f; + + for (auto& it : mouse) + if (Input::down(it)) + return 1.0f; + + for (auto& it : buttons) + if (Input::down(it.controller, it.button)) + return 1.0f; + + float highest = 0; + + for (auto& it : triggers) + { + if (it.controller < 0 || it.controller >= Input::max_controllers) + continue; + + if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis) + continue; + + float raw_value = Input::state()->controllers[it.controller].axis[(int)it.axis]; + + if (it.is_down(raw_value)) + { + float mapped_value = Calc::clamped_map(Calc::abs(raw_value), it.threshold, 1.0f, 0.0f, 1.0f); + if (mapped_value > highest) + highest = mapped_value; + } + } + + return highest; +} + +float AxisBinding::value() const +{ + float neg = negative.value(); + float pos = positive.value(); + + // neither are down + if (neg <= 0 && pos <= 0) + return 0; + + // negative-only is down + if (neg > 0 && pos <= 0) + return -neg; + + // positive-only is down + if (pos > 0 && neg <= 0) + return pos; + + // both are down: + + // overlap cancel out + if (overlap == Overlap::Cancel) + return 0; + + // overlap takes older + if (overlap == Overlap::Older) + { + if (negative.timestamp() < positive.timestamp()) + return -neg; + else + return pos; + } + + // overlap takes newer + if (negative.timestamp() > positive.timestamp()) + return -neg; + else + return pos; +} + +int AxisBinding::sign() const +{ + return (int)Calc::sign(value()); +} + +void AxisBinding::update() +{ + negative.update(); + positive.update(); +} + +void AxisBinding::consume_press() +{ + negative.consume_press(); + positive.consume_press(); +} + +void AxisBinding::consume_release() +{ + negative.consume_release(); + positive.consume_release(); +} + +void AxisBinding::add_left_stick_x(int controller, float threshold) +{ + negative.add(BoundTrigger::left_stick_left(controller, threshold)); + positive.add(BoundTrigger::left_stick_right(controller, threshold)); +} + +void AxisBinding::add_left_stick_y(int controller, float threshold) +{ + negative.add(BoundTrigger::left_stick_up(controller, threshold)); + positive.add(BoundTrigger::left_stick_down(controller, threshold)); +} + +void AxisBinding::add_right_stick_x(int controller, float threshold) +{ + negative.add(BoundTrigger::right_stick_left(controller, threshold)); + positive.add(BoundTrigger::right_stick_right(controller, threshold)); +} + +void AxisBinding::add_right_stick_y(int controller, float threshold) +{ + negative.add(BoundTrigger::right_stick_up(controller, threshold)); + positive.add(BoundTrigger::right_stick_down(controller, threshold)); +} + +void AxisBinding::set_controller(int index) +{ + negative.set_controller(index); + positive.set_controller(index); +} + +void AxisBinding::clear() +{ + negative.clear(); + positive.clear(); +} + +Vec2 StickBinding::value() const +{ + Vec2 result = Vec2(x.value(), y.value()); + if (round_threshold > 0 && result.length() < round_threshold) + return Vec2::zero; + return result; +} + +Point StickBinding::sign() const +{ + Vec2 result = value(); + return Point((int)Calc::sign(result.x), (int)Calc::sign(result.y)); +} + +void StickBinding::update() +{ + x.update(); + y.update(); +} + +void StickBinding::consume_press() +{ + x.consume_press(); + y.consume_press(); +} + +void StickBinding::consume_release() +{ + x.consume_release(); + y.consume_release(); +} + +void StickBinding::add_dpad(int controller) +{ + x.negative.add(BoundButton(controller, Button::Left)); + x.positive.add(BoundButton(controller, Button::Right)); + y.negative.add(BoundButton(controller, Button::Up)); + y.positive.add(BoundButton(controller, Button::Down)); +} + +void StickBinding::add_left_stick(int controller, float threshold) +{ + x.add_left_stick_x(controller, threshold); + y.add_left_stick_y(controller, threshold); +} + +void StickBinding::add_right_stick(int controller, float threshold) +{ + x.add_right_stick_x(controller, threshold); + y.add_right_stick_y(controller, threshold); +} + +void StickBinding::set_controller(int index) +{ + x.set_controller(index); + y.set_controller(index); +} + +void StickBinding::clear() +{ + x.clear(); + y.clear(); +} \ No newline at end of file diff --git a/src/input/binding_registry.cpp b/src/input/binding_registry.cpp new file mode 100644 index 0000000..b4fbb7e --- /dev/null +++ b/src/input/binding_registry.cpp @@ -0,0 +1,70 @@ +#include + +using namespace Blah; + +Vector> BindingRegistry::bindings; +Vector> BindingRegistry::axes; +Vector> BindingRegistry::sticks; + +BindingRef BindingRegistry::register_binding() +{ + auto binding = std::make_shared(); + bindings.push_back(std::weak_ptr(binding)); + return binding; +} + +AxisBindingRef BindingRegistry::register_axis() +{ + auto binding = std::make_shared(); + axes.push_back(std::weak_ptr(binding)); + return binding; +} + +StickBindingRef BindingRegistry::register_stick() +{ + auto binding = std::make_shared(); + sticks.push_back(std::weak_ptr(binding)); + return binding; +} + +void BindingRegistry::update() +{ + for (int i = 0; i < bindings.size(); i++) + { + if (bindings[i].use_count() <= 0) + { + bindings.erase(i); + i--; + } + else if (auto binding = bindings[i].lock()) + { + binding->update(); + } + } + + for (int i = 0; i < axes.size(); i++) + { + if (axes[i].use_count() <= 0) + { + axes.erase(i); + i--; + } + else if (auto binding = axes[i].lock()) + { + binding->update(); + } + } + + for (int i = 0; i < sticks.size(); i++) + { + if (sticks[i].use_count() <= 0) + { + sticks.erase(i); + i--; + } + else if (auto binding = sticks[i].lock()) + { + binding->update(); + } + } +} diff --git a/src/input/input.cpp b/src/input/input.cpp index 3ab26b3..e8edba0 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -66,6 +67,9 @@ void InputBackend::frame() } } } + + // update bindings + BindingRegistry::update(); } void InputBackend::on_mouse_move(float x, float y) diff --git a/src/input/virtual_axis.cpp b/src/input/virtual_axis.cpp deleted file mode 100644 index cc94141..0000000 --- a/src/input/virtual_axis.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include -#include -#include - -using namespace Blah; - -VirtualAxis& VirtualAxis::add_keys(Key negative, Key positive) -{ - if (m_axes_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualAxis Keys out of bounds!"); - else - { - m_keys[m_keys_len].init(negative, positive); - m_keys_len++; - } - - return *this; -} - -VirtualAxis& VirtualAxis::add_buttons(int gamepad_id, Button negative, Button positive) -{ - if (m_axes_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualAxis Buttons out of bounds!"); - else - { - m_buttons[m_buttons_len].init(gamepad_id, negative, positive); - m_buttons_len++; - } - - return *this; -} - -VirtualAxis& VirtualAxis::add_axis(int gamepad_id, Axis axis, float deadzone) -{ - if (m_axes_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualAxis Axes out of bounds!"); - else - { - m_axes[m_axes_len].init(gamepad_id, axis, deadzone); - m_axes_len++; - } - - return *this; -} - -VirtualAxis& VirtualAxis::repeat(float m_repeat_delay, float m_repeat_interval) -{ - this->m_repeat_delay = m_repeat_delay; - this->m_repeat_interval = m_repeat_interval; - return *this; -} - -VirtualAxis& VirtualAxis::press_buffer(float duration) -{ - this->m_press_buffer = duration; - return *this; -} - -VirtualAxis& VirtualAxis::release_buffer(float duration) -{ - this->m_release_buffer = duration; - return *this; -} - -void VirtualAxis::update() -{ - m_last_value = m_value; - m_value = 0; - - for (int i = 0; i < m_keys_len; i++) - { - m_keys[i].update(); - if (m_value == 0) - m_value = (float)m_keys[i].value; - } - - for (int i = 0; i < m_buttons_len; i++) - { - m_buttons[i].update(); - if (m_value == 0) - m_value = (float)m_buttons[i].value; - } - - for (int i = 0; i < m_axes_len; i++) - { - m_axes[i].update(); - if (m_value == 0) - m_value = m_axes[i].value; - } - - //Valuei - m_last_value_i = m_value_i; - if (m_value > 0) - m_value_i = 1; - else if (m_value < 0) - m_value_i = -1; - else - m_value_i = 0; - - //pressed? - m_pressed = false; - if (m_value_i != 0 && m_last_value_i != m_value_i) - { - m_pressed = true; - m_last_press_time = m_repeat_press_time = Time::seconds; - } - else if (m_value_i == m_last_value_i && m_value_i != 0) - { - if (Time::seconds - m_last_press_time <= m_press_buffer) - m_pressed = true; - else if (m_repeat_interval > 0 && Time::seconds >= m_repeat_press_time + m_repeat_delay) - { - int prev = (int)((Time::previous_seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval); - int cur = (int)((Time::seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval); - m_pressed = prev < cur; - } - } - - //released? - if (m_last_value_i != 0 && m_value_i != m_last_value_i) - { - m_released = true; - m_last_release_time = Time::seconds; - } - else if (Time::seconds - m_last_release_time <= m_release_buffer) - m_released = true; - else - m_released = false; -} - -void VirtualAxis::KeysNode::init(Key negative, Key positive) -{ - this->negative = negative; - this->positive = positive; -} - -void VirtualAxis::KeysNode::update() -{ - value = Input::axis_check(value, negative, positive); -} - -void VirtualAxis::ButtonsNode::init(int gamepad_id, Button negative, Button positive) -{ - this->gamepad_id = gamepad_id; - this->negative = negative; - this->positive = positive; -} - -void VirtualAxis::ButtonsNode::update() -{ - value = Input::axis_check(value, gamepad_id, negative, positive); -} - -void VirtualAxis::AxisNode::init(int gamepad_id, Axis axis, float deadzone) -{ - this->gamepad_id = gamepad_id; - this->axis = axis; - this->deadzone = deadzone; -} - -void VirtualAxis::AxisNode::update() -{ - value = Input::axis_check(gamepad_id, axis); - if (value < deadzone && value > -deadzone) - value = 0; -} \ No newline at end of file diff --git a/src/input/virtual_button.cpp b/src/input/virtual_button.cpp deleted file mode 100644 index 22d7828..0000000 --- a/src/input/virtual_button.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include -#include -#include - -using namespace Blah; - -VirtualButton& VirtualButton::add_key(Key key) -{ - if (m_keys_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualButton Keys out of bounds!"); - else - { - m_keys[m_keys_len].init(key); - m_keys_len++; - } - - return *this; -} - -VirtualButton& VirtualButton::add_button(int gamepad_id, Button button) -{ - if (m_buttons_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualButton Buttons out of bounds!"); - else - { - m_buttons[m_buttons_len].init(gamepad_id, button); - m_buttons_len++; - } - - return *this; -} - -VirtualButton& VirtualButton::add_axis(int gamepad_id, Axis axis, float threshold, bool greater_than) -{ - if (m_axes_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualButton Axes out of bounds!"); - else - { - m_axes[m_axes_len].init(gamepad_id, axis, threshold, greater_than); - m_axes_len++; - } - - return *this; -} - -VirtualButton& VirtualButton::repeat(float m_repeat_delay, float m_repeat_interval) -{ - this->m_repeat_delay = m_repeat_delay; - this->m_repeat_interval = m_repeat_interval; - return *this; -} - -VirtualButton& VirtualButton::press_buffer(float duration) -{ - this->m_press_buffer = duration; - return *this; -} - -VirtualButton& VirtualButton::release_buffer(float duration) -{ - this->m_release_buffer = duration; - return *this; -} - -void VirtualButton::update() -{ - m_down = false; - m_pressed = false; - m_released = false; - - // Keys - for (int i = 0; i < m_keys_len; i++) - { - m_keys[i].update(); - - m_down = m_down || m_keys[i].down; - m_pressed = m_pressed || m_keys[i].pressed; - m_released = m_released || m_keys[i].released; - } - - // Buttons - for (int i = 0; i < m_buttons_len; i++) - { - m_buttons[i].update(); - - m_down = m_down || m_buttons[i].down; - m_pressed = m_pressed || m_buttons[i].pressed; - m_released = m_released || m_buttons[i].released; - } - - // Axes - for (int i = 0; i < m_axes_len; i++) - { - m_axes[i].update(); - - m_down = m_down || m_axes[i].down; - m_pressed = m_pressed || m_axes[i].pressed; - m_released = m_released || m_axes[i].released; - } - - // pressed? - if (m_pressed) - { - m_repeat_press_time = m_last_press_time = Time::seconds; - } - else if (Time::seconds - m_last_press_time <= m_press_buffer) - { - m_pressed = true; - } - else if (m_down && m_repeat_interval > 0 && Time::seconds >= m_repeat_press_time + m_repeat_delay) - { - int prev = (int)((Time::previous_seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval); - int cur = (int)((Time::seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval); - m_pressed = prev < cur; - } - - // released? - if (m_released) - m_last_release_time = Time::seconds; - else - m_released = Time::seconds - m_last_release_time <= m_release_buffer; -} - -void VirtualButton::KeyNode::init(Key key) -{ - this->key = key; -} - -void VirtualButton::KeyNode::update() -{ - down = Input::down(key); - pressed = Input::pressed(key); - released = Input::released(key); -} - -void VirtualButton::ButtonNode::init(int gamepad_id, Button button) -{ - this->gamepad_id = gamepad_id; - this->button = button; -} - -void VirtualButton::ButtonNode::update() -{ - down = Input::down(gamepad_id, button); - pressed = Input::pressed(gamepad_id, button); - released = Input::released(gamepad_id, button); -} - -void VirtualButton::AxisNode::init(int gamepad_id, Axis axis, float threshold, bool greater_than) -{ - this->gamepad_id = gamepad_id; - this->axis = axis; - this->threshold = threshold; - this->greater_than = greater_than; -} - -void VirtualButton::AxisNode::update() -{ - float curr = Input::state()->controllers[gamepad_id].axis[(int)axis]; - float prev = Input::last_state()->controllers[gamepad_id].axis[(int)axis]; - - if (greater_than) - { - down = curr >= threshold; - pressed = down && prev < threshold; - released = !down && prev >= threshold; - } - else - { - down = curr <= threshold; - pressed = down && prev > threshold; - released = !down && prev <= threshold; - } -} \ No newline at end of file diff --git a/src/input/virtual_stick.cpp b/src/input/virtual_stick.cpp deleted file mode 100644 index 2792855..0000000 --- a/src/input/virtual_stick.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include -#include -#include - -using namespace Blah; - -VirtualStick::VirtualStick() -{ - this->m_i_deadzone = 0; -} - -VirtualStick::VirtualStick(float iDeadzone) -{ - this->m_i_deadzone = iDeadzone; -} - -VirtualStick& VirtualStick::add_keys(Key left, Key right, Key up, Key down) -{ - if (m_keys_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualStick Keys out of bounds!"); - else - { - m_keys[m_keys_len].init(left, right, up, down); - m_keys_len++; - } - - return *this; -} - -VirtualStick& VirtualStick::add_buttons(int gamepad_id, Button left, Button right, Button up, Button down) -{ - if (m_buttons_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualStick Buttons out of bounds!"); - else - { - m_buttons[m_buttons_len].init(gamepad_id, left, right, up, down); - m_buttons_len++; - } - - return *this; -} - -VirtualStick& VirtualStick::add_axes(int gamepad_id, Axis horizontal, Axis vertical, float deadzone) -{ - if (m_axes_len >= Input::max_virtual_nodes) - BLAH_ERROR("VirtualStick Axes out of bounds!"); - else - { - m_axes[m_axes_len].init(gamepad_id, horizontal, vertical, deadzone); - m_axes_len++; - } - - return *this; -} - -VirtualStick& VirtualStick::repeat(float repeat_delay, float repeat_interval) -{ - this->m_repeat_delay = repeat_delay; - this->m_repeat_interval = repeat_interval; - return *this; -} - -VirtualStick& VirtualStick::press_buffer(float duration) -{ - m_press_buffer = duration; - return *this; -} - -VirtualStick& VirtualStick::release_buffer(float duration) -{ - m_release_buffer = duration; - return *this; -} - -void VirtualStick::update() -{ - m_last_value = m_value; - m_value = Vec2::zero; - - for (int i = 0; i < m_keys_len; i++) - { - m_keys[i].update(); - if (m_value == Vec2::zero) - m_value = m_keys[i].value; - } - - for (int i = 0; i < m_buttons_len; i++) - { - m_buttons[i].update(); - if (m_value == Vec2::zero) - m_value = m_buttons[i].value; - } - - for (int i = 0; i < m_axes_len; i++) - { - m_axes[i].update(); - if (m_value == Vec2::zero) - m_value = m_axes[i].value; - } - - //Valuei - m_last_value_i = m_value_i; - if (m_value.x > m_i_deadzone) - m_value_i.x = 1; - else if (m_value.x < -m_i_deadzone) - m_value_i.x = -1; - else - m_value_i.x = 0; - if (m_value.y > m_i_deadzone) - m_value_i.y = 1; - else if (m_value.y < -m_i_deadzone) - m_value_i.y = -1; - else - m_value_i.y = 0; - - //pressed? - m_pressed = false; - if (m_value_i != Point::zero && m_last_value_i != m_value_i) - { - m_pressed = true; - m_last_press_time = m_repeat_press_time = Time::seconds; - } - else if (m_value_i == m_last_value_i && m_value_i != Point::zero) - { - if (Time::seconds - m_last_press_time <= m_press_buffer) - m_pressed = true; - else if (m_repeat_interval > 0 && Time::seconds >= m_repeat_press_time + m_repeat_delay) - { - int prev = (int)((Time::previous_seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval); - int cur = (int)((Time::seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval); - m_pressed = prev < cur; - } - } - - //released? - if (m_last_value_i != Point::zero && m_value_i != m_last_value_i) - { - m_released = true; - m_last_release_time = Time::seconds; - } - else if (Time::seconds - m_last_release_time <= m_release_buffer) - m_released = true; - else - m_released = false; -} - -void VirtualStick::KeysNode::init(Key left, Key right, Key up, Key down) -{ - this->left = left; - this->right = right; - this->up = up; - this->down = down; -} - -void VirtualStick::KeysNode::update() -{ - value.x = Input::axis_check(value.x, left, right); - value.y = Input::axis_check(value.y, up, down); -} - -void VirtualStick::ButtonsNode::init(int gamepad_id, Button left, Button right, Button up, Button down) -{ - this->gamepad_id = gamepad_id; - this->left = left; - this->right = right; - this->up = up; - this->down = down; -} - -void VirtualStick::ButtonsNode::update() -{ - value.x = Input::axis_check(value.x, gamepad_id, left, right); - value.y = Input::axis_check(value.y, gamepad_id, up, down); -} - -void VirtualStick::AxesNode::init(int gamepad_id, Axis horizontal, Axis vertical, float deadzone) -{ - this->gamepad_id = gamepad_id; - this->horizontal = horizontal; - this->vertical = vertical; - this->deadzone = deadzone; -} - -void VirtualStick::AxesNode::update() -{ - value.x = Input::axis_check(gamepad_id, horizontal); - value.y = Input::axis_check(gamepad_id, vertical); - - if (value.length() < deadzone) - value = Vec2::zero; -} \ No newline at end of file