diff options
18 files changed, 761 insertions, 377 deletions
diff --git a/make/config/oculusvr/oculusvr-ovr-CustomJavaCode.java b/make/config/oculusvr/oculusvr-ovr-CustomJavaCode.java index 281610a20..691a93a16 100644 --- a/make/config/oculusvr/oculusvr-ovr-CustomJavaCode.java +++ b/make/config/oculusvr/oculusvr-ovr-CustomJavaCode.java @@ -10,11 +10,11 @@ } /** No native tool library to load if(!bundle.isToolLibLoaded()) { - System.err.println("Couln't load native OVR/JNI glue library"); + System.err.println("Couldn't load native OVR/JNI glue library"); return null; } */ if(!bundle.isLibComplete()) { - System.err.println("Couln't load native OVR/JNI glue library"); + System.err.println("Couldn't load native OVR/JNI glue library"); return null; } if( !initializeImpl() ) { diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index caa5bfea6..620796960 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -313,7 +313,7 @@ function jrun() { #D_ARGS="-Djogl.debug.StereoDevice" #D_ARGS="-Dnewt.debug.Screen -Djogl.debug.StereoDevice" #D_ARGS="-Dnewt.debug.Screen -Dnewt.test.Screen.disableRandR13" - D_ARGS="-Dnewt.debug.Screen" + #D_ARGS="-Dnewt.debug.Screen" if [ $awton -eq 1 ] ; then export USE_CLASSPATH=$JOGAMP_ALL_AWT_CLASSPATH @@ -411,7 +411,7 @@ function testawtswt() { # # Stereo # -#testnoawt com.jogamp.opengl.test.junit.jogl.stereo.StereoDemo01 $* +testnoawt com.jogamp.opengl.test.junit.jogl.stereo.StereoDemo01 $* # # HiDPI @@ -623,7 +623,7 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode01bNEWT $* #testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode01cNEWT $* #testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode01dNEWT $* -testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode02aNEWT $* +#testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode02aNEWT $* #testnoawt com.jogamp.opengl.test.junit.newt.mm.TestScreenMode02bNEWT $* #testnoawt com.jogamp.opengl.test.junit.newt.mm.ManualScreenMode03aNEWT $* #testnoawt -Djava.awt.headless=true com.jogamp.opengl.test.junit.newt.TestGLWindows01NEWT $* diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java index fad07b026..b1a38ab06 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java @@ -85,9 +85,9 @@ public class StereoClientRenderer implements GLEventListener { this.fboTexs = new TextureAttachment[fboCount]; } - private void initFBOs(final GL gl, final DimensionImmutable size) { + private void initFBOs(final GL gl, final DimensionImmutable[] sizes) { for(int i=0; i<fbos.length; i++) { - fbos[i].init(gl, size.getWidth(), size.getHeight(), numSamples); + fbos[i].init(gl, sizes[i].getWidth(), sizes[i].getHeight(), numSamples); 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]); } @@ -98,7 +98,7 @@ public class StereoClientRenderer implements GLEventListener { fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS); final FBObject ssink = new FBObject(); { - ssink.init(gl, size.getWidth(), size.getHeight(), 0); + ssink.init(gl, sizes[i].getWidth(), sizes[i].getHeight(), 0); ssink.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); } @@ -146,7 +146,9 @@ public class StereoClientRenderer implements GLEventListener { deviceRenderer.init(gl); // We will do some offscreen rendering, setup FBO... - final DimensionImmutable textureSize = deviceRenderer.getTextureCount() > 1 ? deviceRenderer.getSingleSurfaceSize() : deviceRenderer.getTotalSurfaceSize(); + final DimensionImmutable[] textureSize = deviceRenderer.getTextureCount() > 1 ? + deviceRenderer.getEyeSurfaceSize() : + new DimensionImmutable[] { deviceRenderer.getTotalSurfaceSize() }; initFBOs(gl, textureSize); helper.init(drawable, false); diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java index 302e783a2..6d8b85d8e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java @@ -41,11 +41,6 @@ public interface StereoDevice { public static final boolean DEBUG = Debug.debug("StereoDevice"); public static final boolean DUMP_DATA = Debug.isPropertyDefined("jogl.debug.StereoDevice.DumpData", true); - /** Merely a class providing a type-tag for extensions */ - public static class Config { - // NOP - } - /** Return the factory used to create this device. */ public StereoDeviceFactory getFactory(); @@ -62,11 +57,17 @@ public interface StereoDevice { public PointImmutable getPosition(); /** - * Returns the required surface size in pixel. + * Returns the required surface size in pixel + * in target space. */ public DimensionImmutable getSurfaceSize(); /** + * Returns the CCW rotation as required by this display device. + */ + public int getRequiredRotation(); + + /** * Return the device default eye position offset for {@link #createRenderer(int, int, float[], FovHVHalves[], float)}. * <p> * Result is an array of float values for diff --git a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceConfig.java index f2fa74743..c91935e80 100644 --- a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 JogAmp Community. All rights reserved. + * Copyright 2015 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: @@ -25,19 +25,9 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package jogamp.opengl.util.stereo; +package com.jogamp.opengl.util.stereo; -import com.jogamp.opengl.util.stereo.StereoDevice; -import com.jogamp.opengl.util.stereo.StereoDeviceFactory; - -public class GenericStereoDeviceFactory extends StereoDeviceFactory { - - public static boolean isAvailable() { - return true; - } - - @Override - public final StereoDevice createDevice(final int deviceIndex, final StereoDevice.Config config, final boolean verbose) { - return new GenericStereoDevice(this, deviceIndex, config); - } -} +/** Merely a class providing a type-tag for extended configuration. */ +public class StereoDeviceConfig { + // NOP +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java index c4180585c..13aa2e891 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java @@ -42,7 +42,7 @@ import com.jogamp.common.util.ReflectionUtil; */ public abstract class StereoDeviceFactory { private static final String OVRStereoDeviceClazzName = "jogamp.opengl.oculusvr.OVRStereoDeviceFactory"; - private static final String GenericStereoDeviceClazzName = "jogamp.opengl.util.stereo.GenericStereoDeviceFactory"; + private static final String GenericStereoDeviceClazzName = com.jogamp.opengl.util.stereo.generic.GenericStereoDeviceFactory.class.getName(); private static final String isAvailableMethodName = "isAvailable"; /** {@link StereoDevice} type used for {@link StereoDeviceFactory#createFactory(DeviceType) createFactory(type)}. */ @@ -60,9 +60,13 @@ public abstract class StereoDeviceFactory { */ Generic, /** - * OculusVR implementation. + * OculusVR DK1 implementation. */ - OculusVR + OculusVR, + /** + * OculusVR DK2 implementation. + */ + OculusVR_DK2 }; public static StereoDeviceFactory createDefaultFactory() { @@ -80,7 +84,7 @@ public abstract class StereoDeviceFactory { case Default: return createDefaultFactory(); case Generic: className = GenericStereoDeviceClazzName; break; case OculusVR: className = OVRStereoDeviceClazzName; break; - default: throw new InternalError("XXX"); + default: throw new InternalError("Unsupported type "+type); } final ClassLoader cl = StereoDeviceFactory.class.getClassLoader(); return createFactory(cl, className); @@ -95,5 +99,12 @@ public abstract class StereoDeviceFactory { return null; } - public abstract StereoDevice createDevice(final int deviceIndex, final StereoDevice.Config config, final boolean verbose); + /** + * + * @param deviceIndex + * @param config optional custom configuration, matching the implementation, i.e. {@link StereoDeviceConfig.GenericStereoDeviceConfig}. + * @param verbose + * @return + */ + public abstract StereoDevice createDevice(final int deviceIndex, final StereoDeviceConfig config, 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 index 68ae3241b..2078a00a2 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java @@ -155,17 +155,17 @@ public interface StereoDeviceRenderer { public boolean usesSideBySideStereo(); /** - * Returns the unified surface size of one eye's a single image in pixel units. + * Returns the surface size for each eye's a single image in pixel units. */ - public DimensionImmutable getSingleSurfaceSize(); + public DimensionImmutable[] getEyeSurfaceSize(); /** * 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. + * If {@link #usesSideBySideStereo()} the total size spans over both {@link #getEyeSurfaceSize()}, side-by-side. * </p> * <p> - * Otherwise the size is equal to {@link #getSingleSurfaceSize()}. + * Otherwise the size is equal to {@link #getEyeSurfaceSize()}. * </p> */ public DimensionImmutable getTotalSurfaceSize(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java index 3e4e7ccd9..b0ca4ddb2 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java @@ -34,6 +34,36 @@ import com.jogamp.opengl.util.CustomGLEventListener; import com.jogamp.opengl.util.stereo.StereoDeviceRenderer.Eye; public class StereoUtil { + /** + * Return the vertical pupil center from the screen top in the range [0..1]. + * @param screenHeightInMeters + * @param pupilCenterFromScreenTopInMeters + */ + public static float getVertPupilCenterFromTop(final float screenHeightInMeters, final float pupilCenterFromScreenTopInMeters) { + return pupilCenterFromScreenTopInMeters / screenHeightInMeters; + } + + /** + * Return the horizontal pupil center from the left side for both eyes in the range [0..1]. + * <pre> + <-------------left eye------------->| |<-----------right eye--------------> + <------------------------------------screenSizeInMeters.Width-----------------------------------> + <------interpupillaryDistanceInMeters------> + <--centerFromLeftInMeters-> + ^ + center of pupil + * </pre> + * + * @param screenWidthInMeters + * @param interpupillaryDistanceInMeters + */ + public static float[] getHorizPupilCenterFromLeft(final float screenWidthInMeters, final float interpupillaryDistanceInMeters) { + final float visibleWidthOfOneEye = 0.5f * screenWidthInMeters; + final float leftPupilCenterFromLeftInMeters = ( screenWidthInMeters - interpupillaryDistanceInMeters ) * 0.5f; + final float rightPupilCenterFromMiddleInMeters = leftPupilCenterFromLeftInMeters + interpupillaryDistanceInMeters - visibleWidthOfOneEye; + return new float[] { leftPupilCenterFromLeftInMeters / visibleWidthOfOneEye, + rightPupilCenterFromMiddleInMeters / visibleWidthOfOneEye }; + } /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ public static boolean usesBarrelDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_BARREL ) ; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceConfig.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceConfig.java new file mode 100644 index 000000000..04fd8343c --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceConfig.java @@ -0,0 +1,177 @@ +/** + * Copyright 2015 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.generic; + +import java.util.Arrays; + +import jogamp.opengl.util.stereo.DistortionMesh; +import jogamp.opengl.util.stereo.GenericStereoDevice; + +import com.jogamp.nativewindow.util.DimensionImmutable; +import com.jogamp.opengl.util.stereo.EyeParameter; +import com.jogamp.opengl.util.stereo.StereoDeviceConfig; +import com.jogamp.opengl.util.stereo.StereoDeviceRenderer; +import com.jogamp.opengl.util.stereo.StereoUtil; + +/** + * Configuration for {@link GenericStereoDevice}s. + */ +public class GenericStereoDeviceConfig extends StereoDeviceConfig { + public static enum ShutterType { + Global, RollingLeftToRight, RollingRightToLeft, RollingTopToBottom + } + public GenericStereoDeviceConfig(final String name, + final GenericStereoDeviceConfig.ShutterType shutterType, + final DimensionImmutable surfaceSizeInPixels, + final float[] screenSizeInMeters, + final DimensionImmutable[/*pre-eye*/] eyeTextureSize, + final float pupilCenterFromScreenTopInMeters, + final float interpupillaryDistanceInMeters, + final int[] eyeRenderOrder, + final EyeParameter[] defaultEyeParam, + final DistortionMesh.Producer distortionMeshProducer, + final int supportedDistortionBits, + final int recommendedDistortionBits, + final int minimumDistortionBits + ) { + if( eyeRenderOrder.length != defaultEyeParam.length ) { + throw new IllegalArgumentException("eye arrays of different length"); + } + this.name = name; + this.shutterType = shutterType; + this.surfaceSizeInPixels = surfaceSizeInPixels; + this.screenSizeInMeters = screenSizeInMeters; + this.eyeTextureSizes = eyeTextureSize; + this.pupilCenterFromScreenTopInMeters = pupilCenterFromScreenTopInMeters; + this.interpupillaryDistanceInMeters = interpupillaryDistanceInMeters; + this.eyeRenderOrder = eyeRenderOrder; + this.defaultEyeParam = defaultEyeParam; + this.distortionMeshProducer = distortionMeshProducer; + this.supportedDistortionBits = supportedDistortionBits; + this.recommendedDistortionBits = recommendedDistortionBits; + this.minimumDistortionBits = minimumDistortionBits; + this.pupilCenterFromTopLeft = new float[2][2]; + calcPupilCenterFromTopLeft(); + } + /** A variation w/ different surface/screen specs */ + public GenericStereoDeviceConfig(final GenericStereoDeviceConfig source, + final DimensionImmutable surfaceSizeInPixels, + final float[] screenSizeInMeters, + final DimensionImmutable[/*pre-eye*/] eyeTextureSize) { + this.name = source.name; + this.shutterType = source.shutterType; + this.surfaceSizeInPixels = surfaceSizeInPixels; + this.screenSizeInMeters = screenSizeInMeters; + this.eyeTextureSizes = eyeTextureSize; + this.pupilCenterFromScreenTopInMeters = source.pupilCenterFromScreenTopInMeters; + this.interpupillaryDistanceInMeters = source.interpupillaryDistanceInMeters; + this.eyeRenderOrder = source.eyeRenderOrder; + this.defaultEyeParam = source.defaultEyeParam; + this.distortionMeshProducer = source.distortionMeshProducer; + this.supportedDistortionBits = source.supportedDistortionBits; + this.recommendedDistortionBits = source.recommendedDistortionBits; + this.minimumDistortionBits = source.minimumDistortionBits; + this.pupilCenterFromTopLeft = new float[2][2]; + calcPupilCenterFromTopLeft(); + } + private void calcPupilCenterFromTopLeft() { + final float visibleWidthOfOneEye = 0.5f * screenSizeInMeters[0]; + final float leftPupilCenterFromLeftInMeters = ( screenSizeInMeters[0] - interpupillaryDistanceInMeters ) * 0.5f; + final float rightPupilCenterFromMiddleInMeters = leftPupilCenterFromLeftInMeters + interpupillaryDistanceInMeters - visibleWidthOfOneEye; + pupilCenterFromTopLeft[0][0] = leftPupilCenterFromLeftInMeters / visibleWidthOfOneEye; + pupilCenterFromTopLeft[0][1] = pupilCenterFromScreenTopInMeters / screenSizeInMeters[1]; + pupilCenterFromTopLeft[1][0] = rightPupilCenterFromMiddleInMeters / visibleWidthOfOneEye; + pupilCenterFromTopLeft[1][1] = pupilCenterFromTopLeft[0][1]; + } + + /** + * One time lazy initialization before use. + * @see #isInitialized() + */ + public synchronized void init() { + if( !isInitialized ) { + if( null != distortionMeshProducer ) { + final float[] eyeReliefInMeters = new float[defaultEyeParam.length]; + if( 0 < defaultEyeParam.length ) { + eyeReliefInMeters[0] = defaultEyeParam[0].eyeReliefZ; + } + if( 1 < defaultEyeParam.length ) { + eyeReliefInMeters[1] = defaultEyeParam[1].eyeReliefZ; + } + distortionMeshProducer.init(this, eyeReliefInMeters); + } + isInitialized = true; + } + } + /** + * Returns {@code true} if {@link #init() initialized}, otherwise {@code false}. + * @see #init() + */ + public final boolean isInitialized() { return isInitialized; } + + @Override + public String toString() { return "StereoConfig["+name+", shutter "+shutterType+", surfaceSize "+surfaceSizeInPixels+ + ", screenSize "+screenSizeInMeters[0]+" x "+screenSizeInMeters[0]+ + " [m], eyeTexSize "+Arrays.toString(eyeTextureSizes)+", IPD "+interpupillaryDistanceInMeters+ + " [m], eyeParam "+Arrays.toString(defaultEyeParam)+ + ", distortionBits[supported ["+StereoUtil.distortionBitsToString(supportedDistortionBits)+ + "], recommended ["+StereoUtil.distortionBitsToString(recommendedDistortionBits)+ + "], minimum ["+StereoUtil.distortionBitsToString(minimumDistortionBits)+"]]]"; + } + + /** Configuration Name */ + public final String name; + public final GenericStereoDeviceConfig.ShutterType shutterType; + + public final DimensionImmutable surfaceSizeInPixels; + public final float[] screenSizeInMeters; + /** Texture size per eye */ + public final DimensionImmutable[/*pre-eye*/] eyeTextureSizes; + + /** Vertical distance from pupil to screen-top in meters */ + public final float pupilCenterFromScreenTopInMeters; + /** Horizontal interpupillary distance (IPD) in meters */ + public final float interpupillaryDistanceInMeters; + /** + * Pupil center from top left per eye, ranging from [0..1], maybe used to produce FovHVHalves, + * see {@link #getHorizPupilCenterFromLeft(float, float)} and {@link #getVertPupilCenterFromTop(float, float)}. + */ + public final float[/*per-eye*/][/*xy*/] pupilCenterFromTopLeft; + public final int[] eyeRenderOrder; + public final EyeParameter[] defaultEyeParam; + public final DistortionMesh.Producer distortionMeshProducer; + + /** Supported distortion bits, see {@link StereoDeviceRenderer.DISTORTION_BARREL}. */ + public final int supportedDistortionBits; + /** Recommended distortion bits, see {@link StereoDeviceRenderer.DISTORTION_BARREL}. */ + public final int recommendedDistortionBits; + /** Required distortion bits, see {@link StereoDeviceRenderer.DISTORTION_BARREL}. */ + public final int minimumDistortionBits; + + private boolean isInitialized = false; +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceFactory.java new file mode 100644 index 000000000..1d5000c97 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceFactory.java @@ -0,0 +1,193 @@ +/** + * 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.generic; + +import jogamp.opengl.util.stereo.DistortionMesh; +import jogamp.opengl.util.stereo.GenericStereoDevice; + +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.nativewindow.util.Dimension; +import com.jogamp.nativewindow.util.DimensionImmutable; +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.FovHVHalves; +import com.jogamp.opengl.util.stereo.EyeParameter; +import com.jogamp.opengl.util.stereo.StereoDevice; +import com.jogamp.opengl.util.stereo.StereoDeviceConfig; +import com.jogamp.opengl.util.stereo.StereoDeviceFactory; +import com.jogamp.opengl.util.stereo.StereoDeviceRenderer; +import com.jogamp.opengl.util.stereo.StereoUtil; + +public class GenericStereoDeviceFactory extends StereoDeviceFactory { + + /** + * Create a generic monoscopic {@link GenericStereoDeviceConfig generic device config}. + * @param name + * @param surfaceSizeInPixel + * @param screenSizeInMeters + * @param defaultEyePositionOffset + */ + public static GenericStereoDeviceConfig createMono(final String name, + final DimensionImmutable surfaceSizeInPixel, final float[] screenSizeInMeters, + final float[] defaultEyePositionOffset) { + final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; + final float d2r = FloatUtil.PI / 180.0f; + return new GenericStereoDeviceConfig( + name, + GenericStereoDeviceConfig.ShutterType.RollingTopToBottom, + surfaceSizeInPixel, // resolution + screenSizeInMeters, // screenSize [m] + new DimensionImmutable[] { surfaceSizeInPixel }, // eye textureSize + pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] + 0f, // IPD [m] + new int[] { 0 }, // eye order + new EyeParameter[] { + new EyeParameter(0, defaultEyePositionOffset, + // degrees: 45/2 l, 45/2 r, 45/2 * aspect t, 45/2 * aspect b + FovHVHalves.byFovyRadianAndAspect(45f*d2r, 1280f / 800f), + 0f /* distNoseToPupil */, 0f /* verticalDelta */, 0f /* eyeReliefInMeters */) }, + null, // mash producer distortion bits + 0, // supported distortion bits + 0, // recommended distortion bits + 0 // minimum distortion bits + ); + } + + /** + * Create a generic homogenous side-by-side stereoscopic {@link GenericStereoDeviceConfig generic device config}. + * @param name + * @param surfaceSizeInPixel + * @param screenSizeInMeters + * @param interpupillaryDistanceInMeters + * @param fovy + * @param defaultEyePositionOffset + */ + public static GenericStereoDeviceConfig createStereoSBS(final String name, + final DimensionImmutable surfaceSizeInPixel, final float[] screenSizeInMeters, + final float interpupillaryDistanceInMeters, final float fovy, + final float[] defaultEyePositionOffset) { + final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; + final float d2r = FloatUtil.PI / 180.0f; + + final DimensionImmutable eyeTextureSize = new Dimension(surfaceSizeInPixel.getWidth()/2, surfaceSizeInPixel.getHeight()); + final float[] horizPupilCenterFromLeft = StereoUtil.getHorizPupilCenterFromLeft(screenSizeInMeters[0], interpupillaryDistanceInMeters); + final float vertPupilCenterFromTop = StereoUtil.getVertPupilCenterFromTop(screenSizeInMeters[1], pupilCenterFromScreenTopInMeters); + final float aspect = (float)eyeTextureSize.getWidth() / (float)eyeTextureSize.getHeight(); + final FovHVHalves defaultSBSEyeFovLeft = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[0]); + final FovHVHalves defaultSBSEyeFovRight = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[1]); + + return new GenericStereoDeviceConfig( + name, + GenericStereoDeviceConfig.ShutterType.RollingTopToBottom, + surfaceSizeInPixel, // resolution + screenSizeInMeters, // screenSize [m] + new DimensionImmutable[] { eyeTextureSize, eyeTextureSize }, // eye textureSize + pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] + interpupillaryDistanceInMeters, // IPD [m] + new int[] { 0, 1 }, // eye order + new EyeParameter[] { + new EyeParameter(0, defaultEyePositionOffset, defaultSBSEyeFovLeft, + interpupillaryDistanceInMeters/2f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */), + new EyeParameter(1, defaultEyePositionOffset, defaultSBSEyeFovRight, + -interpupillaryDistanceInMeters/2f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */) }, + null, // mash producer distortion bits + 0, // supported distortion bits + 0, // recommended distortion bits + 0 // minimum distortion bits + ); + + } + + /** + * Create a generic lense distorted side-by-side stereoscopic {@link GenericStereoDeviceConfig generic device config}. + * @param name + * @param surfaceSizeInPixel + * @param screenSizeInMeters + * @param interpupillaryDistanceInMeters + * @param fovy + * @param eyeTextureSize + * @param defaultEyePositionOffset + */ + public static GenericStereoDeviceConfig createStereoSBSLense(final String name, + final DimensionImmutable surfaceSizeInPixel, final float[] screenSizeInMeters, + final float interpupillaryDistanceInMeters, final float fovy, + final DimensionImmutable eyeTextureSize, + final float[] defaultEyePositionOffset) { + DistortionMesh.Producer lenseDistMeshProduce = null; + try { + lenseDistMeshProduce = + (DistortionMesh.Producer) + ReflectionUtil.createInstance("jogamp.opengl.oculusvr.stereo.lense.DistortionMeshProducer", GenericStereoDevice.class.getClassLoader()); + } catch (final Throwable t) { + if(StereoDevice.DEBUG) { System.err.println("Caught: "+t.getMessage()); t.printStackTrace(); } + } + if( null == lenseDistMeshProduce ) { + return null; + } + final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; + final float d2r = FloatUtil.PI / 180.0f; + + final float[] horizPupilCenterFromLeft = StereoUtil.getHorizPupilCenterFromLeft(screenSizeInMeters[0], interpupillaryDistanceInMeters); + final float vertPupilCenterFromTop = StereoUtil.getVertPupilCenterFromTop(screenSizeInMeters[1], pupilCenterFromScreenTopInMeters); + final float aspect = (float)eyeTextureSize.getWidth() / (float)eyeTextureSize.getHeight(); + final FovHVHalves defaultSBSEyeFovLeft = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[0]); + final FovHVHalves defaultSBSEyeFovRight = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[1]); + + return new GenericStereoDeviceConfig( + name, + GenericStereoDeviceConfig.ShutterType.RollingTopToBottom, + surfaceSizeInPixel, // resolution + screenSizeInMeters, // screenSize [m] + new DimensionImmutable[] { eyeTextureSize, eyeTextureSize }, // eye textureSize + pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] + interpupillaryDistanceInMeters, // IPD [m] + new int[] { 0, 1 }, // eye order + new EyeParameter[] { + new EyeParameter(0, defaultEyePositionOffset, defaultSBSEyeFovLeft, + interpupillaryDistanceInMeters/2f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */), + new EyeParameter(1, defaultEyePositionOffset, defaultSBSEyeFovRight, + -interpupillaryDistanceInMeters/2f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */) }, + lenseDistMeshProduce, + // supported distortion bits + StereoDeviceRenderer.DISTORTION_BARREL | StereoDeviceRenderer.DISTORTION_CHROMATIC | StereoDeviceRenderer.DISTORTION_VIGNETTE, + // recommended distortion bits + StereoDeviceRenderer.DISTORTION_BARREL | StereoDeviceRenderer.DISTORTION_CHROMATIC | StereoDeviceRenderer.DISTORTION_VIGNETTE, + // minimum distortion bits + StereoDeviceRenderer.DISTORTION_BARREL + ); + } + + + public static boolean isAvailable() { + return true; + } + + @Override + public final StereoDevice createDevice(final int deviceIndex, final StereoDeviceConfig config, final boolean verbose) { + return new GenericStereoDevice(this, deviceIndex, config); + } +} diff --git a/src/jogl/classes/jogamp/opengl/util/stereo/DistortionMesh.java b/src/jogl/classes/jogamp/opengl/util/stereo/DistortionMesh.java index 7a2483121..5270b5956 100644 --- a/src/jogl/classes/jogamp/opengl/util/stereo/DistortionMesh.java +++ b/src/jogl/classes/jogamp/opengl/util/stereo/DistortionMesh.java @@ -28,11 +28,17 @@ package jogamp.opengl.util.stereo; import com.jogamp.opengl.util.stereo.EyeParameter; +import com.jogamp.opengl.util.stereo.generic.GenericStereoDeviceConfig; public class DistortionMesh { public static interface Producer { - /** Initialize */ - void init(final GenericStereoDevice.Config deviceConfig, final float[] eyeReliefInMeters); + /** + * Initialize + * @param deviceConfig + * @param eyeReliefInMeters + * @throws IllegalStateException if already initialized + */ + void init(final GenericStereoDeviceConfig deviceConfig, final float[] eyeReliefInMeters) throws IllegalStateException; /** Distortion Mesh Producer */ DistortionMesh create(final EyeParameter eyeParam, final int distortionBits); diff --git a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java index 69a3f2eda..5bfc775fa 100644 --- a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java +++ b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java @@ -27,304 +27,104 @@ */ package jogamp.opengl.util.stereo; -import java.util.Arrays; - import com.jogamp.nativewindow.util.Dimension; import com.jogamp.nativewindow.util.DimensionImmutable; import com.jogamp.nativewindow.util.Point; import com.jogamp.nativewindow.util.PointImmutable; import com.jogamp.nativewindow.util.Rectangle; import com.jogamp.nativewindow.util.RectangleImmutable; - -import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.FovHVHalves; +import com.jogamp.opengl.util.stereo.StereoDeviceConfig; import com.jogamp.opengl.util.stereo.EyeParameter; import com.jogamp.opengl.util.stereo.StereoDevice; import com.jogamp.opengl.util.stereo.StereoDeviceFactory; import com.jogamp.opengl.util.stereo.StereoDeviceRenderer; -import com.jogamp.opengl.util.stereo.StereoUtil; +import com.jogamp.opengl.util.stereo.generic.GenericStereoDeviceConfig; +import com.jogamp.opengl.util.stereo.generic.GenericStereoDeviceFactory; public class GenericStereoDevice implements StereoDevice { - public static enum ShutterType { - Global, RollingLeftToRight, RollingRightToLeft, RollingTopToBottom - } - public static class Config extends StereoDevice.Config { - public Config(final String name, - final ShutterType shutterType, - final DimensionImmutable surfaceSizeInPixels, - final float[] screenSizeInMeters, - final DimensionImmutable eyeTextureSize, - final float pupilCenterFromScreenTopInMeters, - final float interpupillaryDistanceInMeters, - final int[] eyeRenderOrder, - final EyeParameter[] defaultEyeParam, - final DistortionMesh.Producer distortionMeshProducer, - final int supportedDistortionBits, - final int recommendedDistortionBits, - final int minimumDistortionBits - ) { - this.name = name; - this.shutterType = shutterType; - this.surfaceSizeInPixels = surfaceSizeInPixels; - this.screenSizeInMeters = screenSizeInMeters; - this.eyeTextureSize = eyeTextureSize; - this.pupilCenterFromScreenTopInMeters = pupilCenterFromScreenTopInMeters; - this.interpupillaryDistanceInMeters = interpupillaryDistanceInMeters; - this.eyeRenderOrder = eyeRenderOrder; - this.defaultEyeParam = defaultEyeParam; - this.distortionMeshProducer = distortionMeshProducer; - this.supportedDistortionBits = supportedDistortionBits; - this.recommendedDistortionBits = recommendedDistortionBits; - this.minimumDistortionBits = minimumDistortionBits; - this.pupilCenterFromTopLeft = new float[2][2]; - calcPupilCenterFromTopLeft(); - } - /** A variation w/ different surface/screen specs */ - public Config(final Config source, - final DimensionImmutable surfaceSizeInPixels, - final float[] screenSizeInMeters, - final DimensionImmutable eyeTextureSize) { - this.name = source.name; - this.shutterType = source.shutterType; - this.surfaceSizeInPixels = surfaceSizeInPixels; - this.screenSizeInMeters = screenSizeInMeters; - this.eyeTextureSize = eyeTextureSize; - this.pupilCenterFromScreenTopInMeters = source.pupilCenterFromScreenTopInMeters; - this.interpupillaryDistanceInMeters = source.interpupillaryDistanceInMeters; - this.eyeRenderOrder = source.eyeRenderOrder; - this.defaultEyeParam = source.defaultEyeParam; - this.distortionMeshProducer = source.distortionMeshProducer; - this.supportedDistortionBits = source.supportedDistortionBits; - this.recommendedDistortionBits = source.recommendedDistortionBits; - this.minimumDistortionBits = source.minimumDistortionBits; - this.pupilCenterFromTopLeft = new float[2][2]; - calcPupilCenterFromTopLeft(); - } - private void calcPupilCenterFromTopLeft() { - final float visibleWidthOfOneEye = 0.5f * screenSizeInMeters[0]; - final float leftPupilCenterFromLeftInMeters = ( screenSizeInMeters[0] - interpupillaryDistanceInMeters ) * 0.5f; - final float rightPupilCenterFromMiddleInMeters = leftPupilCenterFromLeftInMeters + interpupillaryDistanceInMeters - visibleWidthOfOneEye; - pupilCenterFromTopLeft[0][0] = leftPupilCenterFromLeftInMeters / visibleWidthOfOneEye; - pupilCenterFromTopLeft[0][1] = pupilCenterFromScreenTopInMeters / screenSizeInMeters[1]; - pupilCenterFromTopLeft[1][0] = rightPupilCenterFromMiddleInMeters / visibleWidthOfOneEye; - pupilCenterFromTopLeft[1][1] = pupilCenterFromTopLeft[0][1]; - } - - /** - * Return the vertical pupil center from the screen top in the range [0..1]. - * @param screenHeightInMeters - * @param pupilCenterFromScreenTopInMeters - */ - public static float getVertPupilCenterFromTop(final float screenHeightInMeters, final float pupilCenterFromScreenTopInMeters) { - return pupilCenterFromScreenTopInMeters / screenHeightInMeters; - } - - /** - * Return the horizontal pupil center from the left side for both eyes in the range [0..1]. - * <pre> - <-------------left eye------------->| |<-----------right eye--------------> - <------------------------------------screenSizeInMeters.Width-----------------------------------> - <------interpupillaryDistanceInMeters------> - <--centerFromLeftInMeters-> - ^ - center of pupil - * </pre> - * - * @param screenWidthInMeters - * @param interpupillaryDistanceInMeters - */ - public static float[] getHorizPupilCenterFromLeft(final float screenWidthInMeters, final float interpupillaryDistanceInMeters) { - final float visibleWidthOfOneEye = 0.5f * screenWidthInMeters; - final float leftPupilCenterFromLeftInMeters = ( screenWidthInMeters - interpupillaryDistanceInMeters ) * 0.5f; - final float rightPupilCenterFromMiddleInMeters = leftPupilCenterFromLeftInMeters + interpupillaryDistanceInMeters - visibleWidthOfOneEye; - return new float[] { leftPupilCenterFromLeftInMeters / visibleWidthOfOneEye, - rightPupilCenterFromMiddleInMeters / visibleWidthOfOneEye }; - } - - void init() { - final float[] eyeReliefInMeters = new float[defaultEyeParam.length]; - if( 0 < defaultEyeParam.length ) { - eyeReliefInMeters[0] = defaultEyeParam[0].eyeReliefZ; - } - if( 1 < defaultEyeParam.length ) { - eyeReliefInMeters[1] = defaultEyeParam[1].eyeReliefZ; - } - if( null != distortionMeshProducer ) { - distortionMeshProducer.init(this, eyeReliefInMeters); - } - } + /** A mono view configuration DK1, only one eye is supported */ + public static final GenericStereoDeviceConfig config01Mono01; + /** A mono view configuration DK2, only one eye is supported */ + public static final GenericStereoDeviceConfig config01Mono02; - @Override - public String toString() { return "StereoConfig["+name+", shutter "+shutterType+", surfaceSize "+surfaceSizeInPixels+ - ", screenSize "+screenSizeInMeters[0]+" x "+screenSizeInMeters[0]+ - " [m], eyeTexSize "+eyeTextureSize+", IPD "+interpupillaryDistanceInMeters+ - " [m], eyeParam "+Arrays.toString(defaultEyeParam)+ - ", distortionBits[supported ["+StereoUtil.distortionBitsToString(supportedDistortionBits)+ - "], recommended ["+StereoUtil.distortionBitsToString(recommendedDistortionBits)+ - "], minimum ["+StereoUtil.distortionBitsToString(minimumDistortionBits)+"]]]"; - } - - /** Configuration Name */ - public final String name; - public final ShutterType shutterType; - - public final DimensionImmutable surfaceSizeInPixels; - public final float[] screenSizeInMeters; - /** Texture size per eye */ - public final DimensionImmutable eyeTextureSize; - - /** Vertical distance from pupil to screen-top in meters */ - public final float pupilCenterFromScreenTopInMeters; - /** Horizontal interpupillary distance (IPD) in meters */ - public final float interpupillaryDistanceInMeters; - /** - * Pupil center from top left per eye, ranging from [0..1], maybe used to produce FovHVHalves, - * see {@link #getHorizPupilCenterFromLeft(float, float)} and {@link #getVertPupilCenterFromTop(float, float)}. - */ - public final float[/*per-eye*/][/*xy*/] pupilCenterFromTopLeft; - public final int[] eyeRenderOrder; - public final EyeParameter[] defaultEyeParam; - public final DistortionMesh.Producer distortionMeshProducer; - - public final int supportedDistortionBits; - public final int recommendedDistortionBits; - public final int minimumDistortionBits; - } - - /** A mono view configuration, only one eye is supported */ - public static final Config config01Mono01; + /** A default stereo SBS view configuration DK1 */ + public static final GenericStereoDeviceConfig config02StereoSBS01; + /** A default stereo SBS view configuration DK2 */ + public static final GenericStereoDeviceConfig config02StereoSBS02; - /** A default stereo SBS view configuration */ - public static final Config config02StereoSBS01; + /** A default stereo SBS lense view configuration DK1, utilizing similar settings as OculusVR DK1 */ + public static final GenericStereoDeviceConfig config03StereoSBSLense01; + /** A default stereo SBS lense view configuration DK2, utilizing similar settings as OculusVR DK1 */ + public static final GenericStereoDeviceConfig config03StereoSBSLense02; - /** A default stereo SBS lense view configuration, utilizing similar settings as OculusVR DK1 */ - public static final Config config03StereoSBSLense01; - - private static final Config[] configs; + private static final GenericStereoDeviceConfig[] configs; static { final float[] DEFAULT_EYE_POSITION_OFFSET_STEREO_LENSES = { 0.0f, 1.6f, -5.0f }; // 1.6 up, 5 forward final float[] DEFAULT_EYE_POSITION_OFFSET_STEREO = { 0.0f, 0.3f, 3.0f }; // 0.3 up, 3 back final float[] DEFAULT_EYE_POSITION_OFFSET_MONO = { 0.0f, 0.0f, 3.0f }; // 3 back - final DimensionImmutable surfaceSizeInPixel = new Dimension(1280, 800); - final float[] screenSizeInMeters = new float[] { 0.1498f, 0.0936f }; - final float interpupillaryDistanceInMeters = 0.0635f; - final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; - final float d2r = FloatUtil.PI / 180.0f; - { - config01Mono01 = new Config( - "Def01Mono01", - ShutterType.RollingTopToBottom, - surfaceSizeInPixel, // resolution - screenSizeInMeters, // screenSize [m] - surfaceSizeInPixel, // eye textureSize - pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] - interpupillaryDistanceInMeters, // IPD [m] - new int[] { 0 }, // eye order - new EyeParameter[] { - new EyeParameter(0, DEFAULT_EYE_POSITION_OFFSET_MONO, - // degrees: 45/2 l, 45/2 r, 45/2 * aspect t, 45/2 * aspect b - FovHVHalves.byFovyRadianAndAspect(45f*d2r, 1280f / 800f), - 0f /* distNoseToPupil */, 0f /* verticalDelta */, 0f /* eyeReliefInMeters */) }, - null, // mash producer distortion bits - 0, // supported distortion bits - 0, // recommended distortion bits - 0 // minimum distortion bits - ); - } + final DimensionImmutable surfaceSizeInPixelDK1 = new Dimension(1280, 800); + final float[] screenSizeInMetersDK1 = new float[] { 0.14976f, 0.0936f }; + final DimensionImmutable eyeTextureSizeDK1 = new Dimension(1122, 1553); - { - final DimensionImmutable eyeTextureSize = new Dimension(surfaceSizeInPixel.getWidth()/2, surfaceSizeInPixel.getHeight()); - final float[] horizPupilCenterFromLeft = Config.getHorizPupilCenterFromLeft(screenSizeInMeters[0], interpupillaryDistanceInMeters); - final float vertPupilCenterFromTop = Config.getVertPupilCenterFromTop(screenSizeInMeters[1], pupilCenterFromScreenTopInMeters); - final float fovy = 45f; - final float aspect = (float)eyeTextureSize.getWidth() / (float)eyeTextureSize.getHeight(); - final FovHVHalves defaultSBSEyeFovLeft = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[0]); - final FovHVHalves defaultSBSEyeFovRight = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[1]); - - config02StereoSBS01 = new Config( - "Def02StereoSBS01", - ShutterType.RollingTopToBottom, - surfaceSizeInPixel, // resolution - screenSizeInMeters, // screenSize [m] - eyeTextureSize, // eye textureSize - pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] - interpupillaryDistanceInMeters, // IPD [m] - new int[] { 0, 1 }, // eye order - new EyeParameter[] { - new EyeParameter(0, DEFAULT_EYE_POSITION_OFFSET_STEREO, defaultSBSEyeFovLeft, - 0.032f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */), - new EyeParameter(1, DEFAULT_EYE_POSITION_OFFSET_STEREO, defaultSBSEyeFovRight, - -0.032f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */) }, - null, // mash producer distortion bits - 0, // supported distortion bits - 0, // recommended distortion bits - 0 // minimum distortion bits - ); - } + final DimensionImmutable surfaceSizeInPixelDK2 = new Dimension(1920, 1080); + final float[] screenSizeInMetersDK2 = new float[] { 0.12576f, 0.07074f }; + final DimensionImmutable eyeTextureSizeDK2 = new Dimension(1182, 1461); - { - DistortionMesh.Producer lenseDistMeshProduce = null; - try { - lenseDistMeshProduce = - (DistortionMesh.Producer) - ReflectionUtil.createInstance("jogamp.opengl.oculusvr.stereo.lense.DistortionMeshProducer", GenericStereoDevice.class.getClassLoader()); - } catch (final Throwable t) { - if(StereoDevice.DEBUG) { System.err.println("Caught: "+t.getMessage()); t.printStackTrace(); } - } + final float interpupillaryDistanceInMeters = 0.0635f; - final DimensionImmutable eyeTextureSize = new Dimension(1122, 1553); - final float[] horizPupilCenterFromLeft = Config.getHorizPupilCenterFromLeft(screenSizeInMeters[0], interpupillaryDistanceInMeters); - final float vertPupilCenterFromTop = Config.getVertPupilCenterFromTop(screenSizeInMeters[1], pupilCenterFromScreenTopInMeters); - final float fovy = 129f; - final float aspect = (float)eyeTextureSize.getWidth() / (float)eyeTextureSize.getHeight(); - final FovHVHalves defaultSBSEyeFovLenseLeft = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[0]); - final FovHVHalves defaultSBSEyeFovLenseRight = FovHVHalves.byFovyRadianAndAspect(fovy * d2r, vertPupilCenterFromTop, aspect, horizPupilCenterFromLeft[1]); - - config03StereoSBSLense01 = null == lenseDistMeshProduce ? null : - new Config( - "Def03StereoSBSLense01", - ShutterType.RollingTopToBottom, - surfaceSizeInPixel, // resolution - screenSizeInMeters, // screenSize [m] - eyeTextureSize, // eye textureSize - pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] - interpupillaryDistanceInMeters, // IPD [m] - new int[] { 0, 1 }, // eye order - new EyeParameter[] { - new EyeParameter(0, DEFAULT_EYE_POSITION_OFFSET_STEREO_LENSES, defaultSBSEyeFovLenseLeft, - 0.032f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */), - new EyeParameter(1, DEFAULT_EYE_POSITION_OFFSET_STEREO_LENSES, defaultSBSEyeFovLenseRight, - -0.032f /* distNoseToPupil */, 0f /* verticalDelta */, 0.010f /* eyeReliefInMeters */) }, - lenseDistMeshProduce, - // supported distortion bits - StereoDeviceRenderer.DISTORTION_BARREL | StereoDeviceRenderer.DISTORTION_CHROMATIC | StereoDeviceRenderer.DISTORTION_VIGNETTE, - // recommended distortion bits - StereoDeviceRenderer.DISTORTION_BARREL | StereoDeviceRenderer.DISTORTION_CHROMATIC | StereoDeviceRenderer.DISTORTION_VIGNETTE, - // minimum distortion bits - StereoDeviceRenderer.DISTORTION_BARREL - ); - } - configs = new Config[] { config01Mono01, config02StereoSBS01, config03StereoSBSLense01 }; + config01Mono01 = GenericStereoDeviceFactory.createMono("Def01Mono01", + surfaceSizeInPixelDK1, screenSizeInMetersDK1, + DEFAULT_EYE_POSITION_OFFSET_MONO); + + config02StereoSBS01 = GenericStereoDeviceFactory.createStereoSBS("Def02StereoSBS01", + surfaceSizeInPixelDK1, screenSizeInMetersDK1, + interpupillaryDistanceInMeters, 45f /* fovy */, + DEFAULT_EYE_POSITION_OFFSET_STEREO); + + config03StereoSBSLense01 = GenericStereoDeviceFactory.createStereoSBSLense("Def03StereoSBSLense01", + surfaceSizeInPixelDK1, screenSizeInMetersDK1, + interpupillaryDistanceInMeters, 129f /* fovy */, + eyeTextureSizeDK1, + DEFAULT_EYE_POSITION_OFFSET_STEREO_LENSES); + + config01Mono02 = GenericStereoDeviceFactory.createMono("Def01Mono02", + surfaceSizeInPixelDK2, screenSizeInMetersDK2, + DEFAULT_EYE_POSITION_OFFSET_MONO); + + config02StereoSBS02 = GenericStereoDeviceFactory.createStereoSBS("Def02StereoSBS02", + surfaceSizeInPixelDK2, screenSizeInMetersDK2, + interpupillaryDistanceInMeters, 45f /* fovy */, + DEFAULT_EYE_POSITION_OFFSET_STEREO); + + config03StereoSBSLense02 = GenericStereoDeviceFactory.createStereoSBSLense("Def03StereoSBSLense02", + surfaceSizeInPixelDK2, screenSizeInMetersDK2, + interpupillaryDistanceInMeters, 129f /* fovy */, + eyeTextureSizeDK2, + DEFAULT_EYE_POSITION_OFFSET_STEREO_LENSES); + + configs = new GenericStereoDeviceConfig[] { + config01Mono01, config02StereoSBS01, config03StereoSBSLense01, + config01Mono02, config02StereoSBS02, config03StereoSBSLense02 }; } private final StereoDeviceFactory factory; public final int deviceIndex; - public final Config config; + public final GenericStereoDeviceConfig config; public final Point surfacePos; private final FovHVHalves[] defaultEyeFov; private boolean sensorsStarted = false; - public GenericStereoDevice(final StereoDeviceFactory factory, final int deviceIndex, final StereoDevice.Config customConfig) { + public GenericStereoDevice(final StereoDeviceFactory factory, final int deviceIndex, final StereoDeviceConfig customConfig) { this.factory = factory; this.deviceIndex = deviceIndex; - if( customConfig instanceof GenericStereoDevice.Config) { - this.config = (GenericStereoDevice.Config) customConfig; + if( customConfig instanceof GenericStereoDeviceConfig) { + this.config = (GenericStereoDeviceConfig) customConfig; } else { final int cfgIdx = Math.min(deviceIndex % 10, configs.length-1); this.config = null != configs[cfgIdx] ? configs[cfgIdx] : config02StereoSBS01; @@ -365,6 +165,8 @@ public class GenericStereoDevice implements StereoDevice { public final DimensionImmutable getSurfaceSize() { return config.surfaceSizeInPixels; } + @Override + public int getRequiredRotation() { return 0; } @Override public float[] getDefaultEyePositionOffset() { @@ -425,7 +227,8 @@ public class GenericStereoDevice implements StereoDevice { @Override public final StereoDeviceRenderer createRenderer(final int distortionBits, final int textureCount, final float[] eyePositionOffset, - final FovHVHalves[] eyeFov, final float pixelsPerDisplayPixel, final int textureUnit) { + final FovHVHalves[] eyeFov, final float pixelsPerDisplayPixel, + final int textureUnit) { final EyeParameter[] eyeParam = new EyeParameter[eyeFov.length]; for(int i=0; i<eyeParam.length; i++) { final EyeParameter defaultEyeParam = config.defaultEyeParam[i]; @@ -436,33 +239,47 @@ public class GenericStereoDevice implements StereoDevice { final boolean usePP = null != config.distortionMeshProducer && 0 != distortionBits; // use post-processing final RectangleImmutable[] eyeViewports = new RectangleImmutable[eyeParam.length]; - final DimensionImmutable eyeTextureSize = config.eyeTextureSize; final DimensionImmutable totalTextureSize; if( 1 < eyeParam.length ) { // Stereo SBS - totalTextureSize = new Dimension(eyeTextureSize.getWidth()*2, eyeTextureSize.getHeight()); + final DimensionImmutable eye0TextureSize = config.eyeTextureSizes[0]; + final DimensionImmutable eye1TextureSize = config.eyeTextureSizes[1]; + final int maxHeight = Math.max(eye0TextureSize.getHeight(), eye1TextureSize.getHeight()); + totalTextureSize = new Dimension(eye0TextureSize.getWidth()+eye1TextureSize.getWidth(), maxHeight); if( 1 == textureCount ) { // validated in ctor below! + // one big texture/FBO, viewport to target space eyeViewports[0] = new Rectangle(0, 0, - eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); - - eyeViewports[1] = new Rectangle(eyeTextureSize.getWidth(), 0, - eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); + eye0TextureSize.getWidth(), + maxHeight); + eyeViewports[1] = new Rectangle(eye0TextureSize.getWidth(), 0, + eye1TextureSize.getWidth(), + maxHeight); + } else if( usePP ) { + // two textures/FBOs w/ postprocessing, which renders textures/FBOs into target space + eyeViewports[0] = new Rectangle(0, 0, + eye0TextureSize.getWidth(), + eye0TextureSize.getHeight()); + eyeViewports[1] = new Rectangle(0, 0, + eye1TextureSize.getWidth(), + eye1TextureSize.getHeight()); } else { - eyeViewports[0] = new Rectangle(0, 0, eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); - if( usePP ) { - eyeViewports[1] = eyeViewports[0]; - } else { - eyeViewports[1] = new Rectangle(eyeTextureSize.getWidth(), 0, - eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); - } + // two textures/FBOs w/o postprocessing, viewport to target space + eyeViewports[0] = new Rectangle(0, 0, + eye0TextureSize.getWidth(), + eye0TextureSize.getHeight()); + eyeViewports[1] = new Rectangle(eye0TextureSize.getWidth(), 0, + eye1TextureSize.getWidth(), + eye1TextureSize.getHeight()); } } else { // Mono - totalTextureSize = eyeTextureSize; - eyeViewports[0] = new Rectangle(0, 0, eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); + final DimensionImmutable eye0TextureSize = config.eyeTextureSizes[0]; + totalTextureSize = eye0TextureSize; + eyeViewports[0] = new Rectangle(0, 0, eye0TextureSize.getWidth(), eye0TextureSize.getHeight()); } - return new GenericStereoDeviceRenderer(this, distortionBits, textureCount, eyePositionOffset, eyeParam, pixelsPerDisplayPixel, textureUnit, - eyeTextureSize, totalTextureSize, eyeViewports); + return new GenericStereoDeviceRenderer(this, distortionBits, textureCount, eyePositionOffset, eyeParam, + pixelsPerDisplayPixel, textureUnit, + config.eyeTextureSizes, totalTextureSize, eyeViewports); } }
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceRenderer.java b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceRenderer.java index 3b9fb9c26..6650342e7 100644 --- a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceRenderer.java +++ b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceRenderer.java @@ -29,6 +29,7 @@ package jogamp.opengl.util.stereo; import java.nio.FloatBuffer; import java.nio.ShortBuffer; +import java.util.Arrays; import com.jogamp.nativewindow.util.Dimension; import com.jogamp.nativewindow.util.DimensionImmutable; @@ -380,7 +381,7 @@ public class GenericStereoDeviceRenderer implements StereoDeviceRenderer { private final GenericEye[] eyes; private final int distortionBits; private final int textureCount; - private final DimensionImmutable singleTextureSize; + private final DimensionImmutable[] eyeTextureSizes; private final DimensionImmutable totalTextureSize; /** if texUnit0 is null: no post-processing */ private final GLUniformData texUnit0; @@ -391,7 +392,7 @@ public class GenericStereoDeviceRenderer implements StereoDeviceRenderer { @Override public String toString() { return "GenericStereo[distortion["+StereoUtil.distortionBitsToString(distortionBits)+ - "], singleSize "+singleTextureSize+ + "], eyeTexSize "+Arrays.toString(eyeTextureSizes)+ ", sbsSize "+totalTextureSize+ ", texCount "+textureCount+", texUnit "+(null != texUnit0 ? texUnit0.intValue() : "n/a")+ ", "+PlatformPropsImpl.NEWLINE+" "+(0 < eyes.length ? eyes[0] : "none")+ @@ -404,13 +405,17 @@ public class GenericStereoDeviceRenderer implements StereoDeviceRenderer { /* pp */ GenericStereoDeviceRenderer(final GenericStereoDevice context, final int distortionBits, final int textureCount, final float[] eyePositionOffset, final EyeParameter[] eyeParam, final float pixelsPerDisplayPixel, final int textureUnit, - final DimensionImmutable singleTextureSize, final DimensionImmutable totalTextureSize, + final DimensionImmutable[] eyeTextureSizes, final DimensionImmutable totalTextureSize, final RectangleImmutable[] eyeViewports) { + if( eyeParam.length != eyeTextureSizes.length || + eyeParam.length != eyeViewports.length ) { + throw new IllegalArgumentException("eye arrays of different length"); + } this.device = context; this.eyes = new GenericEye[eyeParam.length]; this.distortionBits = ( distortionBits | context.getMinimumDistortionBits() ) & context.getSupportedDistortionBits(); final boolean usePP = null != device.config.distortionMeshProducer && 0 != this.distortionBits; - final DimensionImmutable textureSize; + final DimensionImmutable[] textureSizes; if( usePP ) { if( 1 > textureCount || 2 < textureCount ) { @@ -418,19 +423,30 @@ public class GenericStereoDeviceRenderer implements StereoDeviceRenderer { } else { this.textureCount = textureCount; } - this.singleTextureSize = singleTextureSize; + this.eyeTextureSizes = eyeTextureSizes; this.totalTextureSize = totalTextureSize; - textureSize = 1 == textureCount ? totalTextureSize : singleTextureSize; + if( 1 == textureCount ) { + textureSizes = new DimensionImmutable[eyeParam.length]; + for(int i=0; i<eyeParam.length; i++) { + textureSizes[i] = totalTextureSize; + } + } else { + textureSizes = eyeTextureSizes; + } texUnit0 = new GLUniformData("svr_Texture0", textureUnit); } else { this.textureCount = 0; - this.singleTextureSize = zeroSize; + this.eyeTextureSizes = new DimensionImmutable[eyeParam.length]; + textureSizes = new DimensionImmutable[eyeParam.length]; + for(int i=0; i<eyeParam.length; i++) { + this.eyeTextureSizes[i] = zeroSize; + textureSizes[i] = zeroSize; + } this.totalTextureSize = zeroSize; - textureSize = zeroSize; texUnit0 = null; } for(int i=0; i<eyeParam.length; i++) { - eyes[i] = new GenericEye(context, this.distortionBits, eyePositionOffset, eyeParam[i], textureSize, eyeViewports[i]); + eyes[i] = new GenericEye(context, this.distortionBits, eyePositionOffset, eyeParam[i], textureSizes[i], eyeViewports[i]); } sp = null; } @@ -445,7 +461,7 @@ public class GenericStereoDeviceRenderer implements StereoDeviceRenderer { public final boolean usesSideBySideStereo() { return true; } @Override - public final DimensionImmutable getSingleSurfaceSize() { return singleTextureSize; } + public final DimensionImmutable[] getEyeSurfaceSize() { return eyeTextureSizes; } @Override public final DimensionImmutable getTotalSurfaceSize() { return totalTextureSize; } diff --git a/src/newt/classes/com/jogamp/newt/opengl/util/stereo/StereoDeviceUtil.java b/src/newt/classes/com/jogamp/newt/opengl/util/stereo/StereoDeviceUtil.java new file mode 100644 index 000000000..762608fd4 --- /dev/null +++ b/src/newt/classes/com/jogamp/newt/opengl/util/stereo/StereoDeviceUtil.java @@ -0,0 +1,91 @@ +/** + * Copyright 2015 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.newt.opengl.util.stereo; + +import java.util.List; + +import com.jogamp.nativewindow.util.Dimension; +import com.jogamp.nativewindow.util.DimensionImmutable; +import com.jogamp.nativewindow.util.PointImmutable; +import com.jogamp.nativewindow.util.Rectangle; +import com.jogamp.nativewindow.util.RectangleImmutable; + +import com.jogamp.newt.Display; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Screen; +import com.jogamp.newt.util.MonitorModeUtil; +import com.jogamp.opengl.util.stereo.StereoDevice; + +/** + * {@link StereoDevice} NEWT related utilities. + */ +public class StereoDeviceUtil { + /** + * Returns the {@link StereoDevice}'s associated {@link MonitorDevice} or {@code null}, if none is attached. + * <p> + * The returned {@link MonitorDevice}'s {@link Screen}, retrieved via {@link MonitorDevice#getScreen()}, + * has been natively created via {@link Screen#addReference()} and caller shall ensure + * {@link Screen#removeReference()} will be called when no more in use. + * </p> + * <p> + * If {@code adjustRotation} is {@code true} and the {@link StereoDevice} + * {@link StereoDevice#getRequiredRotation() requires rotation}, the {@link MonitorDevice} + * will be rotated. + * </p> + * @param stereoDevice the {@link StereoDevice} + * @param adjustRotation if {@code true} rotate the {@link MonitorDevice} if {@link StereoDevice#getRequiredRotation() required}. + */ + public static MonitorDevice getMonitorDevice(final StereoDevice stereoDevice, final boolean adjustRotation) { + final PointImmutable devicePos = stereoDevice.getPosition(); + final DimensionImmutable deviceRes = stereoDevice.getSurfaceSize(); + final int deviceReqRotation = stereoDevice.getRequiredRotation(); + final RectangleImmutable rect = new Rectangle(devicePos.getX(), devicePos.getY(), 128, 128); + + final Display display = NewtFactory.createDisplay(null); + final Screen screen = NewtFactory.createScreen(display, 0); + screen.addReference(); + final MonitorDevice monitor = screen.getMainMonitor(rect); + if( adjustRotation && 0 != deviceReqRotation ) { + final DimensionImmutable deviceRotRes; + if( 90 == deviceReqRotation || 270 == deviceReqRotation ) { + deviceRotRes = new Dimension(deviceRes.getHeight(), deviceRes.getWidth()); + } else { + deviceRotRes = deviceRes; + } + final List<MonitorMode> mmodes0 = monitor.getSupportedModes(); + final List<MonitorMode> mmodes1 = MonitorModeUtil.filterByResolution(mmodes0, deviceRotRes); + final List<MonitorMode> mmodes2 = MonitorModeUtil.filterByRotation(mmodes1, deviceReqRotation); + if( mmodes2.size() > 0 ) { + monitor.setCurrentMode(mmodes2.get(0)); + } + } + return monitor; + } +} diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java index d0d9f5a85..3dadb94b0 100644 --- a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java +++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java @@ -32,7 +32,6 @@ import com.jogamp.nativewindow.util.DimensionImmutable; import com.jogamp.nativewindow.util.PointImmutable; import com.jogamp.nativewindow.util.Rectangle; import com.jogamp.nativewindow.util.RectangleImmutable; - import com.jogamp.oculusvr.OVR; import com.jogamp.oculusvr.OvrHmdContext; import com.jogamp.oculusvr.ovrEyeRenderDesc; @@ -59,6 +58,12 @@ public class OVRStereoDevice implements StereoDevice { private final int[] eyeRenderOrder; private final int supportedDistortionBits, recommendedDistortionBits, minimumDistortionBits; + private final String deviceName; + private final DimensionImmutable resolution; + private final int requiredRotation; + private final PointImmutable position; + private final int dkVersion; + public OVRStereoDevice(final StereoDeviceFactory factory, final OvrHmdContext nativeContext, final int deviceIndex) { if( null == nativeContext ) { throw new IllegalArgumentException("Passed null nativeContext"); @@ -68,16 +73,30 @@ public class OVRStereoDevice implements StereoDevice { this.deviceIndex = deviceIndex; this.hmdDesc = ovrHmdDesc.create(); OVR.ovrHmd_GetDesc(handle, hmdDesc); - final ovrFovPort[] defaultOVREyeFov = hmdDesc.getDefaultEyeFov(0, new ovrFovPort[hmdDesc.getEyeRenderOrderArrayLength()]); + final ovrFovPort[] defaultOVREyeFov = hmdDesc.getDefaultEyeFov(0, new ovrFovPort[ovrHmdDesc.getEyeRenderOrderArrayLength()]); defaultEyeFov = new FovHVHalves[defaultOVREyeFov.length]; for(int i=0; i<defaultEyeFov.length; i++) { defaultEyeFov[i] = OVRUtil.getFovHV(defaultOVREyeFov[i]); } - eyeRenderOrder = new int[hmdDesc.getEyeRenderOrderArrayLength()]; + eyeRenderOrder = new int[ovrHmdDesc.getEyeRenderOrderArrayLength()]; hmdDesc.getEyeRenderOrder(0, eyeRenderOrder); supportedDistortionBits = OVRUtil.ovrDistCaps2DistBits(hmdDesc.getDistortionCaps()); recommendedDistortionBits = supportedDistortionBits & ~StereoDeviceRenderer.DISTORTION_TIMEWARP; minimumDistortionBits = StereoDeviceRenderer.DISTORTION_BARREL; + + // DK1 delivers unrotated resolution in target orientation + // DK2 delivers rotated resolution in target orientation, monitor screen is rotated 90deg clockwise + deviceName = hmdDesc.getDisplayDeviceNameAsString(); + final ovrSizei res = hmdDesc.getResolution(); + resolution = new Dimension(res.getW(), res.getH()); + if( "OVR0002".equals(deviceName) || "OVR0003".equals(deviceName) ) { + dkVersion = 2; + requiredRotation = 90; + } else { + dkVersion = 1; + requiredRotation = 0; + } + position = OVRUtil.getVec2iAsPoint(hmdDesc.getWindowsPos()); } @Override @@ -88,8 +107,9 @@ public class OVRStereoDevice implements StereoDevice { final StringBuilder sb = new StringBuilder(); sb.append("OVRStereoDevice[product "+hmdDesc.getProductNameAsString()); sb.append(", vendor "+hmdDesc.getManufacturerAsString()); - sb.append(", device "+hmdDesc.getDisplayDeviceNameAsString()); - sb.append(", surfaceSize "+getSurfaceSize()); + sb.append(", device "+deviceName); + sb.append(", DK "+dkVersion); + sb.append(", surfaceSize "+getSurfaceSize()+", reqRotation "+requiredRotation+" ccw-deg"); sb.append(", surfacePos "+getPosition()); sb.append(", distortionBits[supported ["+StereoUtil.distortionBitsToString(getSupportedDistortionBits())+ "], recommended ["+StereoUtil.distortionBitsToString(getRecommendedDistortionBits())+ @@ -103,14 +123,12 @@ public class OVRStereoDevice implements StereoDevice { } @Override - public final PointImmutable getPosition() { - return OVRUtil.getVec2iAsPoint(hmdDesc.getWindowsPos()); - } + public final PointImmutable getPosition() { return position; } @Override - public final DimensionImmutable getSurfaceSize() { - return OVRUtil.getOVRSizei(hmdDesc.getResolution()); - } + public final DimensionImmutable getSurfaceSize() { return resolution; } + @Override + public int getRequiredRotation() { return requiredRotation; } @Override public float[] getDefaultEyePositionOffset() { @@ -182,38 +200,51 @@ public class OVRStereoDevice implements StereoDevice { 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); + final DimensionImmutable eye0TextureSize = OVRUtil.getOVRSizei(OVR.ovrHmd_GetFovTextureSize(handle, OVR.ovrEye_Left, eyeRenderDesc[0].getFov(), pixelsPerDisplayPixel)); + final DimensionImmutable eye1TextureSize = OVRUtil.getOVRSizei(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)); + System.err.println("XXX: recommenedTex0Size "+eye0TextureSize); + System.err.println("XXX: recommenedTex1Size "+eye1TextureSize); } - final int unifiedW = Math.max(recommenedTex0Size.getW(), recommenedTex1Size.getW()); - final int unifiedH = Math.max(recommenedTex0Size.getH(), recommenedTex1Size.getH()); + final int maxWidth = Math.max(eye0TextureSize.getWidth(), eye1TextureSize.getWidth()); + final int maxHeight = Math.max(eye0TextureSize.getHeight(), eye1TextureSize.getHeight()); - final DimensionImmutable singleTextureSize = new Dimension(unifiedW, unifiedH); - final DimensionImmutable totalTextureSize = new Dimension(recommenedTex0Size.getW() + recommenedTex1Size.getW(), unifiedH); + final DimensionImmutable[] eyeTextureSizes = new DimensionImmutable[] { eye0TextureSize, eye1TextureSize }; + final DimensionImmutable totalTextureSize = new Dimension(eye0TextureSize.getWidth() + eye1TextureSize.getWidth(), maxHeight); if( StereoDevice.DEBUG ) { - System.err.println("XXX: textureSize Single "+singleTextureSize); + System.err.println("XXX: textureSize Single "+eyeTextureSizes); System.err.println("XXX: textureSize Total "+totalTextureSize); } - final RectangleImmutable[] eyeRenderViewports = new RectangleImmutable[2]; + final RectangleImmutable[] eyeViewports = 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()); + // one big texture/FBO, viewport to target space + if( false && 2 == dkVersion ) { + eyeViewports[0] = new Rectangle(0, 0, + maxWidth, + eye0TextureSize.getHeight()); + eyeViewports[1] = new Rectangle(0, eye0TextureSize.getHeight(), + maxWidth, + eye1TextureSize.getHeight()); + } else { + eyeViewports[0] = new Rectangle(0, 0, + eye0TextureSize.getWidth(), + maxHeight); + eyeViewports[1] = new Rectangle(eye0TextureSize.getWidth(), 0, + eye1TextureSize.getWidth(), + maxHeight); + } } else { - eyeRenderViewports[0] = new Rectangle(0, 0, - singleTextureSize.getWidth(), - singleTextureSize.getHeight()); - eyeRenderViewports[1] = eyeRenderViewports[0]; + // two textures/FBOs w/ postprocessing, which renders textures/FBOs into target space + // FIXME: DK2 + eyeViewports[0] = new Rectangle(0, 0, + eye0TextureSize.getWidth(), + eye0TextureSize.getHeight()); + eyeViewports[1] = new Rectangle(0, 0, + eye1TextureSize.getWidth(), + eye1TextureSize.getHeight()); } return new OVRStereoDeviceRenderer(this, distortionBits, textureCount, eyePositionOffset, - eyeRenderDesc, singleTextureSize, totalTextureSize, eyeRenderViewports, textureUnit); + eyeRenderDesc, eyeTextureSizes, totalTextureSize, eyeViewports, 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 index 06f716ddc..dfaead1ea 100644 --- a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java +++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java @@ -30,8 +30,8 @@ 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.StereoDeviceConfig; import com.jogamp.opengl.util.stereo.StereoDevice; -import com.jogamp.opengl.util.stereo.StereoDevice.Config; import com.jogamp.opengl.util.stereo.StereoDeviceFactory; public class OVRStereoDeviceFactory extends StereoDeviceFactory { @@ -44,7 +44,7 @@ public class OVRStereoDeviceFactory extends StereoDeviceFactory { } @Override - public final StereoDevice createDevice(final int deviceIndex, final Config config, final boolean verbose) { + public final StereoDevice createDevice(final int deviceIndex, final StereoDeviceConfig config, final boolean verbose) { final OvrHmdContext hmdCtx = OVR.ovrHmd_Create(deviceIndex); if( null == hmdCtx ) { if( verbose ) { diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java index 1e5656129..5da5cbeca 100644 --- a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java +++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceRenderer.java @@ -29,6 +29,7 @@ package jogamp.opengl.oculusvr; import java.nio.FloatBuffer; import java.nio.ShortBuffer; +import java.util.Arrays; import com.jogamp.nativewindow.util.DimensionImmutable; import com.jogamp.nativewindow.util.RectangleImmutable; @@ -375,7 +376,7 @@ public class OVRStereoDeviceRenderer implements StereoDeviceRenderer { private final OVREye[] eyes; private final int distortionBits; private final int textureCount; - private final DimensionImmutable singleTextureSize; + private final DimensionImmutable[] eyeTextureSizes; private final DimensionImmutable totalTextureSize; private final GLUniformData texUnit0; @@ -389,7 +390,7 @@ public class OVRStereoDeviceRenderer implements StereoDeviceRenderer { @Override public String toString() { return "OVRDist[distortion["+StereoUtil.distortionBitsToString(distortionBits)+ - "], singleSize "+singleTextureSize+ + "], eyeTexSize "+Arrays.toString(eyeTextureSizes)+ ", sbsSize "+totalTextureSize+ ", texCount "+textureCount+", texUnit "+getTextureUnit()+ ", "+PlatformPropsImpl.NEWLINE+" "+eyes[0]+", "+PlatformPropsImpl.NEWLINE+" "+eyes[1]+"]"; @@ -412,7 +413,8 @@ public class OVRStereoDeviceRenderer implements StereoDeviceRenderer { /* pp */ OVRStereoDeviceRenderer(final OVRStereoDevice context, final int distortionBits, final int textureCount, final float[] eyePositionOffset, - final ovrEyeRenderDesc[] eyeRenderDescs, final DimensionImmutable singleTextureSize, final DimensionImmutable totalTextureSize, + final ovrEyeRenderDesc[] eyeRenderDescs, + final DimensionImmutable[] eyeTextureSizes, 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); @@ -421,14 +423,21 @@ public class OVRStereoDeviceRenderer implements StereoDeviceRenderer { this.eyes = new OVREye[2]; this.distortionBits = ( distortionBits | context.getMinimumDistortionBits() ) & context.getSupportedDistortionBits(); this.textureCount = textureCount; - this.singleTextureSize = singleTextureSize; + this.eyeTextureSizes = eyeTextureSizes; 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]); + final ovrSizei ovrTexture0Size, ovrTexture1Size; + if( 1 == textureCount ) { + ovrTexture0Size = OVRUtil.createOVRSizei(totalTextureSize); + ovrTexture1Size = ovrTexture0Size; + } else { + ovrTexture0Size = OVRUtil.createOVRSizei(eyeTextureSizes[0]); + ovrTexture1Size = OVRUtil.createOVRSizei(eyeTextureSizes[1]); + } + eyes[0] = new OVREye(context.handle, this.distortionBits, eyePositionOffset, eyeRenderDescs[0], ovrTexture0Size, eyeViewports[0]); + eyes[1] = new OVREye(context.handle, this.distortionBits, eyePositionOffset, eyeRenderDescs[1], ovrTexture1Size, eyeViewports[1]); sp = null; frameTiming = null; } @@ -445,7 +454,7 @@ public class OVRStereoDeviceRenderer implements StereoDeviceRenderer { public final boolean usesSideBySideStereo() { return true; } @Override - public final DimensionImmutable getSingleSurfaceSize() { return singleTextureSize; } + public final DimensionImmutable[] getEyeSurfaceSize() { return eyeTextureSizes; } @Override public final DimensionImmutable getTotalSurfaceSize() { return totalTextureSize; } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java index 4f8fb3bd2..39ca85527 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java @@ -40,9 +40,12 @@ import com.jogamp.opengl.GLProfile; import jogamp.opengl.util.stereo.GenericStereoDevice; import com.jogamp.common.net.Uri; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.Screen; import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.newt.opengl.util.stereo.StereoDeviceUtil; import com.jogamp.opengl.math.FovHVHalves; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSBSStereo; @@ -249,15 +252,21 @@ public class StereoDemo01 { // // // - final GLCapabilities caps = new GLCapabilities(GLProfile.getMaxProgrammable(true /* favorHardwareRasterizer */)); - final GLWindow window = GLWindow.create(caps); - final PointImmutable devicePos = stereoDevice.getPosition(); final DimensionImmutable deviceRes = stereoDevice.getSurfaceSize(); + System.err.println("Device Res "+deviceRes+", reqRotation "+stereoDevice.getRequiredRotation()); + System.err.println("Device Pos "+devicePos); + + final MonitorDevice monitor = StereoDeviceUtil.getMonitorDevice(stereoDevice, true); + final Screen screen = monitor.getScreen(); + + final GLCapabilities caps = new GLCapabilities(GLProfile.getMaxProgrammable(true /* favorHardwareRasterizer */)); + final GLWindow window = GLWindow.create(screen, caps); + if( useStereoScreen ) { window.setPosition(devicePos.getX(), devicePos.getY()); } - window.setSurfaceSize(deviceRes.getWidth(), deviceRes.getHeight()); // might be not correct .. + window.setSurfaceSize(deviceRes.getWidth(), deviceRes.getHeight()); window.setAutoSwapBufferMode(useAutoSwap); window.setUndecorated(true); @@ -361,6 +370,7 @@ public class StereoDemo01 { animator.stop(); } window.destroy(); + screen.removeReference(); // StereoDeviceUtil.getMonitorDevice(stereoDevice, true); stereoDevice.dispose(); } } |