This file is written in the present tense. The date of this document is 2019-01-18
. OpenVR version 1.1.3b
. SteamVR beta version 1.2.7
. The current documentation is yassermalaika edited this page on Nov 21, 2018 · 9 revisions
.
The current documentation for OpenVR's IVRInput is not very thorough, outdated and mostly intended for Unity. I wrote this partly to isolate the possible reasons why my implementation in OpenVR Advanced Settings didn't work, and partly because there are no code examples for a working implementation of even the first few functions you need. This repo was quickly put together because some working code is better than no working code.
The source code should be seen as supplementary material. It might even make more sense for experienced devs to only look at the source code and skim the README.
This document is not intended to be an example of good C++, it's intended to quickly showcase which functions should be called when in order to get started. It is implemented as a class in order to be copy pastable into an existing project in order to test that nothing interferes with IVRInput. Having a known working copy to copy paste into my project would have stopped me from chasing shadows in the depths of the legacy code.
See the last section for links to references that helped me.
You'll need Visual Studio 2017 and a Vive controller. No other controllers are currently supported, because I only have a Vive controller.
-
Build the x64 debug version. No other versions are supported (by me, they'll obviously still work with OpenVR).
-
Copy
action_manifest.json
andlegacy_actions.json
into the executable directoryx64\Debug\
. -
Run the program. SteamVR should start and the console window for the program should open. Output should be blank unless there's an error, or until you turn on your Vive controller.
-
After turning on your Vive controller, the console window will spam
Action Set Active!
. -
Pressing the menu button will print
Next song set!
and exit the application. SteamVR will still be open. -
If you get those two prints when those actions are taken, the interface works on your machine.
-
Errors might be shown on the desktop or they might be shown in the headset. They might also not be shown at all.
-
The
C:\Program Files (x86)\Steam\logs\vrserver.txt
contains helpful logs for SteamVR. Sometimes it'll display messages relevant to input.
The current documentation does not do a very good job of explaining why things are done, and what is basically a list of function calls to make could have been better represented with actual source code. Like we're about to do.
The actions manifest (action_manifest.json
in the repo) contains the following:
- The default bindings.
Whenever a user starts your program without having set any bindings, this is what they use. The defaults are specific to various controllers. In our case we're using vive_controller
and pointing at the legacy_actions.json
file. The legacy_actions.json
file was created by me by entering the SteamVR bindings interface. The current documentation explains this process.
- Actions
An action represents, well, an action. It could be something like Open Inventory, Grab Gun or Play The Next Song. Your side of the API doesn't bother with how the user activated an action, just that he did. Which exact button the user pressed isn't relevant anyway.
In our example we'll be using /actions/main/in/playnexttrack
(the actions do not seem to be case sensitive).
- Action Sets
Action Sets contain a series of actions. I haven't messed with them so I can't give any specifics. In our example we'll just use /actions/main
.
- Localization
The localization string must be en_US
and NOT en
as the official documentation says, otherwise the localization will not show up and you will not get any errors.
The actions manifest that we'll use can be found here
The bindings manifest contains direct links between an action and an input on the controller. This can be generated by the SteamVR bindings interface. See the current documentation for how to enable.
The default bindings that we'll use can be found here.
Initialization is done in the Init function here. Initialization isn't being done in the constructor because the project I was trying to implement this in didn't support it.
vr::VR_Init(&initError, vr::VRApplication_Overlay);
This initializes SteamVR. If this isn't here, nothing else works.
vr::VRInput()->SetActionManifestPath(m_actionManifestPath.c_str());
This points at the action manifest we want to use. I couldn't get it to work without the action manifest being in the executable directory. There weren't error codes, it just didn't work. If your action manifest isn't valid JSON this will not error, but an error might be shown on the screen/in the headset.
vr::VRInput()->GetActionHandle(k_actionPlayNextTrack, &m_nextSongHandler);
Gets a handle to a specific action. If you attempt to get the handle of an action that isn't in the manifest there is generally no warning bells set off. GetDigitalActionData()
might give error 3, InvalidHandle, but I'm not sure. Make sure your strings are spelled correctly.
vr::VRInput()->GetActionSetHandle(k_actionSetMain, &m_mainSetHandler);
This gets a handle to the action set we want to monitor.
m_activeActionSet.ulActionSet = m_mainSetHandler;
m_activeActionSet.ulRestrictedToDevice = vr::k_ulInvalidInputValueHandle;
// When I didn't manually set priority zero it didn't work for me, for unknown reasons.
m_activeActionSet.nPriority = 0;
The struct should point to the set from GetActionSetHandle. I have no experience with anything other than vr::k_ulInvalidInputValueHandle
in ulRestrictedToDevice
.
Deliberately setting nPriority = 0
made it work for me at one point.
Updates are done in the Loop function here.
vr::VRInput()->UpdateActionState(&m_activeActionSet, sizeof(m_activeActionSet), 1);
Updates the action sets. This should be called every frame/tick/whatever. After this, GetDigitalActionData
can be called.
The action state for our one action is gotten here.
vr::VRInput()->GetDigitalActionData(
m_nextSongHandler,
&m_nextSongData,
sizeof(m_nextSongData),
vr::k_ulInvalidInputValueHandle);
This gets the action data of type digital (bool) into the m_nextSongData
struct. I initially tried to call the GetAnalogActionData
function, and forgot to change the sizeof to match the Digital struct when I changed the function call. Make sure you use the correct parameters. No errors will be shown if you mess this up.
See the current documentation for full documentation.
In my experience, bActive
is enabled whenever your set is active and a controller is enabled. bState
is enabled when the button is pressed down. I have not experimented with holding down a button and losing power, so I don't know what happens.
https://github.com/ValveSoftware/openvr/wiki/SteamVR-Input
https://github.com/ValveSoftware/openvr/wiki/Action-manifest
https://github.com/ValveSoftware/openvr/wiki/API-Documentation
The C:\Program Files (x86)\Steam\logs\vrserver.txt
file.
Output from the application run in debug mode. Seems to be mostly the same as vrserver.txt
.
If you have inconsistent issues try rebooting your computer. The same binary/manifests can be completely broken one minute and then work perfectly after a reboot.
If you have any questions just create an issue. Do not PM me. PMs are not indexed by search engines and will not help other people with the same problem.
If you have any suggestions for changing the repo feel free to create a PR. Typo/wording/grammar PRs are welcome.