Skip to content

Commit 8b7e799

Browse files
Fix gamepads with Xbox-like remapping and SDL in lwjgl3 backend (jMonkeyEngine#2579)
* LWJGL 3.4.0 and Remove OpenVR @stephengold notified me that LWJGL 3.4 has been released and has also done some testing in their non jME projects with this new version already. (big thanks for keeping a tab on this area) I still have most of my own jME projects running lwjgl2 ('ve been procrastinating upgrading to LWJGL3 for quite a while now, long overdue), so I will finally do so in my own projects so I can help test as well. OpenVR is also no longer supported in LWJGL 3.4.0 so it appears it needed removed, but (unless I'm mistaken) this should not be an issue since the Tamarin VR library is using the newer openXR, and openVR is considered outdated and deprecated. Any jME apps still relying on openVR can still use v3.9 or earlier without issue (@richardTingle correct me if I'm incorrect on any of this) * Remove OpenVr * Delete jme3-vr directory * Remove OpenVR References from BulletDebugAppState.java * Change int to long for LWJGL 3.4.0 compatibility * Replace switch with if statements * Change int to long in LwjglPlatform.java * map every controller to an xbox-like layout * cleanup glfw mapper, and add SDL based joystick handling * use isEmpty instead of isBlank --------- Co-authored-by: Ryan McDonough <peanut64646@gmail.com>
1 parent d1bdf40 commit 8b7e799

File tree

14 files changed

+1221
-135
lines changed

14 files changed

+1221
-135
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ javadoc_deploy.pub
5050
!.vscode/settings.json
5151
!.vscode/JME_style.xml
5252
!.vscode/extensions.json
53+
joysticks-*.txt

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ lwjgl3-jemalloc = { module = "org.lwjgl:lwjgl-jemalloc", version.ref = "lwjgl3"
3232
lwjgl3-openal = { module = "org.lwjgl:lwjgl-openal", version.ref = "lwjgl3" }
3333
lwjgl3-opencl = { module = "org.lwjgl:lwjgl-opencl", version.ref = "lwjgl3" }
3434
lwjgl3-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl3" }
35+
lwjgl3-sdl = { module = "org.lwjgl:lwjgl-sdl", version.ref = "lwjgl3" }
3536

3637
mokito-core = "org.mockito:mockito-core:3.12.4"
3738

jme3-core/src/main/java/com/jme3/input/DefaultJoystickAxis.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class DefaultJoystickAxis implements JoystickAxis {
4848
private final boolean isAnalog;
4949
private final boolean isRelative;
5050
private float deadZone;
51+
private float jitterThreshold = 0f;
5152

5253
/**
5354
* Creates a new joystick axis instance. Only used internally.
@@ -166,6 +167,12 @@ public void setDeadZone(float f) {
166167
public String toString() {
167168
return "JoystickAxis[name=" + name + ", parent=" + parent.getName() + ", id=" + axisIndex
168169
+ ", logicalId=" + logicalId + ", isAnalog=" + isAnalog
169-
+ ", isRelative=" + isRelative + ", deadZone=" + deadZone + "]";
170+
+ ", isRelative=" + isRelative + ", deadZone=" + deadZone +
171+
", jitterThreshold=" + jitterThreshold + "]";
172+
}
173+
174+
@Override
175+
public float getJitterThreshold() {
176+
return jitterThreshold;
170177
}
171178
}

jme3-core/src/main/java/com/jme3/input/JoystickAxis.java

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,14 @@ public interface JoystickAxis {
4444
public static final String Z_ROTATION = "rz";
4545
public static final String LEFT_TRIGGER = "rx";
4646
public static final String RIGHT_TRIGGER = "ry";
47-
48-
// Note: the left/right trigger bit may be a bit controversial in
49-
// the sense that this is one case where XBox controllers make a lot
50-
// more sense.
51-
// I've seen the following mappings for various things:
52-
//
53-
// Axis | XBox | Non-Xbox (generally) (includes actual Sony PS4 controllers)
54-
// --------------+-------+---------------
55-
// left trigger | z | rx (also button 6)
56-
// right trigger | rz | ry (also button 7)
57-
// left stick x | x | x
58-
// left stick y | y | y
59-
// right stick x | rx | z
60-
// right stick y | ry | rz
61-
//
62-
// The issue is that in all cases I've seen, the XBox controllers will
63-
// use the name "xbox" somewhere in their name. The Non-XBox controllers
64-
// never mention anything uniform... even the PS4 controller only calls
65-
// itself "Wireless Controller". In that light, it seems easier to make
66-
// the default the ugly case and the "XBox" way the exception because it
67-
// can more easily be identified.
47+
48+
public static final String AXIS_XBOX_LEFT_TRIGGER = LEFT_TRIGGER;
49+
public static final String AXIS_XBOX_RIGHT_TRIGGER = RIGHT_TRIGGER;
50+
public static final String AXIS_XBOX_LEFT_THUMB_STICK_X = X_AXIS;
51+
public static final String AXIS_XBOX_LEFT_THUMB_STICK_Y = Y_AXIS;
52+
public static final String AXIS_XBOX_RIGHT_THUMB_STICK_X = Z_AXIS;
53+
public static final String AXIS_XBOX_RIGHT_THUMB_STICK_Y = Z_ROTATION;
54+
6855

6956
public static final String POV_X = "pov_x";
7057
public static final String POV_Y = "pov_y";
@@ -128,4 +115,15 @@ public interface JoystickAxis {
128115
* @return the radius of the dead zone
129116
*/
130117
public float getDeadZone();
118+
119+
120+
/**
121+
* Returns the suggested jitter threshold for this axis. Movements with a delta
122+
* smaller than this threshold will be ignored by the backend input system
123+
*
124+
* @return the jitter threshold
125+
*/
126+
public default float getJitterThreshold(){
127+
return 0;
128+
}
131129
}

jme3-core/src/main/java/com/jme3/input/JoystickButton.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ public interface JoystickButton {
5555
public static final String BUTTON_14 = "14";
5656
public static final String BUTTON_15 = "15";
5757

58+
59+
public static final String BUTTON_XBOX_A = BUTTON_2;
60+
public static final String BUTTON_XBOX_B = BUTTON_1;
61+
public static final String BUTTON_XBOX_X = BUTTON_3;
62+
public static final String BUTTON_XBOX_Y = BUTTON_0;
63+
public static final String BUTTON_XBOX_LB = BUTTON_4;
64+
public static final String BUTTON_XBOX_RB = BUTTON_5;
65+
public static final String BUTTON_XBOX_LT = BUTTON_6;
66+
public static final String BUTTON_XBOX_RT = BUTTON_7;
67+
public static final String BUTTON_XBOX_BACK = BUTTON_8;
68+
public static final String BUTTON_XBOX_START = BUTTON_9;
69+
public static final String BUTTON_XBOX_L3 = BUTTON_10;
70+
public static final String BUTTON_XBOX_R3 = BUTTON_11;
71+
72+
public static final String BUTTON_XBOX_DPAD_UP = BUTTON_12;
73+
public static final String BUTTON_XBOX_DPAD_DOWN = BUTTON_13;
74+
public static final String BUTTON_XBOX_DPAD_LEFT = BUTTON_14;
75+
public static final String BUTTON_XBOX_DPAD_RIGHT = BUTTON_15;
76+
77+
5878
/**
5979
* Assign the mapping name to receive events from the given button index
6080
* on the joystick.

jme3-core/src/main/java/com/jme3/system/AppSettings.java

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,31 @@ public final class AppSettings extends HashMap<String, Object> {
268268
*/
269269
public static final String JOAL = "JOAL";
270270

271+
/**
272+
* Map gamepads to Xbox-like layout.
273+
*/
274+
public static final String JOYSTICKS_XBOX_MAPPER = "JOYSTICKS_XBOX_MAPPER";
275+
276+
/**
277+
* Map gamepads to an Xbox-like layout, with fallback to raw if the gamepad is not recognized.
278+
*/
279+
public static final String JOYSTICKS_XBOX_WITH_FALLBACK_MAPPER = "JOYSTICKS_XBOX_WITH_FALLBACK_MAPPER";
280+
281+
/**
282+
* Map gamepads to an Xbox-like layout using the legacy jME input
283+
*/
284+
public static final String JOYSTICKS_XBOX_LEGACY_MAPPER = "JOYSTICKS_XBOX_LEGACY_MAPPER";
285+
286+
/**
287+
* Map gamepads using the legacy jME mapper and input.
288+
*/
289+
public static final String JOYSTICKS_LEGACY_MAPPER = "JOYSTICKS_LEGACY_MAPPER";
290+
291+
/**
292+
* Don't map gamepads, use raw events instead (ie. bring your own mapper)
293+
*/
294+
public static final String JOYSTICKS_RAW_MAPPER = "JOYSTICKS_RAW_MAPPER";
295+
271296
static {
272297
defaults.put("Display", 0);
273298
defaults.put("CenterWindow", true);
@@ -300,6 +325,10 @@ public final class AppSettings extends HashMap<String, Object> {
300325
defaults.put("WindowYPosition", 0);
301326
defaults.put("WindowXPosition", 0);
302327
defaults.put("X11PlatformPreferred", false);
328+
defaults.put("JoysticksMapper", JOYSTICKS_XBOX_MAPPER);
329+
defaults.put("JoysticksTriggerToButtonThreshold", 0.5f);
330+
defaults.put("JoysticksAxisJitterThreshold", 0.0001f);
331+
defaults.put("SDLGameControllerDBResourcePath", "");
303332
// defaults.put("Icons", null);
304333
}
305334

@@ -1612,4 +1641,85 @@ public void setX11PlatformPreferred(boolean preferred) {
16121641
public boolean isX11PlatformPreferred() {
16131642
return getBoolean("X11PlatformPreferred");
16141643
}
1644+
1645+
/**
1646+
* Set which joystick mapping to use for normalization of controller inputs
1647+
*
1648+
* @param mapper
1649+
* JOYSTICKS_MAPPER_* constant defining which mapping to use
1650+
*/
1651+
public void setJoysticksMapper(String mapper) {
1652+
putString("JoysticksMapper", mapper);
1653+
}
1654+
1655+
/**
1656+
* Get which joystick mapping to use for normalization of controller inputs
1657+
*/
1658+
public String getJoysticksMapper() {
1659+
return getString("JoysticksMapper");
1660+
}
1661+
1662+
/**
1663+
* Sets the threshold above which an analog trigger should also generate a button-press event.
1664+
* If the value is set to -1, the trigger will never generate button-press events.
1665+
*
1666+
* <p>
1667+
* This is intended to normalize behavior between controllers that expose triggers as analog
1668+
* axes and controllers that expose triggers as digital buttons.
1669+
*
1670+
* @param threshold the trigger threshold in the range [0, 1] (default: 0.5f)
1671+
*/
1672+
public void setJoysticksTriggerToButtonThreshold(float threshold) {
1673+
putFloat("JoysticksTriggerToButtonThreshold", threshold);
1674+
}
1675+
1676+
/**
1677+
* Gets the threshold above which an analog trigger should also generate a button-press event.
1678+
*
1679+
* @return the trigger threshold in the range [0, 1] (default: 0.5f)
1680+
* @see #setJoysticksTriggerToButtonThreshold(float)
1681+
*/
1682+
public float getJoysticksTriggerToButtonThreshold() {
1683+
return getFloat("JoysticksTriggerToButtonThreshold");
1684+
}
1685+
1686+
/**
1687+
* Sets the jitter threshold for joystick axes.
1688+
*
1689+
* <p>
1690+
* Axis movements with a delta smaller than this threshold will be ignored. This is intended to reduce
1691+
* noise from analog joysticks.
1692+
*/
1693+
public void setJoysticksAxisJitterThreshold(float threshold) {
1694+
putFloat("JoysticksAxisJitterThreshold", threshold);
1695+
}
1696+
1697+
/**
1698+
* Gets the jitter threshold for joystick axes.
1699+
*
1700+
* @return the jitter threshold
1701+
* @see #setJoysticksAxisJitterThreshold(float)
1702+
*/
1703+
public float getJoysticksAxisJitterThreshold() {
1704+
return getFloat("JoysticksAxisJitterThreshold");
1705+
}
1706+
1707+
/**
1708+
* Set resource path for a custom SDL game controller database.
1709+
*
1710+
* @param path
1711+
*/
1712+
public void setSDLGameControllerDBResourcePath(String path) {
1713+
putString("SDLGameControllerDBResourcePath", path);
1714+
}
1715+
1716+
/**
1717+
* Get resource path for a custom SDL game controller database.
1718+
*
1719+
* @return resource path
1720+
*/
1721+
public String getSDLGameControllerDBResourcePath() {
1722+
return getString("SDLGameControllerDBResourcePath");
1723+
}
16151724
}
1725+

jme3-examples/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ dependencies {
1919
implementation project(':jme3-effects')
2020
implementation project(':jme3-jbullet')
2121
implementation project(':jme3-jogg')
22-
implementation project(':jme3-lwjgl')
23-
// implementation project(':jme3-lwjgl3')
22+
// implementation project(':jme3-lwjgl')
23+
implementation project(':jme3-lwjgl3')
2424
implementation project(':jme3-networking')
2525
implementation project(':jme3-niftygui')
2626
implementation project(':jme3-plugins')

jme3-examples/src/main/java/jme3test/input/TestJoystick.java

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ public class TestJoystick extends SimpleApplication {
4646
public static void main(String[] args){
4747
TestJoystick app = new TestJoystick();
4848
AppSettings settings = new AppSettings(true);
49+
settings.setJoysticksMapper(AppSettings.JOYSTICKS_XBOX_MAPPER);
4950
settings.setUseJoysticks(true);
51+
settings.setX11PlatformPreferred(true);
5052
app.setSettings(settings);
5153
app.start();
5254
}
@@ -155,7 +157,7 @@ protected void setViewedJoystick( Joystick stick ) {
155157

156158
}
157159
}
158-
160+
159161
/**
160162
* Easier to watch for all button and axis events with a raw input listener.
161163
*/
@@ -184,13 +186,15 @@ public void onJoyAxisEvent(JoyAxisEvent evt) {
184186
gamepad.setAxisValue( evt.getAxis(), value );
185187
if( value != 0 ) {
186188
lastValues.put(evt.getAxis(), value);
189+
evt.getAxis().getJoystick().rumble(0.5f);
187190
}
188191
}
189192

190193
@Override
191194
public void onJoyButtonEvent(JoyButtonEvent evt) {
192195
setViewedJoystick( evt.getButton().getJoystick() );
193196
gamepad.setButtonValue( evt.getButton(), evt.isPressed() );
197+
evt.getButton().getJoystick().rumble(1f);
194198
}
195199

196200
@Override
@@ -255,35 +259,39 @@ public GamepadView() {
255259
attachChild(rightStick);
256260

257261
// A "standard" mapping... fits a majority of my game pads
258-
addButton( JoystickButton.BUTTON_0, 371, 512 - 176, 42, 42 );
259-
addButton( JoystickButton.BUTTON_1, 407, 512 - 212, 42, 42 );
260-
addButton( JoystickButton.BUTTON_2, 371, 512 - 248, 42, 42 );
261-
addButton( JoystickButton.BUTTON_3, 334, 512 - 212, 42, 42 );
262+
addButton( JoystickButton.BUTTON_XBOX_Y, 371, 512 - 176, 42, 42 );
263+
addButton( JoystickButton.BUTTON_XBOX_B, 407, 512 - 212, 42, 42 );
264+
addButton( JoystickButton.BUTTON_XBOX_A, 371, 512 - 248, 42, 42 );
265+
addButton( JoystickButton.BUTTON_XBOX_X, 334, 512 - 212, 42, 42 );
262266

263267
// Front buttons Some of these have the top ones and the bottoms ones flipped.
264-
addButton( JoystickButton.BUTTON_4, 67, 512 - 111, 95, 21 );
265-
addButton( JoystickButton.BUTTON_5, 348, 512 - 111, 95, 21 );
266-
addButton( JoystickButton.BUTTON_6, 67, 512 - 89, 95, 21 );
267-
addButton( JoystickButton.BUTTON_7, 348, 512 - 89, 95, 21 );
268+
addButton( JoystickButton.BUTTON_XBOX_LB, 67, 512 - 111, 95, 21 );
269+
addButton( JoystickButton.BUTTON_XBOX_RB, 348, 512 - 111, 95, 21 );
270+
addButton( JoystickButton.BUTTON_XBOX_LT, 67, 512 - 89, 95, 21 );
271+
addButton( JoystickButton.BUTTON_XBOX_RT, 348, 512 - 89, 95, 21 );
268272

269273
// Select and start buttons
270-
addButton( JoystickButton.BUTTON_8, 206, 512 - 198, 48, 30 );
271-
addButton( JoystickButton.BUTTON_9, 262, 512 - 198, 48, 30 );
274+
addButton( JoystickButton.BUTTON_XBOX_BACK, 206, 512 - 198, 48, 30 );
275+
addButton( JoystickButton.BUTTON_XBOX_START, 262, 512 - 198, 48, 30 );
272276

273277
// Joystick push buttons
274-
addButton( JoystickButton.BUTTON_10, 147, 512 - 300, 75, 70 );
275-
addButton( JoystickButton.BUTTON_11, 285, 512 - 300, 75, 70 );
278+
addButton( JoystickButton.BUTTON_XBOX_L3, 147, 512 - 300, 75, 70 );
279+
addButton( JoystickButton.BUTTON_XBOX_R3, 285, 512 - 300, 75, 70 );
276280

277281
// Fake button highlights for the POV axes
278282
//
279283
// +Y
280284
// -X +X
281285
// -Y
282286
//
283-
addButton( "POV +Y", 96, 512 - 174, 40, 38 );
284-
addButton( "POV +X", 128, 512 - 208, 40, 38 );
285-
addButton( "POV -Y", 96, 512 - 239, 40, 38 );
286-
addButton( "POV -X", 65, 512 - 208, 40, 38 );
287+
// addButton( "POV +Y", 96, 512 - 174, 40, 38 );
288+
// addButton( "POV +X", 128, 512 - 208, 40, 38 );
289+
// addButton( "POV -Y", 96, 512 - 239, 40, 38 );
290+
// addButton( "POV -X", 65, 512 - 208, 40, 38 );
291+
addButton( JoystickButton.BUTTON_XBOX_DPAD_UP, 96, 512 - 174, 40, 38 );
292+
addButton( JoystickButton.BUTTON_XBOX_DPAD_RIGHT, 128, 512 - 208, 40, 38 );
293+
addButton( JoystickButton.BUTTON_XBOX_DPAD_DOWN, 96, 512 - 239, 40, 38 );
294+
addButton( JoystickButton.BUTTON_XBOX_DPAD_LEFT, 65, 512 - 208, 40, 38 );
287295

288296
resetPositions();
289297
}
@@ -295,30 +303,29 @@ private void addButton( String name, float x, float y, float width, float height
295303
}
296304

297305
public void setAxisValue( JoystickAxis axis, float value ) {
298-
299-
System.out.println( "Axis:" + axis.getName() + "(id:" + axis.getLogicalId() + ")=" + value );
300-
if( axis == axis.getJoystick().getXAxis() ) {
306+
307+
if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_LEFT_THUMB_STICK_X)){
301308
setXAxis(value);
302-
} else if( axis == axis.getJoystick().getYAxis() ) {
309+
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_LEFT_THUMB_STICK_Y)){
303310
setYAxis(-value);
304-
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.Z_AXIS) ) {
311+
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_RIGHT_THUMB_STICK_X)) {
305312
// Note: in the above condition, we could check the axis name, but
306313
// I have at least one joystick that reports 2 "Z Axis" axes.
307314
// In this particular case, the first one is the right one so
308315
// a name based lookup will find the proper one. It's a problem
309316
// because the erroneous axis sends a constant stream of values.
310317
setZAxis(value);
311-
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.Z_ROTATION) ) {
318+
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_RIGHT_THUMB_STICK_Y) ) {
312319
setZRotation(-value);
313-
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.LEFT_TRIGGER) ) {
320+
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_LEFT_TRIGGER) ) {
314321
if( axis.getJoystick().getButton(JoystickButton.BUTTON_6) == null ) {
315322
// left/right triggers sometimes only show up as axes
316323
boolean pressed = value != 0;
317324
if( pressed != buttons.get(JoystickButton.BUTTON_6).isDown() ) {
318325
setButtonValue(JoystickButton.BUTTON_6, pressed);
319326
}
320327
}
321-
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.RIGHT_TRIGGER) ) {
328+
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_RIGHT_TRIGGER) ) {
322329
if( axis.getJoystick().getButton(JoystickButton.BUTTON_7) == null ) {
323330
// left/right triggers sometimes only show up as axes
324331
boolean pressed = value != 0;

0 commit comments

Comments
 (0)