From fe47c613e3e07681a5366d6ec3f071fdc4ade65d Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Wed, 9 Apr 2014 09:09:51 +0200
Subject: Bug 801: Region Dirty Update; TextureSequence GLMediaPlayer Fix;
 Blending Fix ;

- Region Dirty Update
  - Split dirty -> ShapeDirty + StateDirty,
    where StateDirty forces re-rendering content
    w/o geometry update as req. for 2-pass mode.

- Fix TextureSequence (GLMediaPlayer) usage in RegionRenderer / GLRegion*
  - handle GL_TEXTURE_EXTERNAL_OES incl. Android ES3 bug
  - inject TextureSequence's shader stubs
  - shader: Use abstract lookup 'texture2D' -> 'gcuTexture2D'
  - flip scaled colorTexBBox if TextureSequence 'tex.getMustFlipVertically()'

  - TODO: Handle multiple TextureSequence shader programs!

- Fix Blending: GLRegion* / RegionRenderer / RenderState
  - Disable/Enable depth-writing w/ blending
  - Region impl. sets proper glBlendFunc*(..),
    i.e. 2-pass:
      - render2FBO: glClearColor(0f, 0f, 0f, 0f)
                    glBlendFuncSeparate(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA, GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA)
      - renderFBO:  glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA)
  - User code shall not set glClearColor(..) for 2-pass anymore

- Graph-UI Demo
  - UIShape:
    - Add MouseGestureListener, combining MouseListener + GestureListener
    - EventDetails -> PointerEventInfo
    - PointerEventInfo contains objPos (ray-intersection) and glWin-pos
    - Toggle:
      - Separate color (on/off) if enabled
      - Toggle on click if enabled

  - SceneUIController
    - Use PinchToZoomGesture and propagete same gesture to UIShape

    - Use AABBox.getRayIntersection(..) using 'real' shape coordinates
      for 1st picking.

    - Use shape PMV for secondary picking (drag, zoom 2-pointer, etc),
      see windowToShapeCoords(..)

    - Sort shapes according to z-value (render: ascending; picking: descending)

    - Only 'drag' if pointerId matches 1st pressed pointer
---
 .../classes/com/jogamp/graph/curve/Region.java     | 48 +++++++------
 .../com/jogamp/graph/curve/opengl/GLRegion.java    |  6 +-
 .../jogamp/graph/curve/opengl/RegionRenderer.java  | 83 +++++++++++++++++-----
 .../com/jogamp/graph/curve/opengl/RenderState.java |  6 +-
 4 files changed, 100 insertions(+), 43 deletions(-)

(limited to 'src/jogl/classes/com/jogamp/graph/curve')

diff --git a/src/jogl/classes/com/jogamp/graph/curve/Region.java b/src/jogl/classes/com/jogamp/graph/curve/Region.java
index 230fa324d..45ad20f1b 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/Region.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/Region.java
@@ -105,9 +105,12 @@ public abstract class Region {
 
     public static final int DEFAULT_TWO_PASS_TEXTURE_UNIT = 0;
 
+    protected static final int DIRTY_SHAPE    = 1 << 0 ;
+    protected static final int DIRTY_STATE    = 1 << 1 ;
+
     private final int renderModes;
     private int quality;
-    private boolean dirty = true;
+    private int dirty = DIRTY_SHAPE | DIRTY_STATE;
     private int numVertices = 0;
     protected final AABBox box = new AABBox();
     protected Frustum frustum = null;
@@ -187,7 +190,7 @@ public abstract class Region {
     public final void setQuality(int q) { quality=q; }
 
     protected void clearImpl() {
-        dirty = true;
+        dirty = DIRTY_SHAPE | DIRTY_STATE;
         numVertices = 0;
         box.reset();
     }
@@ -356,7 +359,7 @@ public abstract class Region {
             // int vertsDupCountV = 0, vertsDupCountT = 0;
             System.err.println("Region.addOutlineShape().X: box "+box);
         }
-        setDirty(true);
+        markShapeDirty();
     }
 
     public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final float[] rgbaColor) {
@@ -371,32 +374,35 @@ public abstract class Region {
     }
 
     /**
-     * Check if this region is dirty. A region is marked dirty when new
-     * Vertices, Triangles, and or Lines are added after a call to update().
-     * <p>
-     * A region is also dirty if other render attributes or parameters are changed!
-     * </p>
-     *
-     * @return true if region is Dirty, false otherwise
-     *
-     * @see update(GL2ES2)
+     * Mark this region's shape dirty, i.e. it's
+     * Vertices, Triangles, and or Lines changed.
      */
-    public final boolean isDirty() {
-        return dirty;
+    public final void markShapeDirty() {
+        dirty |= DIRTY_SHAPE;
+    }
+    /** Returns true if this region's shape are dirty, see {@link #markShapeDirty()}. */
+    public final boolean isShapeDirty() {
+        return 0 != ( dirty & DIRTY_SHAPE ) ;
     }
-
     /**
-     * See {@link #isDirty()}.
+     * Mark this region's state dirty, i.e.
+     * it's render attributes or parameters changed.
      */
-    protected final void setDirty(boolean v) {
-        dirty = v;
+    public final void markStateDirty() {
+        dirty |= DIRTY_STATE;
+    }
+    /** Returns true if this region's state is dirty, see {@link #markStateDirty()}. */
+    public final boolean isStateDirty() {
+        return 0 != ( dirty & DIRTY_STATE ) ;
     }
+
     /**
-     * See {@link #isDirty()}.
+     * See {@link #markShapeDirty()} and {@link #markStateDirty()}.
      */
-    public final void markDirty() {
-        dirty = true;
+    protected final void clearDirtyBits(int v) {
+        dirty &= ~v;
     }
+    protected final int getDirtyBits() { return dirty; }
 
     public String toString() {
         return "Region["+getRenderModeString(this.renderModes)+", q "+quality+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]";
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
index 3c6045a1f..49c1944b1 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
@@ -83,7 +83,7 @@ public abstract class GLRegion extends Region {
 
     /**
      * Updates a graph region by updating the ogl related
-     * objects for use in rendering if {@link #isDirty()}.
+     * objects for use in rendering if {@link #isShapeDirty()}.
      * <p>Allocates the ogl related data and initializes it the 1st time.<p>
      * <p>Called by {@link #draw(GL2ES2, RenderState, int, int, int)}.</p>
      */
@@ -139,11 +139,11 @@ public abstract class GLRegion extends Region {
      * @see RegionRenderer#enable(GL2ES2, boolean)
      */
     public final void draw(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount) {
-        if(isDirty()) {
+        if( isShapeDirty() ) {
             updateImpl(gl);
-            setDirty(false);
         }
         drawImpl(gl, renderer, sampleCount);
+        clearDirtyBits(DIRTY_SHAPE|DIRTY_STATE);
     }
 
     protected abstract void drawImpl(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount);
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
index 7337aca36..a0a318a49 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
@@ -32,15 +32,19 @@ import java.util.Iterator;
 
 import javax.media.opengl.GL;
 import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLES2;
 import javax.media.opengl.GLException;
 import javax.media.opengl.fixedfunc.GLMatrixFunc;
 
 import jogamp.graph.curve.opengl.shader.AttributeNames;
+import jogamp.graph.curve.opengl.shader.UniformNames;
 
 import com.jogamp.opengl.GLExtensions;
 import com.jogamp.opengl.util.glsl.ShaderCode;
 import com.jogamp.opengl.util.glsl.ShaderProgram;
+import com.jogamp.opengl.util.texture.TextureSequence;
 import com.jogamp.opengl.util.PMVMatrix;
+import com.jogamp.common.os.Platform;
 import com.jogamp.common.util.IntObjectHashMap;
 import com.jogamp.graph.curve.Region;
 
@@ -65,10 +69,12 @@ public class RegionRenderer {
 
     /**
      * Default {@link GL#GL_BLEND} <i>enable</i> {@link GLCallback},
-     * turning on the {@link GL#GL_BLEND} state and setting up
-     * {@link GL#glBlendFunc(int, int) glBlendFunc}({@link GL#GL_SRC_ALPHA}, {@link GL#GL_ONE_MINUS_SRC_ALPHA}).
+     * turning-off depth writing via {@link GL#glDepthMask(boolean)} and turning-on the {@link GL#GL_BLEND} state.
      * <p>
-     * Implementation also sets {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}.
+     * Implementation also sets {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint},
+     * which will cause {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method}
+     * to set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function}
+     * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering.
      * </p>
      * @see #create(RenderState, GLCallback, GLCallback)
      * @see #enable(GL2ES2, boolean)
@@ -76,16 +82,17 @@ public class RegionRenderer {
     public static final GLCallback defaultBlendEnable = new GLCallback() {
         @Override
         public void run(final GL gl, final RegionRenderer renderer) {
+            gl.glDepthMask(false);
             gl.glEnable(GL.GL_BLEND);
             gl.glBlendEquation(GL.GL_FUNC_ADD); // default
-            gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
             renderer.rs.setHintMask(RenderState.BITHINT_BLENDING_ENABLED);
         }
     };
 
     /**
      * Default {@link GL#GL_BLEND} <i>disable</i> {@link GLCallback},
-     * simply turning off the {@link GL#GL_BLEND} state.
+     * simply turning-off the {@link GL#GL_BLEND} state
+     * and turning-on depth writing via {@link GL#glDepthMask(boolean)}.
      * <p>
      * Implementation also clears {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}.
      * </p>
@@ -97,6 +104,7 @@ public class RegionRenderer {
         public void run(final GL gl, final RegionRenderer renderer) {
             renderer.rs.clearHintMask(RenderState.BITHINT_BLENDING_ENABLED);
             gl.glDisable(GL.GL_BLEND);
+            gl.glDepthMask(true);
         }
     };
 
@@ -160,7 +168,7 @@ public class RegionRenderer {
      * <p>Shall be called by a {@code draw()} method, e.g. {@link RegionRenderer#draw(GL2ES2, Region, int)}</p>
      *
      * @param gl referencing the current GLContext to which the ShaderState is bound to
-     * @param renderModes TODO
+     * @param renderModes
      * @throws GLException if initialization failed
      */
     public final void init(final GL2ES2 gl, final int renderModes) throws GLException {
@@ -270,6 +278,8 @@ public class RegionRenderer {
     private static String USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n";
     private static String USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n";
     private static String DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT ";
+    private static String MAIN_BEGIN = "void main (void)\n{\n";
+    private static final String gcuTexture2D = "gcuTexture2D";
 
     private String getVersionedShaderName() {
         return "curverenderer01";
@@ -367,14 +377,16 @@ public class RegionRenderer {
      * @param pass1
      * @param quality
      * @param sampleCount
+     * @param colorTexSeq
      * @return true if a new shader program is being used and hence external uniform-data and -location,
      *         as well as the attribute-location must be updated, otherwise false.
      */
     public final boolean useShaderProgram(final GL2ES2 gl, final int renderModes,
-                                          final boolean pass1, final int quality, final int sampleCount) {
+                                          final boolean pass1, final int quality, final int sampleCount, final TextureSequence colorTexSeq) {
         final ShaderModeSelector1 sel1 = pass1 ? ShaderModeSelector1.selectPass1(renderModes) :
                                                  ShaderModeSelector1.selectPass2(renderModes, quality, sampleCount);
         final boolean isTwoPass = Region.isTwoPass( renderModes );
+        final boolean isPass1ColorTexSeq = pass1 && null != colorTexSeq;
         final int shaderKey = sel1.ordinal() | ( HIGH_MASK & renderModes ) | ( isTwoPass ? TWO_PASS_BIT : 0 );
 
         /**
@@ -404,15 +416,40 @@ public class RegionRenderer {
         }
         final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, vertexShaderName, true);
         final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, versionedBaseName+"-segment-head", true);
-        int posVp = rsVp.defaultShaderCustomization(gl, true, true);
+
+        int posVp = 0;
+        int posFp = 0;
+
+        if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) {
+            if( !gl.isExtensionAvailable(GLExtensions.OES_EGL_image_external) ) {
+                throw new GLException(GLExtensions.OES_EGL_image_external+" requested but not available");
+            }
+        }
+        boolean supressGLSLVersionES30 = false;
+        if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) {
+            if( Platform.OSType.ANDROID == Platform.getOSType() && gl.isGLES3() ) {
+                // Bug on Nexus 10, ES3 - Android 4.3, where
+                // GL_OES_EGL_image_external extension directive leads to a failure _with_ '#version 300 es' !
+                //   P0003: Extension 'GL_OES_EGL_image_external' not supported
+                supressGLSLVersionES30 = true;
+            }
+        }
+        posVp = rsVp.defaultShaderCustomization(gl, !supressGLSLVersionES30, true);
         // rsFp.defaultShaderCustomization(gl, true, true);
-        int posFp = rsFp.addGLSLVersion(gl);
-        if( gl.isGLES2() && ! gl.isGLES3() ) {
+        posFp = supressGLSLVersionES30 ? 0 : rsFp.addGLSLVersion(gl);
+        if( isPass1ColorTexSeq ) {
+            posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getRequiredExtensionsShaderStub());
+        }
+        if( pass1 && supressGLSLVersionES30 || ( gl.isGLES2() && !gl.isGLES3() ) ) {
             posFp = rsFp.insertShaderSource(0, posFp, ShaderCode.createExtensionDirective(GLExtensions.OES_standard_derivatives, ShaderCode.ENABLE));
         }
-        final String rsFpDefPrecision =  getFragmentShaderPrecision(gl);
-        if( null != rsFpDefPrecision ) {
-            rsFp.insertShaderSource(0, posFp, rsFpDefPrecision);
+        if( false ) {
+            final String rsFpDefPrecision =  getFragmentShaderPrecision(gl);
+            if( null != rsFpDefPrecision ) {
+                posFp = rsFp.insertShaderSource(0, posFp, rsFpDefPrecision);
+            }
+        } else {
+            posFp = rsFp.addDefaultShaderPrecision(gl, posFp);
         }
         if( Region.hasColorChannel( renderModes ) ) {
             posVp = rsVp.insertShaderSource(0, posVp, USE_COLOR_CHANNEL);
@@ -426,20 +463,35 @@ public class RegionRenderer {
             posFp = rsFp.insertShaderSource(0, posFp, DEF_SAMPLE_COUNT+sel1.sampleCount+"\n");
         }
 
+        final String texLookupFuncName;
+        if( isPass1ColorTexSeq ) {
+            posFp = rsFp.insertShaderSource(0, posFp, "uniform "+colorTexSeq.getTextureSampler2DType()+" "+UniformNames.gcu_ColorTexUnit+";\n");
+            texLookupFuncName = colorTexSeq.getTextureLookupFunctionName(gcuTexture2D);
+            posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getTextureLookupFragmentShaderImpl());
+        } else {
+            texLookupFuncName = null;
+        }
+
+        posFp = rsFp.insertShaderSource(0, -1, MAIN_BEGIN);
+
         final String passS = pass1 ? "-pass1-" : "-pass2-";
         final String shaderSegment = versionedBaseName+passS+sel1.tech+sel1.sub+".glsl";
         if(DEBUG) {
             System.err.printf("RegionRendererImpl01.useShaderProgram.1: segment %s%n", shaderSegment);
         }
         try {
-            posFp = rsFp.insertShaderSource(0, -1, AttributeNames.class, shaderSegment);
+            posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, shaderSegment);
         } catch (IOException ioe) {
             throw new RuntimeException("Failed to read: "+shaderSegment, ioe);
         }
         if( 0 > posFp ) {
             throw new RuntimeException("Failed to read: "+shaderSegment);
         }
-        posFp = rsFp.insertShaderSource(0, -1, "}\n");
+        posFp = rsFp.insertShaderSource(0, posFp, "}\n");
+
+        if( isPass1ColorTexSeq ) {
+            rsFp.replaceInShaderSource(gcuTexture2D, texLookupFuncName);
+        }
 
         sp = new ShaderProgram();
         sp.add(rsVp);
@@ -461,5 +513,4 @@ public class RegionRenderer {
         }
         return true;
     }
-
 }
\ No newline at end of file
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java
index 818526cd7..7eb34a62d 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java
@@ -53,9 +53,9 @@ public class RenderState {
      * Shall be set via {@link #setHintMask(int)} and cleared via {@link #clearHintMask(int)}.
      * </p>
      * <p>
-     * Due to alpha blending and multipass rendering, e.g. {@link Region#VBAA_RENDERING_BIT},
-     * the clear-color shall be set to the {@link #getColorStaticUniform() foreground color} and <i>zero alpha</i>,
-     * otherwise blending will amplify the scene's clear-color.
+     * If set, {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method}
+     * will set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function}
+     * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering.
      * </p>
      * <p>
      * Shall be called by custom code, e.g. via {@link RegionRenderer}'s
-- 
cgit v1.2.3