Search Unity

Question Wrapping System-Managed Device in Custom Device

Discussion in 'Input System' started by baakta, Feb 1, 2023.

  1. baakta

    baakta

    Joined:
    May 22, 2019
    Posts:
    14
    I'm trying to copy/reference state from a OculusTouchController device in a custom input device. Both devices inherit XRControllerWithRumble and implement layouts which extend "XRController." My intended goal is to pass the state from the OculusTouchController to my custom device state in the "OnUpdate" callback included with "IInputUpdateCallbackReceiver" on my custom device class. OnUpdate looks like this:

    Code (CSharp):
    1.     public void OnUpdate()
    2.     {
    3.         CustomHandedXRControllerDeviceState state;
    4.  
    5.         InputDevice targetDevice = InputSystem.GetDevice<XRController>(
    6.                 deviceHandedness == CustomHandedness.Active
    7.                 ? InputDeviceManager.ActiveHand
    8.                 : InputDeviceManager.PassiveHand
    9.             );
    10.  
    11.         if (targetDevice != null)
    12.         {
    13.             state = new CustomHandedXRControllerDeviceState()
    14.             {
    15.                 thumbstick = targetDevice.GetChildControl<Vector2Control>("thumbstick"),
    16.                 trigger = targetDevice.GetChildControl<AxisControl>("trigger"),
    17.                 triggerTouched = targetDevice.GetChildControl<AxisControl>("triggerTouched"),
    18.                 grip = targetDevice.GetChildControl<AxisControl>("grip"),
    19.                 buttonSouth = targetDevice.GetChildControl<ButtonControl>("primaryButton"),
    20.                 buttonNorth = targetDevice.GetChildControl<ButtonControl>("secondaryButton"),
    21.                 gripPressed = targetDevice.GetChildControl<ButtonControl>("gripPressed"),
    22.                 buttonSystem = targetDevice.GetChildControl<ButtonControl>("start"),
    23.                 thumbstickClicked = targetDevice.GetChildControl<ButtonControl>("thumbstickClicked"),
    24.                 buttonSouthTouched = targetDevice.GetChildControl<ButtonControl>("primaryTouched"),
    25.                 buttonNorthTouched = targetDevice.GetChildControl<ButtonControl>("secondaryTouched"),
    26.                 thumbstickTouched = targetDevice.GetChildControl<ButtonControl>("thumbstickTouched"),
    27.                 triggerPressed = targetDevice.GetChildControl<ButtonControl>("triggerPressed")
    28.             };
    29.  
    30.             InputSystem.QueueStateEvent(this, state);
    31.         }
    32.     }
    My state declaration looks like this:

    Code (CSharp):
    1. public struct CustomHandedXRControllerDeviceState : IInputStateTypeInfo
    2. {
    3.     [InputControl] public Vector2Control thumbstick { get; set; }
    4.     [InputControl] public AxisControl trigger { get; set; }
    5.     [InputControl] public AxisControl grip { get; set; }
    6.     [InputControl] public ButtonControl buttonSouth { get; set; }
    7.     [InputControl] public ButtonControl buttonNorth { get; set; }
    8.     [InputControl] public ButtonControl gripPressed { get; set; }
    9.     [InputControl] public ButtonControl buttonSystem { get; set; }
    10.     [InputControl] public ButtonControl thumbstickClicked { get; set; }
    11.     [InputControl] public ButtonControl buttonSouthTouched { get; set; }
    12.     [InputControl] public ButtonControl buttonNorthTouched { get; set; }
    13.     [InputControl] public AxisControl triggerTouched { get; set; }
    14.     [InputControl] public ButtonControl triggerPressed { get; set; }
    15.     [InputControl] public ButtonControl thumbstickTouched { get; set; }
    16.  
    17. }
    When I run this code and an OculusTouchController device is added to the system, the state on my custom device is updated with unexpected values that do not correlate to the values in the state of the OculusTouchController (I'm viewing this with Input Debugger). I think I'm transferring the state incorrectly, but all examples I can find online demonstrate QueueStateEvent with empty state or explicitly map their InputControls to memory (OculusTouchController does not do this). And there are no examples of passing state from a system-managed input device - only third-party APIs.

    I think I'm confused, and I haven't been able to find insights in documentation or forum posts. Any help would be appreciated! Thanks in advance!
     
  2. rdjadu

    rdjadu

    Joined:
    May 9, 2022
    Posts:
    116
    State must be blittable structs. For example, for the thumbstick it would be a Vector2 value.

    Code (CSharp):
    1. public struct CustomHandedXRControllerDeviceState : IInputStateTypeInfo
    2. {
    3.     [InputControl]
    4.     public Vector2 thumbstick;
    5.  
    6.     //...
    7.  
    However, in your case, you could even forego the entire custom state struct thing and just let the system put together a state layout automatically.

    Code (CSharp):
    1. public void OnUpdate()
    2. {
    3.     //...
    4.  
    5.     using (StateEvent.From(this, out var eventPtr))
    6.     {
    7.         this["thumbstick"].WriteValueIntoEvent(targetDevice.thumbstick.GetValue(), eventPtr);
    8.         //...
    9.         InputSystem.QueueEvent(eventPtr);
    10.     }
    Just rather expensive this way.
     
  3. baakta

    baakta

    Joined:
    May 22, 2019
    Posts:
    14
    Thank you for the reply! Both of these examples make sense to me and should help me along.

    I think some of my confusion was due to this Oculus device script included in the input system's XR plugin:
    InputSystem/Oculus.cs at develop · Unity-Technologies/InputSystem (github.com)

    I'm struggling to understand how the OculusTouchController class in particular is storing state without an apparent state struct or blittable fields for input controls. If you have a second to take a look, I'd love some clarification; if not, thanks for your initial help.
     
  4. rdjadu

    rdjadu

    Joined:
    May 9, 2022
    Posts:
    116
    It uses what's called a "layout builder". You can find it here (though the code in the input system package is legacy; when you install the XR packages, all that stuff is replaced; but it still does it the same way).

    In the XR case, the XR subsystem inside of Unity native sends a description of the XR device over to C#. The layout builder for XR devices picks that up and constructs a device layout for the device based on that description.

    In other words, when you connect an Oculus device, it doesn't use
    OculusHMD
    as is. Instead it constructs a layout *based* on it. And that layout has all the magic state details in it. But it's dynamic and not hardcoded. Sort of like HID devices just XR specific.
     
  5. rdjadu

    rdjadu

    Joined:
    May 9, 2022
    Posts:
    116
    Just to add that: you can actually see this happening in the input debugger. When you double click an XR device, in the window that comes up it tells you which layout that uses. And if you browse to that layout in the "Layouts" tree of the debugger, you can see all the auto-generated stuff that's been put in there.
     
  6. baakta

    baakta

    Joined:
    May 22, 2019
    Posts:
    14
    Ahhh, makes sense. I actually did notice that there were additional Oculus controller layouts being added when the controllers connected at runtime. Thanks for the explanation! I was able to get my custom device up and running.
     
    rdjadu likes this.