Skip to content

Commit 27f1565

Browse files
committed
Implement basic gamepad stick handling
This implements mouse movement with the right gamepad stick. This is far from a complete gamepad implementation but can hopefully be a good starting point.
1 parent c138d7f commit 27f1565

File tree

5 files changed

+252
-1
lines changed

5 files changed

+252
-1
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
217217
"src/plib/gnw/debug.h"
218218
"src/plib/gnw/dxinput.cc"
219219
"src/plib/gnw/dxinput.h"
220+
"src/plib/gnw/gamepad.cc"
221+
"src/plib/gnw/gamepad.hpp"
220222
"src/plib/gnw/grbuf.cc"
221223
"src/plib/gnw/grbuf.h"
222224
"src/plib/gnw/input.cc"

src/plib/gnw/dxinput.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ static int gMouseWheelDeltaY = 0;
1313
// 0x4E0400
1414
bool dxinput_init()
1515
{
16-
if (SDL_InitSubSystem(SDL_INIT_EVENTS) != 0) {
16+
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
17+
if (SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) {
1718
return false;
1819
}
1920

src/plib/gnw/gamepad.cc

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#include <algorithm>
2+
#include <cmath>
3+
4+
#include <SDL.h>
5+
6+
#include "plib/gnw/debug.h"
7+
#include "plib/gnw/gamepad.hpp"
8+
#include "plib/gnw/mouse.h"
9+
#include "plib/gnw/svga.h"
10+
11+
namespace fallout {
12+
13+
namespace {
14+
15+
// [-32767.0..+32767.0] -> [-1.0..1.0]
16+
void ScaleJoystickAxes(float* x, float* y, float deadzone)
17+
{
18+
if (deadzone == 0) {
19+
return;
20+
}
21+
if (deadzone >= 1.0) {
22+
*x = 0;
23+
*y = 0;
24+
return;
25+
}
26+
27+
const float maximum = 32767.0;
28+
float analogX = *x;
29+
float analogY = *y;
30+
float deadZone = deadzone * maximum;
31+
32+
float magnitude = std::sqrt(analogX * analogX + analogY * analogY);
33+
if (magnitude >= deadZone) {
34+
float scalingFactor = 1.F / magnitude * (magnitude - deadZone) / (maximum - deadZone);
35+
analogX = (analogX * scalingFactor);
36+
analogY = (analogY * scalingFactor);
37+
38+
float clampingFactor = 1.F;
39+
float absAnalogX = std::fabs(analogX);
40+
float absAnalogY = std::fabs(analogY);
41+
if (absAnalogX > 1.0 || absAnalogY > 1.0) {
42+
if (absAnalogX > absAnalogY) {
43+
clampingFactor = 1.F / absAnalogX;
44+
} else {
45+
clampingFactor = 1.F / absAnalogY;
46+
}
47+
}
48+
*x = (clampingFactor * analogX);
49+
*y = (clampingFactor * analogY);
50+
} else {
51+
*x = 0;
52+
*y = 0;
53+
}
54+
}
55+
56+
float leftStickXUnscaled, leftStickYUnscaled, rightStickXUnscaled, rightStickYUnscaled;
57+
58+
void ScaleLeftJoystick()
59+
{
60+
const float leftDeadzone = 0.07F;
61+
leftStickX = leftStickXUnscaled;
62+
leftStickY = leftStickYUnscaled;
63+
ScaleJoystickAxes(&leftStickX, &leftStickY, leftDeadzone);
64+
}
65+
66+
void ScaleRightJoystick()
67+
{
68+
const float rightDeadzone = 0.07F;
69+
rightStickX = rightStickXUnscaled;
70+
rightStickY = rightStickYUnscaled;
71+
ScaleJoystickAxes(&rightStickX, &rightStickY, rightDeadzone);
72+
}
73+
74+
struct RightStickAccumulator {
75+
76+
RightStickAccumulator()
77+
{
78+
lastTc = SDL_GetTicks();
79+
hiresDX = 0;
80+
hiresDY = 0;
81+
}
82+
83+
void Pool(int* x, int* y, int slowdown)
84+
{
85+
const Uint32 tc = SDL_GetTicks();
86+
const int dtc = tc - lastTc;
87+
hiresDX += rightStickX * dtc;
88+
hiresDY += rightStickY * dtc;
89+
const int dx = static_cast<int>(hiresDX / slowdown);
90+
const int dy = static_cast<int>(hiresDY / slowdown);
91+
*x += dx;
92+
*y -= dy;
93+
lastTc = tc;
94+
// keep track of remainder for sub-pixel motion
95+
hiresDX -= dx * slowdown;
96+
hiresDY -= dy * slowdown;
97+
}
98+
99+
void Clear()
100+
{
101+
lastTc = SDL_GetTicks();
102+
}
103+
104+
uint32_t lastTc;
105+
float hiresDX;
106+
float hiresDY;
107+
};
108+
109+
} // namespace
110+
111+
float leftStickX, leftStickY, rightStickX, rightStickY;
112+
113+
void HandleControllerAxisMotion(const SDL_Event& event)
114+
{
115+
switch (event.caxis.axis) {
116+
case SDL_CONTROLLER_AXIS_LEFTX:
117+
leftStickXUnscaled = static_cast<float>(event.caxis.value);
118+
ScaleLeftJoystick();
119+
break;
120+
case SDL_CONTROLLER_AXIS_LEFTY:
121+
leftStickYUnscaled = static_cast<float>(-event.caxis.value);
122+
ScaleLeftJoystick();
123+
break;
124+
case SDL_CONTROLLER_AXIS_RIGHTX:
125+
rightStickXUnscaled = static_cast<float>(event.caxis.value);
126+
ScaleRightJoystick();
127+
break;
128+
case SDL_CONTROLLER_AXIS_RIGHTY:
129+
rightStickYUnscaled = static_cast<float>(-event.caxis.value);
130+
ScaleRightJoystick();
131+
break;
132+
}
133+
}
134+
135+
void ProcessLeftStick()
136+
{
137+
}
138+
139+
void ProcessRightStick()
140+
{
141+
static RightStickAccumulator acc;
142+
// deadzone is handled in ScaleJoystickAxes() already
143+
if (rightStickX == 0 && rightStickY == 0) {
144+
acc.Clear();
145+
return;
146+
}
147+
148+
int x, y;
149+
SDL_GetRelativeMouseState(&x, &y);
150+
int newX = x;
151+
int newY = y;
152+
acc.Pool(&newX, &newY, 2);
153+
154+
// clipping to viewport is handled in mouse_simulate_input
155+
if (newX != x || newY != y) {
156+
mouse_simulate_input(newX - x, newY - y, 0);
157+
}
158+
}
159+
160+
void HandleJoystickDeviceAdded(const SDL_Event& event)
161+
{
162+
const int32_t deviceIndex = event.jdevice.which;
163+
if (SDL_NumJoysticks() <= deviceIndex)
164+
return;
165+
debug_printf("Adding joystick %d: %s\n", deviceIndex,
166+
SDL_JoystickNameForIndex(deviceIndex));
167+
SDL_Joystick* const joystick = SDL_JoystickOpen(deviceIndex);
168+
if (joystick == nullptr) {
169+
debug_printf("%s", SDL_GetError());
170+
SDL_ClearError();
171+
return;
172+
}
173+
}
174+
175+
void HandleJoystickDeviceRemoved(const SDL_Event& event)
176+
{
177+
const int32_t deviceIndex = event.jdevice.which;
178+
debug_printf("Removed joystick %d\n", deviceIndex);
179+
}
180+
181+
void HandleControllerDeviceAdded(const SDL_Event& event)
182+
{
183+
const int32_t joystickIndex = event.cdevice.which;
184+
debug_printf("Opening game controller for joystick %d\n", joystickIndex);
185+
SDL_GameController* const controller = SDL_GameControllerOpen(joystickIndex);
186+
if (controller == nullptr) {
187+
debug_printf("%s", SDL_GetError());
188+
SDL_ClearError();
189+
return;
190+
}
191+
SDL_Joystick* const sdlJoystick = SDL_GameControllerGetJoystick(controller);
192+
193+
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(sdlJoystick);
194+
char* mapping = SDL_GameControllerMappingForGUID(guid);
195+
if (mapping != nullptr) {
196+
debug_printf("Opened game controller with mapping:\n%s\n", mapping);
197+
SDL_free(mapping);
198+
}
199+
}
200+
201+
void HandleControllerDeviceRemoved(const SDL_Event& event)
202+
{
203+
const int32_t joystickIndex = event.cdevice.which;
204+
debug_printf("Removed game controller for joystick %d\n", joystickIndex);
205+
}
206+
207+
} // namespace fallout

src/plib/gnw/gamepad.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef FALLOUT_PLIB_GNW_GAMEPAD_H_
2+
#define FALLOUT_PLIB_GNW_GAMEPAD_H_
3+
4+
#include <SDL.h>
5+
6+
#include "plib/gnw/rect.h"
7+
8+
namespace fallout {
9+
10+
extern float leftStickX, leftStickY, rightStickX, rightStickY;
11+
12+
void HandleJoystickDeviceAdded(const SDL_Event& event);
13+
void HandleJoystickDeviceRemoved(const SDL_Event& event);
14+
void HandleControllerDeviceAdded(const SDL_Event& event);
15+
void HandleControllerDeviceRemoved(const SDL_Event& event);
16+
void HandleControllerAxisMotion(const SDL_Event& event);
17+
void ProcessLeftStick();
18+
void ProcessRightStick();
19+
20+
} // namespace fallout
21+
22+
#endif /* FALLOUT_PLIB_GNW_GAMEPAD_H_ */

src/plib/gnw/input.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "plib/color/color.h"
99
#include "plib/gnw/button.h"
1010
#include "plib/gnw/dxinput.h"
11+
#include "plib/gnw/gamepad.hpp"
1112
#include "plib/gnw/gnw.h"
1213
#include "plib/gnw/grbuf.h"
1314
#include "plib/gnw/intrface.h"
@@ -1085,6 +1086,9 @@ void GNW95_process_message()
10851086
// is disabled, because if we ignore it, we'll never be able to reactivate
10861087
// it again.
10871088

1089+
ProcessLeftStick();
1090+
ProcessRightStick();
1091+
10881092
KeyboardData keyboardData;
10891093
SDL_Event e;
10901094
while (SDL_PollEvent(&e)) {
@@ -1112,6 +1116,21 @@ void GNW95_process_message()
11121116
GNW95_process_key(&keyboardData);
11131117
}
11141118
break;
1119+
case SDL_JOYDEVICEADDED:
1120+
HandleJoystickDeviceAdded(e);
1121+
break;
1122+
case SDL_JOYDEVICEREMOVED:
1123+
HandleJoystickDeviceRemoved(e);
1124+
break;
1125+
case SDL_CONTROLLERDEVICEADDED:
1126+
HandleControllerDeviceAdded(e);
1127+
break;
1128+
case SDL_CONTROLLERDEVICEREMOVED:
1129+
HandleControllerDeviceRemoved(e);
1130+
break;
1131+
case SDL_CONTROLLERAXISMOTION:
1132+
HandleControllerAxisMotion(e);
1133+
break;
11151134
case SDL_WINDOWEVENT:
11161135
switch (e.window.event) {
11171136
case SDL_WINDOWEVENT_EXPOSED:

0 commit comments

Comments
 (0)