commit 89de975ce511731556f3160d67c5841dcf198579
Author: leszek <leszek@koltunski.pl>
Date:   Mon May 15 22:00:45 2017 +0100

    Add possibility to create FBOs with combined DEPTH/STENCIL.

diff --git a/src/main/java/org/distorted/library/DistortedFramebuffer.java b/src/main/java/org/distorted/library/DistortedFramebuffer.java
index a00605f..099a231 100644
--- a/src/main/java/org/distorted/library/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/DistortedFramebuffer.java
@@ -33,7 +33,6 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Must be called from a thread holding OpenGL Context
-// Watch out - this has the side-effect of binding a Texture and a Framebuffer!
 
   void create()
     {
@@ -46,32 +45,45 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
       GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
       GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
       GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, mWidth, mHeight, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
+      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
 
       GLES30.glGenFramebuffers(1, mFBOH, 0);
       GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[0]);
       GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorH[0], 0);
+      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
 
       mColorCreated = checkStatus("color");
       }
-    if( mDepthCreated==NOT_CREATED_YET ) // we need to create a new DEPTH attachment
+    if( mDepthStencilCreated==NOT_CREATED_YET ) // we need to create a new DEPTH or STENCIL attachment
       {
-      GLES30.glGenTextures(1, mDepthH, 0);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthH[0]);
+      GLES30.glGenTextures(1, mDepthStencilH, 0);
+      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthStencilH[0]);
       GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT);
       GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_REPEAT);
       GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
       GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST);
-      GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH_COMPONENT, mWidth, mHeight, 0, GLES30.GL_DEPTH_COMPONENT, GLES30.GL_UNSIGNED_SHORT, null);
 
+      if( mDepthStencil==DEPTH_NO_STENCIL )
+        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH_COMPONENT, mWidth, mHeight, 0, GLES30.GL_DEPTH_COMPONENT, GLES30.GL_UNSIGNED_SHORT, null);
+      else if( mDepthStencil==BOTH_DEPTH_STENCIL )
+        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH24_STENCIL8, mWidth, mHeight, 0, GLES30.GL_DEPTH_STENCIL, GLES30.GL_UNSIGNED_SHORT, null);
+
+      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
       GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[0]);
-      GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthH[0], 0);
 
-      mDepthCreated = checkStatus("depth");
+      if( mDepthStencil==DEPTH_NO_STENCIL )
+        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthStencilH[0], 0);
+      else if( mDepthStencil==BOTH_DEPTH_STENCIL )
+        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthStencilH[0], 0);
+
+      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
+
+      mDepthStencilCreated = checkStatus("depth");
       }
-    if( mDepthCreated==DONT_CREATE && mDepthH[0]>0 ) // we need to detach and recreate the DEPTH attachment.
+    if( mDepthStencilCreated==DONT_CREATE && mDepthStencilH[0]>0 ) // we need to detach and recreate the DEPTH attachment.
       {
-      GLES30.glDeleteTextures(1, mDepthH, 0);
-      mDepthH[0]=0;
+      GLES30.glDeleteTextures(1, mDepthStencilH, 0);
+      mDepthStencilH[0]=0;
       }
     }
 
@@ -86,7 +98,7 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
       android.util.Log.e("DistortedFramebuffer", "FRAMEBUFFER INCOMPLETE, "+message+" error="+status);
 
       GLES30.glDeleteTextures(1, mColorH, 0);
-      GLES30.glDeleteTextures(1, mDepthH, 0);
+      GLES30.glDeleteTextures(1, mDepthStencilH, 0);
       GLES30.glDeleteFramebuffers(1, mFBOH, 0);
       mFBOH[0]= 0;
 
@@ -103,11 +115,11 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
     {
     if( mColorH[0]>0 )
       {
-      if( mDepthH[0]>0 )
+      if( mDepthStencilH[0]>0 )
         {
-        GLES30.glDeleteTextures(1, mDepthH, 0);
-        mDepthH[0]=0;
-        mDepthCreated = NOT_CREATED_YET;
+        GLES30.glDeleteTextures(1, mDepthStencilH, 0);
+        mDepthStencilH[0]=0;
+        mDepthStencilCreated = NOT_CREATED_YET;
         }
 
       GLES30.glDeleteTextures(1, mColorH, 0);
@@ -129,10 +141,10 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
       mColorCreated = NOT_CREATED_YET;
       mColorH[0] = 0;
       }
-    if( mDepthCreated!=DONT_CREATE )
+    if( mDepthStencilCreated!=DONT_CREATE )
       {
-      mDepthCreated = NOT_CREATED_YET;
-      mDepthH[0] = 0;
+      mDepthStencilCreated = NOT_CREATED_YET;
+      mDepthStencilH[0] = 0;
       }
     }
 
@@ -142,9 +154,9 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
 // inside a Tree of DistortedNodes (TREE)
 // SYSTEM surfaces do not get removed in onDestroy().
 
-  DistortedFramebuffer(boolean depthEnabled, int type, int width, int height)
+  DistortedFramebuffer(int depthStencil, int type, int width, int height)
     {
-    super(width,height,NOT_CREATED_YET, (depthEnabled ? NOT_CREATED_YET:DONT_CREATE),NOT_CREATED_YET, type);
+    super(width,height,NOT_CREATED_YET,depthStencil,NOT_CREATED_YET, type);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -152,10 +164,10 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
 
   boolean setAsDepth()
     {
-    if( mDepthH[0]>0 )
+    if( mDepthStencilH[0]>0 )
       {
       GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthH[0]);
+      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthStencilH[0]);
       return true;
       }
 
@@ -170,18 +182,19 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
  *
  * @param width Width of the COLOR attachment.
  * @param height Height of the COLOR attachment.
- * @param depthEnabled Add DEPTH attachment?
+ * @param depthStencil Add DEPTH or STENCIL attachment?
+ *                     Valid values: NO_DEPTH_STENCIL, DEPTH_NO_STENCIL, BOTH_DEPTH_STENCIL.
  */
   @SuppressWarnings("unused")
-  public DistortedFramebuffer(int width, int height, boolean depthEnabled)
+  public DistortedFramebuffer(int width, int height, int depthStencil)
     {
-    super(width,height,NOT_CREATED_YET,(depthEnabled ? NOT_CREATED_YET:DONT_CREATE),NOT_CREATED_YET,TYPE_USER);
+    super(width,height,NOT_CREATED_YET,depthStencil,NOT_CREATED_YET,TYPE_USER);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /**
- * Create a new offscreen Framebuffer.
+ * Create a new offscreen Framebuffer. No DEPTH or STENCIL buffer will be created.
  *
  * @param width Width of the COLOR attachment.
  * @param height Height of the COLOR attachment.
@@ -189,7 +202,7 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
   @SuppressWarnings("unused")
   public DistortedFramebuffer(int width, int height)
     {
-    super(width,height,NOT_CREATED_YET,DONT_CREATE,NOT_CREATED_YET,TYPE_USER);
+    super(width,height,NOT_CREATED_YET,NO_DEPTH_STENCIL,NOT_CREATED_YET,TYPE_USER);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -210,6 +223,40 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
     return false;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Enable.disable DEPTH and STENCIL buffers.
+ *
+ * @param depthStencil Valid values: NO_DEPTH_STENCIL, DEPTH_NO_STENCIL, BOTH_DEPTH_STENCIL.
+ */
+  public void enableDepthStencil(int depthStencil)
+    {
+    if( depthStencil!=NO_DEPTH_STENCIL && mDepthStencilCreated==DONT_CREATE )
+      {
+      mDepthStencilCreated = NOT_CREATED_YET;
+      mDepthStencil = depthStencil;
+
+      if( mBuffer1[0]!=null )
+        {
+        for(int i=0; i<EffectQuality.LENGTH; i++) mBuffer1[i].enableDepthStencil(depthStencil);
+        }
+
+      markForCreation();
+      }
+    if( depthStencil==NO_DEPTH_STENCIL && mDepthStencilCreated!=DONT_CREATE )
+      {
+      mDepthStencilCreated = DONT_CREATE;
+      mDepthStencil = depthStencil;
+
+      if( mBuffer1[0]!=null )
+        {
+        for(int i=0; i<EffectQuality.LENGTH; i++) mBuffer1[i].enableDepthStencil(depthStencil);
+        }
+
+      markForCreation();
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Return the ID of the Texture (COLOR attachment 0) that's backing this FBO.
diff --git a/src/main/java/org/distorted/library/DistortedNode.java b/src/main/java/org/distorted/library/DistortedNode.java
index 7088d50..f50b866 100644
--- a/src/main/java/org/distorted/library/DistortedNode.java
+++ b/src/main/java/org/distorted/library/DistortedNode.java
@@ -245,7 +245,7 @@ public class DistortedNode implements DistortedSlave
       }
     else if( createNewFBO )
       {
-      newData.mFBO = new DistortedFramebuffer(true, DistortedSurface.TYPE_TREE, mSurface.getWidth(),mSurface.getHeight());
+      newData.mFBO = new DistortedFramebuffer(DistortedFramebuffer.DEPTH_NO_STENCIL, DistortedSurface.TYPE_TREE, mSurface.getWidth(),mSurface.getHeight());
       //android.util.Log.d("NODE", "creating new FBO "+newData.mFBO.getID() );
       }
 
@@ -289,7 +289,7 @@ public class DistortedNode implements DistortedSlave
 
       if( mData.mFBO==null )
         {
-        mData.mFBO = new DistortedFramebuffer(true, DistortedSurface.TYPE_TREE, mSurface.getWidth(),mSurface.getHeight());
+        mData.mFBO = new DistortedFramebuffer(DistortedFramebuffer.DEPTH_NO_STENCIL, DistortedSurface.TYPE_TREE, mSurface.getWidth(),mSurface.getHeight());
         }
 
       mData.mFBO.setAsOutput(currTime);
@@ -402,8 +402,15 @@ public class DistortedNode implements DistortedSlave
         }
       else if( node.mSurface instanceof DistortedFramebuffer )
         {
-        boolean hasDepth = ((DistortedFramebuffer) node.mSurface).hasDepth();
-        mSurface = new DistortedFramebuffer(hasDepth,DistortedSurface.TYPE_TREE,w,h);
+        int depthStencil = DistortedFramebuffer.NO_DEPTH_STENCIL;
+
+        if( ((DistortedFramebuffer) node.mSurface).hasDepth() )
+          {
+          boolean hasStencil = ((DistortedFramebuffer) node.mSurface).hasStencil();
+          depthStencil = (hasStencil ? DistortedFramebuffer.BOTH_DEPTH_STENCIL:DistortedFramebuffer.DEPTH_NO_STENCIL);
+          }
+
+        mSurface = new DistortedFramebuffer(depthStencil,DistortedSurface.TYPE_TREE,w,h);
         }
       }
     if( (flags & Distorted.CLONE_CHILDREN) != 0 )
diff --git a/src/main/java/org/distorted/library/DistortedOutputSurface.java b/src/main/java/org/distorted/library/DistortedOutputSurface.java
index b9bb335..7247c5f 100644
--- a/src/main/java/org/distorted/library/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/DistortedOutputSurface.java
@@ -27,6 +27,19 @@ import java.util.ArrayList;
 
 abstract class DistortedOutputSurface extends DistortedSurface implements DistortedSlave
 {
+/**
+ * Do not create DEPTH or STENCIL attachment
+ */
+  public static final int NO_DEPTH_STENCIL = 0;
+/**
+ * Create DEPTH, but not STENCIL
+ */
+  public static final int DEPTH_NO_STENCIL = 1;
+/**
+ * Create both DEPTH and STENCIL
+ */
+  public static final int BOTH_DEPTH_STENCIL= 2;
+
   private static final int ATTACH = 0;
   private static final int DETACH = 1;
   private static final int DETALL = 2;
@@ -58,9 +71,10 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
   float mDistance, mNear;
   float[] mProjectionMatrix;
 
-  int mDepthCreated;
-  int[] mDepthH = new int[1];
-  int[] mFBOH   = new int[1];
+  int mDepthStencilCreated;
+  int mDepthStencil;
+  int[] mDepthStencilH = new int[1];
+  int[] mFBOH          = new int[1];
 
   private float mClearR, mClearG, mClearB, mClearA;
   private float mClearDepth;
@@ -72,7 +86,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  DistortedOutputSurface(int width, int height, int createColor, int createDepth, int fbo, int type)
+  DistortedOutputSurface(int width, int height, int createColor, int depthStencil, int fbo, int type)
     {
     super(width,height,createColor,type);
 
@@ -81,9 +95,11 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
     mFOV = 60.0f;
     mNear=  0.5f;
 
-    mDepthCreated= createDepth;
-    mFBOH[0]     = fbo;
-    mDepthH[0]   = 0;
+    mDepthStencilCreated= (depthStencil==NO_DEPTH_STENCIL ? DONT_CREATE:NOT_CREATED_YET);
+    mDepthStencil = depthStencil;
+
+    mFBOH[0]         = fbo;
+    mDepthStencilH[0]= 0;
 
     mTime = 0;
 
@@ -180,9 +196,9 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 
           for(int j=0; j<EffectQuality.LENGTH; j++)
             {
-            mBuffer1[j] = new DistortedFramebuffer( mDepthCreated!=DONT_CREATE, DistortedObject.TYPE_SYST,
+            mBuffer1[j] = new DistortedFramebuffer( mDepthStencil   , DistortedObject.TYPE_SYST,
                                                     (int)(mWidth*mipmap), (int)(mHeight*mipmap) );
-            mBuffer2[j] = new DistortedFramebuffer( false                     , DistortedObject.TYPE_SYST,
+            mBuffer2[j] = new DistortedFramebuffer( NO_DEPTH_STENCIL, DistortedObject.TYPE_SYST,
                                                     (int)(mWidth*mipmap), (int)(mHeight*mipmap) );
             mBuffer1[j].mMipmap = mipmap;
             mipmap *= EffectQuality.MULTIPLIER;
@@ -431,34 +447,24 @@ if( !sNew.equals(sOld) )
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Create a new DEPTH buffer and attach it or (param=false) detach an existing DEPTH attachment and recreate it.
+ * Return true if the Surface contains a DEPTH attachment.
  *
- * @param enable <bold>true</bold> if we want to attach a new DEPTH buffer to the FBO.<br>
- *               <bold>false</bold> if we want to detach the DEPTH attachment.
+ * @return <bold>true</bold> if the Surface contains a DEPTH attachment.
  */
-  public void enableDepth(boolean enable)
+  public boolean hasDepth()
     {
-    if( enable && mDepthCreated==DONT_CREATE )
-      {
-      mDepthCreated = NOT_CREATED_YET;
-      markForCreation();
-      }
-    if( !enable && mDepthCreated!=DONT_CREATE )
-      {
-      mDepthCreated = DONT_CREATE;
-      markForCreation();
-      }
+    return mDepthStencilCreated==CREATED;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Return true if the Surface contains a DEPTH attachment.
+ * Return true if the Surface contains a STENCIL attachment.
  *
- * @return <bold>true</bold> if the FBO contains a DEPTH attachment.
+ * @return <bold>true</bold> if the Surface contains a STENCIL attachment.
  */
-  public boolean hasDepth()
+  public boolean hasStencil()
     {
-    return mDepthCreated==CREATED;
+    return (mDepthStencilCreated==CREATED && mDepthStencil==BOTH_DEPTH_STENCIL);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/DistortedScreen.java b/src/main/java/org/distorted/library/DistortedScreen.java
index 7f3afe8..05f2609 100644
--- a/src/main/java/org/distorted/library/DistortedScreen.java
+++ b/src/main/java/org/distorted/library/DistortedScreen.java
@@ -25,7 +25,6 @@ import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ConfigurationInfo;
 import android.opengl.GLSurfaceView;
-import android.view.SurfaceView;
 
 /**
  * Class which represents the Screen.
@@ -54,7 +53,7 @@ public class DistortedScreen extends DistortedOutputSurface
     {
     // set color to 'DONT_CREATE' so that Screens will not get added to the Surface lists
     // set depth to 'CREATED' so that depth will be, by default, on.
-    super(0,0,DONT_CREATE,CREATED,0,TYPE_USER);
+    super(0,0,DONT_CREATE,DEPTH_NO_STENCIL,0,TYPE_USER);
 
     if( view!=null )
       {
