Sfoglia il codice sorgente

Joy axis update (#1483)

* Allows the remapper to distinguish between axis and buttons.

Some controllers (such as wireless Xbox 360 controllers on Linux) use the same names for both buttons and controllers (i.e. both the right trigger axis and the right bumper button are named "5").

* Enables the ability to remap the effective range of an axis.

This allows developers to restrict a certain axis to a bound set of values (for example, preventing a one-way trigger access from going below 0, or compensating for a damaged joystick.

* Adds axis range remapping to LWJGL and LWJGL3 modules.

* Adds the old constructor back.

Better for backwards compatibility.

* Fixes the unnecessary whitespace changes.

* Fixes some documentation issues.

* Fixes a merge conflict in the header of JInputJoyInput

* Removed fine logging.
Markil3 4 anni fa
parent
commit
39fc410da4

+ 6 - 4
jme3-android/src/main/java/com/jme3/input/android/AndroidJoystickJoyInput14.java

@@ -177,6 +177,7 @@ public class AndroidJoystickJoyInput14 {
 
     public boolean onGenericMotion(MotionEvent event) {
         boolean consumed = false;
+        float rawValue, value;
 //        logger.log(Level.INFO, "onGenericMotion event: {0}", event);
         event.getDeviceId();
         event.getSource();
@@ -185,7 +186,8 @@ public class AndroidJoystickJoyInput14 {
         if (joystick != null) {
             for (int androidAxis: joystick.getAndroidAxes()) {
                 String axisName = MotionEvent.axisToString(androidAxis);
-                float value = event.getAxisValue(androidAxis);
+                rawValue = event.getAxisValue(androidAxis);
+                value = JoystickCompatibilityMappings.remapAxisRange(joystick.getAxis(androidAxis), rawValue);
                 int action = event.getAction();
                 if (action == MotionEvent.ACTION_MOVE) {
 //                    logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}",
@@ -194,7 +196,7 @@ public class AndroidJoystickJoyInput14 {
                     if (axis != null) {
 //                        logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}, deadzone: {3}",
 //                                new Object[]{androidAxis, axisName, value, axis.getDeadZone()});
-                        JoyAxisEvent axisEvent = new JoyAxisEvent(axis, value);
+                        JoyAxisEvent axisEvent = new JoyAxisEvent(axis, value, rawValue);
                         joyInput.addEvent(axisEvent);
                         consumed = true;
                     } else {
@@ -316,7 +318,7 @@ public class AndroidJoystickJoyInput14 {
                 original = JoystickButton.BUTTON_11;
             }
 
-            String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original );
+            String logicalId = JoystickCompatibilityMappings.remapButton( getName(), original );
             if( logicalId == null ? original != null : !logicalId.equals(original) ) {
                 logger.log(Level.FINE, "Remapped: {0} to: {1}",
                         new Object[]{original, logicalId});
@@ -347,7 +349,7 @@ public class AndroidJoystickJoyInput14 {
             } else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) {
                 original = JoystickAxis.POV_Y;
             }
-            String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original );
+            String logicalId = JoystickCompatibilityMappings.remapAxis( getName(), original );
             if( logicalId == null ? original != null : !logicalId.equals(original) ) {
                 logger.log(Level.FINE, "Remapped: {0} to: {1}",
                         new Object[]{original, logicalId});

+ 2 - 2
jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java

@@ -374,7 +374,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
                                 sensorData.haveData = true;
                             } else {
                                 if (axis.isChanged()) {
-                                    joyInput.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
+                                    joyInput.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue(), axis.getJoystickAxisValue()));
                                 }
                             }
                         }
@@ -553,7 +553,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
                             sensorData.haveData = true;
                         } else {
                             if (axis.isChanged()) {
-                                JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue());
+                                JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue(), axis.getJoystickAxisValue());
 //                                logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event);
                                 joyInput.addEvent(event);
 //                                joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));

+ 403 - 92
jme3-core/src/main/java/com/jme3/input/JoystickCompatibilityMappings.java

@@ -46,14 +46,15 @@ import java.util.regex.Pattern;
 
 
 /**
- *  Provides compatibility mapping to different joysticks
- *  that both report their name in a unique way and require
- *  remapping to achieve a proper default layout.
+ * Provides compatibility mapping to different joysticks
+ * that both report their name in a unique way and require
+ * remapping to achieve a proper default layout.
  *
- *  <p>All mappings MUST be defined before the joystick support
- *  has been initialized in the InputManager.</p>
+ * <p>All mappings MUST be defined before the joystick support
+ * has been initialized in the InputManager.</p>
  *
- *  @author    Paul Speed
+ * @author Paul Speed
+ * @author Markil3
  */
 public class JoystickCompatibilityMappings {
 
@@ -61,9 +62,12 @@ public class JoystickCompatibilityMappings {
 
     // List of resource paths to check for the joystick-mapping.properties
     // files.
-    final private static String[] searchPaths = { "joystick-mapping.properties" };  
+    private static String[] searchPaths = {"joystick-mapping.properties"};
 
-    final private static Map<String,Map<String,String>> joystickMappings = new HashMap<String,Map<String,String>>();
+    private static Map<String, Map<String, String>> joystickMappings = new HashMap<String, Map<String, String>>();
+    private static Map<String, Map<String, AxisData>> axisMappings = new HashMap<String, Map<String, AxisData>>();
+    private static Map<JoystickAxis, float[]> axisRangeMappings = new HashMap<>();
+    private static Map<String, Map<String, String>> buttonMappings = new HashMap<String, Map<String, String>>();
 
     // Remaps names by regex.
     final private static Map<Pattern, String> nameRemappings = new HashMap<>();
@@ -79,159 +83,466 @@ public class JoystickCompatibilityMappings {
     private JoystickCompatibilityMappings() {
     }
 
-    protected static Map<String,String> getMappings( String joystickName, boolean create ) {
-        Map<String,String> result = joystickMappings.get(joystickName.trim());
-        if( result == null && create ) {
-            result = new HashMap<String,String>();
-            joystickMappings.put(joystickName.trim(),result);
+    protected static Map<String, String> getMappings(String joystickName, boolean create) {
+        Map<String, String> result = joystickMappings.get(joystickName.trim());
+        if (result == null && create) {
+            result = new HashMap<String, String>();
+            joystickMappings.put(joystickName.trim(), result);
         }
-        return result;          
+        return result;
     }
- 
+
+    /**
+     * Obtains mappings specific to the joystick axis
+     *
+     * @param joystickName - The name of the joystick type to obtain mappings for.
+     * @param create       - If there are no mappings present and this parameter is true, then a new entry for this joystick is created.
+     * @return The various axis remappings for the requested joystick, or null of there are none.
+     * @author Markil3
+     */
+    protected static Map<String, AxisData> getAxisMappings(String joystickName, boolean create) {
+        Map<String, AxisData> result = axisMappings.get(joystickName.trim());
+        if (result == null && create) {
+            result = new HashMap<String, AxisData>();
+            axisMappings.put(joystickName.trim(), result);
+        }
+        return result;
+    }
+
+    /**
+     * Obtains mappings specific to the joystick buttons
+     *
+     * @param joystickName - The name of the joystick type to obtain mappings for.
+     * @param create       - If there are no mappings present and this parameter is true, then a new entry for this joystick is created.
+     * @return The various button remappings for the requested joystick, or null of there are none.
+     * @author Markil3
+     */
+    protected static Map<String, String> getButtonMappings(String joystickName, boolean create) {
+        Map<String, String> result = buttonMappings.get(joystickName.trim());
+        if (result == null && create) {
+            result = new HashMap<String, String>();
+            buttonMappings.put(joystickName.trim(), result);
+        }
+        return result;
+    }
+
+    /**
+     * This method will take a "raw" axis value from the system and rescale it based on what the remapper has specified. For example, if the remapper specified an axis to be scaled to [0.0,1.0], then a raw value of -0.5 would be converted to 0.25.
+     *
+     * @param axis         - The axis to remap.
+     * @param currentValue - The raw value the system is outputting, on a scale of -1.0 to 1.0.
+     * @return The new value that will be provided to listeners, on a scale specified by the remappings file.
+     * @author Markil3
+     */
+    public static float remapAxisRange(JoystickAxis axis, float currentValue) {
+        String joyName = axis.getJoystick().getName();
+        Map<String, AxisData> map;
+        float[] range = axisRangeMappings.get(axis);
+        if (range == null) {
+            map = getAxisMappings(joyName, false);
+            if (map != null && map.containsKey(axis.getName())) {
+                range = map.get(axis.getName()).range;
+                axisRangeMappings.put(axis, range);
+            } else {
+                // Try the normalized name
+                joyName = getNormalizedName(joyName);
+                if (joyName != null) {
+                    map = getAxisMappings(joyName, false);
+                    if (map != null && map.containsKey(axis.getName())) {
+                        range = map.get(axis.getName()).range;
+                        axisRangeMappings.put(axis, range);
+                    }
+                }
+            }
+        }
+        if (range == null) {
+            axisRangeMappings.put(axis, new float[0]);
+            return currentValue;
+        }
+
+        /*
+         * If we have an array of size 0, that means we have acknowledged this axis (so we don't
+         * need to go searching for it every tick), but that there is no remapping.
+         */
+        if (range.length == 0) {
+            return currentValue;
+        }
+
+        return (currentValue + range[1] + range[0]) * ((range[1] - range[0]) / 2);
+    }
+
+    /**
+     * Takes the original name of an axis, specifically, and returns the new name it will function under.
+     *
+     * @param joystickName - The joystick type the axis comes from.
+     * @param componentId  - The system-provided name for the axis.
+     * @return The new name for the axis, or just componentId if no remapping was provided.
+     * @author Markil3
+     */
+    public static String remapAxis(String joystickName, String componentId) {
+        logger.log(Level.FINE, "remapAxis(" + joystickName + ", " + componentId + ")");
+
+        // Always try the specific name first.
+        joystickName = joystickName.trim();
+        Map map = getAxisMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped axis:" + map.get(componentId));
+            return ((AxisData) map.get(componentId)).name;
+        }
+
+        map = getMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped axis:" + map.get(componentId));
+            return ((String) map.get(componentId));
+        }
+
+        // Try the normalized name
+        joystickName = getNormalizedName(joystickName);
+        logger.log(Level.FINE, "normalized joystick name:" + joystickName);
+        if (joystickName == null) {
+            return componentId;
+        }
+
+        map = getAxisMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped:" + map.get(componentId));
+            return ((AxisData) map.get(componentId)).name;
+        }
+
+        map = getMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped:" + map.get(componentId));
+            return ((String) map.get(componentId));
+        }
+
+        return componentId;
+    }
+
+    /**
+     * Takes the original name of an button, specifically, and returns the new name it will function under.
+     *
+     * @param joystickName - The joystick type the axis comes from.
+     * @param componentId  - The system-provided name for the button.
+     * @return The new name for the button, or just componentId if no remapping was provided.
+     * @author Markil3
+     */
+    public static String remapButton(String joystickName, String componentId) {
+        logger.log(Level.FINE, "remapAxis(" + joystickName + ", " + componentId + ")");
+
+
+        // Always try the specific name first.
+        joystickName = joystickName.trim();
+        Map<String, String> map = getButtonMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped axis:" + map.get(componentId));
+            return map.get(componentId);
+        }
+
+        map = getMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped axis:" + map.get(componentId));
+            return map.get(componentId);
+        }
+
+        // Try the normalized name
+        joystickName = getNormalizedName(joystickName);
+        logger.log(Level.FINE, "normalized joystick name:" + joystickName);
+        if (joystickName == null) {
+            return componentId;
+        }
+
+        map = getButtonMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped:" + map.get(componentId));
+            return map.get(componentId);
+        }
+
+        map = getMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped:" + map.get(componentId));
+            return map.get(componentId);
+        }
+
+        return componentId;
+    }
+
     /**
-     *  Returns the remapped version of the axis/button name if there
-     *  is a mapping for it otherwise it returns the original name.
+     * Returns the remapped version of the axis/button name if there
+     * is a mapping for it otherwise it returns the original name.
      */
-    public static String remapComponent( String joystickName, String componentId ) {
+    public static String remapComponent(String joystickName, String componentId) {
         logger.log(Level.FINE, "remapComponent(" + joystickName + ", " + componentId + ")");
-         
+
         // Always try the specific name first.
-        joystickName = joystickName.trim();     
-        Map<String,String> map = getMappings(joystickName, false);   
-        if( map != null && map.containsKey(componentId) ) {
-            logger.log(Level.FINE, "returning remapped:" + map.get(componentId));        
+        joystickName = joystickName.trim();
+        Map<String, String> map = getMappings(joystickName, false);
+        if (map != null && map.containsKey(componentId)) {
+            logger.log(Level.FINE, "returning remapped:" + map.get(componentId));
             return map.get(componentId);
         }
         // Try the normalized name
         joystickName = getNormalizedName(joystickName);
-        logger.log(Level.FINE, "normalized joystick name:" + joystickName);         
-        if( joystickName == null ) {
+        logger.log(Level.FINE, "normalized joystick name:" + joystickName);
+        if (joystickName == null) {
             return componentId;
         }
         map = getMappings(joystickName, false);
-        if( map == null ) {
+        if (map == null) {
             return componentId;
-        }   
-        if( !map.containsKey(componentId) ) {
+        }
+        if (!map.containsKey(componentId)) {
             return componentId;
         }
-        logger.log(Level.FINE, "returning remapped:" + map.get(componentId));        
-        return map.get(componentId); 
-    }       
- 
+        logger.log(Level.FINE, "returning remapped:" + map.get(componentId));
+        return map.get(componentId);
+    }
+
+    /**
+     * Returns a set of Joystick axis name remappings if they exist otherwise
+     * it returns an empty map.
+     *
+     * @author Markil3
+     */
+    public static Map<String, AxisData> getJoystickAxisMappings(String joystickName) {
+        Map<String, AxisData> result = getAxisMappings(joystickName.trim(), false);
+        if (result == null)
+            return Collections.emptyMap();
+        return Collections.unmodifiableMap(result);
+    }
+
     /**
-     *  Returns a set of Joystick axis/button name remappings if they exist otherwise
-     *  it returns an empty map.
+     * Returns a set of Joystick button name remappings if they exist otherwise
+     * it returns an empty map.
+     *
+     * @author Markil3
      */
-    public static Map<String,String> getJoystickMappings( String joystickName ) {
-        Map<String,String> result = getMappings(joystickName.trim(), false);
-        if( result == null )
+    public static Map<String, String> getJoystickButtonMappings(String joystickName) {
+        Map<String, String> result = getButtonMappings(joystickName.trim(), false);
+        if (result == null)
             return Collections.emptyMap();
         return Collections.unmodifiableMap(result);
     }
-    
+
+    /**
+     * Returns a set of Joystick axis/button name remappings if they exist otherwise
+     * it returns an empty map.
+     */
+    public static Map<String, String> getJoystickMappings(String joystickName) {
+        Map<String, String> result = getMappings(joystickName.trim(), false);
+        if (result == null)
+            return Collections.emptyMap();
+        return Collections.unmodifiableMap(result);
+    }
+
+    /**
+     * Adds a single Joystick axis or button remapping based on the
+     * joystick's name and axis/button name.  The "remap" value will be
+     * used instead.
+     *
+     * @author Markil3
+     */
+    public static void addAxisMapping(String stickName, String sourceComponentId, String remapId) {
+        logger.log(Level.FINE, "addAxisMapping(" + stickName + ", " + sourceComponentId + ", " + remapId + ")");
+        getAxisMappings(stickName, true).put(sourceComponentId, new AxisData(remapId, new float[0]));
+    }
+
+    /**
+     * Adds a single Joystick axis or button remapping based on the
+     * joystick's name and axis/button name.  The "remap" value will be
+     * used instead.
+     *
+     * @author Markil3
+     */
+    public static void addAxisMapping(String stickName, String sourceComponentId, String remapId, float[] range) {
+        logger.log(Level.FINE, "addAxisMapping(" + stickName + ", " + sourceComponentId + ", " + remapId + ")");
+        if (range.length != 2) {
+            throw new IllegalArgumentException("The range must have exactly 2 elements");
+        }
+        getAxisMappings(stickName, true).put(sourceComponentId, new AxisData(remapId, range));
+    }
+
     /**
-     *  Adds a single Joystick axis or button remapping based on the 
-     *  joystick's name and axis/button name.  The "remap" value will be
-     *  used instead.
+     * Adds a single Joystick axis or button remapping based on the
+     * joystick's name and axis/button name.  The "remap" value will be
+     * used instead.
+     *
+     * @author Markil3
      */
-    public static void addMapping( String stickName, String sourceComponentId, String remapId ) {
-        logger.log(Level.FINE, "addMapping(" + stickName + ", " + sourceComponentId + ", " + remapId + ")" );        
-        getMappings(stickName, true).put( sourceComponentId, remapId );
-    } 
- 
+    public static void addButtonMapping(String stickName, String sourceComponentId, String remapId) {
+        logger.log(Level.FINE, "addButtonMapping(" + stickName + ", " + sourceComponentId + ", " + remapId + ")");
+        getButtonMappings(stickName, true).put(sourceComponentId, remapId);
+    }
+
     /**
-     *  Adds a preconfigured set of mappings in Properties object
-     *  form where the names are dot notation "joystick"."axis/button"
-     *  and the values are the remapped component name.  This calls
-     *  addMapping(stickName, sourceComponent, remap) for every property
-     *  that it is able to parse.
+     * Adds a single Joystick axis or button remapping based on the
+     * joystick's name and axis/button name.  The "remap" value will be
+     * used instead.
      */
-    public static void addMappings( Properties p ) {
-        for( Map.Entry<Object,Object> e : p.entrySet() ) {
+    public static void addMapping(String stickName, String sourceComponentId, String remapId) {
+        logger.log(Level.FINE, "addMapping(" + stickName + ", " + sourceComponentId + ", " + remapId + ")");
+        getMappings(stickName, true).put(sourceComponentId, remapId);
+    }
+
+    /**
+     * Adds a preconfigured set of mappings in Properties object
+     * form where the names are dot notation
+     * "axis"/"button"/"". "joystick"."axis/button name"
+     * and the values are the remapped component name.  This calls
+     * addMapping(stickName, sourceComponent, remap) for every property
+     * that it is able to parse.
+     *
+     * @author Paul Speed
+     * @author Markil 3
+     */
+    public static void addMappings(Properties p) {
+        final String AXIS_LABEL = "axis";
+        final String BUTTON_LABEL = "button";
+
+        float[] range;
+        int lBrackIndex, rBrackIndex, commaIndex;
+
+        for (Map.Entry<Object, Object> e : p.entrySet()) {
+            range = null;
             String key = String.valueOf(e.getKey()).trim();
-            
-            int split = key.lastIndexOf( '.' );
-            if( split < 0 ) {
+
+            int firstSplit = key.indexOf('.');
+            int split = key.lastIndexOf('.');
+            if (split < 0) {
                 logger.log(Level.WARNING, "Skipping mapping:{0}", e);
                 continue;
             }
-            
-            String stick = key.substring(0, split).trim();
-            String component = key.substring(split+1).trim();            
+
+            String type;
+            if (firstSplit >= 0 && firstSplit != split) {
+                type = key.substring(0, firstSplit).trim();
+                if (!type.equals(AXIS_LABEL) && !type.equals(BUTTON_LABEL)) {
+                    /*
+                     * In this case, the "type" is probably a part of the
+                     * joystick name.
+                     */
+                    firstSplit = -1;
+                    type = "";
+                }
+            } else {
+                firstSplit = -1;
+                type = "";
+            }
+            String stick = key.substring(firstSplit + 1, split).trim();
+            String component = key.substring(split + 1).trim();
             String value = String.valueOf(e.getValue()).trim();
-            if( "regex".equals(component) ) {
+            if ("regex".equals(component)) {
                 // It's a name remapping
                 addJoystickNameRegex(value, stick);
             }
-            addMapping(stick, component, value);
+            if ((lBrackIndex = value.indexOf('[')) > 0) {
+                /*
+                 * This means that there is an axis range.
+                 */
+                range = new float[2];
+                rBrackIndex = value.indexOf(']');
+                commaIndex = value.indexOf(',');
+                if (rBrackIndex > -1 && commaIndex > -1) {
+                    try {
+                        range[0] = Float.parseFloat(value.substring(lBrackIndex + 1, commaIndex).trim());
+                        range[1] = Float.parseFloat(value.substring(commaIndex + 1, rBrackIndex).trim());
+                        value = value.substring(0, lBrackIndex).trim();
+                        type = AXIS_LABEL;
+                    } catch (NumberFormatException nfe) {
+                        logger.log(Level.SEVERE, "Could not parse axis range \"" + value.substring(lBrackIndex) + "\"", nfe);
+                    }
+                }
+            }
+            switch (type) {
+                case AXIS_LABEL:
+                    if (range == null) {
+                        addAxisMapping(stick, component, value);
+                    } else {
+                        addAxisMapping(stick, component, value, range);
+                    }
+                    break;
+                case BUTTON_LABEL:
+                    addButtonMapping(stick, component, value);
+                    break;
+                default:
+                    addMapping(stick, component, value);
+            }
         }
     }
- 
+
     /**
-     *  Maps a regular expression to a normalized name for that joystick.
+     * Maps a regular expression to a normalized name for that joystick.
      */
-    public static void addJoystickNameRegex( String regex, String name ) {
+    public static void addJoystickNameRegex(String regex, String name) {
         logger.log(Level.FINE, "addJoystickNameRegex(" + regex + ", " + name + ")");
-        nameRemappings.put(Pattern.compile(regex), name);   
+        nameRemappings.put(Pattern.compile(regex), name);
     }
-    
-    protected static String getNormalizedName( String name ) {
+
+    protected static String getNormalizedName(String name) {
         String result = nameCache.get(name);
-        if( result != null ) {
+        if (result != null) {
             return result;
         }
-        for( Map.Entry<Pattern, String> e : nameRemappings.entrySet() ) {
+        for (Map.Entry<Pattern, String> e : nameRemappings.entrySet()) {
             Pattern p = e.getKey();
             Matcher m = p.matcher(name);
-            if( m.matches() ) {
+            if (m.matches()) {
                 nameCache.put(name, e.getValue());
                 return e.getValue();
             }
         }
         return null;
     }
- 
+
     /**
-     *  Loads a set of compatibility mappings from the property file
-     *  specified by the given URL.
-     */   
-    public static void loadMappingProperties( URL u ) throws IOException {
+     * Loads a set of compatibility mappings from the property file
+     * specified by the given URL.
+     */
+    public static void loadMappingProperties(URL u) throws IOException {
         logger.log(Level.FINE, "Loading mapping properties:{0}", u);
         InputStream in = u.openStream();
-        try {        
+        try {
             Properties p = new Properties();
             p.load(in);
-            addMappings(p);            
+            addMappings(p);
         } finally {
             in.close();
-        } 
+        }
     }
 
-    protected static void loadMappings( ClassLoader cl, String path ) throws IOException { 
+    protected static void loadMappings(ClassLoader cl, String path) throws IOException {
         logger.log(Level.FINE, "Searching for mappings for path:{0}", path);
-        for( Enumeration<URL> en = cl.getResources(path); en.hasMoreElements(); ) {            
+        for (Enumeration<URL> en = cl.getResources(path); en.hasMoreElements(); ) {
             URL u = en.nextElement();
-            try { 
+            try {
                 loadMappingProperties(u);
-            } catch( IOException e ) {
-                logger.log(Level.SEVERE, "Error loading:" + u, e);   
-            }                        
-        } 
-           
+            } catch (IOException e) {
+                logger.log(Level.SEVERE, "Error loading:" + u, e);
+            }
+        }
+
     }
 
     /**
-     *  Loads the default compatibility mappings by looking for
-     *  joystick-mapping.properties files on the classpath.
+     * Loads the default compatibility mappings by looking for
+     * joystick-mapping.properties files on the classpath.
      */
     protected static void loadDefaultMappings() {
-        for( String s : searchPaths ) {
-            try {            
+        for (String s : searchPaths) {
+            try {
                 loadMappings(JoystickCompatibilityMappings.class.getClassLoader(), s);
-            } catch( IOException e ) {
+            } catch (IOException e) {
                 logger.log(Level.SEVERE, "Error searching resource path:{0}", s);
             }
         }
-    }     
+    }
+
+    private static class AxisData {
+        String name;
+        float[] range;
+
+        AxisData(String name, float[] range) {
+            this.name = name;
+            this.range = range;
+        }
+    }
 }

+ 35 - 9
jme3-core/src/main/java/com/jme3/input/event/JoyAxisEvent.java

@@ -36,17 +36,36 @@ import com.jme3.input.JoystickAxis;
 
 /**
  * Joystick axis event.
- * 
+ *
  * @author Kirill Vainer, Paul Speed
  */
 public class JoyAxisEvent extends InputEvent {
 
-    final private JoystickAxis axis;
-    final private float value;
+    private JoystickAxis axis;
+    private float value;
+    private float rawValue;
 
+    /**
+     * Creates a new event for a joystick axis.
+     *
+     * @param axis     - The axis that generated this event.
+     * @param value    - The value of the axis.
+     */
     public JoyAxisEvent(JoystickAxis axis, float value) {
+        this(axis, value, value);
+    }
+
+    /**
+     * Creates a new event for a joystick axis.
+     *
+     * @param axis     - The axis that generated this event.
+     * @param value    - The value of the axis, after rescaling took place.
+     * @param rawValue - The original axis value before it was rescaled by {@link com.jme3.input.JoystickCompatibilityMappings JoystickCompatibilityMappings}.
+     */
+    public JoyAxisEvent(JoystickAxis axis, float value, float rawValue) {
         this.axis = axis;
         this.value = value;
+        this.rawValue = rawValue;
     }
 
     /**
@@ -60,9 +79,8 @@ public class JoyAxisEvent extends InputEvent {
 
     /**
      * Returns the joystick axis index.
-     * 
+     *
      * @return joystick axis index.
-     * 
      * @see com.jme3.input.JoystickAxis#assignAxis(java.lang.String, java.lang.String)
      */
     public int getAxisIndex() {
@@ -71,10 +89,9 @@ public class JoyAxisEvent extends InputEvent {
 
     /**
      * The joystick index.
-     * 
+     *
      * @return joystick index.
-     * 
-     * @see InputManager#getJoysticks() 
+     * @see InputManager#getJoysticks()
      */
     public int getJoyIndex() {
         return axis.getJoystick().getJoyId();
@@ -82,10 +99,19 @@ public class JoyAxisEvent extends InputEvent {
 
     /**
      * The value of the axis.
-     * 
+     *
      * @return value of the axis.
      */
     public float getValue() {
         return value;
     }
+
+    /**
+     * The value of the axis before it was remapped.
+     *
+     * @return value of the axis.
+     */
+    public float getRawValue() {
+        return rawValue;
+    }
 }

+ 74 - 72
jme3-lwjgl/src/main/java/com/jme3/input/lwjgl/JInputJoyInput.java

@@ -65,13 +65,13 @@ public class JInputJoyInput implements JoyInput {
     private RawInputListener listener;
 
     private Map<Controller, JInputJoystick> joystickIndex = new HashMap<>();
-    
+
     @Override
     public void setJoyRumble(int joyId, float amount){
 
-        if( joyId >= joysticks.length )        
+        if( joyId >= joysticks.length )
             throw new IllegalArgumentException();
-            
+
         Controller c = joysticks[joyId].controller;
         for (Rumbler r : c.getRumblers()){
             r.rumble(amount);
@@ -84,34 +84,33 @@ public class JInputJoyInput implements JoyInput {
             ControllerEnvironment.getDefaultEnvironment();
 
         Controller[] cs = ce.getControllers();
-        
         List<Joystick> list = new ArrayList<>();
         for( Controller c : ce.getControllers() ) {
             if (c.getType() == Controller.Type.KEYBOARD
              || c.getType() == Controller.Type.MOUSE)
                 continue;
 
-            logger.log(Level.FINE, "Attempting to create joystick for: \"{0}\"", c);        
- 
+            logger.log(Level.FINE, "Attempting to create joystick for: \"{0}\"", c);
+
             // Try to create it like a joystick
-            JInputJoystick stick = new JInputJoystick(inputManager, this, c, list.size(), c.getName()); 
+            JInputJoystick stick = new JInputJoystick(inputManager, this, c, list.size(), c.getName());
             for( Component comp : c.getComponents() ) {
-                stick.addComponent(comp);                   
+                stick.addComponent(comp);
             }
- 
+
             // If it has no axes then we'll assume it's not
             // a joystick
             if( stick.getAxisCount() == 0 ) {
                 logger.log(Level.FINE, "Not a joystick: {0}", c);
                 continue;
             }
- 
+
             joystickIndex.put(c, stick);
-            list.add(stick);                      
+            list.add(stick);
         }
 
         joysticks = list.toArray( new JInputJoystick[list.size()] );
-        
+
         return joysticks;
     }
 
@@ -129,60 +128,63 @@ public class JInputJoyInput implements JoyInput {
         Event e = new Event();
         for (int i = 0; i < cs.length; i++){
             Controller c = cs[i];
-            
+
             JInputJoystick stick = joystickIndex.get(c);
             if( stick == null )
                 continue;
-                
+
             if( !c.poll() )
                 continue;
-        
+
             int joyId = stick.getJoyId();
-                    
+
             EventQueue q = c.getEventQueue();
             while (q.getNextEvent(e)){
                 Identifier id = e.getComponent().getIdentifier();
                 if (id == Identifier.Axis.POV){
-                    float x = 0, y = 0;
+                    float rawX = 0, rawY = 0, x, y;
                     float v = e.getValue();
- 
+
                     if (v == POV.CENTER){
-                        x = 0; y = 0;
+                        rawX = 0; rawY = 0;
                     }else if (v == POV.DOWN){
-                        x = 0; y = -1f;
+                        rawX = 0; rawY = -1f;
                     }else if (v == POV.DOWN_LEFT){
-                        x = -1f; y = -1f;
+                        rawX = -1f; rawY = -1f;
                     }else if (v == POV.DOWN_RIGHT){
-                        x = 1f; y = -1f;
+                        rawX = 1f; rawY = -1f;
                     }else if (v == POV.LEFT){
-                        x = -1f; y = 0;
+                        rawX = -1f; rawY = 0;
                     }else if (v == POV.RIGHT){
-                        x = 1f; y = 0;
+                        rawX = 1f; rawY = 0;
                     }else if (v == POV.UP){
-                        x = 0; y = 1f;
+                        rawX = 0; rawY = 1f;
                     }else if (v == POV.UP_LEFT){
-                        x = -1f; y = 1f;
+                        rawX = -1f; rawY = 1f;
                     }else if (v == POV.UP_RIGHT){
-                        x = 1f; y = 1f;
+                        rawX = 1f; rawY = 1f;
                     }
 
-                    JoyAxisEvent evt1 = new JoyAxisEvent(stick.povX, x);
-                    JoyAxisEvent evt2 = new JoyAxisEvent(stick.povY, y);
+                    x = JoystickCompatibilityMappings.remapAxisRange(stick.povX, rawX);
+                    y = JoystickCompatibilityMappings.remapAxisRange(stick.povY, rawY);
+                    JoyAxisEvent evt1 = new JoyAxisEvent(stick.povX, x, rawX);
+                    JoyAxisEvent evt2 = new JoyAxisEvent(stick.povY, y, rawY);
                     listener.onJoyAxisEvent(evt1);
                     listener.onJoyAxisEvent(evt2);
                 }else if (id instanceof Axis){
-                    float value = e.getValue();
-                    
+                    float rawValue = e.getValue();
+                    float value = JoystickCompatibilityMappings.remapAxisRange(stick.povY, rawValue);
+
                     JoystickAxis axis = stick.axisIndex.get(e.getComponent());
-                    JoyAxisEvent evt = new JoyAxisEvent(axis, value);
+                    JoyAxisEvent evt = new JoyAxisEvent(axis, value, rawValue);
                     listener.onJoyAxisEvent(evt);
                 }else if (id instanceof Button){
-                    
-                    JoystickButton button = stick.buttonIndex.get(e.getComponent());                    
+
+                    JoystickButton button = stick.buttonIndex.get(e.getComponent());
                     JoyButtonEvent evt = new JoyButtonEvent(button, e.getValue() == 1f);
                     listener.onJoyButtonEvent(evt);
                 }
-            }                             
+            }
         }
     }
 
@@ -209,7 +211,7 @@ public class JInputJoyInput implements JoyInput {
     protected class JInputJoystick extends AbstractJoystick {
 
         private JoystickAxis nullAxis;
-        private Controller controller;    
+        private Controller controller;
         private JoystickAxis xAxis;
         private JoystickAxis yAxis;
         private JoystickAxis povX;
@@ -217,22 +219,22 @@ public class JInputJoyInput implements JoyInput {
         private Map<Component, JoystickAxis> axisIndex = new HashMap<>();
         private Map<Component, JoystickButton> buttonIndex = new HashMap<>();
     
-        public JInputJoystick( InputManager inputManager, JoyInput joyInput, Controller controller, 
+        public JInputJoystick( InputManager inputManager, JoyInput joyInput, Controller controller,
                                int joyId, String name ) {
             super( inputManager, joyInput, joyId, name );
-            
+
             this.controller = controller;
-            
-            this.nullAxis = new DefaultJoystickAxis( getInputManager(), this, -1, 
+
+            this.nullAxis = new DefaultJoystickAxis( getInputManager(), this, -1,
                                                      "Null", "null", false, false, 0 );
-            this.xAxis = nullAxis;                                                     
-            this.yAxis = nullAxis;                                                     
+            this.xAxis = nullAxis;
+            this.yAxis = nullAxis;
             this.povX = nullAxis;
-            this.povY = nullAxis;                                                     
+            this.povY = nullAxis;
         }
 
         protected void addComponent( Component comp ) {
-            
+
             Identifier id = comp.getIdentifier();
             if( id instanceof Button ) {
                 addButton(comp);
@@ -244,99 +246,99 @@ public class JInputJoyInput implements JoyInput {
         }
 
         protected void addButton( Component comp ) {
-        
+
             logger.log(Level.FINE, "Adding button: \"{0}\" id:" + comp.getIdentifier(), comp);
-            
-            Identifier id = comp.getIdentifier();            
+
+            Identifier id = comp.getIdentifier();
             if( !(id instanceof Button) ) {
                 throw new IllegalArgumentException( "Component is not an button:" + comp );
             }
 
             String name = comp.getName();
             String original = id.getName();
-            try { 
+            try {
                 Integer.parseInt(original);
             } catch (NumberFormatException e){
                 original = String.valueOf(buttonIndex.size());
             }
-            String logicalId = JoystickCompatibilityMappings.remapComponent( controller.getName(), original );
+            String logicalId = JoystickCompatibilityMappings.remapButton( controller.getName(), original );
             if( logicalId != original ) {
                 logger.log(Level.FINE, "Remapped:" + original + " to:" + logicalId);
             }
- 
+
             JoystickButton button = new DefaultJoystickButton( getInputManager(), this, getButtonCount(),
                                                                name, logicalId );
-            addButton(button);                                                               
+            addButton(button);
             buttonIndex.put( comp, button );
         }
-        
+
         protected void addAxis( Component comp ) {
 
             logger.log(Level.FINE, "Adding axis: \"{0}\" id:" + comp.getIdentifier(), comp );
-                            
+
             Identifier id = comp.getIdentifier();
             if( !(id instanceof Axis) ) {
                 throw new IllegalArgumentException( "Component is not an axis:" + comp );
             }
-            
+
             String name = comp.getName();
             String original = id.getName();
-            String logicalId = JoystickCompatibilityMappings.remapComponent( controller.getName(), original );
+            String logicalId = JoystickCompatibilityMappings.remapAxis( controller.getName(), original );
             if( logicalId != original ) {
                 logger.log(Level.FINE, "Remapped:" + original + " to:" + logicalId);
             }
-            
-            JoystickAxis axis = new DefaultJoystickAxis( getInputManager(), 
+
+            JoystickAxis axis = new DefaultJoystickAxis( getInputManager(),
                                                          this, getAxisCount(), name, logicalId,
-                                                         comp.isAnalog(), comp.isRelative(), 
+                                                         comp.isAnalog(), comp.isRelative(),
                                                          comp.getDeadZone() );
-            addAxis(axis);                                                          
+            addAxis(axis);
             axisIndex.put( comp, axis );
-                       
+
             // Support the X/Y axis indexes
             if( id == Axis.X ) {
                 xAxis = axis;
             } else if( id == Axis.Y ) {
                 yAxis = axis;
             } else if( id == Axis.POV ) {
-                
+
                 // Add two fake axes for the JME provided convenience
                 // axes: AXIS_POV_X, AXIS_POV_Y
-                povX = new DefaultJoystickAxis( getInputManager(), 
-                                                this, getAxisCount(), JoystickAxis.POV_X, 
+                povX = new DefaultJoystickAxis( getInputManager(),
+                                                this, getAxisCount(), JoystickAxis.POV_X,
                                                 id.getName() + "_x",
                                                 comp.isAnalog(), comp.isRelative(), comp.getDeadZone() );
                 logger.log(Level.FINE, "Adding axis: \"{0}\" id:" + id.getName() + "_x", povX.getName() );
                 addAxis(povX);
-                povY = new DefaultJoystickAxis( getInputManager(), 
-                                                this, getAxisCount(), JoystickAxis.POV_Y, 
+                povY = new DefaultJoystickAxis( getInputManager(),
+                                                this, getAxisCount(), JoystickAxis.POV_Y,
                                                 id.getName() + "_y",
                                                 comp.isAnalog(), comp.isRelative(), comp.getDeadZone() );
                 logger.log(Level.FINE, "Adding axis: \"{0}\" id:" + id.getName() + "_y", povY.getName() );
                 addAxis(povY);
             }
-            
+
         }
- 
+
         @Override
         public JoystickAxis getXAxis() {
             return xAxis;
-        }     
+        }
 
         @Override
         public JoystickAxis getYAxis() {
             return yAxis;
-        }     
+        }
 
         @Override
         public JoystickAxis getPovXAxis() {
             return povX;
-        }     
+        }
 
         @Override
         public JoystickAxis getPovYAxis() {
             return povY;
-        }     
+        }
 
         @Override
         public int getXAxisIndex(){
@@ -347,7 +349,7 @@ public class JInputJoyInput implements JoyInput {
         public int getYAxisIndex(){
             return yAxis.getAxisId();
         }
-    }    
+    }
 }
 
 

+ 7 - 5
jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwJoystickInput.java

@@ -100,7 +100,7 @@ public class GlfwJoystickInput implements JoyInput {
                 while (floatBuffer.hasRemaining()) {
                     floatBuffer.get();
 
-                    final String logicalId = JoystickCompatibilityMappings.remapComponent(joystick.getName(), convertAxisIndex(axisIndex));
+                    final String logicalId = JoystickCompatibilityMappings.remapAxis(joystick.getName(), convertAxisIndex(axisIndex));
                     final JoystickAxis joystickAxis = new DefaultJoystickAxis(inputManager, joystick, axisIndex, convertAxisIndex(axisIndex), logicalId, true, false, 0.0f);
                     joystick.addAxis(axisIndex, joystickAxis);
                     axisIndex++;
@@ -112,10 +112,10 @@ public class GlfwJoystickInput implements JoyInput {
                 while (byteBuffer.hasRemaining()) {
                     byteBuffer.get();
 
-                    final String logicalId = JoystickCompatibilityMappings.remapComponent(joystick.getName(), String.valueOf(buttonIndex));
+                    final String logicalId = JoystickCompatibilityMappings.remapButton(joystick.getName(), String.valueOf(buttonIndex));
                     final JoystickButton button = new DefaultJoystickButton(inputManager, joystick, buttonIndex, String.valueOf(buttonIndex), logicalId);
                     joystick.addButton(button);
-                    joyButtonPressed.put(button, false); 
+                    joyButtonPressed.put(button, false);
                     buttonIndex++;
                 }
             }
@@ -145,6 +145,7 @@ public class GlfwJoystickInput implements JoyInput {
 
     @Override
     public void update() {
+        float rawValue, value;
         for (final Map.Entry<Integer, GlfwJoystick> entry : joysticks.entrySet()) {
 
             // Axes
@@ -157,8 +158,9 @@ public class GlfwJoystickInput implements JoyInput {
 
             if (axisValues != null) {
                 for (final JoystickAxis axis : entry.getValue().getAxes()) {
-                    final float value = axisValues.get(axis.getAxisId());
-                    listener.onJoyAxisEvent(new JoyAxisEvent(axis, value));
+                    rawValue = axisValues.get(axis.getAxisId());
+                    value = JoystickCompatibilityMappings.remapAxisRange(axis, rawValue);
+                    listener.onJoyAxisEvent(new JoyAxisEvent(axis, value, rawValue));
                 }
             }