commit 02de77c9c8cc3739793cfa79bc8b46d6b2eba8e8
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Tue Jan 10 23:53:31 2017 +0000

    Progress with multi-program rendering.

diff --git a/src/main/java/org/distorted/library/Distorted.java b/src/main/java/org/distorted/library/Distorted.java
index c34d1ac..05d87b6 100644
--- a/src/main/java/org/distorted/library/Distorted.java
+++ b/src/main/java/org/distorted/library/Distorted.java
@@ -77,10 +77,7 @@ public class Distorted
    */
   public static final int CLONE_CHILDREN= 0x20;
 
-  static int[] mMainProgramAttributes;
-  static int[] mPostProgramAttributes;
-
-  static int mainProgramH, postProgramH;
+  static DistortedProgram mMainProgram, mPostProgram;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // private: hide this from Javadoc
@@ -94,7 +91,7 @@ public class Distorted
 
   static boolean isInitialized()
     {
-    return (mMainProgramAttributes!=null);
+    return (mMainProgram!=null);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -135,11 +132,8 @@ public class Distorted
         mainFragmentHeader += ("#define "+name.name()+" "+name.ordinal()+"\n");
       }
 
-    DistortedProgram mainProgram = new DistortedProgram(mainVertexStream,mainFragmentStream, mainVertexHeader, mainFragmentHeader);
-    mainProgramH = mainProgram.getProgramHandle();
-    GLES20.glUseProgram(mainProgramH);
-    mainProgram.bindAndEnableAttributes();
-    mMainProgramAttributes = mainProgram.getAttributes();
+    mMainProgram = new DistortedProgram(mainVertexStream,mainFragmentStream, mainVertexHeader, mainFragmentHeader);
+    int mainProgramH = mMainProgram.getProgramHandle();
 
     GLES20.glDepthFunc(GLES20.GL_LEQUAL);
     GLES20.glEnable(GLES20.GL_BLEND);
@@ -164,11 +158,8 @@ public class Distorted
         postFragmentHeader += ("#define "+name.name()+" "+name.ordinal()+"\n");
       }
 
-    DistortedProgram postProgram = new DistortedProgram(postVertexStream,postFragmentStream, "", postFragmentHeader);
-    postProgramH = postProgram.getProgramHandle();
-    GLES20.glUseProgram(postProgramH);
-    postProgram.bindAndEnableAttributes();
-    mPostProgramAttributes = postProgram.getAttributes();
+    mPostProgram = new DistortedProgram(postVertexStream,postFragmentStream, "", postFragmentHeader);
+    int postProgramH = mPostProgram.getProgramHandle();
 
     EffectQueuePostprocess.getUniforms(postProgramH);
 
@@ -190,7 +181,7 @@ public class Distorted
     DistortedEffects.onDestroy();
     EffectMessageSender.stopSending();
 
-    mMainProgramAttributes = null;
-    mPostProgramAttributes = null;
+    mMainProgram = null;
+    mPostProgram = null;
     }
   }
diff --git a/src/main/java/org/distorted/library/DistortedEffects.java b/src/main/java/org/distorted/library/DistortedEffects.java
index 7aa98d4..ff168df 100644
--- a/src/main/java/org/distorted/library/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/DistortedEffects.java
@@ -133,11 +133,11 @@ public class DistortedEffects
 
     float halfZ = halfInputW*mesh.zFactor;
 
-    GLES20.glUseProgram(Distorted.mainProgramH);
-    GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
-
     if( mP.mNumEffects==0 )
       {
+      Distorted.mMainProgram.useProgram();
+      GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
+
       mM.send(df,halfInputW,halfInputH,halfZ);
       mV.send(halfInputW,halfInputH,halfZ);
       mF.send(halfInputW,halfInputH);
@@ -146,8 +146,11 @@ public class DistortedEffects
       }
     else
       {
-      if( mV.mNumEffects>0 || mP.mNumEffects>0 )
+      if( mV.mNumEffects>0 || mF.mNumEffects>0 )
         {
+        Distorted.mMainProgram.useProgram();
+        GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
+
         mBufferFBO.resizeFast(df.mWidth, df.mHeight);
         mBufferFBO.setAsOutput();
 
@@ -157,24 +160,25 @@ public class DistortedEffects
 
         mesh.draw();
 
-        GLES20.glUseProgram(Distorted.postProgramH);
-        GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
+        Distorted.mPostProgram.useProgram();
         mBufferFBO.setAsInput();
         df.setAsOutput();
         mP.send(df);
 
-        GLES20.glVertexAttribPointer(Distorted.mPostProgramAttributes[0], POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mQuadPositions);
-        GLES20.glVertexAttribPointer(Distorted.mPostProgramAttributes[1], TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mQuadTexture);
+        GLES20.glVertexAttribPointer(Distorted.mPostProgram.mAttribute[0], POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mQuadPositions);
+        GLES20.glVertexAttribPointer(Distorted.mPostProgram.mAttribute[1], TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mQuadTexture);
         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
         }
       else
         {
-        GLES20.glUseProgram(Distorted.postProgramH);
+        Distorted.mPostProgram.useProgram();
         GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
-        mP.send(df);
 
-        GLES20.glVertexAttribPointer(Distorted.mPostProgramAttributes[0], POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mQuadPositions);
-        GLES20.glVertexAttribPointer(Distorted.mPostProgramAttributes[1], TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mQuadTexture);
+        mM.constructMatrices(df,halfInputW,halfInputH);
+        mP.send(2*halfInputW,2*halfInputH, mM.getMVP() );
+
+        GLES20.glVertexAttribPointer(Distorted.mPostProgram.mAttribute[0], POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mQuadPositions);
+        GLES20.glVertexAttribPointer(Distorted.mPostProgram.mAttribute[1], TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mQuadTexture);
         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
         }
       }
diff --git a/src/main/java/org/distorted/library/DistortedFramebuffer.java b/src/main/java/org/distorted/library/DistortedFramebuffer.java
index b1d6869..485db14 100644
--- a/src/main/java/org/distorted/library/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/DistortedFramebuffer.java
@@ -154,16 +154,6 @@ public class DistortedFramebuffer
     mMarked = false;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void reset()
-    {
-    if( colorIds[0]!=DONT_CREATE )
-      colorIds[0] = NOT_CREATED_YET;
-
-    if( mDepthWanted ) depthIds[0] = NOT_CREATED_YET;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void setAsOutput()
@@ -228,15 +218,11 @@ public class DistortedFramebuffer
 
   static synchronized void onDestroy()
     {
-    // There are issues with this. Namely, if one
-    // 1. creates a DObjectTree (somewhere else than onSurfaceCreated of constructor so it does not get re-created on re-launch)
-    // 2. exits the app (here mList would be cleared)
-    // 3. re-launches the app
-    // 4. deletes some nodes
-    // then the underlying Framebuffers will never be deleted!
-
-    mListMarked = false;
-    mList.clear();
+    for( DistortedFramebuffer fbo : mList)
+      {
+      if( fbo.colorIds[0]!=DONT_CREATE ) fbo.colorIds[0] = NOT_CREATED_YET;
+      if( fbo.mDepthWanted             ) fbo.depthIds[0] = NOT_CREATED_YET;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/DistortedTree.java b/src/main/java/org/distorted/library/DistortedTree.java
index 8c52966..14b3956 100644
--- a/src/main/java/org/distorted/library/DistortedTree.java
+++ b/src/main/java/org/distorted/library/DistortedTree.java
@@ -160,11 +160,7 @@ public class DistortedTree
       {
       tmp = mMapNodeID.get(key);
           
-      if( tmp.mFBO != null )
-        {
-    	  tmp.mFBO.reset();
-        tmp.numRendered = 0;
-        }
+      if( tmp.mFBO != null ) tmp.numRendered = 0;
       }
     }
 
diff --git a/src/main/java/org/distorted/library/EffectQueueMatrix.java b/src/main/java/org/distorted/library/EffectQueueMatrix.java
index 202a6d1..4ac3899 100644
--- a/src/main/java/org/distorted/library/EffectQueueMatrix.java
+++ b/src/main/java/org/distorted/library/EffectQueueMatrix.java
@@ -105,77 +105,15 @@ class EffectQueueMatrix extends EffectQueue
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// here construct the ModelView and the ModelViewProjection Matrices
 
-  static void getUniforms(int mProgramH)
-    {
-    mObjDH     = GLES20.glGetUniformLocation(mProgramH, "u_objD");
-    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( mInter[0][i]!=null && mInter[0][i].interpolateMain(mUniforms ,NUM_UNIFORMS*i, 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,
-                                          mName[i],
-                                          mObjectID);
-
-        if( EffectNames.isUnity(mName[i], mUniforms, NUM_UNIFORMS*i) )
-          {
-          remove(i);
-          i--;
-          continue;
-          }
-        else mInter[0][i] = null;
-        }
-
-      if( mInter[1][i]!=null )
-        {
-        mInter[1][i].interpolateMain(mUniforms, NUM_UNIFORMS*i+4, mCurrentDuration[i], step);
-        }
-
-      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(DistortedFramebuffer df, float halfX, float halfY, float halfZ)
+  void constructMatrices(DistortedFramebuffer df, float halfX, float halfY)
     {
     Matrix.setIdentityM(mViewMatrix, 0);
     Matrix.translateM(mViewMatrix, 0, -df.mWidth/2, df.mHeight/2, -df.mDistance);
-    
+
     float x,y,z, sx,sy,sz;
-   
+
     for(int i=0; i<mNumEffects; i++)
       {
       if (mName[i] == EffectNames.ROTATE.ordinal() )
@@ -193,7 +131,7 @@ class EffectQueueMatrix extends EffectQueue
         x = mUniforms[NUM_UNIFORMS*i+4];
         y = mUniforms[NUM_UNIFORMS*i+5];
         z = mUniforms[NUM_UNIFORMS*i+6];
-     	
+
         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]);
         Matrix.translateM(mViewMatrix, 0,-x, y,-z);
@@ -203,7 +141,7 @@ class EffectQueueMatrix extends EffectQueue
         sx = mUniforms[NUM_UNIFORMS*i  ];
         sy = mUniforms[NUM_UNIFORMS*i+1];
         sz = mUniforms[NUM_UNIFORMS*i+2];
-        
+
         Matrix.translateM(mViewMatrix, 0, sx,-sy, sz);
         }
       else if(mName[i] == EffectNames.SCALE.ordinal() )
@@ -225,17 +163,17 @@ class EffectQueueMatrix extends EffectQueue
         z  = mUniforms[NUM_UNIFORMS*i+6];
 
         Matrix.translateM(mViewMatrix, 0, x,-y, z);
-      
+
         mViewMatrix[4] += sx*mViewMatrix[0]; // Multiply viewMatrix by 1 x 0 0 , i.e. X-shear.
         mViewMatrix[5] += sx*mViewMatrix[1]; //                        0 1 0 0
         mViewMatrix[6] += sx*mViewMatrix[2]; //                        0 0 1 0
         mViewMatrix[7] += sx*mViewMatrix[3]; //                        0 0 0 1
-      
+
         mViewMatrix[0] += sy*mViewMatrix[4]; // Multiply viewMatrix by 1 0 0 0 , i.e. Y-shear.
         mViewMatrix[1] += sy*mViewMatrix[5]; //                        y 1 0 0
         mViewMatrix[2] += sy*mViewMatrix[6]; //                        0 0 1 0
         mViewMatrix[3] += sy*mViewMatrix[7]; //                        0 0 0 1
-      
+
         mViewMatrix[4] += sz*mViewMatrix[8]; // Multiply viewMatrix by 1 0 0 0 , i.e. Z-shear.
         mViewMatrix[5] += sz*mViewMatrix[9]; //                        0 1 0 0
         mViewMatrix[6] += sz*mViewMatrix[10];//                        0 z 1 0
@@ -244,10 +182,86 @@ class EffectQueueMatrix extends EffectQueue
         Matrix.translateM(mViewMatrix, 0,-x, y, -z);
         }
       }
-   
+
     Matrix.translateM(mViewMatrix, 0, halfX,-halfY, 0);
     Matrix.multiplyMM(mMVPMatrix, 0, df.mProjectionMatrix, 0, mViewMatrix, 0);
-    
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void getUniforms(int mProgramH)
+    {
+    mObjDH     = GLES20.glGetUniformLocation(mProgramH, "u_objD");
+    mDepthH    = GLES20.glGetUniformLocation(mProgramH, "u_Depth");
+    mMVPMatrixH= GLES20.glGetUniformLocation(mProgramH, "u_MVPMatrix");
+    mMVMatrixH = GLES20.glGetUniformLocation(mProgramH, "u_MVMatrix"); 
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getMVP()
+    {
+    return mMVPMatrix;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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( mInter[0][i]!=null && mInter[0][i].interpolateMain(mUniforms ,NUM_UNIFORMS*i, 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,
+                                          mName[i],
+                                          mObjectID);
+
+        if( EffectNames.isUnity(mName[i], mUniforms, NUM_UNIFORMS*i) )
+          {
+          remove(i);
+          i--;
+          continue;
+          }
+        else mInter[0][i] = null;
+        }
+
+      if( mInter[1][i]!=null )
+        {
+        mInter[1][i].interpolateMain(mUniforms, NUM_UNIFORMS*i+4, mCurrentDuration[i], step);
+        }
+
+      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];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void send(DistortedFramebuffer df, float halfX, float halfY, float halfZ)
+    {
+    constructMatrices(df,halfX,halfY);
+
     GLES20.glUniform3f( mObjDH , halfX, halfY, halfZ);
     GLES20.glUniform1f( mDepthH, df.mDepth);
     GLES20.glUniformMatrix4fv(mMVMatrixH , 1, false, mViewMatrix, 0);
diff --git a/src/main/java/org/distorted/library/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
index 124832b..1dd4385 100644
--- a/src/main/java/org/distorted/library/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
@@ -113,6 +113,21 @@ class EffectQueuePostprocess extends EffectQueue
     mUniforms[NUM_UNIFORMS*index+2] = mUniforms[NUM_UNIFORMS*(index+1)+2];
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void send(float w, float h, float[] mvp)
+    {
+    GLES20.glUniform1i( mNumEffectsH, mNumEffects);
+    GLES20.glUniform2f( mObjDH , w, h );
+    GLES20.glUniformMatrix4fv(mMVPMatrixH, 1, false, mvp , 0);
+
+    if( mNumEffects>0 )
+      {
+      GLES20.glUniform1iv( mTypeH    ,  mNumEffects, mName    ,0);
+      GLES20.glUniform4fv( mUniformsH,2*mNumEffects, mUniforms,0);
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   synchronized void send(DistortedFramebuffer df)
diff --git a/src/main/java/org/distorted/library/MeshObject.java b/src/main/java/org/distorted/library/MeshObject.java
index 4f3d89c..6883cfd 100644
--- a/src/main/java/org/distorted/library/MeshObject.java
+++ b/src/main/java/org/distorted/library/MeshObject.java
@@ -55,9 +55,9 @@ public abstract class MeshObject
 
    void draw()
      { 
-     GLES20.glVertexAttribPointer(Distorted.mMainProgramAttributes[0], POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mMeshPositions);
-     GLES20.glVertexAttribPointer(Distorted.mMainProgramAttributes[1], NORMAL_DATA_SIZE  , GLES20.GL_FLOAT, false, 0, mMeshNormals);
-     GLES20.glVertexAttribPointer(Distorted.mMainProgramAttributes[2], TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mMeshTexture);
+     GLES20.glVertexAttribPointer(Distorted.mMainProgram.mAttribute[0], POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mMeshPositions);
+     GLES20.glVertexAttribPointer(Distorted.mMainProgram.mAttribute[1], NORMAL_DATA_SIZE  , GLES20.GL_FLOAT, false, 0, mMeshNormals);
+     GLES20.glVertexAttribPointer(Distorted.mMainProgram.mAttribute[2], TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mMeshTexture);
 
      GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, dataLength); 
      }
diff --git a/src/main/java/org/distorted/library/program/DistortedProgram.java b/src/main/java/org/distorted/library/program/DistortedProgram.java
index 49c0934..39629ec 100644
--- a/src/main/java/org/distorted/library/program/DistortedProgram.java
+++ b/src/main/java/org/distorted/library/program/DistortedProgram.java
@@ -23,8 +23,6 @@ import android.opengl.GLES20;
 import android.os.Build;
 
 import org.distorted.library.DistortedEffects;
-import org.distorted.library.EffectNames;
-import org.distorted.library.EffectTypes;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -40,9 +38,10 @@ public class DistortedProgram
   private int mProgramHandle;
 
   private int mNumAttributes;
-  private int[] mAttribute;
   private String[] mAttributeName;
 
+  public final int[] mAttribute;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes)
@@ -168,7 +167,6 @@ public class DistortedProgram
 
     if( doAttributes )
       {
-      mAttribute = new int[mNumAttributes];
       mAttributeName = attrList.split(" ");
       }
 
@@ -274,24 +272,13 @@ public class DistortedProgram
     final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
 
     mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName);
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the indexes off all attributes.
- */
-  public int[] getAttributes()
-    {
-    return mAttribute;
-    }
+    mAttribute = new int[mNumAttributes];
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the names of all vertex attributes.
- */
-  public String[] getAttributeNames()
-    {
-    return mAttributeName;
+    for(int i=0; i<mNumAttributes; i++)
+      {
+      mAttribute[i] = GLES20.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -305,18 +292,17 @@ public class DistortedProgram
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Bind all vertex attributes and enable them.
- * <p>
- * This assumes Program is in use. Call glUseProgram first.
+ * Use the program and enable all vertex attribute arrays.
+ *
  * Needs to be called from a thread holding the OpenGL context.
  */
-
-  public void bindAndEnableAttributes()
+  public void useProgram()
     {
+    GLES20.glUseProgram(mProgramHandle);
+
     for(int i=0; i<mNumAttributes; i++)
-      {
-      mAttribute[i] = GLES20.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
       GLES20.glEnableVertexAttribArray(mAttribute[i]);
-      }
     }
   }
+
+
