summaryrefslogtreecommitdiffstats
path: root/src/oculusvr
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-07-05 04:04:43 +0200
committerSven Gothel <[email protected]>2014-07-05 04:04:43 +0200
commitf8f0f051604721bceaee214b8e5218fd47d2eb9e (patch)
treee9c3103498984fbc5d2f8567e88009c49e8810f7 /src/oculusvr
parent2293a53ba04a8cf2881e8919f8be97c16a9af336 (diff)
Bug 1021: Make OVR access vendor agnostic: Package 'com.jogamp.opengl.util.stereo' contains all public interfaces/classes
Renamed interfaces: CustomRendererListener -> CustomGLEventListener StereoRendererListener -> StereoGLEventListener New vendor agnostic 'stuff' in com.jogamp.opengl.util.stereo: 1 - StereoDeviceFactory To create a vendor specific StereoDeviceFactory instance, which creates the StereoDevice. 2 - StereoDevice For vendor specific implementation. Can create StereoDeviceRenderer. 3 - StereoDeviceRenderer For vendor specific implementation. 4 - StereoClientRenderer Vendor agnostic client StereoGLEventListener renderer, using a StereoDeviceRenderer. Now supports multiple StereoGLEventListener, via add/remove. - MovieSBSStereo demo-able via StereoDemo01 can show SBS 3D movies.
Diffstat (limited to 'src/oculusvr')
-rw-r--r--src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java233
-rw-r--r--src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java206
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java690
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java158
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java51
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java590
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java60
7 files changed, 852 insertions, 1136 deletions
diff --git a/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java b/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java
deleted file mode 100644
index 9af3397cf..000000000
--- a/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/**
- * Copyright 2014 JogAmp Community. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of
- * conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list
- * of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are those of the
- * authors and should not be interpreted as representing official policies, either expressed
- * or implied, of JogAmp Community.
- */
-package com.jogamp.opengl.oculusvr;
-
-import javax.media.opengl.GL;
-import javax.media.opengl.GL2ES2;
-import javax.media.opengl.GLAutoDrawable;
-import javax.media.opengl.GLEventListener;
-
-import jogamp.opengl.oculusvr.OVRDistortion;
-
-import com.jogamp.oculusvr.OVR;
-import com.jogamp.oculusvr.ovrFrameTiming;
-import com.jogamp.opengl.FBObject;
-import com.jogamp.opengl.FBObject.Attachment;
-import com.jogamp.opengl.FBObject.TextureAttachment;
-import com.jogamp.opengl.FBObject.Attachment.Type;
-import com.jogamp.opengl.util.CustomRendererListener;
-import com.jogamp.opengl.util.stereo.StereoRendererListener;
-
-/**
- * OculusVR (OVR) <i>Side By Side</i> Distortion Renderer utilizing {@link OVRDistortion}
- * implementing {@link GLEventListener} for convenience.
- * <p>
- * Implementation renders an {@link StereoRendererListener} instance
- * side-by-side using two {@link FBObject}s according to {@link OVRDistortion}.
- * </p>
- */
-public class OVRSBSRendererDualFBO implements GLEventListener {
- private final OVRDistortion dist;
- private final boolean ownsDist;
- private final StereoRendererListener upstream;
- private final FBObject[] fbos;
- private final int magFilter;
- private final int minFilter;
-
- private int numSamples;
- private final TextureAttachment[] fboTexs;
-
- public OVRSBSRendererDualFBO(final OVRDistortion dist, final boolean ownsDist, final StereoRendererListener upstream,
- final int magFilter, final int minFilter, final int numSamples) {
- this.dist = dist;
- this.ownsDist = ownsDist;
- this.upstream = upstream;
- this.fbos = new FBObject[2];
- this.fbos[0] = new FBObject();
- this.fbos[1] = new FBObject();
- this.magFilter = magFilter;
- this.minFilter = minFilter;
-
- this.numSamples = numSamples;
- this.fboTexs = new TextureAttachment[2];
- }
-
- private void initFBOs(final GL gl, final int width, final int height) {
- // remove all texture attachments, since MSAA uses just color-render-buffer
- // and non-MSAA uses texture2d-buffer
- fbos[0].detachAllColorbuffer(gl);
- fbos[1].detachAllColorbuffer(gl);
-
- fbos[0].reset(gl, width, height, numSamples, false);
- fbos[1].reset(gl, width, height, numSamples, false);
- if(fbos[0].getNumSamples() != fbos[1].getNumSamples()) {
- throw new InternalError("sample size mismatch: \n\t0: "+fbos[0]+"\n\t1: "+fbos[1]);
- }
- numSamples = fbos[0].getNumSamples();
-
- if(numSamples>0) {
- fbos[0].attachColorbuffer(gl, 0, true); // MSAA requires alpha
- fbos[0].attachRenderbuffer(gl, Type.DEPTH, 24);
- final FBObject ssink0 = new FBObject();
- {
- ssink0.reset(gl, width, height);
- ssink0.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
- ssink0.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24);
- }
- fbos[0].setSamplingSink(ssink0);
- fbos[0].resetSamplingSink(gl); // validate
- fboTexs[0] = fbos[0].getSamplingSink();
-
- fbos[1].attachColorbuffer(gl, 0, true); // MSAA requires alpha
- fbos[1].attachRenderbuffer(gl, Type.DEPTH, 24);
- final FBObject ssink1 = new FBObject();
- {
- ssink1.reset(gl, width, height);
- ssink1.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
- ssink1.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24);
- }
- fbos[1].setSamplingSink(ssink1);
- fbos[1].resetSamplingSink(gl); // validate
- fboTexs[1] = fbos[1].getSamplingSink();
- } else {
- fboTexs[0] = fbos[0].attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
- fbos[0].attachRenderbuffer(gl, Type.DEPTH, 24);
- fboTexs[1] = fbos[1].attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
- fbos[1].attachRenderbuffer(gl, Type.DEPTH, 24);
- }
- fbos[0].unbind(gl);
- fbos[1].unbind(gl);
- }
-
- @SuppressWarnings("unused")
- private void resetFBOs(final GL gl, final int width, final int height) {
- fbos[0].reset(gl, width, height, numSamples, true);
- fbos[1].reset(gl, width, height, numSamples, true);
- if(fbos[0].getNumSamples() != fbos[1].getNumSamples()) {
- throw new InternalError("sample size mismatch: \n\t0: "+fbos[0]+"\n\t1: "+fbos[1]);
- }
- numSamples = fbos[0].getNumSamples();
- if(numSamples>0) {
- fboTexs[0] = fbos[0].getSamplingSink();
- fboTexs[1] = fbos[1].getSamplingSink();
- } else {
- fboTexs[0] = (TextureAttachment) fbos[0].getColorbuffer(0);
- fboTexs[1] = (TextureAttachment) fbos[1].getColorbuffer(0);
- }
- }
-
- @Override
- public void init(final GLAutoDrawable drawable) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- dist.init(gl);
-
- // We will do some offscreen rendering, setup FBO...
- if( null != upstream ) {
- final int[] textureSize = dist.textureSize;
- initFBOs(gl, textureSize[0], textureSize[1]);
- upstream.init(drawable);
- }
-
- gl.setSwapInterval(1);
- }
-
- @Override
- public void dispose(final GLAutoDrawable drawable) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- // FIXME complete release
- if( null != upstream ) {
- upstream.dispose(drawable);
- fbos[0].destroy(gl);
- fbos[1].destroy(gl);
- }
- if( ownsDist ) {
- dist.dispose(gl);
- }
- }
-
- @Override
- public void display(final GLAutoDrawable drawable) {
- final ovrFrameTiming frameTiming = OVR.ovrHmd_BeginFrameTiming(dist.hmdCtx, 0);
-
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- if(0 < numSamples) {
- gl.glEnable(GL.GL_MULTISAMPLE);
- }
-
- // FIXME: Instead of setting the viewport,
- // it's better to change the projection matrix!
- if( null != upstream ) {
- for(int eyeNum=0; eyeNum<2; eyeNum++) {
- // final ovrPosef eyeRenderPose = OVR.ovrHmd_GetEyePose(hmdCtx, eyeNum);
- // final float[] eyePos = OVRUtil.getVec3f(eyeRenderPose.getPosition());
- fbos[eyeNum].bind(gl);
-
- final OVRDistortion.EyeData eyeDist = dist.eyes[eyeNum];
- final int[] viewport = eyeDist.viewport;
- gl.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
-
- dist.updateEyePose(eyeNum);
- upstream.reshapeEye(drawable, viewport[0], viewport[1], viewport[2], viewport[3],
- dist.getEyeParam(eyeNum), dist.updateEyePose(eyeNum));
- upstream.display(drawable, eyeNum > 0 ? CustomRendererListener.DISPLAY_REPEAT : 0);
- fbos[eyeNum].unbind(gl);
- }
- gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
- }
-
- gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- gl.glClear(GL.GL_COLOR_BUFFER_BIT);
- gl.glActiveTexture(GL.GL_TEXTURE0 + dist.texUnit0.intValue());
-
- if( null != upstream ) {
- dist.displayOneEyePre(gl, frameTiming.getTimewarpPointSeconds());
- fbos[0].use(gl, fboTexs[0]);
- dist.displayOneEye(gl, 0);
- fbos[0].unuse(gl);
- fbos[1].use(gl, fboTexs[1]);
- dist.displayOneEye(gl, 1);
- fbos[1].unuse(gl);
- dist.displayOneEyePost(gl);
- } else {
- dist.display(gl, frameTiming.getTimewarpPointSeconds());
- }
-
- if( !drawable.getAutoSwapBufferMode() ) {
- drawable.swapBuffers();
- }
- OVR.ovrHmd_EndFrameTiming(dist.hmdCtx);
- }
-
- @Override
- public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
- if( !drawable.getAutoSwapBufferMode() ) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- gl.glViewport(0, 0, width, height);
- }
- }
-}
diff --git a/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java b/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java
deleted file mode 100644
index b18d1634e..000000000
--- a/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- * Copyright 2014 JogAmp Community. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of
- * conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list
- * of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are those of the
- * authors and should not be interpreted as representing official policies, either expressed
- * or implied, of JogAmp Community.
- */
-package com.jogamp.opengl.oculusvr;
-
-import javax.media.opengl.GL;
-import javax.media.opengl.GL2ES2;
-import javax.media.opengl.GLAutoDrawable;
-import javax.media.opengl.GLEventListener;
-
-import jogamp.opengl.oculusvr.OVRDistortion;
-
-import com.jogamp.oculusvr.OVR;
-import com.jogamp.oculusvr.ovrFrameTiming;
-import com.jogamp.opengl.FBObject;
-import com.jogamp.opengl.FBObject.Attachment;
-import com.jogamp.opengl.FBObject.TextureAttachment;
-import com.jogamp.opengl.FBObject.Attachment.Type;
-import com.jogamp.opengl.util.CustomRendererListener;
-import com.jogamp.opengl.util.stereo.StereoRendererListener;
-
-/**
- * OculusVR (OVR) <i>Side By Side</i> Distortion Renderer utilizing {@link OVRDistortion}
- * implementing {@link GLEventListener} for convenience.
- * <p>
- * Implementation renders an {@link StereoRendererListener} instance
- * side-by-side within one {@link FBObject} according to {@link OVRDistortion}.
- * </p>
- */
-public class OVRSBSRendererSingleFBO implements GLEventListener {
-
- private final OVRDistortion dist;
- private final boolean ownsDist;
- private final StereoRendererListener upstream;
- private final FBObject fbo;
- private final int magFilter;
- private final int minFilter;
-
- private int numSamples;
- private TextureAttachment fboTex;
-
- /**
- * @param dist {@link OVRDistortion} instance used for rendering.
- * @param ownsDist if true, {@link OVRDistortion#dispose(GL2ES2)} is issued on this instance's {@link #dispose(GLAutoDrawable)} method, otherwise not.
- * @param upstream the upstream {@link StereoRendererListener}, a.k.a the <i>content</i> to render for both eyes
- * @param magFilter if > 0 value for {@link GL#GL_TEXTURE_MAG_FILTER}
- * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER}
- * @param numSamples sample-count, if > 0 using multisampling w/ given samples, otherwise no multisampling applies
- */
- public OVRSBSRendererSingleFBO(final OVRDistortion dist, final boolean ownsDist, final StereoRendererListener upstream,
- final int magFilter, final int minFilter, final int numSamples) {
- this.dist = dist;
- this.ownsDist = ownsDist;
- this.upstream = upstream;
- this.fbo = new FBObject();
- this.magFilter = magFilter;
- this.minFilter = minFilter;
-
- this.numSamples = numSamples;
- }
-
- private void initFBOs(final GL gl, final int width, final int height) {
- // remove all texture attachments, since MSAA uses just color-render-buffer
- // and non-MSAA uses texture2d-buffer
- fbo.detachAllColorbuffer(gl);
-
- fbo.reset(gl, width, height, numSamples, false);
- numSamples = fbo.getNumSamples();
-
- if(numSamples>0) {
- fbo.attachColorbuffer(gl, 0, true); // MSAA requires alpha
- fbo.attachRenderbuffer(gl, Type.DEPTH, 24);
- final FBObject ssink = new FBObject();
- {
- ssink.reset(gl, width, height);
- ssink.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
- ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24);
- }
- fbo.setSamplingSink(ssink);
- fbo.resetSamplingSink(gl); // validate
- fboTex = fbo.getSamplingSink();
- } else {
- fboTex = fbo.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE);
- fbo.attachRenderbuffer(gl, Type.DEPTH, 24);
- }
- fbo.unbind(gl);
- }
-
- @SuppressWarnings("unused")
- private void resetFBOs(final GL gl, final int width, final int height) {
- fbo.reset(gl, width, height, numSamples, true);
- numSamples = fbo.getNumSamples();
- if(numSamples>0) {
- fboTex = fbo.getSamplingSink();
- } else {
- fboTex = (TextureAttachment) fbo.getColorbuffer(0);
- }
- }
-
- @Override
- public void init(final GLAutoDrawable drawable) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- dist.init(gl);
-
- // We will do some offscreen rendering, setup FBO...
- if( null != upstream ) {
- final int[] textureSize = dist.textureSize;
- initFBOs(gl, textureSize[0], textureSize[1]);
- upstream.init(drawable);
- }
-
- gl.setSwapInterval(1);
- }
-
- @Override
- public void dispose(final GLAutoDrawable drawable) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- // FIXME complete release
- if( null != upstream ) {
- upstream.dispose(drawable);
- fbo.destroy(gl);
- }
- if( ownsDist ) {
- dist.dispose(gl);
- }
- }
-
- @Override
- public void display(final GLAutoDrawable drawable) {
- final ovrFrameTiming frameTiming = OVR.ovrHmd_BeginFrameTiming(dist.hmdCtx, 0);
-
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- if(0 < numSamples) {
- gl.glEnable(GL.GL_MULTISAMPLE);
- }
-
- // FIXME: Instead of setting the viewport,
- // it's better to change the projection matrix!
- if( null != upstream ) {
- fbo.bind(gl);
-
- for(int eyeNum=0; eyeNum<2; eyeNum++) {
- // final ovrPosef eyeRenderPose = OVR.ovrHmd_GetEyePose(hmdCtx, eyeNum);
- // final float[] eyePos = OVRUtil.getVec3f(eyeRenderPose.getPosition());
- final OVRDistortion.EyeData eyeDist = dist.eyes[eyeNum];
- final int[] viewport = eyeDist.viewport;
- gl.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
-
- upstream.reshapeEye(drawable, viewport[0], viewport[1], viewport[2], viewport[3],
- dist.getEyeParam(eyeNum), dist.updateEyePose(eyeNum));
- upstream.display(drawable, eyeNum > 0 ? CustomRendererListener.DISPLAY_REPEAT | CustomRendererListener.DISPLAY_DONTCLEAR : 0);
- }
- fbo.unbind(gl);
- gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
- }
-
- gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- gl.glClear(GL.GL_COLOR_BUFFER_BIT);
- gl.glActiveTexture(GL.GL_TEXTURE0 + dist.texUnit0.intValue());
-
- if( null != upstream ) {
- fbo.use(gl, fboTex);
- dist.display(gl, frameTiming.getTimewarpPointSeconds());
- fbo.unuse(gl);
- } else {
- dist.display(gl, frameTiming.getTimewarpPointSeconds());
- }
-
- if( !drawable.getAutoSwapBufferMode() ) {
- drawable.swapBuffers();
- }
- OVR.ovrHmd_EndFrameTiming(dist.hmdCtx);
- }
-
- @Override
- public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
- if( !drawable.getAutoSwapBufferMode() ) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- gl.glViewport(0, 0, width, height);
- }
- }
-}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java
deleted file mode 100644
index c32270ac4..000000000
--- a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java
+++ /dev/null
@@ -1,690 +0,0 @@
-/**
- * Copyright 2014 JogAmp Community. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of
- * conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list
- * of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are those of the
- * authors and should not be interpreted as representing official policies, either expressed
- * or implied, of JogAmp Community.
- */
-package jogamp.opengl.oculusvr;
-
-import java.nio.FloatBuffer;
-import java.nio.ShortBuffer;
-
-import javax.media.opengl.GL;
-import javax.media.opengl.GL2ES2;
-import javax.media.opengl.GLArrayData;
-import javax.media.opengl.GLEventListener;
-import javax.media.opengl.GLException;
-import javax.media.opengl.GLUniformData;
-
-import jogamp.common.os.PlatformPropsImpl;
-
-import com.jogamp.common.nio.Buffers;
-import com.jogamp.common.os.Platform;
-import com.jogamp.oculusvr.OVR;
-import com.jogamp.oculusvr.OVRException;
-import com.jogamp.oculusvr.OvrHmdContext;
-import com.jogamp.oculusvr.ovrDistortionMesh;
-import com.jogamp.oculusvr.ovrDistortionVertex;
-import com.jogamp.oculusvr.ovrEyeRenderDesc;
-import com.jogamp.oculusvr.ovrFovPort;
-import com.jogamp.oculusvr.ovrMatrix4f;
-import com.jogamp.oculusvr.ovrPosef;
-import com.jogamp.oculusvr.ovrRecti;
-import com.jogamp.oculusvr.ovrSizei;
-import com.jogamp.oculusvr.ovrVector2f;
-import com.jogamp.oculusvr.ovrVector3f;
-import com.jogamp.opengl.JoglVersion;
-import com.jogamp.opengl.math.FloatUtil;
-import com.jogamp.opengl.math.Quaternion;
-import com.jogamp.opengl.math.VectorUtil;
-import com.jogamp.opengl.util.CustomRendererListener;
-import com.jogamp.opengl.util.GLArrayDataServer;
-import com.jogamp.opengl.util.glsl.ShaderCode;
-import com.jogamp.opengl.util.glsl.ShaderProgram;
-import com.jogamp.opengl.util.stereo.EyeParameter;
-import com.jogamp.opengl.util.stereo.EyePose;
-
-/**
- * OculusVR Distortion Data and OpenGL Renderer Utility
- */
-public class OVRDistortion {
- public static final float[] VEC3_UP = { 0f, 1f, 0f };
- public static final float[] VEC3_FORWARD = { 0f, 0f, -1f };
-
- private static final String shaderPrefix01 = "dist01";
- private static final String shaderTimewarpSuffix = "_timewarp";
- private static final String shaderChromaSuffix = "_chroma";
- private static final String shaderPlainSuffix = "_plain";
-
- public static boolean useTimewarp(final int distortionCaps) { return 0 != ( distortionCaps & OVR.ovrDistortionCap_TimeWarp ) ; }
- public static boolean useChromatic(final int distortionCaps) { return 0 != ( distortionCaps & OVR.ovrDistortionCap_Chromatic ) ; }
- public static boolean useVignette(final int distortionCaps) { return 0 != ( distortionCaps & OVR.ovrDistortionCap_Vignette ) ; }
-
- public static class EyeData {
- public final int eyeName;
- public final int distortionCaps;
- public final int vertexCount;
- public final int indexCount;
- public final int[/*4*/] viewport;
-
- public final GLUniformData eyeToSourceUVScale;
- public final GLUniformData eyeToSourceUVOffset;
- public final GLUniformData eyeRotationStart;
- public final GLUniformData eyeRotationEnd;
-
- /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
- public final GLArrayDataServer iVBO;
- public final GLArrayData vboPos, vboParams, vboTexCoordsR, vboTexCoordsG, vboTexCoordsB;
- public final GLArrayDataServer indices;
-
- public final ovrEyeRenderDesc ovrEyeDesc;
- public final ovrFovPort ovrEyeFov;
- public final EyeParameter eyeParameter;
-
- public ovrPosef ovrEyePose;
- public EyePose eyePose;
-
- public final boolean useTimewarp() { return OVRDistortion.useTimewarp(distortionCaps); }
- public final boolean useChromatic() { return OVRDistortion.useChromatic(distortionCaps); }
- public final boolean useVignette() { return OVRDistortion.useVignette(distortionCaps); }
-
- private EyeData(final OvrHmdContext hmdCtx, final int distortionCaps,
- final float[] eyePositionOffset, final ovrEyeRenderDesc eyeDesc,
- final ovrSizei ovrTextureSize, final int[] eyeRenderViewport) {
- this.eyeName = eyeDesc.getEye();
- this.distortionCaps = distortionCaps;
- viewport = new int[4];
- System.arraycopy(eyeRenderViewport, 0, viewport, 0, 4);
-
- final FloatBuffer fstash = Buffers.newDirectFloatBuffer(2+2+16+26);
-
- eyeToSourceUVScale = new GLUniformData("ovr_EyeToSourceUVScale", 2, Buffers.slice2Float(fstash, 0, 2));
- eyeToSourceUVOffset = new GLUniformData("ovr_EyeToSourceUVOffset", 2, Buffers.slice2Float(fstash, 2, 2));
-
- if( useTimewarp() ) {
- eyeRotationStart = new GLUniformData("ovr_EyeRotationStart", 4, 4, Buffers.slice2Float(fstash, 4, 16));
- eyeRotationEnd = new GLUniformData("ovr_EyeRotationEnd", 4, 4, Buffers.slice2Float(fstash, 20, 16));
- } else {
- eyeRotationStart = null;
- eyeRotationEnd = null;
- }
-
- this.ovrEyeDesc = eyeDesc;
- this.ovrEyeFov = eyeDesc.getFov();
-
- final ovrVector3f eyeViewAdjust = eyeDesc.getViewAdjust();
- this.eyeParameter = new EyeParameter(eyeName, eyePositionOffset, OVRUtil.getFovHV(ovrEyeFov),
- eyeViewAdjust.getX(), eyeViewAdjust.getY(), eyeViewAdjust.getZ());
-
- this.eyePose = new EyePose(eyeName);
-
- updateEyePose(hmdCtx);
-
- final ovrDistortionMesh meshData = ovrDistortionMesh.create();
- final ovrFovPort fov = eyeDesc.getFov();
-
- if( !OVR.ovrHmd_CreateDistortionMesh(hmdCtx, eyeName, fov, distortionCaps, meshData) ) {
- throw new OVRException("Failed to create meshData for eye "+eyeName+" and "+OVRUtil.toString(fov));
- }
- vertexCount = meshData.getVertexCount();
- indexCount = meshData.getIndexCount();
-
- /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
- final boolean useChromatic = useChromatic();
- final boolean useVignette = useVignette();
- final int compsPerElement = 2+2+2+( useChromatic ? 2+2 /* texCoordG + texCoordB */: 0 );
- iVBO = GLArrayDataServer.createGLSLInterleaved(compsPerElement, GL.GL_FLOAT, false, vertexCount, GL.GL_STATIC_DRAW);
- vboPos = iVBO.addGLSLSubArray("ovr_Position", 2, GL.GL_ARRAY_BUFFER);
- vboParams = iVBO.addGLSLSubArray("ovr_Params", 2, GL.GL_ARRAY_BUFFER);
- vboTexCoordsR = iVBO.addGLSLSubArray("ovr_TexCoordR", 2, GL.GL_ARRAY_BUFFER);
- if( useChromatic ) {
- vboTexCoordsG = iVBO.addGLSLSubArray("ovr_TexCoordG", 2, GL.GL_ARRAY_BUFFER);
- vboTexCoordsB = iVBO.addGLSLSubArray("ovr_TexCoordB", 2, GL.GL_ARRAY_BUFFER);
- } else {
- vboTexCoordsG = null;
- vboTexCoordsB = null;
- }
- indices = GLArrayDataServer.createData(1, GL.GL_SHORT, indexCount, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER);
-
- // Setup: eyeToSourceUVScale, eyeToSourceUVOffset
- {
- final ovrVector2f[] uvScaleOffsetOut = new ovrVector2f[2];
- uvScaleOffsetOut[0] = ovrVector2f.create(); // FIXME: remove ctor / double check
- uvScaleOffsetOut[1] = ovrVector2f.create();
-
- final ovrRecti ovrEyeRenderViewport = OVRUtil.createOVRRecti(eyeRenderViewport);
- OVR.ovrHmd_GetRenderScaleAndOffset(fov, ovrTextureSize, ovrEyeRenderViewport, uvScaleOffsetOut);
- if( OVRUtil.DEBUG ) {
- System.err.println("XXX."+eyeName+": fov "+OVRUtil.toString(fov));
- System.err.println("XXX."+eyeName+": uvScale "+OVRUtil.toString(uvScaleOffsetOut[0]));
- System.err.println("XXX."+eyeName+": uvOffset "+OVRUtil.toString(uvScaleOffsetOut[0]));
- System.err.println("XXX."+eyeName+": textureSize "+OVRUtil.toString(ovrTextureSize));
- System.err.println("XXX."+eyeName+": viewport "+OVRUtil.toString(ovrEyeRenderViewport));
- }
- final FloatBuffer eyeToSourceUVScaleFB = eyeToSourceUVScale.floatBufferValue();
- eyeToSourceUVScaleFB.put(0, uvScaleOffsetOut[0].getX());
- eyeToSourceUVScaleFB.put(1, uvScaleOffsetOut[0].getY());
- final FloatBuffer eyeToSourceUVOffsetFB = eyeToSourceUVOffset.floatBufferValue();
- eyeToSourceUVOffsetFB.put(0, uvScaleOffsetOut[1].getX());
- eyeToSourceUVOffsetFB.put(1, uvScaleOffsetOut[1].getY());
- }
-
- /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
- final FloatBuffer iVBOFB = (FloatBuffer)iVBO.getBuffer();
- final ovrDistortionVertex[] ovRes = new ovrDistortionVertex[1];
- ovRes[0] = ovrDistortionVertex.create(); // FIXME: remove ctor / double check
-
- for ( int vertNum = 0; vertNum < vertexCount; vertNum++ ) {
- final ovrDistortionVertex ov = meshData.getPVertexData(vertNum, ovRes)[0];
- ovrVector2f v;
-
- // pos
- v = ov.getPos();
- iVBOFB.put(v.getX());
- iVBOFB.put(v.getY());
-
- // params
- if( useVignette ) {
- iVBOFB.put(ov.getVignetteFactor());
- } else {
- iVBOFB.put(1.0f);
- }
- iVBOFB.put(ov.getTimeWarpFactor());
-
- // texCoordR
- v = ov.getTexR();
- iVBOFB.put(v.getX());
- iVBOFB.put(v.getY());
-
- if( useChromatic ) {
- // texCoordG
- v = ov.getTexG();
- iVBOFB.put(v.getX());
- iVBOFB.put(v.getY());
-
- // texCoordB
- v = ov.getTexB();
- iVBOFB.put(v.getX());
- iVBOFB.put(v.getY());
- }
- }
- if( OVRUtil.DEBUG ) {
- System.err.println("XXX."+eyeName+": iVBO "+iVBO);
- }
- {
- final ShortBuffer in = meshData.getPIndexData();
- final ShortBuffer out = (ShortBuffer) indices.getBuffer();
- out.put(in);
- }
- if( OVRUtil.DEBUG ) {
- System.err.println("XXX."+eyeName+": idx "+indices);
- System.err.println("XXX."+eyeName+": distEye "+this);
- }
- OVR.ovrHmd_DestroyDistortionMesh(meshData);
- }
-
- private void linkData(final GL2ES2 gl, final ShaderProgram sp) {
- if( 0 > vboPos.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+vboPos);
- }
- if( 0 > vboParams.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+vboParams);
- }
- if( 0 > vboTexCoordsR.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+vboTexCoordsR);
- }
- if( useChromatic() ) {
- if( 0 > vboTexCoordsG.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+vboTexCoordsG);
- }
- if( 0 > vboTexCoordsB.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+vboTexCoordsB);
- }
- }
- if( 0 > eyeToSourceUVScale.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+eyeToSourceUVScale);
- }
- if( 0 > eyeToSourceUVOffset.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+eyeToSourceUVOffset);
- }
- if( useTimewarp() ) {
- if( 0 > eyeRotationStart.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+eyeRotationStart);
- }
- if( 0 > eyeRotationEnd.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+eyeRotationEnd);
- }
- }
- iVBO.seal(gl, true);
- iVBO.enableBuffer(gl, false);
- indices.seal(gl, true);
- indices.enableBuffer(gl, false);
- }
-
- public void dispose(final GL2ES2 gl) {
- iVBO.destroy(gl);
- indices.destroy(gl);
- }
- public void enableVBO(final GL2ES2 gl, final boolean enable) {
- iVBO.enableBuffer(gl, enable);
- indices.bindBuffer(gl, enable); // keeps VBO binding if enable:=true
- }
-
- public void updateUniform(final GL2ES2 gl, final ShaderProgram sp) {
- gl.glUniform(eyeToSourceUVScale);
- gl.glUniform(eyeToSourceUVOffset);
- if( useTimewarp() ) {
- gl.glUniform(eyeRotationStart);
- gl.glUniform(eyeRotationEnd);
- }
- }
-
- public void updateTimewarp(final OvrHmdContext hmdCtx, final ovrPosef eyeRenderPose, final float[] mat4Tmp1, final float[] mat4Tmp2) {
- final ovrMatrix4f[] timeWarpMatrices = new ovrMatrix4f[2];
- timeWarpMatrices[0] = ovrMatrix4f.create(); // FIXME: remove ctor / double check
- timeWarpMatrices[1] = ovrMatrix4f.create();
- OVR.ovrHmd_GetEyeTimewarpMatrices(hmdCtx, eyeName, eyeRenderPose, timeWarpMatrices);
-
- final float[] eyeRotationStartM = FloatUtil.transposeMatrix(timeWarpMatrices[0].getM(0, mat4Tmp1), mat4Tmp2);
- final FloatBuffer eyeRotationStartU = eyeRotationStart.floatBufferValue();
- eyeRotationStartU.put(eyeRotationStartM);
- eyeRotationStartU.rewind();
-
- final float[] eyeRotationEndM = FloatUtil.transposeMatrix(timeWarpMatrices[1].getM(0, mat4Tmp1), mat4Tmp2);
- final FloatBuffer eyeRotationEndU = eyeRotationEnd.floatBufferValue();
- eyeRotationEndU.put(eyeRotationEndM);
- eyeRotationEndU.rewind();
- }
-
- /**
- * Updates {@link #ovrEyePose} and it's extracted
- * {@link #eyeRenderPoseOrientation} and {@link #eyeRenderPosePosition}.
- * @param hmdCtx used get the {@link #ovrEyePose} via {@link OVR#ovrHmd_GetEyePose(OvrHmdContext, int)}
- */
- public EyePose updateEyePose(final OvrHmdContext hmdCtx) {
- ovrEyePose = OVR.ovrHmd_GetEyePose(hmdCtx, eyeName);
- final ovrVector3f pos = ovrEyePose.getPosition();
- eyePose.setPosition(pos.getX(), pos.getY(), pos.getZ());
- OVRUtil.copyToQuaternion(ovrEyePose.getOrientation(), eyePose.orientation);
- return eyePose;
- }
-
- @Override
- public String toString() {
- return "Eye["+eyeName+", viewport "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]+
- ", "+eyeParameter+
- ", vertices "+vertexCount+", indices "+indexCount+
- ", uvScale["+eyeToSourceUVScale.floatBufferValue().get(0)+", "+eyeToSourceUVScale.floatBufferValue().get(1)+
- "], uvOffset["+eyeToSourceUVOffset.floatBufferValue().get(0)+", "+eyeToSourceUVOffset.floatBufferValue().get(1)+
- "], desc"+OVRUtil.toString(ovrEyeDesc)+", "+eyePose+"]";
- }
- }
-
- public final OvrHmdContext hmdCtx;
- public final EyeData[] eyes;
- public final int distortionCaps;
- public final int[/*2*/] textureSize;
- public final GLUniformData texUnit0;
- public final boolean usesDistMesh;
-
- private final float[] mat4Tmp1 = new float[16];
- private final float[] mat4Tmp2 = new float[16];
-
- private ShaderProgram sp;
-
- @Override
- public String toString() {
- return "OVRDist[caps 0x"+Integer.toHexString(distortionCaps)+", "+
- ", tex "+textureSize[0]+"x"+textureSize[1]+
- ", vignette "+useVignette()+", chromatic "+useChromatic()+", timewarp "+useTimewarp()+
- ", "+PlatformPropsImpl.NEWLINE+" "+eyes[0]+", "+PlatformPropsImpl.NEWLINE+" "+eyes[1]+"]";
- }
-
- public static OVRDistortion create(final OvrHmdContext hmdCtx, final boolean sbsSingleTexture,
- final float[] eyePositionOffset, final ovrFovPort[] eyeFov,
- final float pixelsPerDisplayPixel, final int distortionCaps) {
- final ovrEyeRenderDesc[] eyeRenderDesc = new ovrEyeRenderDesc[2];
- eyeRenderDesc[0] = OVR.ovrHmd_GetRenderDesc(hmdCtx, OVR.ovrEye_Left, eyeFov[0]);
- eyeRenderDesc[1] = OVR.ovrHmd_GetRenderDesc(hmdCtx, OVR.ovrEye_Right, eyeFov[1]);
- if( OVRUtil.DEBUG ) {
- System.err.println("XXX: eyeRenderDesc[0] "+OVRUtil.toString(eyeRenderDesc[0]));
- System.err.println("XXX: eyeRenderDesc[1] "+OVRUtil.toString(eyeRenderDesc[1]));
- }
-
- final ovrSizei recommenedTex0Size = OVR.ovrHmd_GetFovTextureSize(hmdCtx, OVR.ovrEye_Left, eyeRenderDesc[0].getFov(), pixelsPerDisplayPixel);
- final ovrSizei recommenedTex1Size = OVR.ovrHmd_GetFovTextureSize(hmdCtx, OVR.ovrEye_Right, eyeRenderDesc[1].getFov(), pixelsPerDisplayPixel);
- if( OVRUtil.DEBUG ) {
- System.err.println("XXX: recommenedTex0Size "+OVRUtil.toString(recommenedTex0Size));
- System.err.println("XXX: recommenedTex1Size "+OVRUtil.toString(recommenedTex1Size));
- }
- final int[] textureSize = new int[2];
- if( sbsSingleTexture ) {
- textureSize[0] = recommenedTex0Size.getW() + recommenedTex1Size.getW();
- } else {
- textureSize[0] = Math.max(recommenedTex0Size.getW(), recommenedTex1Size.getW());
- }
- textureSize[1] = Math.max(recommenedTex0Size.getH(), recommenedTex1Size.getH());
- if( OVRUtil.DEBUG ) {
- System.err.println("XXX: textureSize "+textureSize[0]+"x"+textureSize[1]);
- }
-
- final int[][] eyeRenderViewports = new int[2][4];
- if( sbsSingleTexture ) {
- eyeRenderViewports[0][0] = 0;
- eyeRenderViewports[0][1] = 0;
- eyeRenderViewports[0][2] = textureSize[0] / 2;
- eyeRenderViewports[0][3] = textureSize[1];
- eyeRenderViewports[1][0] = (textureSize[0] + 1) / 2;
- eyeRenderViewports[1][1] = 0;
- eyeRenderViewports[1][2] = textureSize[0] / 2;
- eyeRenderViewports[1][3] = textureSize[1];
- } else {
- eyeRenderViewports[0][0] = 0;
- eyeRenderViewports[0][1] = 0;
- eyeRenderViewports[0][2] = textureSize[0];
- eyeRenderViewports[0][3] = textureSize[1];
- eyeRenderViewports[1][0] = 0;
- eyeRenderViewports[1][1] = 0;
- eyeRenderViewports[1][2] = textureSize[0];
- eyeRenderViewports[1][3] = textureSize[1];
- }
- return new OVRDistortion(hmdCtx, sbsSingleTexture, eyePositionOffset, eyeRenderDesc, textureSize, eyeRenderViewports, distortionCaps, 0);
- }
-
- public OVRDistortion(final OvrHmdContext hmdCtx, final boolean sbsSingleTexture,
- final float[] eyePositionOffset, final ovrEyeRenderDesc[] eyeRenderDescs,
- final int[] textureSize, final int[][] eyeRenderViewports,
- final int distortionCaps, final int textureUnit) {
- this.hmdCtx = hmdCtx;
- this.eyes = new EyeData[2];
- this.distortionCaps = distortionCaps;
- this.textureSize = new int[2];
- System.arraycopy(textureSize, 0, this.textureSize, 0, 2);
-
- texUnit0 = new GLUniformData("ovr_Texture0", textureUnit);
- usesDistMesh = true;
-
- final ovrSizei ovrTextureSize = OVRUtil.createOVRSizei(textureSize);
- eyes[0] = new EyeData(hmdCtx, distortionCaps, eyePositionOffset, eyeRenderDescs[0], ovrTextureSize, eyeRenderViewports[0]);
- eyes[1] = new EyeData(hmdCtx, distortionCaps, eyePositionOffset, eyeRenderDescs[1], ovrTextureSize, eyeRenderViewports[1]);
- sp = null;
- }
-
- public final boolean useTimewarp() { return useTimewarp(distortionCaps); }
- public final boolean useChromatic() { return useChromatic(distortionCaps); }
- public final boolean useVignette() { return useVignette(distortionCaps); }
-
- public void updateTimewarp(final ovrPosef eyeRenderPose, final int eyeNum) {
- eyes[eyeNum].updateTimewarp(hmdCtx, eyeRenderPose, mat4Tmp1, mat4Tmp2);
- }
- public void updateTimewarp(final ovrPosef[] eyeRenderPoses) {
- eyes[0].updateTimewarp(hmdCtx, eyeRenderPoses[0], mat4Tmp1, mat4Tmp2);
- eyes[1].updateTimewarp(hmdCtx, eyeRenderPoses[1], mat4Tmp1, mat4Tmp2);
- }
-
- public void enableVBO(final GL2ES2 gl, final boolean enable, final int eyeNum) {
- if( null == sp ) {
- throw new IllegalStateException("Not initialized");
- }
- eyes[eyeNum].enableVBO(gl, enable);
- }
-
- public final ShaderProgram getShaderProgram() { return sp; }
-
- public void init(final GL2ES2 gl) {
- if( OVRUtil.DEBUG ) {
- System.err.println(JoglVersion.getGLInfo(gl, null).toString());
- }
- if( null != sp ) {
- throw new IllegalStateException("Already initialized");
- }
- final String vertexShaderBasename;
- final String fragmentShaderBasename;
- {
- final StringBuilder sb = new StringBuilder();
- sb.append(shaderPrefix01);
- if( !useChromatic() && !useTimewarp() ) {
- sb.append(shaderPlainSuffix);
- } else if( useChromatic() && !useTimewarp() ) {
- sb.append(shaderChromaSuffix);
- } else if( useTimewarp() ) {
- sb.append(shaderTimewarpSuffix);
- if( useChromatic() ) {
- sb.append(shaderChromaSuffix);
- }
- }
- vertexShaderBasename = sb.toString();
- sb.setLength(0);
- sb.append(shaderPrefix01);
- if( useChromatic() ) {
- sb.append(shaderChromaSuffix);
- } else {
- sb.append(shaderPlainSuffix);
- }
- fragmentShaderBasename = sb.toString();
- }
- final ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, OVRDistortion.class, "shader",
- "shader/bin", vertexShaderBasename, true);
- final ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, OVRDistortion.class, "shader",
- "shader/bin", fragmentShaderBasename, true);
- vp0.defaultShaderCustomization(gl, true, true);
- fp0.defaultShaderCustomization(gl, true, true);
-
- sp = new ShaderProgram();
- sp.add(gl, vp0, System.err);
- sp.add(gl, fp0, System.err);
- if(!sp.link(gl, System.err)) {
- throw new GLException("could not link program: "+sp);
- }
- sp.useProgram(gl, true);
- if( 0 > texUnit0.setLocation(gl, sp.program()) ) {
- throw new OVRException("Couldn't locate "+texUnit0);
- }
- eyes[0].linkData(gl, sp);
- eyes[1].linkData(gl, sp);
- sp.useProgram(gl, false);
- }
-
- public void dispose(final GL2ES2 gl) {
- sp.useProgram(gl, false);
- eyes[0].dispose(gl);
- eyes[1].dispose(gl);
- sp.destroy(gl);
- }
-
- public EyeParameter getEyeParam(final int eyeNum) {
- return eyes[eyeNum].eyeParameter;
- }
-
- /**
- * Updates the {@link EyeData#ovrEyePose} via {@link EyeData#updateEyePose(OvrHmdContext)}
- * for the denoted eye.
- */
- public EyePose updateEyePose(final int eyeNum) {
- return eyes[eyeNum].updateEyePose(hmdCtx);
- }
-
- public void updateUniforms(final GL2ES2 gl, final int eyeNum) {
- if( null == sp ) {
- throw new IllegalStateException("Not initialized");
- }
- gl.glUniform(texUnit0);
- eyes[eyeNum].updateUniform(gl, sp);
- }
-
- /**
- * <p>
- * {@link #updateEyePose(int)} must be called upfront
- * when rendering upstream {@link GLEventListener}.
- * </p>
- *
- * @param gl
- * @param timewarpPointSeconds
- */
- public void display(final GL2ES2 gl, final double timewarpPointSeconds) {
- if( null == sp ) {
- throw new IllegalStateException("Not initialized");
- }
- if( useTimewarp() ) {
- OVR.ovr_WaitTillTime(timewarpPointSeconds);
- }
- gl.glDisable(GL.GL_CULL_FACE);
- gl.glDisable(GL.GL_DEPTH_TEST);
- gl.glDisable(GL.GL_BLEND);
-
- if( !gl.isGLcore() ) {
- gl.glEnable(GL.GL_TEXTURE_2D);
- }
-
- sp.useProgram(gl, true);
-
- gl.glUniform(texUnit0);
-
- for(int eyeNum=0; eyeNum<2; eyeNum++) {
- final EyeData eye = eyes[eyeNum];
- if( useTimewarp() ) {
- eye.updateTimewarp(hmdCtx, eye.ovrEyePose, mat4Tmp1, mat4Tmp2);
- }
- eye.updateUniform(gl, sp);
- eye.enableVBO(gl, true);
- if( usesDistMesh ) {
- gl.glDrawElements(GL.GL_TRIANGLES, eye.indexCount, GL.GL_UNSIGNED_SHORT, 0);
- } else {
- gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, eye.vertexCount);
- }
- eyes[eyeNum].enableVBO(gl, false);
- }
-
- sp.useProgram(gl, false);
- }
-
- /**
- *
- * @param gl
- * @param timewarpPointSeconds
- */
- public void displayOneEyePre(final GL2ES2 gl, final double timewarpPointSeconds) {
- if( null == sp ) {
- throw new IllegalStateException("Not initialized");
- }
- if( useTimewarp() ) {
- OVR.ovr_WaitTillTime(timewarpPointSeconds);
- }
- gl.glDisable(GL.GL_CULL_FACE);
- gl.glDisable(GL.GL_DEPTH_TEST);
- gl.glDisable(GL.GL_BLEND);
-
- if( !gl.isGLcore() ) {
- gl.glEnable(GL.GL_TEXTURE_2D);
- }
-
- sp.useProgram(gl, true);
-
- gl.glUniform(texUnit0);
- }
-
- /**
- * <p>
- * {@link #updateEyePose(int)} must be called upfront
- * when rendering upstream {@link GLEventListener}.
- * </p>
- *
- * @param gl
- * @param eyeNum
- */
- public void displayOneEye(final GL2ES2 gl, final int eyeNum) {
- if( null == sp ) {
- throw new IllegalStateException("Not initialized");
- }
- final EyeData eye = eyes[eyeNum];
- if( useTimewarp() ) {
- eye.updateTimewarp(hmdCtx, eye.ovrEyePose, mat4Tmp1, mat4Tmp2);
- }
- eye.updateUniform(gl, sp);
- eye.enableVBO(gl, true);
- if( usesDistMesh ) {
- gl.glDrawElements(GL.GL_TRIANGLES, eye.indexCount, GL.GL_UNSIGNED_SHORT, 0);
- } else {
- gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, eye.vertexCount);
- }
- eyes[eyeNum].enableVBO(gl, false);
- }
-
- public void displayOneEyePost(final GL2ES2 gl) {
- sp.useProgram(gl, false);
- }
-
- /**
- * Calculates the <i>Side By Side</i>, SBS, projection- and modelview matrix for one eye.
- * <p>
- * {@link #updateEyePose(int)} must be called upfront.
- * </p>
- * <p>
- * This method merely exist as an example implementation to compute the matrices,
- * which shall be adopted by the
- * {@link CustomRendererListener#reshape(javax.media.opengl.GLAutoDrawable, int, int, int, int, EyeParameter, EyePose) upstream client code}.
- * </p>
- * @param eyeNum eye denominator
- * @param zNear frustum near value
- * @param zFar frustum far value
- * @param mat4Projection float[16] projection matrix result
- * @param mat4Modelview float[16] modelview matrix result
- * @deprecated Only an example implementation, which should be adopted by the {@link CustomRendererListener#reshape(javax.media.opengl.GLAutoDrawable, int, int, int, int, EyeParameter, EyePose) upstream client code}.
- */
- public void getSBSUpstreamPMV(final int eyeNum, final float zNear, final float zFar,
- final float[] mat4Projection, final float[] mat4Modelview) {
- final EyeData eyeDist = eyes[eyeNum];
-
- final float[] vec3Tmp1 = new float[3];
- final float[] vec3Tmp2 = new float[3];
- final float[] vec3Tmp3 = new float[3];
-
- //
- // Projection
- //
- FloatUtil.makePerspective(mat4Projection, 0, true, eyeDist.eyeParameter.fovhv, zNear, zFar);
-
- //
- // Modelview
- //
- final Quaternion rollPitchYaw = new Quaternion();
- // private final float eyeYaw = FloatUtil.PI; // 180 degrees in radians
- // rollPitchYaw.rotateByAngleY(eyeYaw);
- final float[] shiftedEyePos = rollPitchYaw.rotateVector(vec3Tmp1, 0, eyeDist.eyePose.position, 0);
- VectorUtil.addVec3(shiftedEyePos, shiftedEyePos, eyeDist.eyeParameter.positionOffset);
-
- rollPitchYaw.mult(eyeDist.eyePose.orientation);
- final float[] up = rollPitchYaw.rotateVector(vec3Tmp2, 0, VEC3_UP, 0);
- final float[] forward = rollPitchYaw.rotateVector(vec3Tmp3, 0, VEC3_FORWARD, 0);
- final float[] center = VectorUtil.addVec3(forward, shiftedEyePos, forward);
-
- final float[] mLookAt = FloatUtil.makeLookAt(mat4Tmp2, 0, shiftedEyePos, 0, center, 0, up, 0, mat4Tmp1);
- final float[] mViewAdjust = FloatUtil.makeTranslation(mat4Modelview, true,
- eyeDist.eyeParameter.distNoseToPupilX,
- eyeDist.eyeParameter.distMiddleToPupilY,
- eyeDist.eyeParameter.eyeReliefZ);
-
- /* mat4Modelview = */ FloatUtil.multMatrix(mViewAdjust, mLookAt);
- }
-}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java
new file mode 100644
index 000000000..09a348c46
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java
@@ -0,0 +1,158 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.opengl.oculusvr;
+
+import javax.media.nativewindow.util.Dimension;
+import javax.media.nativewindow.util.DimensionImmutable;
+import javax.media.nativewindow.util.PointImmutable;
+import javax.media.nativewindow.util.Rectangle;
+import javax.media.nativewindow.util.RectangleImmutable;
+
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.OvrHmdContext;
+import com.jogamp.oculusvr.ovrEyeRenderDesc;
+import com.jogamp.oculusvr.ovrFovPort;
+import com.jogamp.oculusvr.ovrHmdDesc;
+import com.jogamp.oculusvr.ovrSizei;
+import com.jogamp.opengl.math.FovHVHalves;
+import com.jogamp.opengl.util.stereo.StereoDevice;
+import com.jogamp.opengl.util.stereo.StereoDeviceRenderer;
+
+public class OVRStereoDevice implements StereoDevice {
+ public final OvrHmdContext handle;
+ public final int deviceIndex;
+ public final ovrHmdDesc hmdDesc;
+
+ private boolean sensorsStarted = false;
+
+ public OVRStereoDevice(final OvrHmdContext nativeContext, final int deviceIndex) {
+ this.handle = nativeContext;
+ this.deviceIndex = deviceIndex;
+ this.hmdDesc = ovrHmdDesc.create();
+ OVR.ovrHmd_GetDesc(handle, hmdDesc);
+ }
+
+ @Override
+ public final void dispose() {
+ // NOP
+ }
+
+ @Override
+ public final PointImmutable getPosition() {
+ return OVRUtil.getVec2iAsPoint(hmdDesc.getWindowsPos());
+ }
+
+ @Override
+ public final DimensionImmutable getSurfaceSize() {
+ return OVRUtil.getOVRSizei(hmdDesc.getResolution());
+ }
+
+ @Override
+ public final FovHVHalves[] getDefaultFOV() {
+ final ovrFovPort[] defaultEyeFov = hmdDesc.getDefaultEyeFov(0, new ovrFovPort[2]);
+ final FovHVHalves[] eyeFov = new FovHVHalves[2];
+ eyeFov[0] = OVRUtil.getFovHV(defaultEyeFov[0]);
+ eyeFov[1] = OVRUtil.getFovHV(defaultEyeFov[1]);
+ return eyeFov;
+ }
+
+ @Override
+ public final boolean startSensors(final boolean start) {
+ if( start && !sensorsStarted ) {
+ // Start the sensor which provides the Rift’s pose and motion.
+ final int requiredSensorCaps = 0;
+ final int supportedSensorCaps = requiredSensorCaps | OVR.ovrSensorCap_Orientation | OVR.ovrSensorCap_YawCorrection | OVR.ovrSensorCap_Position;
+ if( OVR.ovrHmd_StartSensor(handle, supportedSensorCaps, requiredSensorCaps) ) {
+ sensorsStarted = true;
+ return true;
+ } else {
+ sensorsStarted = false;
+ return false;
+ }
+ } else if( sensorsStarted ) {
+ OVR.ovrHmd_StopSensor(handle);
+ sensorsStarted = false;
+ return true;
+ } else {
+ // No state change -> Success
+ return true;
+ }
+ }
+ @Override
+ public boolean getSensorsStarted() { return sensorsStarted; }
+
+ @Override
+ public final StereoDeviceRenderer createRenderer(final int distortionBits,
+ final int textureCount, final float[] eyePositionOffset,
+ final FovHVHalves[] eyeFov, final float pixelsPerDisplayPixel, final int textureUnit) {
+ final ovrFovPort ovrEyeFov0 = OVRUtil.getOVRFovPort(eyeFov[0]);
+ final ovrFovPort ovrEyeFov1 = OVRUtil.getOVRFovPort(eyeFov[1]);
+
+ final ovrEyeRenderDesc[] eyeRenderDesc = new ovrEyeRenderDesc[2];
+ eyeRenderDesc[0] = OVR.ovrHmd_GetRenderDesc(handle, OVR.ovrEye_Left, ovrEyeFov0);
+ eyeRenderDesc[1] = OVR.ovrHmd_GetRenderDesc(handle, OVR.ovrEye_Right, ovrEyeFov1);
+ if( StereoDevice.DEBUG ) {
+ System.err.println("XXX: eyeRenderDesc[0] "+OVRUtil.toString(eyeRenderDesc[0]));
+ System.err.println("XXX: eyeRenderDesc[1] "+OVRUtil.toString(eyeRenderDesc[1]));
+ }
+
+ final ovrSizei recommenedTex0Size = OVR.ovrHmd_GetFovTextureSize(handle, OVR.ovrEye_Left, eyeRenderDesc[0].getFov(), pixelsPerDisplayPixel);
+ final ovrSizei recommenedTex1Size = OVR.ovrHmd_GetFovTextureSize(handle, OVR.ovrEye_Right, eyeRenderDesc[1].getFov(), pixelsPerDisplayPixel);
+ if( StereoDevice.DEBUG ) {
+ System.err.println("XXX: recommenedTex0Size "+OVRUtil.toString(recommenedTex0Size));
+ System.err.println("XXX: recommenedTex1Size "+OVRUtil.toString(recommenedTex1Size));
+ }
+ final int unifiedW = Math.max(recommenedTex0Size.getW(), recommenedTex1Size.getW());
+ final int unifiedH = Math.max(recommenedTex0Size.getH(), recommenedTex1Size.getH());
+
+ final DimensionImmutable singleTextureSize = new Dimension(unifiedW, unifiedH);
+ final DimensionImmutable totalTextureSize = new Dimension(recommenedTex0Size.getW() + recommenedTex1Size.getW(), unifiedH);
+ if( StereoDevice.DEBUG ) {
+ System.err.println("XXX: textureSize Single "+singleTextureSize);
+ System.err.println("XXX: textureSize Total "+totalTextureSize);
+ }
+
+ final RectangleImmutable[] eyeRenderViewports = new RectangleImmutable[2];
+ if( 1 == textureCount ) { // validated in ctor below!
+ eyeRenderViewports[0] = new Rectangle(0, 0,
+ totalTextureSize.getWidth() / 2,
+ totalTextureSize.getHeight());
+
+ eyeRenderViewports[1] = new Rectangle((totalTextureSize.getWidth() + 1) / 2, 0,
+ totalTextureSize.getWidth() / 2,
+ totalTextureSize.getHeight());
+ } else {
+ eyeRenderViewports[0] = new Rectangle(0, 0,
+ singleTextureSize.getWidth(),
+ singleTextureSize.getHeight());
+ eyeRenderViewports[1] = eyeRenderViewports[0];
+ }
+ return new OVRStereoDeviceRenderer(this, distortionBits, textureCount, eyePositionOffset,
+ eyeRenderDesc, singleTextureSize, totalTextureSize, eyeRenderViewports, textureUnit);
+ }
+} \ No newline at end of file
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java
new file mode 100644
index 000000000..06454e443
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.opengl.oculusvr;
+
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.OVRVersion;
+import com.jogamp.oculusvr.OvrHmdContext;
+import com.jogamp.opengl.util.stereo.StereoDevice;
+import com.jogamp.opengl.util.stereo.StereoDeviceFactory;
+
+public class OVRStereoDeviceFactory extends StereoDeviceFactory {
+
+ public static boolean isAvailable() {
+ return OVR.ovr_Initialize(); // recursive ..
+ }
+
+ @Override
+ public final StereoDevice createDevice(final int deviceIndex, final boolean verbose) {
+ final OvrHmdContext hmdCtx = OVR.ovrHmd_Create(deviceIndex);
+ final OVRStereoDevice ctx = new OVRStereoDevice(hmdCtx, deviceIndex);
+ if( verbose ) {
+ System.err.println(OVRVersion.getAvailableCapabilitiesInfo(ctx.hmdDesc, deviceIndex, null).toString());
+ }
+ return ctx;
+ }
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java
new file mode 100644
index 000000000..012ad183d
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java
@@ -0,0 +1,590 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.opengl.oculusvr;
+
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import javax.media.nativewindow.util.DimensionImmutable;
+import javax.media.nativewindow.util.RectangleImmutable;
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLArrayData;
+import javax.media.opengl.GLException;
+import javax.media.opengl.GLUniformData;
+
+import jogamp.common.os.PlatformPropsImpl;
+
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.OVRException;
+import com.jogamp.oculusvr.OvrHmdContext;
+import com.jogamp.oculusvr.ovrDistortionMesh;
+import com.jogamp.oculusvr.ovrDistortionVertex;
+import com.jogamp.oculusvr.ovrEyeRenderDesc;
+import com.jogamp.oculusvr.ovrFovPort;
+import com.jogamp.oculusvr.ovrFrameTiming;
+import com.jogamp.oculusvr.ovrMatrix4f;
+import com.jogamp.oculusvr.ovrPosef;
+import com.jogamp.oculusvr.ovrRecti;
+import com.jogamp.oculusvr.ovrSizei;
+import com.jogamp.oculusvr.ovrVector2f;
+import com.jogamp.oculusvr.ovrVector3f;
+import com.jogamp.opengl.JoglVersion;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.util.GLArrayDataServer;
+import com.jogamp.opengl.util.glsl.ShaderCode;
+import com.jogamp.opengl.util.glsl.ShaderProgram;
+import com.jogamp.opengl.util.stereo.EyeParameter;
+import com.jogamp.opengl.util.stereo.EyePose;
+import com.jogamp.opengl.util.stereo.StereoDevice;
+import com.jogamp.opengl.util.stereo.StereoDeviceRenderer;
+import com.jogamp.opengl.util.stereo.StereoUtil;
+
+/**
+ * OculusVR Distortion Data and OpenGL Renderer Utility
+ */
+public class OVRStereoDeviceRenderer implements StereoDeviceRenderer {
+ private static final String shaderPrefix01 = "dist01";
+ private static final String shaderTimewarpSuffix = "_timewarp";
+ private static final String shaderChromaSuffix = "_chroma";
+ private static final String shaderPlainSuffix = "_plain";
+
+ public static class OVREye implements StereoDeviceRenderer.Eye {
+ private final int eyeName;
+ private final int distortionBits;
+ private final int vertexCount;
+ private final int indexCount;
+ private final RectangleImmutable viewport;
+
+ private final GLUniformData eyeToSourceUVScale;
+ private final GLUniformData eyeToSourceUVOffset;
+ private final GLUniformData eyeRotationStart;
+ private final GLUniformData eyeRotationEnd;
+
+ /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
+ private final GLArrayDataServer iVBO;
+ private final GLArrayData vboPos, vboParams, vboTexCoordsR, vboTexCoordsG, vboTexCoordsB;
+ private final GLArrayDataServer indices;
+
+ private final ovrEyeRenderDesc ovrEyeDesc;
+ private final ovrFovPort ovrEyeFov;
+ private final EyeParameter eyeParameter;
+
+ private ovrPosef ovrEyePose;
+ private final EyePose eyePose;
+
+ @Override
+ public final RectangleImmutable getViewport() { return viewport; }
+
+ @Override
+ public final EyeParameter getEyeParameter() { return eyeParameter; }
+
+ @Override
+ public final EyePose getLastEyePose() { return eyePose; }
+
+ private OVREye(final OvrHmdContext hmdCtx, final int distortionBits,
+ final float[] eyePositionOffset, final ovrEyeRenderDesc eyeDesc,
+ final ovrSizei ovrTextureSize, final RectangleImmutable eyeViewport) {
+ this.eyeName = eyeDesc.getEye();
+ this.distortionBits = distortionBits;
+ this.viewport = eyeViewport;
+
+ final boolean usesTimewarp = StereoUtil.usesTimewarpDistortion(distortionBits);
+ final FloatBuffer fstash = Buffers.newDirectFloatBuffer( 2 + 2 + ( usesTimewarp ? 16 + 16 : 0 ) ) ;
+
+ eyeToSourceUVScale = new GLUniformData("ovr_EyeToSourceUVScale", 2, Buffers.slice2Float(fstash, 0, 2));
+ eyeToSourceUVOffset = new GLUniformData("ovr_EyeToSourceUVOffset", 2, Buffers.slice2Float(fstash, 2, 2));
+
+ if( usesTimewarp ) {
+ eyeRotationStart = new GLUniformData("ovr_EyeRotationStart", 4, 4, Buffers.slice2Float(fstash, 4, 16));
+ eyeRotationEnd = new GLUniformData("ovr_EyeRotationEnd", 4, 4, Buffers.slice2Float(fstash, 20, 16));
+ } else {
+ eyeRotationStart = null;
+ eyeRotationEnd = null;
+ }
+
+ this.ovrEyeDesc = eyeDesc;
+ this.ovrEyeFov = eyeDesc.getFov();
+
+ final ovrVector3f eyeViewAdjust = eyeDesc.getViewAdjust();
+ this.eyeParameter = new EyeParameter(eyeName, eyePositionOffset, OVRUtil.getFovHV(ovrEyeFov),
+ eyeViewAdjust.getX(), eyeViewAdjust.getY(), eyeViewAdjust.getZ());
+
+ this.eyePose = new EyePose(eyeName);
+
+ updateEyePose(hmdCtx); // 1st init
+
+ final ovrDistortionMesh meshData = ovrDistortionMesh.create();
+ final ovrFovPort fov = eyeDesc.getFov();
+
+ final int ovrDistortionCaps = distBits2OVRDistCaps(distortionBits);
+ if( !OVR.ovrHmd_CreateDistortionMesh(hmdCtx, eyeName, fov, ovrDistortionCaps, meshData) ) {
+ throw new OVRException("Failed to create meshData for eye "+eyeName+", "+OVRUtil.toString(fov)+" and "+StereoUtil.distortionBitsToString(distortionBits));
+ }
+ vertexCount = meshData.getVertexCount();
+ indexCount = meshData.getIndexCount();
+
+ /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
+ final boolean useChromatic = StereoUtil.usesChromaticDistortion(distortionBits);
+ final boolean useVignette = StereoUtil.usesVignetteDistortion(distortionBits);
+
+ final int compsPerElement = 2+2+2+( useChromatic ? 2+2 /* texCoordG + texCoordB */: 0 );
+ iVBO = GLArrayDataServer.createGLSLInterleaved(compsPerElement, GL.GL_FLOAT, false, vertexCount, GL.GL_STATIC_DRAW);
+ vboPos = iVBO.addGLSLSubArray("ovr_Position", 2, GL.GL_ARRAY_BUFFER);
+ vboParams = iVBO.addGLSLSubArray("ovr_Params", 2, GL.GL_ARRAY_BUFFER);
+ vboTexCoordsR = iVBO.addGLSLSubArray("ovr_TexCoordR", 2, GL.GL_ARRAY_BUFFER);
+ if( useChromatic ) {
+ vboTexCoordsG = iVBO.addGLSLSubArray("ovr_TexCoordG", 2, GL.GL_ARRAY_BUFFER);
+ vboTexCoordsB = iVBO.addGLSLSubArray("ovr_TexCoordB", 2, GL.GL_ARRAY_BUFFER);
+ } else {
+ vboTexCoordsG = null;
+ vboTexCoordsB = null;
+ }
+ indices = GLArrayDataServer.createData(1, GL.GL_SHORT, indexCount, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER);
+
+ // Setup: eyeToSourceUVScale, eyeToSourceUVOffset
+ {
+ final ovrVector2f[] uvScaleOffsetOut = new ovrVector2f[2];
+ uvScaleOffsetOut[0] = ovrVector2f.create(); // FIXME: remove ctor / double check
+ uvScaleOffsetOut[1] = ovrVector2f.create();
+
+ final ovrRecti ovrEyeRenderViewport = OVRUtil.createOVRRecti(eyeViewport);
+ OVR.ovrHmd_GetRenderScaleAndOffset(fov, ovrTextureSize, ovrEyeRenderViewport, uvScaleOffsetOut);
+ if( StereoDevice.DEBUG ) {
+ System.err.println("XXX."+eyeName+": fov "+OVRUtil.toString(fov));
+ System.err.println("XXX."+eyeName+": uvScale "+OVRUtil.toString(uvScaleOffsetOut[0]));
+ System.err.println("XXX."+eyeName+": uvOffset "+OVRUtil.toString(uvScaleOffsetOut[1]));
+ System.err.println("XXX."+eyeName+": textureSize "+OVRUtil.toString(ovrTextureSize));
+ System.err.println("XXX."+eyeName+": viewport "+OVRUtil.toString(ovrEyeRenderViewport));
+ }
+ final FloatBuffer eyeToSourceUVScaleFB = eyeToSourceUVScale.floatBufferValue();
+ eyeToSourceUVScaleFB.put(0, uvScaleOffsetOut[0].getX());
+ eyeToSourceUVScaleFB.put(1, uvScaleOffsetOut[0].getY());
+ final FloatBuffer eyeToSourceUVOffsetFB = eyeToSourceUVOffset.floatBufferValue();
+ eyeToSourceUVOffsetFB.put(0, uvScaleOffsetOut[1].getX());
+ eyeToSourceUVOffsetFB.put(1, uvScaleOffsetOut[1].getY());
+ }
+
+ /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
+ final FloatBuffer iVBOFB = (FloatBuffer)iVBO.getBuffer();
+ final ovrDistortionVertex[] ovRes = new ovrDistortionVertex[1];
+ ovRes[0] = ovrDistortionVertex.create(); // FIXME: remove ctor / double check
+
+ for ( int vertNum = 0; vertNum < vertexCount; vertNum++ ) {
+ final ovrDistortionVertex ov = meshData.getPVertexData(vertNum, ovRes)[0];
+ ovrVector2f v;
+
+ // pos
+ v = ov.getPos();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+
+ // params
+ if( useVignette ) {
+ iVBOFB.put(ov.getVignetteFactor());
+ } else {
+ iVBOFB.put(1.0f);
+ }
+ iVBOFB.put(ov.getTimeWarpFactor());
+
+ // texCoordR
+ v = ov.getTexR();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+
+ if( useChromatic ) {
+ // texCoordG
+ v = ov.getTexG();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+
+ // texCoordB
+ v = ov.getTexB();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+ }
+ }
+ if( StereoDevice.DEBUG ) {
+ System.err.println("XXX."+eyeName+": iVBO "+iVBO);
+ }
+ {
+ final ShortBuffer in = meshData.getPIndexData();
+ final ShortBuffer out = (ShortBuffer) indices.getBuffer();
+ out.put(in);
+ }
+ if( StereoDevice.DEBUG ) {
+ System.err.println("XXX."+eyeName+": idx "+indices);
+ System.err.println("XXX."+eyeName+": "+this);
+ }
+ OVR.ovrHmd_DestroyDistortionMesh(meshData);
+ }
+
+ private void linkData(final GL2ES2 gl, final ShaderProgram sp) {
+ if( 0 > vboPos.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboPos);
+ }
+ if( 0 > vboParams.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboParams);
+ }
+ if( 0 > vboTexCoordsR.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboTexCoordsR);
+ }
+ if( StereoUtil.usesChromaticDistortion(distortionBits) ) {
+ if( 0 > vboTexCoordsG.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboTexCoordsG);
+ }
+ if( 0 > vboTexCoordsB.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboTexCoordsB);
+ }
+ }
+ if( 0 > eyeToSourceUVScale.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeToSourceUVScale);
+ }
+ if( 0 > eyeToSourceUVOffset.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeToSourceUVOffset);
+ }
+ if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
+ if( 0 > eyeRotationStart.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeRotationStart);
+ }
+ if( 0 > eyeRotationEnd.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeRotationEnd);
+ }
+ }
+ iVBO.seal(gl, true);
+ iVBO.enableBuffer(gl, false);
+ indices.seal(gl, true);
+ indices.enableBuffer(gl, false);
+ }
+
+ private void dispose(final GL2ES2 gl) {
+ iVBO.destroy(gl);
+ indices.destroy(gl);
+ }
+ private void enableVBO(final GL2ES2 gl, final boolean enable) {
+ iVBO.enableBuffer(gl, enable);
+ indices.bindBuffer(gl, enable); // keeps VBO binding if enable:=true
+ }
+
+ private void updateUniform(final GL2ES2 gl, final ShaderProgram sp) {
+ gl.glUniform(eyeToSourceUVScale);
+ gl.glUniform(eyeToSourceUVOffset);
+ if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
+ gl.glUniform(eyeRotationStart);
+ gl.glUniform(eyeRotationEnd);
+ }
+ }
+
+ private void updateTimewarp(final OvrHmdContext hmdCtx, final ovrPosef eyeRenderPose, final float[] mat4Tmp1, final float[] mat4Tmp2) {
+ final ovrMatrix4f[] timeWarpMatrices = new ovrMatrix4f[2];
+ timeWarpMatrices[0] = ovrMatrix4f.create(); // FIXME: remove ctor / double check
+ timeWarpMatrices[1] = ovrMatrix4f.create();
+ OVR.ovrHmd_GetEyeTimewarpMatrices(hmdCtx, eyeName, eyeRenderPose, timeWarpMatrices);
+
+ final float[] eyeRotationStartM = FloatUtil.transposeMatrix(timeWarpMatrices[0].getM(0, mat4Tmp1), mat4Tmp2);
+ final FloatBuffer eyeRotationStartU = eyeRotationStart.floatBufferValue();
+ eyeRotationStartU.put(eyeRotationStartM);
+ eyeRotationStartU.rewind();
+
+ final float[] eyeRotationEndM = FloatUtil.transposeMatrix(timeWarpMatrices[1].getM(0, mat4Tmp1), mat4Tmp2);
+ final FloatBuffer eyeRotationEndU = eyeRotationEnd.floatBufferValue();
+ eyeRotationEndU.put(eyeRotationEndM);
+ eyeRotationEndU.rewind();
+ }
+
+ /**
+ * Updates {@link #ovrEyePose} and it's extracted
+ * {@link #eyeRenderPoseOrientation} and {@link #eyeRenderPosePosition}.
+ * @param hmdCtx used get the {@link #ovrEyePose} via {@link OVR#ovrHmd_GetEyePose(OvrHmdContext, int)}
+ */
+ private EyePose updateEyePose(final OvrHmdContext hmdCtx) {
+ ovrEyePose = OVR.ovrHmd_GetEyePose(hmdCtx, eyeName);
+ final ovrVector3f pos = ovrEyePose.getPosition();
+ eyePose.setPosition(pos.getX(), pos.getY(), pos.getZ());
+ OVRUtil.copyToQuaternion(ovrEyePose.getOrientation(), eyePose.orientation);
+ return eyePose;
+ }
+
+ @Override
+ public String toString() {
+ return "Eye["+eyeName+", viewport "+viewport+
+ ", "+eyeParameter+
+ ", vertices "+vertexCount+", indices "+indexCount+
+ ", uvScale["+eyeToSourceUVScale.floatBufferValue().get(0)+", "+eyeToSourceUVScale.floatBufferValue().get(1)+
+ "], uvOffset["+eyeToSourceUVOffset.floatBufferValue().get(0)+", "+eyeToSourceUVOffset.floatBufferValue().get(1)+
+ "], desc"+OVRUtil.toString(ovrEyeDesc)+", "+eyePose+"]";
+ }
+ }
+
+ private final OVRStereoDevice context;
+ private final OVREye[] eyes;
+ private final int distortionBits;
+ private final int textureCount;
+ private final DimensionImmutable singleTextureSize;
+ private final DimensionImmutable totalTextureSize;
+ private final GLUniformData texUnit0;
+
+
+ private final float[] mat4Tmp1 = new float[16];
+ private final float[] mat4Tmp2 = new float[16];
+
+ private ShaderProgram sp;
+ private ovrFrameTiming frameTiming;
+
+ @Override
+ public String toString() {
+ return "OVRDist[distortion["+StereoUtil.distortionBitsToString(distortionBits)+
+ "], singleSize "+singleTextureSize+
+ ", sbsSize "+totalTextureSize+
+ ", texCount "+textureCount+", texUnit "+getTextureUnit()+
+ ", "+PlatformPropsImpl.NEWLINE+" "+eyes[0]+", "+PlatformPropsImpl.NEWLINE+" "+eyes[1]+"]";
+ }
+
+
+ private static int distBits2OVRDistCaps(final int distortionBits) {
+ int bits = 0;
+ if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
+ bits |= OVR.ovrDistortionCap_TimeWarp;
+ }
+ if( StereoUtil.usesChromaticDistortion(distortionBits) ) {
+ bits |= OVR.ovrDistortionCap_Chromatic;
+ }
+ if( StereoUtil.usesVignetteDistortion(distortionBits) ) {
+ bits |= OVR.ovrDistortionCap_Vignette;
+ }
+ return bits;
+ }
+
+ /* pp */ OVRStereoDeviceRenderer(final OVRStereoDevice context, final int distortionBits,
+ final int textureCount, final float[] eyePositionOffset,
+ final ovrEyeRenderDesc[] eyeRenderDescs, final DimensionImmutable singleTextureSize, final DimensionImmutable totalTextureSize,
+ final RectangleImmutable[] eyeViewports, final int textureUnit) {
+ if( 1 > textureCount || 2 < textureCount ) {
+ throw new IllegalArgumentException("textureCount can only be 1 or 2, has "+textureCount);
+ }
+ this.context = context;
+ this.eyes = new OVREye[2];
+ this.distortionBits = distortionBits | StereoDeviceRenderer.DISTORTION_BARREL /* always */;
+ this.textureCount = textureCount;
+ this.singleTextureSize = singleTextureSize;
+ this.totalTextureSize = totalTextureSize;
+
+ texUnit0 = new GLUniformData("ovr_Texture0", textureUnit);
+
+ final ovrSizei ovrTextureSize = OVRUtil.createOVRSizei( 1 == textureCount ? totalTextureSize : singleTextureSize );
+ eyes[0] = new OVREye(context.handle, this.distortionBits, eyePositionOffset, eyeRenderDescs[0], ovrTextureSize, eyeViewports[0]);
+ eyes[1] = new OVREye(context.handle, this.distortionBits, eyePositionOffset, eyeRenderDescs[1], ovrTextureSize, eyeViewports[1]);
+ sp = null;
+ frameTiming = null;
+ }
+
+ @Override
+ public StereoDevice getDevice() {
+ return context;
+ }
+
+ @Override
+ public final int getDistortionBits() { return distortionBits; }
+
+ @Override
+ public final boolean usesSideBySideStereo() { return true; }
+
+ @Override
+ public final DimensionImmutable getSingleSurfaceSize() { return singleTextureSize; }
+
+ @Override
+ public final DimensionImmutable getTotalSurfaceSize() { return totalTextureSize; }
+
+ @Override
+ public final int getTextureCount() { return textureCount; }
+
+ @Override
+ public final int getTextureUnit() { return texUnit0.intValue(); }
+
+ @Override
+ public final boolean ppRequired() { return true; }
+
+ @Override
+ public final void init(final GL gl) {
+ if( StereoDevice.DEBUG ) {
+ System.err.println(JoglVersion.getGLInfo(gl, null).toString());
+ }
+ if( null != sp ) {
+ throw new IllegalStateException("Already initialized");
+ }
+ final GL2ES2 gl2es2 = gl.getGL2ES2();
+
+ final String vertexShaderBasename;
+ final String fragmentShaderBasename;
+ {
+ final boolean usesTimewarp = StereoUtil.usesTimewarpDistortion(distortionBits);
+ final boolean usesChromatic = StereoUtil.usesChromaticDistortion(distortionBits);
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(shaderPrefix01);
+ if( !usesChromatic && !usesTimewarp ) {
+ sb.append(shaderPlainSuffix);
+ } else if( usesChromatic && !usesTimewarp ) {
+ sb.append(shaderChromaSuffix);
+ } else if( usesTimewarp ) {
+ sb.append(shaderTimewarpSuffix);
+ if( usesChromatic ) {
+ sb.append(shaderChromaSuffix);
+ }
+ }
+ vertexShaderBasename = sb.toString();
+ sb.setLength(0);
+ sb.append(shaderPrefix01);
+ if( usesChromatic ) {
+ sb.append(shaderChromaSuffix);
+ } else {
+ sb.append(shaderPlainSuffix);
+ }
+ fragmentShaderBasename = sb.toString();
+ }
+ final ShaderCode vp0 = ShaderCode.create(gl2es2, GL2ES2.GL_VERTEX_SHADER, OVRStereoDeviceRenderer.class, "shader",
+ "shader/bin", vertexShaderBasename, true);
+ final ShaderCode fp0 = ShaderCode.create(gl2es2, GL2ES2.GL_FRAGMENT_SHADER, OVRStereoDeviceRenderer.class, "shader",
+ "shader/bin", fragmentShaderBasename, true);
+ vp0.defaultShaderCustomization(gl2es2, true, true);
+ fp0.defaultShaderCustomization(gl2es2, true, true);
+
+ sp = new ShaderProgram();
+ sp.add(gl2es2, vp0, System.err);
+ sp.add(gl2es2, fp0, System.err);
+ if(!sp.link(gl2es2, System.err)) {
+ throw new GLException("could not link program: "+sp);
+ }
+ sp.useProgram(gl2es2, true);
+ if( 0 > texUnit0.setLocation(gl2es2, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+texUnit0);
+ }
+ eyes[0].linkData(gl2es2, sp);
+ eyes[1].linkData(gl2es2, sp);
+ sp.useProgram(gl2es2, false);
+ }
+
+ @Override
+ public final void dispose(final GL gl) {
+ final GL2ES2 gl2es2 = gl.getGL2ES2();
+ sp.useProgram(gl2es2, false);
+ eyes[0].dispose(gl2es2);
+ eyes[1].dispose(gl2es2);
+ sp.destroy(gl2es2);
+ frameTiming = null;
+ }
+
+ @Override
+ public final Eye getEye(final int eyeNum) {
+ return eyes[eyeNum];
+ }
+
+ @Override
+ public final EyePose updateEyePose(final int eyeNum) {
+ return eyes[eyeNum].updateEyePose(context.handle);
+ }
+
+ @Override
+ public final void beginFrame(final GL gl) {
+ frameTiming = OVR.ovrHmd_BeginFrameTiming(context.handle, 0);
+ }
+
+ @Override
+ public final void endFrame(final GL gl) {
+ if( null == frameTiming ) {
+ throw new IllegalStateException("beginFrame not called");
+ }
+ OVR.ovrHmd_EndFrameTiming(context.handle);
+ frameTiming = null;
+ }
+
+ @Override
+ public final void ppBegin(final GL gl) {
+ if( null == sp ) {
+ throw new IllegalStateException("Not initialized");
+ }
+ if( null == frameTiming ) {
+ throw new IllegalStateException("beginFrame not called");
+ }
+ if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
+ OVR.ovr_WaitTillTime(frameTiming.getTimewarpPointSeconds());
+ }
+ final GL2ES2 gl2es2 = gl.getGL2ES2();
+
+ gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT);
+ gl.glActiveTexture(GL.GL_TEXTURE0 + getTextureUnit());
+
+ gl2es2.glDisable(GL.GL_CULL_FACE);
+ gl2es2.glDisable(GL.GL_DEPTH_TEST);
+ gl2es2.glDisable(GL.GL_BLEND);
+
+ if( !gl2es2.isGLcore() ) {
+ gl2es2.glEnable(GL.GL_TEXTURE_2D);
+ }
+
+ sp.useProgram(gl2es2, true);
+
+ gl2es2.glUniform(texUnit0);
+ }
+
+ @Override
+ public final void ppBothEyes(final GL gl) {
+ final GL2ES2 gl2es2 = gl.getGL2ES2();
+ for(int eyeNum=0; eyeNum<2; eyeNum++) {
+ final OVREye eye = eyes[eyeNum];
+ if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
+ eye.updateTimewarp(context.handle, eye.ovrEyePose, mat4Tmp1, mat4Tmp2);
+ }
+ eye.updateUniform(gl2es2, sp);
+ eye.enableVBO(gl2es2, true);
+ gl2es2.glDrawElements(GL.GL_TRIANGLES, eye.indexCount, GL.GL_UNSIGNED_SHORT, 0);
+ eyes[eyeNum].enableVBO(gl2es2, false);
+ }
+ }
+
+ @Override
+ public final void ppOneEye(final GL gl, final int eyeNum) {
+ final OVREye eye = eyes[eyeNum];
+ if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
+ eye.updateTimewarp(context.handle, eye.ovrEyePose, mat4Tmp1, mat4Tmp2);
+ }
+ final GL2ES2 gl2es2 = gl.getGL2ES2();
+
+ eye.updateUniform(gl2es2, sp);
+ eye.enableVBO(gl2es2, true);
+ gl2es2.glDrawElements(GL.GL_TRIANGLES, eye.indexCount, GL.GL_UNSIGNED_SHORT, 0);
+ eyes[eyeNum].enableVBO(gl2es2, false);
+ }
+
+ @Override
+ public final void ppEnd(final GL gl) {
+ sp.useProgram(gl.getGL2ES2(), false);
+ }
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java
index 6c1cdc015..4de05fc92 100644
--- a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java
@@ -27,7 +27,11 @@
*/
package jogamp.opengl.oculusvr;
-import jogamp.opengl.Debug;
+import javax.media.nativewindow.util.Dimension;
+import javax.media.nativewindow.util.DimensionImmutable;
+import javax.media.nativewindow.util.Point;
+import javax.media.nativewindow.util.PointImmutable;
+import javax.media.nativewindow.util.RectangleImmutable;
import com.jogamp.oculusvr.ovrEyeRenderDesc;
import com.jogamp.oculusvr.ovrFovPort;
@@ -44,8 +48,6 @@ import com.jogamp.opengl.math.Quaternion;
* OculusVR Data Conversion Helper Functions
*/
public class OVRUtil {
- public static final boolean DEBUG = Debug.debug("OVR");
-
public static ovrRecti createOVRRecti(final int[] rect) {
final ovrRecti res = ovrRecti.create();
final ovrVector2i pos = res.getPos();
@@ -56,6 +58,16 @@ public class OVRUtil {
size.setH(rect[3]);
return res;
}
+ public static ovrRecti createOVRRecti(final RectangleImmutable rect) {
+ final ovrRecti res = ovrRecti.create();
+ final ovrVector2i pos = res.getPos();
+ final ovrSizei size = res.getSize();
+ pos.setX(rect.getX());
+ pos.setY(rect.getY());
+ size.setW(rect.getWidth());
+ size.setH(rect.getHeight());
+ return res;
+ }
public static ovrRecti[] createOVRRectis(final int[][] rects) {
final ovrRecti[] res = new ovrRecti[rects.length];
for(int i=0; i<res.length; i++) {
@@ -69,11 +81,24 @@ public class OVRUtil {
res.setH(size[1]);
return res;
}
- public static Quaternion getQuaternion(final ovrQuatf q) {
- return new Quaternion(q.getX(), q.getY(), q.getZ(), q.getW());
+ public static ovrSizei createOVRSizei(final DimensionImmutable size) {
+ final ovrSizei res = ovrSizei.create();
+ res.setW(size.getWidth());
+ res.setH(size.getHeight());
+ return res;
}
- public static void copyToQuaternion(final ovrQuatf in, final Quaternion out) {
- out.set(in.getX(), in.getY(), in.getZ(), in.getW());
+ public static DimensionImmutable getOVRSizei(final ovrSizei v) {
+ return new Dimension(v.getW(), v.getH());
+ }
+ public static PointImmutable getVec2iAsPoint(final ovrVector2i v) {
+ return new Point(v.getX(), v.getY());
+ }
+ public static int[] getVec2i(final ovrVector2i v) {
+ return new int[] { v.getX(), v.getY() };
+ }
+ public static void copyVec2iToInt(final ovrVector2i v, final int[] res) {
+ res[0] = v.getX();
+ res[1] = v.getY();
}
public static float[] getVec3f(final ovrVector3f v) {
return new float[] { v.getX(), v.getY(), v.getZ() };
@@ -83,12 +108,33 @@ public class OVRUtil {
res[1] = v.getY();
res[2] = v.getZ();
}
+ public static Quaternion getQuaternion(final ovrQuatf q) {
+ return new Quaternion(q.getX(), q.getY(), q.getZ(), q.getW());
+ }
+ public static void copyToQuaternion(final ovrQuatf in, final Quaternion out) {
+ out.set(in.getX(), in.getY(), in.getZ(), in.getW());
+ }
public static FovHVHalves getFovHV(final ovrFovPort tanHalfFov) {
return new FovHVHalves(tanHalfFov.getLeftTan(), tanHalfFov.getRightTan(),
tanHalfFov.getUpTan(), tanHalfFov.getDownTan(),
true);
}
+ public static ovrFovPort getOVRFovPort(final FovHVHalves fovHVHalves) {
+ final ovrFovPort tanHalfFov = ovrFovPort.create();
+ if( fovHVHalves.inTangents ) {
+ tanHalfFov.setLeftTan(fovHVHalves.left);
+ tanHalfFov.setRightTan(fovHVHalves.right);
+ tanHalfFov.setUpTan(fovHVHalves.top);
+ tanHalfFov.setDownTan(fovHVHalves.bottom);
+ } else {
+ tanHalfFov.setLeftTan((float)Math.tan(fovHVHalves.left));
+ tanHalfFov.setRightTan((float)Math.tan(fovHVHalves.right));
+ tanHalfFov.setUpTan((float)Math.tan(fovHVHalves.top));
+ tanHalfFov.setDownTan((float)Math.tan(fovHVHalves.bottom));
+ }
+ return tanHalfFov;
+ }
public static String toString(final ovrFovPort fov) {
return "["+fov.getLeftTan()+" l, "+fov.getRightTan()+" r, "+