What would be a good generic way to add Point of View Hat input on BABYLON.GenericPad?

My question would be, what a proper/good generic way could be to add standard Point of View Hat input to BABYLON.GenericPad class?

Gamepads with DPads
Some joysticks and gamepads are equipped with something called a Hat Switch or a Point of View adjustment knob. This is a default HID feature and shows up in the standard Gamepad configuration dialog box in Windows.

In my case I have a very generic $5 wired USB gamepad which looks like a Dual Shock controller in almost every way. It has the same amount of (software exposed) buttons, 2 handles, and vibration/rumble option.

DPad exposed as POV hat
It also has a button + LED to toggle between digital and analog controller mode:

  • In digital mode it converts the DPad buttons to the same as the left stick (so DPad up-right becomes leftStick.x = 1 and leftStick.y = -1
  • In analog mode the DPad buttons work as a point of view hat switch. DPad up-right becomes axis[9] = -0.71429. The left stick then works independently of the DPad buttons.

Reported axis values for POV hat
We will continue with the analog mode, as that is the POV mode:


No DPad button pressed (if pressed, shows up as arrow in any of the 8 marked directions)


DPad button Up+Right pressed.

When no DPad is pressed, then Windows won’t show an arrow in any of the 8 directions on that gamepad configuration screen.

Apparently, POV (Hat Switch in HID) is most often exposed to the web on gamepad axis 9 (so the 10th axis).

  • POV will show up as axis[9] with a value between -1 and 1 inclusive, when a DPad is pressed.
  • When no DPad is pressed, a constant ‘NULL’ meaning value outside -1 and 1 range will be reported (e.g. 3.28571 but I’ve also seen 1.24 etc.).

Converting POV hat axis value to DPad directions
Decoding of the axis values is quite simple. The values for the number of DPad combinations (angles) are linearly spread out over -1 and 1 axis[9] range. So for n=8 directions, Up become -1, Up-Right becomes -0.71429, etc. until you have 1 for Up-Left. Some systems divide the range over n+1, meaning that you’ll end up at a little less than 1 for Up-Left.

POV hat as BABYLON.GenericDPad?
Hence I have the impression that a good way to model standard POV hat data at the last axis (axis[9]) would be to expose something like a DPad also on the BABYLON.GenericPad. And then convert the axis[9] values to (a combination of) pressed/released GenericDPad ‘buttons’. Similarly to how you can subscribe to XBox360DPad and DualShockDPad observers.

RSVP
Such a POV Hat 2 DPad feature would probably enable a lot of USB Gamepads out there with 4 extra ‘buttons’ to play with in your BabylonJS apps.

  • How do you guys and gals think about this?

Adding @PolygonalSun and @RaananW who know a lot about gamepad

Hey @QuintusHegie, so if this is a common feature in several gamepads, it might be worth adding support for POV Hat input. I am curious if the single axis for analog POV is a common way of handling that or if there would be other ways that it’d be handled, depending on the Gamepad. As an example, I have a cheap, third-party SNES style controller that I use for input testing and its buttons are mapped differently from the way more modern style controllers are mapped. Furthermore, is the axis that is used for this POV Hat Input always the last axis?

Some things that I would be curious about are how the GamePad API (what we use as the backbone for our GamepadManager and DeviceInputSystem) behaves when this button is toggled (does it trigger a button press, disconnect/reconnect, etc.). Additionally, how does the API handle the available axes and buttons when the Digital/Analog button is pressed. The reason that I wonder about this would be if buttons are pressed while that button is toggled, it could erroneously report a button’s status.

With the single-axis values, it would also good to have a range of detection when interpreting input (eg. -1 to -0.99999 being up). Floating point numbers tend to have a bit of variance and so having that range of detection helps to avoid missed inputs due to precision.

I could see this feature being implemented by adding both a DPad and Analog Stick object to be added to Babylon’s GamePad object. Then, only the available version of it would update and if that gamepad doesn’t have that button, they would do nothing.

In any case, I think that you’ve got a good amount of data and it might not hurt to try creating a POC for the Gamepad object.

Yes, a POV Hat Switch is a common feature, that also appears in the HID tables.

image

Besides PC USB controllers, there are also some consoles that have gamepads with analog/digital switch on it, such as Sony Playstation versions.

The analog/digital mode button on the device is (so far as I have seen with controllers connected using Gamepad API and/or Windows Configure Gamepad dialog) not mapped as a button to the software (i.e. data exposed in the input report send to PC). If I remember the HID documentation well, devices may be allowed to use different HID descriptors for different modes or something like that. But so far I have not seen this ‘state indicator’ go through to the Gamepad API.

When I toggle the analog/digital switch on the device, some default axis values change very slightly. With a controller like in the picture above, for example the axis[0] and axis[1] both in rest change from 0.00392 to -0.00392 (still within ‘dead zone’). This might be due to a changed input report descriptor or something.

Some games can force a gamepad to explicitly use analog mode. That means that the ‘state toggle’ may be get/set stuffed somewhere deep in the device features (part of HID framework).

I will run some more tests in the coming weeks with a set of controllers I have; some PC USB controllers and also some (retro) console controllers that require a console-to-USB-HID-gamepad adapter.

Note that active adapters decide upon how the device is exposed to the PC, as in what the HID descriptor will be and how the data to PC will look. That can be different from the ‘raw’ data send from the controller to the adapter.

image

This gameport adapter for example has a hardware switch that allows the user to present the connected device in 4 different ways to the PC. Two of which are showing a Hat Switch in this case. So same input from controller to adapter, but different HIDs from adapter to PC.

Also when I find some more documentation on how Gamepad API and/or Windows maps HID descriptors of gamepads to axis (e.g. what are the rules to map HID X to axis[0] and HID Y to axis[1] and HID Z to axis[2] and HID POV to axis[9] etc.) I’ll post that here.

3 Likes

Ok, I did some further research on the POV hat switch topic.

On Windows 10, notice the labels below the 4 axis in the gamepad setup screen:
image
So user axis 1 (gamepad api axis 0) represents HID Usage X.
So user axis 2 (gamepad api axis 1) represents HID Usage Y.
User axis 3 (gamepad api axis 2) represents HID Usage Z.
But user axis 4 on this screen, from HID Usage Rz, is found as gamepad api axis 5.
Babylon.js properly picks up gamepad axis 5 to use for the right stick y axis, so that goes fine.
But why axis #5 all of a sudden?

Well I found an old post on another forum on HID mappings going on inside Windows system.
Here is the screenshot:

When you take a close look at the last hex part of the Usage number, you might spot a pattern here.
0x30 (X) mapped to gamepad axis 0.
0x31 (Y) mapped to gamepad axis 1.
0x32 (Z) mapped to gamepad axis 2.
0x35 (RZ) mapped to gamepad axis 5.
0x39 (Hat Switch) mapped to gamepad axis 9.

So the logical mapping of POV to axis may be the same for all devices (gamepads and/or adapters) that describe their POV properly as a POV Hat Switch in HID descriptor. I have no clue how Linux or Apple OS will map, but at the least there seems consistency on Windows platform.

Axis 9 values for POV Hat Switch
(unpressed): 3.28571 (a value outside -1 and 1 range)
North: -1.00000
North-East: -0.71429
East: -0.42857
South-East: -0.14286
South: 0.14286
South-West: 0.42857
West: 0.71429
North-West: 1.00000

There is some radial logic to these steps (0.28571 == 2/7), and some controller (adapters) take a bit smaller instead of 2/7 so North-West will end somewhat below 1.0 value. I guess this depends a bit on the HID Descriptor from the adapter/device (the HID descriptor can specify the physical and logical min/max values). Also 11x 2/7 equals almost Pi, an important number when the POV is expressed as some kind of rotation.

But what to do with this in a generic matter for BabylonJS gamepads and cameras?

I suggest 2 new standard features to the framework:

  1. The POV hat switch is meant to adjust the POV. Thus based on the direction received, the POV (camera) should rotate accordingly relative but to the current POV. I believe we should think of its best fitting use to mimic something like a VR headset, that adjusts your orientation (not your camera position) on how you tilt/turn your head.
  2. Optionally expose the POV also as DPad buttons pressed/released. Because for gamepad controllers in analog mode (having axes enabled), the POV is typically commanded through the D-pad buttons on the gamepad. But do note that the gamepad API won’t give you button indices for this, so it will probably be like to create 4 additional virtual buttons in the BabylonJS gamepad layer over the Gamepad API layer.

How do you feel about these suggestions?