Skip to main content
Inspiring
April 23, 2014

JOVR 0.3.2.5: Java/Maven bindings for 0.3.2 [Updated 7/22]

Update 7/22:
Updated to 0.3.2.5 (apparently I made a 0.3.2.4 release that I didn't announce. Must not have been very important)

Having had some conversations with the Oculus devs and done some writing on the topic I've come to understand the Timewarp functionality a little better. More importantly I understand the reasoning behind the timing logic in the SDK used to perform the buffer swapping. However, the SDK requirement of native handles is still somewhat overbearing in my opinion.

To rectify the situation and allow the SDK to perform the appropriate timing actions I've altered the SDK slightly to add support for providing a callback function for performing the buffer swap.


typedef void (*ovrSwapBufferCallback)(void * userData);

OVR_EXPORT void ovrHmd_SetSwapBuffersCallback(ovrHmd hmd,
ovrSwapBufferCallback callback,
void * userData);


Note that the ovrDistortionCap_NoSwapBuffers flag still exists, but it should not be used in combination with this function. If you enable ovrDistortionCap_NoSwapBuffers then the SDK will not call your buffer swapping callback.




Update 6/3:
Updated to 0.3.2.3

There are some breaking changes in this API due to some cleanup and some moved classes. Sorry for the inconvenience.


  • Added Guava dependency

  • Added some argument checking with more informative errors than a crash inside the binary would provide

  • Added JSR305 dependency

  • Updated some Pointer types to String

  • Moved OvrLibrary.ovrHmd type to Hmd

  • Removed the need to expose the .ByValue types to clients

  • Added some static methods to Hmd to remove the need to call OvrLibrary.INSTANCE directly

  • Changed some functions which had output arguments to return the output instead: i.e. 'void foo(String[] results)' becomes 'String [] foo()'

  • Defaulted all API values to OpenGL.

  • Added some unit tests


Known Bugs

  • If you call Hmd.startSensor() within 0.07 seconds of calling OvrLibrary.ovrHmd_Initialize(), the reported position can be incorrect. This is a bug in the Oculus SDK itself, and will be fixed in my next build. You can work around the bug by putting a tenth of a second delay in after calling the ovrHmd_Initialize() function.


Update 5/30:
Updated to 0.3.2.2

Modified the OculusSDK to include a new value for ovrDistortionCaps: ovrDistortionCap_NoSwapBuffers

If this value isn't specified in ovrHmd_ConfigureRendering then the Oculus SDK will revert to it's default behavior of performing the SwapBuffers() call inside the SDK. However, if you include this value when configuring rendering, then the SDK will continue to exhibit the modified behavior I've introduced, and NOT perform the SwapBuffers() call in the SDK.

Most Java applications will want to include this new distortion cap value, since the parameters required for buffer swapping are not normally exposed to Java client applications.

Update 5/26:
Updated to 0.3.2.1. The Linux binary was a 32 bit shared object, instead of 64 bit.

Update 5/24:
I've built a new release of the Maven jars to connect with the Oculus SDK version 0.3.2. Like my previous releases, I've disabled the buffer swapping in the SDK, which means you don't need to pass any window information to the SDK in the configureRendering call.

I've also renamed the Artifact so that I can reset the versioning and match the Java jar version to the Oculus SDK version. Adding a dependency on the Java bindings should now look like this.


<dependency>
<groupId>org.saintandreas</groupId>
<artifactId>jovr</artifactId>
<version>[0.3.2.0, 0.3.3)</version>
</dependency>


The Jar no longer depends on another jar for the binaries. Rather, the binary shared library files are baked into the main jar. The only dependency of this jar is the JNA library, which I have bumped up to 4.1.

Since the SDK now supports Linux natively, I'm using their Linux code. I've also added the OSX binary as well. Both Linux and OSX are 64-bit only. Windows still has 32 and 64 bit jars built in.

Update 5/21:
I've migrated my releases to the Maven central repository. You can now use the Oculus SDK simply by adding the following into your maven project file:


<dependency>
<groupId>org.saintandreas</groupId>
<artifactId>jocular</artifactId>
<version>[2.0.2, 3)</version>
</dependency>


My example project has been updated to reflect the latest version of the code and the central repository hosting.

The Java example project can be found independently in it's own repository here: https://github.com/jherico/jocular-examples

The main branch of the examples project consists of only three files. The Maven project file, a RiftApp base class for creating simple applications for the Rift, and a single RiftDemo class which actually draws a simple scene with a skybox and number of cubes surrounding the user.

Update 5/4:

As I mentioned in this thread, I've refactored my Github copy of the Oculus SDK to extract the C API out of the main body of code and made an individual project out of it, which produces a standalone DLL. I've now created a JNA binding for that DLL that will let you use the C API from Java.

The Java project for the bindings can be found here: https://github.com/jherico/jocular <--- new URL

The URL has changed from the old location in my OculusSDK repository, because I forgot how annoying it is to try to release a Maven project from within a larger repository.

Right now, in order to use it, you'll need to use CMake and whatever tool you desire to build the DLL and place it in your system path. Having done so, you can then use the Java OvrLibrary class to access all the C API functions.

This is now usable directly from Maven without the need to build or download the Oculus SDK. The Jocular artifact has a dependency on a 'jocular-platform' artifact which contains binaries for Windows (32 and 64 bit) and Linux (64 bit only).

What works:

  • SDK startup and shutdown

  • Fetching the HMD description

  • Head tracking

  • SDK side rendering distortion (with the caveat that you still have to do your own buffer swapping)

  • Conversion from the struct style matrix, vector and quaternion types in the OVR interface to my own Java types with full functionality (based on the JMonkeyEngine math types)


Anything not explicitly mentioned here is basically untested.

Roadmap:

  • Done. Get SDK distortion rendering functional (without swapbuffers)

  • Done. Create an example Java application

  • Done. Create a build mechanism that creates a jar with the platform binary built

  • Done. Deployment to my maven repository

  • Done. Helper methods to convert the OVR math types to Java equivalents with full functionality

  • Done. Porting to Linux

  • Porting to OSX


Here's an example of what using the API looks like:


OvrLibrary.INSTANCE.ovr_Initialize();
ovrHmd hmd = ovrHmd.create(0);
hmd.startSensor(0, 0);
...
hmd.stopSensor();
hmd.destroy();
OvrLibrary.INSTANCE.ovr_Shutdown();


You can also use my helper class 'RiftApp' which automatically detects the SDK and does all the setup and distortion work. All you have to do is subclass it and implement a renderScene() method like this one:


@Override
public void renderScene() {
glEnable(GL_DEPTH_TEST);
glClearColor(0.2f, 0.2f, 0.2f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

MatrixStack mv = MatrixStack.MODELVIEW;
mv.push();
{
mv.scale(OvrLibrary.OVR_DEFAULT_IPD);
program.use();
MatrixStack.bindAll(program);
geometry.bindVertexArray();
geometry.draw();
VertexArray.unbind();
Program.clear();
}
mv.pop();
}


You can see the full example code here.

Many props to the developers on the JNA and JNAerator projects that made doing this a breeze.

    46 replies

    jhericoAuthor
    Inspiring
    June 8, 2014
    "bluenote" wrote:
    Great job Brad! Compared to the situation before JOVR, this really is a difference as night and day. In combination with your book, this really makes it so much more feasible to port JVM based stuff to the Rift.


    Thanks. I'm working on the Python bindings now.

    "bluenote" wrote:

    The example code currently relies on some stuff from the "org.saintandreas.gl" package, which probably is not available on github.


    Everything I do is available on github. The GL functionality wrappers are here.

    "bluenote" wrote:
    the infamous quaternion-to-rotation matrix conversion (which involves handedness convention), and the question whether getPerspectiveProjection(.).M returns an array in column or row major format, in case a user wants to work with some other matrix library.


    The math library is here, although the conversion from the Oculus types to the math types is simply done as a straight copy of the floating point values. Adapting it to work with any other math library should be as simple as writing these functions:



    public static Vector3f toVector3f(OvrVector3f v) {
    return new Vector3f(v.x, v.y, v.z);
    }

    public static Quaternion toQuaternion(OvrQuaternionf q) {
    return new Quaternion(q.x, q.y, q.z, q.w);
    }

    public static Matrix4f toMatrix4f(Posef p) {
    return new Matrix4f().rotate(toQuaternion(p.Orientation)).mult(new Matrix4f().translate(toVector3f(p.Position)));
    }

    public static Matrix4f toMatrix4f(OvrMatrix4f m) {
    return new org.saintandreas.math.Matrix4f(m.M).transpose();
    }


    I didn't know that waiting for a vsync requires platform specific handles, so for the time being deactivating it completely might be the best solution. Unfortunately, the possibilities to force a vsync with lwjgl are also limited, since it requires running in fullscreen and depending on the display configuration / platform you can only run lwjgl windowed with a position matching the Rift display.


    Waiting for vsync in LWJGL can be done by setting

    Display.setVSyncEnabled(true);


    I'm pretty sure it works regardless of whether you're full screen or not.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Up and Coming
    June 19, 2014
    "jherico" wrote:

    Waiting for vsync in LWJGL can be done by setting

    Display.setVSyncEnabled(true);


    I'm pretty sure it works regardless of whether you're full screen or not.


    You are right, it does work (in contrast to what the lwjgl documentations says)! And in theory I would also say that it should not matter whether buffer swapping is performed by lwjgl or by the SDK. But in practice it feels like a huge difference to me. The perceived latency has improved significantly in native applications that rely on SDK distortion rendering (benefiting from time warp and a proper estimation of the prediction time delta). On the other hand, using lwjgl there seems to be no benefit at all (it even reminds me of running a prediction delta of zero with my SDK 0.2.5 based implementation). Naively I would argue that not letting the SDK do the buffer swapping messes with the prediction of the "next frame time point". But currently this is only a wild guess, maybe it's just my test setup, my flawed perception, or a Linux issue. Would be interesting to hear about other experiences regarding the perceived latency using JOVR.
    jhericoAuthor
    Inspiring
    June 20, 2014
    "bluenote" wrote:
    On the other hand, using lwjgl there seems to be no benefit at all (it even reminds me of running a prediction delta of zero with my SDK 0.2.5 based implementation). Naively I would argue that not letting the SDK do the buffer swapping messes with the prediction of the "next frame time point". But currently this is only a wild guess, maybe it's just my test setup, my flawed perception, or a Linux issue. Would be interesting to hear about other experiences regarding the perceived latency using JOVR.


    All I know is that my experience with the 0.3.x SDK shows that it uses a horrifying busy-wait mechanism for it's 'wait until time' method and that if I enable the SDK side buffer swapping and allow the wait mechanism it actually reduces my frame-rate because occasionally the 'wait' misses the V-Sync and you lose an entire frame update.

    On a Windows machine, it should be possible to compare apples-to-apples by doing a little work and making the SDK side buffer swapping toggle-able in real-time. Then you could compare directly. However, I'd suggest you do it as a double-blind trial to ensure you're not letting expectations cloud your judgement.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Up and Coming
    June 20, 2014
    "jherico" wrote:
    On a Windows machine, it should be possible to compare apples-to-apples by doing a little work and making the SDK side buffer swapping toggle-able in real-time. Then you could compare directly. However, I'd suggest you do it as a double-blind trial to ensure you're not letting expectations cloud your judgement.


    I'm already looking forward to getting DK2 just for the fact that it comes with a latency tester. I was never really satisfied with the reliability/precision of my personal latency judgment :).
    New Participant
    June 20, 2014
    Hi jherico,

    I'm so excited about this project. I've been waiting for a light java wrapper for both the head-tracking and the shading. But I seem to be having some difficulty with the demo, which for me would be the natural starting point.

    Is it the intention that we be able to check out jocular-examples and run them? I've tried this in both Eclipse Kepler with the m2e Maven support and NetBeans.

    In Eclipse, I get

    Description	Resource	Path	Location	Type
    ArtifactDescriptorException: Failed to read artifact descriptor for org.saintandreas:xres:jar:1.0.2-SNAPSHOT: UnresolvableModelException: Could not find artifact org.saintandreas:parent:pom:1.0.0-SNAPSHOT in sonatype-nexus-snapshots (https://oss.sonatype.org/content/repositories/snapshots) pom.xml /jocular-examples line 1 Maven Dependency Problem


    Oddly enough, it downloads xres-1.0.4-SNAPSHOT.jar .

    In NetBeans, when I try to build I get.

    Failed to execute goal on project jocular-examples: Could not resolve dependencies for project org.saintandreas:jocular-examples:jar:1.0-SNAPSHOT: Failed to collect dependencies for [org.saintandreas:glamour-lwjgl:jar:[1.0.6,2) (compile), org.saintandreas:math:jar:[1.0.4,2) (compile), org.saintandreas:oria-resources:jar:[1.0.2,2) (compile), org.saintandreas:jovr:jar:0.3.2.4 (compile)]: Failed to read artifact descriptor for org.saintandreas:xres:jar:1.0.2-SNAPSHOT: Failure to find org.saintandreas:parent:pom:1.0.0-SNAPSHOT in https://oss.sonatype.org/content/repositories/snapshots was cached in the local repository, resolution will not be reattempted until the update interval of sonatype-nexus-snapshots has elapsed or updates are forced -> [Help 1]


    Is the dependency chain currently correct?

    Apologies if I have overlooked some basic thing. It's a possibility that I do not discount.

    Cheers,

    Rich
    jhericoAuthor
    Inspiring
    June 20, 2014
    "rharris115" wrote:
    Is the dependency chain currently correct?

    Apologies if I have overlooked some basic thing. It's a possibility that I do not discount.


    No, you're not the only one who's had this problem. It has something to do with dependency resolution pulling in snapshot versions from the OSS Sonatype repository.

    I've just tried deleting the SNAPSHOT versions from oss.sonatype.org, so they shouldn't be picked up anymore, but you'll need to delete your local copies (probably the easiest way is to wipe out your ~/.m2/repository/org/saintandreas directory)

    Give that a shot and let me know if you're still having issues.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    New Participant
    June 21, 2014
    Thanks jherico,

    The Maven stuff works a charm now. I just had to do a few more things to get the OSX natives to load. After unpacking libOVR_C.dylib to the natives directory, I added the VM arguments.

    -Djava.library.path=/Users/rich/git/jocular-examples/target/natives
    -Djna.library.path=/Users/rich/git/jocular-examples/target/natives


    I appreciate that this may not be the right way to get it running.

    Unfortunately my more capable gaming laptop is away for repairs and my Macbook Air only supports OpenGL 2.1 and I had to comment out the draw colour cube calls, but I consider this a start!

    Cheers,

    Rich

    BTW, I'm more familiar with jogl (but not very). Do you consider lwjgl to be a big improvement?
    jhericoAuthor
    Inspiring
    June 25, 2014
    "rharris115" wrote:
    BTW, I'm more familiar with jogl (but not very). Do you consider lwjgl to be a big improvement?


    I don't have much of an opinion on the LWJGL vs JOGL front. If I recall, JOGL required less work getting the shared libraries to load, as it seemed to happen automatically. However, I prefer the LWJGL mechanisms because they allow you to call GL functions statically, rather than requiring that you pass around the GL context object that JOGL requires you to do.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Up and Coming
    July 5, 2014
    Just a quick question whether the following is a known issue or something caused on my side. For debugging reasons I tried to replace "Hmd.create(0)" by "Hmd.createDebug(ovrHmd_DK1)". My codes works when I use the former (i.e. when I really use a physical HMD), but switching to createDebug causes a SIGSEGV fatal error in OVR::CAPI::HMDState::StartSensor. If I understand it correctly, the idea behind createDebug is to allow testing without a Rift, so it would be strange if I'd have to adjust my code. Is this a general issue of SDK 0.3.2 or maybe JOVR specific?
    New Participant
    July 17, 2014
    Hi,

    I've finally been able to get back to this. Everything works fine, except for one observation. The "redux" demo seems to be creating an incredible number of FloatBuffers and Matrix4fs in each loop and I have very noticeable pauses due to garbage collection. Since this is a pretty bad thing in VR, have you guys been able to play with the garbage collector VM arguments to mittigate this?

    Cheers,

    Rich