Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ javadoc_deploy.pub
!.vscode/settings.json
!.vscode/JME_style.xml
!.vscode/extensions.json
joysticks-*.txt
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ lwjgl3-jemalloc = { module = "org.lwjgl:lwjgl-jemalloc", version.ref = "lwjgl3"
lwjgl3-openal = { module = "org.lwjgl:lwjgl-openal", version.ref = "lwjgl3" }
lwjgl3-opencl = { module = "org.lwjgl:lwjgl-opencl", version.ref = "lwjgl3" }
lwjgl3-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl3" }
lwjgl3-sdl = { module = "org.lwjgl:lwjgl-sdl", version.ref = "lwjgl3" }

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class DefaultJoystickAxis implements JoystickAxis {
private final boolean isAnalog;
private final boolean isRelative;
private float deadZone;
private float jitterThreshold = 0f;

/**
* Creates a new joystick axis instance. Only used internally.
Expand Down Expand Up @@ -166,6 +167,12 @@ public void setDeadZone(float f) {
public String toString() {
return "JoystickAxis[name=" + name + ", parent=" + parent.getName() + ", id=" + axisIndex
+ ", logicalId=" + logicalId + ", isAnalog=" + isAnalog
+ ", isRelative=" + isRelative + ", deadZone=" + deadZone + "]";
+ ", isRelative=" + isRelative + ", deadZone=" + deadZone +
", jitterThreshold=" + jitterThreshold + "]";
}

@Override
public float getJitterThreshold() {
return jitterThreshold;
}
}
40 changes: 19 additions & 21 deletions jme3-core/src/main/java/com/jme3/input/JoystickAxis.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,14 @@ public interface JoystickAxis {
public static final String Z_ROTATION = "rz";
public static final String LEFT_TRIGGER = "rx";
public static final String RIGHT_TRIGGER = "ry";

// Note: the left/right trigger bit may be a bit controversial in
// the sense that this is one case where XBox controllers make a lot
// more sense.
// I've seen the following mappings for various things:
//
// Axis | XBox | Non-Xbox (generally) (includes actual Sony PS4 controllers)
// --------------+-------+---------------
// left trigger | z | rx (also button 6)
// right trigger | rz | ry (also button 7)
// left stick x | x | x
// left stick y | y | y
// right stick x | rx | z
// right stick y | ry | rz
//
// The issue is that in all cases I've seen, the XBox controllers will
// use the name "xbox" somewhere in their name. The Non-XBox controllers
// never mention anything uniform... even the PS4 controller only calls
// itself "Wireless Controller". In that light, it seems easier to make
// the default the ugly case and the "XBox" way the exception because it
// can more easily be identified.

public static final String AXIS_XBOX_LEFT_TRIGGER = LEFT_TRIGGER;
public static final String AXIS_XBOX_RIGHT_TRIGGER = RIGHT_TRIGGER;
public static final String AXIS_XBOX_LEFT_THUMB_STICK_X = X_AXIS;
public static final String AXIS_XBOX_LEFT_THUMB_STICK_Y = Y_AXIS;
public static final String AXIS_XBOX_RIGHT_THUMB_STICK_X = Z_AXIS;
public static final String AXIS_XBOX_RIGHT_THUMB_STICK_Y = Z_ROTATION;


public static final String POV_X = "pov_x";
public static final String POV_Y = "pov_y";
Expand Down Expand Up @@ -128,4 +115,15 @@ public interface JoystickAxis {
* @return the radius of the dead zone
*/
public float getDeadZone();


/**
* Returns the suggested jitter threshold for this axis. Movements with a delta
* smaller than this threshold will be ignored by the backend input system
*
* @return the jitter threshold
*/
public default float getJitterThreshold(){
return 0;
}
}
20 changes: 20 additions & 0 deletions jme3-core/src/main/java/com/jme3/input/JoystickButton.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ public interface JoystickButton {
public static final String BUTTON_14 = "14";
public static final String BUTTON_15 = "15";


public static final String BUTTON_XBOX_A = BUTTON_2;
public static final String BUTTON_XBOX_B = BUTTON_1;
public static final String BUTTON_XBOX_X = BUTTON_3;
public static final String BUTTON_XBOX_Y = BUTTON_0;
public static final String BUTTON_XBOX_LB = BUTTON_4;
public static final String BUTTON_XBOX_RB = BUTTON_5;
public static final String BUTTON_XBOX_LT = BUTTON_6;
public static final String BUTTON_XBOX_RT = BUTTON_7;
public static final String BUTTON_XBOX_BACK = BUTTON_8;
public static final String BUTTON_XBOX_START = BUTTON_9;
public static final String BUTTON_XBOX_L3 = BUTTON_10;
public static final String BUTTON_XBOX_R3 = BUTTON_11;

public static final String BUTTON_XBOX_DPAD_UP = BUTTON_12;
public static final String BUTTON_XBOX_DPAD_DOWN = BUTTON_13;
public static final String BUTTON_XBOX_DPAD_LEFT = BUTTON_14;
public static final String BUTTON_XBOX_DPAD_RIGHT = BUTTON_15;


/**
* Assign the mapping name to receive events from the given button index
* on the joystick.
Expand Down
110 changes: 110 additions & 0 deletions jme3-core/src/main/java/com/jme3/system/AppSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,31 @@ public final class AppSettings extends HashMap<String, Object> {
*/
public static final String JOAL = "JOAL";

/**
* Map gamepads to Xbox-like layout.
*/
public static final String JOYSTICKS_XBOX_MAPPER = "JOYSTICKS_XBOX_MAPPER";

/**
* Map gamepads to an Xbox-like layout, with fallback to raw if the gamepad is not recognized.
*/
public static final String JOYSTICKS_XBOX_WITH_FALLBACK_MAPPER = "JOYSTICKS_XBOX_WITH_FALLBACK_MAPPER";

/**
* Map gamepads to an Xbox-like layout using the legacy jME input
*/
public static final String JOYSTICKS_XBOX_LEGACY_MAPPER = "JOYSTICKS_XBOX_LEGACY_MAPPER";

/**
* Map gamepads using the legacy jME mapper and input.
*/
public static final String JOYSTICKS_LEGACY_MAPPER = "JOYSTICKS_LEGACY_MAPPER";

/**
* Don't map gamepads, use raw events instead (ie. bring your own mapper)
*/
public static final String JOYSTICKS_RAW_MAPPER = "JOYSTICKS_RAW_MAPPER";

static {
defaults.put("Display", 0);
defaults.put("CenterWindow", true);
Expand Down Expand Up @@ -300,6 +325,10 @@ public final class AppSettings extends HashMap<String, Object> {
defaults.put("WindowYPosition", 0);
defaults.put("WindowXPosition", 0);
defaults.put("X11PlatformPreferred", false);
defaults.put("JoysticksMapper", JOYSTICKS_XBOX_MAPPER);
defaults.put("JoysticksTriggerToButtonThreshold", 0.5f);
defaults.put("JoysticksAxisJitterThreshold", 0.0001f);
defaults.put("SDLGameControllerDBResourcePath", "");
// defaults.put("Icons", null);
}

Expand Down Expand Up @@ -1612,4 +1641,85 @@ public void setX11PlatformPreferred(boolean preferred) {
public boolean isX11PlatformPreferred() {
return getBoolean("X11PlatformPreferred");
}

/**
* Set which joystick mapping to use for normalization of controller inputs
*
* @param mapper
* JOYSTICKS_MAPPER_* constant defining which mapping to use
*/
public void setJoysticksMapper(String mapper) {
putString("JoysticksMapper", mapper);
}

/**
* Get which joystick mapping to use for normalization of controller inputs
*/
public String getJoysticksMapper() {
return getString("JoysticksMapper");
}

/**
* Sets the threshold above which an analog trigger should also generate a button-press event.
* If the value is set to -1, the trigger will never generate button-press events.
*
* <p>
* This is intended to normalize behavior between controllers that expose triggers as analog
* axes and controllers that expose triggers as digital buttons.
*
* @param threshold the trigger threshold in the range [0, 1] (default: 0.5f)
*/
public void setJoysticksTriggerToButtonThreshold(float threshold) {
putFloat("JoysticksTriggerToButtonThreshold", threshold);
}

/**
* Gets the threshold above which an analog trigger should also generate a button-press event.
*
* @return the trigger threshold in the range [0, 1] (default: 0.5f)
* @see #setJoysticksTriggerToButtonThreshold(float)
*/
public float getJoysticksTriggerToButtonThreshold() {
return getFloat("JoysticksTriggerToButtonThreshold");
}

/**
* Sets the jitter threshold for joystick axes.
*
* <p>
* Axis movements with a delta smaller than this threshold will be ignored. This is intended to reduce
* noise from analog joysticks.
*/
public void setJoysticksAxisJitterThreshold(float threshold) {
putFloat("JoysticksAxisJitterThreshold", threshold);
}

/**
* Gets the jitter threshold for joystick axes.
*
* @return the jitter threshold
* @see #setJoysticksAxisJitterThreshold(float)
*/
public float getJoysticksAxisJitterThreshold() {
return getFloat("JoysticksAxisJitterThreshold");
}

/**
* Set resource path for a custom SDL game controller database.
*
* @param path
*/
public void setSDLGameControllerDBResourcePath(String path) {
putString("SDLGameControllerDBResourcePath", path);
}

/**
* Get resource path for a custom SDL game controller database.
*
* @return resource path
*/
public String getSDLGameControllerDBResourcePath() {
return getString("SDLGameControllerDBResourcePath");
}
}

4 changes: 2 additions & 2 deletions jme3-examples/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ dependencies {
implementation project(':jme3-effects')
implementation project(':jme3-jbullet')
implementation project(':jme3-jogg')
implementation project(':jme3-lwjgl')
// implementation project(':jme3-lwjgl3')
// implementation project(':jme3-lwjgl')
implementation project(':jme3-lwjgl3')
implementation project(':jme3-networking')
implementation project(':jme3-niftygui')
implementation project(':jme3-plugins')
Expand Down
57 changes: 32 additions & 25 deletions jme3-examples/src/main/java/jme3test/input/TestJoystick.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ public class TestJoystick extends SimpleApplication {
public static void main(String[] args){
TestJoystick app = new TestJoystick();
AppSettings settings = new AppSettings(true);
settings.setJoysticksMapper(AppSettings.JOYSTICKS_XBOX_MAPPER);
settings.setUseJoysticks(true);
settings.setX11PlatformPreferred(true);
app.setSettings(settings);
app.start();
}
Expand Down Expand Up @@ -155,7 +157,7 @@ protected void setViewedJoystick( Joystick stick ) {

}
}

/**
* Easier to watch for all button and axis events with a raw input listener.
*/
Expand Down Expand Up @@ -184,13 +186,15 @@ public void onJoyAxisEvent(JoyAxisEvent evt) {
gamepad.setAxisValue( evt.getAxis(), value );
if( value != 0 ) {
lastValues.put(evt.getAxis(), value);
evt.getAxis().getJoystick().rumble(0.5f);
}
}

@Override
public void onJoyButtonEvent(JoyButtonEvent evt) {
setViewedJoystick( evt.getButton().getJoystick() );
gamepad.setButtonValue( evt.getButton(), evt.isPressed() );
evt.getButton().getJoystick().rumble(1f);
}

@Override
Expand Down Expand Up @@ -255,35 +259,39 @@ public GamepadView() {
attachChild(rightStick);

// A "standard" mapping... fits a majority of my game pads
addButton( JoystickButton.BUTTON_0, 371, 512 - 176, 42, 42 );
addButton( JoystickButton.BUTTON_1, 407, 512 - 212, 42, 42 );
addButton( JoystickButton.BUTTON_2, 371, 512 - 248, 42, 42 );
addButton( JoystickButton.BUTTON_3, 334, 512 - 212, 42, 42 );
addButton( JoystickButton.BUTTON_XBOX_Y, 371, 512 - 176, 42, 42 );
addButton( JoystickButton.BUTTON_XBOX_B, 407, 512 - 212, 42, 42 );
addButton( JoystickButton.BUTTON_XBOX_A, 371, 512 - 248, 42, 42 );
addButton( JoystickButton.BUTTON_XBOX_X, 334, 512 - 212, 42, 42 );

// Front buttons Some of these have the top ones and the bottoms ones flipped.
addButton( JoystickButton.BUTTON_4, 67, 512 - 111, 95, 21 );
addButton( JoystickButton.BUTTON_5, 348, 512 - 111, 95, 21 );
addButton( JoystickButton.BUTTON_6, 67, 512 - 89, 95, 21 );
addButton( JoystickButton.BUTTON_7, 348, 512 - 89, 95, 21 );
addButton( JoystickButton.BUTTON_XBOX_LB, 67, 512 - 111, 95, 21 );
addButton( JoystickButton.BUTTON_XBOX_RB, 348, 512 - 111, 95, 21 );
addButton( JoystickButton.BUTTON_XBOX_LT, 67, 512 - 89, 95, 21 );
addButton( JoystickButton.BUTTON_XBOX_RT, 348, 512 - 89, 95, 21 );

// Select and start buttons
addButton( JoystickButton.BUTTON_8, 206, 512 - 198, 48, 30 );
addButton( JoystickButton.BUTTON_9, 262, 512 - 198, 48, 30 );
addButton( JoystickButton.BUTTON_XBOX_BACK, 206, 512 - 198, 48, 30 );
addButton( JoystickButton.BUTTON_XBOX_START, 262, 512 - 198, 48, 30 );

// Joystick push buttons
addButton( JoystickButton.BUTTON_10, 147, 512 - 300, 75, 70 );
addButton( JoystickButton.BUTTON_11, 285, 512 - 300, 75, 70 );
addButton( JoystickButton.BUTTON_XBOX_L3, 147, 512 - 300, 75, 70 );
addButton( JoystickButton.BUTTON_XBOX_R3, 285, 512 - 300, 75, 70 );

// Fake button highlights for the POV axes
//
// +Y
// -X +X
// -Y
//
addButton( "POV +Y", 96, 512 - 174, 40, 38 );
addButton( "POV +X", 128, 512 - 208, 40, 38 );
addButton( "POV -Y", 96, 512 - 239, 40, 38 );
addButton( "POV -X", 65, 512 - 208, 40, 38 );
// addButton( "POV +Y", 96, 512 - 174, 40, 38 );
// addButton( "POV +X", 128, 512 - 208, 40, 38 );
// addButton( "POV -Y", 96, 512 - 239, 40, 38 );
// addButton( "POV -X", 65, 512 - 208, 40, 38 );
addButton( JoystickButton.BUTTON_XBOX_DPAD_UP, 96, 512 - 174, 40, 38 );
addButton( JoystickButton.BUTTON_XBOX_DPAD_RIGHT, 128, 512 - 208, 40, 38 );
addButton( JoystickButton.BUTTON_XBOX_DPAD_DOWN, 96, 512 - 239, 40, 38 );
addButton( JoystickButton.BUTTON_XBOX_DPAD_LEFT, 65, 512 - 208, 40, 38 );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

binding dpad buttons is more intuitive, however the POV axis is still supported


resetPositions();
}
Expand All @@ -295,30 +303,29 @@ private void addButton( String name, float x, float y, float width, float height
}

public void setAxisValue( JoystickAxis axis, float value ) {

System.out.println( "Axis:" + axis.getName() + "(id:" + axis.getLogicalId() + ")=" + value );
if( axis == axis.getJoystick().getXAxis() ) {

if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_LEFT_THUMB_STICK_X)){
setXAxis(value);
} else if( axis == axis.getJoystick().getYAxis() ) {
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_LEFT_THUMB_STICK_Y)){
setYAxis(-value);
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.Z_AXIS) ) {
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_RIGHT_THUMB_STICK_X)) {
// Note: in the above condition, we could check the axis name, but
// I have at least one joystick that reports 2 "Z Axis" axes.
// In this particular case, the first one is the right one so
// a name based lookup will find the proper one. It's a problem
// because the erroneous axis sends a constant stream of values.
setZAxis(value);
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.Z_ROTATION) ) {
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_RIGHT_THUMB_STICK_Y) ) {
setZRotation(-value);
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.LEFT_TRIGGER) ) {
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_LEFT_TRIGGER) ) {
if( axis.getJoystick().getButton(JoystickButton.BUTTON_6) == null ) {
// left/right triggers sometimes only show up as axes
boolean pressed = value != 0;
if( pressed != buttons.get(JoystickButton.BUTTON_6).isDown() ) {
setButtonValue(JoystickButton.BUTTON_6, pressed);
}
}
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.RIGHT_TRIGGER) ) {
} else if( axis == axis.getJoystick().getAxis(JoystickAxis.AXIS_XBOX_RIGHT_TRIGGER) ) {
if( axis.getJoystick().getButton(JoystickButton.BUTTON_7) == null ) {
// left/right triggers sometimes only show up as axes
boolean pressed = value != 0;
Expand Down
Loading