commit 8426bd6a346e17b491f9a61f8d9fd1c72d9862f7
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Thu Apr 27 14:56:06 2017 +0100

    Mipmap levels!

diff --git a/src/main/java/org/distorted/library/DistortedEffects.java b/src/main/java/org/distorted/library/DistortedEffects.java
index 74caf23..ddd6b98 100644
--- a/src/main/java/org/distorted/library/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/DistortedEffects.java
@@ -294,9 +294,7 @@ public class DistortedEffects
 
     float halfZ = halfW*mesh.zFactor;
 
-    float mipmap = 1.0f;//surface.mMipmap;
-    GLES30.glViewport(0, 0, (int)(mipmap*surface.mWidth), (int)(mipmap*surface.mHeight) );
-    //GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight);
+    GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
 
     mMainProgram.useProgram();
     GLES30.glUniform1i(mMainTextureH, 0);
@@ -317,14 +315,13 @@ public class DistortedEffects
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
-  static void blitPriv(DistortedOutputSurface projection)
+  static void blitPriv(DistortedOutputSurface surface)
     {
     mBlitProgram.useProgram();
 
-    float mipmap = 1.0f;//projection.mMipmap;
-    GLES30.glViewport(0, 0, (int)(mipmap*projection.mWidth), (int)(mipmap*projection.mHeight) );
+    GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
     GLES30.glUniform1i(mBlitTextureH, 0);
-    GLES30.glUniform1f( mBlitDepthH , 1.0f-projection.mNear);
+    GLES30.glUniform1f( mBlitDepthH , 1.0f-surface.mNear);
     GLES30.glVertexAttribPointer(mBlitProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
     GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
     }
diff --git a/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java b/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java
index a5478b1..7191a8c 100644
--- a/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java
+++ b/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java
@@ -83,6 +83,13 @@ public class DistortedEffectsPostprocess
     mP = null;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getQuality()
+    {
+    return mP.mQuality;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   static void onDestroy()
@@ -278,6 +285,18 @@ public class DistortedEffectsPostprocess
     return EffectQueue.setMax(EffectTypes.POSTPROCESS.ordinal(),max);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * The higher the quality, the better the effect will look like and the slower it will be.
+ * <p>
+ * This works by rendering into smaller and smaller intermediate buffers. Each step renders into a
+ * buffer that's half the size of the previous one.
+ */
+  public void setQuality(EffectQuality quality)
+    {
+    mP.mQuality = quality.level;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Individual effect functions.
diff --git a/src/main/java/org/distorted/library/DistortedOutputSurface.java b/src/main/java/org/distorted/library/DistortedOutputSurface.java
index c21efae..c85ca8f 100644
--- a/src/main/java/org/distorted/library/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/DistortedOutputSurface.java
@@ -27,9 +27,6 @@ import java.util.ArrayList;
 
 abstract class DistortedOutputSurface extends DistortedSurface implements DistortedSlave
 {
-  private static final int NUM_MIPMAP = 4;
-          static final int CUR_MIPMAP = 1;
-
   private static final int ATTACH = 0;
   private static final int DETACH = 1;
   private static final int DETALL = 2;
@@ -99,10 +96,10 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 
     mClearDepth = 1.0f;
 
-    mBuffer1 = new DistortedFramebuffer[NUM_MIPMAP];
-    mBuffer2 = new DistortedFramebuffer[NUM_MIPMAP];
+    mBuffer1 = new DistortedFramebuffer[EffectQuality.LENGTH];
+    mBuffer2 = new DistortedFramebuffer[EffectQuality.LENGTH];
 
-    mMipmap = (this instanceof DistortedScreen ? 0.5f : 1.0f);
+    mMipmap = 1.0f;
 
     createProjection();
     }
@@ -113,14 +110,11 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
     {
     if( mWidth>0 && mHeight>1 )
       {
-      float mw = mWidth;//mMipmap*mWidth;
-      float mh = mHeight;//mMipmap*mHeight;
-
       if( mFOV>0.0f )  // perspective projection
         {
         float a = 2.0f*(float)Math.tan(mFOV*Math.PI/360);
-        float q = mw*mNear;
-        float c = mh*mNear;
+        float q = mWidth*mNear;
+        float c = mHeight*mNear;
 
         float left   = -q/2;
         float right  = +q/2;
@@ -128,20 +122,20 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
         float top    = +c/2;
         float near   =  c/a;
 
-        mDistance    = mh/a;
+        mDistance    = mHeight/a;
         float far    = 2*mDistance-near;
 
         Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
         }
       else             // parallel projection
         {
-        float left   = -mw/2.0f;
-        float right  = +mw/2.0f;
-        float bottom = -mh/2.0f;
-        float top    = +mh/2.0f;
-        float near   = mw+mh-mh*(1.0f-mNear);
-        mDistance    = mw+mh;
-        float far    = mw+mh+mh*(1.0f-mNear);
+        float left   = -mWidth/2.0f;
+        float right  = +mWidth/2.0f;
+        float bottom = -mHeight/2.0f;
+        float top    = +mHeight/2.0f;
+        float near   = mWidth+mHeight-mHeight*(1.0f-mNear);
+        mDistance    = mWidth+mHeight;
+        float far    = mWidth+mHeight+mHeight*(1.0f-mNear);
 
         Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
         }
@@ -185,7 +179,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
           {
           float mipmap=1.0f;
 
-          for(int j=0; j<NUM_MIPMAP; j++)
+          for(int j=0; j<EffectQuality.LENGTH; j++)
             {
             mBuffer1[j] = new DistortedFramebuffer( mDepthCreated!=DONT_CREATE, DistortedSurface.TYPE_SYST,
                                                     (int)(mWidth*mipmap), (int)(mHeight*mipmap) );
@@ -197,7 +191,8 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
           DistortedSurface.toDo();  // create immediately
           }
 
-        numRenders += child.draw(time,mBuffer1[CUR_MIPMAP]);
+        numRenders += child.draw(time,mBuffer1[currP.getQuality()]);
+
         if( i==num-1 )
           {
           numRenders += currP.postprocess(time,this);
@@ -316,6 +311,11 @@ if( !sNew.equals(sOld) )
  * such prepared OutputSurface, the library will make sure to scale any Effects used so that the end
  * scene will end up looking identical no matter which object we render to. Identical, that is, except
  * for the loss of quality and gain in speed associated with rendering to a smaller Surface.
+ * <p>
+ * Example: if you create two FBOs, one 1000x1000 and another 500x500 in size, and set the second one
+ * mipmap to 0.5 (the first one's is 1.0 by default), define Effects to be a single move by (100,100),
+ * and render a skinned Mesh into both FBO, the end result will look proportionally the same, because
+ * in the second case the move vector (100,100) will be auto-scaled to (50,50).
  *
  * @param mipmap The mipmap level. Acceptable range: 0&lt;mipmap&lt;infinity, although mipmap&gt;1
  *               does not make any sense (that would result in loss of speed and no gain in quality)
diff --git a/src/main/java/org/distorted/library/EffectQuality.java b/src/main/java/org/distorted/library/EffectQuality.java
new file mode 100644
index 0000000..093f90f
--- /dev/null
+++ b/src/main/java/org/distorted/library/EffectQuality.java
@@ -0,0 +1,49 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A list of quality levels.
+ * <p>
+ * One can set quality of a Postprocessing Effect to one of those. The lower the quality, the faster
+ * the rendering will be.
+ *
+ * @see DistortedEffectsPostprocess
+ */
+public enum EffectQuality
+  {
+  HIGHEST  ( 0 ),   // has to start from 0
+  HIGH     ( 1 ),
+  MEDIUM   ( 2 ),
+  LOW      ( 3 );
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  final static int LENGTH = values().length;
+
+  final int level;
+
+  EffectQuality(int level)
+    {
+    this.level = level;
+    }
+  }
+
diff --git a/src/main/java/org/distorted/library/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
index b4e1ecd..401e82c 100644
--- a/src/main/java/org/distorted/library/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
@@ -99,11 +99,15 @@ class EffectQueuePostprocess extends EffectQueue
   private static float[] mOffsets = new float[MAX_BLUR];
   // another effect ....
 
+  int mQuality;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   EffectQueuePostprocess(long id)
     { 
     super(id,NUM_UNIFORMS,NUM_CACHE,INDEX );
+
+    mQuality = 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -259,8 +263,11 @@ class EffectQueuePostprocess extends EffectQueue
       {
       compute(time);
 
-      float w1 = surface.mBuffer1[DistortedOutputSurface.CUR_MIPMAP].mWidth;
-      float h1 = surface.mBuffer2[DistortedOutputSurface.CUR_MIPMAP].mHeight;
+      DistortedFramebuffer buffer1 = surface.mBuffer1[mQuality];
+      DistortedFramebuffer buffer2 = surface.mBuffer1[mQuality];
+
+      float w1 = buffer1.mWidth;
+      float h1 = buffer1.mHeight;
       float w2 = surface.mWidth;
       float h2 = surface.mHeight;
 
@@ -274,8 +281,8 @@ class EffectQueuePostprocess extends EffectQueue
       // horizontal blur
       GLES30.glViewport(0, 0, (int)w1, (int)h1);
       mBlur1Program.useProgram();
-      surface.mBuffer1[DistortedOutputSurface.CUR_MIPMAP].setAsInput();
-      surface.mBuffer2[DistortedOutputSurface.CUR_MIPMAP].setAsOutput(time);
+      buffer1.setAsInput();
+      buffer2.setAsOutput(time);
 
       GLES30.glUniform1fv( mWeights1H, radius+1, weightsCache,offset);
       GLES30.glUniform1i( mRadius1H, radius);
@@ -290,8 +297,8 @@ class EffectQueuePostprocess extends EffectQueue
       // vertical blur
       GLES30.glViewport(0, 0, (int)w2, (int)h2);
       mBlur2Program.useProgram();
-      surface.mBuffer2[DistortedOutputSurface.CUR_MIPMAP].setAsInput();
-      surface.mBuffer1[DistortedOutputSurface.CUR_MIPMAP].setAsDepth();
+      buffer2.setAsInput();
+      buffer1.setAsDepth();
       surface.setAsOutput(time);
 
       GLES30.glUniform1fv( mWeights2H, radius+1, weightsCache,offset);
