diff options
author | Sven Gothel <[email protected]> | 2014-07-05 04:04:43 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-07-05 04:04:43 +0200 |
commit | f8f0f051604721bceaee214b8e5218fd47d2eb9e (patch) | |
tree | e9c3103498984fbc5d2f8567e88009c49e8810f7 /src/jogl/classes/com/jogamp | |
parent | 2293a53ba04a8cf2881e8919f8be97c16a9af336 (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/jogl/classes/com/jogamp')
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/CustomGLEventListener.java (renamed from src/jogl/classes/com/jogamp/opengl/util/CustomRendererListener.java) | 2 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java | 2 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java | 2 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java | 255 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java | 93 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java | 64 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java | 235 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/StereoGLEventListener.java (renamed from src/jogl/classes/com/jogamp/opengl/util/stereo/StereoRendererListener.java) | 13 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java | 125 |
9 files changed, 782 insertions, 9 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/CustomRendererListener.java b/src/jogl/classes/com/jogamp/opengl/util/CustomGLEventListener.java index 0e6de5178..86443087e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/CustomRendererListener.java +++ b/src/jogl/classes/com/jogamp/opengl/util/CustomGLEventListener.java @@ -34,7 +34,7 @@ import javax.media.opengl.GLEventListener; * Extended {@link GLEventListener} interface * supporting more fine grained control over the implementation. */ -public interface CustomRendererListener extends GLEventListener { +public interface CustomGLEventListener extends GLEventListener { /** * {@link #display(GLAutoDrawable, int) display flag}: Repeat last produced image. * <p> diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java index 7774d67e2..075da340b 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java @@ -62,7 +62,7 @@ public final class EyeParameter { this.eyeReliefZ = eyeRelief; } public final String toString() { - return "EyeParam[num"+number+", posOff["+positionOffset[0]+", "+positionOffset[1]+", "+positionOffset[2]+"], "+fovhv+ + return "EyeParam[num "+number+", posOff["+positionOffset[0]+", "+positionOffset[1]+", "+positionOffset[2]+"], "+fovhv+ ", distPupil[noseX "+distNoseToPupilX+", middleY "+distMiddleToPupilY+", reliefZ "+eyeReliefZ+"]]"; } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java index 2690097f1..aa64ff130 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java @@ -64,6 +64,6 @@ public final class EyePose { position[2] = posZ; } public final String toString() { - return "EyePose[num"+number+", pos["+position[0]+", "+position[1]+", "+position[2]+"], "+orientation+"]"; + return "EyePose[num "+number+", pos["+position[0]+", "+position[1]+", "+position[2]+"], "+orientation+"]"; } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java new file mode 100644 index 000000000..9f9ebdf2a --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java @@ -0,0 +1,255 @@ +/** + * 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.util.stereo; + +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.GLAutoDrawable; +import javax.media.opengl.GLEventListener; + +import jogamp.opengl.GLDrawableHelper; +import jogamp.opengl.GLDrawableHelper.GLEventListenerAction; + +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.CustomGLEventListener; + +/** + * {@link StereoClientRenderer} utilizing {@link StereoDeviceRenderer} + * implementing {@link GLEventListener} for convenience. + * <p> + * Implementation renders {@link StereoGLEventListener} + * using one or more {@link FBObject} according to {@link StereoDeviceRenderer#getTextureCount()}. + * </p> + */ +public class StereoClientRenderer implements GLEventListener { + private final GLDrawableHelper helper; + private final StereoDeviceRenderer deviceRenderer; + private final boolean ownsDevice; + private final FBObject[] fbos; + private final int magFilter; + private final int minFilter; + private final boolean usePostprocessing; + + private int numSamples; + private final TextureAttachment[] fboTexs; + + public StereoClientRenderer(final StereoDeviceRenderer deviceRenderer, final boolean ownsDevice, + final int magFilter, final int minFilter, final int numSamples) { + final int fboCount = deviceRenderer.getTextureCount(); + if( 0 > fboCount || 2 < fboCount ) { + throw new IllegalArgumentException("fboCount must be within [0..2], has "+fboCount+", due to "+deviceRenderer); + } + this.helper = new GLDrawableHelper(); + this.deviceRenderer = deviceRenderer; + this.usePostprocessing = deviceRenderer.ppRequired() || deviceRenderer.usesSideBySideStereo() && fboCount > 1; + this.ownsDevice = ownsDevice; + this.magFilter = magFilter; + this.minFilter = minFilter; + + this.numSamples = numSamples; + + this.fbos = new FBObject[fboCount]; + for(int i=0; i<fboCount; i++) { + this.fbos[i] = new FBObject(); + } + this.fboTexs = new TextureAttachment[fboCount]; + } + + private void initFBOs(final GL gl, final DimensionImmutable size) { + for(int i=0; i<fbos.length; i++) { + fbos[i].detachAllColorbuffer(gl); + fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples, false); + if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { + throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); + } + numSamples = fbos[i].getNumSamples(); + + if(numSamples>0) { + fbos[i].attachColorbuffer(gl, 0, true); // MSAA requires alpha + fbos[i].attachRenderbuffer(gl, Type.DEPTH, 24); + final FBObject ssink = new FBObject(); + { + ssink.reset(gl, size.getWidth(), size.getHeight()); + ssink.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + } + fbos[i].setSamplingSink(ssink); + fbos[i].resetSamplingSink(gl); // validate + fboTexs[i] = fbos[i].getSamplingSink(); + } else { + fboTexs[i] = fbos[i].attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); + fbos[i].attachRenderbuffer(gl, Type.DEPTH, 24); + } + fbos[i].unbind(gl); + System.err.println("FBO["+i+"]: "+fbos[i]); + } + + } + + @SuppressWarnings("unused") + private void resetFBOs(final GL gl, final DimensionImmutable size) { + for(int i=0; i<fbos.length; i++) { + fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples, true); + if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { + throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); + } + numSamples = fbos[i].getNumSamples(); + if(numSamples>0) { + fboTexs[i] = fbos[i].getSamplingSink(); + } else { + fboTexs[i] = (TextureAttachment) fbos[i].getColorbuffer(0); + } + } + } + + public final StereoDeviceRenderer getStereoDeviceRenderer() { return deviceRenderer; } + + public final void addGLEventListener(final StereoGLEventListener l) { + helper.addGLEventListener(l); + } + public final void removeGLEventListener(final StereoGLEventListener l) { + helper.removeGLEventListener(l); + } + + @Override + public void init(final GLAutoDrawable drawable) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + deviceRenderer.init(gl); + + // We will do some offscreen rendering, setup FBO... + final DimensionImmutable textureSize = deviceRenderer.getTextureCount() > 1 ? deviceRenderer.getSingleSurfaceSize() : deviceRenderer.getTotalSurfaceSize(); + initFBOs(gl, textureSize); + helper.init(drawable, false); + + gl.setSwapInterval(1); + } + + @Override + public void dispose(final GLAutoDrawable drawable) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + helper.disposeAllGLEventListener(drawable, false); + for(int i=0; i<fbos.length; i++) { + fbos[i].destroy(gl); + fboTexs[i] = null; + } + if( ownsDevice ) { + deviceRenderer.dispose(gl); + } + } + + @Override + public void display(final GLAutoDrawable drawable) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + + deviceRenderer.beginFrame(gl); + + if(0 < numSamples) { + gl.glEnable(GL.GL_MULTISAMPLE); + } + + final int fboCount = fbos.length; + final int displayRepeatFlags; + if( 1 == fboCount ) { + displayRepeatFlags = CustomGLEventListener.DISPLAY_DONTCLEAR; + } else { + displayRepeatFlags = 0; + } + + // Update eye pos upfront to have same (almost) results + deviceRenderer.updateEyePose(0); + deviceRenderer.updateEyePose(1); + + if( 1 == fboCount ) { + fbos[0].bind(gl); + } + + for(int eyeNum=0; eyeNum<2; eyeNum++) { + if( 1 < fboCount ) { + fbos[eyeNum].bind(gl); + } + + final StereoDeviceRenderer.Eye eye = deviceRenderer.getEye(eyeNum); + final RectangleImmutable viewport = eye.getViewport(); + gl.glViewport(viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight()); + + final int displayFlags = eyeNum > 0 ? CustomGLEventListener.DISPLAY_REPEAT | displayRepeatFlags : 0; + final GLEventListenerAction reshapeDisplayAction = new GLEventListenerAction() { + public void run(final GLAutoDrawable drawable, final GLEventListener listener) { + final StereoGLEventListener sl = (StereoGLEventListener) listener; + sl.reshapeForEye(drawable, viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight(), + eye.getEyeParameter(), eye.getLastEyePose()); + sl.display(drawable, displayFlags); + } }; + helper.runForAllGLEventListener(drawable, reshapeDisplayAction); + + if( 1 < fboCount ) { + fbos[eyeNum].unbind(gl); + } + } + if( 1 == fboCount ) { + fbos[0].unbind(gl); + } + // restore viewport + gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + + if( usePostprocessing ) { + deviceRenderer.ppBegin(gl); + if( 1 == fboCount ) { + fbos[0].use(gl, fboTexs[0]); + deviceRenderer.ppBothEyes(gl); + fbos[0].unuse(gl); + } else { + fbos[0].use(gl, fboTexs[0]); + deviceRenderer.ppOneEye(gl, 0); + fbos[0].unuse(gl); + fbos[1].use(gl, fboTexs[1]); + deviceRenderer.ppOneEye(gl, 1); + fbos[1].unuse(gl); + } + deviceRenderer.ppEnd(gl); + } + + if( !drawable.getAutoSwapBufferMode() ) { + drawable.swapBuffers(); + } + deviceRenderer.endFrame(gl); + } + + @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/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java new file mode 100644 index 000000000..e5c0e3646 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java @@ -0,0 +1,93 @@ +/** + * 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.util.stereo; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PointImmutable; + +import jogamp.opengl.Debug; + +import com.jogamp.opengl.math.FovHVHalves; + +/** + * Interface describing a native stereoscopic device + */ +public interface StereoDevice { + public static final boolean DEBUG = Debug.debug("StereoDevice"); + + /** + * Default eye position offset for {@link #createRenderer(int, int, float[], FovHVHalves[], float)}. + * <p> + * Default offset is 1.6f <i>up</i> and 5.0f <i>away</i>. + * </p> + */ + public static final float[] DEFAULT_EYE_POSITION_OFFSET = { 0.0f, 1.6f, -5.0f }; + + /** Disposes this {@link StereoDevice}. */ + public void dispose(); + + /** + * If operation within a device spanning virtual desktop, + * returns the device position. + * <p> + * Otherwise simply 0/0. + * </p> + */ + public PointImmutable getPosition(); + + /** + * Returns the required surface size in pixel. + */ + public DimensionImmutable getSurfaceSize(); + + /** + * Returns the device default {@link FovHVHalves} per eye. + */ + public FovHVHalves[] getDefaultFOV(); + + /** Start or stop sensors. Returns true if action was successful, otherwise false. */ + public boolean startSensors(boolean start); + + /** Return true if sensors have been started, false otherwise */ + public boolean getSensorsStarted(); + + /** + * Create a new {@link StereoDeviceRenderer} instance. + * + * @param distortionBits {@link StereoDeviceRenderer} distortion bits, e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, etc. + * @param textureCount desired texture count for post-processing, see {@link StereoDeviceRenderer#getTextureCount()} and {@link StereoDeviceRenderer#ppRequired()} + * @param eyePositionOffset eye position offset, e.g. {@link #DEFAULT_EYE_POSITION_OFFSET}. + * @param eyeFov FovHVHalves[2] field-of-view per eye + * @param pixelsPerDisplayPixel + * @param textureUnit + * @return + */ + public StereoDeviceRenderer createRenderer(final int distortionBits, + final int textureCount, final float[] eyePositionOffset, + final FovHVHalves[] eyeFov, final float pixelsPerDisplayPixel, final int textureUnit); +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java new file mode 100644 index 000000000..d9054ce28 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java @@ -0,0 +1,64 @@ +/** + * 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.util.stereo; + +import com.jogamp.common.util.ReflectionUtil; + +/** + * Platform agnostic {@link StereoDevice} factory. + */ +public abstract class StereoDeviceFactory { + private static final String OVRStereoDeviceClazzName = "jogamp.opengl.oculusvr.OVRStereoDeviceFactory"; + private static final Object[] ctorArgs; + private static final String isAvailableMethodName = "isAvailable"; + + static { + ctorArgs = new Object[6]; + ctorArgs[0] = null; + + } + public static StereoDeviceFactory createDefaultFactory() { + final ClassLoader cl = StereoDeviceFactory.class.getClassLoader(); + final StereoDeviceFactory sink = createFactory(cl, OVRStereoDeviceClazzName); + if( null == sink ) { + // sink = create(cl, ANYOTHERCLAZZNAME); + } + return sink; + } + + public static StereoDeviceFactory createFactory(final ClassLoader cl, final String implName) { + try { + if(((Boolean)ReflectionUtil.callStaticMethod(implName, isAvailableMethodName, null, null, cl)).booleanValue()) { + return (StereoDeviceFactory) ReflectionUtil.createInstance(implName, cl); + } + } catch (final Throwable t) { if(StereoDevice.DEBUG) { System.err.println("Caught "+t.getClass().getName()+": "+t.getMessage()); t.printStackTrace(); } } + return null; + } + + public abstract StereoDevice createDevice(final int deviceIndex, final boolean verbose); +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java new file mode 100644 index 000000000..fd94f6bc3 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java @@ -0,0 +1,235 @@ +/** + * 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.util.stereo; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.RectangleImmutable; +import javax.media.opengl.GL; + +/** + * Stereoscopic device rendering interface. + * <p> + * The following pseudo-code describes how to implement a renderer + * using a {@link StereoDeviceRenderer}. + * See {@link StereoClientRenderer} which implements the following: + * <ul> + * <li>device.{@link #beginFrame(GL)}</li> + * <li>For both eyes:<ul> + * <li>device.{@link #updateEyePose(int)}</li> + * <li>if device.{@link #ppRequired()}: Set the render target, e.g. FBO</li> + * <li>Set the viewport using {@link Eye#getViewport()}</li> + * <li>{@link StereoGLEventListener#reshapeForEye(javax.media.opengl.GLAutoDrawable, int, int, int, int, EyeParameter, EyePose) upstream.reshapeEye(..)}</li> + * <li>{@link StereoGLEventListener#display(javax.media.opengl.GLAutoDrawable, int) upstream.display(..)}.</li> + * </ul></li> + * <li>Reset the viewport</li> + * <li>If device.{@link #ppRequired()}:<ul> + * <li>device.{@link #ppBegin(GL)}</li> + * <li>Use render target, e.g. FBO's texture</li> + * <li>device.{@link #ppBothEyes(GL)} or device.{@link #ppOneEye(GL, int)} for both eyes</li> + * <li>device.{@link #ppEnd(GL)}</li> + * </ul></li> + * <li>device.{@link #endFrame(GL)}</li> + * <ul> + */ +public interface StereoDeviceRenderer { + /** + * Distortion Bit: Barrel distortion compensating lens pincushion distortion + */ + public static final int DISTORTION_BARREL = 1 << 0; + + /** + * Distortion Bit: Chromatic distortion compensating lens chromatic aberration. + */ + public static final int DISTORTION_CHROMATIC = 1 << 1; + + /** + * Distortion Bit: Vignette distortion compensating lens chromatic aberration. + */ + public static final int DISTORTION_VIGNETTE = 1 << 2; + + /** + * Distortion Bit: Timewarp distortion technique to predict + * {@link EyePose} movement to reduce latency. + * <p> + * FIXME: Explanation needs refinement! + * </p> + */ + public static final int DISTORTION_TIMEWARP = 1 << 3; + + + /** Returns the {@link StereoDevice} of this {@link StereoDeviceRenderer} instance. */ + public StereoDevice getDevice(); + + /** + * Interface describing one eye of the stereoscopic device, + * see {@link StereoDeviceRenderer#getEye(int)}. + */ + public static interface Eye { + /** + * Returns the viewport for this eye. + */ + public RectangleImmutable getViewport(); + /** + * Returns the {@link EyeParameter} of this eye. + */ + public EyeParameter getEyeParameter(); + /** + * Returns the last {@link EyePose} of this eye. + */ + public EyePose getLastEyePose(); + } + + /** + * Returns the {@link Eye} instance for the denoted <code>eyeNum</code>. + */ + public Eye getEye(final int eyeNum); + + /** + * Updates the {@link Eye#getLastEyePose()} + * for the denoted <code>eyeNum</code>. + */ + public EyePose updateEyePose(final int eyeNum); + + /** + * Returns distortion compensation bits, e.g. {@link #DISTORTION_BARREL}, + * in case the stereoscopic display requires such, i.e. in case lenses are utilized. + * <p> + * Distortion requires {@link #ppRequired() post-processing}. + * </p> + */ + public int getDistortionBits(); + + /** + * Method returns <code>true</code> if using <i>side-by-side</i> (SBS) + * stereoscopic images, otherwise <code>false</code>. + * <p> + * SBS requires that both eye's images are presented + * <i>side-by-side</i> in the final framebuffer. + * </p> + * <p> + * Either the renderer presents the images <i>side-by-side</i> according to the {@link Eye#getViewport() eye's viewport}, + * or {@link #ppRequired() post-processing} is utilized to merge {@link #getTextureCount() textures} + * to a <i>side-by-side</i> configuration. + * </p> + */ + public boolean usesSideBySideStereo(); + + /** + * Returns the unified surface size of one eye's a single image in pixel units. + */ + public DimensionImmutable getSingleSurfaceSize(); + + /** + * Returns the total surface size required for the complete images in pixel units. + * <p> + * If {@link #usesSideBySideStereo()} the total size spans over both {@link #getSingleSurfaceSize()}, side-by-side. + * </p> + * <p> + * Otherwise the size is equal to {@link #getSingleSurfaceSize()}. + * </p> + */ + public DimensionImmutable getTotalSurfaceSize(); + + /** + * Returns the used texture-image count for post-processing, see {@link #ppRequired()}. + * <p> + * In case the renderer does not support multiple textures for post-processing, + * or no post-processing at all, method returns zero despite the request + * from {@link StereoDevice#createRenderer(int, int, float[], com.jogamp.opengl.math.FovHVHalves[], float)}. + * </p> + */ + public int getTextureCount(); + + /** Returns the desired texture-image unit for post-processing, see {@link #ppRequired()}. */ + public int getTextureUnit(); + + /** Initialize OpenGL related resources */ + public void init(final GL gl); + + /** Release all OpenGL related resources */ + public void dispose(final GL gl); + + /** Notifying that a new frame is about to start. */ + public void beginFrame(final GL gl); + + /** Notifying that the frame has been rendered completely. */ + public void endFrame(final GL gl); + + /** + * Returns <code>true</code> if stereoscopic post-processing is required, + * otherwise <code>false</code>. + * <p> + * Stereoscopic post-processing is usually required if: + * <ul> + * <li>one of the <i>distortion</i> modes are set, i.e. {@link #usesBarrelDistortion()}</li> + * <li>texture-images are being used, see {@link #getTextureCount()}</li> + * </ul> + * </p> + * <p> + * If stereoscopic post-processing is used + * the following post-processing methods must be called to before {@link #endFrame()}: + * <ul> + * <li>{@link #ppBegin(GL)}</li> + * <li>{@link #ppBothEyes(GL)} or {@link #ppOneEye(GL, int)} for both eyes</li> + * <li>{@link #ppEnd(GL)}</li> + * </ul> + * </p> + */ + public boolean ppRequired(); + + /** + * Begin stereoscopic post-processing, see {@link #ppRequired()}. + * <p> + * {@link #updateEyePose(int)} for both eyes must be called upfront + * when rendering upstream {@link StereoGLEventListener}. + * </p> + * + * @param gl + */ + public void ppBegin(final GL gl); + + /** + * Performs stereoscopic post-processing for both eyes, see {@link #ppRequired()}. + * @param gl + */ + public void ppBothEyes(final GL gl); + + /** + * Performs stereoscopic post-processing for one eye, see {@link #ppRequired()}. + * @param gl + * @param eyeNum + */ + public void ppOneEye(final GL gl, final int eyeNum); + + /** + * End stereoscopic post-processing, see {@link #ppRequired()}. + * @param gl + */ + public void ppEnd(final GL gl); + +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoRendererListener.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoGLEventListener.java index 5e6e40a08..ec580cbf9 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoRendererListener.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoGLEventListener.java @@ -31,15 +31,16 @@ import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLEventListener; import com.jogamp.opengl.math.FloatUtil; -import com.jogamp.opengl.util.CustomRendererListener; +import com.jogamp.opengl.util.CustomGLEventListener; /** - * Extended {@link GLEventListener} and {@link CustomRendererListener} interface + * Extended {@link GLEventListener} and {@link CustomGLEventListener} interface * supporting stereoscopic client rendering. */ -public interface StereoRendererListener extends CustomRendererListener { +public interface StereoGLEventListener extends CustomGLEventListener { /** - * Stereo capable specialization of {@link #reshape(GLAutoDrawable, int, int, int, int)}. + * Stereo capable specialization of {@link #reshape(GLAutoDrawable, int, int, int, int)} + * for one {@link StereoDeviceRenderer.Eye}. * <p> * Called by the stereo renderer before each {@link #display(GLAutoDrawable)} * or {@link #display(GLAutoDrawable, int)} call. @@ -66,8 +67,8 @@ public interface StereoRendererListener extends CustomRendererListener { * @param eyePose current eye position and orientation * @see FloatUtil#makePerspective(float[], int, boolean, com.jogamp.opengl.math.FloatUtil.FovHVHalves, float, float) */ - public void reshapeEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, - final EyeParameter eyeParam, final EyePose eyePose); + public void reshapeForEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, + final EyeParameter eyeParam, final EyePose eyePose); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java new file mode 100644 index 000000000..280d99233 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java @@ -0,0 +1,125 @@ +/** + * 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.util.stereo; + +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Quaternion; +import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.opengl.util.CustomGLEventListener; +import com.jogamp.opengl.util.stereo.StereoDeviceRenderer.Eye; + +public class StereoUtil { + + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesBarrelDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_BARREL ) ; } + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesTimewarpDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_TIMEWARP ) ; } + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesChromaticDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_CHROMATIC ) ; } + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesVignetteDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_VIGNETTE ) ; } + + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static String distortionBitsToString(final int distortionBits) { + boolean appendComma = false; + final StringBuilder sb = new StringBuilder(); + if( usesBarrelDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("barrell"); appendComma=true; + } + if( usesVignetteDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("vignette"); appendComma=true; + } + if( usesChromaticDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("chroma"); appendComma=true; + } + if( usesTimewarpDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("timewarp"); appendComma=true; + } + return sb.toString(); + } + + /** + * 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 CustomGLEventListener#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 + */ + public static void getSBSUpstreamPMV(final Eye eye, final float zNear, final float zFar, + final float[] mat4Projection, final float[] mat4Modelview) { + final float[] mat4Tmp1 = new float[16]; + final float[] mat4Tmp2 = new float[16]; + final float[] vec3Tmp1 = new float[3]; + final float[] vec3Tmp2 = new float[3]; + final float[] vec3Tmp3 = new float[3]; + + final EyeParameter eyeParam = eye.getEyeParameter(); + final EyePose eyePose = eye.getLastEyePose(); + + // + // Projection + // + FloatUtil.makePerspective(mat4Projection, 0, true, eyeParam.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, eyePose.position, 0); + VectorUtil.addVec3(shiftedEyePos, shiftedEyePos, eyeParam.positionOffset); + + rollPitchYaw.mult(eyePose.orientation); + final float[] up = rollPitchYaw.rotateVector(vec3Tmp2, 0, VectorUtil.VEC3_UNIT_Y, 0); + final float[] forward = rollPitchYaw.rotateVector(vec3Tmp3, 0, VectorUtil.VEC3_UNIT_Z_NEG, 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, + eyeParam.distNoseToPupilX, + eyeParam.distMiddleToPupilY, + eyeParam.eyeReliefZ); + + /* mat4Modelview = */ FloatUtil.multMatrix(mViewAdjust, mLookAt); + } + +} |