commit a51fe5214ea060a65ab141e6172558dcb75b85a5
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Tue May 9 13:35:02 2017 +0100

    Move from unpacked to packad server-side Vertex attribute buffer.

diff --git a/src/main/java/org/distorted/library/DistortedEffects.java b/src/main/java/org/distorted/library/DistortedEffects.java
index 3a8ecb8..ffbaa7a 100644
--- a/src/main/java/org/distorted/library/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/DistortedEffects.java
@@ -300,13 +300,11 @@ public class DistortedEffects
     mV.send(halfW,halfH,halfZ);
     mF.send(halfW,halfH);
 
-    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mesh.mPosVBO[0]);
-    GLES30.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POSITION_DATA_SIZE, GLES30.GL_FLOAT, false, 0, 0);
-    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mesh.mNorVBO[0]);
-    GLES30.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NORMAL_DATA_SIZE  , GLES30.GL_FLOAT, false, 0, 0);
-    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mesh.mTexVBO[0]);
-    GLES30.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE     , GLES30.GL_FLOAT, false, 0, 0);
-    GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.dataLength);
+    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mesh.mAttVBO[0]);
+    GLES30.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POSITION_DATA_SIZE, GLES30.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET0);
+    GLES30.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NORMAL_DATA_SIZE  , GLES30.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET1);
+    GLES30.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE     , GLES30.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET2);
+    GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.numVertices);
     GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0 );
 
     /// DEBUG ONLY //////
diff --git a/src/main/java/org/distorted/library/MeshCubes.java b/src/main/java/org/distorted/library/MeshCubes.java
index d0b16b0..a68a87a 100644
--- a/src/main/java/org/distorted/library/MeshCubes.java
+++ b/src/main/java/org/distorted/library/MeshCubes.java
@@ -185,9 +185,9 @@ public class MeshCubes extends MeshObject
 
    private void prepareDataStructures(int cols, String desc, boolean frontOnly)
      {
-     mRows     =0;
-     mCols     =0;
-     dataLength=0;
+     mRows      =0;
+     mCols      =0;
+     numVertices=0;
      
      if( cols>0 && desc.contains("1") )
        {
@@ -205,9 +205,9 @@ public class MeshCubes extends MeshObject
 //android.util.Log.d("cubes", "VERT STRING:"+debug(mBoundingVert,3));
 
        markRegions();
-       dataLength = computeDataLength(frontOnly);
+       numVertices = computeDataLength(frontOnly);
 
-       remainingVert = dataLength;
+       remainingVert = numVertices;
        }
      }
 
@@ -216,9 +216,9 @@ public class MeshCubes extends MeshObject
 
    private void prepareDataStructures(int cols, int rows, boolean frontOnly)
      {
-     mRows     =rows;
-     mCols     =cols;
-     dataLength=   0;
+     mRows       =rows;
+     mCols       =cols;
+     numVertices =   0;
 
      if( cols>0 && rows>0 )
        {
@@ -233,9 +233,9 @@ public class MeshCubes extends MeshObject
 //android.util.Log.d("cubes", "VERT FULL:"+debug(mBoundingVert,3));
 
        markRegions();
-       dataLength = computeDataLength(frontOnly);
+       numVertices = computeDataLength(frontOnly);
 
-       remainingVert = dataLength;
+       remainingVert = numVertices;
        }
      }
 
@@ -267,7 +267,7 @@ public class MeshCubes extends MeshObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
- private float retLeftmost(int row)
+  private float retLeftmost(int row)
     {
     if( row==0 )
       {
@@ -439,7 +439,7 @@ public class MeshCubes extends MeshObject
 // inside). Each Edge needs to point from Land to Water (thus the '(SOUTH,i-1,j)' below) - otherwise
 // later on setting up normal vectors wouldn't work.
    
-   private void markRegions()
+  private void markRegions()
      {
      int i, j, numWater=1, numLand=0;
      
@@ -506,7 +506,7 @@ public class MeshCubes extends MeshObject
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // when calling, make sure that newVal != val
    
-   private void markRegion(short newVal, int row, int col)
+  private void markRegion(short newVal, int row, int col)
      {
      int val = mCubes[row][col];
      mCubes[row][col] = newVal;
@@ -519,7 +519,7 @@ public class MeshCubes extends MeshObject
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
-   private void createNormals(boolean front, int row, int col)
+  private void createNormals(boolean front, int row, int col)
      {
      int td,lr; 
       
@@ -591,31 +591,10 @@ public class MeshCubes extends MeshObject
      android.util.Log.d("CUBES", mNormalX[3]+" "+mNormalY[3]);
      */
      }
-   
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private int addFrontVertex(int vertex, int index, float vectZ, int col, int row, float[] position, float[] normal, float[] texture)
-     {
-     remainingVert--;
-
-     float x = (float)col/mCols;
-     float y = (float)row/mRows;
-
-     position[3*vertex  ] = x-0.5f;
-     position[3*vertex+1] = 0.5f-y;
-     position[3*vertex+2] = vectZ;
-     normal[3*vertex  ]   = mNormalX[index];
-     normal[3*vertex+1]   = mNormalY[index];
-     normal[3*vertex+2]   = mNormalZ[index];
-     texture[2*vertex  ]  = x;
-     texture[2*vertex+1]  = 1.0f-y;
-
-     return vertex+1;
-     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private int buildFrontBackGrid(boolean front, int vertex, float[] position, float[] normal, float[] texture)
+  private int buildFrontBackGrid(boolean front, int vertex, float[] attribs)
      {
      int last, current;
      boolean seenLand=false;
@@ -641,7 +620,7 @@ public class MeshCubes extends MeshObject
              {
              //android.util.Log.d("CUBES","repeating winding2 vertex");
 
-             vertex = repeatLast(vertex,position,normal,texture);
+             vertex = repeatLast(vertex,attribs);
              }
 
            createNormals(front,row,col);
@@ -650,27 +629,27 @@ public class MeshCubes extends MeshObject
              {
              if( (last!=current) || !lastBlockIsNE )
                {
-               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,position,normal,texture);
-               vertex= addFrontVertex( vertex, 0, vectZ, col, row, position, normal, texture);
-               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,position,normal,texture);
-               if( !lastBlockIsNE || (!front && !seenLand) ) vertex = repeatLast(vertex,position,normal,texture);
-               vertex= addFrontVertex( vertex, 1, vectZ, col, row+1, position, normal, texture);
+               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,attribs);
+               vertex= addFrontVertex( vertex, 0, vectZ, col, row, attribs);
+               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,attribs);
+               if( !lastBlockIsNE || (!front && !seenLand) ) vertex = repeatLast(vertex,attribs);
+               vertex= addFrontVertex( vertex, 1, vectZ, col, row+1, attribs);
                }
-             vertex= addFrontVertex( vertex, 2, vectZ, col+1, row, position, normal, texture);
-             vertex= addFrontVertex( vertex, 3, vectZ, col+1, row+1, position, normal, texture);
+             vertex= addFrontVertex( vertex, 2, vectZ, col+1, row, attribs);
+             vertex= addFrontVertex( vertex, 3, vectZ, col+1, row+1, attribs);
              }
            else
              {
              if( (last!=current) || lastBlockIsNE )
                {
-               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,position,normal,texture);
-               vertex= addFrontVertex( vertex, 1, vectZ, col, row+1, position, normal, texture);
-               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,position,normal,texture);
-               if( lastBlockIsNE || (!front && !seenLand) ) vertex = repeatLast(vertex,position,normal,texture);
-               vertex= addFrontVertex( vertex, 0, vectZ, col, row, position, normal, texture);
+               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,attribs);
+               vertex= addFrontVertex( vertex, 1, vectZ, col, row+1, attribs);
+               if( seenLand  && (last != current) ) vertex = repeatLast(vertex,attribs);
+               if( lastBlockIsNE || (!front && !seenLand) ) vertex = repeatLast(vertex,attribs);
+               vertex= addFrontVertex( vertex, 0, vectZ, col, row, attribs);
                }
-             vertex= addFrontVertex( vertex, 3, vectZ, col+1, row+1, position, normal, texture);
-             vertex= addFrontVertex( vertex, 2, vectZ, col+1, row  , position, normal, texture);
+             vertex= addFrontVertex( vertex, 3, vectZ, col+1, row+1, attribs);
+             vertex= addFrontVertex( vertex, 2, vectZ, col+1, row  , attribs);
              }
 
            seenLand = true;
@@ -686,7 +665,7 @@ public class MeshCubes extends MeshObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private int repeatLast(int vertex, float[] position, float[] normal, float[] texture)
+  private int repeatLast(int vertex, float[] attribs)
      {
      //android.util.Log.e("CUBES", "repeating last vertex!");
 
@@ -694,16 +673,14 @@ public class MeshCubes extends MeshObject
        {
        remainingVert--;
 
-       position[3*vertex  ] = position[3*vertex-3];
-       position[3*vertex+1] = position[3*vertex-2];
-       position[3*vertex+2] = position[3*vertex-1];
-
-       normal[3*vertex  ]   = normal[3*vertex-3];
-       normal[3*vertex+1]   = normal[3*vertex-2];
-       normal[3*vertex+2]   = normal[3*vertex-1];
-
-       texture[2*vertex  ]  = texture[2*vertex-2];
-       texture[2*vertex+1]  = texture[2*vertex-1];
+       attribs[8*vertex  ] = attribs[8*vertex-8];
+       attribs[8*vertex+1] = attribs[8*vertex-7];
+       attribs[8*vertex+2] = attribs[8*vertex-6];
+       attribs[8*vertex+3] = attribs[8*vertex-5];
+       attribs[8*vertex+4] = attribs[8*vertex-4];
+       attribs[8*vertex+5] = attribs[8*vertex-3];
+       attribs[8*vertex+6] = attribs[8*vertex-2];
+       attribs[8*vertex+7] = attribs[8*vertex-1];
          
        vertex++;     
        }
@@ -713,13 +690,13 @@ public class MeshCubes extends MeshObject
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private int buildSideGrid(int vertex, float[] position, float[] normal, float[] texture)
+  private int buildSideGrid(int vertex, float[] attribs)
      {
      //android.util.Log.d("CUBES", "buildSide");
 
      for(int i=0; i<mEdgeNum; i++)
        {
-       vertex = buildIthSide(mEdges.get(i), vertex, position, normal, texture);  
+       vertex = buildIthSide(mEdges.get(i), vertex, attribs);
        } 
       
      return vertex;
@@ -727,7 +704,7 @@ public class MeshCubes extends MeshObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private int buildIthSide(Edge curr, int vertex, float[] position, float[] normal, float[] texture)
+  private int buildIthSide(Edge curr, int vertex, float[] attribs)
      {
      Edge prev; 
      
@@ -746,22 +723,22 @@ public class MeshCubes extends MeshObject
      int side= curr.side;  
      Edge next = getNextEdge(curr);
      
-     addSideVertex(curr,BACK,LOWER,prev.side,vertex,position,normal,texture);
+     addSideVertex(curr,BACK,LOWER,prev.side,vertex,attribs);
      vertex++;
      
      do
        {
        if( prev.side!=curr.side )
          {
-         addSideVertex(curr,BACK,LOWER,prev.side,vertex,position,normal,texture);
+         addSideVertex(curr,BACK,LOWER,prev.side,vertex,attribs);
          vertex++;
-         addSideVertex(curr,BACK,UPPER,prev.side,vertex,position,normal,texture);
+         addSideVertex(curr,BACK,UPPER,prev.side,vertex,attribs);
          vertex++;
          }
        
-       addSideVertex(curr,FRONT,LOWER,next.side,vertex,position,normal,texture);
+       addSideVertex(curr,FRONT,LOWER,next.side,vertex,attribs);
        vertex++;
-       addSideVertex(curr,FRONT,UPPER,next.side,vertex,position,normal,texture);
+       addSideVertex(curr,FRONT,UPPER,next.side,vertex,attribs);
        vertex++;
        
        prev = curr;
@@ -770,14 +747,14 @@ public class MeshCubes extends MeshObject
        }
      while( curr.col!=col || curr.row!=row || curr.side!=side );
      
-     vertex = repeatLast(vertex,position,normal,texture);
+     vertex = repeatLast(vertex,attribs);
      
      return vertex;
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private Edge getNextEdge(Edge curr)
+  private Edge getNextEdge(Edge curr)
      {
      int col = curr.col;
      int row = curr.row;
@@ -824,9 +801,30 @@ public class MeshCubes extends MeshObject
        }
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int addFrontVertex(int vertex, int index, float vectZ, int col, int row, float[] attribs)
+     {
+     remainingVert--;
+
+     float x = (float)col/mCols;
+     float y = (float)row/mRows;
+
+     attribs[8*vertex  ] = x-0.5f;
+     attribs[8*vertex+1] = 0.5f-y;
+     attribs[8*vertex+2] = vectZ;
+     attribs[8*vertex+3] = mNormalX[index];
+     attribs[8*vertex+4] = mNormalY[index];
+     attribs[8*vertex+5] = mNormalZ[index];
+     attribs[8*vertex+6] = x;
+     attribs[8*vertex+7] = 1.0f-y;
+
+     return vertex+1;
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
-   private void addSideVertex(Edge curr, boolean back, boolean lower,int side, int vertex, float[] position, float[] normal, float[] texture)
+  private void addSideVertex(Edge curr, boolean back, boolean lower,int side, int vertex, float[] attribs)
      {
      //android.util.Log.e("CUBES", "adding Side vertex!");
 
@@ -838,123 +836,103 @@ public class MeshCubes extends MeshObject
        {
        case NORTH: x = (float)(back ? (curr.col  ):(curr.col+1))/mCols;
 
-                   position[3*vertex  ] = x - 0.5f;
-                   position[3*vertex+1] = 0.5f - (float)curr.row/mRows;
-                   position[3*vertex+2] = lower ? BACKZ : FRONTZ;
-
-                   normal[3*vertex  ]   = side==NORTH ? 0.0f : (side==WEST?-R:R);
-                   normal[3*vertex+1]   = 1.0f;
-                   normal[3*vertex+2]   = lower ? -R:R;
-
-                   texture[2*vertex  ]  = x;
-                   texture[2*vertex+1]  = 1.0f-(float)(lower? (curr.row-1):(curr.row  ))/mRows;
+                   attribs[8*vertex  ] = x - 0.5f;
+                   attribs[8*vertex+1] = 0.5f - (float)curr.row/mRows;
+                   attribs[8*vertex+2] = lower ? BACKZ : FRONTZ;
+                   attribs[8*vertex+3] = side==NORTH ? 0.0f : (side==WEST?-R:R);
+                   attribs[8*vertex+4] = 1.0f;
+                   attribs[8*vertex+5] = lower ? -R:R;
+                   attribs[8*vertex+6] = x;
+                   attribs[8*vertex+7] = 1.0f-(float)(lower? (curr.row-1):(curr.row  ))/mRows;
                    break;
        case SOUTH: x = (float)(back ? (curr.col+1):(curr.col  ))/mCols;
 
-                   position[3*vertex  ] = x - 0.5f;
-                   position[3*vertex+1] = 0.5f - (float)(curr.row+1)/mRows;
-                   position[3*vertex+2] = lower ? BACKZ : FRONTZ;  
-            
-                   normal[3*vertex  ]   = side==SOUTH ? 0.0f: (side==EAST?-R:R);
-                   normal[3*vertex+1]   =-1.0f;
-                   normal[3*vertex+2]   = lower ? -R:R;
-
-                   texture[2*vertex  ]  = x;
-                   texture[2*vertex+1]  = 1.0f-(float)(lower? (curr.row+2):(curr.row+1))/mRows;
+                   attribs[8*vertex  ] = x - 0.5f;
+                   attribs[8*vertex+1] = 0.5f - (float)(curr.row+1)/mRows;
+                   attribs[8*vertex+2] = lower ? BACKZ : FRONTZ;
+                   attribs[8*vertex+3] = side==SOUTH ? 0.0f: (side==EAST?-R:R);
+                   attribs[8*vertex+4] =-1.0f;
+                   attribs[8*vertex+5] = lower ? -R:R;
+                   attribs[8*vertex+6] = x;
+                   attribs[8*vertex+7] = 1.0f-(float)(lower? (curr.row+2):(curr.row+1))/mRows;
                    break;
        case WEST : y = (float)(back  ? (curr.row+1):(curr.row))/mRows;
 
-                   position[3*vertex  ] = (float)curr.col/mCols -0.5f;
-                   position[3*vertex+1] = 0.5f - y;
-                   position[3*vertex+2] = lower ? BACKZ : FRONTZ;
-
-                   normal[3*vertex  ]   =-1.0f;
-                   normal[3*vertex+1]   = side==WEST ? 0.0f : (side==NORTH?-R:R);
-                   normal[3*vertex+2]   = lower ? -R:R;
- 
-                   texture[2*vertex  ]  = (float)(lower ? (curr.col-1):(curr.col  ))/mCols;
-                   texture[2*vertex+1]  = 1.0f - y;
+                   attribs[8*vertex  ] = (float)curr.col/mCols -0.5f;
+                   attribs[8*vertex+1] = 0.5f - y;
+                   attribs[8*vertex+2] = lower ? BACKZ : FRONTZ;
+                   attribs[8*vertex+3] =-1.0f;
+                   attribs[8*vertex+4] = side==WEST ? 0.0f : (side==NORTH?-R:R);
+                   attribs[8*vertex+5] = lower ? -R:R;
+                   attribs[8*vertex+6] = (float)(lower ? (curr.col-1):(curr.col  ))/mCols;
+                   attribs[8*vertex+7] = 1.0f - y;
                    break;
        case EAST : y = (float)(back  ? (curr.row):(curr.row+1))/mRows;
 
-                   position[3*vertex  ] = (float)(curr.col+1)/mCols -0.5f;
-                   position[3*vertex+1] = 0.5f - y;
-                   position[3*vertex+2] = lower ? BACKZ : FRONTZ;
-
-                   normal[3*vertex  ]   = 1.0f;
-                   normal[3*vertex+1]   = side==EAST ? 0.0f : (side==SOUTH?-R:R);
-                   normal[3*vertex+2]   = lower ? -R:R; 
-
-                   texture[2*vertex  ]  = (float)(lower ? (curr.col+2):(curr.col+1))/mCols;
-                   texture[2*vertex+1]  = 1.0f - y;
+                   attribs[8*vertex  ] = (float)(curr.col+1)/mCols -0.5f;
+                   attribs[8*vertex+1] = 0.5f - y;
+                   attribs[8*vertex+2] = lower ? BACKZ : FRONTZ;
+                   attribs[8*vertex+3] = 1.0f;
+                   attribs[8*vertex+4] = side==EAST ? 0.0f : (side==SOUTH?-R:R);
+                   attribs[8*vertex+5] = lower ? -R:R;
+                   attribs[8*vertex+6] = (float)(lower ? (curr.col+2):(curr.col+1))/mCols;
+                   attribs[8*vertex+7] = 1.0f - y;
                    break;
        }
      
-     if(texture[2*vertex  ]>1.0f) texture[2*vertex  ] =2.0f-texture[2*vertex  ];
-     if(texture[2*vertex  ]<0.0f) texture[2*vertex  ] =    -texture[2*vertex  ];
-     if(texture[2*vertex+1]>1.0f) texture[2*vertex+1] =2.0f-texture[2*vertex+1];
-     if(texture[2*vertex+1]<0.0f) texture[2*vertex+1] =    -texture[2*vertex+1];
+     if(attribs[8*vertex+6]>1.0f) attribs[8*vertex+6] = 2.0f-attribs[8*vertex+6];
+     if(attribs[8*vertex+6]<0.0f) attribs[8*vertex+6] =     -attribs[8*vertex+6];
+     if(attribs[8*vertex+7]>1.0f) attribs[8*vertex+7] = 2.0f-attribs[8*vertex+7];
+     if(attribs[8*vertex+7]<0.0f) attribs[8*vertex+7] =     -attribs[8*vertex+7];
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private void build(boolean frontOnly)
+  private void build(boolean frontOnly)
      {
-     int numVertices=0;
-     float[] positionData= new float[POSITION_DATA_SIZE*dataLength];
-     float[] normalData  = new float[NORMAL_DATA_SIZE  *dataLength];
-     float[] textureData = new float[TEX_DATA_SIZE     *dataLength];
+     int vertSoFar=0;
+     float[] attribs= new float[(POSITION_DATA_SIZE+NORMAL_DATA_SIZE+TEX_DATA_SIZE)*numVertices];
 
-     //android.util.Log.d("CUBES","building front grid...");
+     //android.util.Log.d("MeshCubes","building front grid...");
 
-     numVertices = buildFrontBackGrid(true, numVertices,positionData,normalData,textureData);
+     vertSoFar = buildFrontBackGrid(true, vertSoFar,attribs);
 
      if( !frontOnly )
        {
-       numVertices = repeatLast(numVertices,positionData,normalData,textureData);
-       if( numVertices%2==1 )
+       vertSoFar = repeatLast(vertSoFar,attribs);
+       if( vertSoFar%2==1 )
          {
-         //android.util.Log.d("CUBES","repeating winding1 vertex");
+         //android.util.Log.d("MeshCubes","repeating winding1 vertex");
 
-         numVertices = repeatLast(numVertices,positionData,normalData,textureData);
+         vertSoFar = repeatLast(vertSoFar,attribs);
          }
 
-       //android.util.Log.d("CUBES","building side grid...");
+       //android.util.Log.d("MeshCubes","building side grid...");
 
-       numVertices = buildSideGrid (numVertices,positionData,normalData,textureData);
+       vertSoFar = buildSideGrid (vertSoFar,attribs);
 
-       //android.util.Log.d("CUBES","building back grid...");
+       //android.util.Log.d("MeshCubes","building back grid...");
 
-       numVertices = buildFrontBackGrid (false,numVertices,positionData,normalData,textureData);
+       buildFrontBackGrid (false,vertSoFar,attribs);
        }
 
-     /*
-     android.util.Log.e("CUBES","dataLen="+dataLength+" vertex="+numVertices);
-     android.util.Log.d("CUBES", "position: "+debug(positionData,3) );
-     android.util.Log.d("CUBES", "normal: "  +debug(  normalData,3) );
-     android.util.Log.d("CUBES", "texture: " +debug( textureData,2) );
-     */
+     //android.util.Log.e("MeshCubes", "dataLen="+numVertices);
+     //android.util.Log.d("MeshCubes", "attribs: "+debug(attribs,8) );
 
      mEdges.clear();
      mEdges = null;
      mCubes = null;
 
      if( remainingVert!=0 )
-       android.util.Log.d("CUBES", "remainingVert " +remainingVert );
+       android.util.Log.e("MeshCubes", "remainingVert " +remainingVert );
 
-     mMeshPositions = ByteBuffer.allocateDirect(POSITION_DATA_SIZE*dataLength*BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
-     mMeshPositions.put(positionData).position(0);
-
-     mMeshNormals = ByteBuffer.allocateDirect(NORMAL_DATA_SIZE*dataLength*BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
-     mMeshNormals.put(normalData).position(0);
-
-     mMeshTexture = ByteBuffer.allocateDirect(TEX_DATA_SIZE*dataLength*BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
-     mMeshTexture.put(textureData).position(0);
+     mVertAttribs = ByteBuffer.allocateDirect(numVertices*VERTSIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
+     mVertAttribs.put(attribs).position(0);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   float[] getBoundingVertices()
+  float[] getBoundingVertices()
      {
      return mBoundingVert;
      }
@@ -986,12 +964,12 @@ public class MeshCubes extends MeshObject
  *                  </p>
  * @param frontOnly Only create the front wall or side and back as well?
  */
-   public MeshCubes(int cols, String desc, boolean frontOnly)
-      {
-      super(frontOnly ? 0.0f:1.0f/cols);
-      prepareDataStructures(cols,desc,frontOnly);
-      build(frontOnly);
-      }
+ public MeshCubes(int cols, String desc, boolean frontOnly)
+   {
+   super(frontOnly ? 0.0f:1.0f/cols);
+   prepareDataStructures(cols,desc,frontOnly);
+   build(frontOnly);
+   }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -1001,12 +979,10 @@ public class MeshCubes extends MeshObject
  * @param rows      Number of rows.
  * @param frontOnly Only create the front wall or side and back as well?
  */
-   public MeshCubes(int cols, int rows, boolean frontOnly)
-      {
-      super(frontOnly ? 0.0f:1.0f/cols);
-      prepareDataStructures(cols,rows,frontOnly);
-      build(frontOnly);
-      }
+ public MeshCubes(int cols, int rows, boolean frontOnly)
+   {
+   super(frontOnly ? 0.0f:1.0f/cols);
+   prepareDataStructures(cols,rows,frontOnly);
+   build(frontOnly);
    }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
+ }
diff --git a/src/main/java/org/distorted/library/MeshFlat.java b/src/main/java/org/distorted/library/MeshFlat.java
index 3295c40..1219419 100644
--- a/src/main/java/org/distorted/library/MeshFlat.java
+++ b/src/main/java/org/distorted/library/MeshFlat.java
@@ -42,49 +42,49 @@ public class MeshFlat extends MeshObject
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Create a flat, full grid.
 
-   private void computeNumberOfVertices(int cols, int rows)
+  private void computeNumberOfVertices(int cols, int rows)
      {
      mRows=rows;
      mCols=cols;
 
      if( cols==1 && rows==1 )
        {
-       dataLength = 4;
+       numVertices = 4;
        }
      else
        {
-       dataLength = 2*( mRows*mCols +2*mRows - 1) +2*(mCols>=2 ? mRows:0) +
-                    (mCols>=2 && mRows>=2 ? 2*mRows-2 : 1);
+       numVertices = 2*( mRows*mCols +2*mRows - 1) +2*(mCols>=2 ? mRows:0) +
+                     (mCols>=2 && mRows>=2 ? 2*mRows-2 : 1);
        }
 
-     //android.util.Log.e("BITMAP","vertices="+dataLength+" rows="+mRows+" cols="+mCols);
+     //android.util.Log.e("BITMAP","vertices="+numVertices+" rows="+mRows+" cols="+mCols);
 
-     remainingVert = dataLength;
+     remainingVert = numVertices;
      }
 
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private int addVertex(int vertex, float x, float y, float[] position, float[] normal, float[] texture)
+  private int addVertex(int vertex, float x, float y, float[] attribs)
      {
      remainingVert--;
 
-     position[3*vertex  ] = x-0.5f;
-     position[3*vertex+1] = 0.5f-y;
-     position[3*vertex+2] = 0;
+     attribs[8*vertex  ] = x-0.5f;
+     attribs[8*vertex+1] = 0.5f-y;
+     attribs[8*vertex+2] = 0;
 
-     texture[2*vertex  ]  = x;
-     texture[2*vertex+1]  = 1.0f-y;
+     attribs[8*vertex+3] = 0.0f;
+     attribs[8*vertex+4] = 0.0f;
+     attribs[8*vertex+5] = 1.0f;
 
-     normal[3*vertex  ]   = 0.0f;
-     normal[3*vertex+1]   = 0.0f;
-     normal[3*vertex+2]   = 1.0f;
+     attribs[8*vertex+6] = x;
+     attribs[8*vertex+7] = 1.0f-y;
 
      return vertex+1;
      }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private int repeatLast(int vertex, float[] position, float[] normal, float[] texture)
+  private int repeatLast(int vertex, float[] attribs)
      {
      remainingVert--;
 
@@ -92,16 +92,14 @@ public class MeshFlat extends MeshObject
 
      if( vertex>0 )
        {
-       position[3*vertex  ] = position[3*vertex-3];
-       position[3*vertex+1] = position[3*vertex-2];
-       position[3*vertex+2] = position[3*vertex-1];
-
-       normal[3*vertex  ]   = normal[3*vertex-3];
-       normal[3*vertex+1]   = normal[3*vertex-2];
-       normal[3*vertex+2]   = normal[3*vertex-1];
-
-       texture[2*vertex  ]  = texture[2*vertex-2];
-       texture[2*vertex+1]  = texture[2*vertex-1];
+       attribs[8*vertex  ] = attribs[8*vertex-8];
+       attribs[8*vertex+1] = attribs[8*vertex-7];
+       attribs[8*vertex+2] = attribs[8*vertex-6];
+       attribs[8*vertex+3] = attribs[8*vertex-5];
+       attribs[8*vertex+4] = attribs[8*vertex-4];
+       attribs[8*vertex+5] = attribs[8*vertex-3];
+       attribs[8*vertex+6] = attribs[8*vertex-2];
+       attribs[8*vertex+7] = attribs[8*vertex-1];
 
        vertex++;
        }
@@ -111,7 +109,7 @@ public class MeshFlat extends MeshObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private void buildGrid(float[] position, float[] normal, float[] texture)
+  private void buildGrid(float[] attribs)
      {
      boolean lastBlockIsNE = false;
      boolean currentBlockIsNE;
@@ -135,14 +133,14 @@ public class MeshFlat extends MeshObject
 
          if( col==0 || (lastBlockIsNE^currentBlockIsNE) )
            {
-           if( row!=0 && col==0 ) vertex = repeatLast(vertex,position,normal,texture);
-           vertex= addVertex( vertex, x, y+(currentBlockIsNE?0:Y), position, normal, texture);
-           if( row!=0 && col==0 ) vertex = repeatLast(vertex,position,normal,texture);
-           if( lastBlockIsNE^currentBlockIsNE)  vertex = repeatLast(vertex,position,normal,texture);
-           vertex= addVertex( vertex, x, y+(currentBlockIsNE?Y:0), position, normal, texture);
+           if( row!=0 && col==0 ) vertex = repeatLast(vertex,attribs);
+           vertex= addVertex( vertex, x, y+(currentBlockIsNE?0:Y), attribs);
+           if( row!=0 && col==0 ) vertex = repeatLast(vertex,attribs);
+           if( lastBlockIsNE^currentBlockIsNE)  vertex = repeatLast(vertex,attribs);
+           vertex= addVertex( vertex, x, y+(currentBlockIsNE?Y:0), attribs);
            }
-         vertex= addVertex( vertex, x+X, y+(currentBlockIsNE?0:Y), position, normal, texture);
-         vertex= addVertex( vertex, x+X, y+(currentBlockIsNE?Y:0), position, normal, texture);
+         vertex= addVertex( vertex, x+X, y+(currentBlockIsNE?0:Y), attribs);
+         vertex= addVertex( vertex, x+X, y+(currentBlockIsNE?Y:0), attribs);
 
          lastBlockIsNE = currentBlockIsNE;
          x+=X;
@@ -156,7 +154,7 @@ public class MeshFlat extends MeshObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /*
-   private static String debug(float[] val, int stop)
+  private static String debug(float[] val, int stop)
      {
      String ret="";
 
@@ -169,10 +167,9 @@ public class MeshFlat extends MeshObject
      return ret;
      }
 */
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   float[] getBoundingVertices()
+  float[] getBoundingVertices()
      {
      return mBoundingVert;
      }
@@ -186,32 +183,22 @@ public class MeshFlat extends MeshObject
  * @param cols Number of columns in the grid.
  * @param rows Number of rows in the grid.
  */
-   public MeshFlat(int cols, int rows)
-      {
-      super(0.0f);
-      computeNumberOfVertices(cols,rows);
-
-      float[] positionData= new float[POSITION_DATA_SIZE*dataLength];
-      float[] normalData  = new float[NORMAL_DATA_SIZE  *dataLength];
-      float[] textureData = new float[TEX_DATA_SIZE     *dataLength];
-
-      buildGrid(positionData,normalData,textureData);
+ public MeshFlat(int cols, int rows)
+    {
+    super(0.0f);
+    computeNumberOfVertices(cols,rows);
 
-      //android.util.Log.e("CUBES","dataLen="+dataLength);
-      //android.util.Log.d("CUBES", "position: "+debug(positionData,3) );
-      //android.util.Log.d("CUBES", "normal: "  +debug(  normalData,3) );
-      //android.util.Log.d("CUBES", "texture: " +debug( textureData,2) );
+    float[] attribs= new float[(POSITION_DATA_SIZE+NORMAL_DATA_SIZE+TEX_DATA_SIZE)*numVertices];
 
-      if( remainingVert!=0 )
-        android.util.Log.d("BITMAP", "remainingVert " +remainingVert );
+    buildGrid(attribs);
 
-      mMeshPositions = ByteBuffer.allocateDirect(POSITION_DATA_SIZE*dataLength*BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
-      mMeshPositions.put(positionData).position(0);
+    //android.util.Log.e("MeshFlat", "dataLen="+numVertices);
+    //android.util.Log.d("MeshFlat", "attribs: "+debug(attribs,8) );
 
-      mMeshNormals = ByteBuffer.allocateDirect(NORMAL_DATA_SIZE*dataLength*BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
-      mMeshNormals.put(normalData).position(0);
+    if( remainingVert!=0 )
+      android.util.Log.d("BITMAP", "remainingVert " +remainingVert );
 
-      mMeshTexture = ByteBuffer.allocateDirect(TEX_DATA_SIZE*dataLength*BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
-      mMeshTexture.put(textureData).position(0);
-      }
-  }
\ No newline at end of file
+    mVertAttribs = ByteBuffer.allocateDirect(numVertices*VERTSIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
+    mVertAttribs.put(attribs).position(0);
+    }
+ }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/MeshObject.java b/src/main/java/org/distorted/library/MeshObject.java
index 23e560d..17d2da8 100644
--- a/src/main/java/org/distorted/library/MeshObject.java
+++ b/src/main/java/org/distorted/library/MeshObject.java
@@ -28,24 +28,28 @@ import java.nio.FloatBuffer;
  * Abstract class which represents a Mesh, ie 3 arrays of Vertex attributes: 1) positions
  * 2) normals 3) texture coordinates.
  * <p>
- * If you want to render to a particular shape, extend from here, construct the three FloatBuffers and
- * provide correct dataLength, i.e. the number of vertices.
+ * If you want to render to a particular shape, extend from here, construct the attrib FloatBuffer
+ * and provide correct numVertices.
  */
 public abstract class MeshObject extends DistortedObject
    {
-   static final int BYTES_PER_FLOAT   = 4; //
-   static final int POSITION_DATA_SIZE= 3; // Size of the position data in elements
-   static final int NORMAL_DATA_SIZE  = 3; // Size of the normal data in elements.
-   static final int TEX_DATA_SIZE     = 2; // Size of the texture coordinate data in elements.
+   private static final int BYTES_PER_FLOAT = 4;
 
-   int dataLength;
-   FloatBuffer mMeshPositions, mMeshNormals, mMeshTexture;
-   int[] mPosVBO = new int[1];
-   int[] mNorVBO = new int[1];
-   int[] mTexVBO = new int[1];
+   static final int POSITION_DATA_SIZE= 3;
+   static final int NORMAL_DATA_SIZE  = 3;
+   static final int TEX_DATA_SIZE     = 2;
 
-   final float zFactor; // strange workaround for the fact that we need to somehow store the 'depth'
-                        // of the Mesh. Used in DistortedEffects. See DistortedTexture.getDepth().
+   static final int OFFSET0 =                                                                   0;
+   static final int OFFSET1 = (POSITION_DATA_SIZE                               )*BYTES_PER_FLOAT;
+   static final int OFFSET2 = (POSITION_DATA_SIZE+NORMAL_DATA_SIZE              )*BYTES_PER_FLOAT;
+   static final int VERTSIZE= (POSITION_DATA_SIZE+NORMAL_DATA_SIZE+TEX_DATA_SIZE)*BYTES_PER_FLOAT;
+
+   int numVertices;
+   FloatBuffer mVertAttribs;   // packed: PosX,PosY,PosZ, NorX, NorY,NorZ, TexS, TexT
+   int[] mAttVBO = new int[1]; // server-side packed vertex attributes
+
+   final float zFactor;        // strange workaround for the fact that we need to somehow store the 'depth'
+                               // of the Mesh. Used in DistortedEffects. See DistortedTexture.getDepth().
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -60,31 +64,18 @@ public abstract class MeshObject extends DistortedObject
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // must be called from a thread holding OpenGL Context
 //
-// Do NOT release mMeshPositions etc as we will need them when we need to re-create the buffers after
+// Do NOT release mVertAttribs etc as we will need them when we need to re-create the buffers after
 // a loss of OpenGL context!
 
    void create()
      {
-     if( mPosVBO[0]<0 )
-       {
-       GLES30.glGenBuffers(1, mPosVBO, 0);
-       GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mPosVBO[0]);
-       GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, dataLength*POSITION_DATA_SIZE*BYTES_PER_FLOAT, mMeshPositions, GLES30.GL_STATIC_READ);
-       }
-     if( mNorVBO[0]<0 )
-       {
-       GLES30.glGenBuffers(1, mNorVBO, 0);
-       GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mNorVBO[0]);
-       GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, dataLength*  NORMAL_DATA_SIZE*BYTES_PER_FLOAT, mMeshNormals  , GLES30.GL_STATIC_READ);
-       }
-     if( mTexVBO[0]<0 )
+     if( mAttVBO[0]<0 )
        {
-       GLES30.glGenBuffers(1, mTexVBO, 0);
-       GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mTexVBO[0]);
-       GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, dataLength*    TEX_DATA_SIZE*BYTES_PER_FLOAT, mMeshTexture  , GLES30.GL_STATIC_READ);
+       GLES30.glGenBuffers(1, mAttVBO, 0);
+       GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mAttVBO[0]);
+       GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, numVertices*VERTSIZE, mVertAttribs, GLES30.GL_STATIC_READ);
+       GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
        }
-
-     GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -92,20 +83,10 @@ public abstract class MeshObject extends DistortedObject
 
    void delete()
      {
-     if( mPosVBO[0]>=0 )
-       {
-       GLES30.glDeleteBuffers(1, mPosVBO, 0);
-       mPosVBO[0] = -1;
-       }
-     if( mNorVBO[0]>=0 )
-       {
-       GLES30.glDeleteBuffers(1, mNorVBO, 0);
-       mNorVBO[0] = -1;
-       }
-     if( mTexVBO[0]>=0 )
+     if( mAttVBO[0]>=0 )
        {
-       GLES30.glDeleteBuffers(1, mTexVBO, 0);
-       mTexVBO[0] = -1;
+       GLES30.glDeleteBuffers(1, mAttVBO, 0);
+       mAttVBO[0] = -1;
        }
      }
 
@@ -113,9 +94,7 @@ public abstract class MeshObject extends DistortedObject
 
    void recreate()
      {
-     mPosVBO[0] = -1;
-     mNorVBO[0] = -1;
-     mTexVBO[0] = -1;
+     mAttVBO[0] = -1;
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -123,7 +102,7 @@ public abstract class MeshObject extends DistortedObject
 
    String printDetails()
      {
-     return getClass().getSimpleName()+" vertices:"+dataLength;
+     return getClass().getSimpleName()+" vertices:"+ numVertices;
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
