commit 1e438fc7ebb12af767e28fe84e203e1e6aa3478a
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Fri Jun 10 02:03:13 2016 +0100

    Introduce ENUM EffectTypes

diff --git a/src/main/java/org/distorted/library/Distorted.java b/src/main/java/org/distorted/library/Distorted.java
index f1d9214..d3bfcc7 100644
--- a/src/main/java/org/distorted/library/Distorted.java
+++ b/src/main/java/org/distorted/library/Distorted.java
@@ -67,22 +67,6 @@ public class Distorted
    * on the screen, with (optionally) different effects of the top-level root Bitmap.   
    */
   public static final int CLONE_CHILDREN= 0x10;
-  /**
-   * Constant used to represent a PreShader-based effect.
-   */
-  public static final int TYPE_PRE  = 0x1;
-  /**
-   * Constant used to represent a Vertex-based effect.
-   */
-  public static final int TYPE_VERT = 0x2;
-  /**
-   * Constant used to represent a Fragment-based effect.
-   */
-  public static final int TYPE_FRAG = 0x4;
-  /**
-   * Constant used to represent a PostShader-based effect.
-   */
-  public static final int TYPE_POST = 0x8;
 
   private static final String TAG = Distorted.class.getSimpleName();
   private static boolean mInitialized = false;
@@ -224,7 +208,7 @@ public class Distorted
      
                                       for(EffectNames name: EffectNames.values() )
                                         {
-                                        if( name.getType()==TYPE_VERT )  
+                                        if( name.getType()==EffectTypes.VERTEX)
                                         header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
                                         }
                                       break;
@@ -232,7 +216,7 @@ public class Distorted
      
                                       for(EffectNames name: EffectNames.values() )
                                         {
-                                        if( name.getType()==TYPE_FRAG )  
+                                        if( name.getType()==EffectTypes.FRAGMENT)
                                         header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
                                         }
                                       break;
@@ -360,7 +344,7 @@ public class Distorted
     
     EffectListFragment.getUniforms(mProgramH);
     EffectListVertex.getUniforms(mProgramH);
-    EffectListPreShader.getUniforms(mProgramH);
+    EffectListMatrix.getUniforms(mProgramH);
     
     GLES20.glEnableVertexAttribArray(mPositionH);        
     GLES20.glEnableVertexAttribArray(mColorH);
@@ -397,7 +381,7 @@ public class Distorted
 
     EffectListVertex.reset();
     EffectListFragment.reset();
-    EffectListPreShader.reset();  // no need to reset PostShader stuff
+    EffectListMatrix.reset();  // no need to reset Other EffectList
 
     EffectMessageSender.stopSending();
    
@@ -418,13 +402,13 @@ public class Distorted
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Returns the maximum number of PreShader effects.
+ * Returns the maximum number of Matrix effects.
  *    
- * @return The maximum number of PreShader effects
+ * @return The maximum number of Matrix effects
  */
-  public static int getMaxPreShader()
+  public static int getMaxMatrix()
     {
-    return EffectListPreShader.getMax();
+    return EffectListMatrix.getMax();
     }
  
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -451,16 +435,16 @@ public class Distorted
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Sets the maximum number of PreShader effects that can be applied to a single DistortedBitmap at one time.
+ * Sets the maximum number of Matrix effects that can be applied to a single DistortedBitmap at one time.
  * This can fail if the value of 'max' is outside permitted range. 
  * 
- * @param max new maximum number of simultaneous PreShader Effects. Has to be a non-negative number not greater
+ * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
  *            than Byte.MAX_VALUE 
  * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
  */
-  public static boolean setMaxPreShader(int max)
+  public static boolean setMaxMatrix(int max)
     {
-    return EffectListPreShader.setMax(max);
+    return EffectListMatrix.setMax(max);
     }
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/DistortedObject.java b/src/main/java/org/distorted/library/DistortedObject.java
index db636d6..5c8d578 100644
--- a/src/main/java/org/distorted/library/DistortedObject.java
+++ b/src/main/java/org/distorted/library/DistortedObject.java
@@ -9,15 +9,13 @@ import android.opengl.GLUtils;
  * All Objects to which Distorted Graphics effects can be applied need to be extended from here.
  */
 public abstract class DistortedObject 
-{ 
-    static final int TYPE_NUM = 4;
-    private static final int TYPE_MASK= (1<<TYPE_NUM)-1;
+{
     private static float[] mViewMatrix = new float[16];
    
-    protected EffectListPreShader  mM;
+    protected EffectListMatrix     mM;
     protected EffectListFragment   mF;
     protected EffectListVertex     mV;
-    protected EffectListPostShader mP;
+    protected EffectListOther      mO;
 
     protected boolean matrixCloned, vertexCloned, fragmentCloned;
  
@@ -62,7 +60,7 @@ public abstract class DistortedObject
         } 
       else
         {
-        mM = new EffectListPreShader(d);
+        mM = new EffectListMatrix(d);
         matrixCloned = false;  
         }
     
@@ -88,7 +86,7 @@ public abstract class DistortedObject
         fragmentCloned = false;   
         }
 
-      mP = new EffectListPostShader(d); // PostShader effects are never cloned.
+      mO= new EffectListOther(d); // Other effects are never cloned.
       }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -134,7 +132,7 @@ public abstract class DistortedObject
        
       mGrid.draw();
 
-      mP.send();
+      mO.send();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -156,14 +154,14 @@ public abstract class DistortedObject
       if( vertexCloned  ==false) mV.abortAll();
       if( fragmentCloned==false) mF.abortAll();
 
-      mP.abortAll();
+      mO.abortAll();
 
       mBmp          = null;
       mGrid         = null;
       mM            = null;
       mV            = null;
       mF            = null;
-      mP            = null;
+      mO            = null;
       mTextureDataH = null;
       }
  
@@ -291,7 +289,7 @@ public abstract class DistortedObject
      mV.addListener(el);
      mF.addListener(el);
      mM.addListener(el);
-     mP.addListener(el);
+     mO.addListener(el);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -305,7 +303,7 @@ public abstract class DistortedObject
      mV.removeListener(el);
      mF.removeListener(el);
      mM.removeListener(el);
-     mP.removeListener(el);
+     mO.removeListener(el);
      }
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -361,7 +359,7 @@ public abstract class DistortedObject
       mM.abortAll();
       mV.abortAll();
       mF.abortAll();
-      mP.abortAll();
+      mO.abortAll();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -372,10 +370,10 @@ public abstract class DistortedObject
  */
     public void abortAllEffects(int mask)
       {
-      if( (mask & Distorted.TYPE_PRE ) != 0 ) mM.abortAll();
-      if( (mask & Distorted.TYPE_VERT) != 0 ) mV.abortAll();
-      if( (mask & Distorted.TYPE_FRAG) != 0 ) mF.abortAll();
-      if( (mask & Distorted.TYPE_POST) != 0 ) mP.abortAll();
+      if( (mask & EffectTypes.MATRIX.type   ) != 0 ) mM.abortAll();
+      if( (mask & EffectTypes.VERTEX.type   ) != 0 ) mV.abortAll();
+      if( (mask & EffectTypes.FRAGMENT.type ) != 0 ) mF.abortAll();
+      if( (mask & EffectTypes.OTHER.type    ) != 0 ) mO.abortAll();
       }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -387,14 +385,14 @@ public abstract class DistortedObject
  */
     public boolean abortEffect(long id)
       {
-      switch( (int)(id&TYPE_MASK) )
-        {
-        case Distorted.TYPE_PRE : return mM.removeByID(id>>TYPE_NUM);
-        case Distorted.TYPE_VERT: return mV.removeByID(id>>TYPE_NUM);
-        case Distorted.TYPE_FRAG: return mF.removeByID(id>>TYPE_NUM);
-        case Distorted.TYPE_POST: return mP.removeByID(id>>TYPE_NUM);
-        default                 : return false;
-        }
+      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.OTHER.type    )  return mO.removeByID(id>>EffectTypes.LENGTH);
+
+      return false;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -408,11 +406,11 @@ public abstract class DistortedObject
       {
       switch(effectType.getType())
         {
-        case Distorted.TYPE_PRE : return mM.removeByType(effectType);
-        case Distorted.TYPE_VERT: return mV.removeByType(effectType);
-        case Distorted.TYPE_FRAG: return mF.removeByType(effectType);
-        case Distorted.TYPE_POST: return mP.removeByType(effectType);
-        default                 : return false;
+        case MATRIX  : return mM.removeByType(effectType);
+        case VERTEX  : return mV.removeByType(effectType);
+        case FRAGMENT: return mF.removeByType(effectType);
+        case OTHER   : return mO.removeByType(effectType);
+        default           : return false;
         }
       }
     
@@ -426,14 +424,14 @@ public abstract class DistortedObject
     
     public boolean printEffect(long id)
       {
-      switch( (int)(id&TYPE_MASK) )
-        {
-        case Distorted.TYPE_PRE : return mM.printByID(id>>TYPE_NUM);
-        case Distorted.TYPE_VERT: return mV.printByID(id>>TYPE_NUM);
-        case Distorted.TYPE_FRAG: return mF.printByID(id>>TYPE_NUM);
-        case Distorted.TYPE_POST: return mP.printByID(id>>TYPE_NUM);
-        default                 : return false;
-        }
+      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.OTHER.type    )  return mO.printByID(id>>EffectTypes.LENGTH);
+
+      return false;
       }
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////   
diff --git a/src/main/java/org/distorted/library/EffectList.java b/src/main/java/org/distorted/library/EffectList.java
index 5a23ad1..3ca5e20 100644
--- a/src/main/java/org/distorted/library/EffectList.java
+++ b/src/main/java/org/distorted/library/EffectList.java
@@ -6,12 +6,6 @@ import java.util.Vector;
 
 abstract class EffectList
   {
-  protected static final int DEFAULT_NUM_EFFECTS = 5;
-  
-  protected static final int PRESHADER =0;
-  protected static final int VERTEX    =1;
-  protected static final int FRAGMENT  =2;
-
   protected byte mNumEffects;   // number of effects at the moment
   protected long mTotalEffects; // total number of effects ever created
   
@@ -27,9 +21,9 @@ abstract class EffectList
   protected long mTime=0;
   protected float mObjHalfX, mObjHalfY, mObjHalfZ;
   
-  protected static int[] mMax = new int[3];
+  protected static int[] mMax = new int[EffectTypes.LENGTH];
   protected int mMaxIndex;
-  protected static boolean mCreated = false;
+  protected static boolean mCreated;
  
   protected Vector<EffectListener> mListeners =null;
   protected int mNumListeners=0;  // ==mListeners.length(), but we only create mListeners if the first one gets added
@@ -37,9 +31,7 @@ abstract class EffectList
   
   static
     {
-    mMax[PRESHADER]= DEFAULT_NUM_EFFECTS;
-    mMax[VERTEX]   = DEFAULT_NUM_EFFECTS;
-    mMax[FRAGMENT] = DEFAULT_NUM_EFFECTS;
+    reset();
     }
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -49,13 +41,16 @@ abstract class EffectList
     mNumEffects   = 0;
     mTotalEffects = 0;
     mMaxIndex     = index;
-    
-    mObjHalfX = obj.getWidth() /2.0f;
-    mObjHalfY = obj.getHeight()/2.0f;
-    mObjHalfZ = obj.getDepth() /2.0f;
 
-    mBitmapID = obj.getID();
-    
+    if( obj!=null )
+      {
+      mObjHalfX = obj.getWidth() / 2.0f;
+      mObjHalfY = obj.getHeight() / 2.0f;
+      mObjHalfZ = obj.getDepth() / 2.0f;
+
+      mBitmapID = obj.getID();
+      }
+
     if( mMax[mMaxIndex]>0 )
       {
       mType            = new int[mMax[mMaxIndex]];
@@ -105,10 +100,7 @@ abstract class EffectList
 
   static void reset()
     {
-    mMax[PRESHADER]= DEFAULT_NUM_EFFECTS;
-    mMax[VERTEX]   = DEFAULT_NUM_EFFECTS;
-    mMax[FRAGMENT] = DEFAULT_NUM_EFFECTS;
-   
+    EffectTypes.reset(mMax);
     mCreated = false;  
     }
  
@@ -203,7 +195,7 @@ abstract class EffectList
     for(int i=0; i<mNumListeners; i++) 
       EffectMessageSender.newMessage( mListeners.elementAt(i),
                                       EffectMessage.EFFECT_REMOVED, 
-                                      (removedID<<DistortedObject.TYPE_NUM)+EffectNames.getType(removedType), 
+                                      (removedID<<EffectTypes.LENGTH)+EffectNames.getType(removedType).type,
                                       removedType,
                                       mBitmapID);  
     }
@@ -223,7 +215,7 @@ abstract class EffectList
     mNumEffects++; 
     mTotalEffects++;
    
-    return (id<<DistortedObject.TYPE_NUM)+eln.getType();
+    return (id<<EffectTypes.LENGTH)+eln.getType().type;
     }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/EffectListFragment.java b/src/main/java/org/distorted/library/EffectListFragment.java
index 6832f60..b7610cb 100644
--- a/src/main/java/org/distorted/library/EffectListFragment.java
+++ b/src/main/java/org/distorted/library/EffectListFragment.java
@@ -6,7 +6,8 @@ import android.opengl.GLES20;
 
 class EffectListFragment extends EffectList
   {
-  private static final int NUM_UNIFORMS = 9; 
+  private static final int NUM_UNIFORMS = 9;
+  private static final int INDEX = EffectTypes.FRAGMENT.ordinal();
   private float[] mBuf;
   private static int mNumEffectsH;
   private static int mTypeH;
@@ -16,11 +17,11 @@ class EffectListFragment extends EffectList
    
   public EffectListFragment(DistortedObject obj)
     { 
-    super(obj,NUM_UNIFORMS,FRAGMENT);
+    super(obj,NUM_UNIFORMS,INDEX);
    
-    if( mMax[FRAGMENT]>0 )
+    if( mMax[INDEX]>0 )
       {
-      mBuf= new float[4*mMax[FRAGMENT]];
+      mBuf= new float[4*mMax[INDEX]];
       }
     }
   
@@ -30,12 +31,12 @@ class EffectListFragment extends EffectList
   
   static boolean setMax(int m)
     {
-    if( (mCreated==false && !Distorted.isInitialized()) || m<=mMax[FRAGMENT] ) 
+    if( (mCreated==false && !Distorted.isInitialized()) || m<=mMax[INDEX] )
       {
            if( m<0              ) m = 0;
       else if( m>Byte.MAX_VALUE ) m = Byte.MAX_VALUE;
       
-      mMax[FRAGMENT] = m;
+      mMax[INDEX] = m;
       return true;
       }
    
@@ -46,7 +47,7 @@ class EffectListFragment extends EffectList
 
   static int getMax()
     {
-    return mMax[FRAGMENT];
+    return mMax[INDEX];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -77,7 +78,7 @@ class EffectListFragment extends EffectList
         for(int j=0; j<mNumListeners; j++)   
           EffectMessageSender.newMessage( mListeners.elementAt(j),
                                           EffectMessage.EFFECT_FINISHED, 
-                                          (mID[i]<<DistortedObject.TYPE_NUM)+Distorted.TYPE_FRAG, 
+                                          (mID[i]<<EffectTypes.LENGTH)+EffectTypes.FRAGMENT.type,
                                           mType[i], 
                                           mBitmapID); 
       
@@ -167,7 +168,7 @@ class EffectListFragment extends EffectList
        
   synchronized long add(EffectNames eln, Interpolator inter, Float4D region, Interpolator2D point)
     {
-    if( mMax[FRAGMENT]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       EffectNames.fillWithUnities(eln.ordinal(), mUniforms, NUM_UNIFORMS*mNumEffects); 
       mInterI[mNumEffects] = inter;
@@ -185,7 +186,7 @@ class EffectListFragment extends EffectList
 
   synchronized long add(EffectNames eln, Interpolator inter, Float4D region, float x, float y)
     {
-    if( mMax[FRAGMENT]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       EffectNames.fillWithUnities(eln.ordinal(), mUniforms, NUM_UNIFORMS*mNumEffects);    
       mInterI[mNumEffects] = inter;
@@ -205,7 +206,7 @@ class EffectListFragment extends EffectList
        
   synchronized long add(EffectNames eln, Interpolator1D inter, Float3D c, Float4D region, Interpolator2D point)
     {
-    if( mMax[FRAGMENT]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       mInterI[mNumEffects] = inter;
       mInterP[mNumEffects] = point;
@@ -226,7 +227,7 @@ class EffectListFragment extends EffectList
 
   synchronized long add(EffectNames eln, Interpolator1D inter, Float3D c, Float4D region, float x, float y)
     {
-    if( mMax[FRAGMENT]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       mInterI[mNumEffects] = inter;
       mInterP[mNumEffects] = null;
@@ -249,7 +250,7 @@ class EffectListFragment extends EffectList
        
   synchronized long add(EffectNames eln, float t, Float3D c, Float4D region, Interpolator2D point)
     {
-    if( mMax[FRAGMENT]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       mInterI[mNumEffects] = null;
       mInterP[mNumEffects] = point;
@@ -271,7 +272,7 @@ class EffectListFragment extends EffectList
 
   synchronized long add(EffectNames eln, float t, Float3D c, Float4D region, float x, float y)
     {
-    if( mMax[FRAGMENT]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       mInterI[mNumEffects] = null;
       mInterP[mNumEffects] = null;
diff --git a/src/main/java/org/distorted/library/EffectListMatrix.java b/src/main/java/org/distorted/library/EffectListMatrix.java
new file mode 100644
index 0000000..667c5e2
--- /dev/null
+++ b/src/main/java/org/distorted/library/EffectListMatrix.java
@@ -0,0 +1,345 @@
+package org.distorted.library;
+
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class EffectListMatrix extends EffectList
+  {   
+  private static final int NUM_UNIFORMS = 7;
+  private static final int INDEX = EffectTypes.MATRIX.ordinal();
+  private static float[] mMVPMatrix= new float[16];
+  private static float[] mTmpMatrix= new float[16];
+  
+  private static int mBmpDH;      // This is a handle to half a bitmap dimensions
+  private static int mDepthH;     // Handle to the max Depth, i.e (farplane-nearplane)/2
+  private static int mMVPMatrixH; // pass in the transformation matrix
+  private static int mMVMatrixH;  // pass in the modelview matrix.
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+   
+  public EffectListMatrix(DistortedObject obj)
+    { 
+    super(obj,NUM_UNIFORMS, INDEX );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void multiplyByQuat(float[] matrix, float X, float Y, float Z, float W)
+    {
+    float xx= X * X;
+    float xy= X * Y;
+    float xz= X * Z;
+    float xw= X * W;
+    float yy= Y * Y;
+    float yz= Y * Z;
+    float yw= Y * W;
+    float zz= Z * Z;
+    float zw= Z * W;
+
+    mTmpMatrix[0]  = 1 - 2 * ( yy + zz );
+    mTmpMatrix[1]  =     2 * ( xy - zw );
+    mTmpMatrix[2]  =     2 * ( xz + yw );
+    mTmpMatrix[4]  =     2 * ( xy + zw );
+    mTmpMatrix[5]  = 1 - 2 * ( xx + zz );
+    mTmpMatrix[6]  =     2 * ( yz - xw );
+    mTmpMatrix[8]  =     2 * ( xz - yw );
+    mTmpMatrix[9]  =     2 * ( yz + xw );
+    mTmpMatrix[10] = 1 - 2 * ( xx + yy );
+    mTmpMatrix[3]  = mTmpMatrix[7] = mTmpMatrix[11] = mTmpMatrix[12] = mTmpMatrix[13] = mTmpMatrix[14] = 0;
+    mTmpMatrix[15] = 1;
+    
+    Matrix.multiplyMM(mMVPMatrix, 0, matrix, 0, mTmpMatrix, 0);  
+    for(int j=0; j<16; j++) matrix[j] = mMVPMatrix[j];   
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Only max Byte.MAX_VALUE concurrent effects per bitmap.
+// If you want more, change type of the mNumEffects, mIDIndex and mFreeIndexes variables to shorts.
+  
+  static boolean setMax(int m)
+    {
+    if( (mCreated==false && !Distorted.isInitialized()) || m<=mMax[INDEX] )
+      {
+           if( m<0              ) m = 0;
+      else if( m>Byte.MAX_VALUE ) m = Byte.MAX_VALUE;
+      
+      mMax[INDEX] = m;
+      return true;
+      }
+   
+    return false;
+    }
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static int getMax()
+    {
+    return mMax[INDEX];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void getUniforms(int mProgramH)
+    {
+    mBmpDH     = GLES20.glGetUniformLocation(mProgramH, "u_bmpD");
+    mDepthH    = GLES20.glGetUniformLocation(mProgramH, "u_Depth");
+    mMVPMatrixH= GLES20.glGetUniformLocation(mProgramH, "u_MVPMatrix");
+    mMVMatrixH = GLES20.glGetUniformLocation(mProgramH, "u_MVMatrix"); 
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  synchronized void compute(long currTime) 
+    {
+    if( currTime==mTime ) return;
+    if( mTime==0 ) mTime = currTime;
+    long step = (currTime-mTime);
+   
+    for(int i=0; i<mNumEffects; i++)
+      {
+      if( mInterI[i]==null ) continue;    
+           
+      if( mInterP[i]!=null ) 
+        {
+        mInterP[i].interpolateMain(mUniforms, NUM_UNIFORMS*i, mCurrentDuration[i]);
+        }
+        
+      if( mInterI[i].interpolateMain(mUniforms ,NUM_UNIFORMS*i+3, mCurrentDuration[i], step) )      
+        {   
+        for(int j=0; j<mNumListeners; j++)   
+          EffectMessageSender.newMessage( mListeners.elementAt(j),
+                                          EffectMessage.EFFECT_FINISHED, 
+                                         (mID[i]<<EffectTypes.LENGTH)+EffectTypes.MATRIX.type,
+                                          mType[i], 
+                                          mBitmapID); 
+       
+        if( EffectNames.isUnity(mType[i], mUniforms, NUM_UNIFORMS*i+3) )
+          {  
+          remove(i);
+          i--;
+          continue;
+          }
+        }
+    
+      mCurrentDuration[i] += step;
+      }
+     
+    mTime = currTime;  
+    }  
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  protected void moveEffect(int index)
+    {
+    mUniforms[NUM_UNIFORMS*index  ] = mUniforms[NUM_UNIFORMS*(index+1)  ];
+    mUniforms[NUM_UNIFORMS*index+1] = mUniforms[NUM_UNIFORMS*(index+1)+1];
+    mUniforms[NUM_UNIFORMS*index+2] = mUniforms[NUM_UNIFORMS*(index+1)+2];
+    mUniforms[NUM_UNIFORMS*index+3] = mUniforms[NUM_UNIFORMS*(index+1)+3];
+    mUniforms[NUM_UNIFORMS*index+4] = mUniforms[NUM_UNIFORMS*(index+1)+4];
+    mUniforms[NUM_UNIFORMS*index+5] = mUniforms[NUM_UNIFORMS*(index+1)+5];
+    mUniforms[NUM_UNIFORMS*index+6] = mUniforms[NUM_UNIFORMS*(index+1)+6];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// here construct the ModelView Matrix
+
+  synchronized void send(float[] viewMatrix, DistortedProjection dp) 
+    {
+    Matrix.setIdentityM(viewMatrix, 0);
+    Matrix.translateM(viewMatrix, 0, -dp.width/2, dp.height/2, -dp.distance);
+    
+    float x,y,z, sx,sy,sz=1.0f;
+   
+    for(int i=0; i<mNumEffects; i++)
+      {
+      if (mType[i] == EffectNames.ROTATE.ordinal() )
+        {
+        x = mUniforms[NUM_UNIFORMS*i  ];
+        y = mUniforms[NUM_UNIFORMS*i+1];
+        z = mUniforms[NUM_UNIFORMS*i+2];
+     
+        Matrix.translateM(viewMatrix, 0, x,-y, z); 
+        Matrix.rotateM( viewMatrix, 0, mUniforms[NUM_UNIFORMS*i+3], mUniforms[NUM_UNIFORMS*i+4], mUniforms[NUM_UNIFORMS*i+5], mUniforms[NUM_UNIFORMS*i+6]);  
+        Matrix.translateM(viewMatrix, 0,-x, y,-z);  
+        }
+      else if(mType[i] == EffectNames.QUATERNION.ordinal() )
+        {
+        x = mUniforms[NUM_UNIFORMS*i  ];
+        y = mUniforms[NUM_UNIFORMS*i+1];
+        z = mUniforms[NUM_UNIFORMS*i+2];
+     	
+        Matrix.translateM(viewMatrix, 0, x,-y, z); 
+        multiplyByQuat(viewMatrix, mUniforms[NUM_UNIFORMS*i+3], mUniforms[NUM_UNIFORMS*i+4], mUniforms[NUM_UNIFORMS*i+5], mUniforms[NUM_UNIFORMS*i+6]);
+        Matrix.translateM(viewMatrix, 0,-x, y,-z);  
+        }
+      else if(mType[i] == EffectNames.MOVE.ordinal() )
+        {
+        sx = mUniforms[NUM_UNIFORMS*i+3];   
+        sy = mUniforms[NUM_UNIFORMS*i+4];   
+        sz = mUniforms[NUM_UNIFORMS*i+5];   
+        
+        Matrix.translateM(viewMatrix, 0, sx,-sy, sz);   
+        }
+      else if(mType[i] == EffectNames.SCALE.ordinal() )
+        {
+        sx = mUniforms[NUM_UNIFORMS*i+3];   
+        sy = mUniforms[NUM_UNIFORMS*i+4];   
+        sz = mUniforms[NUM_UNIFORMS*i+5];   
+
+        Matrix.scaleM(viewMatrix, 0, sx, sy, sz);  
+        }
+      else if(mType[i] == EffectNames.SHEAR.ordinal() )
+        {
+        x  = mUniforms[NUM_UNIFORMS*i  ];
+        y  = mUniforms[NUM_UNIFORMS*i+1];
+        z  = mUniforms[NUM_UNIFORMS*i+2];
+        
+        sx = mUniforms[NUM_UNIFORMS*i+3];   
+        sy = mUniforms[NUM_UNIFORMS*i+4];   
+        sz = mUniforms[NUM_UNIFORMS*i+5];   
+        
+        Matrix.translateM(viewMatrix, 0, x,-y, z); 
+      
+        viewMatrix[4] += sx*viewMatrix[0]; // Multiply viewMatrix by 1 x 0 0 , i.e. X-shear. TODO: change this so it is symmetric w respect to all the axis.
+        viewMatrix[5] += sx*viewMatrix[1]; //                        0 1 0 0 
+        viewMatrix[6] += sx*viewMatrix[2]; //                        0 0 1 0
+        viewMatrix[7] += sx*viewMatrix[3]; //                        0 0 0 1
+      
+        viewMatrix[0] += sy*viewMatrix[4]; // Multiply viewMatrix by 1 0 0 0 , i.e. Y-shear. TODO: change this so it is symmetric w respect to all the axis.
+        viewMatrix[1] += sy*viewMatrix[5]; //                        y 1 0 0
+        viewMatrix[2] += sy*viewMatrix[6]; //                        0 0 1 0
+        viewMatrix[3] += sy*viewMatrix[7]; //                        0 0 0 1      
+      
+        // TODO: implement Z-shear.
+        
+        Matrix.translateM(viewMatrix, 0,-x, y, -z);
+        }
+      }
+   
+    Matrix.translateM(viewMatrix, 0, mObjHalfX,-mObjHalfY, -mObjHalfZ);
+    Matrix.multiplyMM(mMVPMatrix, 0, dp.projectionMatrix, 0, viewMatrix, 0);
+    
+    GLES20.glUniform3f( mBmpDH , mObjHalfX, mObjHalfY, mObjHalfZ);
+    GLES20.glUniform1f( mDepthH, dp.depth);   
+    GLES20.glUniformMatrix4fv(mMVMatrixH , 1, false, viewMatrix, 0);
+    GLES20.glUniformMatrix4fv(mMVPMatrixH, 1, false, mMVPMatrix, 0);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// here construct the ModelView Matrix, but without any effects
+
+  synchronized void sendNoEffects(DistortedProjection dp) 
+    {
+    Matrix.setIdentityM(mTmpMatrix, 0);
+    Matrix.translateM(mTmpMatrix, 0, mObjHalfX-dp.width/2, dp.height/2-mObjHalfY, mObjHalfZ-dp.distance);
+    Matrix.multiplyMM(mMVPMatrix, 0, dp.projectionMatrix, 0, mTmpMatrix, 0);
+    
+    GLES20.glUniform3f( mBmpDH , mObjHalfX, mObjHalfY, mObjHalfZ);
+    GLES20.glUniform1f( mDepthH, dp.depth);  
+    GLES20.glUniformMatrix4fv(mMVMatrixH , 1, false, mTmpMatrix, 0);
+    GLES20.glUniformMatrix4fv(mMVPMatrixH, 1, false, mMVPMatrix, 0);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  synchronized long add(EffectNames eln, Interpolator3D p, Interpolator i)
+    {
+    if( mMax[INDEX]>mNumEffects )
+      {
+      mInterP[mNumEffects] = p;
+      mInterI[mNumEffects] = i;
+      
+      return addBase(eln);
+      }
+      
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  synchronized long add(EffectNames eln, float x, float y, float z, Interpolator i)
+    {
+    if( mMax[INDEX]>mNumEffects )
+      {
+      mInterP[mNumEffects] = null;
+      mInterI[mNumEffects] = i;
+      
+      mUniforms[NUM_UNIFORMS*mNumEffects  ] = x;
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = y;
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = z;
+            
+      return addBase(eln);
+      }
+      
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  synchronized long add(EffectNames eln, float x, float y, float z, Interpolator1D i, float aX, float aY, float aZ)
+    {
+    if( mMax[INDEX]>mNumEffects )
+      {
+      mInterP[mNumEffects] = null;
+      mInterI[mNumEffects] = i;
+      
+      mUniforms[NUM_UNIFORMS*mNumEffects  ] = x;
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = y;
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = z;
+      
+      mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
+      mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
+      mUniforms[NUM_UNIFORMS*mNumEffects+6] = aZ;  
+      
+      return addBase(eln);
+      }
+      
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  synchronized long add(EffectNames eln, Interpolator3D p, Interpolator1D i, float aX, float aY, float aZ)
+    {
+    if( mMax[INDEX]>mNumEffects )
+      {
+      mInterP[mNumEffects] = p;
+      mInterI[mNumEffects] = i;
+      
+      mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
+      mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
+      mUniforms[NUM_UNIFORMS*mNumEffects+6] = aZ;  
+      
+      return addBase(eln);
+      }
+      
+    return -1;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  synchronized long add(EffectNames eln, float x, float y, float z, float aA, float aX, float aY, float aZ)
+    {
+    if( mMax[INDEX]>mNumEffects )
+      {
+      mInterP[mNumEffects] = null; 
+      mInterI[mNumEffects] = null;
+      
+      mUniforms[NUM_UNIFORMS*mNumEffects  ] =  x;
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] =  y;  
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] =  z;  
+      mUniforms[NUM_UNIFORMS*mNumEffects+3] = aA;  
+      mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
+      mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
+      mUniforms[NUM_UNIFORMS*mNumEffects+6] = aZ;  
+      
+      return addBase(eln);   
+      }
+      
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  }
diff --git a/src/main/java/org/distorted/library/EffectListOther.java b/src/main/java/org/distorted/library/EffectListOther.java
new file mode 100644
index 0000000..21a0dcb
--- /dev/null
+++ b/src/main/java/org/distorted/library/EffectListOther.java
@@ -0,0 +1,97 @@
+package org.distorted.library;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+import java.util.Vector;
+
+/**
+ * Do NOT base this on EffectList - this is an entirely different animal than the first 3 EffectLists.
+ * The Effects in here will be executed after all the shaders have been executed - thus there are no
+ * uniforms to send, no real queues to maintain.
+ * <p>
+ * Only 2 effects here ATM:
+ * - save current Surface to a PNG file
+ * - save current animation to a .MP4 file
+ *
+ * In contrast to the other EffectLists, only one instance of each allowed at any given moment - thus
+ * this is not even a real EffectList, it is named so only for consistency with the others.
+ */
+public class EffectListOther
+  {
+  private Vector<EffectListener> mListeners =null;
+  private int mNumListeners=0;  // ==mListeners.length(), but we only create mListeners if the first one gets added
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public EffectListOther(DistortedObject obj)
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void send()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void abortAll()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void addListener(EffectListener el)
+    {
+    if( mListeners==null ) mListeners = new Vector<>(2,2);
+
+    mListeners.add(el);
+    mNumListeners++;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void removeListener(EffectListener el)
+    {
+    if( mNumListeners>0 )
+      {
+      mListeners.remove(el);
+      mNumListeners--;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized boolean removeByID(long id)
+    {
+    //....
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized boolean removeByType(EffectNames effect)
+    {
+    // ...
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  protected boolean printByID(long id)
+    {
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized long add(EffectNames eln, String filename)
+    {
+    return 0;
+    }
+  }
diff --git a/src/main/java/org/distorted/library/EffectListPostShader.java b/src/main/java/org/distorted/library/EffectListPostShader.java
deleted file mode 100644
index 5dbaad0..0000000
--- a/src/main/java/org/distorted/library/EffectListPostShader.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package org.distorted.library;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-import java.util.Vector;
-
-/**
- * Do NOT base this on EffectList - this is an entirely different animal than the first 3 EffectLists.
- * The Effects in here will be executed after all the shaders have been executed - thus there are no
- * uniforms to send, no real queues to maintain.
- * <p>
- * Only 2 effects here ATM:
- * - save current Surface to a PNG file
- * - save current animation to a .MP4 file
- *
- * In contrast to the other EffectLists, only one instance of each allowed at any given moment - thus
- * this is not even a real EffectList, it is named so only for consistency with the others.
- */
-public class EffectListPostShader
-  {
-  private Vector<EffectListener> mListeners =null;
-  private int mNumListeners=0;  // ==mListeners.length(), but we only create mListeners if the first one gets added
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public EffectListPostShader(DistortedObject obj)
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized void send()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized void abortAll()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void addListener(EffectListener el)
-    {
-    if( mListeners==null ) mListeners = new Vector<>(2,2);
-
-    mListeners.add(el);
-    mNumListeners++;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void removeListener(EffectListener el)
-    {
-    if( mNumListeners>0 )
-      {
-      mListeners.remove(el);
-      mNumListeners--;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized boolean removeByID(long id)
-    {
-    //....
-
-    return false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized boolean removeByType(EffectNames effect)
-    {
-    // ...
-
-    return false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  protected boolean printByID(long id)
-    {
-    return false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized long add(EffectNames eln, String filename)
-    {
-    return 0;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/EffectListPreShader.java b/src/main/java/org/distorted/library/EffectListPreShader.java
deleted file mode 100644
index 76907da..0000000
--- a/src/main/java/org/distorted/library/EffectListPreShader.java
+++ /dev/null
@@ -1,344 +0,0 @@
-package org.distorted.library;
-
-import android.opengl.GLES20;
-import android.opengl.Matrix;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class EffectListPreShader extends EffectList
-  {   
-  private static final int NUM_UNIFORMS = 7; 
-  private static float[] mMVPMatrix= new float[16];
-  private static float[] mTmpMatrix= new float[16];
-  
-  private static int mBmpDH;      // This is a handle to half a bitmap dimensions
-  private static int mDepthH;     // Handle to the max Depth, i.e (farplane-nearplane)/2
-  private static int mMVPMatrixH; // pass in the transformation matrix
-  private static int mMVMatrixH;  // pass in the modelview matrix.
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  public EffectListPreShader(DistortedObject obj)
-    { 
-    super(obj,NUM_UNIFORMS, PRESHADER);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void multiplyByQuat(float[] matrix, float X, float Y, float Z, float W)
-    {
-    float xx= X * X;
-    float xy= X * Y;
-    float xz= X * Z;
-    float xw= X * W;
-    float yy= Y * Y;
-    float yz= Y * Z;
-    float yw= Y * W;
-    float zz= Z * Z;
-    float zw= Z * W;
-
-    mTmpMatrix[0]  = 1 - 2 * ( yy + zz );
-    mTmpMatrix[1]  =     2 * ( xy - zw );
-    mTmpMatrix[2]  =     2 * ( xz + yw );
-    mTmpMatrix[4]  =     2 * ( xy + zw );
-    mTmpMatrix[5]  = 1 - 2 * ( xx + zz );
-    mTmpMatrix[6]  =     2 * ( yz - xw );
-    mTmpMatrix[8]  =     2 * ( xz - yw );
-    mTmpMatrix[9]  =     2 * ( yz + xw );
-    mTmpMatrix[10] = 1 - 2 * ( xx + yy );
-    mTmpMatrix[3]  = mTmpMatrix[7] = mTmpMatrix[11] = mTmpMatrix[12] = mTmpMatrix[13] = mTmpMatrix[14] = 0;
-    mTmpMatrix[15] = 1;
-    
-    Matrix.multiplyMM(mMVPMatrix, 0, matrix, 0, mTmpMatrix, 0);  
-    for(int j=0; j<16; j++) matrix[j] = mMVPMatrix[j];   
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Only max Byte.MAX_VALUE concurrent effects per bitmap.
-// If you want more, change type of the mNumEffects, mIDIndex and mFreeIndexes variables to shorts.
-  
-  static boolean setMax(int m)
-    {
-    if( (mCreated==false && !Distorted.isInitialized()) || m<=mMax[PRESHADER] )
-      {
-           if( m<0              ) m = 0;
-      else if( m>Byte.MAX_VALUE ) m = Byte.MAX_VALUE;
-      
-      mMax[PRESHADER] = m;
-      return true;
-      }
-   
-    return false;
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static int getMax()
-    {
-    return mMax[PRESHADER];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void getUniforms(int mProgramH)
-    {
-    mBmpDH     = GLES20.glGetUniformLocation(mProgramH, "u_bmpD");
-    mDepthH    = GLES20.glGetUniformLocation(mProgramH, "u_Depth");
-    mMVPMatrixH= GLES20.glGetUniformLocation(mProgramH, "u_MVPMatrix");
-    mMVMatrixH = GLES20.glGetUniformLocation(mProgramH, "u_MVMatrix"); 
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  synchronized void compute(long currTime) 
-    {
-    if( currTime==mTime ) return;
-    if( mTime==0 ) mTime = currTime;
-    long step = (currTime-mTime);
-   
-    for(int i=0; i<mNumEffects; i++)
-      {
-      if( mInterI[i]==null ) continue;    
-           
-      if( mInterP[i]!=null ) 
-        {
-        mInterP[i].interpolateMain(mUniforms, NUM_UNIFORMS*i, mCurrentDuration[i]);
-        }
-        
-      if( mInterI[i].interpolateMain(mUniforms ,NUM_UNIFORMS*i+3, mCurrentDuration[i], step) )      
-        {   
-        for(int j=0; j<mNumListeners; j++)   
-          EffectMessageSender.newMessage( mListeners.elementAt(j),
-                                          EffectMessage.EFFECT_FINISHED, 
-                                         (mID[i]<<DistortedObject.TYPE_NUM)+Distorted.TYPE_PRE,
-                                          mType[i], 
-                                          mBitmapID); 
-       
-        if( EffectNames.isUnity(mType[i], mUniforms, NUM_UNIFORMS*i+3) )
-          {  
-          remove(i);
-          i--;
-          continue;
-          }
-        }
-    
-      mCurrentDuration[i] += step;
-      }
-     
-    mTime = currTime;  
-    }  
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  protected void moveEffect(int index)
-    {
-    mUniforms[NUM_UNIFORMS*index  ] = mUniforms[NUM_UNIFORMS*(index+1)  ];
-    mUniforms[NUM_UNIFORMS*index+1] = mUniforms[NUM_UNIFORMS*(index+1)+1];
-    mUniforms[NUM_UNIFORMS*index+2] = mUniforms[NUM_UNIFORMS*(index+1)+2];
-    mUniforms[NUM_UNIFORMS*index+3] = mUniforms[NUM_UNIFORMS*(index+1)+3];
-    mUniforms[NUM_UNIFORMS*index+4] = mUniforms[NUM_UNIFORMS*(index+1)+4];
-    mUniforms[NUM_UNIFORMS*index+5] = mUniforms[NUM_UNIFORMS*(index+1)+5];
-    mUniforms[NUM_UNIFORMS*index+6] = mUniforms[NUM_UNIFORMS*(index+1)+6];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// here construct the ModelView Matrix
-
-  synchronized void send(float[] viewMatrix, DistortedProjection dp) 
-    {
-    Matrix.setIdentityM(viewMatrix, 0);
-    Matrix.translateM(viewMatrix, 0, -dp.width/2, dp.height/2, -dp.distance);
-    
-    float x,y,z, sx,sy,sz=1.0f;
-   
-    for(int i=0; i<mNumEffects; i++)
-      {
-      if (mType[i] == EffectNames.ROTATE.ordinal() )
-        {
-        x = mUniforms[NUM_UNIFORMS*i  ];
-        y = mUniforms[NUM_UNIFORMS*i+1];
-        z = mUniforms[NUM_UNIFORMS*i+2];
-     
-        Matrix.translateM(viewMatrix, 0, x,-y, z); 
-        Matrix.rotateM( viewMatrix, 0, mUniforms[NUM_UNIFORMS*i+3], mUniforms[NUM_UNIFORMS*i+4], mUniforms[NUM_UNIFORMS*i+5], mUniforms[NUM_UNIFORMS*i+6]);  
-        Matrix.translateM(viewMatrix, 0,-x, y,-z);  
-        }
-      else if(mType[i] == EffectNames.QUATERNION.ordinal() )
-        {
-        x = mUniforms[NUM_UNIFORMS*i  ];
-        y = mUniforms[NUM_UNIFORMS*i+1];
-        z = mUniforms[NUM_UNIFORMS*i+2];
-     	
-        Matrix.translateM(viewMatrix, 0, x,-y, z); 
-        multiplyByQuat(viewMatrix, mUniforms[NUM_UNIFORMS*i+3], mUniforms[NUM_UNIFORMS*i+4], mUniforms[NUM_UNIFORMS*i+5], mUniforms[NUM_UNIFORMS*i+6]);
-        Matrix.translateM(viewMatrix, 0,-x, y,-z);  
-        }
-      else if(mType[i] == EffectNames.MOVE.ordinal() )
-        {
-        sx = mUniforms[NUM_UNIFORMS*i+3];   
-        sy = mUniforms[NUM_UNIFORMS*i+4];   
-        sz = mUniforms[NUM_UNIFORMS*i+5];   
-        
-        Matrix.translateM(viewMatrix, 0, sx,-sy, sz);   
-        }
-      else if(mType[i] == EffectNames.SCALE.ordinal() )
-        {
-        sx = mUniforms[NUM_UNIFORMS*i+3];   
-        sy = mUniforms[NUM_UNIFORMS*i+4];   
-        sz = mUniforms[NUM_UNIFORMS*i+5];   
-
-        Matrix.scaleM(viewMatrix, 0, sx, sy, sz);  
-        }
-      else if(mType[i] == EffectNames.SHEAR.ordinal() )
-        {
-        x  = mUniforms[NUM_UNIFORMS*i  ];
-        y  = mUniforms[NUM_UNIFORMS*i+1];
-        z  = mUniforms[NUM_UNIFORMS*i+2];
-        
-        sx = mUniforms[NUM_UNIFORMS*i+3];   
-        sy = mUniforms[NUM_UNIFORMS*i+4];   
-        sz = mUniforms[NUM_UNIFORMS*i+5];   
-        
-        Matrix.translateM(viewMatrix, 0, x,-y, z); 
-      
-        viewMatrix[4] += sx*viewMatrix[0]; // Multiply viewMatrix by 1 x 0 0 , i.e. X-shear. TODO: change this so it is symmetric w respect to all the axis.
-        viewMatrix[5] += sx*viewMatrix[1]; //                        0 1 0 0 
-        viewMatrix[6] += sx*viewMatrix[2]; //                        0 0 1 0
-        viewMatrix[7] += sx*viewMatrix[3]; //                        0 0 0 1
-      
-        viewMatrix[0] += sy*viewMatrix[4]; // Multiply viewMatrix by 1 0 0 0 , i.e. Y-shear. TODO: change this so it is symmetric w respect to all the axis.
-        viewMatrix[1] += sy*viewMatrix[5]; //                        y 1 0 0
-        viewMatrix[2] += sy*viewMatrix[6]; //                        0 0 1 0
-        viewMatrix[3] += sy*viewMatrix[7]; //                        0 0 0 1      
-      
-        // TODO: implement Z-shear.
-        
-        Matrix.translateM(viewMatrix, 0,-x, y, -z);
-        }
-      }
-   
-    Matrix.translateM(viewMatrix, 0, mObjHalfX,-mObjHalfY, -mObjHalfZ);
-    Matrix.multiplyMM(mMVPMatrix, 0, dp.projectionMatrix, 0, viewMatrix, 0);
-    
-    GLES20.glUniform3f( mBmpDH , mObjHalfX, mObjHalfY, mObjHalfZ);
-    GLES20.glUniform1f( mDepthH, dp.depth);   
-    GLES20.glUniformMatrix4fv(mMVMatrixH , 1, false, viewMatrix, 0);
-    GLES20.glUniformMatrix4fv(mMVPMatrixH, 1, false, mMVPMatrix, 0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// here construct the ModelView Matrix, but without any effects
-
-  synchronized void sendNoEffects(DistortedProjection dp) 
-    {
-    Matrix.setIdentityM(mTmpMatrix, 0);
-    Matrix.translateM(mTmpMatrix, 0, mObjHalfX-dp.width/2, dp.height/2-mObjHalfY, mObjHalfZ-dp.distance);
-    Matrix.multiplyMM(mMVPMatrix, 0, dp.projectionMatrix, 0, mTmpMatrix, 0);
-    
-    GLES20.glUniform3f( mBmpDH , mObjHalfX, mObjHalfY, mObjHalfZ);
-    GLES20.glUniform1f( mDepthH, dp.depth);  
-    GLES20.glUniformMatrix4fv(mMVMatrixH , 1, false, mTmpMatrix, 0);
-    GLES20.glUniformMatrix4fv(mMVPMatrixH, 1, false, mMVPMatrix, 0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  synchronized long add(EffectNames eln, Interpolator3D p, Interpolator i)
-    {
-    if( mMax[PRESHADER]>mNumEffects )
-      {
-      mInterP[mNumEffects] = p;
-      mInterI[mNumEffects] = i;
-      
-      return addBase(eln);
-      }
-      
-    return -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  synchronized long add(EffectNames eln, float x, float y, float z, Interpolator i)
-    {
-    if( mMax[PRESHADER]>mNumEffects )
-      {
-      mInterP[mNumEffects] = null;
-      mInterI[mNumEffects] = i;
-      
-      mUniforms[NUM_UNIFORMS*mNumEffects  ] = x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = z;
-            
-      return addBase(eln);
-      }
-      
-    return -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  synchronized long add(EffectNames eln, float x, float y, float z, Interpolator1D i, float aX, float aY, float aZ)
-    {
-    if( mMax[PRESHADER]>mNumEffects )
-      {
-      mInterP[mNumEffects] = null;
-      mInterI[mNumEffects] = i;
-      
-      mUniforms[NUM_UNIFORMS*mNumEffects  ] = x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = z;
-      
-      mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
-      mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
-      mUniforms[NUM_UNIFORMS*mNumEffects+6] = aZ;  
-      
-      return addBase(eln);
-      }
-      
-    return -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  synchronized long add(EffectNames eln, Interpolator3D p, Interpolator1D i, float aX, float aY, float aZ)
-    {
-    if( mMax[PRESHADER]>mNumEffects )
-      {
-      mInterP[mNumEffects] = p;
-      mInterI[mNumEffects] = i;
-      
-      mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
-      mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
-      mUniforms[NUM_UNIFORMS*mNumEffects+6] = aZ;  
-      
-      return addBase(eln);
-      }
-      
-    return -1;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  synchronized long add(EffectNames eln, float x, float y, float z, float aA, float aX, float aY, float aZ)
-    {
-    if( mMax[PRESHADER]>mNumEffects )
-      {
-      mInterP[mNumEffects] = null; 
-      mInterI[mNumEffects] = null;
-      
-      mUniforms[NUM_UNIFORMS*mNumEffects  ] =  x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] =  y;  
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] =  z;  
-      mUniforms[NUM_UNIFORMS*mNumEffects+3] = aA;  
-      mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
-      mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
-      mUniforms[NUM_UNIFORMS*mNumEffects+6] = aZ;  
-      
-      return addBase(eln);   
-      }
-      
-    return -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  }
diff --git a/src/main/java/org/distorted/library/EffectListVertex.java b/src/main/java/org/distorted/library/EffectListVertex.java
index 063e3c6..bbb7309 100644
--- a/src/main/java/org/distorted/library/EffectListVertex.java
+++ b/src/main/java/org/distorted/library/EffectListVertex.java
@@ -6,7 +6,8 @@ import android.opengl.GLES20;
 
 class EffectListVertex extends EffectList
   { 
-  private static final int NUM_UNIFORMS = 9;    
+  private static final int NUM_UNIFORMS = 9;
+  private static final int INDEX = EffectTypes.VERTEX.ordinal();
   private static int mNumEffectsH;
   private static int mTypeH;
   private static int mUniformsH;
@@ -15,7 +16,7 @@ class EffectListVertex extends EffectList
    
   public EffectListVertex(DistortedObject obj)
     { 
-    super(obj,NUM_UNIFORMS,VERTEX);
+    super(obj,NUM_UNIFORMS,INDEX);
     }
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -24,12 +25,12 @@ class EffectListVertex extends EffectList
   
   static boolean setMax(int m)
     {
-    if( (mCreated==false && !Distorted.isInitialized()) || m<=mMax[VERTEX] ) 
+    if( (mCreated==false && !Distorted.isInitialized()) || m<=mMax[INDEX] )
       {
            if( m<0              ) m = 0;
       else if( m>Byte.MAX_VALUE ) m = Byte.MAX_VALUE;
       
-      mMax[VERTEX] = m;
+      mMax[INDEX] = m;
       return true;
       }
    
@@ -40,7 +41,7 @@ class EffectListVertex extends EffectList
 
   static int getMax()
     {
-    return mMax[VERTEX];
+    return mMax[INDEX];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -77,7 +78,7 @@ class EffectListVertex extends EffectList
         for(int j=0; j<mNumListeners; j++)   
           EffectMessageSender.newMessage( mListeners.elementAt(j),
                                           EffectMessage.EFFECT_FINISHED, 
-                                         (mID[i]<<DistortedObject.TYPE_NUM)+Distorted.TYPE_VERT, 
+                                         (mID[i]<<EffectTypes.LENGTH)+EffectTypes.VERTEX.type,
                                           mType[i], 
                                           mBitmapID); 
       
@@ -154,7 +155,7 @@ class EffectListVertex extends EffectList
   
   synchronized long add(EffectNames eln, Interpolator inter, Float4D region, Interpolator2D point)
     {
-    if( mMax[VERTEX]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       EffectNames.fillWithUnities(eln.ordinal(), mUniforms, NUM_UNIFORMS*mNumEffects);    
       
@@ -171,7 +172,7 @@ class EffectListVertex extends EffectList
   
   synchronized long add(EffectNames eln, Interpolator inter, Float4D region, float x, float y)
     {
-    if( mMax[VERTEX]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       EffectNames.fillWithUnities(eln.ordinal(), mUniforms, NUM_UNIFORMS*mNumEffects);    
       
@@ -190,7 +191,7 @@ class EffectListVertex extends EffectList
   
   synchronized long add(EffectNames eln, float v1, float v2, float v3, Float4D region, float x, float y)
     {
-    if( mMax[VERTEX]>mNumEffects )
+    if( mMax[INDEX]>mNumEffects )
       {
       EffectNames.fillWithUnities(eln.ordinal(), mUniforms, NUM_UNIFORMS*mNumEffects); 
       mUniforms[NUM_UNIFORMS*mNumEffects  ] = v1;
diff --git a/src/main/java/org/distorted/library/EffectNames.java b/src/main/java/org/distorted/library/EffectNames.java
index ef46dde..19d6012 100644
--- a/src/main/java/org/distorted/library/EffectNames.java
+++ b/src/main/java/org/distorted/library/EffectNames.java
@@ -6,44 +6,44 @@ enum EffectNames
   {
   // EFFECT NAME /////// EFFECT TYPE ////////////// UNITY /////////////////////////
    
-  ROTATE           ( Distorted.TYPE_PRE ,   new float[] {0.0f}           ),
-  QUATERNION       ( Distorted.TYPE_PRE ,   new float[] {0.0f,0.0f,0.0f} ),      // quaternion is a unity iff its axis (x,y,z) is (0,0,0)
-  MOVE             ( Distorted.TYPE_PRE ,   new float[] {0.0f,0.0f,0.0f} ),
-  SCALE            ( Distorted.TYPE_PRE ,   new float[] {1.0f,1.0f,1.0f} ),
-  SHEAR            ( Distorted.TYPE_PRE ,   new float[] {0.0f,0.0f,0.0f} ),
-  // add new PreShader effects here...
+  ROTATE           ( EffectTypes.MATRIX  ,   new float[] {0.0f}           ),
+  QUATERNION       ( EffectTypes.MATRIX  ,   new float[] {0.0f,0.0f,0.0f} ),      // quaternion is a unity iff its axis (x,y,z) is (0,0,0)
+  MOVE             ( EffectTypes.MATRIX  ,   new float[] {0.0f,0.0f,0.0f} ),
+  SCALE            ( EffectTypes.MATRIX  ,   new float[] {1.0f,1.0f,1.0f} ),
+  SHEAR            ( EffectTypes.MATRIX  ,   new float[] {0.0f,0.0f,0.0f} ),
+  // add new Matrix effects here...
   
-  DISTORT          ( Distorted.TYPE_VERT,   new float[] {0.0f,0.0f,0.0f} ),      // keep this the first VERT effect (reason: getType)
-  DEFORM           ( Distorted.TYPE_VERT,   new float[] {0.0f,0.0f}      ),
-  SINK             ( Distorted.TYPE_VERT,   new float[] {1.0f}           ),
-  SWIRL            ( Distorted.TYPE_VERT,   new float[] {0.0f}           ),
-  WAVE             ( Distorted.TYPE_VERT,   new float[] {0.0f}           ),
+  DISTORT          ( EffectTypes.VERTEX  ,   new float[] {0.0f,0.0f,0.0f} ),      // keep this the first VERT effect (reason: getType)
+  DEFORM           ( EffectTypes.VERTEX  ,   new float[] {0.0f,0.0f}      ),
+  SINK             ( EffectTypes.VERTEX  ,   new float[] {1.0f}           ),
+  SWIRL            ( EffectTypes.VERTEX  ,   new float[] {0.0f}           ),
+  WAVE             ( EffectTypes.VERTEX  ,   new float[] {0.0f}           ),
   // add new Vertex Effects here...
   
-  MACROBLOCK       ( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),      // keep this the first FRAG effect (reason: getType)
-  ALPHA            ( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  SMOOTH_ALPHA     ( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  CHROMA           ( Distorted.TYPE_FRAG,   new float[] {0.0f}           ),
-  SMOOTH_CHROMA    ( Distorted.TYPE_FRAG,   new float[] {0.0f}           ),
-  BRIGHTNESS       ( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  SMOOTH_BRIGHTNESS( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  SATURATION       ( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  SMOOTH_SATURATION( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  CONTRAST         ( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  SMOOTH_CONTRAST  ( Distorted.TYPE_FRAG,   new float[] {1.0f}           ),
-  HUE              ( Distorted.TYPE_FRAG,   new float[] {0.0f}           ),
-  SMOOTH_HUE       ( Distorted.TYPE_FRAG,   new float[] {0.0f}           ),
+  MACROBLOCK       ( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),      // keep this the first FRAG effect (reason: getType)
+  ALPHA            ( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  SMOOTH_ALPHA     ( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  CHROMA           ( EffectTypes.FRAGMENT,   new float[] {0.0f}           ),
+  SMOOTH_CHROMA    ( EffectTypes.FRAGMENT,   new float[] {0.0f}           ),
+  BRIGHTNESS       ( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  SMOOTH_BRIGHTNESS( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  SATURATION       ( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  SMOOTH_SATURATION( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  CONTRAST         ( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  SMOOTH_CONTRAST  ( EffectTypes.FRAGMENT,   new float[] {1.0f}           ),
+  HUE              ( EffectTypes.FRAGMENT,   new float[] {0.0f}           ),
+  SMOOTH_HUE       ( EffectTypes.FRAGMENT,   new float[] {0.0f}           ),
   // add new Fragment effects here...
 
-  SAVE_PNG         ( Distorted.TYPE_POST,   null                         ),      // PostShader Effects don't have Unities.
-  SAVE_MP4         ( Distorted.TYPE_POST,   null                         );      //
-  // add new PostShader effects here...
+  SAVE_PNG         ( EffectTypes.OTHER   ,   null                         ),      // OTHER Effects don't have Unities.
+  SAVE_MP4         ( EffectTypes.OTHER   ,   null                         );      //
+  // add new Other effects here...
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
   
   private static final int MAXDIM = 4;  // maximum supported dimension of an effect  
   
-  private int type;
+  private EffectTypes type;
   private float[] unity;
   
   static private float[] unities;
@@ -76,7 +76,7 @@ enum EffectNames
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
   
-  private EffectNames(int type, float[] unity)
+  EffectNames(EffectTypes type, float[] unity)
     {
     this.type = type;  
     this.unity= unity;
@@ -84,20 +84,20 @@ enum EffectNames
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
   
-  public int getType()
+  public EffectTypes getType()
     {
     return type;  
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
   
-  static int getType(int ordinal)
+  static EffectTypes getType(int ordinal)
     {
-    if( ordinal<DISTORT.ordinal()     ) return Distorted.TYPE_PRE;
-    if( ordinal<MACROBLOCK.ordinal()  ) return Distorted.TYPE_VERT;
-    if( ordinal<SAVE_PNG.ordinal()    ) return Distorted.TYPE_FRAG;
+    if( ordinal<DISTORT.ordinal()     ) return EffectTypes.MATRIX;
+    if( ordinal<MACROBLOCK.ordinal()  ) return EffectTypes.VERTEX;
+    if( ordinal<SAVE_PNG.ordinal()    ) return EffectTypes.FRAGMENT;
 
-    return Distorted.TYPE_POST;
+    return EffectTypes.OTHER;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/EffectTypes.java b/src/main/java/org/distorted/library/EffectTypes.java
new file mode 100644
index 0000000..e9ab8cd
--- /dev/null
+++ b/src/main/java/org/distorted/library/EffectTypes.java
@@ -0,0 +1,39 @@
+package org.distorted.library;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public enum EffectTypes
+  {
+  MATRIX   ( 0x1 ),  // values will be bitwise ORed and ANDed
+  VERTEX   ( 0x2 ),  // so need to be 1,2,4,8...
+  FRAGMENT ( 0x4 ),  //
+  OTHER    ( 0x8 );  //
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  final static int LENGTH = values().length;  // The number of effect types.
+  final static int MASK= (1<<LENGTH)-1;       // Needed when we do bitwise operations on Effect Types.
+
+  public final int type;
+
+  EffectTypes(int type)
+    {
+    this.type = type;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// called from EffectList
+
+  static void reset(int[] maxtable)
+    {
+    maxtable[0] = 5;  // By default, there can be a maximum 5 MATRIX effects acting on a single
+                      // DistortedObject at any given time. This can be changed with a call to
+                      // EffectListMatrix.setMax(int)
+
+    maxtable[1] = 5;  // Max 5 VERTEX Effects
+    maxtable[2] = 5;  // Max 5 FRAGMENT Effects
+    maxtable[3] = 5;  // Max 5 OTHER Effects
+    }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  }
+
