1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-16 01:54:38 +02:00

#15 add XInput support for Windows

This commit is contained in:
XProger
2018-02-21 07:02:16 +03:00
parent b38747f50e
commit 0efa5aa90c
3 changed files with 166 additions and 61 deletions

View File

@@ -1046,7 +1046,7 @@ namespace Core {
ctrl.keys[ cDuck ] = KeySet( ikZ, jkLT );
ctrl.keys[ cDash ] = KeySet( ikX, jkRT );
ctrl.keys[ cRoll ] = KeySet( ikA, jkB );
ctrl.keys[ cInventory ] = KeySet( ikTab, jkSelect );
ctrl.keys[ cInventory ] = KeySet( ikEscape, jkSelect );
ctrl.keys[ cStart ] = KeySet( ikEnter, jkStart );
}
@@ -1076,7 +1076,8 @@ namespace Core {
// use D key for jump in browsers
#ifdef __EMSCRIPTEN__
settings.controls[0].keys[cJump].key = ikD;
settings.controls[0].keys[ cJump ].key = ikD;
settings.controls[0].keys[ cInventory ].key = ikTab;
#endif
#ifdef __RPI__

View File

@@ -596,6 +596,7 @@ struct Lara : Character {
void reset(int room, const vec3 &pos, float angle, Stand forceStand = STAND_GROUND) {
visibleMask = 0xFFFFFFFF;
health = LARA_MAX_HEALTH;
if (room == TR::NO_ROOM) {
stand = STAND_AIR;

View File

@@ -141,21 +141,85 @@ InputKey mouseToInputKey(int msg) {
}
// joystick
typedef struct _XINPUT_GAMEPAD
{
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;
typedef struct _XINPUT_STATE
{
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
} XINPUT_STATE, *PXINPUT_STATE;
typedef struct _XINPUT_VIBRATION
{
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XINPUT_VIBRATION, *PXINPUT_VIBRATION;
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
DWORD (WINAPI *XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
DWORD (WINAPI *XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
#define JOY_DEAD_ZONE_STICK 0.3f
#define JOY_DEAD_ZONE_TRIGGER 0.01f
#define JOY_MAX_COUNT 2
#define JOY_NONE 255
bool joyReady[2];
uint8 joyIndex[JOY_MAX_COUNT];
void joyInit(int index) {
JOYINFOEX info;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
joyReady[index] = joyGetPosEx(index, &info) == JOYERR_NOERROR;
void osJoyVibrate(int index, float L, float R) {
if (XInputSetState && joyIndex[index] != JOY_NONE) {
XINPUT_VIBRATION vibration;
vibration.wLeftMotorSpeed = int(L * 65535.0f);
vibration.wRightMotorSpeed = int(R * 65535.0f);
XInputSetState(joyIndex[index], &vibration);
}
}
void joyFree(int index) {
joyReady[index] = false;
memset(&Input::joy[index], 0, sizeof(Input::joy[index]));
void joyInit() {
memset(joyIndex, JOY_NONE, sizeof(joyIndex));
int index = 0;
HMODULE h = LoadLibrary("xinput9_1_0.dll");
XInputGetState = (decltype(XInputGetState))GetProcAddress(h, "XInputGetState");
XInputSetState = (decltype(XInputSetState))GetProcAddress(h, "XInputSetState");
for (int j = 0; j < 4; j++) {
if (XInputGetState) { // XInput
XINPUT_STATE state;
if (XInputGetState(j, &state) == ERROR_SUCCESS)
joyIndex[index] = j;
} else { // mmSystem (legacy)
JOYINFOEX info;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
if (joyGetPosEx(j, &info) == JOYERR_NOERROR)
joyIndex[index] = j;
}
if (joyIndex[index] != JOY_NONE) {
index++;
if (index >= JOY_MAX_COUNT)
break;
}
}
}
void joyFree() {
memset(joyIndex, JOY_NONE, sizeof(joyIndex));
Input::reset();
}
float joyAxis(int x, int xMin, int xMax) {
@@ -170,56 +234,99 @@ vec2 joyDir(float ax, float ay) {
return dir.normal() * dist;
}
void joyUpdate(int index) {
if (!joyReady[index]) return;
float joyGetPOV(int mask) {
switch (mask) {
case 0b0001 : return 1;
case 0b1001 : return 2;
case 0b1000 : return 3;
case 0b1010 : return 4;
case 0b0010 : return 5;
case 0b0110 : return 6;
case 0b0100 : return 7;
case 0b0101 : return 8;
}
return 0;
}
JOYINFOEX info;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
int joyDeadZone(int value, int zone) {
return (value < -zone || value > zone) ? value : 0;
}
if (joyGetPosEx(index, &info) == JOYERR_NOERROR) {
JOYCAPS caps;
joyGetDevCaps(0, &caps, sizeof(caps));
void joyUpdate() {
for (int j = 0; j < JOY_MAX_COUNT; j++) {
if (joyIndex[j] == JOY_NONE) break;
int index = joyIndex[j];
if (caps.wNumAxes > 0) {
Input::setJoyPos(index, jkL, joyDir(joyAxis(info.dwXpos, caps.wXmin, caps.wXmax),
joyAxis(info.dwYpos, caps.wYmin, caps.wYmax)));
if (XInputGetState) { // XInput
XINPUT_STATE state;
if (XInputGetState(index, &state) == ERROR_SUCCESS) {
// osJoyVibrate(j, state.Gamepad.bLeftTrigger / 255.0f, state.Gamepad.bRightTrigger / 255.0f); // vibration test
if ((caps.wCaps & JOYCAPS_HASR) && (caps.wCaps & JOYCAPS_HASU))
Input::setJoyPos(index, jkR, joyDir(joyAxis(info.dwUpos, caps.wUmin, caps.wUmax),
joyAxis(info.dwRpos, caps.wRmin, caps.wRmax)));
Input::setJoyPos(j, jkL, joyDir(joyAxis(joyDeadZone( state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE), -32768, 32767),
joyAxis(joyDeadZone(-state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE), -32768, 32767)));
Input::setJoyPos(j, jkR, joyDir(joyAxis(joyDeadZone( state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE), -32768, 32767),
joyAxis(joyDeadZone(-state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE), -32768, 32767)));
Input::setJoyPos(j, jkLT, vec2(joyDeadZone(state.Gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD) / 255.0f, 0.0f));
Input::setJoyPos(j, jkRT, vec2(joyDeadZone(state.Gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD) / 255.0f, 0.0f));
Input::setJoyPos(j, jkPOV, vec2(joyGetPOV(state.Gamepad.wButtons & 15), 0));
if (caps.wCaps & JOYCAPS_HASZ) {
float z = joyAxis(info.dwZpos, caps.wZmin, caps.wZmax);
Input::setJoyPos(index, jkLT, vec2(0.0f));
Input::setJoyPos(index, jkRT, vec2(0.0f));
static const JoyKey keys[] = { jkUp, jkDown, jkLeft, jkRight, jkStart, jkSelect, jkL, jkR, jkLB, jkRB, jkNone, jkNone, jkA, jkB, jkX, jkY };
for (int i = 4; i < 16; i++)
Input::setJoyDown(j, keys[i], (state.Gamepad.wButtons & (1 << i)) != 0);
} else {
joyFree();
joyInit();
break;
}
} else { // mmSystem (legacy)
JOYINFOEX info;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
JoyKey key = z > JOY_DEAD_ZONE_TRIGGER ? jkLT : (z < -JOY_DEAD_ZONE_TRIGGER ? jkRT : jkNone);
if (key != jkNone)
Input::setJoyPos(index, key, vec2(fabsf(z), 0.0f));
if (joyGetPosEx(index, &info) == JOYERR_NOERROR) {
JOYCAPS caps;
joyGetDevCaps(index, &caps, sizeof(caps));
if (caps.wNumAxes > 0) {
Input::setJoyPos(index, jkL, joyDir(joyAxis(info.dwXpos, caps.wXmin, caps.wXmax),
joyAxis(info.dwYpos, caps.wYmin, caps.wYmax)));
if ((caps.wCaps & JOYCAPS_HASR) && (caps.wCaps & JOYCAPS_HASU))
Input::setJoyPos(index, jkR, joyDir(joyAxis(info.dwUpos, caps.wUmin, caps.wUmax),
joyAxis(info.dwRpos, caps.wRmin, caps.wRmax)));
if (caps.wCaps & JOYCAPS_HASZ) {
float z = joyAxis(info.dwZpos, caps.wZmin, caps.wZmax);
Input::setJoyPos(index, jkLT, vec2(0.0f));
Input::setJoyPos(index, jkRT, vec2(0.0f));
JoyKey key = z > JOY_DEAD_ZONE_TRIGGER ? jkLT : (z < -JOY_DEAD_ZONE_TRIGGER ? jkRT : jkNone);
if (key != jkNone)
Input::setJoyPos(index, key, vec2(fabsf(z), 0.0f));
}
}
if (caps.wCaps & JOYCAPS_HASPOV)
if (info.dwPOV == JOY_POVCENTERED)
Input::setJoyPos(index, jkPOV, vec2(0.0f));
else
Input::setJoyPos(index, jkPOV, vec2(float(1 + info.dwPOV / 4500), 0.0f));
for (int i = 0; i < 10; i++)
Input::setJoyDown(index, JoyKey(jkA + i), (info.dwButtons & (1 << i)) > 0);
} else {
joyFree();
joyInit();
break;
}
}
if (caps.wCaps & JOYCAPS_HASPOV)
if (info.dwPOV == JOY_POVCENTERED)
Input::setJoyPos(index, jkPOV, vec2(0.0f));
else
Input::setJoyPos(index, jkPOV, vec2(float(1 + info.dwPOV / 4500), 0.0f));
for (int i = 0; i < 10; i++)
Input::setJoyDown(index, JoyKey(jkA + i), (info.dwButtons & (1 << i)) > 0);
} else
joyFree(index);
}
}
// touch
typedef BOOL (__stdcall *PREGISTERTOUCHWINDOW)(HWND, ULONG);
typedef BOOL (__stdcall *PGETTOUCHINPUTINFO)(HTOUCHINPUT, UINT, PTOUCHINPUT, int);
typedef BOOL (__stdcall *PCLOSETOUCHINPUTHANDLE)(HTOUCHINPUT);
PREGISTERTOUCHWINDOW RegisterTouchWindowX;
PGETTOUCHINPUTINFO GetTouchInputInfoX;
PCLOSETOUCHINPUTHANDLE CloseTouchInputHandleX;
BOOL (WINAPI *RegisterTouchWindowX)(HWND, ULONG);
BOOL (WINAPI *GetTouchInputInfoX)(HTOUCHINPUT, UINT, PTOUCHINPUT, int);
BOOL (WINAPI *CloseTouchInputHandleX)(HTOUCHINPUT);
#define MAX_TOUCH_COUNT 6
@@ -227,9 +334,9 @@ void touchInit(HWND hWnd) {
int value = GetSystemMetrics(SM_DIGITIZER);
if (value) {
HMODULE hUser32 = LoadLibrary("user32.dll");
RegisterTouchWindowX = (PREGISTERTOUCHWINDOW)GetProcAddress(hUser32, "RegisterTouchWindow");
GetTouchInputInfoX = (PGETTOUCHINPUTINFO)GetProcAddress(hUser32, "GetTouchInputInfo");
CloseTouchInputHandleX = (PCLOSETOUCHINPUTHANDLE)GetProcAddress(hUser32, "CloseTouchInputHandle");
RegisterTouchWindowX = (decltype(RegisterTouchWindowX)) GetProcAddress(hUser32, "RegisterTouchWindow");
GetTouchInputInfoX = (decltype(GetTouchInputInfoX)) GetProcAddress(hUser32, "GetTouchInputInfo");
CloseTouchInputHandleX = (decltype(CloseTouchInputHandleX)) GetProcAddress(hUser32, "CloseTouchInputHandle");
if (RegisterTouchWindowX && GetTouchInputInfoX && CloseTouchInputHandleX)
RegisterTouchWindowX(hWnd, 0);
}
@@ -366,10 +473,8 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara
break;
// joystick
case WM_DEVICECHANGE :
joyFree(0);
joyFree(1);
joyInit(0);
joyInit(1);
joyFree();
joyInit();
return 1;
// touch
case WM_TOUCH :
@@ -557,8 +662,7 @@ int main(int argc, char** argv) {
osStartTime = osGetTime();
touchInit(hWnd);
joyInit(0);
joyInit(1);
joyInit();
sndInit(hWnd);
Game::init(argc > 1 ? argv[1] : NULL);
@@ -580,8 +684,7 @@ int main(int argc, char** argv) {
if (msg.message == WM_QUIT)
Core::quit();
} else {
joyUpdate(0);
joyUpdate(1);
joyUpdate();
#ifdef VR_SUPPORT
vrUpdateInput();
#endif