Forum Discussion
jherico
11 years agoAdventurer
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.
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.
Known Bugs
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.
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:
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:
Anything not explicitly mentioned here is basically untested.
Roadmap:
Here's an example of what using the API looks like:
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:
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.
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.
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
renderingdistortion (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
- bluenoteExplorerThat's great news -- I highly appreciate your efforts on this!
Since I'm developing exclusively under Linux I probably have to wait until a 0.3.1 Linux version is released, right? (or does using CMake mean that you already have created a cross-platform version of the SDK?!)Eventually I'm hoping to reproduce the LWJGL mechanism of pushing platform specific jars with embedded shared libraries up to my Maven repository so that others can make use of these bindings with only the effort of adding a dependency (and possibly a repository) to their Maven project files.
This is exactly what I was hoping we someday get from Oculus VR themselves. Would make cross-platform access to the Rift so easy... - jhericoAdventurer
"bluenote" wrote:
Since I'm developing exclusively under Linux I probably have to wait until a 0.3.1 Linux version is released, right? (or does using CMake mean that you already have created a cross-platform version of the SDK?!)
No, using CMake doesn't resolve the platform issues. Right now there are a number of places in the codebase that are windows only, although I haven't made an effort to see how hard it would be to supplant them with cross-platform code. Right now I'm mostly trying to get the Java bindings to work on one platform at a time. - electHonored GuestThanks jherico :)
- jhericoAdventurerI'm still working on this btw. I'm having issues getting the rendering mechanisms to work owing to the platform specific requirements of the API. I'm attempting to produce an updated version of both the C interface and the Java code with a full example of doing the new SDK side distortion, but debugging C++ crashes through Java is a bit slow going.
- jhericoAdventurerMaven support for accessing the SDK through Java now released. Original post updated.
- ShadowLinkExplorerThat's awesome. Will take a look at it when I get home.
- jhericoAdventurerPer the thread here, I'm updating the Java bindings with a new build of the platform binaries. The current version of the SDK sends the wrong parameters into the draw call, which in turn appears to cause problems on AMD cards, and possibly others.
DoZo1971 tracked down the issue and I've updated my Github version of the SDK to include a fix for the issue (although slightly different than the fix he applied). Consequently I've rebuilt the binaries and updated the jocular Jar to 2.0.1 to include the fixes. - TheNeuroPodHonored GuestHello jherico,
Thanks a lot for your work. I am trying to convert a Unity project to Java using your jOcular library, and I am learning OpenGL while doing so. I can run your RiftDemo app within Eclipse without issues, and am trying to add a single textured Quad. And I hit a brick wall ... I have already spent more than 10 hours on this simple thing, which is not that simple with OpenGL apparently :-)
Could you publish the source of the most recent version of your RiftApp.Java? I think it would help me to follow the initialization of the GL view and connect this with the other examples and tutorials I got that load and display textures.
My current issue is that while the JPG image loads without error (I am using the loader from the Slick library), I have either:
* a white quad, when using the line:
program = new Program(new BasicResource("shaders/Colored.vs"), new BasicResource("shaders/Colored.fs));
(from RiftDemo.jabva)
* a black quad if I use instead:
program = new Program(ExampleResource.SHADERS_TEXTURED_VS, ExampleResource.SHADERS_TEXTURED_FS);
I am guessing I am not initializing this shader correctly.
In RenderScene I have:
glEnable(GL_DEPTH_TEST);
glClearColor(0.2f, 0.2f, 0.2f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
// enable alpha blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
MatrixStack mv = MatrixStack.MODELVIEW;
mv.push();
{
mv.scale(OvrLibrary.OVR_DEFAULT_IPD);
program.use();
MatrixStack.bindAll(program);
glEnable(GL_TEXTURE_2D);
Color.white.bind();
texture.bind();
glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex3f(blx,bly,z);
glTexCoord2f(1,0);
glVertex3f(blx+width,bly,z);
glTexCoord2f(1,1);
glVertex3f(blx+width,bly+heigth,z);
glTexCoord2f(0,1);
glVertex3f(blx,bly+heigth,z);
glEnd();
Program.clear();
}
mv.pop();
}
If you can publish your recent RiftApp, it could help me (I found the old RiftApp, but it is not compatible anymore with the recent jOcular).
Thanks by advance for any advice that could help me ! - jhericoAdventurerI moved the RiftApp base class out of Jocular and into Jocular-Examples, but apparently I only added it to the dev branch,, not the master branch. I've updated the Jocular-examples repository so that there is a RiftApp.java in both places now.
However, this is not your problem. The problem you're having is that you can't simply combine the use of shaders with immediate mode vertex specification.
When you call this lineprogram.use();
You're telling OpenGL to use a shader that has specific inputs and output. However, when you callglTexCoord2f(0,0);
You haven't told OpenGL what shader intput that goes to. The fact that you see a quad at all is because by convention I use shader input '0' for the position variable, which almost always corresponds to the data sent through from glVertex*. However, there's nothing that OpenGL know that your glTexCoord* calls should send their data to a specific input in the vertex shader.
You have two choices for fixing this. First, you could stick with immediate mode. This means that you need to remove the call to program.use() and MatrixStack.bindAll(program);. You still need to update GL matrix stacks with projection and modelview information, but you can do it like this:
public static void bindAllGl() {
FloatBuffer fb = BufferUtils.createFloatBuffer(16);
MatrixStack.PROJECTION.top().fillFloatBuffer(fb, true);
fb.rewind();
glMatrixMode(GL_PROJECTION);
glLoadMatrix(fb);
fb.rewind();
MatrixStack.MODELVIEW.top().fillFloatBuffer(fb, true);
fb.rewind();
glMatrixMode(GL_MODELVIEW);
glLoadMatrix(fb);
}
Preferably, you can switch away from using immediate mode (which is deprecated) to using vertex buffers. you can create a texture quad buffer like this:
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);
indices.add((short) 3);
IndexedGeometry.Builder builder = new IndexedGeometry.Builder(indices, vertices);
builder.withAttribute(Attribute.POSITION).withAttribute(Attribute.TEX);
return builder.build();
}
(Note this actually creates two triangles, not a single quad, since quads are also deprecated)
At which point you'd replace your glBegin/glEnd code with code similar to the RiftDemo code:
program.use();
MatrixStack.bindAll(program);
geometry.bindVertexArray();
geometry.draw();
VertexArray.unbind();
Program.clear();
The difference being you'd create the geometry with the above function, rather than with the makeColorCube function I use in my demo.
Quick Links
- Horizon Developer Support
- Quest User Forums
- Troubleshooting Forum for problems with a game or app
- Quest Support for problems with your device
Other Meta Support
Related Content
- 1 year ago