commit 470820a715371d9b57969bef02a9c8ed9473a432
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sun Jun 7 00:28:09 2020 +0100

    Begin porting RubikCube to the new SingleMesh library. This will make rendering much faster - e.g. in case of Cube 5, instead of 98 renders of individual Cubits, there will be one render of the whole Mesh.

diff --git a/src/main/java/org/distorted/objects/Cubit.java b/src/main/java/org/distorted/objects/Cubit.java
index 1dccb2ed..e4d3b6e1 100644
--- a/src/main/java/org/distorted/objects/Cubit.java
+++ b/src/main/java/org/distorted/objects/Cubit.java
@@ -27,8 +27,6 @@ import org.distorted.library.effect.MatrixEffectMove;
 import org.distorted.library.effect.MatrixEffectQuaternion;
 import org.distorted.library.effect.MatrixEffectRotate;
 import org.distorted.library.main.DistortedEffects;
-import org.distorted.library.main.DistortedNode;
-import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.message.EffectListener;
 import org.distorted.library.type.Dynamic1D;
 import org.distorted.library.type.Static1D;
@@ -41,23 +39,52 @@ import org.distorted.main.RubikSurfaceView;
 class Cubit
   {
   private static final int POST_ROTATION_MILLISEC = 500;
-  private static final Static3D matrCenter = new Static3D(0,0,0);
+  private static final Static3D MATRIX_CENTER = new Static3D(0,0,0);
 
   private final Static3D mOrigPosition;
 
   private RubikObject mParent;
-  private MeshBase mMesh;
   private Static3D mRotationAxis;
   private MatrixEffectRotate mRotateEffect;
   private Static3D mCurrentPosition;
   private int mNumAxis;
   private Dynamic1D mRotationAngle;
 
-  DistortedNode mNode;
   DistortedEffects mEffect;
   Static4D mQuatScramble;
   float[] mRotationRow;
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Cubit(RubikObject parent, Static3D position)
+    {
+    float x = position.get0();
+    float y = position.get1();
+    float z = position.get2();
+
+    Static3D vector = new Static3D(x,y,z);
+
+    mParent          = parent;
+    mOrigPosition    = new Static3D(x,y,z);
+    mQuatScramble    = new Static4D(0,0,0,1);
+    mRotationAngle   = new Dynamic1D();
+    mRotationAxis    = new Static3D(1,0,0);
+    mCurrentPosition = position;
+    mRotateEffect    = new MatrixEffectRotate(mRotationAngle, mRotationAxis, MATRIX_CENTER);
+
+    mNumAxis     = mParent.ROTATION_AXIS.length;
+    mRotationRow = new float[mNumAxis];
+    computeRotationRow();
+
+    mEffect = new DistortedEffects();
+    mEffect.apply( new MatrixEffectMove(vector) );
+    mEffect.apply( new MatrixEffectQuaternion(mQuatScramble, MATRIX_CENTER));
+    mEffect.apply(mRotateEffect);
+    mEffect.apply(mParent.mQuatAEffect);
+    mEffect.apply(mParent.mQuatCEffect);
+    mEffect.apply(mParent.mScaleEffect);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Because of quatMultiplication, errors can accumulate - so to avoid this, we
 // correct the value of the 'scramble' quat to what it should be - one of the legal quats from the
@@ -163,40 +190,6 @@ class Cubit
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Cubit(RubikObject parent, MeshBase mesh, Static3D position)
-    {
-    float x = position.get0();
-    float y = position.get1();
-    float z = position.get2();
-
-    Static3D vector = new Static3D(x,y,z);
-
-    mParent          = parent;
-    mMesh            = mesh;
-    mOrigPosition    = new Static3D(x,y,z);
-    mQuatScramble    = new Static4D(0,0,0,1);
-    mRotationAngle   = new Dynamic1D();
-    mRotationAxis    = new Static3D(1,0,0);
-    mCurrentPosition = position;
-    mRotateEffect    = new MatrixEffectRotate(mRotationAngle, mRotationAxis, matrCenter);
-
-    mNumAxis     = mParent.ROTATION_AXIS.length;
-    mRotationRow = new float[mNumAxis];
-    computeRotationRow();
-
-    mEffect = new DistortedEffects();
-    mEffect.apply( new MatrixEffectMove(vector) );
-    mEffect.apply( new MatrixEffectQuaternion(mQuatScramble, matrCenter));
-    mEffect.apply(mRotateEffect);
-    mEffect.apply(mParent.mQuatAEffect);
-    mEffect.apply(mParent.mQuatCEffect);
-    mEffect.apply(mParent.mScaleEffect);
-
-    mNode = new DistortedNode(mParent.mTexture,mEffect,mMesh);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void savePreferences(SharedPreferences.Editor editor)
@@ -357,17 +350,6 @@ class Cubit
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
-// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
-// memory leak.
-
-  void releaseResources()
-    {
-    mMesh.markForDeletion();
-    mNode.markForDeletion();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void solve()
@@ -395,19 +377,4 @@ class Cubit
 
     return dx*dx + dy*dy + dz*dz;
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getColorIndex(int face)
-    {
-    Static4D texMap = mMesh.getTextureMap(face);
-    return (int)(texMap.get0() / texMap.get2());
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase getMesh()
-    {
-    return mMesh;
-    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/objects/RubikCube.java b/src/main/java/org/distorted/objects/RubikCube.java
index 36ed4c24..e6f1aeb9 100644
--- a/src/main/java/org/distorted/objects/RubikCube.java
+++ b/src/main/java/org/distorted/objects/RubikCube.java
@@ -232,9 +232,11 @@ class RubikCube extends RubikObject
       mMesh.apply(effect12);
       mMesh.apply(effect13);
       mMesh.apply(effect14);
+
+      mMesh.mergeEffComponents();
       }
 
-    return mMesh.copy(false);
+    return mMesh.copy(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/RubikObject.java b/src/main/java/org/distorted/objects/RubikObject.java
index 7c59e44d..b1600c8b 100644
--- a/src/main/java/org/distorted/objects/RubikObject.java
+++ b/src/main/java/org/distorted/objects/RubikObject.java
@@ -25,12 +25,14 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 
 import org.distorted.library.effect.Effect;
+import org.distorted.library.effect.MatrixEffectMove;
 import org.distorted.library.effect.MatrixEffectQuaternion;
 import org.distorted.library.effect.MatrixEffectScale;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedNode;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.mesh.MeshJoined;
 import org.distorted.library.mesh.MeshRectangles;
 import org.distorted.library.message.EffectListener;
 import org.distorted.library.type.Static1D;
@@ -47,6 +49,7 @@ public abstract class RubikObject extends DistortedNode
   private static final int TEXTURE_HEIGHT = 128;
   final float[] LEGAL_QUATS;
   final Static3D[] ROTATION_AXIS;
+  final int NUM_FACES;
 
   static float OBJECT_SCREEN_RATIO;
 
@@ -59,6 +62,8 @@ public abstract class RubikObject extends DistortedNode
   private Cubit[] mCubits;
   private int mSize;
   private RubikObjectList mList;
+  private MeshBase mMesh;
+  private DistortedEffects mEffects;
 
   float mStart, mStep;
 
@@ -85,6 +90,7 @@ public abstract class RubikObject extends DistortedNode
     NUM_CUBITS  = mOrigPos.length;
     ROTATION_AXIS = getRotationAxis();
     OBJECT_SCREEN_RATIO = getScreenRatio();
+    NUM_FACES = getNumFaces();
 
     mSize = size;
     computeStartAndStep(mOrigPos);
@@ -106,16 +112,26 @@ public abstract class RubikObject extends DistortedNode
 
     mCubits = new Cubit[NUM_CUBITS];
     mTexture = new DistortedTexture();
+    MeshBase[] cubitMesh = new MeshBase[NUM_CUBITS];
 
     for(int i=0; i<NUM_CUBITS; i++)
       {
-      MeshBase cubitMesh = createCubitMesh(i);
-      mCubits[i] = new Cubit(this,cubitMesh,mOrigPos[i]);
-      textureCubitMesh(cubitMesh,i);
-
-      attach(mCubits[i].mNode);
+      cubitMesh[i] = createCubitMesh(i);
+      cubitMesh[i].apply(new MatrixEffectMove(mOrigPos[i]),1,0);
+      mCubits[i] = new Cubit(this,mOrigPos[i]);
       }
 
+    mMesh = new MeshJoined(cubitMesh);
+
+    resetAllTextureMaps();
+
+    mEffects = new DistortedEffects();
+    mEffects.apply(mQuatAEffect);
+    mEffects.apply(mQuatCEffect);
+    mEffects.apply(mScaleEffect);
+
+    attach( new DistortedNode(mTexture,mEffects,mMesh) );
+
     setupPosition(moves);
 
     setProjection(fov, 0.1f);
@@ -126,28 +142,27 @@ public abstract class RubikObject extends DistortedNode
   private void textureCubitMesh(MeshBase mesh, int cubit)
     {
     boolean belongs;
-    final int numFaces = getNumFaces();
-    final Static4D[] maps = new Static4D[numFaces];
-    final float ratio = 1.0f/(numFaces+1);
+    final Static4D[] maps = new Static4D[NUM_FACES];
+    final float ratio = 1.0f/(NUM_FACES+1);
 
-    if( 2*ROTATION_AXIS.length == numFaces )  // i.e. there are faces on both ends of the axis (cube)
+    if( 2*ROTATION_AXIS.length == NUM_FACES )  // i.e. there are faces on both ends of the axis (cube)
       {
-      for(int i=0; i<numFaces; i++)
+      for(int i=0; i<NUM_FACES; i++)
         {
         belongs = isOnFace(cubit, i/2, i%2==0 ? mSize-1:0 );
-        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
+        maps[i] = new Static4D( (belongs?i:NUM_FACES)*ratio, 0.0f, ratio, 1.0f);
         }
       }
-    else if( ROTATION_AXIS.length == numFaces )  // just a single face on the right end of an axis (pyraminx)
+    else if( ROTATION_AXIS.length == NUM_FACES )  // just a single face on the right end of an axis (pyraminx)
       {
-      for(int i=0; i<numFaces; i++)
+      for(int i=0; i<NUM_FACES; i++)
         {
         belongs = isOnFace(cubit, i, 0 );
-        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
+        maps[i] = new Static4D( (belongs?i:NUM_FACES)*ratio, 0.0f, ratio, 1.0f);
         }
       }
 
-    mesh.setTextureMap(maps);
+    mesh.setTextureMap(maps,NUM_FACES*cubit);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -246,7 +261,8 @@ public abstract class RubikObject extends DistortedNode
 
   int getCubitFaceColorIndex(int cubit, int face)
     {
-    return mCubits[cubit].getColorIndex(face);
+    Static4D texMap = mMesh.getTextureMap(NUM_FACES*cubit + face);
+    return (int)(texMap.get0() / texMap.get2());
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -286,10 +302,8 @@ public abstract class RubikObject extends DistortedNode
     {
     Bitmap bitmap;
 
-    final int numColors = getNumFaces();
-
     Paint paint = new Paint();
-    bitmap = Bitmap.createBitmap( (numColors+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
+    bitmap = Bitmap.createBitmap( (NUM_FACES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
     Canvas canvas = new Canvas(bitmap);
 
     paint.setAntiAlias(true);
@@ -297,9 +311,9 @@ public abstract class RubikObject extends DistortedNode
     paint.setStyle(Paint.Style.FILL);
 
     paint.setColor(INTERIOR_COLOR);
-    canvas.drawRect(0, 0, (numColors+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
+    canvas.drawRect(0, 0, (NUM_FACES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
 
-    for(int i=0; i<numColors; i++)
+    for(int i=0; i<NUM_FACES; i++)
       {
       createFaceTexture(canvas, paint, i, i*TEXTURE_HEIGHT, 0, TEXTURE_HEIGHT);
       }
@@ -356,7 +370,7 @@ public abstract class RubikObject extends DistortedNode
     {
     mTexture.markForDeletion();
 
-    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].releaseResources();
+    // TODO ?
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -400,7 +414,7 @@ public abstract class RubikObject extends DistortedNode
     {
     for(int i=0; i<NUM_CUBITS; i++)
       {
-      textureCubitMesh( mCubits[i].getMesh() , i );
+      textureCubitMesh( mMesh , i );
       }
     }
 
@@ -408,14 +422,13 @@ public abstract class RubikObject extends DistortedNode
 
   public void setTextureMap(int cubit, int face, int newColor)
     {
-    final int numFaces = getNumFaces();
-    final float ratio = 1.0f/(numFaces+1);
-    final Static4D[] maps = new Static4D[numFaces];
+    final float ratio = 1.0f/(NUM_FACES+1);
+    final Static4D[] maps = new Static4D[NUM_FACES];
 
     try
       {
       maps[face] = new Static4D( newColor*ratio, 0.0f, ratio, 1.0f);
-      mCubits[cubit].getMesh().setTextureMap(maps);
+      mMesh.setTextureMap(maps,NUM_FACES*cubit);
       }
     catch(ArrayIndexOutOfBoundsException ignored)
       {
diff --git a/src/main/java/org/distorted/objects/RubikPyraminx.java b/src/main/java/org/distorted/objects/RubikPyraminx.java
index 1a32a4e1..e4d251a0 100644
--- a/src/main/java/org/distorted/objects/RubikPyraminx.java
+++ b/src/main/java/org/distorted/objects/RubikPyraminx.java
@@ -225,10 +225,7 @@ public class RubikPyraminx extends RubikObject
       meshes[i].setEffectAssociation(0,association,0);
       }
 
-    Static4D[] textureMaps = new Static4D[MESHES];
-    for(int i=0; i<MESHES; i++) textureMaps[i] = new Static4D(i*0.25f,0.0f,0.25f,1.0f);
     MeshBase result = new MeshJoined(meshes);
-    result.setTextureMap(textureMaps);
 
     Static3D a0 = new Static3D(         0,        1,       0 );
     Static3D a1 = new Static3D(         0,  -1.0f/3, 2*SQ2/3 );
@@ -301,6 +298,8 @@ public class RubikPyraminx extends RubikObject
       result.apply( ROTATION[mRotArray[cubit]] );
       }
 
+    result.mergeEffComponents();
+
     return result;
     }
 
@@ -313,12 +312,12 @@ public class RubikPyraminx extends RubikObject
     if( kind>=0 )
       {
       if( mMeshRotated[kind]==null ) mMeshRotated[kind] = createStaticMesh(cubit);
-      return mMeshRotated[kind].copy(false);
+      return mMeshRotated[kind].copy(true);
       }
     else
       {
       if( mMesh==null ) mMesh = createStaticMesh(cubit);
-      return mMesh.copy(false);
+      return mMesh.copy(true);
       }
     }
 
