Introduction
If you want to read the position (x,y,z) and rotation (qw, qx, qy, qz) of HTC Vive headset or controllers, then this article is for you. In this article, we create a background Windows application that grabs the positional data and write them into a text file and on the console at the same time using powershell. The final project is available here at github.
Step-by-step Explanation
In order to get raw positional (and rotational) data out of HTC Vive system, do this:
1) Get this project (which is provided by Valve Software): https://github.com/ValveSoftware/openvr
2) Open samples/samples_vs2010.sln solution file by visual studio. Then, run it to make sure everyting works. You should see a window like the following appears and you can turn your head around and see the cubes. If you turn on the controllers, you should also be able to see them.
Note: If you get the error "Unable to init VR runtime: Shared IPC Connect Failed After Multiple Attempts (308)", you should run your visual studio as administrator (by right-clicking on its icon and choosing 'Run as administrator).
3) Make the application as a background application. To do this, in "hellovr_opengl_main.cpp" file, find vr::VR_Init()
call and change this:
m_pHMD = vr::VR_Init( &eError, vr::VRApplication_Scene );
to this:
m_pHMD = vr::VR_Init(&eError, vr::VRApplication_Background);
This has two effects:
- It makes the application to run in the background so the window will not be displayed.
- It does not start SteamVR anymore. So for running your background application, you should make sure SteamVR is already running.
4) Take out all the stuff related to openGL and scene creation.
In the CMainApplication::BInit()
method, comment the following (I'm displaying it as commented):
...
...
In the CMainApplication::RunMainLoop()
method, comment the following (shown as commented):
...
...
In the CMainApplication::ProcessVREvent(...)
method, comment the following (shown as commented):
5) Write code in order to grab and print out positional data. To do this, find the method CMainApplication::HandleInput()
, and add printPositionalData()
method call (not created yet) as follows:
bool CMainApplication::HandleInput()
{
...
vr::VREvent_t event;
while( m_pHMD->PollNextEvent( &event, sizeof( event ) ) )
{
ProcessVREvent( event );
}
printPositionalData();
for( vr::TrackedDeviceIndex_t unDevice = 0; unDevice < vr::k_unMaxTrackedDeviceCount; unDevice++ )
{
...
}
Now, we need to create this funtion:
void CMainApplication::printPositionalData()
{
for (vr::TrackedDeviceIndex_t unDevice = 0; unDevice < vr::k_unMaxTrackedDeviceCount; unDevice++)
{
if (!m_pHMD->IsTrackedDeviceConnected(unDevice))
continue;
vr::VRControllerState_t state;
if (m_pHMD->GetControllerState(unDevice, &state, sizeof(state)))
{
vr::TrackedDevicePose_t trackedDevicePose;
vr::TrackedDevicePose_t trackedControllerPose;
vr::VRControllerState_t controllerState;
vr::HmdMatrix34_t poseMatrix;
vr::HmdVector3_t position;
vr::HmdQuaternion_t quaternion;
vr::ETrackedDeviceClass trackedDeviceClass = vr::VRSystem()->GetTrackedDeviceClass(unDevice);
switch (trackedDeviceClass) {
case vr::ETrackedDeviceClass::TrackedDeviceClass_HMD:
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0, &trackedDevicePose, 1);
poseMatrix = trackedDevicePose.mDeviceToAbsoluteTracking;
position = GetPosition(trackedDevicePose.mDeviceToAbsoluteTracking);
quaternion = GetRotation(trackedDevicePose.mDeviceToAbsoluteTracking);
printDevicePositionalData("HMD", poseMatrix, position, quaternion);
break;
case vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker:
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0, &trackedDevicePose, 1);
break;
case vr::ETrackedDeviceClass::TrackedDeviceClass_Controller:
vr::VRSystem()->GetControllerStateWithPose(vr::TrackingUniverseStanding, unDevice, &controllerState,
sizeof(controllerState), &trackedControllerPose);
poseMatrix = trackedControllerPose.mDeviceToAbsoluteTracking;
position = GetPosition(trackedControllerPose.mDeviceToAbsoluteTracking);
quaternion = GetRotation(trackedControllerPose.mDeviceToAbsoluteTracking);
auto trackedControllerRole = vr::VRSystem()->GetControllerRoleForTrackedDeviceIndex(unDevice);
std::string whichHand = "";
if (trackedControllerRole == vr::TrackedControllerRole_LeftHand)
{
whichHand = "LeftHand";
}
else if (trackedControllerRole == vr::TrackedControllerRole_RightHand)
{
whichHand = "RightHand";
}
switch (trackedControllerRole)
{
case vr::TrackedControllerRole_Invalid:
break;
case vr::TrackedControllerRole_LeftHand:
case vr::TrackedControllerRole_RightHand:
printDevicePositionalData(whichHand.c_str(), poseMatrix, position, quaternion);
break;
}
break;
}
}
}
}
This method uses three other methods: GetRotation()
, GetPosition()
and printDevicePositionalData(...)
, we create the first two methods as follows (from Omnifinity github repo):
vr::HmdQuaternion_t CMainApplication::GetRotation(vr::HmdMatrix34_t matrix) {
vr::HmdQuaternion_t q;
q.w = sqrt(fmax(0, 1 + matrix.m[0][0] + matrix.m[1][1] + matrix.m[2][2])) / 2;
q.x = sqrt(fmax(0, 1 + matrix.m[0][0] - matrix.m[1][1] - matrix.m[2][2])) / 2;
q.y = sqrt(fmax(0, 1 - matrix.m[0][0] + matrix.m[1][1] - matrix.m[2][2])) / 2;
q.z = sqrt(fmax(0, 1 - matrix.m[0][0] - matrix.m[1][1] + matrix.m[2][2])) / 2;
q.x = copysign(q.x, matrix.m[2][1] - matrix.m[1][2]);
q.y = copysign(q.y, matrix.m[0][2] - matrix.m[2][0]);
q.z = copysign(q.z, matrix.m[1][0] - matrix.m[0][1]);
return q;
}
vr::HmdVector3_t CMainApplication::GetPosition(vr::HmdMatrix34_t matrix) {
vr::HmdVector3_t vector;
vector.v[0] = matrix.m[0][3];
vector.v[1] = matrix.m[1][3];
vector.v[2] = matrix.m[2][3];
return vector;
}
And here is the third method printDevicePositionalData()
which adds high-resolution time stamps and format data:
void CMainApplication::printDevicePositionalData(const char * deviceName, vr::HmdMatrix34_t posMatrix, vr::HmdVector3_t position, vr::HmdQuaternion_t quaternion)
{
LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc);
dprintf("\n%lld, %s, x = %.5f, y = %.5f, z = %.5f, qw = %.5f, qx = %.5f, qy = %.5f, qz = %.5f",
qpc.QuadPart, deviceName,
position.v[0], position.v[1], position.v[2],
quaternion.w, quaternion.x, quaternion.y, quaternion.z);
}
Now, make sure the SteamVR is running and it can detect your controllers and your headset. Run the application from within Visual Studio (make sure you're running Visual Studio as administrator).
At this stage, you should be able to see the output of the application in the 'Output' window of Visual Studio as follows:
4487146818625, RightHand, x = 0.41616, y = 1.26001, z = -0.22615, qw = 0.22874, qx = -0.96962, qy = 0.03770, qz = 0.07802
4487146818983, HMD, x = 0.35692, y = 1.12997, z = -0.34819, qw = 0.92676, qx = -0.02268, qy = 0.37498, qz = 0.00021
4487146819317, LeftHand, x = 0.47935, y = 1.41869, z = -0.36343, qw = 0.68717, qx = -0.04171, qy = -0.65423, qz = 0.31311
4487146819667, RightHand, x = 0.41616, y = 1.26001, z = -0.22615, qw = 0.22874, qx = -0.96962, qy = 0.03770, qz = 0.07802
4487146820151, HMD, x = 0.35692, y = 1.12997, z = -0.34819, qw = 0.92676, qx = -0.02268, qy = 0.37498, qz = 0.00017
4487146820610, LeftHand, x = 0.47935, y = 1.41869, z = -0.36343, qw = 0.68717, qx = -0.04171, qy = -0.65423, qz = 0.31311
4487146821543, RightHand, x = 0.41616, y = 1.26001, z = -0.22615, qw = 0.22874, qx = -0.96962, qy = 0.03770, qz = 0.07802
4487146822058, HMD, x = 0.35692, y = 1.12997, z = -0.34818, qw = 0.92676, qx = -0.02267, qy = 0.37498, qz = 0.00017
4487146822583, LeftHand, x = 0.47935, y = 1.41869, z = -0.36343, qw = 0.68717, qx = -0.04171, qy = -0.65423, qz = 0.31311
5) Write the outputs into a text file. To do this, run the 'PowerShell' as administrator. Then, cd into the folder containing the binary file:
cd C:\[...]\samples\bin\win32
Afterwards, enter the following command:
C:\[...]\samples\bin\win32> .\hellovr_opengl.exe | tee vroutput.txt
The output is written (at the same time) both on the console and on the file "vroutput.txt" (located in the same folder):
Text file "vroutput.txt" :