"Hello World!" for Oculus Rift Development Kit 2 (DK2)
Update: This blog post discusses building a simple C++ app for the Oculus v0.5.0 beta SDK. An updated example which shows how to build a simple C++ console app for the production Oculus Rift CV1/DK2 v1.3.2 SDK can be found here.
__
This post walks through the basics of building a simple C++ app for the Oculus Rift Development Kit 2 (DK2). Setup of the development environment is covered first, with the coding of a simple app which reads the device's sensors and outputs their state following after.
We're working with the Oculus Rift 0.5.0 SDK for this example and a DK2 device. Everything posted in here is subject to change as the Oculus API matures (that's no fault of Oculus -- we were all told we're getting a work-in-progress developer kit, and that's what's been delivered), so please do let me know if these notes no longer reflect reality for a developer new to the device.
Oculus DK2 Dev Environment Setup
If you've not already installed the Oculus Rift SDK, you'll want to do so now. It is also assumed for Oculus development that the DirectX SDK and Windows SDK are already installed on your computer. We won't need those SDKs for the initial 'Hello World!' completed here but will definitely need them later, so best to fetch those before starting to code.
The DirectX SDK can be retrieved from here:
http://www.microsoft.com/en-us/download/details.aspx?id=6812
And the Windows 8.x SDK (which works fine for Windows 7) from here:
https://msdn.microsoft.com/en-us/windows/desktop/bg162891.aspx
It turns out that the DirectX SDK installer can conflict with the Microsoft Visual C++ 2010 Redistributable, resulting in a cryptic "Error Code: S1023" installation failure. If that happens to you (as it did to me), this StackOverflow post will sort you out.
Microsoft Visual Studio (VS2013) is used as the development environment for the examples shown here. To test that environment after all SDKs are installed (and give us a VS2013 project into which we'll put our example code) we'll start out with a traditional 'Hello World!' program.
Using the VS2013 File > New > Project... wizard to create a new console app (being sure to turn off the "Precompiled header" checkbox option), we'll pop into the editor:
#include <iostream>
int main()
{ std::cout << "Hello World!";
return 0;
}
and then use the VS2013 Build > Build Solution menu option to create an executable.
With "Hello World!" output to the console when running that exe, we've confirmed our development environment is good to go. Onwards to the DK2!
The Oculus developer documentation is a bit vague on the libraries needed for the basics, and the code examples included with the SDK cover a good amount of DirectX and other extra bits we're not needing just yet. Checking the Oculus developer forums, the latest news on libraries tells us that the only ones needed for the linker are:
libovr.lib (libovrd.lib in debug)
Winmm.lib
ws2_32.lib
We're just working with the Oculus SDK for now, so the libovr.lib and libovrd.lib alone will be sufficient.
Continuing with our simple 'Hello World!' project environment, we'll add in the libovr.lib and libovrd.lib libraries through the VS2013 project's properties (right-click the project for the Properties dialog selection) under Linker > Input > Additional Dependencies, adding in the file and path for those two .lib files. On my own system, those were under:
e:\OculusSDK\LibOVR\Lib\Windows\Win32\Debug\VS2013\LibOVR.lib
e:\OculusSDK\LibOVR\Lib\Windows\Win32\Release\VS2013\LibOVR.lib
Your own path, of course, will depend upon the installation location of your Oculus SDK.
Next, we'll include the Oculus SDK.
In VS2013 again, under the project's properties, Configuration Properties > C/C++ > Additional Include Directories options, we'll set the path to the SDK's LibOVR Include, which could be found on my own system at:
e:\OculusSDK\LibOVR\Include
Our development environment is now ready to create our basic Oculus program.
Starting Oculus DK2 Coding
To get a feel for how an Oculus program works, I opened up the Tiny Room sample code included with the SDK and...
It's been a while since I last worked in C++ and that sample code felt more unfamiliar than I was comfortable with. Before going any further, it was back to school for me.
The full MIT Open Courseware introduction to C++ class is available online, and stepping away from things to read the full lecture notes and coding up the assignment basics provided just the refresher I needed. If it's been a while since you've coded in C++, I'd recommend completing that course yourself to scrape the rust off your knowledge.
With that done, back to Oculus.
The Oculus Developers Guide provides an example initialization of the device through this snippet:
// Include the OculusVR SDK
#include <OVR_CAPI_0_5_0.h>
void Application()
{
if (ovr_Initialize(NULL))
{
ovrHmd hmd = ovrHmd_Create(0);
if (hmd)
{
// Get more details about the HMD.
ovrSizei resolution = hmd->Resolution;
...
// Do something with the HMD.
...
ovrHmd_Destroy(hmd);
}
ovr_Shutdown();
}
}
Let's put that into working code.
Using the above example for reference, we'll confirm we can connect to the DK2 by initializing the device and fetching its product name and firmware version. That's accomplished with this bit of code:
#include <iostream>
#include <OVR_CAPI_0_5_0.h>
using namespace std;
int main()
{ if (ovr_Initialize(NULL))
{ ovrHmd hmd = ovrHmd_Create(0);
if (hmd)
{ // Output some attributes of the HMD.
cout << "Connected to HMD with name " << hmd->ProductName
<< " and firmware version " << hmd->FirmwareMajor << "."
<< hmd->FirmwareMinor << endl;
ovrHmd_Destroy(hmd);
}
ovr_Shutdown();
}
return 0;
}
With the DK2 turned on (look for the orange light on the headset), running the exe compiled from the above gives us the device's attributes output to console:
Connected to HMD with name Oculus Rift DK2 and firmware version 2.12
So far so good. Now we'll try reading some sensor data.
Referencing the Oculus Developer Guide again, under the Head Tracking and Sensors section we can see how to start up the DK2's sensor and read in the tracking state. Putting all that into code with a loop to push out the results gives us:
#include <iostream>
#include <iomanip>
#include <OVR_CAPI_0_5_0.h>
#define COLW setw(15)
using namespace std;
int main()
{ if (ovr_Initialize(NULL))
{ ovrHmd hmd = ovrHmd_Create(0);
if (hmd)
{ // Start the sensor which provides the Rift's pose and motion.
ovrHmd_ConfigureTracking(hmd, ovrTrackingCap_Orientation |
ovrTrackingCap_MagYawCorrection |
ovrTrackingCap_Position, 0);
ovrTrackingState ts;
ovrSensorData sensorData;
ovrVector3f gyroData;
ovrVector3f magData;
while (true)
{ // Query the HMD for the current tracking state.
ts = ovrHmd_GetTrackingState(hmd, ovr_GetTimeInSeconds());
// Fetch the sensor data.
sensorData = ts.RawSensorData;
// Retrieve the gyroscope and magnetometer numbers.
gyroData = sensorData.Gyro;
magData = sensorData.Magnetometer;
// And output the lot to console.
cout << "Gyro (x,y,z): " << COLW << gyroData.x << "," << COLW
<< gyroData.y << "," << COLW << gyroData.z
<< " Mag (x,y,z): " << COLW << magData.x << "," << COLW
<< magData.y << "," << COLW << magData.z << endl;
}
ovrHmd_Destroy(hmd);
}
ovr_Shutdown();
}
return 0;
}
With the output to console being:
Gyro (x,y,z): -0.000476604, 0.00169012, -0.00504548 Mag (x,y,z):
-0.347324, -1.14652, 0.548826
Gyro (x,y,z): 0.000524145, -0.000410698, -0.0029435 Mag (x,y,z):
-0.347324, -1.14652, 0.548826
Gyro (x,y,z): -0.000476467, -0.0026112, 0.00245846 Mag (x,y,z):
-0.347324, -1.14652, 0.548826
Gyro (x,y,z): 0.00162392, -0.00471226, 0.00245943 Mag (x,y,z):
-0.349417, -1.13972, 0.558212
Gyro (x,y,z): -0.00477799, -0.000411771, 0.000259385 Mag (x,y,z):
-0.349417, -1.13972, 0.558212
Gyro (x,y,z): -0.00157782, 0.00278784, 0.000260718 Mag (x,y,z):
-0.339885, -1.14628, 0.573855
Gyro (x,y,z): -0.000477026, 0.0027865, 0.000261538 Mag (x,y,z):
-0.339885, -1.14628, 0.573855
Gyro (x,y,z): 0.00052226, 0.00168425, -0.00393636 Mag (x,y,z):
-0.339885, -1.14628, 0.573855
Gyro (x,y,z): 0.000522759, -0.00361625, -0.00183387 Mag (x,y,z):
-0.339885, -1.14628, 0.573855
Gyro (x,y,z): -0.000478059, 0.00168509, -0.00293224 Mag (x,y,z):
-0.340583, -1.14769, 0.551232
Gyro (x,y,z): -0.000479875, 0.00598387, 0.000268184 Mag (x,y,z):
-0.340583, -1.14769, 0.551232
Gyro (x,y,z): 0.00591569, 0.00278188, 0.000269853 Mag (x,y,z):
-0.341512, -1.15074, 0.550511
Gyro (x,y,z): 0.00161441, 0.000581309, -0.00392838 Mag (x,y,z):
-0.341512, -1.15074, 0.550511
Gyro (x,y,z): -0.000486488, -0.000418246, 0.000274017 Mag (x,y,z):
-0.341512, -1.15074, 0.550511
Gyro (x,y,z): 0.000512119, 0.00168321, -0.00292529 Mag (x,y,z):
-0.348022, -1.14464, 0.555083
Success! The DK2's sensors are being read and updating to reflect the device's orientation. 'Hello World!' is accomplished.
Now, to get my head around the Tiny World sample code...