commit 78db8663b888226978f5c909cdfed72977d8a357
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Wed Apr 26 17:07:39 2017 +0100

    Beginning of work on Mipmap levels.

diff --git a/src/main/java/org/distorted/library/DistortedFramebuffer.java b/src/main/java/org/distorted/library/DistortedFramebuffer.java
index a00605f..2dd5cde 100644
--- a/src/main/java/org/distorted/library/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/DistortedFramebuffer.java
@@ -225,4 +225,23 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
     {
     return mColorH[0];
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Set mipmap level.
+ * <p>
+ * Trick for speeding up your renders - one can create a pyramid of DistortedFramebuffer objects,
+ * each next one some constant FACTOR smaller than the previous (0.5 is the common value), then set
+ * the Mipmap Level of the i-th object to be FACTOR^i (we start counting from 0). When rendering any
+ * scene into such prepared Framebuffer, 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.
+ *
+ * @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)
+ */
+  public void setMipmap(float mipmap)
+    {
+    mMipmap = mipmap;
+    }
   }
diff --git a/src/main/java/org/distorted/library/DistortedOutputSurface.java b/src/main/java/org/distorted/library/DistortedOutputSurface.java
index d60d618..724e6ca 100644
--- a/src/main/java/org/distorted/library/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/DistortedOutputSurface.java
@@ -27,6 +27,9 @@ import java.util.ArrayList;
 
 abstract class DistortedOutputSurface extends DistortedSurface implements DistortedSlave
 {
+  private static final int NUM_MIPMAP = 4;
+          static final int CUR_MIPMAP = 0;
+
   private static final int ATTACH = 0;
   private static final int DETACH = 1;
   private static final int DETALL = 2;
@@ -51,7 +54,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 
   private ArrayList<Job> mJobs = new ArrayList<>();
 
-  DistortedFramebuffer mBuffer1, mBuffer2;
+  DistortedFramebuffer[] mBuffer1, mBuffer2;
 
   private long mTime;
   private float mFOV;
@@ -67,6 +70,8 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 
 //private String sNew="", sOld="";
 
+  float mMipmap;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   DistortedOutputSurface(int width, int height, int createColor, int createDepth, int fbo, int type)
@@ -94,6 +99,10 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 
     mClearDepth = 1.0f;
 
+    mBuffer1 = new DistortedFramebuffer[NUM_MIPMAP];
+    mBuffer2 = new DistortedFramebuffer[NUM_MIPMAP];
+    mMipmap = 1.0f;
+
     createProjection();
     }
 
@@ -168,14 +177,23 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
         }
       else
         {
-        if( mBuffer1==null )
+        if( mBuffer1[0]==null )
           {
-          mBuffer1 = new DistortedFramebuffer(mDepthCreated!=DONT_CREATE, DistortedSurface.TYPE_SYST, mWidth, mHeight);
-          mBuffer2 = new DistortedFramebuffer(false                     , DistortedSurface.TYPE_SYST, mWidth, mHeight);
+          float mipmap=1.0f;
+
+          for(int j=0; j<NUM_MIPMAP; j++)
+            {
+            mBuffer1[j] = new DistortedFramebuffer( mDepthCreated!=DONT_CREATE, DistortedSurface.TYPE_SYST,
+                                                    (int)(mWidth*mipmap), (int)(mHeight*mipmap) );
+            mBuffer2[j] = new DistortedFramebuffer(false                     , DistortedSurface.TYPE_SYST,
+                                                    (int)(mWidth*mipmap), (int)(mHeight*mipmap) );
+            mBuffer1[j].mMipmap = mipmap;
+            mipmap *= 0.5f;
+            }
           DistortedSurface.toDo();  // create immediately
           }
 
-        numRenders += child.draw(time,mBuffer1);
+        numRenders += child.draw(time,mBuffer1[CUR_MIPMAP]);
         if( i==num-1 )
           {
           numRenders += currP.postprocess(time,this);
diff --git a/src/main/java/org/distorted/library/EffectQueueMatrix.java b/src/main/java/org/distorted/library/EffectQueueMatrix.java
index 05e4a88..38dc239 100644
--- a/src/main/java/org/distorted/library/EffectQueueMatrix.java
+++ b/src/main/java/org/distorted/library/EffectQueueMatrix.java
@@ -112,14 +112,15 @@ class EffectQueueMatrix extends EffectQueue
     Matrix.translateM(mViewMatrix, 0, -projection.mWidth/2, projection.mHeight/2, -projection.mDistance);
 
     float x,y,z, sx,sy,sz;
+    float mipmap = projection.mMipmap;
 
     for(int i=0; i<mNumEffects; i++)
       {
       if (mName[i] == EffectNames.ROTATE.ordinal() )
         {
-        x = mUniforms[NUM_UNIFORMS*i+4];
-        y = mUniforms[NUM_UNIFORMS*i+5];
-        z = mUniforms[NUM_UNIFORMS*i+6];
+        x = mUniforms[NUM_UNIFORMS*i+4]*mipmap;
+        y = mUniforms[NUM_UNIFORMS*i+5]*mipmap;
+        z = mUniforms[NUM_UNIFORMS*i+6]*mipmap;
 
         Matrix.translateM(mViewMatrix, 0, x,-y, z);
         Matrix.rotateM( mViewMatrix, 0, mUniforms[NUM_UNIFORMS*i], mUniforms[NUM_UNIFORMS*i+1], mUniforms[NUM_UNIFORMS*i+2], mUniforms[NUM_UNIFORMS*i+3]);
@@ -127,9 +128,9 @@ class EffectQueueMatrix extends EffectQueue
         }
       else if(mName[i] == EffectNames.QUATERNION.ordinal() )
         {
-        x = mUniforms[NUM_UNIFORMS*i+4];
-        y = mUniforms[NUM_UNIFORMS*i+5];
-        z = mUniforms[NUM_UNIFORMS*i+6];
+        x = mUniforms[NUM_UNIFORMS*i+4]*mipmap;
+        y = mUniforms[NUM_UNIFORMS*i+5]*mipmap;
+        z = mUniforms[NUM_UNIFORMS*i+6]*mipmap;
 
         Matrix.translateM(mViewMatrix, 0, x,-y, z);
         multiplyByQuat(mViewMatrix, mUniforms[NUM_UNIFORMS*i], mUniforms[NUM_UNIFORMS*i+1], mUniforms[NUM_UNIFORMS*i+2], mUniforms[NUM_UNIFORMS*i+3]);
@@ -137,17 +138,17 @@ class EffectQueueMatrix extends EffectQueue
         }
       else if(mName[i] == EffectNames.MOVE.ordinal() )
         {
-        sx = mUniforms[NUM_UNIFORMS*i  ];
-        sy = mUniforms[NUM_UNIFORMS*i+1];
-        sz = mUniforms[NUM_UNIFORMS*i+2];
+        sx = mUniforms[NUM_UNIFORMS*i  ]*mipmap;
+        sy = mUniforms[NUM_UNIFORMS*i+1]*mipmap;
+        sz = mUniforms[NUM_UNIFORMS*i+2]*mipmap;
 
         Matrix.translateM(mViewMatrix, 0, sx,-sy, sz);
         }
       else if(mName[i] == EffectNames.SCALE.ordinal() )
         {
-        sx = mUniforms[NUM_UNIFORMS*i  ];
-        sy = mUniforms[NUM_UNIFORMS*i+1];
-        sz = mUniforms[NUM_UNIFORMS*i+2];
+        sx = mUniforms[NUM_UNIFORMS*i  ]*mipmap;
+        sy = mUniforms[NUM_UNIFORMS*i+1]*mipmap;
+        sz = mUniforms[NUM_UNIFORMS*i+2]*mipmap;
 
         Matrix.scaleM(mViewMatrix, 0, sx, sy, sz);
         }
@@ -157,9 +158,9 @@ class EffectQueueMatrix extends EffectQueue
         sy = mUniforms[NUM_UNIFORMS*i+1];
         sz = mUniforms[NUM_UNIFORMS*i+2];
 
-        x  = mUniforms[NUM_UNIFORMS*i+4];
-        y  = mUniforms[NUM_UNIFORMS*i+5];
-        z  = mUniforms[NUM_UNIFORMS*i+6];
+        x  = mUniforms[NUM_UNIFORMS*i+4]*mipmap;
+        y  = mUniforms[NUM_UNIFORMS*i+5]*mipmap;
+        z  = mUniforms[NUM_UNIFORMS*i+6]*mipmap;
 
         Matrix.translateM(mViewMatrix, 0, x,-y, z);
 
diff --git a/src/main/java/org/distorted/library/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
index 7674470..b4e1ecd 100644
--- a/src/main/java/org/distorted/library/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
@@ -259,9 +259,10 @@ class EffectQueuePostprocess extends EffectQueue
       {
       compute(time);
 
-      surface.mBuffer1.setAsInput();
-      float w = surface.mBuffer1.mWidth;
-      float h = surface.mBuffer2.mHeight;
+      float w1 = surface.mBuffer1[DistortedOutputSurface.CUR_MIPMAP].mWidth;
+      float h1 = surface.mBuffer2[DistortedOutputSurface.CUR_MIPMAP].mHeight;
+      float w2 = surface.mWidth;
+      float h2 = surface.mHeight;
 
       int radius = (int)mUniforms[0];
       if( radius>=MAX_BLUR ) radius = MAX_BLUR-1;
@@ -270,26 +271,27 @@ class EffectQueuePostprocess extends EffectQueue
       int offset = radius + radius*radius/4;
       radius = (radius+1)/2;
 
-      GLES30.glViewport(0, 0, (int)w, (int)h);
-
       // horizontal blur
+      GLES30.glViewport(0, 0, (int)w1, (int)h1);
       mBlur1Program.useProgram();
-      surface.mBuffer2.setAsOutput(time);
+      surface.mBuffer1[DistortedOutputSurface.CUR_MIPMAP].setAsInput();
+      surface.mBuffer2[DistortedOutputSurface.CUR_MIPMAP].setAsOutput(time);
 
       GLES30.glUniform1fv( mWeights1H, radius+1, weightsCache,offset);
       GLES30.glUniform1i( mRadius1H, radius);
       GLES30.glUniform1f( mDepth1H , 1.0f-surface.mNear);
       GLES30.glUniform1i( mColorTexture1H , 0 );
-      for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]/h;
+      for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]/h1;
       GLES30.glUniform1fv( mOffsets1H ,radius+1, mOffsets,0);
       GLES30.glVertexAttribPointer(mBlur1Program.mAttribute[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, mQuadPositions);
       GLES30.glVertexAttribPointer(mBlur1Program.mAttribute[1], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, 0, mQuadTexture);
       GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
 
       // vertical blur
+      GLES30.glViewport(0, 0, (int)w2, (int)h2);
       mBlur2Program.useProgram();
-      surface.mBuffer2.setAsInput();
-      surface.mBuffer1.setAsDepth();
+      surface.mBuffer2[DistortedOutputSurface.CUR_MIPMAP].setAsInput();
+      surface.mBuffer1[DistortedOutputSurface.CUR_MIPMAP].setAsDepth();
       surface.setAsOutput(time);
 
       GLES30.glUniform1fv( mWeights2H, radius+1, weightsCache,offset);
@@ -297,7 +299,7 @@ class EffectQueuePostprocess extends EffectQueue
       GLES30.glUniform1f( mDepth2H , 1.0f-surface.mNear);
       GLES30.glUniform1i( mColorTexture2H , 0 );
       GLES30.glUniform1i( mDepthTexture2H , 1 );
-      for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]/w;
+      for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]/w1;
       GLES30.glUniform1fv( mOffsets2H ,radius+1, mOffsets,0);
       GLES30.glVertexAttribPointer(mBlur2Program.mAttribute[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, mQuadPositions);
       GLES30.glVertexAttribPointer(mBlur2Program.mAttribute[1], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, 0, mQuadTexture);
