Quellcode durchsuchen

#1734 actions based vr input (#1735)

* #1734 Bring the LWJGL OpenVR implementation up to the modern actions based input/vr/AnalogActionState.java

All the other apis are vender specific or are alternative of versions OpenVR, so are deprecated

- Deprecate everything but lwjgl openVr

- Add support for restricting analog, digital and haptics to a particular hand

- improve vr javadocs

- Add haptics to the new openVR api

- Add analogue inputs

- Add action based digital controls into jme-vr

* #1734 Improve VR javadoc to make it clear that the action based API is only for OpenVr
richardTingle vor 3 Jahren
Ursprung
Commit
4af7aabf30

+ 13 - 0
jme3-vr/src/main/java/com/jme3/app/VRConstants.java

@@ -123,22 +123,31 @@ public class VRConstants {
 
     /**
      * The identifier of the OpenVR system.
+     *
+     * Deprecated as only the lwjgl OpenVr version has been upgraded to modern action based inputs
+     *
      * @see #SETTING_VRAPI_OSVR_VALUE
      * @see #SETTING_VRAPI_OPENVR_LWJGL_VALUE
      * @see #SETTING_VRAPI_OCULUSVR_VALUE
      */
+    @Deprecated
     public static final int SETTING_VRAPI_OPENVR_VALUE       = 1;
 
     /**
      * The identifier of the OSVR system.
+     *
+     * Deprecated as an OpenVr system should be used instead for a non vender specific api
+     *
      * @see #SETTING_VRAPI_OPENVR_VALUE
      * @see #SETTING_VRAPI_OPENVR_LWJGL_VALUE
      * @see #SETTING_VRAPI_OCULUSVR_VALUE
      */
+    @Deprecated
     public static final int SETTING_VRAPI_OSVR_VALUE         = 2;
 
     /**
      * The identifier of the OpenVR from LWJGL system.
+     *
      * @see #SETTING_VRAPI_OPENVR_VALUE
      * @see #SETTING_VRAPI_OSVR_VALUE
      * @see #SETTING_VRAPI_OCULUSVR_VALUE
@@ -147,10 +156,14 @@ public class VRConstants {
 
     /**
      * The identifier of the Oculus Rift system.
+     *
+     * Deprecated as an OpenVr system should be used instead (and the rift itself is discontinued)
+     *
      * @see #SETTING_VRAPI_OPENVR_VALUE
      * @see #SETTING_VRAPI_OSVR_VALUE
      * @see #SETTING_VRAPI_OPENVR_LWJGL_VALUE
      */
+    @Deprecated
     public static final int SETTING_VRAPI_OCULUSVR_VALUE    = 4;
 
     /**

+ 2 - 0
jme3-vr/src/main/java/com/jme3/app/VREnvironment.java

@@ -513,6 +513,8 @@ public class VREnvironment {
 
             if (settings.get(VRConstants.SETTING_VRAPI) != null){
                 vrBinding = settings.getInteger(VRConstants.SETTING_VRAPI);
+            }else{
+                vrBinding = VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE; //this is the binding that is best supported so makes sense to be the default
             }
 
             if (settings.get(VRConstants.SETTING_SEATED_EXPERIENCE) != null){

+ 48 - 0
jme3-vr/src/main/java/com/jme3/input/vr/AnalogActionState.java

@@ -0,0 +1,48 @@
+package com.jme3.input.vr;
+
+public class AnalogActionState{
+
+    /**
+     * The X coordinate of the analog data (typically between -1 and 1 for joystick coordinates or 0 and 1 for
+     * trigger pulls)
+     */
+    public final float x;
+
+    /**
+     * The Y coordinate of the analog data (typically between -1 and 1)
+     *
+     * Will be zero if the analog action doesn't have at least 2 dimensions
+     */
+    public final float y;
+
+    /**
+     * The Z coordinate of the analog data (typically between -1 and 1)
+     *
+     * Will be zero if the analog action doesn't have at least 3 dimensions
+     */
+    public final float z;
+
+    /**
+     * The change in the X coordinate since the last frame
+     */
+    public final float deltaX;
+
+    /**
+     * The change in the Y coordinate since the last frame
+     */
+    public final float deltaY;
+
+    /**
+     * The change in the Z coordinate since the last frame
+     */
+    public final float deltaZ;
+
+    public AnalogActionState(float x, float y, float z, float deltaX, float deltaY, float deltaZ){
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.deltaX = deltaX;
+        this.deltaY = deltaY;
+        this.deltaZ = deltaZ;
+    }
+}

+ 19 - 0
jme3-vr/src/main/java/com/jme3/input/vr/DigitalActionState.java

@@ -0,0 +1,19 @@
+package com.jme3.input.vr;
+
+public class DigitalActionState{
+
+    /**
+     * The current value of this action
+     */
+    public final boolean state;
+
+    /**
+     * If since the last loop the value of this action has changed
+     */
+    public final boolean changed;
+
+    public DigitalActionState(boolean state, boolean changed){
+        this.state = state;
+        this.changed = changed;
+    }
+}

+ 188 - 0
jme3-vr/src/main/java/com/jme3/input/vr/VRInputAPI.java

@@ -4,40 +4,182 @@ import com.jme3.math.Quaternion;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
 
+import java.util.Collection;
+
 /**
  * An interface that represents a VR input (typically a VR device such as a controller).
  * @author reden - phr00t - https://github.com/phr00t
  * @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
  */
 public interface VRInputAPI {
+
+    /**
+     * Registers an action manifest. An actions manifest is a file that defines "actions" a player can make.
+     * (An action is an abstract version of a button press). The action manifest may then also include references to
+     * further files that define default mappings between those actions and physical buttons on the VR controllers.
+     *
+     * Note that registering an actions manifest will deactivate legacy inputs (i.e. methods such as {@link #isButtonDown}
+     * will no longer work
+     *
+     * See https://github.com/ValveSoftware/openvr/wiki/Action-manifest for documentation on how to create an
+     * action manifest
+     *
+     * This option is only relevant to OpenVR
+     *
+     * @param actionManifestAbsolutePath
+     *          the absolute file path to an actions manifest
+     * @param startingActiveActionSet
+     *          the actions in the manifest are divided into action sets (groups) by their prefix (e.g. "/actions/main").
+     *          These action sets can be turned off and on per frame. This argument sets the action set that will be
+     *          active now. The active action sets can be later be changed by calling {@link #setActiveActionSet}.
+     *          Note that at present only a single set at a time is supported
+     *
+     */
+    default void registerActionManifest( String actionManifestAbsolutePath, String startingActiveActionSet ){
+        throw new UnsupportedOperationException("Action manifests are not supported for the currently used VR API");
+    }
+
+    /**
+     * Updates the active action set (the action group that will have their states available to be polled).
+     *
+     * Note that this update will not take effect until the next loop
+     * Note that at present only a single set at a time is supported
+     *
+     * @param activeActionSet
+     *          the actions in the manifest are divided into action sets (groups) by their prefix (e.g. "/actions/main").
+     *          These action sets can be turned off and on per frame. This argument sets the action set that will be
+     *          active now.
+     */
+    default void setActiveActionSet( String activeActionSet ){
+        throw new UnsupportedOperationException("Action manifests are not supported for the currently used VR API");
+    }
+
+    /**
+     * Gets the current state of the action (abstract version of a button press).
+     *
+     * This is called for digital style actions (a button is pressed, or not)
+     *
+     * This method is commonly called when it's not important which hand the action is bound to (e.g. if a button press
+     * is opening your inventory that could be bound to either left or right hand and that would not matter).
+     *
+     * If the handedness matters use {@link #getDigitalActionState(String, String)}
+     *
+     * {@link #registerActionManifest} must have been called before using this method.
+     *
+     * @param actionName The name of the action. Will be something like /actions/main/in/openInventory
+     * @return the DigitalActionState that has details on if the state has changed, what the state is etc.
+     */
+    default DigitalActionState getDigitalActionState( String actionName ){
+        return getDigitalActionState(actionName, null);
+    }
+
+    /**
+     * Gets the current state of the action (abstract version of a button press).
+     *
+     * This is called for digital style actions (a button is pressed, or not)
+     *
+     * This method is commonly called when it is important which hand the action is found on. For example while
+     * holding a weapon a button may be bound to "eject magazine" to allow you to load a new one, but that would only
+     * want to take effect on the hand that is holding the weapon
+     *
+     * Note that restrictToInput only restricts, it must still be bound to the input you want to receive the input from in
+     * the action manifest default bindings.
+     *
+     * {@link #registerActionManifest} must have been called before using this method.
+     *
+     * @param actionName The name of the action. E.g. /actions/main/in/openInventory
+     * @param restrictToInput the input to restrict the action to. E.g. /user/hand/right. Or null, which means "any input"
+     * @return the DigitalActionState that has details on if the state has changed, what the state is etc.
+     */
+    default DigitalActionState getDigitalActionState( String actionName, String restrictToInput ){
+        throw new UnsupportedOperationException("Action manifests are not supported for the currently used VR API");
+    }
+
+    /**
+     * Gets the current state of the action (abstract version of a button press).
+     *
+     * This is called for analog style actions (most commonly joysticks, but button pressure can also be mapped in analog).
+     *
+     * This method is commonly called when it's not important which hand the action is bound to (e.g. if the thumb stick
+     * is controlling a third-person character in-game that could be bound to either left or right hand and that would
+     * not matter).
+     *
+     * If the handedness matters use {@link #getAnalogActionState(String, String)}
+     *
+     * {@link #registerActionManifest} must have been called before using this method.
+     *
+     * @param actionName The name of the action. E.g. /actions/main/in/openInventory
+     * @return the DigitalActionState that has details on if the state has changed, what the state is etc.
+     */
+    default AnalogActionState getAnalogActionState( String actionName ){
+        return getAnalogActionState(actionName, null);
+    }
+
+    /**
+     * Gets the current state of the action (abstract version of a button press).
+     *
+     * This is called for analog style actions (most commonly joysticks, but button pressure can also be mapped in analog).
+     *
+     * This method is commonly called when it is important which hand the action is found on. For example an "in universe"
+     * joystick that has a hat control might (while you are holding it) bind to the on-controller hat, but only on the hand
+     * holding it
+     *
+     * Note that restrictToInput only restricts, it must still be bound to the input you want to receive the input from in
+     * the action manifest default bindings.
+     *
+     * {@link #registerActionManifest} must have been called before using this method.
+     *
+     * @param actionName The name of the action. E.g. /actions/main/in/openInventory
+     * @param restrictToInput the input to restrict the action to. E.g. /user/hand/right. Or null, which means "any input"
+     * @return the DigitalActionState that has details on if the state has changed, what the state is etc.
+     */
+    default AnalogActionState getAnalogActionState( String actionName, String restrictToInput ){
+        throw new UnsupportedOperationException("Action manifests are not supported for the currently used VR API");
+    }
+
     /**
      * Check if the given button is down (more generally if the given input type is activated).
+     *
+     * Deprecated as should use an actions manifest approach. See {@link #registerActionManifest}. Note; action based will only work with the OpenVR api
+     *
      * @param controllerIndex the index of the controller to check.
      * @param checkButton the button / input to check.
      * @return <code>true</code> if the button / input is down / activated and <code>false</code> otherwise.
      */
+    @Deprecated
     public boolean isButtonDown(int controllerIndex, VRInputType checkButton);
 
     /**
      * Check if the given button / input from the given controller has been just pressed / activated.
+     *
+     * Deprecated as should use an actions manifest approach. See {@link #registerActionManifest}.  Note; action based will only work with the OpenVR api
+     *
      * @param controllerIndex the index of the controller.
      * @param checkButton the button / input to check.
      * @return <code>true</code> if the given button / input from the given controller has been just pressed / activated and <code>false</code> otherwise.
      */
+    @Deprecated
     public boolean wasButtonPressedSinceLastCall(int controllerIndex, VRInputType checkButton);
 
     /**
      * Reset the current activation of the inputs. After a call to this method, all input activation is considered as new activation.
      * @see #wasButtonPressedSinceLastCall(int, VRInputType)
+     *
+     * Deprecated as should use an actions manifest approach. See {@link #registerActionManifest}.  Note; action based will only work with the OpenVR api
      */
+    @Deprecated
     public void resetInputSinceLastCall();
 
     /**
      * Get the controller axis delta from the last value.
+     *
+     * Deprecated as should use an actions manifest approach. See {@link #registerActionManifest}.  Note; action based will only work with the OpenVR api
+     *
      * @param controllerIndex the index of the controller.
      * @param forAxis the axis.
      * @return the controller axis delta from the last call.
      */
+    @Deprecated
     public Vector2f getAxisDeltaSinceLastCall(int controllerIndex, VRInputType forAxis);
 
     /**
@@ -59,21 +201,29 @@ public interface VRInputAPI {
     /**
      * Get the axis value for the given input on the given controller.
      * This value is the {@link #getAxisRaw(int, VRInputType) raw value} multiplied by the  {@link #getAxisMultiplier() axis multiplier}.
+     *
+     * Deprecated as should use an actions manifest approach. See {@link #registerActionManifest}. Note; action based will only work with the OpenVR api
+     *
      * @param controllerIndex the index of the controller.
      * @param forAxis the axis.
      * @return the axis value for the given input on the given controller.
      * @see #getAxisRaw(int, VRInputType)
      * @see #getAxisMultiplier()
      */
+    @Deprecated
     public Vector2f getAxis(int controllerIndex, VRInputType forAxis);
 
     /**
      * Get the axis value for the given input on the given controller.
+     *
+     * Deprecated as should use an actions manifest approach. See {@link #registerActionManifest}  Note; action based will only work with the OpenVR api
+     *
      * @param controllerIndex the index of the controller.
      * @param forAxis the axis.
      * @return the axis value for the given input on the given controller.
      * @see #getAxis(int, VRInputType)
      */
+    @Deprecated
     public Vector2f getAxisRaw(int controllerIndex, VRInputType forAxis);
 
     /**
@@ -184,8 +334,46 @@ public interface VRInputAPI {
 
     /**
      * Trigger a haptic pulse on the selected controller for the duration given in parameters (in seconds).
+     *
+     * Deprecated, use triggerHapticAction instead (as it has more options and doesn't use deprecated methods)
+     *
      * @param controllerIndex the index of the controller.
      * @param seconds the duration of the pulse in seconds.
      */
+    @Deprecated
     public void triggerHapticPulse(int controllerIndex, float seconds);
+
+    /**
+     * Triggers a haptic action (aka a vibration).
+     *
+     * Note if you want a haptic action in only one hand that is done either by only binding the action to one hand in
+     * the action manifest's standard bindings or by binding to both and using {@link #triggerHapticAction(String, float, float, float, String)}
+     * to control which input it gets set to at run time
+     *
+     * @param actionName The name of the action. Will be something like /actions/main/out/vibrate
+     * @param duration how long in seconds the
+     * @param frequency in cycles per second
+     * @param amplitude between 0 and 1
+     */
+   default void triggerHapticAction( String actionName, float duration, float frequency, float amplitude){
+       triggerHapticAction( actionName, duration, frequency, amplitude, null );
+   }
+
+    /**
+     * Triggers a haptic action (aka a vibration) restricted to just one input (e.g. left or right hand).
+     *
+     * Note that restrictToInput only restricts, it must still be bound to the input you want to send the haptic to in
+     * the action manifest default bindings.
+     *
+     * This method is typically used to bind the haptic to both hands then decide at run time which hand to sent to     *
+     *
+     * @param actionName The name of the action. Will be something like /actions/main/out/vibrate
+     * @param duration how long in seconds the
+     * @param frequency in cycles per second
+     * @param amplitude between 0 and 1
+     * @param restrictToInput the input to restrict the action to. E.g. /user/hand/right, /user/hand/left. Or null, which means "any input"
+     */
+    default void triggerHapticAction( String actionName, float duration, float frequency, float amplitude, String restrictToInput){
+        throw new UnsupportedOperationException("Action manifests are not supported for the currently used VR API");
+    }
 }

+ 3 - 0
jme3-vr/src/main/java/com/jme3/input/vr/VRInputType.java

@@ -5,7 +5,10 @@ package com.jme3.input.vr;
  * @author reden - phr00t - https://github.com/phr00t
  * @author Julien Seinturier - COMEX SA - <a href="http://www.seinturier.fr">http://www.seinturier.fr</a>
  *
+ * Deprecated, use the LWJGL openVR bindings and use actions instead
+ *
  */
+@Deprecated
 public enum VRInputType {
     /**
      * an HTC vive trigger axis (about <a href="https://www.vive.com/us/support/category_howto/720435.html">Vive controller</a>).

+ 34 - 0
jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVRAnalogActionData.java

@@ -0,0 +1,34 @@
+package com.jme3.input.vr.lwjgl_openvr;
+
+import org.lwjgl.openvr.InputAnalogActionData;
+
+/**
+ * This is a set of reusable parts that are used when accessing an analogue action
+ * (Analogue meaning something like a trigger pull or joystick coordinate)
+ */
+public class LWJGLOpenVRAnalogActionData{
+
+    /**
+     * This is the address string for the action. It will be something like /actions/main/in/openInventory
+     */
+    String actionName;
+
+    /**
+     * The handle used to request the action's state from LWJGL.
+     *
+     * It is how the action is addressed efficiently
+     */
+    long actionHandle;
+
+    /**
+     * This is a LWJGL object that will have the actions state passed into it. It is mapped to native memory so we
+     * don't want to keep creating new ones.
+     */
+    InputAnalogActionData actionData;
+
+    public LWJGLOpenVRAnalogActionData(String actionName, long actionHandle, InputAnalogActionData actionData){
+        this.actionName = actionName;
+        this.actionHandle = actionHandle;
+        this.actionData = actionData;
+    }
+}

+ 34 - 0
jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVRDigitalActionData.java

@@ -0,0 +1,34 @@
+package com.jme3.input.vr.lwjgl_openvr;
+
+import org.lwjgl.openvr.InputDigitalActionData;
+
+/**
+ * This is a set of reusable parts that are used when accessing a digital action
+ * (Digital meaning something like a button press, that is either on or off)
+ */
+public class LWJGLOpenVRDigitalActionData{
+
+    /**
+     * This is the address string for the action. It will be something like /actions/main/in/openInventory
+     */
+    String actionName;
+
+    /**
+     * The handle used to request the action's state from LWJGL.
+     *
+     * It is how the action is addressed efficiently
+     */
+    long actionHandle;
+
+    /**
+     * This is a LWJGL object that will have the actions state passed into it. It is mapped to native memory so we
+     * don't want to keep creating new ones.
+     */
+    InputDigitalActionData actionData;
+
+    public LWJGLOpenVRDigitalActionData(String actionName, long actionHandle, InputDigitalActionData actionData){
+        this.actionName = actionName;
+        this.actionHandle = actionHandle;
+        this.actionData = actionData;
+    }
+}

+ 218 - 7
jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVRInput.java

@@ -1,10 +1,16 @@
 package com.jme3.input.vr.lwjgl_openvr;
 
+import java.nio.LongBuffer;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.logging.Logger;
 
 import com.jme3.app.VREnvironment;
+import com.jme3.input.vr.AnalogActionState;
+import com.jme3.input.vr.DigitalActionState;
 import com.jme3.input.vr.VRInputAPI;
 import com.jme3.input.vr.VRInputType;
 import com.jme3.input.vr.VRTrackedController;
@@ -18,8 +24,12 @@ import com.jme3.util.VRUtil;
 import java.nio.IntBuffer;
 import org.lwjgl.BufferUtils;
 import org.lwjgl.openvr.HmdVector3;
+import org.lwjgl.openvr.InputAnalogActionData;
+import org.lwjgl.openvr.InputDigitalActionData;
 import org.lwjgl.openvr.VR;
+import org.lwjgl.openvr.VRActiveActionSet;
 import org.lwjgl.openvr.VRControllerState;
+import org.lwjgl.openvr.VRInput;
 import org.lwjgl.openvr.VRSystem;
 
 /*
@@ -69,6 +79,10 @@ public class LWJGLOpenVRInput implements VRInputAPI {
 
     private static final Logger logger = Logger.getLogger(LWJGLOpenVRInput.class.getName());
 
+    /**
+     * Deprecated as used controller specific values. Should use Actions manifest instead
+     */
+    @Deprecated
     private final VRControllerState[] cStates = new VRControllerState[VR.k_unMaxTrackedDeviceCount];
 
     private final Quaternion[] rotStore = new Quaternion[VR.k_unMaxTrackedDeviceCount];
@@ -81,9 +95,40 @@ public class LWJGLOpenVRInput implements VRInputAPI {
 
     private final Vector2f tempAxis = new Vector2f(), temp2Axis = new Vector2f();
 
-    private final Vector2f lastCallAxis[] = new Vector2f[VR.k_unMaxTrackedDeviceCount];
+    private final Vector2f[] lastCallAxis = new Vector2f[VR.k_unMaxTrackedDeviceCount];
+
+    /**
+     * Deprecated as used controller specific values. Should use Actions manifest instead
+     */
+    @Deprecated
+    private final boolean[][] buttonDown = new boolean[VR.k_unMaxTrackedDeviceCount][16];
+
+    /**
+     * A map of the action name to the objects/data required to read states from lwjgl
+     */
+    private final Map<String, LWJGLOpenVRDigitalActionData> digitalActions = new HashMap<>();
 
-    private final boolean buttonDown[][] = new boolean[VR.k_unMaxTrackedDeviceCount][16];
+    /**
+     * A map of the action name to the objects/data required to read states from lwjgl
+     */
+    private final Map<String, LWJGLOpenVRAnalogActionData> analogActions = new HashMap<>();
+
+    /**
+     * A map of the action name to the handle of a haptic action
+     */
+    private final Map<String, Long> hapticActionHandles = new HashMap<>();
+
+    /**
+     * A map of the action set name to the handle that is used to refer to it when talking to LWJGL
+     */
+    private final Map<String, Long> actionSetHandles = new HashMap<>();
+
+    /**
+     * A map of input names (e.g. /user/hand/right) to the handle used to address it.
+     *
+     * Note that null is a special case that maps to VR.k_ulInvalidInputValueHandle and means "any input"
+     */
+    private final Map<String, Long> inputHandles = new HashMap<>();
 
     private float axisMultiplier = 1f;
 
@@ -95,6 +140,25 @@ public class LWJGLOpenVRInput implements VRInputAPI {
 
     private List<VRTrackedController> trackedControllers = null;
 
+    /**
+     * A lwjgl object that contains handles to the active action sets (is used each frame to tell lwjgl which actions to
+     * fetch states back for)
+     */
+    private VRActiveActionSet.Buffer activeActionSets;
+
+    InputMode inputMode = InputMode.LEGACY;
+
+    private enum InputMode{
+        /**
+         * Simple bitfield, no way to map new controllers
+         */
+        LEGACY,
+        /**
+         * Actions manifest based.
+         */
+        ACTION_BASED;
+    }
+
     /**
      * Create a new
      * <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a>
@@ -104,6 +168,89 @@ public class LWJGLOpenVRInput implements VRInputAPI {
      */
     public LWJGLOpenVRInput(VREnvironment environment) {
         this.environment = environment;
+
+        inputHandles.put(null, VR.k_ulInvalidInputValueHandle);
+    }
+
+    @Override
+    public void registerActionManifest(String actionManifestAbsolutePath, String startingActiveActionSets){
+        inputMode = InputMode.ACTION_BASED;
+        int errorCode = VRInput.VRInput_SetActionManifestPath(actionManifestAbsolutePath);
+
+        if ( errorCode != 0 )
+        {
+            logger.warning( "An error code of " + errorCode + " was reported while registering an action manifest" );
+        }
+        setActiveActionSet(startingActiveActionSets);
+    }
+
+    @Override
+    public void setActiveActionSet(String actionSet){
+        assert inputMode == InputMode.ACTION_BASED : "registerActionManifest must be called before attempting to fetch action states";
+
+
+        long actionSetHandle;
+        if (actionSetHandles.containsKey(actionSet)){
+            actionSetHandle = actionSetHandles.get(actionSet);
+        }else{
+            LongBuffer longBuffer = BufferUtils.createLongBuffer(1);
+            int errorCode = VRInput.VRInput_GetActionHandle(actionSet, longBuffer);
+            if ( errorCode != 0 )
+            {
+                logger.warning( "An error code of " + errorCode + " was reported while fetching an action set handle for " + actionSet );
+            }
+            actionSetHandle = longBuffer.get(0);
+            actionSetHandles.put(actionSet,actionSetHandle);
+        }
+
+        //Todo: this seems to imply that you could have multiple active action sets at once (Although I was not able to get that to work), allow multiple action sets
+        activeActionSets = VRActiveActionSet.create(1);
+        activeActionSets.ulActionSet(actionSetHandle);
+        activeActionSets.ulRestrictedToDevice(VR.k_ulInvalidInputValueHandle); // both hands
+    }
+
+    @Override
+    public DigitalActionState getDigitalActionState(String actionName, String restrictToInput){
+        assert inputMode == InputMode.ACTION_BASED : "registerActionManifest must be called before attempting to fetch action states";
+
+        LWJGLOpenVRDigitalActionData actionDataObjects = digitalActions.get(actionName);
+        if (actionDataObjects == null){
+            //this is the first time the action has been used. We must obtain a handle to it to efficiently fetch it in future
+            long handle = fetchActionHandle(actionName);
+            actionDataObjects = new LWJGLOpenVRDigitalActionData(actionName, handle, InputDigitalActionData.create());
+            digitalActions.put(actionName, actionDataObjects);
+        }
+        int errorCode = VRInput.VRInput_GetDigitalActionData(actionDataObjects.actionHandle, actionDataObjects.actionData, getOrFetchInputHandle(restrictToInput));
+
+        if (errorCode == VR.EVRInputError_VRInputError_WrongType){
+            throw new RuntimeException("Attempted to fetch a non-digital state as if it is digital");
+        }else if (errorCode!=0){
+            logger.warning( "An error code of " + errorCode + " was reported while fetching an action state for " + actionName );
+        }
+
+        return new DigitalActionState(actionDataObjects.actionData.bState(), actionDataObjects.actionData.bChanged());
+    }
+
+    @Override
+    public AnalogActionState getAnalogActionState(String actionName, String restrictToInput ){
+        assert inputMode == InputMode.ACTION_BASED : "registerActionManifest must be called before attempting to fetch action states";
+
+        LWJGLOpenVRAnalogActionData actionDataObjects = analogActions.get(actionName);
+        if (actionDataObjects == null){
+            //this is the first time the action has been used. We must obtain a handle to it to efficiently fetch it in future
+            long handle = fetchActionHandle(actionName);
+            actionDataObjects = new LWJGLOpenVRAnalogActionData(actionName, handle, InputAnalogActionData.create());
+            analogActions.put(actionName, actionDataObjects);
+        }
+        int errorCode = VRInput.VRInput_GetAnalogActionData(actionDataObjects.actionHandle, actionDataObjects.actionData, getOrFetchInputHandle(restrictToInput));
+
+        if (errorCode == VR.EVRInputError_VRInputError_WrongType){
+            throw new RuntimeException("Attempted to fetch a non-analog state as if it is analog");
+        }else if (errorCode!=0){
+            logger.warning( "An error code of " + errorCode + " was reported while fetching an action state for " + actionName );
+        }
+
+        return new AnalogActionState(actionDataObjects.actionData.x(), actionDataObjects.actionData.y(), actionDataObjects.actionData.z(), actionDataObjects.actionData.deltaX(), actionDataObjects.actionData.deltaY(), actionDataObjects.actionData.deltaZ());
     }
 
     @Override
@@ -128,6 +275,7 @@ public class LWJGLOpenVRInput implements VRInputAPI {
 
     @Override
     public boolean isButtonDown(int controllerIndex, VRInputType checkButton) {
+        assert inputMode != InputMode.ACTION_BASED : "registerActionManifest has been called, legacy button access disabled";
         VRControllerState cs = cStates[LWJGLOpenVRInput.controllerIndex[controllerIndex]];
         switch (checkButton) {
             default:
@@ -434,6 +582,20 @@ public class LWJGLOpenVRInput implements VRInputAPI {
                 0, (short) Math.round(3f * seconds / 1e-3f));
     }
 
+    @Override
+    public void triggerHapticAction(String actionName, float duration, float frequency, float amplitude, String restrictToInput ){
+        long hapticActionHandle;
+        if (!hapticActionHandles.containsKey(actionName)){
+            //this is the first time the action has been used. We must obtain a handle to it to efficiently fetch it in future
+            hapticActionHandle = fetchActionHandle(actionName);
+            hapticActionHandles.put(actionName, hapticActionHandle);
+        }else{
+            hapticActionHandle = hapticActionHandles.get(actionName);
+        }
+
+        VRInput.VRInput_TriggerHapticVibrationAction(hapticActionHandle, 0, duration, frequency, amplitude, getOrFetchInputHandle(restrictToInput));
+    }
+
     @Override
     public void updateConnectedControllers() {
         logger.config("Updating connected controllers.");
@@ -478,16 +640,65 @@ public class LWJGLOpenVRInput implements VRInputAPI {
     public void updateControllerStates() {
 
         if (environment != null) {
-            for (int i = 0; i < controllerCount; i++) {
-                int index = controllerIndex[i];
-                VRSystem.VRSystem_GetControllerState(index, cStates[index], 64);
-                cStates[index].ulButtonPressed();
-                cStates[index].rAxis();
+            switch(inputMode){
+                case ACTION_BASED:
+                    int errorCode = VRInput.VRInput_UpdateActionState(activeActionSets,  VRActiveActionSet.SIZEOF);
+                    if(errorCode!=0){
+                        logger.warning("An error code of " + errorCode + " was returned while upding the action states");
+                    }
+                    break;
+                case LEGACY:
+                    for (int i = 0; i < controllerCount; i++) {
+                        int index = controllerIndex[i];
+                        VRSystem.VRSystem_GetControllerState(index, cStates[index], 64);
+                        cStates[index].ulButtonPressed();
+                        cStates[index].rAxis();
+                    }
+                    break;
             }
+
         } else {
             throw new IllegalStateException("VR input is not attached to a VR environment.");
         }
 
     }
 
+    /**
+     * Converts an action name (as it appears in the action manifest) to a handle (long) that the rest of the
+     * lwjgl (and openVR) wants to talk in
+     * @param actionName The name of the action. Will be something like /actions/main/in/openInventory
+     * @return a long that is the handle that can be used to refer to the action
+     */
+    private long fetchActionHandle( String actionName ){
+        LongBuffer longBuffer = BufferUtils.createLongBuffer(1);
+        int errorCode = VRInput.VRInput_GetActionHandle(actionName, longBuffer);
+        if (errorCode !=0 ){
+            logger.warning( "An error code of " + errorCode + " was reported while registering an action manifest" );
+        }
+        return longBuffer.get(0);
+    }
+
+    /**
+     * Given an input name returns the handle to address it.
+     *
+     * If a cached handle is available it is returned, if not it is fetched from openVr
+     *
+     * @param inputName the input name, e.g. /user/hand/right. Or null, which means "any input"
+     * @return
+     */
+    public long getOrFetchInputHandle( String inputName ){
+        if(!inputHandles.containsKey(inputName)){
+            LongBuffer longBuffer = BufferUtils.createLongBuffer(1);
+
+            int errorCode = VRInput.VRInput_GetInputSourceHandle(inputName, longBuffer);
+            if (errorCode !=0 ){
+                logger.warning( "An error code of " + errorCode + " was reported while fetching an input manifest" );
+            }
+            long handle = longBuffer.get(0);
+            inputHandles.put(inputName, handle);
+        }
+
+        return inputHandles.get(inputName);
+    }
+
 }