commit bc208a9c0bc897d372edb1f3d7684a92660644a3
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sat May 23 11:30:24 2020 +0100

    Introduce 'tags' (soon to be renamed to 'associations') to Mesh and VertexEffect.
    A 'tag' is a way to associate a Vertex Effect only to a subgroup of a given Mesh's vertices (more precisely, only to an arbitrary subset of its Components)

diff --git a/src/main/java/org/distorted/library/effect/Effect.java b/src/main/java/org/distorted/library/effect/Effect.java
index b810e11..27c778d 100644
--- a/src/main/java/org/distorted/library/effect/Effect.java
+++ b/src/main/java/org/distorted/library/effect/Effect.java
@@ -48,6 +48,8 @@ public abstract class Effect
   private final static float[] mUnity= new float[MAX_UNITY_DIM*NUM_EFFECTS];
   private final static int[]   mUnityDim = new int[NUM_EFFECTS];
 
+  int mTag;
+
   static boolean[] mEnabled = new boolean[NUM_EFFECTS];
 
   static
@@ -65,6 +67,8 @@ public abstract class Effect
     mCenterDim = name.getCenterDimension();
     mRegionDim = name.getRegionDimension();
 
+    mTag = 0xffffffff;
+
     int n = name.ordinal();
     float[] u = name.getUnity();
     int l = u.length;
@@ -130,6 +134,17 @@ public abstract class Effect
     return null;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Only for use by the library itself.
+ *
+ * @y.exclude
+ */
+  public int getTag()
+    {
+    return mTag;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/effect/VertexEffect.java b/src/main/java/org/distorted/library/effect/VertexEffect.java
index 15575bd..9d3bd88 100644
--- a/src/main/java/org/distorted/library/effect/VertexEffect.java
+++ b/src/main/java/org/distorted/library/effect/VertexEffect.java
@@ -58,7 +58,7 @@ public abstract class VertexEffect extends Effect
     {
     return
 
-        "if( vName[i]=="+effect+")\n" +
+        "if( vName[i]=="+effect+" && (int(a_Tag) & vTag[i]) != 0 )\n" +
           "{\n" +
            code +"\n" +
           "}\n" +
@@ -187,4 +187,20 @@ public abstract class VertexEffect extends Effect
     {
     return mNumEnabled;
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Set a tag.
+ *
+ * A 'tag' of a Vertex Effect joins the effect with a component of a Mesh the effect gets applied to.
+ * One can set a Tag of the effect and of a Component, and the Effect will only be active on vertices
+ * of Components such that (effect tag) & (component tag) != 0. (see retSection() above)
+ *
+ * The point: this way we can configure the system so that each Vertex Effect acts only on a certain
+ * subset of the Mesh, thus potentially significantly reducing the number of render calls.
+ */
+  public void setTag(int tag)
+    {
+    mTag = tag;
+    }
   }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueue.java b/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
index 3d4099c..d8e47f6 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
@@ -51,6 +51,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
   long[] mCurrentDuration;
   Effect[] mEffects;
   int[] mName;
+  int[] mTag;
   long mTime;
 
   private static int[] mMax = new int[EffectType.LENGTH];
@@ -141,6 +142,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
         mCurrentDuration = new long[max];
         mEffects         = new Effect[max];
         mName            = new int[max];
+        mTag             = new int[max];
         }
 
       for(int i=0; i<mNumEffects; i++ )
@@ -148,6 +150,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
         mEffects[i]         = source.mEffects[i];
         mCurrentDuration[i] = source.mCurrentDuration[i];
         mName[i]            = source.mName[i];
+        mTag[i]             = source.mTag[i];
         }
       }
     }
@@ -276,6 +279,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
       mEffects[j]         = mEffects[j+1];
       mCurrentDuration[j] = mCurrentDuration[j+1];
       mName[j]            = mName[j+1];
+      mTag[j]             = mTag[j+1];
       }
 
     mEffects[mNumEffects] = null;
@@ -426,6 +430,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
                        mCurrentDuration = new long[max];
                        mEffects         = new Effect[max];
                        mName            = new int[max];
+                       mTag             = new int[max];
                        }
                      mCreated = true;
 
@@ -439,6 +444,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
                          mCurrentDuration[mNumEffects] = 0;
                          mEffects[mNumEffects] = job.effect;
                          mName[mNumEffects] = job.effect.getName().ordinal();
+                         mTag[mNumEffects]  = job.effect.getTag();
 
                          mNumEffects++;
                          changed = true;
@@ -450,11 +456,13 @@ public abstract class EffectQueue implements InternalMaster.Slave
                            mCurrentDuration[j] = mCurrentDuration[j-1];
                            mEffects[j]         = mEffects[j-1];
                            mName[j]            = mName[j-1];
+                           mTag[j]             = mTag[j-1];
                            }
 
                          mCurrentDuration[position] = 0;
                          mEffects[position] = job.effect;
                          mName[position] = job.effect.getName().ordinal();
+                         mTag[position]  = job.effect.getTag();
 
                          mNumEffects++;
                          changed = true;
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
index 19bfecf..90d1851 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
@@ -39,6 +39,7 @@ public class EffectQueueVertex extends EffectQueue
   private static int[] mNumEffectsH = new int[MAIN_VARIANTS];
   private static int[] mNameH       = new int[MAIN_VARIANTS];
   private static int[] mUniformsH   = new int[MAIN_VARIANTS];
+  private static int[] mTagH        = new int[MAIN_VARIANTS];
   private static int[] mInflateH    = new int[MAIN_VARIANTS];
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -62,6 +63,7 @@ public class EffectQueueVertex extends EffectQueue
     mNumEffectsH[variant]= GLES30.glGetUniformLocation( mProgramH, "vNumEffects");
     mNameH[variant]      = GLES30.glGetUniformLocation( mProgramH, "vName");
     mUniformsH[variant]  = GLES30.glGetUniformLocation( mProgramH, "vUniforms");
+    mTagH[variant]       = GLES30.glGetUniformLocation( mProgramH, "vTag");
     mInflateH[variant]   = GLES30.glGetUniformLocation( mProgramH, "u_Inflate");
     }
 
@@ -80,6 +82,7 @@ public class EffectQueueVertex extends EffectQueue
     for(int i=0; i<mNumEffects; i++)
       {
       mCurrentDuration[i] += step;
+      mTag[i] = mEffects[i].getTag();
 
       if( mEffects[i].compute(mUniforms, NUM_UNIFORMS*i, mCurrentDuration[i], step) )
         {
@@ -104,6 +107,7 @@ public class EffectQueueVertex extends EffectQueue
     if( mNumEffects>0 )
       {
       GLES30.glUniform1iv( mNameH[variant]    ,                 mNumEffects, mName    ,0);
+      GLES30.glUniform1iv( mTagH[variant]     ,                 mNumEffects, mTag     ,0);
       GLES30.glUniform4fv( mUniformsH[variant],(NUM_UNIFORMS/4)*mNumEffects, mUniforms,0);
       }
     }
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index a623ab5..575523d 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -43,18 +43,23 @@ import java.util.ArrayList;
  */
 public abstract class MeshBase
    {
+   static final float DEFAULT_TAG = 0xffffffff;
+
    // sizes of attributes of an individual vertex.
    private static final int POS_DATA_SIZE= 3; // vertex coordinates: x,y,z
    private static final int NOR_DATA_SIZE= 3; // normal vector: x,y,z
    private static final int INF_DATA_SIZE= 3; // 'inflate' vector: x,y,z
    private static final int TEX_DATA_SIZE= 2; // texture coordinates: s,t
+   private static final int TAG_DATA_SIZE= 1; // tag, a single float
 
    static final int POS_ATTRIB   = 0;
    static final int NOR_ATTRIB   = POS_DATA_SIZE;
    static final int INF_ATTRIB   = POS_DATA_SIZE + NOR_DATA_SIZE;
    static final int TEX_ATTRIB   = 0;
+   static final int TAG_ATTRIB   = TEX_DATA_SIZE;
+
    static final int VERT1_ATTRIBS= POS_DATA_SIZE + NOR_DATA_SIZE + INF_DATA_SIZE;  // number of attributes of a vertex (the part changed by preapply)
-   static final int VERT2_ATTRIBS= TEX_DATA_SIZE;                                  // number of attributes of a vertex (the 'preapply invariant' part)
+   static final int VERT2_ATTRIBS= TEX_DATA_SIZE + TAG_DATA_SIZE;                  // number of attributes of a vertex (the 'preapply invariant' part)
    static final int TRAN_ATTRIBS = POS_DATA_SIZE + NOR_DATA_SIZE + INF_DATA_SIZE;  // number of attributes of a transform feedback vertex
 
    private static final int BYTES_PER_FLOAT = 4;
@@ -63,6 +68,8 @@ public abstract class MeshBase
    private static final int OFFSET_NOR = NOR_ATTRIB*BYTES_PER_FLOAT;
    private static final int OFFSET_INF = INF_ATTRIB*BYTES_PER_FLOAT;
    private static final int OFFSET_TEX = TEX_ATTRIB*BYTES_PER_FLOAT;
+   private static final int OFFSET_TAG = TAG_ATTRIB*BYTES_PER_FLOAT;
+
    private static final int TRAN_SIZE  = TRAN_ATTRIBS*BYTES_PER_FLOAT;
    private static final int VERT1_SIZE = VERT1_ATTRIBS*BYTES_PER_FLOAT;
    private static final int VERT2_SIZE = VERT2_ATTRIBS*BYTES_PER_FLOAT;
@@ -132,6 +139,7 @@ public abstract class MeshBase
 
      int size = original.mComponent.size();
      mComponent = new ArrayList<>();
+
      for(int i=0; i<size; i++)
        {
        Component comp = new Component(original.mComponent.get(i));
@@ -392,6 +400,7 @@ public abstract class MeshBase
      GLES30.glVertexAttribPointer(program.mAttribute[2], INF_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_INF);
      GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2 );
      GLES30.glVertexAttribPointer(program.mAttribute[3], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX);
+     GLES30.glVertexAttribPointer(program.mAttribute[4], TAG_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TAG);
      GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
      }
 
@@ -574,10 +583,39 @@ public abstract class MeshBase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Deep copy
+ * Set a component's tag.
+ *
+ * A 'tag' of a component associates all vertices which belong to the component with all Vertex Effects
+ * with a matching tag - i.e. one can set a tag of an Effect and of a Component, and the Effect will
+ * only be active on vertices of Components such that (effect tag) & (component tag) != 0.
+ *
+ * The point: this way we can configure the system so that each Vertex Effect acts only on a certain
+ * subset of the Mesh, thus potentially significantly reducing the number of render calls.
  */
-   public abstract MeshBase deepCopy();
-   }
+  public void setTag(int component, int tag)
+    {
+    int num = mComponent.size();
 
+    if( component>=0 && component<num )
+      {
+      int sta = component>0 ? mComponent.get(component-1).mEndIndex+1 : 0;
+      int end = mComponent.get(component).mEndIndex;
 
+      sta = sta*VERT2_ATTRIBS + TAG_ATTRIB;
+      end = end*VERT2_ATTRIBS + TAG_ATTRIB;
 
+      for(int i=sta; i<=end; i+=VERT2_ATTRIBS)
+        {
+        mVertAttribs2[i] = tag;
+        }
+
+      mVBO2.invalidate();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Deep copy
+ */
+   public abstract MeshBase deepCopy();
+   }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshCubes.java b/src/main/java/org/distorted/library/mesh/MeshCubes.java
index 15aa518..17ac9af 100644
--- a/src/main/java/org/distorted/library/mesh/MeshCubes.java
+++ b/src/main/java/org/distorted/library/mesh/MeshCubes.java
@@ -643,6 +643,8 @@ public class MeshCubes extends MeshBase
        attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[BACK]  + (1.0f-y) * mTexMappingH[BACK];
        }
 
+     attribs2[VERT2_ATTRIBS*currVert + TAG_ATTRIB] = DEFAULT_TAG;
+
      currVert++;
      }
 
@@ -675,6 +677,7 @@ public class MeshCubes extends MeshBase
 
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[TOP] +       x  * mTexMappingW[TOP];
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[TOP] + (0.5f-z) * mTexMappingH[TOP];
+                   attribs2[VERT2_ATTRIBS*currVert + TAG_ATTRIB  ] = DEFAULT_TAG;
 
                    break;
        case SOUTH: row = curr.row+1;
@@ -697,6 +700,7 @@ public class MeshCubes extends MeshBase
 
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[BOTTOM] +       x  * mTexMappingW[BOTTOM];
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[BOTTOM] + (0.5f-z) * mTexMappingH[BOTTOM];
+                   attribs2[VERT2_ATTRIBS*currVert + TAG_ATTRIB  ] = DEFAULT_TAG;
 
                    break;
        case WEST : row = (back  ? (curr.row+1):(curr.row));
@@ -719,6 +723,7 @@ public class MeshCubes extends MeshBase
 
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[LEFT] + (0.5f-z) * mTexMappingW[LEFT];
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[LEFT] + (1.0f-y) * mTexMappingH[LEFT];
+                   attribs2[VERT2_ATTRIBS*currVert + TAG_ATTRIB  ] = DEFAULT_TAG;
 
                    break;
        case EAST : row = (back  ? (curr.row):(curr.row+1));
@@ -741,6 +746,7 @@ public class MeshCubes extends MeshBase
 
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[RIGHT] + (0.5f-z) * mTexMappingW[RIGHT];
                    attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[RIGHT] + (1.0f-y) * mTexMappingH[RIGHT];
+                   attribs2[VERT2_ATTRIBS*currVert + TAG_ATTRIB  ] = DEFAULT_TAG;
 
                    break;
        }
@@ -766,6 +772,7 @@ public class MeshCubes extends MeshBase
 
      attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(currVert-1) + TEX_ATTRIB  ];
      attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(currVert-1) + TEX_ATTRIB+1];
+     attribs2[VERT2_ATTRIBS*currVert + TAG_ATTRIB  ] = DEFAULT_TAG;
 
      currVert++;
      }
diff --git a/src/main/java/org/distorted/library/mesh/MeshQuad.java b/src/main/java/org/distorted/library/mesh/MeshQuad.java
index 1a12892..17b6084 100644
--- a/src/main/java/org/distorted/library/mesh/MeshQuad.java
+++ b/src/main/java/org/distorted/library/mesh/MeshQuad.java
@@ -46,6 +46,7 @@ public class MeshQuad extends MeshBase
 
     attribs2[VERT2_ATTRIBS*index + TEX_ATTRIB  ] = x;
     attribs2[VERT2_ATTRIBS*index + TEX_ATTRIB+1] = 1.0f-y;
+    attribs2[VERT2_ATTRIBS*index + TAG_ATTRIB  ] = DEFAULT_TAG;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/mesh/MeshRectangles.java b/src/main/java/org/distorted/library/mesh/MeshRectangles.java
index 6c6c2dd..f07f5af 100644
--- a/src/main/java/org/distorted/library/mesh/MeshRectangles.java
+++ b/src/main/java/org/distorted/library/mesh/MeshRectangles.java
@@ -73,6 +73,7 @@ public class MeshRectangles extends MeshBase
 
      attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = x;
      attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = 1.0f-y;
+     attribs2[VERT2_ATTRIBS*vertex + TAG_ATTRIB  ] = DEFAULT_TAG;
 
      return vertex+1;
      }
@@ -99,6 +100,7 @@ public class MeshRectangles extends MeshBase
 
        attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(vertex-1) + TEX_ATTRIB  ];
        attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(vertex-1) + TEX_ATTRIB+1];
+       attribs2[VERT2_ATTRIBS*vertex + TAG_ATTRIB  ] = DEFAULT_TAG;
 
        vertex++;
        }
diff --git a/src/main/java/org/distorted/library/mesh/MeshSphere.java b/src/main/java/org/distorted/library/mesh/MeshSphere.java
index a92b099..91c0598 100644
--- a/src/main/java/org/distorted/library/mesh/MeshSphere.java
+++ b/src/main/java/org/distorted/library/mesh/MeshSphere.java
@@ -33,7 +33,7 @@ public class MeshSphere extends MeshBase
   private static final int NUMFACES = 16;
   private static final double P = Math.PI;
 
-  // An array of 16 entries, each describing a single face of the solid in an (admittedly) weird
+  // An array of 16 entries, each describing a single face of a solid in an (admittedly) weird
   // fashion. Each face is a triangle, with 2 vertices on the same latitude.
   // Single row is (longitude of V1, longitude of V2, (common) latitude of V1 and V2, latitude of V3)
   // longitude of V3 is simply midpoint of V1 and V2 so we don't have to specify it here.
@@ -95,6 +95,7 @@ public class MeshSphere extends MeshBase
 
       attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB  ];
       attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB+1];
+      attribs2[VERT2_ATTRIBS*currentVert + TAG_ATTRIB  ] = DEFAULT_TAG;
 
       currentVert++;
       }
@@ -198,6 +199,7 @@ public class MeshSphere extends MeshBase
 
     attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB  ] = (float)texX;
     attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB+1] = (float)texY;
+    attribs2[VERT2_ATTRIBS*currentVert + TAG_ATTRIB  ] = DEFAULT_TAG;
 
     currentVert++;
 
diff --git a/src/main/java/org/distorted/library/mesh/MeshTriangles.java b/src/main/java/org/distorted/library/mesh/MeshTriangles.java
index 6af5ec6..dad30a1 100644
--- a/src/main/java/org/distorted/library/mesh/MeshTriangles.java
+++ b/src/main/java/org/distorted/library/mesh/MeshTriangles.java
@@ -48,6 +48,7 @@ public class MeshTriangles extends MeshBase
 
      attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = x;
      attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = y;
+     attribs2[VERT2_ATTRIBS*vertex + TAG_ATTRIB  ] = DEFAULT_TAG;
 
      return vertex+1;
      }
diff --git a/src/main/res/raw/main_vertex_shader.glsl b/src/main/res/raw/main_vertex_shader.glsl
index ea833f6..9d06c16 100644
--- a/src/main/res/raw/main_vertex_shader.glsl
+++ b/src/main/res/raw/main_vertex_shader.glsl
@@ -25,6 +25,8 @@ in vec3 a_Normal;                    // Per-vertex normal vector.
 in vec3 a_Inflate;                   // This vector describes the direction this vertex needs to go when we 'inflate' the whole mesh.
                                      // If the mesh is locally smooth, this is equal to the normal vector. Otherwise (on sharp edges) - no.
 in vec2 a_TexCoordinate;             // Per-vertex texture coordinate.
+in float a_Tag;                      // Per-vertex tag. Connects the vertex (really the mesh component the vertex is a member of) to a vertex effect.
+                                     // An effect will only be active on a vertex iff (a_Tag & vTag[effect]) != 0.  ( see VertexEffect.retSection() )
 
 out vec3 v_Position;                 //
 out vec3 v_endPosition;              // for Transform Feedback only
@@ -46,6 +48,7 @@ uniform int vName[NUM_VERTEX];       // their names.
 uniform vec4 vUniforms[3*NUM_VERTEX];// i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
                                      // The first vec4 is the Interpolated values,
                                      // second vec4: first float - cache, next 3: Center, the third -  the Region.
+uniform int vTag[NUM_VERTEX];        // Tags of the vertex effects. Tags are used to connect an effect to a Mesh component.
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 // HELPER FUNCTIONS
