commit 36d65d88c10a1dafebe56c5159223115e5e7cb39
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri May 29 16:48:05 2020 +0100

    Progress making it possible to apply Vertex Effects only to some Components of a Mesh.

diff --git a/src/main/java/org/distorted/library/effect/Effect.java b/src/main/java/org/distorted/library/effect/Effect.java
index 519e4c9..52376e7 100644
--- a/src/main/java/org/distorted/library/effect/Effect.java
+++ b/src/main/java/org/distorted/library/effect/Effect.java
@@ -49,6 +49,7 @@ public abstract class Effect
   private final static int[]   mUnityDim = new int[NUM_EFFECTS];
 
   int mAssociation;
+  int mComponent;
 
   static boolean[] mEnabled = new boolean[NUM_EFFECTS];
 
@@ -68,6 +69,7 @@ public abstract class Effect
     mRegionDim = name.getRegionDimension();
 
     mAssociation = 0xffffffff;
+    mComponent   = 0;
 
     int n = name.ordinal();
     float[] u = name.getUnity();
@@ -145,6 +147,17 @@ public abstract class Effect
     return mAssociation;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Only for use by the library itself.
+ *
+ * @y.exclude
+ */
+  public int getComponent()
+    {
+    return mComponent;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // 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 36d303e..c08a981 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+" && (int(a_Association) & vAssociation[i]) != 0 )\n" +
+        "if( vName[i]=="+effect+" )\n" +
           "{\n" +
            code +"\n" +
           "}\n" +
@@ -194,13 +194,18 @@ public abstract class VertexEffect extends Effect
  *
  * This creates an association between this Vertex Effect and a Component of a Mesh.
  * One can set the association of an Effect and of a Component, and the Effect will only be active on
- * vertices of Components such that (effect assoc) & (component assoc) != 0. (see retSection() above)
+ * vertices of Components such that
+ *
+ * (effect assoc) & (component assoc) != 0 || (effect component) == (mesh component)
+ *
+ * (see main_vertex_shader)
  *
  * The point: this way we can configure the system so that each Vertex Effect acts only on a certain
  * subset of a Mesh, thus potentially significantly reducing the number of render calls.
  */
-  public void setMeshAssociation(int association)
+  public void setMeshAssociation(int component, int association)
     {
+    mComponent   = component;
     mAssociation = association;
     }
   }
\ 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 b3208ca..abfc45f 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
@@ -37,7 +37,7 @@ import java.util.HashMap;
  */
 public abstract class EffectQueue implements InternalMaster.Slave
   {
-  static final int MAIN_VARIANTS = 4; // Number of Main program variants (ATM 4: MAIN, MAIN OIT, PREPROCESS, FULL)
+  public static final int MAIN_VARIANTS = 4; // Number of Main program variants (ATM 4: MAIN, MAIN OIT, PREPROCESS, FULL)
 
   private static final int CREATE = 0;
   private static final int ATTACH = 1;
@@ -52,6 +52,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
   Effect[] mEffects;
   int[] mName;
   int[] mAssociation;
+  int[] mComponent;
   long mTime;
 
   private static int[] mMax = new int[EffectType.LENGTH];
@@ -143,6 +144,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
         mEffects         = new Effect[max];
         mName            = new int[max];
         mAssociation     = new int[max];
+        mComponent       = new int[max];
         }
 
       for(int i=0; i<mNumEffects; i++ )
@@ -151,6 +153,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
         mCurrentDuration[i] = source.mCurrentDuration[i];
         mName[i]            = source.mName[i];
         mAssociation[i]     = source.mAssociation[i];
+        mComponent[i]       = source.mComponent[i];
         }
       }
     }
@@ -280,6 +283,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
       mCurrentDuration[j] = mCurrentDuration[j+1];
       mName[j]            = mName[j+1];
       mAssociation[j]     = mAssociation[j+1];
+      mComponent[j]       = mComponent[j+1];
       }
 
     mEffects[mNumEffects] = null;
@@ -431,6 +435,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
                        mEffects         = new Effect[max];
                        mName            = new int[max];
                        mAssociation     = new int[max];
+                       mComponent       = new int[max];
                        }
                      mCreated = true;
 
@@ -445,6 +450,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
                          mEffects[mNumEffects]    = job.effect;
                          mName[mNumEffects]       = job.effect.getName().ordinal();
                          mAssociation[mNumEffects]= job.effect.getAssociation();
+                         mComponent[mNumEffects]  = job.effect.getComponent();
 
                          mNumEffects++;
                          changed = true;
@@ -457,12 +463,14 @@ public abstract class EffectQueue implements InternalMaster.Slave
                            mEffects[j]         = mEffects[j-1];
                            mName[j]            = mName[j-1];
                            mAssociation[j]     = mAssociation[j-1];
+                           mComponent[j]       = mComponent[j-1];
                            }
 
                          mCurrentDuration[position] = 0;
                          mEffects[position]    = job.effect;
                          mName[position]       = job.effect.getName().ordinal();
                          mAssociation[position]= job.effect.getAssociation();
+                         mComponent[position]  = job.effect.getComponent();
 
                          mNumEffects++;
                          changed = true;
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
index 1233660..6ceabb7 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
@@ -126,6 +126,8 @@ public class EffectQueuePostprocess extends EffectQueue
     String mainVertHeader= version + ("#define NUM_VERTEX "   + ( numV>0 ? DistortedLibrary.getMax(EffectType.VERTEX  ) : 0 ) + "\n");
     String mainFragHeader= version + "\n";
 
+    mainVertHeader += "#define MAX_COMPON " + MeshBase.getMaxComponents() + "\n";
+
     String enabledEffectV= VertexEffect.getGLSL();
 
     try
@@ -142,6 +144,7 @@ public class EffectQueuePostprocess extends EffectQueue
     int preProgramH = mPreProgram.getProgramHandle();
     EffectQueueVertex.getUniforms( preProgramH,2 );
     EffectQueueMatrix.getUniforms( preProgramH,2 );
+    MeshBase.getUniforms(preProgramH,2);
     mPreColorH  = GLES30.glGetUniformLocation( preProgramH, "u_Color"  );
     mPreTextureH= GLES30.glGetUniformLocation( preProgramH, "u_Texture");
     }
@@ -159,7 +162,7 @@ public class EffectQueuePostprocess extends EffectQueue
 
   public boolean getRender()
     {
-    return mNumEffects>0 ? ((PostprocessEffect)mEffects[0]).getRender() : false;
+    return mNumEffects > 0 && ((PostprocessEffect) mEffects[0]).getRender();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -180,6 +183,7 @@ public class EffectQueuePostprocess extends EffectQueue
     mPreProgram.useProgram();
 
     mesh.bindVertexAttribs(mPreProgram);
+    mesh.send(2);
 
     EffectQueue[] queues = effects.getQueues();
     EffectQueueMatrix matrix = (EffectQueueMatrix)queues[0];
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
index d04a21a..358bcdf 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
@@ -40,6 +40,7 @@ public class EffectQueueVertex extends EffectQueue
   private static int[] mNameH       = new int[MAIN_VARIANTS];
   private static int[] mUniformsH   = new int[MAIN_VARIANTS];
   private static int[] mAssociationH= new int[MAIN_VARIANTS];
+  private static int[] mComponentH  = new int[MAIN_VARIANTS];
   private static int[] mInflateH    = new int[MAIN_VARIANTS];
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -63,7 +64,8 @@ public class EffectQueueVertex extends EffectQueue
     mNumEffectsH[variant] = GLES30.glGetUniformLocation( mProgramH, "vNumEffects");
     mNameH[variant]       = GLES30.glGetUniformLocation( mProgramH, "vName");
     mUniformsH[variant]   = GLES30.glGetUniformLocation( mProgramH, "vUniforms");
-    mAssociationH[variant]= GLES30.glGetUniformLocation( mProgramH, "vAssociation");
+    mAssociationH[variant]= GLES30.glGetUniformLocation( mProgramH, "vEffectAssoc");
+    mComponentH[variant]  = GLES30.glGetUniformLocation( mProgramH, "vEffectCompo");
     mInflateH[variant]    = GLES30.glGetUniformLocation( mProgramH, "u_Inflate");
     }
 
@@ -83,6 +85,7 @@ public class EffectQueueVertex extends EffectQueue
       {
       mCurrentDuration[i] += step;
       mAssociation[i] = mEffects[i].getAssociation();
+      mComponent[i] = mEffects[i].getComponent();
 
       if( mEffects[i].compute(mUniforms, NUM_UNIFORMS*i, mCurrentDuration[i], step) )
         {
@@ -108,6 +111,7 @@ public class EffectQueueVertex extends EffectQueue
       {
       GLES30.glUniform1iv( mNameH[variant]       ,                 mNumEffects, mName       , 0);
       GLES30.glUniform1iv( mAssociationH[variant],                 mNumEffects, mAssociation, 0);
+      GLES30.glUniform1iv( mComponentH[variant]  ,                 mNumEffects, mComponent  , 0);
       GLES30.glUniform4fv( mUniformsH[variant]   ,(NUM_UNIFORMS/4)*mNumEffects, mUniforms   , 0);
       }
     }
diff --git a/src/main/java/org/distorted/library/main/DistortedLibrary.java b/src/main/java/org/distorted/library/main/DistortedLibrary.java
index 472276d..22873b8 100644
--- a/src/main/java/org/distorted/library/main/DistortedLibrary.java
+++ b/src/main/java/org/distorted/library/main/DistortedLibrary.java
@@ -215,6 +215,9 @@ public class DistortedLibrary
 
     String mainVertHeader= mGLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX  ) : 0 ) + "\n");
     String mainFragHeader= mGLSL_VERSION + ("#define NUM_FRAGMENT " + ( numF>0 ? getMax(EffectType.FRAGMENT) : 0 ) + "\n");
+
+    mainVertHeader += "#define MAX_COMPON " + MeshBase.getMaxComponents() + "\n";
+
     String enabledEffectV= VertexEffect.getGLSL();
     String enabledEffectF= FragmentEffect.getGLSL();
 
@@ -233,6 +236,7 @@ public class DistortedLibrary
 
     int mainProgramH = mMainProgram.getProgramHandle();
     EffectQueue.getUniforms(mainProgramH,0);
+    MeshBase.getUniforms(mainProgramH,0);
     mMainTextureH= GLES30.glGetUniformLocation( mainProgramH, "u_Texture");
 
     // BLIT PROGRAM ////////////////////////////////////
@@ -302,6 +306,9 @@ public class DistortedLibrary
 
     String fullVertHeader= mGLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX ) : 0 ) + "\n");
     String fullFragHeader= mGLSL_VERSION + ("#define NUM_FRAGMENT " +                                         0   + "\n");
+
+    fullVertHeader += "#define MAX_COMPON " + MeshBase.getMaxComponents() + "\n";
+
     String enabledEffectV= VertexEffect.getAllGLSL();
     String enabledEffectF= "{}";
 
@@ -322,6 +329,7 @@ public class DistortedLibrary
 
     int fullProgramH = mFullProgram.getProgramHandle();
     EffectQueue.getUniforms(fullProgramH,3);
+    MeshBase.getUniforms(fullProgramH,3);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -338,6 +346,8 @@ public class DistortedLibrary
     String mainVertHeader= mGLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX  ) : 0 ) + "\n") + ("#define OIT\n");
     String mainFragHeader= mGLSL_VERSION + ("#define NUM_FRAGMENT " + ( numF>0 ? getMax(EffectType.FRAGMENT) : 0 ) + "\n") + ("#define OIT\n");
 
+    mainVertHeader += "#define MAX_COMPON " + MeshBase.getMaxComponents() + "\n";
+
     String enabledEffectV= VertexEffect.getGLSL();
     String enabledEffectF= FragmentEffect.getGLSL();
 
@@ -354,6 +364,7 @@ public class DistortedLibrary
 
     int mainOITProgramH = mMainOITProgram.getProgramHandle();
     EffectQueue.getUniforms(mainOITProgramH,1);
+    MeshBase.getUniforms(mainOITProgramH,1);
     mMainOITTextureH    = GLES30.glGetUniformLocation( mainOITProgramH, "u_Texture");
     mMainOITSizeH       = GLES30.glGetUniformLocation( mainOITProgramH, "u_Size");
     mMainOITNumRecordsH = GLES30.glGetUniformLocation( mainOITProgramH, "u_numRecords");
@@ -468,7 +479,7 @@ public class DistortedLibrary
     {
     if( mFullProgram!=null )
       {
-      GLES30.glViewport(0, 0, 500, 500 ); // TODO ???
+      GLES30.glViewport(0, 0, 500, 500 ); // this is fine, we don't need viewport at all
 
       int num = mesh.getNumVertices();
       int tfo = mesh.getTFO();
@@ -477,6 +488,7 @@ public class DistortedLibrary
       mesh.bindVertexAttribs(mFullProgram);
       mesh.computeQueue();
       mesh.sendQueue();
+      mesh.send(3);
 
       GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfo );
       GLES30.glBeginTransformFeedback( GLES30.GL_POINTS);
@@ -503,6 +515,7 @@ public class DistortedLibrary
     GLES30.glUniform2ui(DistortedLibrary.mMainOITSizeH, surface.mWidth, surface.mHeight);
     GLES30.glUniform1ui(DistortedLibrary.mMainOITNumRecordsH, (int)(DistortedLibrary.mBufferSize*surface.mWidth*surface.mHeight) );
     mesh.bindVertexAttribs(DistortedLibrary.mMainOITProgram);
+    mesh.send(1);
 
     float inflate     = mesh.getInflate();
     float distance    = surface.mDistance;
@@ -515,6 +528,7 @@ public class DistortedLibrary
     if( mesh.getShowNormals() )
       {
       DistortedLibrary.mMainProgram.useProgram();
+      mesh.send(0);
       EffectQueue.send(queues, distance, mipmap, projection, inflate, mesh, 0 );
       displayNormals(queues,mesh);
       }
@@ -534,6 +548,7 @@ public class DistortedLibrary
       mMainProgram.useProgram();
       GLES30.glUniform1i(DistortedLibrary.mMainTextureH, 0);
       mesh.bindVertexAttribs(DistortedLibrary.mMainProgram);
+      mesh.send(0);
 
       float inflate     = mesh.getInflate();
       float distance    = surface.mDistance;
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index 4c9ecb8..6029ba4 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -23,6 +23,7 @@ import android.opengl.GLES30;
 import android.util.Log;
 
 import org.distorted.library.effect.VertexEffect;
+import org.distorted.library.effectqueue.EffectQueue;
 import org.distorted.library.effectqueue.EffectQueueVertex;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.InternalBuffer;
@@ -43,23 +44,24 @@ import java.util.ArrayList;
  */
 public abstract class MeshBase
    {
-   static final float DEFAULT_ASSOCIATION = 0xffffffff;
+   private static final int MAX_COMPONENTS= 100;
+   static final int DEFAULT_ASSOCIATION = 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 ASS_DATA_SIZE= 1; // association, a single float
+   private static final int COM_DATA_SIZE= 1; // component number, 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 ASS_ATTRIB   = TEX_DATA_SIZE;
+   static final int COM_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 + ASS_DATA_SIZE;                  // number of attributes of a vertex (the 'preapply invariant' part)
+   static final int VERT2_ATTRIBS= TEX_DATA_SIZE + COM_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;
@@ -68,7 +70,7 @@ 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 = ASS_ATTRIB*BYTES_PER_FLOAT;
+   private static final int OFFSET_COM = COM_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;
@@ -78,9 +80,12 @@ public abstract class MeshBase
    private InternalBuffer mVBO1, mVBO2, mTFO; // main vertex buffer and transform feedback buffer
    private int mNumVertices;
    private float[] mVertAttribs1;             // packed: PosX,PosY,PosZ, NorX,NorY,NorZ, InfX,InfY,InfZ
-   private float[] mVertAttribs2;             // packed: TexS,TexT
+   private float[] mVertAttribs2;             // packed: TexS,TexT, Component
    private float mInflate;
    private boolean[] mNeedsAdjustment;
+   private int[] mAssociation;
+
+   private static int[] mComponentAssociationH = new int[EffectQueue.MAIN_VARIANTS];
 
    private static class Component
      {
@@ -121,9 +126,11 @@ public abstract class MeshBase
      mShowNormals= false;
      mInflate    = 0.0f;
      mComponent  = new ArrayList<>();
+     mAssociation= new int[MAX_COMPONENTS];
+
+     for(int i=0; i<MAX_COMPONENTS; i++) mAssociation[i] = DEFAULT_ASSOCIATION;
 
      mNeedsAdjustment = new boolean[1];
-     mNeedsAdjustment[0] = false;
 
      mVBO1= new InternalBuffer(GLES30.GL_ARRAY_BUFFER             , GLES30.GL_STATIC_READ);
      mVBO2= new InternalBuffer(GLES30.GL_ARRAY_BUFFER             , GLES30.GL_STATIC_READ);
@@ -148,6 +155,9 @@ public abstract class MeshBase
        mComponent.add(comp);
        }
 
+     mAssociation= new int[MAX_COMPONENTS];
+     System.arraycopy(original.mAssociation, 0, mAssociation, 0, MAX_COMPONENTS);
+
      if( deep )
        {
        mNeedsAdjustment = new boolean[1];
@@ -174,6 +184,20 @@ public abstract class MeshBase
      mTFO.invalidate();
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public static int getMaxComponents()
+     {
+     return MAX_COMPONENTS;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   public static void getUniforms(int mProgramH, int variant)
+     {
+     mComponentAssociationH[variant] = GLES30.glGetUniformLocation( mProgramH, "vComponAssoc");
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    int numComponents()
@@ -190,7 +214,7 @@ public abstract class MeshBase
      mVertAttribs1= vert1Attribs;
      mVertAttribs2= vert2Attribs;
 
-     mComponent.add(new Component(mNumVertices));
+     mComponent.add(new Component(mNumVertices-1));
 
      mVBO1.invalidate();
      mVBO2.invalidate();
@@ -203,16 +227,16 @@ public abstract class MeshBase
      {
      MeshBase mesh;
      Component comp;
-     int numComponents, numVertices, numMeshes = meshes.length;
+     int origComponents=0, numComponents, numVertices, numMeshes = meshes.length;
      int origVertices = mNumVertices;
 
      // compute new numVertices; take care of Components
 
      if( origVertices>0 )
        {
-       numComponents = mComponent.size();
+       origComponents = mComponent.size();
        mNumVertices+= ( mNumVertices%2==1 ? 2:1 );
-       mComponent.get(numComponents-1).mEndIndex = mNumVertices;
+       mComponent.get(origComponents-1).mEndIndex = mNumVertices-1;
        }
 
      for(int i=0; i<numMeshes; i++)
@@ -220,24 +244,25 @@ public abstract class MeshBase
        mesh = meshes[i];
        numComponents = mesh.mComponent.size();
 
+       numVertices = mesh.mNumVertices;
+
+       int extraVerticesBefore = mNumVertices==0 ? 0:1;
+       int extraVerticesAfter  = (i==numMeshes-1) ? 0 : (numVertices%2==1 ? 2:1);
+
        for(int j=0; j<numComponents; j++)
          {
          comp = new Component(mesh.mComponent.get(j));
-         comp.mEndIndex += mNumVertices;
+         comp.mEndIndex += (extraVerticesBefore+mNumVertices+extraVerticesAfter);
          mComponent.add(comp);
-         }
 
-       numVertices = mesh.mNumVertices;
-
-       if( mNumVertices==0 )
-         {
-         if( numMeshes>1 )
-            mNumVertices += (numVertices%2==1 ? numVertices+2 : numVertices+1);
-         else
-            mNumVertices +=  numVertices;
+         if( origComponents<MAX_COMPONENTS )
+           {
+           mAssociation[origComponents] = mesh.mAssociation[j];
+           origComponents++;
+           }
          }
-       else if( i==numMeshes-1 ) mNumVertices += (numVertices+1);
-       else                      mNumVertices += (numVertices%2==1 ? numVertices+3 : numVertices+2);
+
+       mNumVertices += (extraVerticesBefore+numVertices+extraVerticesAfter);
        }
 
      // allocate new attrib array
@@ -296,6 +321,15 @@ public abstract class MeshBase
        android.util.Log.e("mesh", "join: origVertices: "+origVertices+" numVertices: "+mNumVertices);
        }
 
+     int endIndex, index=0, numComp = mComponent.size();
+
+     for(int component=0; component<numComp; component++)
+       {
+       endIndex = mComponent.get(component).mEndIndex;
+
+       for( ; index<=endIndex; index++) newAttribs2[VERT2_ATTRIBS*index+COM_ATTRIB] = component;
+       }
+
      mVertAttribs1 = newAttribs1;
      mVertAttribs2 = newAttribs2;
 
@@ -389,6 +423,17 @@ public abstract class MeshBase
      return mNumVertices;
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Not part of public API, do not document (public only because has to be used from the main package)
+ *
+ * @y.exclude
+ */
+   public void send(int variant)
+     {
+     GLES30.glUniform1iv( mComponentAssociationH[variant], MAX_COMPONENTS, mAssociation, 0);
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Not part of public API, do not document (public only because has to be used from the main package)
@@ -412,7 +457,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], ASS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TAG);
+     GLES30.glVertexAttribPointer(program.mAttribute[4], COM_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM);
      GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
      }
 
@@ -599,29 +644,20 @@ public abstract class MeshBase
  *
  * This creates an association between a Component of this Mesh and a Vertex Effect.
  * One can set the association of an Effect and of a Component, and the Effect will only be active on
- * vertices of Components such that (effect assoc) & (component assoc) != 0. (see VertexEffect.retSection() )
+ * vertices of Components such that
+ *
+ * (effect assoc) & (component assoc) != 0 || (effect component) == (mesh component)
+ *
+ * (see main_vertex_shader)
  *
  * The point: this way we can configure the system so that each Vertex Effect acts only on a certain
  * subset of a Mesh, thus potentially significantly reducing the number of render calls.
  */
   public void setEffectAssociation(int component, int association)
     {
-    int num = mComponent.size();
-
-    if( component>=0 && component<num )
+    if( component>=0 && component<MAX_COMPONENTS )
       {
-      int sta = component>0 ? mComponent.get(component-1).mEndIndex : 0;
-      int end = mComponent.get(component).mEndIndex;
-
-      sta = sta*VERT2_ATTRIBS + ASS_ATTRIB;
-      end = end*VERT2_ATTRIBS + ASS_ATTRIB;
-
-      for(int i=sta; i<end; i+=VERT2_ATTRIBS)
-        {
-        mVertAttribs2[i] = association;
-        }
-
-      mVBO2.invalidate();
+      mAssociation[component] = association;
       }
     }
 
@@ -631,7 +667,7 @@ public abstract class MeshBase
  *
  * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
  *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect assocciations, is always deep copied)
+ *             coordinates and effect associations, is always deep copied)
  */
    public abstract MeshBase copy(boolean deep);
    }
\ 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 0915b09..7fd77fe 100644
--- a/src/main/java/org/distorted/library/mesh/MeshCubes.java
+++ b/src/main/java/org/distorted/library/mesh/MeshCubes.java
@@ -643,8 +643,6 @@ public class MeshCubes extends MeshBase
        attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[BACK]  + (1.0f-y) * mTexMappingH[BACK];
        }
 
-     attribs2[VERT2_ATTRIBS*currVert + ASS_ATTRIB] = DEFAULT_ASSOCIATION;
-
      currVert++;
      }
 
@@ -677,7 +675,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
                    break;
        case SOUTH: row = curr.row+1;
@@ -700,7 +697,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
                    break;
        case WEST : row = (back  ? (curr.row+1):(curr.row));
@@ -723,7 +719,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
                    break;
        case EAST : row = (back  ? (curr.row):(curr.row+1));
@@ -746,7 +741,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
                    break;
        }
@@ -772,7 +766,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
      currVert++;
      }
diff --git a/src/main/java/org/distorted/library/mesh/MeshQuad.java b/src/main/java/org/distorted/library/mesh/MeshQuad.java
index efefdf4..6cb142f 100644
--- a/src/main/java/org/distorted/library/mesh/MeshQuad.java
+++ b/src/main/java/org/distorted/library/mesh/MeshQuad.java
@@ -46,7 +46,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/mesh/MeshRectangles.java b/src/main/java/org/distorted/library/mesh/MeshRectangles.java
index ae33550..0c92fcf 100644
--- a/src/main/java/org/distorted/library/mesh/MeshRectangles.java
+++ b/src/main/java/org/distorted/library/mesh/MeshRectangles.java
@@ -73,7 +73,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
      return vertex+1;
      }
@@ -100,7 +99,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
        vertex++;
        }
diff --git a/src/main/java/org/distorted/library/mesh/MeshSphere.java b/src/main/java/org/distorted/library/mesh/MeshSphere.java
index 57d3b0b..a88dea0 100644
--- a/src/main/java/org/distorted/library/mesh/MeshSphere.java
+++ b/src/main/java/org/distorted/library/mesh/MeshSphere.java
@@ -95,7 +95,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
       currentVert++;
       }
@@ -199,7 +198,6 @@ 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 + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
     currentVert++;
 
diff --git a/src/main/java/org/distorted/library/mesh/MeshTriangles.java b/src/main/java/org/distorted/library/mesh/MeshTriangles.java
index 76e8d8e..2df3c81 100644
--- a/src/main/java/org/distorted/library/mesh/MeshTriangles.java
+++ b/src/main/java/org/distorted/library/mesh/MeshTriangles.java
@@ -48,7 +48,6 @@ public class MeshTriangles extends MeshBase
 
      attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = x;
      attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = y;
-     attribs2[VERT2_ATTRIBS*vertex + ASS_ATTRIB  ] = DEFAULT_ASSOCIATION;
 
      return vertex+1;
      }
diff --git a/src/main/java/org/distorted/library/program/DistortedProgram.java b/src/main/java/org/distorted/library/program/DistortedProgram.java
index 5adcb78..2d453e0 100644
--- a/src/main/java/org/distorted/library/program/DistortedProgram.java
+++ b/src/main/java/org/distorted/library/program/DistortedProgram.java
@@ -185,8 +185,6 @@ public class DistortedProgram
 
       if( subline.startsWith(mAttributeStr))
         {
-        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
-
         for(nameBegin=subLen-1; nameBegin>mAttributeLen-2; nameBegin--)
           {
           currChar=subline.charAt(nameBegin);
@@ -209,28 +207,25 @@ public class DistortedProgram
     {
     String attribute, attrList="", uniform;
     String[] lines = shader.split("\n");
-    int length = lines.length;
 
-    for(int i=0; i<length; i++)
+    for (String line : lines)
       {
-      if( doAttributes )
+      if (doAttributes)
         {
-        attribute = parseOutAttribute(lines[i]);
+        attribute = parseOutAttribute(line);
 
-        if( attribute!=null )
+        if (attribute != null)
           {
-          //android.util.Log.d("program", "new attribute: "+attribute);
-          if( attrList.length()>0 ) attrList+=" ";
+          if (attrList.length() > 0) attrList += " ";
           attrList += attribute;
           }
         }
 
-      uniform = parseOutUniform(lines[i]);
+      uniform = parseOutUniform(line);
 
-      if( uniform!=null )
+      if (uniform != null)
         {
-        //android.util.Log.d("program", "new uniform: "+uniform);
-        if( mUniList.length()>0 ) mUniList+=" ";
+        if (mUniList.length() > 0) mUniList += " ";
         mUniList += uniform;
         }
       }
@@ -323,13 +318,7 @@ public class DistortedProgram
       {
       String begin = code.substring(0,place-1);
       String end   = code.substring(place+length);
-/*
-int len = begin.length();
 
-android.util.Log.d("Program", begin.substring(len-40));
-android.util.Log.d("Program", effects);
-android.util.Log.d("Program", end.substring(0,40));
-*/
       return begin + effects + end;
       }
     else
diff --git a/src/main/res/raw/main_vertex_shader.glsl b/src/main/res/raw/main_vertex_shader.glsl
index 6c72b4b..830a7e8 100644
--- a/src/main/res/raw/main_vertex_shader.glsl
+++ b/src/main/res/raw/main_vertex_shader.glsl
@@ -25,7 +25,7 @@ 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_Association;              // Per-vertex association. Connects the vertex (really the mesh component the vertex is a member of)
+in float a_Component;                // The component a vertex belongs to.
                                      // to a vertex effect. An effect will only be active on a vertex iff (a_Association & vAssociation[effect]) != 0.
                                      // ( see VertexEffect.retSection() )
 
@@ -49,7 +49,9 @@ 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 vAssociation[NUM_VERTEX];// Associations of the vertex effects. Those are used to connect an effect to a Mesh component.
+uniform int vEffectAssoc[NUM_VERTEX];// Associations of the vertex effects. Those are used to connect an effect to a Mesh component.
+uniform int vEffectCompo[NUM_VERTEX];// Components the vertex effects work on. Likewise used to conneect an effect to a Mesh component.
+uniform int vComponAssoc[MAX_COMPON];// Associations of the component.
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 // HELPER FUNCTIONS
@@ -104,11 +106,15 @@ void main()
 
 #if NUM_VERTEX>0
   int effect=0;
+  int component = int(a_Component);
 
   for(int i=0; i<vNumEffects; i++)
     {
-    // ENABLED EFFECTS WILL BE INSERTED HERE
+    if( ((vComponAssoc[component] & vEffectAssoc[i]) != 0) || (component == vEffectCompo[i]) )
+      {
+      // ENABLED EFFECTS WILL BE INSERTED HERE
 
+      }
     effect+=3;
     }
 #endif
