commit 97755c02b4f8ce24c3a7bbc42c1f98b2024fc16f
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Aug 19 21:06:55 2020 +0100

    Move the 'effect association' part of MeshBase to a separate class.

diff --git a/src/main/java/org/distorted/library/main/DistortedLibrary.java b/src/main/java/org/distorted/library/main/DistortedLibrary.java
index 3bbdd4b..0095544 100644
--- a/src/main/java/org/distorted/library/main/DistortedLibrary.java
+++ b/src/main/java/org/distorted/library/main/DistortedLibrary.java
@@ -36,6 +36,7 @@ import org.distorted.library.effect.FragmentEffect;
 import org.distorted.library.effect.PostprocessEffect;
 import org.distorted.library.effect.VertexEffect;
 import org.distorted.library.effectqueue.EffectQueueVertex;
+import org.distorted.library.mesh.AssociationUniformBlock;
 import org.distorted.library.mesh.DeferredJobs;
 import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.message.EffectMessageSender;
@@ -308,7 +309,7 @@ public class DistortedLibrary
     int[] params = new int[1];
     int index = GLES30.glGetUniformBlockIndex(mMainProgramH, "meshAssociation");
     GLES30.glGetActiveUniformBlockiv( mMainProgramH, index, GLES30.GL_UNIFORM_BLOCK_DATA_SIZE, params, 0);
-    MeshBase.setAssociationSize(params[0]);
+    AssociationUniformBlock.setAssociationSize(params[0]);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/mesh/AssociationUniformBlock.java b/src/main/java/org/distorted/library/mesh/AssociationUniformBlock.java
new file mode 100644
index 0000000..d9e017f
--- /dev/null
+++ b/src/main/java/org/distorted/library/mesh/AssociationUniformBlock.java
@@ -0,0 +1,191 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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;
+
+import android.opengl.GLES30;
+
+import org.distorted.library.main.InternalBuffer;
+
+import static org.distorted.library.mesh.MeshBase.MAX_EFFECT_COMPONENTS;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class AssociationUniformBlock
+  {
+  private static int mAssociationSize = 8*MAX_EFFECT_COMPONENTS;
+  private static final int DEFAULT_ASSOCIATION = 0xffffffff;
+
+  private InternalBuffer mUBO;
+
+  private int[] mAssociations;
+  private int mAssociationBlock;
+  private boolean mNeedAdjustAssociation;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Public only because DistortedLibrary needs to see this to call setAssociationSize
+ *
+ * @y.exclude
+ */
+  AssociationUniformBlock()
+    {
+    mNeedAdjustAssociation = true;
+    mAssociationBlock = computeAssociationBlockSize();
+    mAssociations= new int[mAssociationSize/4];
+
+    for(int i=0; i<MAX_EFFECT_COMPONENTS; i++)
+      {
+      mAssociations[getAndIndex(i)] = DEFAULT_ASSOCIATION;
+      mAssociations[getEquIndex(i)] = i;
+      }
+
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  AssociationUniformBlock( AssociationUniformBlock original)
+    {
+    mNeedAdjustAssociation = original.mNeedAdjustAssociation;
+    mAssociationBlock = original.mAssociationBlock;
+
+    int size = original.mAssociations.length;
+    mAssociations= new int[size];
+    System.arraycopy(original.mAssociations, 0, mAssociations, 0, size);
+
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * @y.exclude
+ */
+   public static void setAssociationSize(int size)
+     {
+     mAssociationSize = size;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   private int getAndIndex(int component)
+     {
+     return mAssociationBlock*component;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   private int getEquIndex(int component)
+     {
+     return mAssociationBlock*(MAX_EFFECT_COMPONENTS+component);
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   private int computeAssociationBlockSize()
+     {
+     return 1 + (mAssociationSize/4 - 2*MAX_EFFECT_COMPONENTS) / (2*MAX_EFFECT_COMPONENTS-1);
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   private int getAndAssoc(int comp)
+     {
+     return mAssociations[getAndIndex(comp)];
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   private int getEquAssoc(int comp)
+     {
+     return mAssociations[getEquIndex(comp)];
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// The problem here is that at MeshBase object creation time, we might not know the size of the
+// 'meshAssociation' Uniform Block Object in the vertex shader (the UBO is packed according to the
+// 'shared' layout, which means the size of the block is known only after linking the shaders).
+//
+// We thus, in the constructor, guess that the layout will be tight (so we will need 8*MAX_EFFECT_C
+// bytes there), allocate mAssociation already, and if later on, as a result of linking the shaders,
+// we get a call to setAssociationSize() which changes the size to something else, we need to reallocate.
+//
+// It can happen that even before the linking the value of mAssociationSize is already 'right' because
+// this is a static variable and it might persist from an earlier run. All the better then; this should
+// never be wrong.
+
+   private void adjustAssociation()
+     {
+     int oldLen = mAssociations.length;
+
+     if( mAssociationSize != 4*oldLen )
+       {
+       int[] tmp = new int[oldLen];
+       System.arraycopy(mAssociations, 0, tmp, 0, oldLen);
+
+       int newLen = mAssociationSize/4;
+       mAssociations = new int[newLen];
+       mAssociationBlock = computeAssociationBlockSize();
+
+       for(int i=0; i<oldLen/2; i++)
+         {
+         mAssociations[getAndIndex(i)] = tmp[i];                         // oldLen must be equal to
+         mAssociations[getEquIndex(i)] = tmp[MAX_EFFECT_COMPONENTS+i];   // 8*MAX_EFFECT_COM
+         }
+       }
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   boolean matchesAssociation( int component, int andAssoc, int equAssoc)
+     {
+     return (andAssoc & mAssociations[getAndIndex(component)]) != 0 || (equAssoc == mAssociations[getEquIndex(component)]);
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   void setEffectAssociationNow(int component, int andAssociation, int equAssociation)
+     {
+     mAssociations[getAndIndex(component)] = andAssociation;
+     mAssociations[getEquIndex(component)] = equAssociation;
+
+     mUBO.invalidate();
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   int getIndex()
+     {
+     if( mNeedAdjustAssociation )
+       {
+       mNeedAdjustAssociation = false;
+       adjustAssociation();
+       }
+
+     return mUBO.createImmediatelyInt( mAssociationSize, mAssociations);
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   void copy( int compTo, AssociationUniformBlock assocFrom, int compFrom)
+     {
+     mAssociations[getAndIndex(compTo)] = assocFrom.getAndAssoc(compFrom);
+     mAssociations[getEquIndex(compTo)] = assocFrom.getEquAssoc(compFrom);
+     }
+  }
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index 1886b7d..67577ee 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -47,8 +47,7 @@ import java.util.ArrayList;
 public abstract class MeshBase
    {
    private static final int UBO_BINDING = 1;
-   private static final int MAX_EFFECT_COMPONENTS= 100;
-   private static final int DEFAULT_ASSOCIATION = 0xffffffff;
+           static final int MAX_EFFECT_COMPONENTS= 100;
 
    // sizes of attributes of an individual vertex.
    private static final int POS_DATA_SIZE= 3; // vertex coordinates: x,y,z
@@ -79,18 +78,13 @@ public abstract class MeshBase
    private static final int VERT1_SIZE = VERT1_ATTRIBS*BYTES_PER_FLOAT;
    private static final int VERT2_SIZE = VERT2_ATTRIBS*BYTES_PER_FLOAT;
 
-   private static int mAssociationSize = 8*MAX_EFFECT_COMPONENTS;
-
    private boolean mShowNormals;              // when rendering this mesh, draw normal vectors?
    private InternalBuffer mVBO1, mVBO2, mTFO; // main vertex buffer and transform feedback buffer
-   private InternalBuffer mUBO;               // Uniform Buffer Object
    private int mNumVertices;
    private float[] mVertAttribs1;             // packed: PosX,PosY,PosZ, NorX,NorY,NorZ, InfX,InfY,InfZ
    private float[] mVertAttribs2;             // packed: TexS,TexT, Component
    private float mInflate;
-   private int[] mAssociations;
-   private int mAssociationBlock;
-   private boolean mNeedAdjustAssociation;
+   private AssociationUniformBlock mAUB;
 
    DeferredJobs.JobNode[] mJobNode;
 
@@ -135,22 +129,13 @@ public abstract class MeshBase
      mTexComponent = new ArrayList<>();
      mEffComponent = new ArrayList<>();
 
-     mNeedAdjustAssociation = true;
-     mAssociationBlock = computeAssociationBlockSize();
-     mAssociations= new int[mAssociationSize/4];
-
      mJobNode = new DeferredJobs.JobNode[1];
 
-     for(int i=0; i<MAX_EFFECT_COMPONENTS; i++)
-       {
-       mAssociations[getAndIndex(i)] = DEFAULT_ASSOCIATION;
-       mAssociations[getEquIndex(i)] = i;
-       }
+     mAUB = new AssociationUniformBlock();
 
      mVBO1= new InternalBuffer(GLES30.GL_ARRAY_BUFFER             , GLES30.GL_STATIC_READ);
      mVBO2= new InternalBuffer(GLES30.GL_ARRAY_BUFFER             , GLES30.GL_STATIC_READ);
      mTFO = new InternalBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, GLES30.GL_STATIC_READ);
-     mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER           , GLES30.GL_STATIC_READ);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -162,12 +147,7 @@ public abstract class MeshBase
      mInflate    = original.mInflate;
      mNumVertices= original.mNumVertices;
 
-     mNeedAdjustAssociation = original.mNeedAdjustAssociation;
-     mAssociationBlock = original.mAssociationBlock;
-
-     int size = original.mAssociations.length;
-     mAssociations= new int[size];
-     System.arraycopy(original.mAssociations, 0, mAssociations, 0, size);
+     mAUB = new AssociationUniformBlock(original.mAUB);
 
      if( deep )
        {
@@ -184,71 +164,6 @@ public abstract class MeshBase
        }
 
      mTFO = new InternalBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, GLES30.GL_STATIC_READ);
-     mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER           , GLES30.GL_STATIC_READ);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @y.exclude
- */
-   public static void setAssociationSize(int size)
-     {
-     mAssociationSize = size;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private int getAndIndex(int component)
-     {
-     return mAssociationBlock*component;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private int getEquIndex(int component)
-     {
-     return mAssociationBlock*(MAX_EFFECT_COMPONENTS+component);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private int computeAssociationBlockSize()
-     {
-     return 1 + (mAssociationSize/4 - 2*MAX_EFFECT_COMPONENTS) / (2*MAX_EFFECT_COMPONENTS-1);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The problem here is that at MeshBase object creation time, we might not know the size of the
-// 'meshAssociation' Uniform Block Object in the vertex shader (the UBO is packed according to the
-// 'shared' layout, which means the size of the block is known only after linking the shaders).
-//
-// We thus, in the constructor, guess that the layout will be tight (so we will need 8*MAX_EFFECT_C
-// bytes there), allocate mAssociation already, and if later on, as a result of linking the shaders,
-// we get a call to setAssociationSize() which changes the size to something else, we need to reallocate.
-//
-// It can happen that even before the linking the value of mAssociationSize is already 'right' because
-// this is a static variable and it might persist from an earlier run. All the better then; this should
-// never be wrong.
-
-   private void adjustAssociation()
-     {
-     int oldLen = mAssociations.length;
-
-     if( mAssociationSize != 4*oldLen )
-       {
-       int[] tmp = new int[oldLen];
-       System.arraycopy(mAssociations, 0, tmp, 0, oldLen);
-
-       int newLen = mAssociationSize/4;
-       mAssociations = new int[newLen];
-       mAssociationBlock = computeAssociationBlockSize();
-
-       for(int i=0; i<oldLen/2; i++)
-         {
-         mAssociations[getAndIndex(i)] = tmp[i];                         // oldLen must be equal to
-         mAssociations[getEquIndex(i)] = tmp[MAX_EFFECT_COMPONENTS+i];   // 8*MAX_EFFECT_COM
-         }
-       }
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -334,12 +249,12 @@ public abstract class MeshBase
      effect.compute(uniforms,0,0,0);
      effect.apply(matrixP, matrixV, uniforms, 0);
 
-     for(int i=0; i<numComp; i++)
+     for(int comp=0; comp<numComp; comp++)
        {
        start = end+1;
-       end   = mEffComponent.get(i);
+       end   = mEffComponent.get(comp);
 
-       if( (andAssoc & mAssociations[getAndIndex(i)]) != 0 || (equAssoc == mAssociations[getEquIndex(i)]) )
+       if( mAUB.matchesAssociation(comp, andAssoc, equAssoc) )
          {
          applyMatrixToComponent(matrixP, matrixV, start, end);
          }
@@ -412,10 +327,7 @@ public abstract class MeshBase
 
    void setEffectAssociationNow(int component, int andAssociation, int equAssociation)
      {
-     mAssociations[getAndIndex(component)] = andAssociation;
-     mAssociations[getEquIndex(component)] = equAssociation;
-
-     mUBO.invalidate();
+     mAUB.setEffectAssociationNow(component, andAssociation, equAssociation);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -481,8 +393,7 @@ public abstract class MeshBase
 
          if( origEffComponents<MAX_EFFECT_COMPONENTS )
            {
-           mAssociations[getAndIndex(origEffComponents)] = mesh.mAssociations[mesh.getAndIndex(j)];
-           mAssociations[getEquIndex(origEffComponents)] = mesh.mAssociations[mesh.getEquIndex(j)];
+           mAUB.copy(origEffComponents, mesh.mAUB, j);
            origEffComponents++;
            }
          }
@@ -779,14 +690,7 @@ public abstract class MeshBase
  */
    public void send(int programH)
      {
-     if( mNeedAdjustAssociation )
-       {
-       mNeedAdjustAssociation = false;
-       adjustAssociation();
-       }
-
-     int index = mUBO.createImmediatelyInt( mAssociationSize, mAssociations);
-
+     int index = mAUB.getIndex();
      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, UBO_BINDING, index);
      GLES30.glUniformBlockBinding(programH, UBO_BINDING, index);
      }
