commit d6e94c84ac681abcfcc6e8a28ae480246d89ea82
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Thu Jan 5 02:23:16 2017 +0000

    progress with Postprocessing.

diff --git a/src/main/java/org/distorted/library/Distorted.java b/src/main/java/org/distorted/library/Distorted.java
index be932b2..701d6fa 100644
--- a/src/main/java/org/distorted/library/Distorted.java
+++ b/src/main/java/org/distorted/library/Distorted.java
@@ -20,6 +20,7 @@
 package org.distorted.library;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.opengl.GLES20;
 import org.distorted.library.program.*;
 
@@ -62,15 +63,24 @@ public class Distorted
    * This way we can have two different DistortedEffects sharing the FRAGMENT queue.
    */
   public static final int CLONE_FRAGMENT= 0x8;
+   /**
+   * When creating an instance of a DistortedEffects from another instance, clone the PostProcess Effects.
+   * <p>
+   * This way we can have two different DistortedEffects sharing the POSTPROCESS queue.
+   */
+  public static final int CLONE_POSTPROCESS= 0x10;
   /**
    * When creating an instance of a DistortedTree from another instance, clone the children Nodes.
    * <p>
    * This is mainly useful for creating many similar sub-trees and rendering then at different places
    * on the screen with (optionally) different Effects.
    */
-  public static final int CLONE_CHILDREN= 0x10;
+  public static final int CLONE_CHILDREN= 0x20;
 
   static int[] mMainProgramAttributes;
+  static int[] mPostProgramAttributes;
+
+  static int mainProgramH, postProgramH;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -102,13 +112,15 @@ public class Distorted
  */
   public static void onCreate(final Context context)
   throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
-    { 
-    final InputStream vertexStream   = context.getResources().openRawResource(R.raw.main_vertex_shader);
-    final InputStream fragmentStream = context.getResources().openRawResource(R.raw.main_fragment_shader);
+    {
+    final Resources resources = context.getResources();
 
-    DistortedProgram mainProgram = new DistortedProgram(vertexStream,fragmentStream);
-    int programH = mainProgram.getProgramHandle();
-    GLES20.glUseProgram(programH);
+    final InputStream mainVertexStream   = resources.openRawResource(R.raw.main_vertex_shader);
+    final InputStream mainFragmentStream = resources.openRawResource(R.raw.main_fragment_shader);
+
+    DistortedProgram mainProgram = new DistortedProgram(mainVertexStream,mainFragmentStream);
+    mainProgramH = mainProgram.getProgramHandle();
+    GLES20.glUseProgram(mainProgramH);
     mainProgram.bindAndEnableAttributes();
     mMainProgramAttributes = mainProgram.getAttributes();
 
@@ -119,10 +131,21 @@ public class Distorted
     GLES20.glCullFace(GLES20.GL_BACK);
     GLES20.glFrontFace(GLES20.GL_CW);
 
-    EffectQueueFragment.getUniforms(programH);
-    EffectQueueVertex.getUniforms(programH);
-    EffectQueueMatrix.getUniforms(programH);
-    DistortedTexture.getUniforms(programH);
+    EffectQueueFragment.getUniforms(mainProgramH);
+    EffectQueueVertex.getUniforms(mainProgramH);
+    EffectQueueMatrix.getUniforms(mainProgramH);
+    DistortedTexture.getUniforms(mainProgramH);
+
+    final InputStream postVertexStream   = resources.openRawResource(R.raw.post_vertex_shader);
+    final InputStream postFragmentStream = resources.openRawResource(R.raw.post_fragment_shader);
+
+    DistortedProgram postProgram = new DistortedProgram(postVertexStream,postFragmentStream);
+    postProgramH = postProgram.getProgramHandle();
+    GLES20.glUseProgram(postProgramH);
+    postProgram.bindAndEnableAttributes();
+    mPostProgramAttributes = postProgram.getAttributes();
+
+    EffectQueuePostprocess.getUniforms(postProgramH);
 
     DistortedTree.reset();
     EffectMessageSender.startSending();
diff --git a/src/main/java/org/distorted/library/DistortedEffects.java b/src/main/java/org/distorted/library/DistortedEffects.java
index 7a644a1..1f2a62e 100644
--- a/src/main/java/org/distorted/library/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/DistortedEffects.java
@@ -40,11 +40,12 @@ public class DistortedEffects
   private static long mNextID =0;
   private long mID;
 
-  private EffectQueueMatrix    mM;
-  private EffectQueueFragment  mF;
-  private EffectQueueVertex    mV;
+  private EffectQueueMatrix      mM;
+  private EffectQueueFragment    mF;
+  private EffectQueueVertex      mV;
+  private EffectQueuePostprocess mP;
 
-  private boolean matrixCloned, vertexCloned, fragmentCloned;
+  private boolean matrixCloned, vertexCloned, fragmentCloned, postprocessCloned;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
     
@@ -82,37 +83,75 @@ public class DistortedEffects
       mF = new EffectQueueFragment(mID);
       fragmentCloned = false;
       }
+
+    if( (flags & Distorted.CLONE_POSTPROCESS) != 0 )
+      {
+      mP = d.mP;
+      postprocessCloned = true;
+      }
+    else
+      {
+      mP = new EffectQueuePostprocess(mID);
+      postprocessCloned = false;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
   void drawPriv(float halfInputW, float halfInputH, MeshObject mesh, DistortedFramebuffer df, long currTime)
     {
-    GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
-
-    float halfZ = halfInputW*mesh.zFactor;
-
     mM.compute(currTime);
-    mM.send(df,halfInputW,halfInputH,halfZ);
-      
     mV.compute(currTime);
-    mV.send(halfInputW,halfInputH,halfZ);
-        
     mF.compute(currTime);
-    mF.send(halfInputW,halfInputH);
+    mP.compute(currTime);
 
-    mesh.draw();
+    float halfZ = halfInputW*mesh.zFactor;
+
+    GLES20.glUseProgram(Distorted.mainProgramH);
+
+    if( mP.mNumEffects==0 )
+      {
+      GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
+
+      mM.send(df,halfInputW,halfInputH,halfZ);
+      mV.send(halfInputW,halfInputH,halfZ);
+      mF.send(halfInputW,halfInputH);
+
+      mesh.draw();
+      }
+    else
+      {
+      DistortedFramebuffer fbo = null;
+      // TODO : set this as output
+
+      // render to the FBO just like above
+      /*
+      GLES20.glViewport(0, 0, fbo.mWidth, fbo.mHeight);
+
+      mM.send(fbo,halfInputW,halfInputH,halfZ);
+      mV.send(halfInputW,halfInputH,halfZ);
+      mF.send(halfInputW,halfInputH);
+
+      mesh.draw();
+      */
+
+      GLES20.glUseProgram(Distorted.postProgramH);
+
+      // apply postprocess effects
+      mP.postprocess(fbo,df);
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
-  void drawNoEffectsPriv(float halfInputW, float halfInputH, MeshObject mesh, DistortedFramebuffer df)
+  static void drawNoEffectsPriv(float halfInputW, float halfInputH, MeshObject mesh, DistortedFramebuffer df)
     {
     GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
 
-    mM.sendZero(df,halfInputW,halfInputH,halfInputW*mesh.zFactor);
-    mV.sendZero();
-    mF.sendZero();
+    EffectQueueMatrix.sendZero(df,halfInputW,halfInputH,halfInputW*mesh.zFactor);
+    EffectQueueVertex.sendZero();
+    EffectQueueFragment.sendZero();
+    EffectQueuePostprocess.sendZero(2*halfInputW,2*halfInputH);
 
     mesh.draw();
     }
@@ -121,13 +160,15 @@ public class DistortedEffects
    
   private void releasePriv()
     {
-    if( !matrixCloned  ) mM.abortAll(false);
-    if( !vertexCloned  ) mV.abortAll(false);
-    if( !fragmentCloned) mF.abortAll(false);
+    if( !matrixCloned     ) mM.abortAll(false);
+    if( !vertexCloned     ) mV.abortAll(false);
+    if( !fragmentCloned   ) mF.abortAll(false);
+    if( !postprocessCloned) mP.abortAll(false);
 
     mM = null;
     mV = null;
     mF = null;
+    mP = null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -197,6 +238,7 @@ public class DistortedEffects
     mV.registerForMessages(el);
     mF.registerForMessages(el);
     mM.registerForMessages(el);
+    mP.registerForMessages(el);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -210,6 +252,7 @@ public class DistortedEffects
     mV.deregisterForMessages(el);
     mF.deregisterForMessages(el);
     mM.deregisterForMessages(el);
+    mP.deregisterForMessages(el);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -219,7 +262,7 @@ public class DistortedEffects
  */
   public int abortAllEffects()
       {
-      return mM.abortAll(true) + mV.abortAll(true) + mF.abortAll(true);
+      return mM.abortAll(true) + mV.abortAll(true) + mF.abortAll(true) + mP.abortAll(true);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -233,10 +276,11 @@ public class DistortedEffects
     {
     switch(type)
       {
-      case MATRIX  : return mM.abortAll(true);
-      case VERTEX  : return mV.abortAll(true);
-      case FRAGMENT: return mF.abortAll(true);
-      default      : return 0;
+      case MATRIX     : return mM.abortAll(true);
+      case VERTEX     : return mV.abortAll(true);
+      case FRAGMENT   : return mF.abortAll(true);
+      case POSTPROCESS: return mP.abortAll(true);
+      default         : return 0;
       }
     }
     
@@ -251,9 +295,10 @@ public class DistortedEffects
     {
     int type = (int)(id&EffectTypes.MASK);
 
-    if( type==EffectTypes.MATRIX.type   ) return mM.removeByID(id>>EffectTypes.LENGTH);
-    if( type==EffectTypes.VERTEX.type   ) return mV.removeByID(id>>EffectTypes.LENGTH);
-    if( type==EffectTypes.FRAGMENT.type ) return mF.removeByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.MATRIX.type      ) return mM.removeByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.VERTEX.type      ) return mV.removeByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.FRAGMENT.type    ) return mF.removeByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.POSTPROCESS.type ) return mP.removeByID(id>>EffectTypes.LENGTH);
 
     return 0;
     }
@@ -269,10 +314,11 @@ public class DistortedEffects
     {
     switch(name.getType())
       {
-      case MATRIX  : return mM.removeByType(name);
-      case VERTEX  : return mV.removeByType(name);
-      case FRAGMENT: return mF.removeByType(name);
-      default      : return 0;
+      case MATRIX     : return mM.removeByType(name);
+      case VERTEX     : return mV.removeByType(name);
+      case FRAGMENT   : return mF.removeByType(name);
+      case POSTPROCESS: return mP.removeByType(name);
+      default         : return 0;
       }
     }
     
@@ -288,9 +334,10 @@ public class DistortedEffects
     {
     int type = (int)(id&EffectTypes.MASK);
 
-    if( type==EffectTypes.MATRIX.type   )  return mM.printByID(id>>EffectTypes.LENGTH);
-    if( type==EffectTypes.VERTEX.type   )  return mV.printByID(id>>EffectTypes.LENGTH);
-    if( type==EffectTypes.FRAGMENT.type )  return mF.printByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.MATRIX.type      )  return mM.printByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.VERTEX.type      )  return mV.printByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.FRAGMENT.type    )  return mF.printByID(id>>EffectTypes.LENGTH);
+    if( type==EffectTypes.POSTPROCESS.type )  return mP.printByID(id>>EffectTypes.LENGTH);
 
     return false;
     }
@@ -864,4 +911,20 @@ public class DistortedEffects
     {
     return mV.add(EffectNames.WAVE, wave, center, region);
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Postprocess-based effects
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Blur the object.
+ *
+ * @param radius The 'strength' if the effect, in pixels. 0 = no blur, 10 = when blurring a given pixel,
+ *               take into account 10 pixels in each direction.
+ * @param center 2-dimensional Data that, at any given time, returns the Center of the Effect.
+ * @return ID of the effect added, or -1 if we failed to add one.
+ */
+  public long blur(Data1D radius, Data2D center)
+    {
+    return mP.add(EffectNames.BLUR, radius, center);
+    }
   }
diff --git a/src/main/java/org/distorted/library/DistortedFramebuffer.java b/src/main/java/org/distorted/library/DistortedFramebuffer.java
index b9812af..135e402 100644
--- a/src/main/java/org/distorted/library/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/DistortedFramebuffer.java
@@ -243,7 +243,7 @@ public class DistortedFramebuffer
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // must be called form a thread holding OpenGL Context
 
-  static synchronized void deleteAllMarked()
+  private static synchronized void deleteAllMarked()
     {
     if( mListMarked )
       {
diff --git a/src/main/java/org/distorted/library/DistortedTree.java b/src/main/java/org/distorted/library/DistortedTree.java
index df8f853..8c52966 100644
--- a/src/main/java/org/distorted/library/DistortedTree.java
+++ b/src/main/java/org/distorted/library/DistortedTree.java
@@ -222,7 +222,7 @@ public class DistortedTree
         GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
 
         if( mTexture.setAsInput() )
-          mEffects.drawNoEffectsPriv(mTexture.mHalfX, mTexture.mHalfY, mMesh, mData.mFBO);
+          DistortedEffects.drawNoEffectsPriv(mTexture.mHalfX, mTexture.mHalfY, mMesh, mData.mFBO);
 
         synchronized(this)
           {
diff --git a/src/main/java/org/distorted/library/EffectQueueFragment.java b/src/main/java/org/distorted/library/EffectQueueFragment.java
index 5a136fe..c303866 100644
--- a/src/main/java/org/distorted/library/EffectQueueFragment.java
+++ b/src/main/java/org/distorted/library/EffectQueueFragment.java
@@ -134,7 +134,7 @@ class EffectQueueFragment extends EffectQueue
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  synchronized void sendZero() 
+  synchronized static void sendZero()
     {
     GLES20.glUniform1i( mNumEffectsH, 0);
     }
diff --git a/src/main/java/org/distorted/library/EffectQueueMatrix.java b/src/main/java/org/distorted/library/EffectQueueMatrix.java
index b6ab54e..202a6d1 100644
--- a/src/main/java/org/distorted/library/EffectQueueMatrix.java
+++ b/src/main/java/org/distorted/library/EffectQueueMatrix.java
@@ -257,7 +257,7 @@ class EffectQueueMatrix extends EffectQueue
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // here construct the ModelView Matrix, but without any effects
 
-  synchronized void sendZero(DistortedFramebuffer df, float halfX, float halfY, float halfZ)
+  synchronized static void sendZero(DistortedFramebuffer df, float halfX, float halfY, float halfZ)
     {
     Matrix.setIdentityM(mTmpMatrix, 0);
     Matrix.translateM(mTmpMatrix, 0, halfX-df.mWidth/2, df.mHeight/2-halfY, -df.mDistance);
diff --git a/src/main/java/org/distorted/library/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
index da635a5..7c710d8 100644
--- a/src/main/java/org/distorted/library/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
@@ -98,6 +98,13 @@ class EffectQueuePostprocess extends EffectQueue
     mTime = currTime;  
     }  
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void postprocess(DistortedFramebuffer input, DistortedFramebuffer output)
+    {
+    // TODO
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   protected void moveEffect(int index)
@@ -121,6 +128,14 @@ class EffectQueuePostprocess extends EffectQueue
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized static void sendZero(float objX, float objY)
+    {
+    GLES20.glUniform1i( mNumEffectsH, 0);
+    GLES20.glUniform2f( mObjDH , objX, objY);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // blur
 
diff --git a/src/main/java/org/distorted/library/EffectQueueVertex.java b/src/main/java/org/distorted/library/EffectQueueVertex.java
index a27d577..fb80362 100644
--- a/src/main/java/org/distorted/library/EffectQueueVertex.java
+++ b/src/main/java/org/distorted/library/EffectQueueVertex.java
@@ -153,7 +153,7 @@ class EffectQueueVertex extends EffectQueue
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  synchronized void sendZero() 
+  synchronized static void sendZero()
     {
     GLES20.glUniform1i( mNumEffectsH, 0);
     }
diff --git a/src/main/res/raw/post_fragment_shader.glsl b/src/main/res/raw/post_fragment_shader.glsl
index 87e32e4..dd84a33 100644
--- a/src/main/res/raw/post_fragment_shader.glsl
+++ b/src/main/res/raw/post_fragment_shader.glsl
@@ -17,29 +17,35 @@
 // along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
 //////////////////////////////////////////////////////////////////////////////////////////////
 
-varying vec2 vTexCoordinate;
+#define NUM_POSTPROCESS 3
+#define BLUR 0
+
+precision lowp float;
+
+varying vec2 v_TexCoordinate;
 uniform sampler2D u_Texture;
 uniform vec2 u_objD;
-uniform int fNumEffects;                   // total number of postprocessing effects
+uniform int pNumEffects;                   // total number of postprocessing effects
 
 #if NUM_POSTPROCESS>0
 uniform int pType[NUM_POSTPROCESS];        // their types.
 uniform vec4 pUniforms[2*NUM_POSTPROCESS]; // i-th effect is 2 consecutive vec4's: [2*i], [2*i+1].
-                                           //
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 // BLUR EFFECT
 
-void blur(out vec4 color)
+void blur(out vec4 pixel)
   {
-  color = vec4(0.0);
+  pixel = vec4(0.0);
 
   float blurSizeH = 1.0 / u_objD.x;
   float blurSizeV = 1.0 / u_objD.y;
 
-  for (int x = -4; x <= 4; x++)
-    for (int y = -4; y <= 4; y++)
-      color += texture( u_Texture, vec2(vTexCoord.x + x * blurSizeH, vTexCoord.y + y * blurSizeV) ) / 81.0;
+  for (float x = -4.0; x <= 4.0; x+=1.0)
+    for (float y = -4.0; y <= 4.0; y+=1.0)
+      {
+      pixel += texture2D( u_Texture, vec2(v_TexCoordinate.x + x * blurSizeH, v_TexCoordinate.y + y * blurSizeV) ) / 81.0;
+      }
   }
 
 #endif
@@ -51,7 +57,7 @@ void main()
   vec4 pixel = texture2D(u_Texture,v_TexCoordinate);
 
 #if NUM_POSTPROCESS>0
-  for(int i=0; i<fNumEffects; i++)
+  for(int i=0; i<pNumEffects; i++)
     {
     if( pType[i]==BLUR ) blur(pixel);
     }
