commit 0d4aae8896fdbae3d7199b013d3eca53792ce756
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Mar 4 00:13:02 2020 +0000

    New MeshJoined.
    Test app says it's not working - bug in MeshBase.join().

diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index 80838f9..a1dd0b6 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -76,24 +76,26 @@ public abstract class MeshBase
      private int mEndIndex;
      private float[] mTextureMap;
 
-     Component()
+     Component(int end)
        {
-       mTextureMap = new float[8];
-
-       mTextureMap[ 0] = 0.0f;  // LD_X
-       mTextureMap[ 1] = 0.0f;  // LD_Y
-       mTextureMap[ 2] = 0.0f;  // LU_X
-       mTextureMap[ 3] = 1.0f;  // LU_Y
-       mTextureMap[ 4] = 1.0f;  // RU_X
-       mTextureMap[ 5] = 1.0f;  // RU_Y
-       mTextureMap[ 6] = 1.0f;  // RD_X
-       mTextureMap[ 7] = 0.0f;  // RD_Y
+       mEndIndex = end;
+
+       mTextureMap    = new float[4];
+       mTextureMap[0] = 0.0f;  // LowerLeft_X
+       mTextureMap[1] = 0.0f;  // LowerLeft_Y
+       mTextureMap[2] = 1.0f;  // Width
+       mTextureMap[3] = 1.0f;  // Height
        }
      Component(Component original)
        {
        mEndIndex = original.mEndIndex;
-       mTextureMap = new float[8];
-       System.arraycopy(original.mTextureMap,0,mTextureMap,0,8);
+       mTextureMap = new float[4];
+       System.arraycopy(original.mTextureMap,0,mTextureMap,0,4);
+       }
+
+     void setMap(float[] newMap)
+       {
+       mTextureMap = newMap;
        }
      }
 
@@ -113,8 +115,7 @@ public abstract class MeshBase
 
      mShowNormals = false;
      mInflate     = 0.0f;
-     mComponent = new ArrayList<>();
-     mComponent.add(new Component());
+     mComponent   = new ArrayList<>();
 
      mVBO = new InternalBuffer(GLES31.GL_ARRAY_BUFFER             , GLES31.GL_STATIC_READ);
      mTFO = new InternalBuffer(GLES31.GL_TRANSFORM_FEEDBACK_BUFFER, GLES31.GL_STATIC_READ);
@@ -151,6 +152,31 @@ public abstract class MeshBase
      setAttribs(mVertAttribs);
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// change mVertAttribs from index 'begin' to index 'end' (inclusive) to the new Texture Map.
+// x varies from -mBoundingX to +mBoundingX; y accordingly.
+
+   private void changeTextureMap( float[] newMap, float[] oldMap, int begin, int end)
+     {
+
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   int numComponents()
+     {
+     return mComponent.size();
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   void setBounding(float bx, float by, float bz)
+     {
+     mBoundingX = bx/2;
+     mBoundingY = by/2;
+     mBoundingZ = bz/2;
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // when a derived class is done computing its mesh, it has to call this method.
 
@@ -159,7 +185,7 @@ public abstract class MeshBase
      mNumVertices = vertexAttribs.length/VERT_ATTRIBS;
      mVertAttribs = vertexAttribs;
 
-     mComponent.get(0).mEndIndex = mNumVertices;
+     mComponent.add(new Component(mNumVertices));
 
      FloatBuffer attribs = ByteBuffer.allocateDirect(mNumVertices*VERT_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
      attribs.put(vertexAttribs).position(0);
@@ -169,109 +195,114 @@ public abstract class MeshBase
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public int getTFO()
-     {
-     return mTFO.mIndex[0];
-     }
+// called from MeshJoined
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public int getNumVertices()
+   void join(MeshBase[] meshes)
      {
-     return mNumVertices;
-     }
+     MeshBase mesh;
+     Component comp;
+     int com, num, len = meshes.length;
+     int origVertices = mNumVertices;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Each mesh has its 'bounding box' - return half of its X-length.
- * <p>
- * In case of all 'simple' Meshes, the bounding box is always 1x1x1 (Sphere, Cubes) or 1x1x0
- * (Rectangles, Triangles, Quad - i.e. all 'flat' Meshes). But this can be something else in case of
- * MeshComponent.
- */
-   public float getBoundingX()
-    {
-    return mBoundingX*mStretchX;
-    }
+     // compute new numVertices; take care of Components
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Each mesh has its 'bounding box' - return half of its Y-length.
- */
-   public float getBoundingY()
-    {
-    return mBoundingY*mStretchY;
-    }
+     if( origVertices>0 )
+       {
+       com = mComponent.size();
+       mNumVertices+= ( mNumVertices%2==1 ? 2:1 );
+       mComponent.get(com-1).mEndIndex = mNumVertices;
+       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Each mesh has its 'bounding box' - return half of its Z-length.
- */
-   public float getBoundingZ()
-    {
-    return mBoundingZ*mStretchZ;
-    }
+     for(int i=0; i<len; i++)
+       {
+       mesh = meshes[i];
+       com = mesh.mComponent.size();
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sometimes we want to display a Mesh on a rectangular screen. Then we need to stretch it by
- * different factors in x and y (or z) directions. If we also wanted do display some vertex effects
- * done on this mesh, let's say a bulge done by a Distort effect, and wanted the bulge to be round,
- * (i.e the same in x and y directions) then doing so without this method would be impossible.
- *
- * This sets 'stretch' factors in each 3 dimensions. All vertices of this Mesh will be premultiplied
- * by those factors in the very first line of the Vertex Shader, before any Effects are done on it.
- * Using this we can thus pre-stretch the mesh to aspect ratio equal to the surface we eventually
- * want to display the Mesh on, and this way we can achieve a round Distort bulge!
- *
- * This could also be used to pre-stretch a Rectangles Mesh to a size equal (in pixels) to the bitmap
- * this mesh is textured with - and this lets us work with all Effects in natural, pixel units.
- *
- * @param sx stretch factor in x.
- * @param sy stretch factor in y.
- * @param sz stretch factor in z.
- */
-   public void setStretch(float sx, float sy, float sz)
-     {
-     mStretchX = sx;
-     mStretchY = sy;
-     mStretchZ = sz;
-     }
+       for(int j=0; j<com; j++)
+         {
+         comp = new Component(mesh.mComponent.get(j));
+         comp.mEndIndex += mNumVertices;
+         mComponent.add(comp);
+         }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the x-factor set by setStretch().
- */
-   public float getStretchX()
-     {
-     return mStretchX;
+       num = mesh.mNumVertices;
+       mNumVertices+= (i<len-1 ? ( num%2==1 ? num+2 : num+1 ) : num);
+       }
+
+     // allocate new attrib array
+     float[] newAttribs = new float[VERT_ATTRIBS*mNumVertices];
+     num = origVertices;
+
+     if( origVertices>0 )
+       {
+       System.arraycopy(mVertAttribs,                             0, newAttribs,                         0, VERT_ATTRIBS*num);
+       System.arraycopy(mVertAttribs, VERT_ATTRIBS*(origVertices-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS    );
+       origVertices++;
+
+       if( num%2==1 )
+         {
+         System.arraycopy(mVertAttribs, VERT_ATTRIBS*(origVertices-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS);
+         origVertices++;
+         }
+       }
+
+     for(int i=0; i<len; i++)
+       {
+       mesh = meshes[i];
+       num = mesh.mNumVertices;
+
+       System.arraycopy(mesh.mVertAttribs, 0, newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS    );
+       origVertices++;
+       System.arraycopy(mesh.mVertAttribs, 0, newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS*num);
+       origVertices+=num;
+
+       if( i<len-1 )
+         {
+         System.arraycopy(mesh.mVertAttribs, VERT_ATTRIBS*(num-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS);
+         origVertices++;
+
+         if( num%2==1 )
+           {
+           System.arraycopy(mesh.mVertAttribs, VERT_ATTRIBS*(num-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS);
+           origVertices++;
+           }
+         }
+       }
+
+     if( origVertices!=mNumVertices )
+       {
+       android.util.Log.e("mesh", "join: origVertices: "+origVertices+" numVertices: "+mNumVertices);
+       }
+
+     mVertAttribs = newAttribs;
+
+     FloatBuffer attribs = ByteBuffer.allocateDirect(mNumVertices*VERT_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
+     attribs.put(mVertAttribs).position(0);
+
+     mVBO.setData(mNumVertices*VERT_SIZE, attribs);
+     mTFO.setData(mNumVertices*TRAN_SIZE, null   );
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Returns the y-factor set by setStretch().
+ * Not part of public API, do not document (public only because has to be used from the main package)
+ *
+ * @y.exclude
  */
-   public float getStretchY()
+   public int getTFO()
      {
-     return mStretchY;
+     return mTFO.mIndex[0];
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Returns the z-factor set by setStretch().
+ * Not part of public API, do not document (public only because has to be used from the main package)
+ *
+ * @y.exclude
  */
-   public float getStretchZ()
+   public int getNumVertices()
      {
-     return mStretchZ;
+     return mNumVertices;
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -378,7 +409,7 @@ public abstract class MeshBase
      {
      float[][] matrix = new float[effects.length][16];
      float[] tmp;
-     float[] array = new float[4];
+     float[] array = new float[7];
      float x,y,z;
      int numEffects = 0;
 
@@ -434,84 +465,34 @@ public abstract class MeshBase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Join a list of Meshes into this one.
+ * Sets texture maps for all components of this mesh.
  * <p>
- * Please note that calling this once with the complete list of Meshes will be much faster than
- * calling it repeatedly with one Mesh at a time, as we have to reallocate the array of vertices
+ * Please note that calling this once with the complete list of Maps will be much faster than
+ * calling it repeatedly with one Maps at a time, as we have to reallocate the array of vertices
  * each time.
+ * 'maps' needs to be maps[NumComponentsInThisMesh][4]. [0] is the lower-left corner's X, [1]- its Y,
+ * [2] - width, [3] - height of the map.
+ * For example map[0] = new float { 0.0, 0.5, 0.5, 0.5 } sets the 0th component texture map to the
+ * upper-left quadrant of the texture.
  */
-   public void join(MeshBase[] meshes)
+   public void setTextureMap(float[][] maps)
      {
-     MeshBase mesh;
-     Component comp;
-     int com, num, len = meshes.length;
-     int origVertices = mNumVertices;
-
-     // compute new numVertices; take care of Components
-     mNumVertices+= ( mNumVertices%2==1 ? 2:1 );
-     com = mComponent.size();
-     mComponent.get(com-1).mEndIndex = mNumVertices;
-
-     for(int i=0; i<len; i++)
-       {
-       mesh = meshes[i];
-       com = mesh.mComponent.size();
-
-       for(int j=0; j<com; j++)
-         {
-         comp = new Component(mesh.mComponent.get(j));
-         comp.mEndIndex += mNumVertices;
-         mComponent.add(comp);
-         }
-
-       num = mesh.mNumVertices;
-       mNumVertices+= (i<len-1 ? ( num%2==1 ? num+2 : num+1 ) : num);
-       }
-
-     // allocate new attrib array
-     float[] newAttribs = new float[VERT_ATTRIBS*mNumVertices];
-     num = origVertices;
+     int num_comp = mComponent.size();
+     int num_maps = maps.length;
+     int min = num_comp<num_maps ? num_comp : num_maps;
+     int lastEnd = 0;
 
-     System.arraycopy(mVertAttribs,                             0, newAttribs,                         0, VERT_ATTRIBS*num);
-     System.arraycopy(mVertAttribs, VERT_ATTRIBS*(origVertices-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS    );
-     origVertices++;
-
-     if( num%2==1 )
-       {
-       System.arraycopy(mVertAttribs, VERT_ATTRIBS*(origVertices-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS);
-       origVertices++;
-       }
-
-     for(int i=0; i<len; i++)
+     for(int i=0; i<min; i++)
        {
-       mesh = meshes[i];
-       num = mesh.mNumVertices;
-
-       System.arraycopy(mesh.mVertAttribs, 0, newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS    );
-       origVertices++;
-       System.arraycopy(mesh.mVertAttribs, 0, newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS*num);
-       origVertices+=num;
-
-       if( i<len-1 )
+       if( maps[i]!=null )
          {
-         System.arraycopy(mesh.mVertAttribs, VERT_ATTRIBS*(num-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS);
-         origVertices++;
-
-         if( num%2==1 )
-           {
-           System.arraycopy(mesh.mVertAttribs, VERT_ATTRIBS*(num-1), newAttribs, VERT_ATTRIBS*origVertices, VERT_ATTRIBS);
-           origVertices++;
-           }
+         Component comp = mComponent.get(i);
+         changeTextureMap(maps[i],comp.mTextureMap,lastEnd,comp.mEndIndex);
+         comp.setMap(maps[i]);
+         lastEnd = comp.mEndIndex;
          }
        }
 
-     if( origVertices!=mNumVertices )
-       {
-       android.util.Log.e("mesh", "join: origVertices: "+origVertices+" numVertices: "+mNumVertices);
-       }
-
-     mVertAttribs = newAttribs;
-
      FloatBuffer attribs = ByteBuffer.allocateDirect(mNumVertices*VERT_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
      attribs.put(mVertAttribs).position(0);
 
@@ -521,20 +502,86 @@ public abstract class MeshBase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Sets texture maps for all components of this mesh.
+ * Each mesh has its 'bounding box' - return half of its X-length.
  * <p>
- * Please note that calling this once with the complete list of Maps will be much faster than
- * calling it repeatedly with one Maps at a time, as we have to reallocate the array of vertices
- * each time.
+ * In case of all 'simple' Meshes, the bounding box is always 1x1x1 (Sphere, Cubes) or 1x1x0
+ * (Rectangles, Triangles, Quad - i.e. all 'flat' Meshes). But this can be something else in case of
+ * MeshComponent.
  */
-   public void setTextureMap(float[][] maps)
+   public float getBoundingX()
+    {
+    return mBoundingX*mStretchX;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Each mesh has its 'bounding box' - return half of its Y-length.
+ */
+   public float getBoundingY()
+    {
+    return mBoundingY*mStretchY;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Each mesh has its 'bounding box' - return half of its Z-length.
+ */
+   public float getBoundingZ()
+    {
+    return mBoundingZ*mStretchZ;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sometimes we want to display a Mesh on a rectangular screen. Then we need to stretch it by
+ * different factors in x and y (or z) directions. If we also wanted do display some vertex effects
+ * done on this mesh, let's say a bulge done by a Distort effect, and wanted the bulge to be round,
+ * (i.e the same in x and y directions) then doing so without this method would be impossible.
+ *
+ * This sets 'stretch' factors in each 3 dimensions. All vertices of this Mesh will be premultiplied
+ * by those factors in the very first line of the Vertex Shader, before any Effects are done on it.
+ * Using this we can thus pre-stretch the mesh to aspect ratio equal to the surface we eventually
+ * want to display the Mesh on, and this way we can achieve a round Distort bulge!
+ *
+ * This could also be used to pre-stretch a Rectangles Mesh to a size equal (in pixels) to the bitmap
+ * this mesh is textured with - and this lets us work with all Effects in natural, pixel units.
+ *
+ * @param sx stretch factor in x.
+ * @param sy stretch factor in y.
+ * @param sz stretch factor in z.
+ */
+   public void setStretch(float sx, float sy, float sz)
      {
-     int components = mComponent.size();
+     mStretchX = sx;
+     mStretchY = sy;
+     mStretchZ = sz;
+     }
 
-     for(int comp=0; comp<components; comp++)
-       {
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the x-factor set by setStretch().
+ */
+   public float getStretchX()
+     {
+     return mStretchX;
+     }
 
-       }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the y-factor set by setStretch().
+ */
+   public float getStretchY()
+     {
+     return mStretchY;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the z-factor set by setStretch().
+ */
+   public float getStretchZ()
+     {
+     return mStretchZ;
      }
    }
 
diff --git a/src/main/java/org/distorted/library/mesh/MeshJoined.java b/src/main/java/org/distorted/library/mesh/MeshJoined.java
new file mode 100644
index 0000000..84c1daa
--- /dev/null
+++ b/src/main/java/org/distorted/library/mesh/MeshJoined.java
@@ -0,0 +1,66 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.mesh;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class MeshJoined extends MeshBase
+  {
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Join a list of (probably already changed by Matrix Effects) Meshes into one.
+ * <p>
+ * Try to keep the origin (0,0,0) in the center of gravity of the whole thing.
+ */
+  public MeshJoined(MeshBase[] meshes)
+    {
+    super(1,1,1);
+    join(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the lengths of the bounding box.
+ * <p>
+ * The 'bounding box' is an imaginary rectanguloid of size (bx,by,bz) inside of which this Mesh
+ * fits tightly.
+ * 'Normal' 3D meshes (e.g. Sphere) have this equal to 1,1,1. 'Normal' flat meshes (e.g. Quad) -
+ * to 1,1,0. Here however we are joining several, probably already changed by MatrixEffects,
+ * Meshes together. If the result fits into the standard 1,1,1 (recommended!) - we don't need to
+ * call this function at all.
+ *
+ * @param bx x-length of the bounding box
+ * @param by y-length of the bounding box
+ * @param bz z-length of the bounding box
+ */
+   public void setBounding(float bx, float by, float bz)
+     {
+     super.setBounding(bx,by,bz);
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return how many basic Meshes is this Mesh joined from.
+ */
+   public int getNumComponents()
+     {
+     return numComponents();
+     }
+  }
