commit a8dfedcc3535194101b599e67fc9fc57ebe6c9c5
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sun May 31 18:39:48 2020 +0100

    Progress with DeferredJobs (apply, copy, merge & join should be working now - untested)

diff --git a/src/main/java/org/distorted/library/mesh/DeferredJobs.java b/src/main/java/org/distorted/library/mesh/DeferredJobs.java
index bbfbdbe..9b57d01 100644
--- a/src/main/java/org/distorted/library/mesh/DeferredJobs.java
+++ b/src/main/java/org/distorted/library/mesh/DeferredJobs.java
@@ -45,27 +45,21 @@ public class DeferredJobs
   private static class Job
     {
     private int mType;
+    private int mArg;
     private MeshBase mTarget;
     private MeshBase[] mSource;
     private EffectQueueVertex mEffects;
 
-    Job(int type, MeshBase target, MeshBase[] source, VertexEffect effect)
+    Job(int type, MeshBase target, MeshBase[] source, VertexEffect effect, int arg)
       {
       mType   = type;
       mTarget = target;
       mSource = source;
+      mArg    = arg;
       mEffects= new EffectQueueVertex();
       mEffects.add(effect);
       }
 
-    Job(Job job)
-      {
-      mType   = job.mType;
-      mTarget = job.mTarget;
-      mSource = job.mSource;
-      mEffects= new EffectQueueVertex(job.mEffects);
-      }
-
     void addEffect(VertexEffect effect)
       {
       mEffects.add(effect);
@@ -77,8 +71,10 @@ public class DeferredJobs
         {
         case JOB_TYPE_VERTEX: DistortedLibrary.adjustVertices(mTarget, mEffects);
                               break;
-        case JOB_TYPE_MERGE : break;
-        case JOB_TYPE_JOIN  : break;
+        case JOB_TYPE_MERGE : mTarget.merge();
+                              break;
+        case JOB_TYPE_JOIN  : mTarget.joinAttrib1(mSource,mArg);
+                              break;
         case JOB_TYPE_COPY  : mTarget.deepCopyAttribs1(mSource[0]);
                               break;
         }
@@ -105,27 +101,6 @@ public class DeferredJobs
       mJob      = job;
       }
 
-    JobNode(JobNode node)
-      {
-      mPrevJobs = new ArrayList<>();
-      mNextJobs = new ArrayList<>();
-      mJob      = new Job(node.mJob);
-
-      int numPrev = node.mPrevJobs.size();
-
-      for(int i=0; i<numPrev; i++)
-        {
-        mPrevJobs.add(node.mPrevJobs.get(i));
-        }
-
-      int numNext = node.mNextJobs.size();
-
-      for(int i=0; i<numNext; i++)
-        {
-        mNextJobs.add(node.mNextJobs.get(i));
-        }
-      }
-
     void execute()
       {
       JobNode node;
@@ -187,7 +162,7 @@ public class DeferredJobs
 
     if( jn==null )
       {
-      Job job = new Job(JOB_TYPE_VERTEX,target,null,effect);
+      Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,0);
       JobNode node = new JobNode(job);
       mJobs.add(node);
       return node;
@@ -201,7 +176,7 @@ public class DeferredJobs
         }
       else
         {
-        Job job = new Job(JOB_TYPE_VERTEX,target,null,effect);
+        Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,0);
         JobNode node = new JobNode(job);
         node.mPrevJobs.add(jn);
         jn.mNextJobs.add(node);
@@ -215,21 +190,92 @@ public class DeferredJobs
 
   static JobNode merge(MeshBase target)
     {
-    return null;
+    JobNode jn = target.mJobNode[0];
+
+    if( jn==null )
+      {
+      android.util.Log.e("deferredJobs", "Error: trying to add a merge job to an empty graph!");
+      return jn;
+      }
+    else
+      {
+      if( jn.mJob.mType==JOB_TYPE_MERGE )
+        {
+        android.util.Log.e("deferredJobs", "Error: trying to add a second consecutive merge job");
+        return jn;
+        }
+      else
+        {
+        Job job = new Job(JOB_TYPE_MERGE,target,null,null,0);
+        JobNode node = new JobNode(job);
+        node.mPrevJobs.add(jn);
+        jn.mNextJobs.add(node);
+        mJobs.add(node);
+        return node;
+        }
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  static JobNode join(MeshBase target, MeshBase[] meshes)
+  static JobNode join(MeshBase target, MeshBase[] meshes, int origVertices)
     {
-    return null;
+    JobNode jn = target.mJobNode[0];
+
+    if( jn==null )
+      {
+      android.util.Log.e("deferredJobs", "Error: trying to add a join job to an empty graph!");
+      return jn;
+      }
+    else
+      {
+      if( jn.mJob.mType==JOB_TYPE_JOIN )
+        {
+        android.util.Log.e("deferredJobs", "Error: trying to add a second consecutive join job");
+        return jn;
+        }
+      else
+        {
+        Job job = new Job(JOB_TYPE_JOIN,target,meshes,null,origVertices);
+        JobNode node = new JobNode(job);
+        node.mPrevJobs.add(jn);
+        jn.mNextJobs.add(node);
+        mJobs.add(node);
+        return node;
+        }
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   static JobNode copy(MeshBase target, MeshBase mesh)
     {
-    return null;
+    JobNode jn = target.mJobNode[0];
+
+    if( jn==null )
+      {
+      android.util.Log.e("deferredJobs", "Error: trying to add a copy job to an empty graph!");
+      return jn;
+      }
+    else
+      {
+      if( jn.mJob.mType==JOB_TYPE_COPY )
+        {
+        android.util.Log.e("deferredJobs", "Error: trying to add a second consecutive copy job");
+        return jn;
+        }
+      else
+        {
+        MeshBase[] meshes = new MeshBase[1];
+        meshes[0] = mesh;
+        Job job = new Job(JOB_TYPE_COPY,target,meshes,null,0);
+        JobNode node = new JobNode(job);
+        node.mPrevJobs.add(jn);
+        jn.mNextJobs.add(node);
+        mJobs.add(node);
+        return node;
+        }
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index 90d4e87..8d1244b 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -208,6 +208,26 @@ public abstract class MeshBase
      mVBO1.invalidate();
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   void merge()
+     {
+     int num = mComponent.size();
+
+     if( num>1 )
+       {
+       mComponent.clear();
+       mComponent.add(new Component(mNumVertices-1));
+
+       for(int index=0; index<mNumVertices; index++)
+         {
+         mVertAttribs2[VERT2_ATTRIBS*index+COM_ATTRIB] = 0;
+         }
+
+       mVBO2.invalidate();
+       }
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    int numComponents()
@@ -231,66 +251,76 @@ public abstract class MeshBase
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// called from MeshJoined
 
-   void join(MeshBase[] meshes)
+   void joinAttrib1(MeshBase[] meshes, int origVertices)
      {
      MeshBase mesh;
-     Component comp;
-     int origComponents=0, numComponents, numVertices, numMeshes = meshes.length;
-     int origVertices = mNumVertices;
+     int numMeshes = meshes.length;
+     int numVertices = origVertices;
 
-     // compute new numVertices; take care of Components
+     float[] newAttribs1 = new float[VERT1_ATTRIBS*mNumVertices];
 
      if( origVertices>0 )
        {
-       origComponents = mComponent.size();
-       mNumVertices+= ( mNumVertices%2==1 ? 2:1 );
-       mComponent.get(origComponents-1).mEndIndex = mNumVertices-1;
+       System.arraycopy(mVertAttribs1,                              0, newAttribs1,                         0, VERT1_ATTRIBS*numVertices);
+       System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS    );
+       origVertices++;
+
+       if( numVertices%2==1 )
+         {
+         System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
+         origVertices++;
+         }
        }
 
      for(int i=0; i<numMeshes; i++)
        {
        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);
+       if( origVertices>0 )
+         {
+         System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS    );
+         origVertices++;
+         }
+       System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS*numVertices);
+       origVertices+=numVertices;
 
-       for(int j=0; j<numComponents; j++)
+       if( i<numMeshes-1 )
          {
-         comp = new Component(mesh.mComponent.get(j));
-         comp.mEndIndex += (extraVerticesBefore+mNumVertices+extraVerticesAfter);
-         mComponent.add(comp);
+         System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
+         origVertices++;
 
-         if( origComponents<MAX_COMPONENTS )
+         if( numVertices%2==1 )
            {
-           mAssociation[origComponents] = mesh.mAssociation[j];
-           origComponents++;
+           System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
+           origVertices++;
            }
          }
-
-       mNumVertices += (extraVerticesBefore+numVertices+extraVerticesAfter);
        }
 
-     // allocate new attrib array
-     float[] newAttribs1 = new float[VERT1_ATTRIBS*mNumVertices];
+     mVertAttribs1 = newAttribs1;
+     mVBO1.invalidate();
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   private void joinAttrib2(MeshBase[] meshes, int origVertices)
+     {
+     MeshBase mesh;
+     int numMeshes = meshes.length;
+     int numVertices = origVertices;
+
      float[] newAttribs2 = new float[VERT2_ATTRIBS*mNumVertices];
-     numVertices = origVertices;
 
      if( origVertices>0 )
        {
-       System.arraycopy(mVertAttribs1,                              0, newAttribs1,                         0, VERT1_ATTRIBS*numVertices);
        System.arraycopy(mVertAttribs2,                              0, newAttribs2,                         0, VERT2_ATTRIBS*numVertices);
-       System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS    );
        System.arraycopy(mVertAttribs2, VERT2_ATTRIBS*(origVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS    );
        origVertices++;
 
        if( numVertices%2==1 )
          {
-         System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
          System.arraycopy(mVertAttribs2, VERT2_ATTRIBS*(origVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS);
          origVertices++;
          }
@@ -303,23 +333,19 @@ public abstract class MeshBase
 
        if( origVertices>0 )
          {
-         System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS    );
          System.arraycopy(mesh.mVertAttribs2, 0, newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS    );
          origVertices++;
          }
-       System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS*numVertices);
        System.arraycopy(mesh.mVertAttribs2, 0, newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS*numVertices);
        origVertices+=numVertices;
 
        if( i<numMeshes-1 )
          {
-         System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
          System.arraycopy(mesh.mVertAttribs2, VERT2_ATTRIBS*(numVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS);
          origVertices++;
 
          if( numVertices%2==1 )
            {
-           System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
            System.arraycopy(mesh.mVertAttribs2, VERT2_ATTRIBS*(numVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS);
            origVertices++;
            }
@@ -340,13 +366,65 @@ public abstract class MeshBase
        for( ; index<=endIndex; index++) newAttribs2[VERT2_ATTRIBS*index+COM_ATTRIB] = component;
        }
 
-     mVertAttribs1 = newAttribs1;
      mVertAttribs2 = newAttribs2;
-
-     mVBO1.invalidate();
      mVBO2.invalidate();
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// called from MeshJoined
+
+   void join(MeshBase[] meshes)
+     {
+     MeshBase mesh;
+     Component comp;
+     int origComponents=0, numComponents, numVertices, numMeshes = meshes.length;
+     int origVertices = mNumVertices;
+
+     if( origVertices>0 )
+       {
+       origComponents = mComponent.size();
+       mNumVertices+= ( mNumVertices%2==1 ? 2:1 );
+       mComponent.get(origComponents-1).mEndIndex = mNumVertices-1;
+       }
+
+     for(int i=0; i<numMeshes; i++)
+       {
+       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 += (extraVerticesBefore+mNumVertices+extraVerticesAfter);
+         mComponent.add(comp);
+
+         if( origComponents<MAX_COMPONENTS )
+           {
+           mAssociation[origComponents] = mesh.mAssociation[j];
+           origComponents++;
+           }
+         }
+
+       mNumVertices += (extraVerticesBefore+numVertices+extraVerticesAfter);
+       }
+
+     if( mJobNode[0]!=null )
+       {
+       mJobNode[0] = DeferredJobs.join(this,meshes,origVertices);
+       }
+     else
+       {
+       joinAttrib1(meshes,origVertices);
+       }
+
+     joinAttrib2(meshes,origVertices);
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Not part of public API, do not document (public only because has to be used from the main package)
@@ -494,6 +572,22 @@ public abstract class MeshBase
      return mShowNormals;
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Merge all components of this Mesh into a single one.
+ */
+   public void mergeComponents()
+     {
+     if( mJobNode==null )
+       {
+       merge();
+       }
+     else
+       {
+       mJobNode[0] = DeferredJobs.merge(this);
+       }
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Release all internal resources.
