commit 7a9edb927ebe133a9ef751a7428dfcd2bc1718bc
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Aug 26 01:24:57 2020 +0100

    Add component centers to dmesh version 2.

diff --git a/src/main/java/org/distorted/library/mesh/DeferredJobs.java b/src/main/java/org/distorted/library/mesh/DeferredJobs.java
index faf9a2b..205bcc2 100644
--- a/src/main/java/org/distorted/library/mesh/DeferredJobs.java
+++ b/src/main/java/org/distorted/library/mesh/DeferredJobs.java
@@ -45,6 +45,7 @@ public class DeferredJobs
   private static final int JOB_TYPE_COPY     = 5;
   private static final int JOB_TYPE_TEXTURE  = 6;
   private static final int JOB_TYPE_ASSOC    = 7;
+  private static final int JOB_TYPE_CENTER   = 8;
 
   private static ArrayList<JobNode> mJobs = new ArrayList<>();
 
@@ -59,8 +60,9 @@ public class DeferredJobs
     private MatrixEffect mMatrixEffect;
     private Static4D[] mMaps;
     private int mComp, mAndAssoc, mEquAssoc;
+    private float mX,mY,mZ;
 
-    Job(int type, MeshBase target, MeshBase[] source, VertexEffect vEff, MatrixEffect mEff, Static4D[] maps, int comp, int and, int equ)
+    Job(int type, MeshBase target, MeshBase[] source, VertexEffect vEff, MatrixEffect mEff, Static4D[] maps, int comp, int and, int equ, float x, float y, float z)
       {
       mType     = type;
       mTarget   = target;
@@ -69,6 +71,9 @@ public class DeferredJobs
       mComp     = comp;
       mAndAssoc = and;
       mEquAssoc = equ;
+      mX        = x;
+      mY        = y;
+      mZ        = z;
 
       if( vEff!=null )
         {
@@ -104,6 +109,8 @@ public class DeferredJobs
                                  break;
         case JOB_TYPE_ASSOC    : mTarget.setEffectAssociationNow(mComp,mAndAssoc,mEquAssoc);
                                  break;
+        case JOB_TYPE_CENTER   : mTarget.setComponentCenterNow(mComp,mX,mY,mZ);
+                                 break;
         }
 /*
       Log.e("jobs", "executing "+print()+" job");
@@ -128,6 +135,7 @@ public class DeferredJobs
         case JOB_TYPE_COPY     : return "COPY";
         case JOB_TYPE_TEXTURE  : return "TEXTURE";
         case JOB_TYPE_ASSOC    : return "ASSOC";
+        case JOB_TYPE_CENTER   : return "CENTER";
         }
 
       return null;
@@ -231,7 +239,7 @@ public class DeferredJobs
 
     if( jn==null )
       {
-      Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,null,null,0,0,0);
+      Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,null,null,0,0,0,0,0,0);
       JobNode node = new JobNode(job);
       mJobs.add(node);
       return node;
@@ -245,7 +253,7 @@ public class DeferredJobs
         }
       else
         {
-        Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,null,null,0,0,0);
+        Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,null,null,0,0,0,0,0,0);
         JobNode node = new JobNode(job);
         node.mPrevJobs.add(jn);
         jn.mNextJobs.add(node);
@@ -260,7 +268,7 @@ public class DeferredJobs
   static JobNode matrix(MeshBase target, MatrixEffect effect, int andAssoc, int ecuAssoc)
     {
     JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_MATRIX,target,null,null,effect,null,0,andAssoc,ecuAssoc);
+    Job job = new Job(JOB_TYPE_MATRIX,target,null,null,effect,null,0,andAssoc,ecuAssoc,0,0,0);
     JobNode node = new JobNode(job);
     node.mPrevJobs.add(jn);
     jn.mNextJobs.add(node);
@@ -273,7 +281,7 @@ public class DeferredJobs
   static JobNode mergeTex(MeshBase target)
     {
     JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_MERGE_TEX,target,null,null,null,null,0,0,0);
+    Job job = new Job(JOB_TYPE_MERGE_TEX,target,null,null,null,null,0,0,0,0,0,0);
     JobNode node = new JobNode(job);
     node.mPrevJobs.add(jn);
     jn.mNextJobs.add(node);
@@ -286,7 +294,7 @@ public class DeferredJobs
   static JobNode mergeEff(MeshBase target)
     {
     JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_MERGE_EFF,target,null,null,null,null,0,0,0);
+    Job job = new Job(JOB_TYPE_MERGE_EFF,target,null,null,null,null,0,0,0,0,0,0);
     JobNode node = new JobNode(job);
     node.mPrevJobs.add(jn);
     jn.mNextJobs.add(node);
@@ -300,7 +308,7 @@ public class DeferredJobs
     {
     JobNode jn;
 
-    Job job = new Job(JOB_TYPE_JOIN,target,meshes,null,null,null,0,0,0);
+    Job job = new Job(JOB_TYPE_JOIN,target,meshes,null,null,null,0,0,0,0,0,0);
     JobNode node = new JobNode(job);
 
     for (MeshBase mesh : meshes)
@@ -325,7 +333,7 @@ public class DeferredJobs
     JobNode jn = mesh.mJobNode[0];
     MeshBase[] meshes = new MeshBase[1];
     meshes[0] = mesh;
-    Job job = new Job(JOB_TYPE_COPY,target,meshes,null,null,null,0,0,0);
+    Job job = new Job(JOB_TYPE_COPY,target,meshes,null,null,null,0,0,0,0,0,0);
     JobNode node = new JobNode(job);
     node.mPrevJobs.add(jn);
     jn.mNextJobs.add(node);
@@ -338,7 +346,7 @@ public class DeferredJobs
   static JobNode textureMap(MeshBase target, Static4D[] maps, int comp)
     {
     JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_TEXTURE,target,null,null,null,maps,comp,0,0);
+    Job job = new Job(JOB_TYPE_TEXTURE,target,null,null,null,maps,comp,0,0,0,0,0);
     JobNode node = new JobNode(job);
     node.mPrevJobs.add(jn);
     jn.mNextJobs.add(node);
@@ -351,7 +359,20 @@ public class DeferredJobs
   static JobNode effectAssoc(MeshBase target, int comp, int andAssoc, int equAssoc)
     {
     JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_ASSOC,target,null,null,null,null,comp,andAssoc,equAssoc);
+    Job job = new Job(JOB_TYPE_ASSOC,target,null,null,null,null,comp,andAssoc,equAssoc,0,0,0);
+    JobNode node = new JobNode(job);
+    node.mPrevJobs.add(jn);
+    jn.mNextJobs.add(node);
+    mJobs.add(node);
+    return node;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static JobNode componentCenter(MeshBase target, int comp, float x, float y, float z)
+    {
+    JobNode jn = target.mJobNode[0];
+    Job job = new Job(JOB_TYPE_CENTER,target,null,null,null,null,comp,0,0,x,y,z);
     JobNode node = new JobNode(job);
     node.mPrevJobs.add(jn);
     jn.mNextJobs.add(node);
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index 500b8ee..7572abc 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -312,7 +312,7 @@ public abstract class MeshBase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   void setEffectCenterNow(int component, float x, float y, float z)
+   void setComponentCenterNow(int component, float x, float y, float z)
      {
      mUBC.setEffectCenterNow(component, x, y, z);
      }
@@ -702,11 +702,11 @@ public abstract class MeshBase
        try
          {
          String name = "/sdcard/"+mNumVertices+".dmesh";
-         DataOutputStream dos = new DataOutputStream(new FileOutputStream(name));
+         DataOutputStream dos = new DataOutputStream(new java.io.FileOutputStream(name));
          write(dos);
          android.util.Log.e("mesh", "file written: "+name);
          }
-       catch(FileNotFoundException ex)
+       catch(java.io.FileNotFoundException ex)
          {
          android.util.Log.e("mesh", "file not found exception: "+ex.toString());
          }
@@ -770,6 +770,10 @@ public abstract class MeshBase
      byte[] buffer = new byte[BYTES_PER_FLOAT*4];
      int version, numEff, numTex;
 
+     //////////////////////////////////////////////////////////////////////////////////////////
+     // read version, number of vertices, number of tex components, number of effect components
+     //////////////////////////////////////////////////////////////////////////////////////////
+
      try
        {
        stream.read(buffer);
@@ -795,6 +799,10 @@ public abstract class MeshBase
        return 0;
        }
 
+     //////////////////////////////////////////////////////////////////////////////////////////
+     // read contents of all texture and effect components
+     //////////////////////////////////////////////////////////////////////////////////////////
+
      if( mNumVertices>0 && numEff>0 && numTex>0 )
        {
        int size = numEff+TEX_COMP_SIZE*numTex;
@@ -803,9 +811,11 @@ public abstract class MeshBase
        mVertAttribs2 = new float[VERT2_ATTRIBS*mNumVertices];
 
        // version 1 had extra 3 floats (the 'inflate' vector) in its vert1 array
-       int vert1InFile = version==1 ? VERT1_ATTRIBS+3 : VERT1_ATTRIBS;
+       int vert1InFile = version==2 ? VERT1_ATTRIBS : VERT1_ATTRIBS+3;
+       // ... and there was no center.
+       int centerSize  = version==2 ? 3*numEff : 0;
 
-       buffer = new byte[BYTES_PER_FLOAT*(size + mNumVertices*(vert1InFile+VERT2_ATTRIBS))];
+       buffer = new byte[BYTES_PER_FLOAT*(size + centerSize + mNumVertices*(vert1InFile+VERT2_ATTRIBS))];
 
        try
          {
@@ -822,30 +832,6 @@ public abstract class MeshBase
 
        floatBuf.get(tmp,0,size);
 
-       switch(version)
-         {
-         case 1 : int index = floatBuf.position();
-
-                  for(int vert=0; vert<mNumVertices; vert++)
-                    {
-                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB  ] = floatBuf.get(index+POS_ATTRIB  );
-                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+1] = floatBuf.get(index+POS_ATTRIB+1);
-                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+2] = floatBuf.get(index+POS_ATTRIB+2);
-                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB  ] = floatBuf.get(index+NOR_ATTRIB  );
-                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB+1] = floatBuf.get(index+NOR_ATTRIB+1);
-                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB+2] = floatBuf.get(index+NOR_ATTRIB+2);
-                    index+=vert1InFile;
-                    }
-                  floatBuf.position(index);
-                  break;
-         case 2 : floatBuf.get(mVertAttribs1, 0, VERT1_ATTRIBS*mNumVertices);
-                  break;
-         default: android.util.Log.e("mesh", "Error: unknown mesh file version "+String.format("0x%08X", version));
-                  return 0;
-         }
-
-       floatBuf.get(mVertAttribs2, 0, VERT2_ATTRIBS*mNumVertices);
-
        TexComponent tex;
        int index, texComp;
        float x, y, z, w;
@@ -870,6 +856,42 @@ public abstract class MeshBase
          index = (int)tmp[TEX_COMP_SIZE*texComp + effComp ];
          mEffComponent.add(index);
          }
+
+       //////////////////////////////////////////////////////////////////////////////////////////
+       // read vert1 array, vert2 array and (if version>1) the component centers.
+       //////////////////////////////////////////////////////////////////////////////////////////
+
+       switch(version)
+         {
+         case 1 : index = floatBuf.position();
+
+                  for(int vert=0; vert<mNumVertices; vert++)
+                    {
+                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB  ] = floatBuf.get(index+POS_ATTRIB  );
+                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+1] = floatBuf.get(index+POS_ATTRIB+1);
+                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+2] = floatBuf.get(index+POS_ATTRIB+2);
+                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB  ] = floatBuf.get(index+NOR_ATTRIB  );
+                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB+1] = floatBuf.get(index+NOR_ATTRIB+1);
+                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB+2] = floatBuf.get(index+NOR_ATTRIB+2);
+                    index+=vert1InFile;
+                    }
+                  floatBuf.position(index);
+                  break;
+         case 2 : float[] centers = new float[3*numEff];
+                  floatBuf.get(centers,0,3*numEff);
+
+                  for(int eff=0; eff<numEff; eff++)
+                    {
+                    mUBC.setEffectCenterNow(eff, centers[3*eff], centers[3*eff+1], centers[3*eff+2]);
+                    }
+
+                  floatBuf.get(mVertAttribs1, 0, VERT1_ATTRIBS*mNumVertices);
+                  break;
+         default: android.util.Log.e("mesh", "Error: unknown mesh file version "+String.format("0x%08X", version));
+                  return 0;
+         }
+
+       floatBuf.get(mVertAttribs2, 0, VERT2_ATTRIBS*mNumVertices);
        }
 
      return BYTES_PER_FLOAT*( 4 + numEff+TEX_COMP_SIZE*numTex + VERT1_ATTRIBS*mNumVertices + VERT2_ATTRIBS*mNumVertices );
@@ -939,6 +961,15 @@ public abstract class MeshBase
          stream.writeFloat((float)mEffComponent.get(i));
          }
 
+       float[] centers = mUBC.getBackingArray();
+
+       for(int i=0; i<numEff; i++)
+         {
+         stream.writeFloat(centers[4*i  ]);
+         stream.writeFloat(centers[4*i+1]);
+         stream.writeFloat(centers[4*i+2]);
+         }
+
        ByteBuffer vertBuf1 = ByteBuffer.allocate(VERT1_SIZE*mNumVertices);
        vertBuf1.asFloatBuffer().put(mVertAttribs1);
        ByteBuffer vertBuf2 = ByteBuffer.allocate(VERT2_SIZE*mNumVertices);
@@ -1139,6 +1170,34 @@ public abstract class MeshBase
        }
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Set center of a Component.
+ *
+ * A 'center' of a (effect) component is a 3D point in space. The array of centers gets sent to
+ * the vertex shader as a Uniform Buffer Object; in the vertex shader we can then use it to compute
+ * the 'Inflation' of the whole Mesh per-component. 'Inflation' is needed by postprocess effects to
+ * add the 'halo' around an object.
+ *
+ * This is all 'per-component' so that a user has a chance to make the halo look right in case of
+ * non-convex meshes: then we need to ensure that each component of such a mesh is a convex
+ * sub-mesh with its center being more-or-less the center of gravity of the sub-mesh.
+ */
+   public void setComponentCenter(int component, float centerX, float centerY, float centerZ)
+     {
+     if( component>=0 && component<MAX_EFFECT_COMPONENTS )
+       {
+       if( mJobNode[0]==null )
+         {
+         setComponentCenterNow(component, centerX, centerY, centerZ);
+         }
+       else
+         {
+         mJobNode[0] = DeferredJobs.componentCenter(this,component,centerX, centerY, centerZ);
+         }
+       }
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Return the number of texture components, i.e. individual subsets of the whole set of vertices
diff --git a/src/main/java/org/distorted/library/mesh/UniformBlockCenter.java b/src/main/java/org/distorted/library/mesh/UniformBlockCenter.java
index 69306fd..b642b9b 100644
--- a/src/main/java/org/distorted/library/mesh/UniformBlockCenter.java
+++ b/src/main/java/org/distorted/library/mesh/UniformBlockCenter.java
@@ -55,28 +55,35 @@ class UniformBlockCenter
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   void setEffectCenterNow(int comp, float x, float y, float z)
-     {
-     mCenter[4*comp  ] = x;
-     mCenter[4*comp+1] = y;
-     mCenter[4*comp+2] = z;
+  void setEffectCenterNow(int comp, float x, float y, float z)
+    {
+    mCenter[4*comp  ] = x;
+    mCenter[4*comp+1] = y;
+    mCenter[4*comp+2] = z;
 
-     mUBO.invalidate();
-     }
+    mUBO.invalidate();
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   int getIndex()
-     {
-     return mUBO.createImmediatelyFloat( BLOCK_SIZE, mCenter);
-     }
+  int getIndex()
+    {
+    return mUBO.createImmediatelyFloat( BLOCK_SIZE, mCenter);
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   void copy(int compTo, UniformBlockCenter blockFrom, int compFrom)
-     {
-     mCenter[4*compTo  ] = blockFrom.mCenter[4*compFrom  ];
-     mCenter[4*compTo+1] = blockFrom.mCenter[4*compFrom+1];
-     mCenter[4*compTo+2] = blockFrom.mCenter[4*compFrom+2];
-     }
+  void copy(int compTo, UniformBlockCenter blockFrom, int compFrom)
+    {
+    mCenter[4*compTo  ] = blockFrom.mCenter[4*compFrom  ];
+    mCenter[4*compTo+1] = blockFrom.mCenter[4*compFrom+1];
+    mCenter[4*compTo+2] = blockFrom.mCenter[4*compFrom+2];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getBackingArray()
+    {
+    return mCenter;
+    }
   }
