mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-06-25 11:56:18 +00:00
Emulate motion controls with a mouse (#3122)
* Rework framework to allow for more types of mouse-to-something emulation and hook up gyro to it * Remove the unnecessary null check now that deltatime is handled differently * Fix toggle key * Basic gyro emulation working for two out of the three dimensions * clang * Added bindable key to hold for switching from looking to the sides to rolling * documentation
This commit is contained in:
parent
be12305f65
commit
551751df3c
7 changed files with 88 additions and 30 deletions
|
@ -447,21 +447,18 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
|
||||||
|
|
||||||
// Only do this on handle 1 for now
|
// Only do this on handle 1 for now
|
||||||
if (engine && handle == 1) {
|
if (engine && handle == 1) {
|
||||||
const auto gyro_poll_rate = engine->GetAccelPollRate();
|
auto now = std::chrono::steady_clock::now();
|
||||||
if (gyro_poll_rate != 0.0f) {
|
float deltaTime =
|
||||||
auto now = std::chrono::steady_clock::now();
|
std::chrono::duration_cast<std::chrono::microseconds>(now - controller->GetLastUpdate())
|
||||||
float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
|
.count() /
|
||||||
now - controller->GetLastUpdate())
|
1000000.0f;
|
||||||
.count() /
|
controller->SetLastUpdate(now);
|
||||||
1000000.0f;
|
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
|
||||||
controller->SetLastUpdate(now);
|
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
|
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, deltaTime,
|
||||||
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
lastOrientation, outputOrientation);
|
||||||
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
|
pData->orientation = outputOrientation;
|
||||||
deltaTime, lastOrientation, outputOrientation);
|
controller->SetLastOrientation(outputOrientation);
|
||||||
pData->orientation = outputOrientation;
|
|
||||||
controller->SetLastOrientation(outputOrientation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pData->touchData.touchNum =
|
pData->touchData.touchNum =
|
||||||
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
|
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
|
||||||
|
|
|
@ -66,6 +66,7 @@ auto output_array = std::array{
|
||||||
ControllerOutput(LEFTJOYSTICK_HALFMODE),
|
ControllerOutput(LEFTJOYSTICK_HALFMODE),
|
||||||
ControllerOutput(RIGHTJOYSTICK_HALFMODE),
|
ControllerOutput(RIGHTJOYSTICK_HALFMODE),
|
||||||
ControllerOutput(KEY_TOGGLE),
|
ControllerOutput(KEY_TOGGLE),
|
||||||
|
ControllerOutput(MOUSE_GYRO_ROLL_MODE),
|
||||||
|
|
||||||
// Button mappings
|
// Button mappings
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_NORTH), // Triangle
|
ControllerOutput(SDL_GAMEPAD_BUTTON_NORTH), // Triangle
|
||||||
|
@ -534,6 +535,9 @@ void ControllerOutput::FinalizeUpdate() {
|
||||||
// to do it, and it would be inconvenient to force it here, when AddUpdate does the job just
|
// to do it, and it would be inconvenient to force it here, when AddUpdate does the job just
|
||||||
// fine, and a toggle doesn't have to checked against every input that's bound to it, it's
|
// fine, and a toggle doesn't have to checked against every input that's bound to it, it's
|
||||||
// enough that one is pressed
|
// enough that one is pressed
|
||||||
|
case MOUSE_GYRO_ROLL_MODE:
|
||||||
|
SetMouseGyroRollMode(new_button_state);
|
||||||
|
break;
|
||||||
default: // is a normal key (hopefully)
|
default: // is a normal key (hopefully)
|
||||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#define BACK_BUTTON 0x00040000
|
#define BACK_BUTTON 0x00040000
|
||||||
|
|
||||||
#define KEY_TOGGLE 0x00200000
|
#define KEY_TOGGLE 0x00200000
|
||||||
|
#define MOUSE_GYRO_ROLL_MODE 0x00400000
|
||||||
|
|
||||||
#define SDL_UNMAPPED UINT32_MAX - 1
|
#define SDL_UNMAPPED UINT32_MAX - 1
|
||||||
|
|
||||||
|
@ -114,6 +115,7 @@ const std::map<std::string, u32> string_to_cbutton_map = {
|
||||||
{"lpaddle_low", SDL_GAMEPAD_BUTTON_LEFT_PADDLE2},
|
{"lpaddle_low", SDL_GAMEPAD_BUTTON_LEFT_PADDLE2},
|
||||||
{"rpaddle_high", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1},
|
{"rpaddle_high", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1},
|
||||||
{"rpaddle_low", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2},
|
{"rpaddle_low", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2},
|
||||||
|
{"mouse_gyro_roll_mode", MOUSE_GYRO_ROLL_MODE},
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::map<std::string, AxisMapping> string_to_axis_map = {
|
const std::map<std::string, AxisMapping> string_to_axis_map = {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "input/controller.h"
|
#include "input/controller.h"
|
||||||
#include "input_mouse.h"
|
#include "input_mouse.h"
|
||||||
|
@ -13,12 +14,19 @@ namespace Input {
|
||||||
|
|
||||||
int mouse_joystick_binding = 0;
|
int mouse_joystick_binding = 0;
|
||||||
float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250;
|
float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250;
|
||||||
|
bool mouse_gyro_roll_mode = false;
|
||||||
Uint32 mouse_polling_id = 0;
|
Uint32 mouse_polling_id = 0;
|
||||||
bool mouse_enabled = false;
|
MouseMode mouse_mode = MouseMode::Off;
|
||||||
|
|
||||||
// We had to go through 3 files of indirection just to update a flag
|
// Switches mouse to a set mode or turns mouse emulation off if it was already in that mode.
|
||||||
void ToggleMouseEnabled() {
|
// Returns whether the mode is turned on.
|
||||||
mouse_enabled = !mouse_enabled;
|
bool ToggleMouseModeTo(MouseMode m) {
|
||||||
|
if (mouse_mode == m) {
|
||||||
|
mouse_mode = MouseMode::Off;
|
||||||
|
} else {
|
||||||
|
mouse_mode = m;
|
||||||
|
}
|
||||||
|
return mouse_mode == m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMouseToJoystick(int joystick) {
|
void SetMouseToJoystick(int joystick) {
|
||||||
|
@ -31,10 +39,11 @@ void SetMouseParams(float mdo, float ms, float mso) {
|
||||||
mouse_speed_offset = mso;
|
mouse_speed_offset = mso;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
void SetMouseGyroRollMode(bool mode) {
|
||||||
auto* controller = (GameController*)param;
|
mouse_gyro_roll_mode = mode;
|
||||||
if (!mouse_enabled)
|
}
|
||||||
return interval;
|
|
||||||
|
void EmulateJoystick(GameController* controller, u32 interval) {
|
||||||
|
|
||||||
Axis axis_x, axis_y;
|
Axis axis_x, axis_y;
|
||||||
switch (mouse_joystick_binding) {
|
switch (mouse_joystick_binding) {
|
||||||
|
@ -47,7 +56,7 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
||||||
axis_y = Axis::RightY;
|
axis_y = Axis::RightY;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return interval; // no update needed
|
return; // no update needed
|
||||||
}
|
}
|
||||||
|
|
||||||
float d_x = 0, d_y = 0;
|
float d_x = 0, d_y = 0;
|
||||||
|
@ -67,7 +76,35 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
||||||
controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, 0));
|
controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, 0));
|
||||||
controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, 0));
|
controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, 0));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float constant_down_accel[3] = {0.0f, 10.0f, 0.0f};
|
||||||
|
void EmulateGyro(GameController* controller, u32 interval) {
|
||||||
|
// LOG_INFO(Input, "todo gyro");
|
||||||
|
float d_x = 0, d_y = 0;
|
||||||
|
SDL_GetRelativeMouseState(&d_x, &d_y);
|
||||||
|
controller->Acceleration(1, constant_down_accel);
|
||||||
|
float gyro_from_mouse[3] = {-d_y / 100, -d_x / 100, 0.0f};
|
||||||
|
if (mouse_gyro_roll_mode) {
|
||||||
|
gyro_from_mouse[1] = 0.0f;
|
||||||
|
gyro_from_mouse[2] = -d_x / 100;
|
||||||
|
}
|
||||||
|
controller->Gyro(1, gyro_from_mouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
||||||
|
auto* controller = (GameController*)param;
|
||||||
|
switch (mouse_mode) {
|
||||||
|
case MouseMode::Joystick:
|
||||||
|
EmulateJoystick(controller, interval);
|
||||||
|
break;
|
||||||
|
case MouseMode::Gyro:
|
||||||
|
EmulateGyro(controller, interval);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
return interval;
|
return interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,21 @@
|
||||||
|
|
||||||
namespace Input {
|
namespace Input {
|
||||||
|
|
||||||
void ToggleMouseEnabled();
|
enum MouseMode {
|
||||||
|
Off = 0,
|
||||||
|
Joystick,
|
||||||
|
Gyro,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ToggleMouseModeTo(MouseMode m);
|
||||||
void SetMouseToJoystick(int joystick);
|
void SetMouseToJoystick(int joystick);
|
||||||
void SetMouseParams(float mouse_deadzone_offset, float mouse_speed, float mouse_speed_offset);
|
void SetMouseParams(float mouse_deadzone_offset, float mouse_speed, float mouse_speed_offset);
|
||||||
|
void SetMouseGyroRollMode(bool mode);
|
||||||
|
|
||||||
// Polls the mouse for changes, and simulates joystick movement from it.
|
void EmulateJoystick(GameController* controller, u32 interval);
|
||||||
|
void EmulateGyro(GameController* controller, u32 interval);
|
||||||
|
|
||||||
|
// Polls the mouse for changes
|
||||||
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval);
|
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval);
|
||||||
|
|
||||||
} // namespace Input
|
} // namespace Input
|
||||||
|
|
|
@ -60,7 +60,8 @@ A: -F12: Triggers Renderdoc capture
|
||||||
-Ctrl F10: Open the debug menu
|
-Ctrl F10: Open the debug menu
|
||||||
-F9: Pauses emultor, if the debug menu is open
|
-F9: Pauses emultor, if the debug menu is open
|
||||||
-F8: Reparses the config file while in-game
|
-F8: Reparses the config file while in-game
|
||||||
-F7: Toggles mouse capture and mouse input
|
-F7: Toggles mouse-to-joystick emulation
|
||||||
|
-F6: Toggles mouse-to-gyro emulation
|
||||||
|
|
||||||
Q: How do I change between mouse and controller joystick input, and why is it even required?
|
Q: How do I change between mouse and controller joystick input, and why is it even required?
|
||||||
A: You can switch between them with F7, and it is required, because mouse input is done with polling, which means mouse movement is checked every frame, and if it didn't move, the code manually sets the emulator's virtual controller to 0 (back to the center), even if other input devices would update it.
|
A: You can switch between them with F7, and it is required, because mouse input is done with polling, which means mouse movement is checked every frame, and if it didn't move, the code manually sets the emulator's virtual controller to 0 (back to the center), even if other input devices would update it.
|
||||||
|
@ -175,6 +176,8 @@ You can find these here, with detailed comments, examples and suggestions for mo
|
||||||
Values go from 1 to 127 (no deadzone to max deadzone), first is the inner, second is the outer deadzone
|
Values go from 1 to 127 (no deadzone to max deadzone), first is the inner, second is the outer deadzone
|
||||||
If you only want inner or outer deadzone, set the other to 1 or 127, respectively
|
If you only want inner or outer deadzone, set the other to 1 or 127, respectively
|
||||||
Devices: leftjoystick, rightjoystick, l2, r2
|
Devices: leftjoystick, rightjoystick, l2, r2
|
||||||
|
'mouse_gyro_roll_mode':
|
||||||
|
Controls whether moving the mouse sideways causes a panning or a rolling motion while mouse-to-gyro emulation is active.
|
||||||
)";
|
)";
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -474,11 +474,16 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
|
||||||
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
|
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Toggle mouse capture and movement input
|
// Toggle mouse capture and joystick input emulation
|
||||||
else if (input_id == SDLK_F7) {
|
else if (input_id == SDLK_F7) {
|
||||||
Input::ToggleMouseEnabled();
|
|
||||||
SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(),
|
SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(),
|
||||||
!SDL_GetWindowRelativeMouseMode(this->GetSDLWindow()));
|
Input::ToggleMouseModeTo(Input::MouseMode::Joystick));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Toggle mouse capture and gyro input emulation
|
||||||
|
else if (input_id == SDLK_F6) {
|
||||||
|
SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(),
|
||||||
|
Input::ToggleMouseModeTo(Input::MouseMode::Gyro));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Toggle fullscreen
|
// Toggle fullscreen
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue