From 38e51e4a5f6f35c658df10f6d48a33e3ffaea2f3 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Mon, 7 Jul 2014 23:46:19 +0200
Subject: Bug 1021: Add GenericStereoDevice* Supporting custom configurations;
 Hook-in oculusvr-sdk java distortion-mesh calculation if available

StereoDeviceFactory support new GenericStereoDeviceFactory, with it's GenericStereoDevice and GenericStereoDeviceRenderer.

GenericStereoDevice maintains different configurations, triggered either by passing a GenericStereoDevice.Config
instance directly or by the device-index parameter:

  - 0: monoscopi device: No post-processing

  - 1: stereoscopic device SBS: No post-processing

  - 2: stereoscopic device SBS + Lenses: Distortion post-processing
       (only available w/ oculusvr-sdk sub-module)

Producing a 'GenericStereoDevice.Config' instance is self containing
and may extend if supporting more device types like top-bottom, interlaced etc.

StereoDemo01 handles all use-cases and may be used as a test-bed
to add and experiment with stereoscopy, devices and settings.
---
 .../opengl/util/stereo/StereoClientRenderer.java   | 40 +++++-----
 .../jogamp/opengl/util/stereo/StereoDevice.java    | 88 +++++++++++++++++++---
 .../opengl/util/stereo/StereoDeviceFactory.java    | 33 ++++++--
 .../opengl/util/stereo/StereoDeviceRenderer.java   | 37 ++++-----
 .../com/jogamp/opengl/util/stereo/StereoUtil.java  |  2 +-
 5 files changed, 140 insertions(+), 60 deletions(-)

(limited to 'src/jogl/classes/com/jogamp/opengl/util/stereo')

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 9f9ebdf2a..dfb676456 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java
@@ -58,7 +58,6 @@ public class StereoClientRenderer implements GLEventListener {
     private final FBObject[] fbos;
     private final int magFilter;
     private final int minFilter;
-    private final boolean usePostprocessing;
 
     private int numSamples;
     private final TextureAttachment[] fboTexs;
@@ -71,7 +70,6 @@ public class StereoClientRenderer implements GLEventListener {
         }
         this.helper = new GLDrawableHelper();
         this.deviceRenderer = deviceRenderer;
-        this.usePostprocessing = deviceRenderer.ppRequired() || deviceRenderer.usesSideBySideStereo() && fboCount > 1;
         this.ownsDevice = ownsDevice;
         this.magFilter = magFilter;
         this.minFilter = minFilter;
@@ -179,26 +177,31 @@ public class StereoClientRenderer implements GLEventListener {
 
         final int fboCount = fbos.length;
         final int displayRepeatFlags;
-        if( 1 == fboCount ) {
+        if( 1 >= fboCount ) {
             displayRepeatFlags = CustomGLEventListener.DISPLAY_DONTCLEAR;
         } else {
             displayRepeatFlags = 0;
         }
 
+        final int[] eyeOrder = deviceRenderer.getDevice().getEyeRenderOrder();
+        final int eyeCount = eyeOrder.length;
+
         // Update eye pos upfront to have same (almost) results
-        deviceRenderer.updateEyePose(0);
-        deviceRenderer.updateEyePose(1);
+        for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) {
+            deviceRenderer.updateEyePose(eyeNum);
+        }
 
         if( 1 == fboCount ) {
             fbos[0].bind(gl);
         }
 
-        for(int eyeNum=0; eyeNum<2; eyeNum++) {
+        for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) {
+            final int eyeName = eyeOrder[eyeNum];
             if( 1 < fboCount ) {
-                fbos[eyeNum].bind(gl);
+                fbos[eyeName].bind(gl);
             }
 
-            final StereoDeviceRenderer.Eye eye = deviceRenderer.getEye(eyeNum);
+            final StereoDeviceRenderer.Eye eye = deviceRenderer.getEye(eyeName);
             final RectangleImmutable viewport = eye.getViewport();
             gl.glViewport(viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight());
 
@@ -213,28 +216,31 @@ public class StereoClientRenderer implements GLEventListener {
             helper.runForAllGLEventListener(drawable, reshapeDisplayAction);
 
             if( 1 < fboCount ) {
-                fbos[eyeNum].unbind(gl);
+                fbos[eyeName].unbind(gl);
             }
         }
+
         if( 1 == fboCount ) {
             fbos[0].unbind(gl);
         }
         // restore viewport
         gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
 
-        if( usePostprocessing ) {
+        if( deviceRenderer.ppAvailable() ) {
             deviceRenderer.ppBegin(gl);
             if( 1 == fboCount ) {
                 fbos[0].use(gl, fboTexs[0]);
-                deviceRenderer.ppBothEyes(gl);
+                for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) {
+                    deviceRenderer.ppOneEye(gl, eyeOrder[eyeNum]);
+                }
                 fbos[0].unuse(gl);
             } else {
-                fbos[0].use(gl, fboTexs[0]);
-                deviceRenderer.ppOneEye(gl, 0);
-                fbos[0].unuse(gl);
-                fbos[1].use(gl, fboTexs[1]);
-                deviceRenderer.ppOneEye(gl, 1);
-                fbos[1].unuse(gl);
+                for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) {
+                    final int eyeName = eyeOrder[eyeNum];
+                    fbos[eyeName].use(gl, fboTexs[eyeName]);
+                    deviceRenderer.ppOneEye(gl, eyeName);
+                    fbos[eyeName].unuse(gl);
+                }
             }
             deviceRenderer.ppEnd(gl);
         }
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 e5c0e3646..2091d0843 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java
@@ -39,14 +39,12 @@ import com.jogamp.opengl.math.FovHVHalves;
  */
 public interface StereoDevice {
     public static final boolean DEBUG = Debug.debug("StereoDevice");
+    public static final boolean DUMP_DATA = Debug.isPropertyDefined("jogl.debug.StereoDevice.DumpData", true);
 
-    /**
-     * Default eye position offset for {@link #createRenderer(int, int, float[], FovHVHalves[], float)}.
-     * <p>
-     * Default offset is 1.6f <i>up</i> and 5.0f <i>away</i>.
-     * </p>
-     */
-    public static final float[] DEFAULT_EYE_POSITION_OFFSET = { 0.0f, 1.6f, -5.0f };
+    /** Merely a class providing a type-tag for extensions */
+    public static class Config {
+        // NOP
+    }
 
     /** Disposes this {@link StereoDevice}. */
     public void dispose();
@@ -66,7 +64,25 @@ public interface StereoDevice {
     public DimensionImmutable getSurfaceSize();
 
     /**
-     * Returns the device default {@link FovHVHalves} per eye.
+     * Return the device default eye position offset for {@link #createRenderer(int, int, float[], FovHVHalves[], float)}.
+     * <p>
+     * Result is an array of float values for
+     * <ul>
+     *   <li><i>right</i> (positive)</li>
+     *   <li><i>up</i> (positive)</li>
+     *   <li><i>forward</i> (negative)</li>
+     * </ul>
+     * </p>
+     * @return
+     */
+    public float[] getDefaultEyePositionOffset();
+
+    /**
+     * Returns the device default {@link FovHVHalves} for all supported eyes
+     * in natural order, i.e. left and right if supported.
+     * <p>
+     * Monoscopic devices return an array length of one, without the value for the right-eye!
+     * </p>
      */
     public FovHVHalves[] getDefaultFOV();
 
@@ -76,13 +92,61 @@ public interface StereoDevice {
     /** Return true if sensors have been started, false otherwise */
     public boolean getSensorsStarted();
 
+    /**
+     * Returns an array of the preferred eye rendering order.
+     * The array length reflects the supported eye count.
+     * <p>
+     * Monoscopic devices only support one eye, where stereoscopic device two eyes.
+     * </p>
+     */
+    public int[] getEyeRenderOrder();
+
+    /**
+     * Returns the supported distortion compensation by the {@link StereoDeviceRenderer},
+     * e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, {@link StereoDeviceRenderer#DISTORTION_CHROMATIC}, etc.
+     * @see StereoDeviceRenderer#getDistortionBits()
+     * @see #createRenderer(int, int, float[], FovHVHalves[], float, int)
+     * @see #getRecommendedDistortionBits()
+     * @see #getMinimumDistortionBits()
+     */
+    public int getSupportedDistortionBits();
+
+    /**
+     * Returns the recommended distortion compensation bits for the {@link StereoDeviceRenderer},
+     * e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, {@link StereoDeviceRenderer#DISTORTION_CHROMATIC}
+     * {@link StereoDeviceRenderer#DISTORTION_VIGNETTE}.
+     * <p>
+     * User shall use the recommended distortion compensation to achieve a distortion free view.
+     * </p>
+     * @see StereoDeviceRenderer#getDistortionBits()
+     * @see #createRenderer(int, int, float[], FovHVHalves[], float, int)
+     * @see #getSupportedDistortionBits()
+     * @see #getMinimumDistortionBits()
+     */
+    public int getRecommendedDistortionBits();
+
+    /**
+     * Returns the minimum distortion compensation bits as required by the {@link StereoDeviceRenderer},
+     * e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL} in case the stereoscopic display uses [a]spherical lenses.
+     * <p>
+     * Minimum distortion compensation bits are being enforced by the {@link StereoDeviceRenderer}.
+     * </p>
+     * @see #getSupportedDistortionBits()
+     * @see #getRecommendedDistortionBits()
+     * @see StereoDeviceRenderer#getDistortionBits()
+     * @see #createRenderer(int, int, float[], FovHVHalves[], float, int)
+     */
+    public int getMinimumDistortionBits();
+
     /**
      * Create a new {@link StereoDeviceRenderer} instance.
      *
-     * @param distortionBits {@link StereoDeviceRenderer} distortion bits, e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, etc.
-     * @param textureCount desired texture count for post-processing, see {@link StereoDeviceRenderer#getTextureCount()} and {@link StereoDeviceRenderer#ppRequired()}
-     * @param eyePositionOffset eye position offset, e.g. {@link #DEFAULT_EYE_POSITION_OFFSET}.
-     * @param eyeFov FovHVHalves[2] field-of-view per eye
+     * @param distortionBits {@link StereoDeviceRenderer} distortion bits, e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, etc,
+     *                       see {@link #getRecommendedDistortionBits()}.
+     * @param textureCount desired texture count for post-processing, see {@link StereoDeviceRenderer#getTextureCount()} and {@link StereoDeviceRenderer#ppAvailable()}
+     * @param eyePositionOffset eye position offset, e.g. {@link #getDefaultEyePositionOffset()}.
+     * @param eyeFov FovHVHalves[] field-of-view per eye, e.g. {@link #getDefaultFOV()}. May contain only one value for monoscopic devices,
+     *               see {@link #getEyeRenderOrder()}.
      * @param pixelsPerDisplayPixel
      * @param textureUnit
      * @return
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 d9054ce28..46ce82f03 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java
@@ -31,26 +31,43 @@ import com.jogamp.common.util.ReflectionUtil;
 
 /**
  * Platform agnostic {@link StereoDevice} factory.
+ * <p>
+ * To implement a new {@link StereoDevice}, the following interfaces/classes must be implemented:
+ * <ul>
+ *   <li>{@link StereoDeviceFactory}</li>
+ *   <li>{@link StereoDevice}</li>
+ *   <li>{@link StereoDeviceRenderer}</li>
+ * </ul>
+ * </p>
  */
 public abstract class StereoDeviceFactory {
     private static final String OVRStereoDeviceClazzName = "jogamp.opengl.oculusvr.OVRStereoDeviceFactory";
-    private static final Object[] ctorArgs;
+    private static final String GenericStereoDeviceClazzName = "jogamp.opengl.util.stereo.GenericStereoDeviceFactory";
     private static final String isAvailableMethodName = "isAvailable";
 
-    static {
-        ctorArgs = new Object[6];
-        ctorArgs[0] = null;
+    public static enum DeviceType { Default, Generic, OculusVR };
 
-    }
     public static StereoDeviceFactory createDefaultFactory() {
         final ClassLoader cl = StereoDeviceFactory.class.getClassLoader();
-        final StereoDeviceFactory sink = createFactory(cl, OVRStereoDeviceClazzName);
+        StereoDeviceFactory sink = createFactory(cl, OVRStereoDeviceClazzName);
         if( null == sink ) {
-            // sink = create(cl, ANYOTHERCLAZZNAME);
+            sink = createFactory(cl, GenericStereoDeviceClazzName);
         }
         return sink;
     }
 
+    public static StereoDeviceFactory createFactory(final DeviceType type) {
+        final String className;
+        switch( type ) {
+            case Default: return createDefaultFactory();
+            case Generic: className = GenericStereoDeviceClazzName; break;
+            case OculusVR: className = OVRStereoDeviceClazzName; break;
+            default: throw new InternalError("XXX");
+        }
+        final ClassLoader cl = StereoDeviceFactory.class.getClassLoader();
+        return createFactory(cl, className);
+    }
+
     public static StereoDeviceFactory createFactory(final ClassLoader cl, final String implName) {
         try {
             if(((Boolean)ReflectionUtil.callStaticMethod(implName, isAvailableMethodName, null, null, cl)).booleanValue()) {
@@ -60,5 +77,5 @@ public abstract class StereoDeviceFactory {
         return null;
     }
 
-    public abstract StereoDevice createDevice(final int deviceIndex, final boolean verbose);
+    public abstract StereoDevice createDevice(final int deviceIndex, final StereoDevice.Config 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 fd94f6bc3..1805b71bc 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java
@@ -41,13 +41,13 @@ import javax.media.opengl.GL;
  *   <li>device.{@link #beginFrame(GL)}</li>
  *   <li>For both eyes:<ul>
  *     <li>device.{@link #updateEyePose(int)}</li>
- *     <li>if device.{@link #ppRequired()}: Set the render target, e.g. FBO</li>
+ *     <li>if device.{@link #ppAvailable()}: Set the render target, e.g. FBO</li>
  *     <li>Set the viewport using {@link Eye#getViewport()}</li>
  *     <li>{@link StereoGLEventListener#reshapeForEye(javax.media.opengl.GLAutoDrawable, int, int, int, int, EyeParameter, EyePose) upstream.reshapeEye(..)}</li>
  *     <li>{@link StereoGLEventListener#display(javax.media.opengl.GLAutoDrawable, int) upstream.display(..)}.</li>
  *   </ul></li>
  *   <li>Reset the viewport</li>
- *   <li>If device.{@link #ppRequired()}:<ul>
+ *   <li>If device.{@link #ppAvailable()}:<ul>
  *     <li>device.{@link #ppBegin(GL)}</li>
  *     <li>Use render target, e.g. FBO's texture</li>
  *     <li>device.{@link #ppBothEyes(GL)} or device.{@link #ppOneEye(GL, int)} for both eyes</li>
@@ -116,10 +116,10 @@ public interface StereoDeviceRenderer {
     public EyePose updateEyePose(final int eyeNum);
 
     /**
-     * Returns distortion compensation bits, e.g. {@link #DISTORTION_BARREL},
+     * Returns used distortion compensation bits, e.g. {@link #DISTORTION_BARREL},
      * in case the stereoscopic display requires such, i.e. in case lenses are utilized.
      * <p>
-     * Distortion requires {@link #ppRequired() post-processing}.
+     * Distortion requires {@link #ppAvailable() post-processing}.
      * </p>
      */
     public int getDistortionBits();
@@ -133,7 +133,7 @@ public interface StereoDeviceRenderer {
      * </p>
      * <p>
      * Either the renderer presents the images <i>side-by-side</i> according to the {@link Eye#getViewport() eye's viewport},
-     * or {@link #ppRequired() post-processing} is utilized to merge {@link #getTextureCount() textures}
+     * or {@link #ppAvailable() post-processing} is utilized to merge {@link #getTextureCount() textures}
      * to a <i>side-by-side</i> configuration.
      * </p>
      */
@@ -156,7 +156,7 @@ public interface StereoDeviceRenderer {
     public DimensionImmutable getTotalSurfaceSize();
 
     /**
-     * Returns the used texture-image count for post-processing, see {@link #ppRequired()}.
+     * Returns the used texture-image count for post-processing, see {@link #ppAvailable()}.
      * <p>
      * In case the renderer does not support multiple textures for post-processing,
      * or no post-processing at all, method returns zero despite the request
@@ -165,7 +165,7 @@ public interface StereoDeviceRenderer {
      */
     public int getTextureCount();
 
-    /** Returns the desired texture-image unit for post-processing, see {@link #ppRequired()}. */
+    /** Returns the desired texture-image unit for post-processing, see {@link #ppAvailable()}. */
     public int getTextureUnit();
 
     /** Initialize OpenGL related resources */
@@ -181,13 +181,12 @@ public interface StereoDeviceRenderer {
     public void endFrame(final GL gl);
 
     /**
-     * Returns <code>true</code> if stereoscopic post-processing is required,
+     * Returns <code>true</code> if stereoscopic post-processing is required and available,
      * otherwise <code>false</code>.
      * <p>
-     * Stereoscopic post-processing is usually required if:
+     * Stereoscopic post-processing is available if:
      * <ul>
-     *   <li>one of the <i>distortion</i> modes are set, i.e. {@link #usesBarrelDistortion()}</li>
-     *   <li>texture-images are being used, see {@link #getTextureCount()}</li>
+     *   <li>one of the <i>distortion</i> bits are set, see {@link #getDistortionBits()}</li>
      * </ul>
      * </p>
      * <p>
@@ -195,15 +194,15 @@ public interface StereoDeviceRenderer {
      * the following post-processing methods must be called to before {@link #endFrame()}:
      * <ul>
      *   <li>{@link #ppBegin(GL)}</li>
-     *   <li>{@link #ppBothEyes(GL)} or {@link #ppOneEye(GL, int)} for both eyes</li>
+     *   <li>{@link #ppOneEye(GL, int)} for both eyes</li>
      *   <li>{@link #ppEnd(GL)}</li>
      * </ul>
      * </p>
      */
-    public boolean ppRequired();
+    public boolean ppAvailable();
 
     /**
-     * Begin stereoscopic post-processing, see {@link #ppRequired()}.
+     * Begin stereoscopic post-processing, see {@link #ppAvailable()}.
      * <p>
      * {@link #updateEyePose(int)} for both eyes must be called upfront
      * when rendering upstream {@link StereoGLEventListener}.
@@ -214,20 +213,14 @@ public interface StereoDeviceRenderer {
     public void ppBegin(final GL gl);
 
     /**
-     * Performs stereoscopic post-processing for both eyes, see {@link #ppRequired()}.
-     * @param gl
-     */
-    public void ppBothEyes(final GL gl);
-
-    /**
-     * Performs stereoscopic post-processing for one eye, see {@link #ppRequired()}.
+     * Performs stereoscopic post-processing for one eye, see {@link #ppAvailable()}.
      * @param gl
      * @param eyeNum
      */
     public void ppOneEye(final GL gl, final int eyeNum);
 
     /**
-     * End stereoscopic post-processing, see {@link #ppRequired()}.
+     * End stereoscopic post-processing, see {@link #ppAvailable()}.
      * @param gl
      */
     public void ppEnd(final GL gl);
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 280d99233..3031013b8 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java
@@ -50,7 +50,7 @@ public class StereoUtil {
         final StringBuilder sb = new StringBuilder();
         if( usesBarrelDistortion(distortionBits) ) {
             if( appendComma ) { sb.append(", "); };
-            sb.append("barrell"); appendComma=true;
+            sb.append("barrel"); appendComma=true;
         }
         if( usesVignetteDistortion(distortionBits) ) {
             if( appendComma ) { sb.append(", "); };
-- 
cgit v1.2.3