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
    July 17, 2014
    "rharris115" wrote:
    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.


    It's less of an issue of garbage collection as optimizing the way we do draws and uniform bindings.

    The triply nested for loop that's used to draw the most of the scene geometry is a really really bad way of drawing lots of cubes. Each cube draw involves binding the program, binding the VAO, setting a bunch of uniform values and executing a draw call that renders a single cube, then unwinding all of that and doing it again all for the purpose of setting everything back to the same values except for the position of the cube. It's woefully inefficient.

    In our C++ code we eventually moved to a mechanism that used instanced geometry drawing, so that we'd set up the positions of all the cubes only once, and then stuff those locations in a uniform buffer and execute a draw call that renders them all at once.

    None of this is really all that relevant to a potential user of the jovr library. If someone already has rendering code that isn't needlessly choking the garbage collector, the additional work you'd do to add Rift support to it shouldn't be enough to trigger it. i.e. what you're seeing is basically bad use of OpenGL in drawing a scene, which is orthogonal to proper use of the Rift from Java.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Up and Coming
    July 17, 2014
    As jherico explained, it is probably just the drawing code. However, picking the right garbage collector strategy is still crucial imho, since some strategies may cause short freezes (even though they are supposed to run in separate threads). I was experimenting a bit and got the best results with "-XX:+UseConcMarkSweepGC". If I remember correctly the documentation also described this mode as a low pause GC recommended for real time processing.
    New Participant
    July 20, 2014
    Thanks guys.

    I've been using this time to give myself a crash course in OpenGL 3.2+ using LWJGL. I had no idea the changes from OpenGL 1.1+ were so profound.

    Anyway, I've taken the Redux demo and stripped away the glamour and san andreas math code in favour of the not as nice and mutable LWJGL math classes. I only did this as a learning exercise, but it works well with no noticeable garbage collection pauses. In retrospect, I really appreciate the challenge it is to code in a modern "effective java" style without creating tonnes of immutable instances that have to be garbage collected given even a modest set of matrix operations.

    At any rate, I feel I can get started on my real project now (that I've been waiting a year to do with some frustration). Thanks for the bindings and good luck with the book!

    Cheers,

    Rich
    jhericoAuthor
    Inspiring
    July 23, 2014
    Bumping for new version
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Up and Coming
    August 24, 2014
    Sorry for bringing up this old topic. I'm still hesitant with moving on to 0.4 since I don't want to lose Linux compatibility at the moment. But I got curious to try my projects with DK2, so I set up a makeshift development environment under Windows. With all native lib / classpath related issues out of the way, I'm now faced with something really strange: All my test projects work fine under Linux, but they all crash at the same point under Windows. In particular, when calling Hmd.configureRendering I get


    java.lang.Error: Invalid memory access
    at com.sun.jna.Native.invokeInt(Native Method)
    at com.sun.jna.Function.invoke(Function.java:374)
    at com.sun.jna.Function.invoke(Function.java:315)
    at com.sun.jna.Library$Handler.invoke(Library.java:212)
    at com.sun.proxy.$Proxy0.ovrHmd_ConfigureRendering(Unknown Source)
    at com.oculusvr.capi.Hmd.configureRendering(Hmd.java:127)
    at ...


    I was investigating this line 127 for a while, but so far, I have no idea why this works on Linux but not on Windows. A Google search on "jna invalid memory access" also wasn't of much help. What I still don't understand: Does this error mean that there is something wrong with the parameters passed to the function (the distortionCaps would be of type Int, so maybe the pointer to distortionCaps is wrong, but why?) or is the meaning of "Invalid memory access" merely "something went wrong in the native code"? Any ideas to narrow down the issue are highly appreciated.

    My system is a Windows 7 64 bit and I tried JOVR versions 0.3.2.3 and 0.3.2.5.
    Up and Coming
    August 27, 2014
    I was able to solve my problem. In case anyone else will have this issue: I could solve the problem by a different call to "Display.create(...)". So far I was only specifying a PixelFormat and I didn't care about specifying ContextAttribs. So by default my Windows driver gave my an OpenGL 4.3 context, which does not seem to work with the SDK. I now use a version 3.3 core context (following @jherico's example code) and suddenly configureRendering works fine. If I remember correctly, @jherico has mentioned all this before -- I was just not aware of the whole context selection question, since I always got a working context by chance...