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

    Participant
    May 13, 2014
    Thanks a lot for your detailed explanation jherico, it is very much appreciated and exactly what I needed to progress. OpenGL is really an odd library with this not-OO-at-all state-machine-with-many-variables-to-set approach, but it is nice to feel the hardware so close, so close ... :-)
    Participant
    May 17, 2014
    I have been able to advance a little bit. I think there was a typo in makeTextureQuad(), I modified it slightly and now it display a quad, instead of two overlapping triangles.

    I modfied the part listing the triangles vertices:


    public static IndexedGeometry makeTexturedQuad() {
    Vector2f min = new Vector2f(-0.5f, -0.5f);
    Vector2f max = new Vector2f(0.5f, 0.5f);
    Vector2f texMin = new Vector2f(0, 0);
    Vector2f texMax = new Vector2f(1, 1);
    List<Vector4f> vertices = new ArrayList<>();
    vertices.add(new Vector4f(min.x, min.y, 0, 0));
    vertices.add(new Vector4f(texMin.x, texMin.y, 0, 0));
    vertices.add(new Vector4f(max.x, min.y, 0, 0));
    vertices.add(new Vector4f(texMax.x, texMin.y, 0, 0));
    vertices.add(new Vector4f(max.x, max.y, 0, 0));
    vertices.add(new Vector4f(texMax.x, texMax.y, 0, 0));
    vertices.add(new Vector4f(min.x, max.y, 0, 0));
    vertices.add(new Vector4f(texMin.x, texMax.y, 0, 0));
    List<Short> indices = new ArrayList<>();
    indices.add((short) 0);
    indices.add((short) 1);
    indices.add((short) 2);
    indices.add((short) 3);
    indices.add((short) 2);
    indices.add((short) 1);// should be 0 I think
    indices.add((short) 3);// useless?
    IndexedGeometry.Builder builder = new IndexedGeometry.Builder(indices, vertices);
    builder.withAttribute(Attribute.POSITION).withAttribute(Attribute.TEX);
    return builder.build();
    }


    by


    public static IndexedGeometry makeTexturedQuad() {
    Vector2f min = new Vector2f(-0.5f, -0.5f);
    Vector2f max = new Vector2f(0.5f, 0.5f);
    Vector2f texMin = new Vector2f(0, 0);
    Vector2f texMax = new Vector2f(1, 1);
    List<Vector4f> vertices = new ArrayList<>();
    vertices.add(new Vector4f(min.x, min.y, 0, 0));
    vertices.add(new Vector4f(texMin.x, texMin.y, 0, 0));
    vertices.add(new Vector4f(max.x, min.y, 0, 0));
    vertices.add(new Vector4f(texMax.x, texMin.y, 0, 0));
    vertices.add(new Vector4f(max.x, max.y, 0, 0));
    vertices.add(new Vector4f(texMax.x, texMax.y, 0, 0));
    vertices.add(new Vector4f(min.x, max.y, 0, 0));
    vertices.add(new Vector4f(texMin.x, texMax.y, 0, 0));
    List<Short> indices = new ArrayList<>();
    indices.add((short) 0);
    indices.add((short) 1);
    indices.add((short) 2);
    indices.add((short) 3);
    indices.add((short) 2);
    indices.add((short) 0);
    //indices.add((short) 3);
    IndexedGeometry.Builder builder = new IndexedGeometry.Builder(indices, vertices);
    builder.withAttribute(Attribute.POSITION).withAttribute(Attribute.TEX);
    return builder.build();
    }


    Hopefully my change is correct? I now see a quad that I can move, scale and rotate, but there are still no texture appearing on it.

    When I use in the initGl() method the following:


    ...
    program = new Program(ExampleResource.SHADERS_COLORED_VS, ExampleResource.SHADERS_COLORED_FS);
    program.link();
    geometry = OpenGL.makeColorCube();


    And use this inside the {} in renderScene():


    mv.scale(OvrLibrary.OVR_DEFAULT_IPD);
    mv.rotate(90, new Vector3f(0, 1, 0));
    mv.translate(new Vector3f(-0.5f, 0, 0));
    program.use();
    MatrixStack.bindAll(program);
    geometry.bindVertexArray();
    geometry.draw();
    VertexArray.unbind();
    Program.clear();


    I then have the colored cube in front of me when entering the Rift, rotated around its vertical axis.

    But if replace these lines by:


    ...
    program = new Program(ExampleResource.SHADERS_TEXTURED_VS, ExampleResource.SHADERS_TEXTURED_FS);
    program.link();
    geometry = makeTexturedQuad();


    in initGL() and this in renderScene:


    mv.scale(OvrLibrary.OVR_DEFAULT_IPD);
    mv.rotate(90, new Vector3f(0, 1, 0));
    mv.translate(new Vector3f(-0.5f, 0, 0));
    texture.bind();
    program.use();
    MatrixStack.bindAll(program);
    geometry.bindVertexArray();
    geometry.draw();
    VertexArray.unbind();
    Program.clear();


    Then I get this:


    (and it strobes constantly).

    The texture itself is loaded by :


    public void init() {
    try {
    texture =Texture.loadImage(new File("D:\\Documents\\Photos pour emporter sur le Ipod\\1005563547_12273ba4d5_o.jpg").toURI().toURL());
    } catch (IOException e) {
    e.printStackTrace();
    }
    }


    Do you have any idea on what I did wrong?
    jhericoAuthor
    Inspiring
    May 18, 2014
    OK, a couple of issues. First, my Texture vertex shader is expecting vec4 values, so my vertices were wrong, because they were passing 0 as the final element of the vector. Also, the indices may have been messed up. By convention I usually order my quads thusly: 'lower left', 'lower right', 'upper right' and 'upper left'. If you want to make a quad, the easiest way is with a triangle strip, which only requires 4 indices in this order: 0, 1, 3, 2.

    So the fixed 'makeTexturedQuad' function is


    public static IndexedGeometry makeTexturedQuad() {
    Vector2f min = new Vector2f(-0.5f, -0.5f);
    Vector2f max = new Vector2f(0.5f, 0.5f);
    Vector2f texMin = new Vector2f(0, 0);
    Vector2f texMax = new Vector2f(1, 1);
    List<Vector4f> vertices = new ArrayList<>();
    vertices.add(new Vector4f(min.x, min.y, 0, 1)); // LAST ELEMENT MUST BE 1, not 0
    vertices.add(new Vector4f(texMin.x, texMin.y, 0, 0));
    vertices.add(new Vector4f(max.x, min.y, 0, 1));
    vertices.add(new Vector4f(texMax.x, texMin.y, 0, 0));
    vertices.add(new Vector4f(max.x, max.y, 0, 1));
    vertices.add(new Vector4f(texMax.x, texMax.y, 0, 0));
    vertices.add(new Vector4f(min.x, max.y, 0, 1));
    vertices.add(new Vector4f(texMin.x, texMax.y, 0, 0));
    List<Short> indices = new ArrayList<>();
    indices.add((short) 0); // LL
    indices.add((short) 1); // LR
    indices.add((short) 3); // UL
    indices.add((short) 2); // UR
    IndexedGeometry.Builder builder = new IndexedGeometry.Builder(indices, vertices);
    builder.withDrawType(GL_TRIANGLE_STRIP).withAttribute(Attribute.POSITION).withAttribute(Attribute.TEX);
    return builder.build();
    }


    The other issue you may be encountering is that you haven't set the MIN/MAG filters on the texture. Unless I set a min filter, I get a blank screen.

    You can do so like this:


    texture.bind();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    texture.unbind();


    You only have to do this once, right after you load the image file into the texture.

    My full working code can be found here now produces this image:

    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Participant
    May 18, 2014
    And it now works perfectly indeed 8-) Many thanks jherico, for your library and your kind help.
    jhericoAuthor
    Inspiring
    May 21, 2014
    Shamelessly bumping this topic because of the new maven release.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Up and Coming
    May 22, 2014
    Hey jherico!

    I'm working with rickard on jMonkeyEngine Oculus support here:

    http://hub.jmonkeyengine.org/forum/topic/oculus-rift-support/page/14/#post-285627

    We have lots of stuff working already, thanks to rickard and his DK1. I have a DK2 coming in August & hope to have jMonkeyEngine ready for it when it arrives. I'm wondering if we can combine some efforts here. I think the 2 biggest things missing in what we have implemented for jMonkeyEngine now is positional tracking & SDK-side distortion (0.3.1 goodies). I'm also thinking it will be easier to just add those features to what we have already developed, instead of replacing what we have with joculus (since what we already have is designed and integrated for jMonkeyEngine specifically).

    Anyway, any advice will be greatly appreciated. Thank you for your contributions!

    - Phr00t

    PS: Shameless plug for my 4089 game, which is going to be an Oculus-supported title with jMonkeyEngine:

    https://www.facebook.com/phr00ts.software
    jhericoAuthor
    Inspiring
    May 22, 2014
    "phr00t" wrote:
    I'm also thinking it will be easier to just add those features to what we have already developed, instead of replacing what we have with joculus (since what we already have is designed and integrated for jMonkeyEngine specifically).


    I'm not sure this is the case. Jocular is really a very thin wrapper around the C API. If you want to use SDK-side distortion you're actually not going to do much better than such a thin wrapper.

    The only place where there's any duplicated effort is that Jocular currently contains some method to take the auto-generated Java types that were created from the OVR_CAPI headers and convert them into more fully featured Java types. For instance, the Jocular class to contain the ovrQuatf type contains 4 floats, but none of the methods that you'd expect on a quaternion class, so I've added functions to convert it to my org.saintandreas.math.Quaternion class, which does have fully functionality. The whole org.saintandreas.math package is lifted out of JMonkeyEngine, becuase it struck me as the most fully featured and easy to use Java math library when I was looking for one.

    Regardless, you could easily pull in Jocular and ignore the math conversion functions. I'm considering removing them anyway, since it might be more prudent to put that kind of functionality into a static utility class in places where the Jocular library is used and there may already be an extant set of vector and matrix classes, as in the case of JMonkeyEngine.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    jhericoAuthor
    Inspiring
    May 25, 2014
    Bumping for new release
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    Up and Coming
    May 26, 2014
    Hey jherico -- trying to integrate jocular into jMonkeyEngine.

    When I add joculuar-2.0.3.jar & jocular-platform-0.3.1.3.jar, I get this error:

    SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
    java.lang.UnsatisfiedLinkError: Unable to load library 'OVR_C': Native library (linux-x86-64/libOVR_C.so) not found in resource path ([file:/home/phr00t/jme3src_rw/engine/dist/lib/jME3-core.jar, file:/home/phr00t/jme3src_rw/engine/build/classes/, file:/home/phr00t/.jmonkeyplatform/3.0/libs/jME3-niftygui.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/nifty.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/nifty-default-controls.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/eventbus.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/xmlpull-xpp3.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/jME3-desktop.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/jME3-lwjgl.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/jME3-lwjgl-natives.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/jinput.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/lwjgl.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/jME3-jogg.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/j-ogg-oggd.jar, file:/home/phr00t/.jmonkeyplatform/3.0/libs/j-ogg-vorbisd.jar, file:/home/phr00t/jmonkeyengine-oculus-rift/jna-4.1.0.jar, file:/home/phr00t/jmonkeyengine-oculus-rift/jocular-2.0.3.jar, file:/home/phr00t/jmonkeyengine-oculus-rift/jocular-platform-0.3.1.3.jar, file:/home/phr00t/jmonkeyengine-oculus-rift/build/classes/, file:/home/phr00t/jmonkeyengine-oculus-rift/assets/])
    at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:271)
    at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:398)
    at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:337)
    at com.oculusvr.capi.OvrLibrary.<clinit>(OvrLibrary.java:27)
    at oculusvr.input.OculusRift.initialize(OculusRift.java:17)


    Looks like it isn't grabbing the library easily...?

    If I put the resources/ folder in my project folder, and add it to the project, I get this error:

    SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
    java.lang.UnsatisfiedLinkError: /home/phr00t/jmonkeyengine-oculus-rift/resources/linux-x86-64/libOVR_C.so: wrong ELF class: ELFCLASS32
    at com.sun.jna.Native.open(Native Method)
    at com.sun.jna.Native.open(Native.java:1759)
    at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:260)
    at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:398)
    at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:337)
    at com.oculusvr.capi.OvrLibrary.<clinit>(OvrLibrary.java:27)
    at oculusvr.input.OculusRift.initialize(OculusRift.java:17)


    I'm running Linux 64... maybe your library isn't properly built as a 64-bit one, but 32-bit instead?
    jhericoAuthor
    Inspiring
    May 26, 2014
    "phr00t" wrote:
    Hey jherico -- trying to integrate jocular into jMonkeyEngine.

    When I add joculuar-2.0.3.jar & jocular-platform-0.3.1.3.jar, I get this error:


    I would suggest switching to the jovr module. I renamed it so I could match everything to the SDK version number, hence jocular-2.0.x becomes jovr-0.3.2.x. Currently the version is 0.3.2.0, but see below...

    "phr00t" wrote:
    SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
    java.lang.UnsatisfiedLinkError: /home/phr00t/jmonkeyengine-oculus-rift/resources/linux-x86-64/libOVR_C.so: wrong ELF class: ELFCLASS32

    I'm running Linux 64... maybe your library isn't properly built as a 64-bit one, but 32-bit instead?


    Yeah, it looks like the host I was using the build the SO was a 32 bit host. I'm rebuilding it now on a 64 bit host. The new version will be jovr-0.3.2.1. It should be up within about 20 minutes.

    "phr00t" wrote:
    SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
    java.lang.UnsatisfiedLinkError: Unable to load library 'OVR_C': Native library (linux-x86-64/libOVR_C.so) not found in resource path ...

    Looks like it isn't grabbing the library easily...?


    Not sure what that's about. JNA should automatically look inside the jar for the appropriate library. It's possible that it's another artifact of the fact that it's a 32 bit file and that the other error is getting masked somehow.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action