Thursday, June 26, 2014

Unity 4: Using the player eye height from the user's profile

I had been planning on writing a post on setting the default character eye height in Unity a while ago but I got side tracked and then Oculus put out the preview version of the 0.3 SDK and Unity Integration package. The first preview version didn’t work for the Mac so, although I had read there were some significant changes, I wasn’t able to test them out. Now that it is available on the Mac and I’ve had some time to play with it, I wanted to come back to the character default height question.

The big change  for character height from version 0.2 to 0.3 is that the player eye height set in the Oculus Profile tool is now used by the OVRCamraController script by default to set the character eye height.

Previously if you wanted to use the player's height as set in the user profile, you needed to go in to the Inspector for the OVRCameraController script (attached to the OVRCameraController prefab) and check the box for Use Player Eye Hght. As of version 0.3, this box is checked by default.



Assuming that the player has created a user profile, this makes it easy to have a character of the player’s actual height and, even if a profile isn’t found, the SDK provides values for a default profile. But as we looked at in another post, Working around the SDK default profile setting for gender,  there are issues with those default values, as they are for what Oculus terms the "common user" (a.k.a average adult male) and not for the average human.

If the profile is currently set to the default, the solutions we looked at there included documentation, changing the SDK values, and giving the user an option to set a profile. Like working directly with the SDK, when developing with Unity, emphasizing in the documentation that a profile needs to be set is still a big part of the solution (and a good idea anyway). And, like working directly with the SDK, changing the default values is still not a good solution.

But unlike working directly with the SDK, when working with Unity, checking for the profile used is problematic. When using the SDK directly, there is a call to get the name of the profile being used.

ovrHmd_GetString(hmd, OVR_KEY_USER, "")

Unfortunately, the Unity integration package does not appear to to include a similar call for getting the user profile name. The OVRDevice script provides the following functions to get profile data:

GetPlayerEyeHeight(ref float eyeHeight);
GetIPD(ref float IPD);
IsMagCalibrated();
IsYawCorrectionEnabled();


As you can see, there isn't a call to get the name of the profile in use. So, what to do? One suggestion I saw on the Oculus forums, was to at least warn the user that the headset has has not been calibrated. You can do that by checking if magnetic calibration and yaw correction are false using OVRDevice.IsMagCalibrated() and OVRDevice.IsYawCorrectionEnabled().

While this is a good start, it would be nice if there was an easy way to allow users to see which profile is in use and to allow them to switch between available profiles.

Sunday, June 8, 2014

Creating JOVR

When the Rift DK1 came out, the supplied software didn't support Linux, or Java or anything other than Windows by way of C++.  While other platform support for Linux and OSX were eventually added, and in the 0.3.x versions of the SDK, support for straight C was added as well, if you were a Java developer and wanted to work with the Rift you were out of luck.

Early on I set about digging into the SDK's inner workings to see how easily it could be ported to Java.  Eventually I succeeded in replicating both the head tracker reading code and some of the sensor fusion code in a Java project I called Jocular.  It was basically functional, but it didn't have all the same features as the C++ SDK.  In particular I never spent a lot of effort on magnetic yaw correction or prediction.  Largely I lost interest in it because I didn't want to make myself responsible for constantly porting stuff in order to maintain feature parity with the official codebase.

When the 0.3.x came out it included a simplified C API that provided not only access to the sensors but also included an implementation of the distortion rendering inside the SDK itself.  This was something of a boon to developers, because the implementation of distortion is non-trivial.  It was also a boon to Java developers, because tooling for creating Java bindings to C functionality is pretty functional.  That's not to say that binding the the C++ code would have been impossible, but the C API is much simpler, requiring fewer hurdles to jump to get the same results.  So I bumped the Jocular version from 1 to 2, and set about producing just such a binding.

JNA vs JNI

Binding from Java to C can be done in a couple of ways, typically either through JNI (Java Native Interface) or through JNA (Java Native Access).

JNI functionality requires that you write C code specifically designed for Java to call.  Within this C code you can then call other native C libraries, or C++ code for that matter.  This typically produces the fastest implementation, but it's a bit of overhead I wanted to avoid.

JNA on the other hand can be called with pure Java.  The magic of loading native libraries, locating the appropriate functions within them and conversion of parameters is all baked inside the JNA library, which internally uses JNI.  So really, there's only JNI for accessing C code, but the JNA library makes it easy to do in a non-library specific way.

JNA tends to be slower than JNI, but the difference tends to reflect how much information you're passing through parameters.  The Rift API is simple and doesn't require much information to be passed over the API.  The head tracking data and timing information is trivial in almost any context, while the OpenGL subsystem actually holds the bulk of the information that is used for distortion.  So despite the ostensible speed difference, the ease of use of JNA wins out here.

Building the binding

Actually creating the Java classes to map to the Oculus SDK C API was the next task, and not one I relished.  While the functions and structures I wanted to map weren't complex, there are a couple dozen of them, and going through them would have been tedious.  Fortunately, there exist tools to do this for me.  In particular I found a tool called JNAerator, which would accept as input a C header file and produce as output Java classes.  This was suitable for producing a good first pass implementation and was essentially what I used for the first release of Jocular 2.

JNAerator is kind of a finicky tool.  There's both a command line interface as well as a GUI of sorts, but neither is exactly what you'd think of as polished.  It's possible there are better tools out there, but I started working with this one and it seemed to be suitable for doing the bulk of the work I needed done, producing output classes for all the required structures, constants for all the required enums and static methods on a library wrapper for all the functions.

The mapping was imperfect.  For instance, the C API header declares the structures with underscores in their names and uses typedefs to produce non-underscored names for use.  The generated code didn't recognize the typedefs as important, so the generated types include the underscores.

All of the types also include ovr in the structure name, since C has no concept of namespacing, so all types are always in the global namespace.  Java has strong namespacing, so the naming verbosity is unnecessary.

So for instance, the OVR C API has a raw type name ovrSensorState_.  The Java name should be SensorState.  Fortunately, JNA doesn't care about the type names, just the signatures, so it was a simple matter of going through the API and using Eclipse to refactor the names.

The next problem was the access pattern for using the ovrHmd handle provided by the SDK.  This handle is essentially a pointer to a class in the C API implementation (which is written in C++), and most of the functions in the C API take the handle as their first parameter.  This pattern indicates that the best mapping for the type was to create a class that wrapped the SDK functions internally.  Fortunately the generator for the Java code was smart enough to recognize this pattern and create members on the Hmd type that wrapped calls to the static members in the library.  However, some of the calls were ripe for some improvement.

Some of the functions needed to return complex data but also indicate error state.  In these cases the C API provides an function parameter which is used for output, and the function return value itself is a flag, where a non-zero value indicates success.  In Java, it's preferable to use the exception handling in the language to indicate error conditions.  So I modified the wrappers for these functions to no longer require the user to pass in output parameters, and to raise an exception in the case of failure.

Consider the case of the rendering config function.  In the C API it's declared like this

ovrBool ovrHmd_ConfigureRendering(
  ovrHmd hmd,
  const ovrRenderAPIConfig* apiConfig,
  unsigned int distortionCaps,
  const ovrFovPort[2] eyeFovIn,
  ovrEyeRenderDesc[2] eyeRenderDescOut)
The generated code produces this equivalent (after my refactoring of the naming conventions):
byte ovrHmd_ConfigureRendering(  Hmd hmd,   RenderAPIConfig apiConfig,   int distortionCaps,   FovPort eyeFovIn[],   EyeRenderDesc eyeRenderDescOut[]);
This is made much more use friendly in the Hmd wrapper class like so:
public EyeRenderDesc[] configureRendering(    RenderAPIConfig apiConfig,     int distortionCaps,     FovPort eyeFovIn[]) {  EyeRenderDesc eyeRenderDescs[] =     (EyeRenderDesc[]) new EyeRenderDesc().toArray(2);  if (0 == OvrLibrary.INSTANCE.ovrHmd_ConfigureRendering(      this, apiConfig, distortionCaps,       eyeFovIn, eyeRenderDescs)) {    throw new IllegalStateException(      "Unable to configure rendering");  }  configuredRendering = true;  return eyeRenderDescs;}
The end user no longer needs to allocate the EyeRenderDesc array and pass it in.  The wrapper function handles this and simply returns the results, or throws an exception in the case of failure.

Finally, in order to make the API more completely accessible through the Hmd class alone, I created static methods in the Hmd type to wrap the corresponding methods in the OvrLibrary interface.  

PAX Dev 2014 panel

We'll be giving a presentation at PAX Dev 2014 on topics culled from chapters 7 and 8 of our book, titled Pitfalls & Perils of VR Development: How to Avoid Them.

Wednesday, June 4, 2014

Working around the SDK default profile setting for gender

Here’s how the Oculus SDK default profile handles gender:

#define OVR_DEFAULT_GENDER                   "Male"

According to Oculus, the purpose of this value is for avatar selection. This makes the logic behind the “male” assumption just plain odd. If a user doesn’t specify their gender, they are “male” but if they create a profile they can specify that their gender is “unspecified” by specifying it “unspecified.” It would be much better if the data matched reality. If a user’s gender is actually unspecified it should be set to “unspecified.” Users can choose to leave it that way, or they can choose to specify male or female. I really don’t like having to wonder if data I’m using is real data or not.

But more than bad data, what those default values say to me is that Oculus believes that by default men should have a good experience with the Rift, but for the same level of experience, women should be required to create a profile. This assumption sets up an artificial barrier of entry for women in VR. Not cool.

When I asked Oculus Support about this assumption, I was told that these values are for the "common user" and  "if you are worried about your users having a non-optimal experience, then you should make sure they understand they need to configure the device before usage.”

While I agree that it is absolutely correct that for the best experience users need to set up a profile, it does not change the sexist implication of the current SDK default gender value nor does it change the problem of responding to false data. But, just because the SDK default is sexist, it doesn't mean my software has to be. So, what can I do to work around the SDK default profile setting for gender? Not sure yet what the best solution is, but some ideas are:

Stress the need to create a profile in the product documentation.  This is a  good start, but relying on users to read and follow the documentation, something we all know many users will not do, isn't a complete solution.

Make sure the user has at least been offered the chance to create a personal profile. It is certainly possible to prompt the user with something like “This is the profile being used, do you want to use it or select/create another?” This approach will get people who didn't read and follow the  documentation to create a profile, something we want to do anyway,  but it also means sticking the assumption that the user is male into the face of every women who uses the Rift. I don’t want to be that rude.

Tinker with the SDK and change the defaults. This is an option. But, this approach creates a maintenance issue that will need to be addressed for each update of the SDK.