Unity XR Controller Input Data Provider

  • Script that inherits from InputDataProvider and uses XR controller as input source

  • It uses Unity's built-in XR input API, therefore it is platform-agnostic and can be used for multiple different XR platforms

  • It tracks the state of common XR controller buttons and sets the target poses based on iput buttons state

  • It is highly customizable, all target poses to provide can be configured as well as all possible conditions that need to be fulfilled in order to trigger the specific poses

Public properties

HandType HandType { get; set; }

  • Specifies for which hand is input being provided by this script, left or right

bool RunInFixedUpdate { get; set; }

  • UnityXRControllerInputDataProvider executes its main logic in the Update method by default. If this property is set to true, then input data provider will execute its main logic in FixedUpdate instead of Update

XRControllerButtonCollection Buttons { get; }

  • Collection of XR controller button states for every possible XR controller button. This collection can be indexed into using a zero-based integer or the XRControllerButton enumeration. Buttons being tracked are:

    • Primary button

    • Secondary button

    • Trigger button

    • Grip button

    • Primary 2D axis

    • Secondary 2D axis

  • Each entry in this collection is a XRControllerButtonState object that provides various information and values about the state of the corresponding controller button, such as whether the button is touched or pressed and the value or strength of the button press as well as the current values for controller axes. It also provides a number of events for common actions performed with the button - touch, press, depress and untouch

HandSkeletonPose BindPose { get; set; }

  • Specifies initial or default pose for a hand skeleton. The type of this property is a scriptable object, so different skeleton poses can be created and shared as assets

  • This pose is also the pose that the input data provider will provide when it is not tracking its input source (XR controller), but only if the ProvideBindPoseWhenNotTracking property (described below) is set to true

  • This property is optional, if nothing is assigned then input data provider is simply not going to set the target pose for a hand skeleton in case it is not tracking, the provided pose is going to remain the same as the last one that was set (assuming nothing else changes it in some way)

bool ProvideBindPoseWhenNotTracking { get; set; }

  • As mentioned above in BindPose property description, this property specifies whether the XR controller input data provider is going to set the assigned bind pose as the target pose to provide when the corresponding XR controller device is not being tracked

  • If false, the effect will be the same as having no bind pose assigned - nothing will be performed by the XR controller input data provider regarding the provided hand skeleton pose in case no corresponding XR controller device is being tracked

float MaxBoneVelocity { get; set; }

  • Whenever the XR controller input data provider triggers a new hand skeleton pose to provide, it does not set/provide the triggered pose immediately, but rather it gradually interpolates the provided pose towards the target, otherwise the rendered hand model that uses the XR controller input data provider would snap between poses instantly whenever some input state that triggers a new hand pose is changed (some specific button being pressed for example)

  • The method for interpolating provided pose is velocity based, i.e. the input data provider moves the provided pose to target pose gradually by limiting the velocities with which the bone poses are changed

  • MaxBoneVelocity specifies the amount of maximum linear velocity, i.e. maximum velocity with which the position component of a bone pose can change

  • This applies to change in position of a bone relative to its parent bone, root pose is not limited by this

  • In most cases linear pose change will not be a concern since the bones are probably not going to have their relative positions ever changed, i.e. every bone will have constant length

  • To achieve instant snap to a new target pose, set this value to a very large number, e.g. float.MaxValue is supported

float MaxBoneAngularVelocity { get; set; }

  • This has the same effect as MaxBoneVelocity, but it applies to bone rotations instead of positions - to be exact it applies to bone rotations relative to their corresponding parent bones

  • All the rules and effects of MaxBoneVelocity equivalently apply here for bone rotations

List<XRControllerPoseTrigger> PoseTriggers { get; }

  • List of objects that specify various conditions and settings that control the target hand poses that the XR controller input data provider will use to provide hand skeleton poses

  • Every entry in this list is of XRControllerPoseTrigger type. It provides access to the condition that needs to be fulfilled in order to trigger the target pose, which is usually a specific XR controller button press or touch (although it can be other), and also the reference to the hand skeleton pose to trigger as well as certain other properties that specify which exact hand skeleton bones should be manipulated and what pose components to consider (position and/or rotation)

  • Important to note is that entries have their conditions evaluated in order in which they appear in the PoseTriggers list, the first that has its condition fulfilled is the one that will have its assigned hand skeleton pose triggered, so special care should be taken when it comes to ordering the triggers in the list

XR Controller Button State

XRControllerButtonState is an abstract class in OctoXR that is used in the context of a XR controller input data provider to represent a state of a XR controller button.

Public properties

XRControllerButton Button { get; }

  • Property of XRControllerButton enumeration type, it specifies the exact XR controller button whose state is encapsulated by a XRControllerButtonState instance

bool IsTouched { get; }

  • Specifies whether the button is being touched currently

bool IsPressed { get; }

  • Specifies whether the button is pressed

abstract float Value { get; }

  • A value from 0 to 1 that specifies the amount of button press: some buttons have a relatively large range of motion from being completely depressed to being completely pressed. For such buttons this range can be detected and represented with a value that ranges from 0 to 1, 0 being for completely depressed/released and 1 for fully pressed. XR controllers usually have trigger and grip buttons which are this type of a button

  • For regular controller buttons (primary, secondary and axes buttons) this value will be either 0 or 1, there will be no in-between values

UnityEvent OnTouch { get; }

  • Event sent when the button has been touched

UnityEvent OnPressed { get; }

  • Event sent when the button has been pressed

UnityEvent OnDepressed { get; }

  • Event sent when the button has been depressed/released

UnityEvent OnUntouch { get; }

  • Event sent when the button touching has ended

Public methods

abstract void Update(InputDevice inputDevice)

  • Updates the state of the corresponding XR controller button based on the values obtained from a XR input device specified by the method parameter

  • This is called by UnityXRControllerInputDataProvider when updating the input and hand pose state

virtual void OnValidateWhenTracking()

  • Validates the values that represent the current button state, this is mostly used for making UnityXRControllerInputDataProvider behaviour in editor the same as in play mode so any effects of changes made are immediately observable in edit-time

  • This is also called by UnityXRControllerInputDataProvider at appropriate moments and only if its IsTracking property is true

Protected methods

void SetState(bool isTouched, bool isPressed)

  • Sets the button's base state to the values specified by the method parameters and also raises the appropriate events based on resulting button state changes

There are three concrete implementation of the XRControllerButtonState class:

  • XRControllerBinaryButton

    • This class is used to represent the state of a XR controller button that basically has two states - either it is pressed or depressed, hence the 'binary' designation

    • As mentioned in the previous text, Value property of this type of button will either be 0 when depressed or 1 when pressed

    • This is used to track the state of the primary and the secondary XR controller button

  • XRControllerLerpButton

    • This class is used to represent a XR controller button that can be partially or gradually pressed, its Value property goes from 0 to 1 based on how much the button is being pressed

    • XRControllerLerpButton exposes two additional properties: MinValueToTouch and MinValueToPress - these are values from 0 to 1 that define minimum amount of button press (Value) in order to consider the button as being touched/pressed

    • This is used to track the state of the trigger and the grip button of a XR controller

  • XRController2DAxis

    • This class is used to represent the state of a XR controller axis

    • It defines X and Y properties that specify the current value for the X and Y contoller axis, the values range from -1 to 1

    • For the pressed/depressed state it behaves the same as XRControllerBinaryButton

    • This is used to track the state of the primary and the secondary 2D axis button of a XR controller

UnityXRControllerInputDataProvider exposes the Buttons collection of six objects of XRControllerButtonState type:

  • The first two entries correspond to the primary and secondary controller button

  • The middle two entries are for trigger and grip controller button

  • The last two are used for primary and secondary 2D axis of a XR controller

Depending on the specific hardware used not all of the buttons will necessarily be supported, for example Oculus Touch controller has no secondary 2D axis. Such buttons will never have their corresponding entries updated/changed, they will always appear inactive in the Buttons list.

Buttons collection can be indexed into using the XRControllerButton enumeration or a zero-based integer (0 to 5) and the result of indexing operation is the corresponding XRControllerButtonState object. For convenience, the Buttons collection, which is of XRControllerButtonCollection type, exposes properties for each button separately. Unlike indexing results, properties are strongly typed so each button is exactly represented by its corresponding button type - XRControllerBinaryButton for primary and secondary buttons, XRControllerLerpButton for trigger and grip buttons and finally XRController2DAxis for primary and secondary 2D axis buttons.

XR Controller Pose Trigger

XRControllerPoseTrigger is a class in OctoXR that encapsulates parameters and settings for triggering a hand pose for a XR controller input data provider to provide. It consists of condition that needs to be fulfilled, a hand skeleton pose reference that specifies pose that is triggered by the pose trigger and other properties that specify how to drive the skeleton pose towards the triggered target pose.

Public properties

bool Enabled { get; set; }

  • Specifies whether the pose trigger is enabled. Disabled triggers will be ignored by the UnityXRControllerInputDataProvider when it is evaluating conditions for triggering new hand poses

XRControllerCondition Condition { get; set; }

  • Reference to an object of type XRControllerCondition that defines a condition that needs to be fulfilled in order to trigger the pose trigger's assigned hand skeleton pose

HandSkeletonPose TargetPose { get; set; }

  • Reference to the hand skeleton pose that is triggered when the pose trigger's condition is fulfilled

  • If no pose is assigned and the pose trigger's condition is fulfilled then UnityXRControllerInputDataProvider will keep providing the same pose it has been providing on its last run

HandBones TargetBones { get; set; }

  • Bit mask that specifies hand bones that the XR controller input data provider should manipulate when it drives the provided hand skeleton pose towards the target pose that is specified by the pose trigger

  • As mentioned above, UnityXRControllerInputDataProvider provides hand skeleton poses that are gradually driven towards target poses. TargetBones property specifies which exact hand bone poses should be driven towards the pose trigger's target pose after it is triggered, hand bones that are not indicated by the TargetBones mask will not have their corresponding provided poses updated by the UnityXRControllerInputDataProvider

PoseComponents TargetPoseComponents { get; set; }

  • Specifies which components of the target bone poses should be manipulated by the XR controller input data provider when it drives the provided hand skeleton pose towards the target pose that is specified by the pose trigger

  • This property controls whether the UnityXRControllerInputDataProvider will update positions, rotations or both of the TargetBones after the pose trigger's target pose is triggered

  • Usually you will want rotations to be driven since relative positions between hand bones are not likely to differ between different hand poses, although in that case driving positions as well as rotations will not have different effect

bool InterpolateTargetPose { get; set; }

  • When pose trigger's condition is evaluated, one of the results obtained is total button value that specifies the amount of button press (or how much is the button pressed) for one or more buttons that affect the pose trigger's condition

  • This property specifies whether the pose that will be provided by the UnityXRControllerInputDataProvider (as long as the pose trigger is triggered) will be obtained not by driving it directly towards the trigger's target pose, but towards the pose that is obtained by interpolating between the last active target pose and the trigger's target pose using the previously described button value as interpolation factor. This means that if the total button value is 1, then the XR controller input data provider will drive the provided pose towards the trigger's target pose; if it is 0, then it is still going to drive provided pose towards the previously active one

  • This property is most likely to be set to true for XR controller's grip button, so when the pose trigger's target pose is triggered/activated, the provided pose is going to be as close to the target pose as is the strength with which the grip button is being pressed. The most common example of this behaviour is the process of grabbing with VR hand - the more you press the controller's grip button the stonger the grab is and rendered hand becomes more closed until it is completely closed in fist pose once the grip button is fully pressed

XR Controller Condition

Class in OctoXR that represents a XR controller button based condition that can be evaluated at any time. It is mainly used by UnityXRControllerInputDataProvider in conjunction with XRControllerPoseTrigger in order to determine whether a new hand pose should be targeted when determining the hand pose to provide.

XRControllerCondition is an abstract class, it requires an implementation of two members:

public abstract XRControllerConditionType ConditionType { get; }

  • This property just returns a value of XRControllerConditionType enumeration type that specifies the type of condition, currently there are three types (explained in more detail further below):

    • Button Action - Button state based condition

    • Binary - Condition that has two XRControllerCondition instances nested inside it and also a property that specifies which binary logic operation (and, or, xor) to perform between those nested conditions when the binary condition is being evaluated

    • Constant - Condition that always evaluates to true, i.e. it is always fulfilled, unless it is inverted in which case it is never fulfilled

  • Important thing to note is that ConditionType property does not really have any practical use by built application's runtime, it is only defined to support changing a XRControllerPoseTrigger's condition (or any other property of XRControllerCondition type serialized by reference) using Unity's editor UI

protected abstract bool EvaluateCore(XRContollerButtonCollection buttons, out float buttonValue)

  • Method that evaluates whether the condition is fulfilled or not based on state of XR controller buttons specified by the first parameter

  • Second parameter is an output parameter that should specify the amount of button press for the button that the condition is affected by. This is usually very simple for a button action condition which evaluates the state of a single XR controller button, but it gets more complicated for e.g. binary condition that can have multiple other conditions nested in it (including more binary conditions too). Binary condition solves this by applying certain aggregating function on the button values resulting from its two nested conditions

  • Note that in the context of a UnityXRControllerInputDataProvider this button value that is output by a XRControllerCondition is completely ignored if the condition is used in a XRControllerPoseTrigger that has its InterpolateTargetPose set to false

There are two more public members defined in XRControllerCondition:

bool Invert { get; set; }

  • This property specifies whether the result of the condition's evaluation method (EvaluateCore) should be inverted, i.e. true to false and false to true

  • Simply put if this property is true the result of the condition's evaluation is !EvaluateCore(...)

bool Evaluate(XRContollerButtonCollection buttons, out float buttonValue)

  • Basically this method just wraps the call to abstract EvaluateCore method described above. It potentially flips the result of EvaluateCore method based on the value of the Invert property

  • This method is called by UnityXRControllerInputDataProvider when it evaluates the hand pose to provide

There are three concrete XRControllerCondition implementations:

ButtonActionXRControllerCondition

This is probably the most commonly used condition for triggering hand poses, it is fulfilled when a specified XR controller button is pressed or touched. When exactly will it be fulfilled is configured via two properties:

XRControllerButton Button { get; set; }

  • Specifies which XR controller button should be checked

XRControllerButtonAction Action { get; set; }

  • Specifies an action that should be performed with the controller button in order for the condition to evaluate to true - it can be either Press or Touch

BinaryXRControllerCondition

As briefly mentioned before, this condition consists of two XRControllerCondition's that could be viewed as the binary condition's child conditions. When binary condition is being evaluated, it will recursively evaluate its child conditions (call their Evaluate methods) and combine their evaluation results with a boolean logical operation that can be specified via property. Operation can be AND, OR or XOR. In addition to that, binary condition will also aggregate button values resulting from its child conditions in order to output the final button value. Aggregation method can also be specified via property and it can be set to one of several values - use button value from the binary condition's left child condition and ignore the other one's or vice-versa, or use one of the math functions on the two button values: average, maximum or minimum.

Properties of the BinaryXRControllerCondition:

XRControllerCondition Condition1 { get; set; }

  • The first condition nested in the binary condition, or binary condition's left child condition

  • Null value is not allowed

BooleanBinaryOp Op { get; set; }

  • The binary boolean logic operation to perform between the evaluation results of the binary condition's child conditions when the binary condition is being evaluated

XRControllerCondition Condition2 { get; set; }

  • The second condition nested in the binary condition, or binary condition's right child condition

  • Null value is not allowed

BinaryXRControllerConditionButtonValueCombine ButtonValueCombine { get; set; }

  • Specifies how should the binary condition combine the button values resulting from evaluations of its child conditions in order to arrive to the final button value

Binary condition is a very powerful construct, it is essentially what allows for specifying complex conditions that should be fulfilled in order to trigger different hand skeleton poses in the context of a UnityXRControllerInputDataProvider.

For example, if you want to trigger some specific hand pose when primary XR controller button is being touched, but only if the grip button is pressed, you would set BinaryXRControllerCondition to the corresponding XRControllerPoseTrigger's Condition (or choose the Binary value for the pose trigger's ConditionType in Unity editor), then set a ButtonActionXRControllerCondition instance for binary condition's Condition1 and also repeat the same for the binary condition's Condition2 (again, if you want to do this via editor UI, just choose ButtonAction for the ConditionType1 and ConditionType2 properties under the binary condition's UI section). After that you just need to configure Condition1 and Condition2: for Condition1 set Button to Primary and Action to Touch, for Condition2 set Button to Grip and Action to Press. Finally set the binary condition's Op to And and choose whichever value you want for ButtonValueCombine. That is how you would define the given condition for triggering desired hand pose.

This was still relatively simple example. With the combination of all different types of conditions, the ability to nest them inside binary conditions (including other binary conditions as well) and Invert property of a condition you can define any kind of button state-based condition with very complex logic behind it.

ConstantXRControllerCondition

The most simple type of XRControllerCondition. It always evaluates to true, i.e. its EvaluateCore method always returns true and for button value outputs 1. Consequently, if its Invert property is set to true, it always evaluates to false. Note however that for button value it will still output 1.

Custom XR controller condition

While you can can certainly implement your own XRControllerCondition and then use it in the context of a UnityXRControllerInputDataProvider, you will not be able to set it via Unity editor UI to a pose trigger's Condition or a binary condition's Condition1 or Condition2 because of the previously mentioned ways of setting those properties (via ConditionType). This is because of the Unity editor's restrictions imposed when dealing with references to abstract classes. In future versions of OctoXR this may be implemeneted in some more advanced way to allow for setting those properties using editor UI, but for now, you will only ever going to be able to set them to your own condition type via code.

Last updated