diff options
author | Sven Gothel <[email protected]> | 2015-03-21 04:31:27 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2015-03-21 04:31:27 +0100 |
commit | 32fc8f3a64cfeee8936af98ae49f8e7c8dfe982f (patch) | |
tree | 9f4a250b235bcf6c41bd5c98f19c7e2666e45e57 /src/jogl/classes/com/jogamp/opengl | |
parent | 2c88b6dfd4eb7e2cd9a50fa48e08ecafc980931a (diff) |
Bug 1116 - Add OculusVR DK2 Support - Part-1 (DK2 on DK1 SDK w/o Eye Tracker)
- DK2's screen on X11 (at least) starts in rotated mode,
detect and apply MonitorDevice rotation via NEWT's OpenGL StereoDeviceUtil
- Move StereoDevice.Config -> StereoDeviceConfig
- Expose generic StereoDevice to public: GenericStereoDeviceConfig + GenericStereoDeviceFactory
- GenericStereoDeviceFactory exposes public GenericStereoDeviceConfig creation
for mono, sbs-stereo and lense-sbs-stereo w/ diff. parameters.
- Pass eye surface/texture size for each eye from device to renderer,
instead of assuming unified values.
- Unify GenericStereoDevice.createRenderer(..) and OVRStereoDevice.createRenderer(..) code
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl')
8 files changed, 466 insertions, 19 deletions
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/com/jogamp/opengl/util/stereo/StereoDeviceConfig.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceConfig.java new file mode 100644 index 000000000..c91935e80 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceConfig.java @@ -0,0 +1,33 @@ +/** + * 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; + +/** 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); + } +} |