Input refactor to allow arbitrary bindings
This commit is contained in:
parent
b11e652a20
commit
971d728169
|
@ -5,58 +5,47 @@
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
namespace recomp {
|
namespace recomp {
|
||||||
struct ControllerState {
|
struct InputField {
|
||||||
enum Button : uint32_t {
|
uint32_t device_type;
|
||||||
BUTTON_NORTH = 1 << 0,
|
int32_t input_id;
|
||||||
BUTTON_SOUTH = 1 << 1,
|
|
||||||
BUTTON_EAST = 1 << 2,
|
|
||||||
BUTTON_WEST = 1 << 3,
|
|
||||||
BUTTON_L1 = 1 << 4, // Left Bumper
|
|
||||||
BUTTON_R1 = 1 << 5, // Right Bumper
|
|
||||||
BUTTON_L2 = 1 << 6, // Left Trigger Press
|
|
||||||
BUTTON_R2 = 1 << 7, // Right Trigger Press
|
|
||||||
BUTTON_L3 = 1 << 8, // Left Joystick Press
|
|
||||||
BUTTON_R3 = 1 << 9, // Right Joystick Press
|
|
||||||
BUTTON_DPAD_UP = 1 << 10,
|
|
||||||
BUTTON_DPAD_DOWN = 1 << 11,
|
|
||||||
BUTTON_DPAD_RIGHT = 1 << 12,
|
|
||||||
BUTTON_DPAD_LEFT = 1 << 13,
|
|
||||||
BUTTON_START = 1 << 14,
|
|
||||||
};
|
|
||||||
enum Axis : size_t {
|
|
||||||
AXIS_LEFT_X,
|
|
||||||
AXIS_LEFT_Y,
|
|
||||||
AXIS_RIGHT_X,
|
|
||||||
AXIS_RIGHT_Y,
|
|
||||||
AXIS_LEFT_TRIGGER,
|
|
||||||
AXIS_RIGHT_TRIGGER,
|
|
||||||
AXIS_MAX
|
|
||||||
};
|
|
||||||
uint32_t buttons;
|
|
||||||
float axes[AXIS_MAX];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MouseState {
|
void poll_inputs();
|
||||||
enum Button : uint32_t {
|
float get_input_analog(const InputField& field);
|
||||||
LEFT = 1 << 0,
|
float get_input_analog(const std::span<const recomp::InputField> fields);
|
||||||
RIGHT = 1 << 1,
|
bool get_input_digital(const InputField& field);
|
||||||
MIDDLE = 1 << 2,
|
bool get_input_digital(const std::span<const recomp::InputField> fields);
|
||||||
BACK = 1 << 3,
|
|
||||||
FORWARD = 1 << 4,
|
struct DefaultN64Mappings {
|
||||||
};
|
std::vector<InputField> a;
|
||||||
int32_t wheel_pos;
|
std::vector<InputField> b;
|
||||||
int32_t position_x;
|
std::vector<InputField> l;
|
||||||
int32_t position_y;
|
std::vector<InputField> r;
|
||||||
uint32_t buttons;
|
std::vector<InputField> z;
|
||||||
|
std::vector<InputField> start;
|
||||||
|
|
||||||
|
std::vector<InputField> c_left;
|
||||||
|
std::vector<InputField> c_right;
|
||||||
|
std::vector<InputField> c_up;
|
||||||
|
std::vector<InputField> c_down;
|
||||||
|
|
||||||
|
std::vector<InputField> dpad_left;
|
||||||
|
std::vector<InputField> dpad_right;
|
||||||
|
std::vector<InputField> dpad_up;
|
||||||
|
std::vector<InputField> dpad_down;
|
||||||
|
|
||||||
|
std::vector<InputField> analog_left;
|
||||||
|
std::vector<InputField> analog_right;
|
||||||
|
std::vector<InputField> analog_up;
|
||||||
|
std::vector<InputField> analog_down;
|
||||||
};
|
};
|
||||||
|
|
||||||
using InputState = std::variant<ControllerState, MouseState>;
|
extern const DefaultN64Mappings default_n64_mappings;
|
||||||
|
|
||||||
void get_keyboard_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
|
||||||
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
||||||
std::vector<InputState> get_input_states();
|
|
||||||
void handle_events();
|
void handle_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,23 @@ extern "C" {
|
||||||
void name(uint8_t* rdram, recomp_context* ctx);
|
void name(uint8_t* rdram, recomp_context* ctx);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum RecompDigitalInput {
|
||||||
|
RECOMP_DIGITAL_INPUT_ITEM1,
|
||||||
|
RECOMP_DIGITAL_INPUT_ITEM2,
|
||||||
|
RECOMP_DIGITAL_INPUT_ITEM3,
|
||||||
|
RECOMP_DIGITAL_INPUT_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RecompAnalogInput {
|
||||||
|
RECOMP_ANALOG_INPUT_MOVEMENT_X,
|
||||||
|
RECOMP_ANALOG_INPUT_MOVEMENT_Y,
|
||||||
|
RECOMP_ANALOG_INPUT_CAMERA_X,
|
||||||
|
RECOMP_ANALOG_INPUT_CAMERA_Y,
|
||||||
|
RECOMP_ANALOG_INPUT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_FUNC(u32, recomp_get_digital_input, u32 which);
|
||||||
|
DECLARE_FUNC(float, recomp_get_analog_input, u32 which);
|
||||||
DECLARE_FUNC(void, recomp_get_item_inputs, u32* buttons);
|
DECLARE_FUNC(void, recomp_get_item_inputs, u32* buttons);
|
||||||
DECLARE_FUNC(void, recomp_get_camera_inputs, float* x_out, float* y_out);
|
DECLARE_FUNC(void, recomp_get_camera_inputs, float* x_out, float* y_out);
|
||||||
// TODO move this
|
// TODO move this
|
||||||
|
|
|
@ -36,141 +36,80 @@ struct GameControllerButtonMapping {
|
||||||
};
|
};
|
||||||
using button_map_t = std::vector<GameControllerButtonMapping>;
|
using button_map_t = std::vector<GameControllerButtonMapping>;
|
||||||
|
|
||||||
uint32_t process_controller_mappings(const recomp::ControllerState& controller_state, const axis_map_t& axis_map, const button_map_t& button_map) {
|
|
||||||
uint32_t cur_buttons = 0;
|
|
||||||
|
|
||||||
for (const auto& mapping : axis_map) {
|
|
||||||
float input_value = controller_state.axes[mapping.axis];
|
|
||||||
if (mapping.threshold > 0) {
|
|
||||||
if (input_value > mapping.threshold) {
|
|
||||||
cur_buttons |= mapping.output_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (input_value < mapping.threshold) {
|
|
||||||
cur_buttons |= mapping.output_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& mapping : button_map) {
|
|
||||||
int input_value = controller_state.buttons & mapping.button;
|
|
||||||
if (input_value) {
|
|
||||||
cur_buttons |= mapping.output_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cur_buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
||||||
static const axis_map_t general_axis_map{
|
|
||||||
{ recomp::ControllerState::AXIS_RIGHT_X, -controller_default_threshold, N64Inputs::C_LEFT },
|
|
||||||
{ recomp::ControllerState::AXIS_RIGHT_X, controller_default_threshold, N64Inputs::C_RIGHT },
|
|
||||||
{ recomp::ControllerState::AXIS_RIGHT_Y, -controller_default_threshold, N64Inputs::C_UP },
|
|
||||||
{ recomp::ControllerState::AXIS_RIGHT_Y, controller_default_threshold, N64Inputs::C_DOWN },
|
|
||||||
{ recomp::ControllerState::AXIS_LEFT_TRIGGER, 0.30f, N64Inputs::Z },
|
|
||||||
};
|
|
||||||
static const button_map_t general_button_map{
|
|
||||||
{ recomp::ControllerState::BUTTON_START, N64Inputs::START },
|
|
||||||
{ recomp::ControllerState::BUTTON_SOUTH, N64Inputs::A },
|
|
||||||
{ recomp::ControllerState::BUTTON_EAST, N64Inputs::B },
|
|
||||||
{ recomp::ControllerState::BUTTON_WEST, N64Inputs::B },
|
|
||||||
{ recomp::ControllerState::BUTTON_L1, N64Inputs::L },
|
|
||||||
{ recomp::ControllerState::BUTTON_R1, N64Inputs::R },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_LEFT, N64Inputs::DPAD_LEFT },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_RIGHT, N64Inputs::DPAD_RIGHT },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_UP, N64Inputs::DPAD_UP },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_DOWN, N64Inputs::DPAD_DOWN },
|
|
||||||
};
|
|
||||||
|
|
||||||
uint16_t cur_buttons = 0;
|
uint16_t cur_buttons = 0;
|
||||||
float cur_x = 0.0f;
|
float cur_x = 0.0f;
|
||||||
float cur_y = 0.0f;
|
float cur_y = 0.0f;
|
||||||
|
|
||||||
recomp::get_keyboard_input(&cur_buttons, &cur_x, &cur_y);
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.a) ? N64Inputs::A : 0;
|
||||||
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.b) ? N64Inputs::B : 0;
|
||||||
std::vector<InputState> input_states = recomp::get_input_states();
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.l) ? N64Inputs::L : 0;
|
||||||
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.r) ? N64Inputs::R : 0;
|
||||||
for (const InputState& state : input_states) {
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.z) ? N64Inputs::Z : 0;
|
||||||
if (const auto* controller_state = std::get_if<ControllerState>(&state)) {
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.start) ? N64Inputs::START : 0;
|
||||||
cur_x += controller_state->axes[ControllerState::AXIS_LEFT_X];
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.c_left) ? N64Inputs::C_LEFT : 0;
|
||||||
cur_y -= controller_state->axes[ControllerState::AXIS_LEFT_Y];
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.c_right) ? N64Inputs::C_RIGHT : 0;
|
||||||
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.c_up) ? N64Inputs::C_UP : 0;
|
||||||
cur_buttons |= (uint16_t)process_controller_mappings(*controller_state, general_axis_map, general_button_map);
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.c_down) ? N64Inputs::C_DOWN : 0;
|
||||||
}
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_left) ? N64Inputs::DPAD_LEFT : 0;
|
||||||
else if (const auto* mouse_state = std::get_if<MouseState>(&state)) {
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_right) ? N64Inputs::DPAD_RIGHT : 0;
|
||||||
// Mouse currently unused
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_up) ? N64Inputs::DPAD_UP : 0;
|
||||||
}
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_down) ? N64Inputs::DPAD_DOWN : 0;
|
||||||
}
|
|
||||||
|
|
||||||
*buttons_out = cur_buttons;
|
*buttons_out = cur_buttons;
|
||||||
cur_x = std::clamp(cur_x, -1.0f, 1.0f);
|
cur_x = recomp::get_input_analog(recomp::default_n64_mappings.analog_right) - recomp::get_input_analog(recomp::default_n64_mappings.analog_left);
|
||||||
cur_y = std::clamp(cur_y, -1.0f, 1.0f);
|
cur_y = recomp::get_input_analog(recomp::default_n64_mappings.analog_up) - recomp::get_input_analog(recomp::default_n64_mappings.analog_down);
|
||||||
*x_out = cur_x;
|
*x_out = cur_x;
|
||||||
*y_out = cur_y;
|
*y_out = cur_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void recomp_get_item_inputs(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void recomp_get_item_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||||
static const axis_map_t item_axis_map{
|
|
||||||
{ recomp::ControllerState::AXIS_RIGHT_X, -controller_default_threshold, N64Inputs::C_LEFT },
|
|
||||||
{ recomp::ControllerState::AXIS_RIGHT_X, controller_default_threshold, N64Inputs::C_RIGHT },
|
|
||||||
{ recomp::ControllerState::AXIS_RIGHT_Y, controller_default_threshold, N64Inputs::C_DOWN },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const button_map_t item_button_map {
|
|
||||||
{ recomp::ControllerState::BUTTON_EAST, N64Inputs::B },
|
|
||||||
{ recomp::ControllerState::BUTTON_WEST, N64Inputs::B },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_LEFT, N64Inputs::DPAD_LEFT },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_RIGHT, N64Inputs::DPAD_RIGHT },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_UP, N64Inputs::DPAD_UP },
|
|
||||||
{ recomp::ControllerState::BUTTON_DPAD_DOWN, N64Inputs::DPAD_DOWN },
|
|
||||||
};
|
|
||||||
|
|
||||||
u32* buttons_out = _arg<0, u32*>(rdram, ctx);
|
u32* buttons_out = _arg<0, u32*>(rdram, ctx);
|
||||||
|
|
||||||
uint32_t cur_buttons = 0;
|
uint32_t cur_buttons = 0;
|
||||||
|
|
||||||
// TODO do this in a way that will allow for remapping keyboard inputs
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.b) ? N64Inputs::B : 0;
|
||||||
uint16_t keyboard_buttons;
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.c_left) ? N64Inputs::C_LEFT : 0;
|
||||||
float dummy_x, dummy_y;
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.c_right) ? N64Inputs::C_RIGHT : 0;
|
||||||
recomp::get_keyboard_input(&keyboard_buttons, &dummy_x, &dummy_y);
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.c_down) ? N64Inputs::C_DOWN : 0;
|
||||||
cur_buttons |= keyboard_buttons;
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_left) ? N64Inputs::DPAD_LEFT : 0;
|
||||||
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_right) ? N64Inputs::DPAD_RIGHT : 0;
|
||||||
// Process controller inputs
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_up) ? N64Inputs::DPAD_UP : 0;
|
||||||
std::vector<recomp::InputState> input_states = recomp::get_input_states();
|
cur_buttons |= recomp::get_input_digital(recomp::default_n64_mappings.dpad_down) ? N64Inputs::DPAD_DOWN : 0;
|
||||||
|
|
||||||
for (const recomp::InputState& state : input_states) {
|
|
||||||
if (const auto* controller_state = std::get_if<recomp::ControllerState>(&state)) {
|
|
||||||
cur_buttons |= process_controller_mappings(*controller_state, item_axis_map, item_button_map);
|
|
||||||
}
|
|
||||||
else if (const auto* mouse_state = std::get_if<recomp::MouseState>(&state)) {
|
|
||||||
// Mouse currently unused
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*buttons_out = cur_buttons;
|
*buttons_out = cur_buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool recomp_digital_input_state[RECOMP_DIGITAL_INPUT_MAX];
|
||||||
|
float recomp_analog_input_state[RECOMP_ANALOG_INPUT_MAX];
|
||||||
|
|
||||||
|
extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
recomp::poll_inputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void recomp_get_digital_input(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
u32 input_slot = _arg<0, u32>(rdram, ctx);
|
||||||
|
|
||||||
|
// TODO implement this
|
||||||
|
|
||||||
|
_return<u32>(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void recomp_get_analog_input(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
u32 input_slot = _arg<0, u32>(rdram, ctx);
|
||||||
|
|
||||||
|
// TODO implement this
|
||||||
|
|
||||||
|
_return<float>(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||||
float* x_out = _arg<0, float*>(rdram, ctx);
|
float* x_out = _arg<0, float*>(rdram, ctx);
|
||||||
float* y_out = _arg<1, float*>(rdram, ctx);
|
float* y_out = _arg<1, float*>(rdram, ctx);
|
||||||
|
|
||||||
float x_val = 0.0f;
|
float x_val = recomp::get_input_analog(recomp::default_n64_mappings.c_right) - recomp::get_input_analog(recomp::default_n64_mappings.c_left);
|
||||||
float y_val = 0.0f;
|
float y_val = recomp::get_input_analog(recomp::default_n64_mappings.c_up) - recomp::get_input_analog(recomp::default_n64_mappings.c_down);
|
||||||
|
|
||||||
// Process controller inputs
|
|
||||||
std::vector<recomp::InputState> input_states = recomp::get_input_states();
|
|
||||||
|
|
||||||
for (const recomp::InputState& state : input_states) {
|
|
||||||
if (const auto* controller_state = std::get_if<recomp::ControllerState>(&state)) {
|
|
||||||
x_val += controller_state->axes[recomp::ControllerState::AXIS_RIGHT_X];
|
|
||||||
y_val += controller_state->axes[recomp::ControllerState::AXIS_RIGHT_Y];
|
|
||||||
}
|
|
||||||
else if (const auto* mouse_state = std::get_if<recomp::MouseState>(&state)) {
|
|
||||||
// Mouse currently unused
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*x_out = x_val;
|
*x_out = x_val;
|
||||||
*y_out = y_val;
|
*y_out = y_val;
|
||||||
|
|
|
@ -6,8 +6,14 @@
|
||||||
#include "recomp_ui.h"
|
#include "recomp_ui.h"
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
const Uint8* keys = nullptr;
|
||||||
|
int numkeys = 0;
|
||||||
std::atomic_int32_t mouse_wheel_pos = 0;
|
std::atomic_int32_t mouse_wheel_pos = 0;
|
||||||
std::vector<SDL_JoystickID> controllers{};
|
std::vector<SDL_JoystickID> controller_ids{};
|
||||||
|
std::vector<SDL_GameController*> cur_controllers{};
|
||||||
|
} InputState;
|
||||||
|
|
||||||
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
|
@ -36,7 +42,7 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
printf("Controller added: %d\n", controller_event->which);
|
printf("Controller added: %d\n", controller_event->which);
|
||||||
if (controller != nullptr) {
|
if (controller != nullptr) {
|
||||||
printf(" Instance ID: %d\n", SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
printf(" Instance ID: %d\n", SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
||||||
controllers.push_back(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
InputState.controller_ids.push_back(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -44,7 +50,7 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
{
|
{
|
||||||
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
||||||
printf("Controller removed: %d\n", controller_event->which);
|
printf("Controller removed: %d\n", controller_event->which);
|
||||||
std::erase(controllers, controller_event->which);
|
std::erase(InputState.controller_ids, controller_event->which);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_EventType::SDL_QUIT:
|
case SDL_EventType::SDL_QUIT:
|
||||||
|
@ -53,7 +59,7 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
case SDL_EventType::SDL_MOUSEWHEEL:
|
case SDL_EventType::SDL_MOUSEWHEEL:
|
||||||
{
|
{
|
||||||
SDL_MouseWheelEvent* wheel_event = (SDL_MouseWheelEvent*)event;
|
SDL_MouseWheelEvent* wheel_event = (SDL_MouseWheelEvent*)event;
|
||||||
mouse_wheel_pos.fetch_add(wheel_event->y * (wheel_event->direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1));
|
InputState.mouse_wheel_pos.fetch_add(wheel_event->y * (wheel_event->direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1));
|
||||||
}
|
}
|
||||||
recomp::queue_event(*event);
|
recomp::queue_event(*event);
|
||||||
break;
|
break;
|
||||||
|
@ -72,126 +78,203 @@ void recomp::handle_events() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<recomp::InputState> recomp::get_input_states() {
|
enum class DeviceType {
|
||||||
std::vector<InputState> ret{};
|
Keyboard,
|
||||||
|
Mouse,
|
||||||
int32_t mouse_x;
|
ControllerDigital,
|
||||||
int32_t mouse_y;
|
ControllerAnalog // Axis input_id values are the SDL value + 1
|
||||||
|
|
||||||
Uint32 sdl_mouse_buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
|
||||||
|
|
||||||
struct MouseButtonMapping {
|
|
||||||
Sint32 input;
|
|
||||||
decltype(MouseState::buttons) output;
|
|
||||||
};
|
|
||||||
static const std::vector<MouseButtonMapping> input_mouse_map{
|
|
||||||
{ SDL_BUTTON_LMASK, MouseState::LEFT },
|
|
||||||
{ SDL_BUTTON_RMASK, MouseState::RIGHT },
|
|
||||||
{ SDL_BUTTON_MMASK, MouseState::MIDDLE },
|
|
||||||
{ SDL_BUTTON_X1MASK, MouseState::BACK },
|
|
||||||
{ SDL_BUTTON_X2MASK, MouseState::FORWARD },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
decltype(MouseState::buttons) mouse_buttons = 0;
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A;
|
||||||
for (const MouseButtonMapping& mapping : input_mouse_map) {
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B;
|
||||||
if (sdl_mouse_buttons & mapping.input) {
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X;
|
||||||
mouse_buttons |= mapping.output;
|
constexpr SDL_GameControllerButton SDL_CONTROLLER_BUTTON_NORTH = SDL_CONTROLLER_BUTTON_Y;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mouse_buttons & MouseState::FORWARD) {
|
const recomp::DefaultN64Mappings recomp::default_n64_mappings = {
|
||||||
printf("forward\n");
|
.a = {
|
||||||
}
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_SOUTH},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_SPACE}
|
||||||
if (mouse_buttons & MouseState::BACK) {
|
},
|
||||||
printf("back\n");
|
.b = {
|
||||||
}
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_EAST},
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_WEST},
|
||||||
ret.emplace_back(
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_LSHIFT}
|
||||||
MouseState {
|
},
|
||||||
.wheel_pos = mouse_wheel_pos,
|
.l = {
|
||||||
.position_x = mouse_x,
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||||
.position_y = mouse_y,
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_E}
|
||||||
.buttons = mouse_buttons
|
},
|
||||||
}
|
.r = {
|
||||||
);
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_R}
|
||||||
for (SDL_JoystickID controller_id : controllers) {
|
},
|
||||||
struct InputButtonMapping {
|
.z = {
|
||||||
SDL_GameControllerButton input;
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_TRIGGERLEFT + 1},
|
||||||
decltype(ControllerState::buttons) output;
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_Q}
|
||||||
|
},
|
||||||
|
.start = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_START},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_RETURN}
|
||||||
|
},
|
||||||
|
.c_left = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTX + 1)},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_LEFT}
|
||||||
|
},
|
||||||
|
.c_right = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTX + 1},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_RIGHT}
|
||||||
|
},
|
||||||
|
.c_up = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_RIGHTY + 1)},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_UP}
|
||||||
|
},
|
||||||
|
.c_down = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_RIGHTY + 1},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_DOWN}
|
||||||
|
},
|
||||||
|
.dpad_left = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_LEFT},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_J}
|
||||||
|
},
|
||||||
|
.dpad_right = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_L}
|
||||||
|
},
|
||||||
|
.dpad_up = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_UP},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_I}
|
||||||
|
},
|
||||||
|
.dpad_down = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerDigital, .input_id = SDL_CONTROLLER_BUTTON_DPAD_DOWN},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_K}
|
||||||
|
},
|
||||||
|
.analog_left = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTX + 1)},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_A}
|
||||||
|
},
|
||||||
|
.analog_right = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTX + 1},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_D}
|
||||||
|
},
|
||||||
|
.analog_up = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = -(SDL_CONTROLLER_AXIS_LEFTY + 1)},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_W}
|
||||||
|
},
|
||||||
|
.analog_down = {
|
||||||
|
{.device_type = (uint32_t)DeviceType::ControllerAnalog, .input_id = SDL_CONTROLLER_AXIS_LEFTY + 1},
|
||||||
|
{.device_type = (uint32_t)DeviceType::Keyboard, .input_id = SDL_SCANCODE_S}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
static const std::vector<InputButtonMapping> input_button_map{
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START, ControllerState::BUTTON_START },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A, ControllerState::BUTTON_SOUTH },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B, ControllerState::BUTTON_EAST },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X, ControllerState::BUTTON_WEST },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y, ControllerState::BUTTON_NORTH },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER, ControllerState::BUTTON_L1 },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, ControllerState::BUTTON_R1 },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK, ControllerState::BUTTON_L3 },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK, ControllerState::BUTTON_R3 },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT, ControllerState::BUTTON_DPAD_LEFT },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT, ControllerState::BUTTON_DPAD_RIGHT },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP, ControllerState::BUTTON_DPAD_UP },
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN, ControllerState::BUTTON_DPAD_DOWN },
|
|
||||||
};
|
|
||||||
SDL_GameController* controller = SDL_GameControllerFromInstanceID(controller_id);
|
|
||||||
decltype(ControllerState::buttons) buttons = 0;
|
|
||||||
|
|
||||||
for (const InputButtonMapping& mapping : input_button_map) {
|
void recomp::poll_inputs() {
|
||||||
if (SDL_GameControllerGetButton(controller, mapping.input)) {
|
InputState.keys = SDL_GetKeyboardState(&InputState.numkeys);
|
||||||
buttons |= mapping.output;
|
|
||||||
|
InputState.cur_controllers.clear();
|
||||||
|
|
||||||
|
for (SDL_JoystickID id : InputState.controller_ids) {
|
||||||
|
SDL_GameController* controller = SDL_GameControllerFromInstanceID(id);
|
||||||
|
if (controller != nullptr) {
|
||||||
|
InputState.cur_controllers.push_back(controller);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& new_input_state = ret.emplace_back(
|
bool controller_button_state(int32_t input_id) {
|
||||||
ControllerState {
|
if (input_id >= 0 && input_id < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX) {
|
||||||
.buttons = buttons,
|
SDL_GameControllerButton button = (SDL_GameControllerButton)input_id;
|
||||||
.axes = {},
|
bool ret = false;
|
||||||
}
|
|
||||||
);
|
for (const auto& controller : InputState.cur_controllers) {
|
||||||
auto& new_state = std::get<ControllerState>(new_input_state);
|
ret |= SDL_GameControllerGetButton(controller, button);
|
||||||
new_state.axes[ControllerState::AXIS_LEFT_X] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX) * (1/32768.0f);
|
|
||||||
new_state.axes[ControllerState::AXIS_LEFT_Y] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY) * (1/32768.0f);
|
|
||||||
new_state.axes[ControllerState::AXIS_RIGHT_X] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX) * (1/32768.0f);
|
|
||||||
new_state.axes[ControllerState::AXIS_RIGHT_Y] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY) * (1/32768.0f);
|
|
||||||
new_state.axes[ControllerState::AXIS_LEFT_TRIGGER] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT) * (1/32768.0f);
|
|
||||||
new_state.axes[ControllerState::AXIS_LEFT_TRIGGER] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * (1/32768.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO figure out a way to handle this more generally without exposing SDL to controls.cpp
|
float controller_axis_state(int32_t input_id) {
|
||||||
void recomp::get_keyboard_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
if (abs(input_id) - 1 < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX) {
|
||||||
static const std::vector<std::pair<SDL_Scancode, int>> keyboard_button_map{
|
SDL_GameControllerAxis axis = (SDL_GameControllerAxis)(abs(input_id) - 1);
|
||||||
{ SDL_Scancode::SDL_SCANCODE_LEFT, 0x0002 }, // c left
|
bool negative_range = input_id < 0;
|
||||||
{ SDL_Scancode::SDL_SCANCODE_RIGHT, 0x0001 }, // c right
|
float ret = 0.0f;
|
||||||
{ SDL_Scancode::SDL_SCANCODE_UP, 0x0008 }, // c up
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_DOWN, 0x0004 }, // c down
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_RETURN, 0x1000 }, // start
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_SPACE, 0x8000 }, // a
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_LSHIFT, 0x4000 }, // b
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_Q, 0x2000 }, // z
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_E, 0x0020 }, // l
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_R, 0x0010 }, // r
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_J, 0x0200 }, // dpad left
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_L, 0x0100 }, // dpad right
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_I, 0x0800 }, // dpad up
|
|
||||||
{ SDL_Scancode::SDL_SCANCODE_K, 0x0400 }, // dpad down
|
|
||||||
};
|
|
||||||
|
|
||||||
const Uint8* key_states = SDL_GetKeyboardState(nullptr);
|
for (const auto& controller : InputState.cur_controllers) {
|
||||||
|
float cur_val = SDL_GameControllerGetAxis(controller, axis) * (1/32768.0f);
|
||||||
|
if (negative_range) {
|
||||||
|
cur_val = -cur_val;
|
||||||
|
}
|
||||||
|
ret += std::clamp(cur_val, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
*buttons_out = 0;
|
return std::clamp(ret, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& mapping : keyboard_button_map) {
|
float recomp::get_input_analog(const recomp::InputField& field) {
|
||||||
if (key_states[mapping.first]) {
|
switch ((DeviceType)field.device_type) {
|
||||||
*buttons_out |= mapping.second;
|
case DeviceType::Keyboard:
|
||||||
|
{
|
||||||
|
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
||||||
|
return InputState.keys[field.input_id] ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
case DeviceType::ControllerDigital:
|
||||||
|
{
|
||||||
|
return controller_button_state(field.input_id) ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
case DeviceType::ControllerAnalog:
|
||||||
|
{
|
||||||
|
return controller_axis_state(field.input_id);
|
||||||
|
}
|
||||||
|
case DeviceType::Mouse:
|
||||||
|
{
|
||||||
|
// TODO mouse support
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*x_out = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
|
float recomp::get_input_analog(const std::span<const recomp::InputField> fields) {
|
||||||
*y_out = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
|
float ret = 0.0f;
|
||||||
|
for (const auto& field : fields) {
|
||||||
|
ret += get_input_analog(field);
|
||||||
|
}
|
||||||
|
return std::clamp(ret, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool recomp::get_input_digital(const recomp::InputField& field) {
|
||||||
|
switch ((DeviceType)field.device_type) {
|
||||||
|
case DeviceType::Keyboard:
|
||||||
|
{
|
||||||
|
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
||||||
|
return InputState.keys[field.input_id] != 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case DeviceType::ControllerDigital:
|
||||||
|
{
|
||||||
|
return controller_button_state(field.input_id);
|
||||||
|
}
|
||||||
|
case DeviceType::ControllerAnalog:
|
||||||
|
{
|
||||||
|
// TODO adjustable threshold
|
||||||
|
return controller_axis_state(field.input_id) >= 0.5f;
|
||||||
|
}
|
||||||
|
case DeviceType::Mouse:
|
||||||
|
{
|
||||||
|
// TODO mouse support
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool recomp::get_input_digital(const std::span<const recomp::InputField> fields) {
|
||||||
|
bool ret = 0;
|
||||||
|
for (const auto& field : fields) {
|
||||||
|
ret |= get_input_digital(field);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,6 +184,7 @@ int main(int argc, char** argv) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ultramodern::input_callbacks_t input_callbacks{
|
ultramodern::input_callbacks_t input_callbacks{
|
||||||
|
.poll_input = recomp::poll_inputs,
|
||||||
.get_input = recomp::get_n64_input,
|
.get_input = recomp::get_n64_input,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,10 @@ extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
if (input_callbacks.poll_input) {
|
||||||
|
input_callbacks.poll_input();
|
||||||
|
}
|
||||||
|
|
||||||
ultramodern::send_si_message();
|
ultramodern::send_si_message();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,9 @@ struct audio_callbacks_t {
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
struct input_callbacks_t {
|
struct input_callbacks_t {
|
||||||
|
using poll_input_t = void(void);
|
||||||
using get_input_t = void(uint16_t*, float*, float*);
|
using get_input_t = void(uint16_t*, float*, float*);
|
||||||
|
poll_input_t* poll_input;
|
||||||
get_input_t* get_input;
|
get_input_t* get_input;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue