commit 7cb8d4b068156abde9fee95b851e78dd0ddb134e
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Mar 29 17:20:17 2022 +0200

    Mode the 'iconMode' from static variable in ObjectControl to member varaible in TwistyObject (safer)

diff --git a/src/main/java/org/distorted/bandaged/BandagedCreatorActivity.java b/src/main/java/org/distorted/bandaged/BandagedCreatorActivity.java
index 3f6d5b30..a5d70ae0 100644
--- a/src/main/java/org/distorted/bandaged/BandagedCreatorActivity.java
+++ b/src/main/java/org/distorted/bandaged/BandagedCreatorActivity.java
@@ -19,6 +19,7 @@
 
 package org.distorted.bandaged;
 
+import android.graphics.Bitmap;
 import android.os.Build;
 import android.os.Bundle;
 import android.util.DisplayMetrics;
@@ -186,6 +187,8 @@ public class BandagedCreatorActivity extends AppCompatActivity
 
       if( mScreen==null ) mScreen = new BandagedCreatorScreen();
       mScreen.onAttachedToWindow(this);
+
+      BandagedCreatorWorkerThread.create(this);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -231,9 +234,9 @@ public class BandagedCreatorActivity extends AppCompatActivity
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public int getHeightBar()
+    public void iconCreationDone(Bitmap bmp)
       {
-      return mHeightBar;
+      mScreen.iconCreationDone(bmp);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/bandaged/BandagedCreatorObjectView.java b/src/main/java/org/distorted/bandaged/BandagedCreatorObjectView.java
index 35fe8fd4..c546fcd5 100644
--- a/src/main/java/org/distorted/bandaged/BandagedCreatorObjectView.java
+++ b/src/main/java/org/distorted/bandaged/BandagedCreatorObjectView.java
@@ -20,6 +20,7 @@
 package org.distorted.bandaged;
 
 import android.view.LayoutInflater;
+import android.view.View;
 import android.widget.LinearLayout;
 
 import org.distorted.helpers.TransparentButton;
@@ -34,6 +35,20 @@ public class BandagedCreatorObjectView
   private final LinearLayout mPane;
   private final String mName;
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void playObject()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void deleteObject()
+    {
+
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public BandagedCreatorObjectView(BandagedCreatorActivity act, String name, boolean leftmost)
@@ -64,6 +79,24 @@ public class BandagedCreatorObjectView
 
     bottom.addView(plaButton);
     bottom.addView(delButton);
+
+    plaButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        playObject();
+        }
+      });
+
+    delButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        deleteObject();
+        }
+      });
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java b/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java
index 6cd14d49..bb8e9319 100644
--- a/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java
+++ b/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java
@@ -23,17 +23,24 @@ import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
 
 import android.app.Activity;
+import android.opengl.GLES31;
 import android.opengl.GLSurfaceView;
 import android.widget.Toast;
 
 import org.distorted.dialogs.RubikDialogSaveBandaged;
+import org.distorted.library.effect.PostprocessEffectBorder;
+import org.distorted.library.main.DistortedEffects;
+import org.distorted.library.main.DistortedFramebuffer;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedNode;
 import org.distorted.library.main.DistortedScreen;
 
+import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.json.JsonWriter;
+import org.distorted.objectlib.main.ObjectControl;
+import org.distorted.objectlib.main.ShapeHexahedron;
 import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objectlib.objects.TwistyBandagedGeneric;
 import org.json.JSONException;
@@ -41,11 +48,14 @@ import org.json.JSONException;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, DistortedLibrary.ExceptionListener
 {
+   public static final float BRIGHTNESS = 0.333f;
    private static final int DURATION = 1000;
 
    static final int COLOR_DEFAULT = 0xffffff55;
@@ -95,13 +105,14 @@ public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, Distorte
    private float mScaleValue;
    private float mX, mY, mZ, mW;
    private boolean mResetQuats, mSetQuatT, mResettingObject;
+   private int mSaveIcon;
+   private DistortedFramebuffer mFramebuffer;
+   private String mPath;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    BandagedCreatorRenderer(BandagedCreatorView v)
      {
-     final float BRIGHTNESS = 0.333f;
-
      mQuatT = new Static4D(0,0,0,1);
      mQuatA = new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
 
@@ -111,6 +122,8 @@ public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, Distorte
      mSetQuatT       = false;
      mResettingObject= false;
 
+     mSaveIcon = -1;
+
      mScreen = new DistortedScreen();
      mScreen.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
      mScale = new Static3D(1,1,1);
@@ -216,6 +229,9 @@ public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, Distorte
        boolean done = continueResetting(time);
        if( done ) mResettingObject = false;
        }
+
+     if( mSaveIcon>=0 ) mSaveIcon++;
+     if( mSaveIcon>=2 ) { saveIcon(); mSaveIcon = -1; }
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -347,6 +363,7 @@ public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, Distorte
      BandagedCreatorActivity act = (BandagedCreatorActivity) mView.getContext();
 
      boolean success = createObjectJson(obj,act);
+     setupIconCreation(obj,act);
 
      if( success )
        {
@@ -411,31 +428,59 @@ public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, Distorte
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-/*
-   private void createIcon(TwistyObject object)
+
+   private void setupIconCreation(TwistyObject object, Activity act)
      {
-     if( !mIconActive )
-       {
-       mIconActive = true;
-
-       ObjectControl control = getControl();
-       DistortedEffects effects = object.getObjectEffects();
-
-       Static4D defRot = ShapeHexahedron.DEFAULT_ROT;
-       Static1D halo = new Static1D(5);
-       control.rotateNow(defRot);
-       float scale = 1.15f;
-       control.scaleNow(scale);
-       Static4D color = new Static4D(0,0,0,1);
-       PostprocessEffectBorder border = new PostprocessEffectBorder(halo,color);
-       border.setHaloDepth(false);
-       effects.apply(border);
-       String objectName = object.getShortName();
-       final String name = STORAGE+objectName+".png";
-       renderer.setSaveIcon(name);
-       }
+/*
+     // create framebuffer, attach object's node to it
+
+     ObjectControl control = getControl();
+     DistortedEffects effects = object.getObjectEffects();
+
+     Static4D defRot = ShapeHexahedron.DEFAULT_ROT;
+     Static1D halo = new Static1D(5);
+     control.rotateNow(defRot);
+     float scale = 1.15f;
+     control.scaleNow(scale);
+     Static4D color = new Static4D(0,0,0,1);
+     PostprocessEffectBorder border = new PostprocessEffectBorder(halo,color);
+     border.setHaloDepth(false);
+     effects.apply(border);
+
+     final String name = object.getShortName()+".png";
+     File file = new File(act.getFilesDir(), name);
+     String filename = file.getAbsolutePath();
+
+     mSaveIcon = 0;
+     mPath = filename;
+ */
      }
-*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void saveIcon()
+      {
+      int fW = mFramebuffer.getWidth();
+      int fH = mFramebuffer.getHeight();
+      ByteBuffer buf = ByteBuffer.allocateDirect(fW*fH*4);
+      buf.order(ByteOrder.LITTLE_ENDIAN);
+
+      int textureID = mFramebuffer.getTextureID();
+
+      if( textureID>=0 )
+        {
+        GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, textureID);
+        GLES31.glReadPixels( 0, 0, fW, fH, GLES31.GL_RGBA, GLES31.GL_UNSIGNED_BYTE, buf);
+        BandagedCreatorWorkerThread.newBuffer(buf,fW,fH,6,mPath);
+        }
+      else
+        {
+        android.util.Log.e("Save", "Error trying to read from offscreen FBO, textureID="+textureID);
+        }
+
+      mSaveIcon = -1;
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    public void displaySavingDialog()
diff --git a/src/main/java/org/distorted/bandaged/BandagedCreatorScreen.java b/src/main/java/org/distorted/bandaged/BandagedCreatorScreen.java
index a4f42645..bff1f309 100644
--- a/src/main/java/org/distorted/bandaged/BandagedCreatorScreen.java
+++ b/src/main/java/org/distorted/bandaged/BandagedCreatorScreen.java
@@ -19,7 +19,9 @@
 
 package org.distorted.bandaged;
 
+import android.graphics.Bitmap;
 import android.view.View;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 
 import org.distorted.helpers.TransparentImageButton;
@@ -139,4 +141,22 @@ public class BandagedCreatorScreen
     mObjectView.addView(pane);
     mNumObjects++;
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void iconCreationDone(Bitmap bmp)
+    {
+    int numChildren = mObjectView.getChildCount();
+    LinearLayout pane = (LinearLayout)mObjectView.getChildAt(numChildren-1);
+    ImageView view = pane.findViewById(R.id.bandagedCreatorObjectIcon);
+
+    if( view!=null )
+      {
+      view.setImageBitmap(bmp);
+      }
+    else
+      {
+      android.util.Log.e("D", "ImageView not found!");
+      }
+    }
 }
diff --git a/src/main/java/org/distorted/bandaged/BandagedCreatorWorkerThread.java b/src/main/java/org/distorted/bandaged/BandagedCreatorWorkerThread.java
new file mode 100644
index 00000000..8af52219
--- /dev/null
+++ b/src/main/java/org/distorted/bandaged/BandagedCreatorWorkerThread.java
@@ -0,0 +1,342 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2022 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.bandaged;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.widget.Toast;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class BandagedCreatorWorkerThread extends Thread
+  {
+  private static Vector<WorkLoad> mBuffers;
+  private static BandagedCreatorWorkerThread mThis=null;
+  private static WeakReference<Activity> mWeakAct;
+
+  private static class WorkLoad
+    {
+    ByteBuffer buffer;
+    int width;
+    int height;
+    int numColors;
+    String filename;
+
+    WorkLoad(ByteBuffer buf, int w, int h, int n, String name)
+      {
+      buffer   = buf;
+      width    = w;
+      height   = h;
+      numColors= n;
+      filename = name;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private BandagedCreatorWorkerThread()
+    {
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void create(Activity act)
+    {
+    mWeakAct = new WeakReference<>(act);
+
+    if( mThis==null )
+      {
+      mBuffers = new Vector<>();
+      mThis = new BandagedCreatorWorkerThread();
+      mThis.start();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void run()
+    {
+    WorkLoad load;
+
+    while(true)
+      {
+      synchronized(mThis)
+        {
+        while( mBuffers.size()>0 )
+          {
+          load = mBuffers.remove(0);
+          process(load);
+          }
+
+        try  { mThis.wait(); }
+        catch(InterruptedException ex) { }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void newBuffer(ByteBuffer buffer, int width, int height, int numColors, String filename)
+    {
+    synchronized(mThis)
+      {
+      WorkLoad load = new WorkLoad(buffer,width,height,numColors,filename);
+      mBuffers.add(load);
+      mThis.notify();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean containsBlackness(byte[] tmp, int width)
+    {
+    for(int i=0; i<width; i++)
+      {
+      if( tmp[4*i]==0 && tmp[4*i+3]==-1 ) return true;
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int computeFirstRow(ByteBuffer buf, byte[] tmp, int width, int height)
+    {
+    int wBytes = 4*width;
+
+    for(int i=0; i<height; i++)
+      {
+      buf.position(i*wBytes);
+      buf.get(tmp);
+
+      if( containsBlackness(tmp,width) ) return i;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int computeLastRow(ByteBuffer buf, byte[] tmp, int width, int height)
+    {
+    int wBytes = 4*width;
+
+    for(int i=height-1; i>=0; i--)
+      {
+      buf.position(i*wBytes);
+      buf.get(tmp);
+
+      if( containsBlackness(tmp,width) ) return i;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// GL uses a coordinate system from mathematics; i.e. (0,0) is in the lower-left corner. 2D stuff
+// has the origin on the upper-left corner; we have to flip our bitmap upside down!
+//
+// We also need to figure out the topmost and bottommost rows where the object starts and cut out
+// a square section from the 'input' so that the object is vertically centralized.
+
+  private ByteBuffer adjustBuffer(ByteBuffer input, byte[] tmp, int width, int height)
+    {
+    int wBytes = 4*width;
+
+    ByteBuffer output = ByteBuffer.allocateDirect(wBytes*width);
+    output.order(ByteOrder.LITTLE_ENDIAN);
+
+    int firstRow = computeFirstRow(input,tmp,width,height);
+    int lastRow  = computeLastRow(input,tmp,width,height);
+
+    int startRow = (firstRow+lastRow+width)/2;
+
+    for(int i=0; i<width; i++)
+      {
+      input.position((startRow-i)*wBytes);
+      input.get(tmp);
+      output.position(i*wBytes);
+      output.put(tmp);
+      }
+
+    output.rewind();
+    return output;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void replaceColor(byte[] tmp, int index, int numColors)
+    {
+    byte A = (byte)0xab;
+    int CUTOFF = 100;
+
+    switch(numColors)
+      {
+      case 4: if( tmp[index+1]==0 ) { tmp[index]=A; tmp[index+1]=0; tmp[index+2]=0; }
+              else
+                {
+                if( tmp[index]==tmp[index+1] )
+                  {
+                  if( tmp[index]<0 || tmp[index]>CUTOFF )  { tmp[index]=A; tmp[index+1]=A; tmp[index+2]=0; }
+                  }
+                else { tmp[index]=0; tmp[index+1]=0; tmp[index+2]=A; }
+                }
+              break;
+      case 6: if( tmp[index+2]!=0 )
+                {
+                if( tmp[index]==0 ) { tmp[index]=0; tmp[index+1]=0; tmp[index+2]=A; }
+                }
+              else
+                {
+                if( tmp[index]==tmp[index+1] ) { tmp[index]=A; tmp[index+1]=A; tmp[index+2]=0; }
+                else { tmp[index]=A; tmp[index+1]=0; tmp[index+2]=0; }
+                }
+              break;
+      case 8: if( tmp[index+2]!=0 )
+                {
+                if( tmp[index+1]==0 ) { tmp[index]=A; tmp[index+1]=0; tmp[index+2]=0; }
+                }
+              else
+                {
+                if( tmp[index+1]==0 ) { tmp[index]=0; tmp[index+1]=0; tmp[index+2]=A; }
+                else
+                  {
+                  if( tmp[index]==tmp[index+1] ) { tmp[index]=A; tmp[index+1]=A; tmp[index+2]=0; }
+                  else { tmp[index]=0; tmp[index+1]=A; tmp[index+2]=0; }
+                  }
+                }
+              break;
+
+      case 12:if( tmp[index+2]==0 )
+                {
+                if( tmp[index+1]==0 )  {tmp[index]=A; tmp[index+1]=0; tmp[index+2]=0;}
+                else  {tmp[index]=0; tmp[index+1]=A; tmp[index+2]=0;}
+                }
+              else
+                {
+                if( tmp[index]==tmp[index+1] )
+                  {
+                  if( tmp[index]<0 || tmp[index]>CUTOFF ) {tmp[index]=A; tmp[index+1]=A; tmp[index+2]=0;}
+                  }
+                else  {tmp[index]=0; tmp[index+1]=0; tmp[index+2]=A;}
+                }
+              break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private ByteBuffer replaceColors(ByteBuffer input, byte[] tmp, int size, int numColors)
+    {
+    int marker = (int)(255*BandagedCreatorRenderer.BRIGHTNESS);
+    int wBytes = 4*size;
+
+    ByteBuffer output = ByteBuffer.allocateDirect(wBytes*size);
+    output.order(ByteOrder.LITTLE_ENDIAN);
+
+    for(int i=0; i<size; i++)
+      {
+      input.position(i*wBytes);
+      input.get(tmp);
+
+      for(int j=0; j<size; j++)
+        {
+        if( tmp[4*j]==marker && tmp[4*j+1]==marker && tmp[4*j+2]==marker )
+          {
+          tmp[4*j  ]=0;
+          tmp[4*j+1]=0;
+          tmp[4*j+2]=0;
+          tmp[4*j+3]=0;
+          }
+        else if( tmp[4*j]!=0 || tmp[4*j+1]!=0 || tmp[4*j+2]!=0 ) replaceColor(tmp,4*j,numColors);
+        }
+
+      output.position(i*wBytes);
+      output.put(tmp);
+      }
+
+    output.rewind();
+    return output;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void process(WorkLoad load)
+    {
+    int width = load.width;
+    int height= load.height;
+
+    byte[] tmp = new byte[4*width];
+    ByteBuffer bufSquare = adjustBuffer(load.buffer,tmp,width,height);
+    ByteBuffer bufTransp = replaceColors(bufSquare,tmp,width,load.numColors);
+
+    final String filename = load.filename;
+    BufferedOutputStream bos =null;
+    final Activity act = mWeakAct.get();
+
+    try
+      {
+      bos = new BufferedOutputStream(new FileOutputStream(filename));
+      Bitmap bmp = Bitmap.createBitmap( width, width, Bitmap.Config.ARGB_8888);
+      bmp.copyPixelsFromBuffer(bufTransp);
+      bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
+
+      BandagedCreatorActivity cact = (BandagedCreatorActivity)act;
+      cact.iconCreationDone(bmp);
+      }
+    catch(final Exception ex)
+      {
+      act.runOnUiThread(new Runnable()
+        {
+        public void run()
+          {
+          Toast.makeText(act,
+              "Saving to \n\n"+filename+"\n\n failed: "+"\n\n"+ex.getMessage() ,
+              Toast.LENGTH_LONG).show();
+          }
+        });
+      }
+    finally
+      {
+      if(bos!=null)
+        {
+        try { bos.close(); }
+        catch(IOException io) {}
+        }
+      }
+
+    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+    File f = new File(filename);
+    Uri contentUri = Uri.fromFile(f);
+    mediaScanIntent.setData(contentUri);
+    act.sendBroadcast(mediaScanIntent);
+    }
+  }
diff --git a/src/main/java/org/distorted/bandaged/BandagedCubit.java b/src/main/java/org/distorted/bandaged/BandagedCubit.java
index b730de1a..b52f0e54 100644
--- a/src/main/java/org/distorted/bandaged/BandagedCubit.java
+++ b/src/main/java/org/distorted/bandaged/BandagedCubit.java
@@ -79,7 +79,7 @@ public class BandagedCubit
       mMove = new Static3D(0,0,0);
 
       FactoryBandaged3x3Cubit factory = FactoryBandaged3x3Cubit.getInstance();
-      MeshBase mesh = factory.createMesh(mPosition);
+      MeshBase mesh = factory.createMesh(mPosition,false);
 
       mTexture = new DistortedTexture();
       mTexture.setColorARGB(color);
@@ -114,7 +114,7 @@ public class BandagedCubit
       mPosition = tmpPosition;
 
       FactoryBandaged3x3Cubit factory = FactoryBandaged3x3Cubit.getInstance();
-      MeshBase mesh = factory.createMesh(mPosition);
+      MeshBase mesh = factory.createMesh(mPosition,false);
       mNode.setMesh(mesh);
       mMove.set( scale*mUnscaledX, scale*mUnscaledY, scale*mUnscaledZ);
       }
@@ -135,7 +135,7 @@ public class BandagedCubit
       computeMove(mPosition);
 
       FactoryBandaged3x3Cubit factory = FactoryBandaged3x3Cubit.getInstance();
-      MeshBase mesh = factory.createMesh(mPosition);
+      MeshBase mesh = factory.createMesh(mPosition,false);
       mNode.setMesh(mesh);
       mMove.set( scale*mUnscaledX, scale*mUnscaledY, scale*mUnscaledZ);
       }
diff --git a/src/main/java/org/distorted/bandaged/BandagedPlayActivity.java b/src/main/java/org/distorted/bandaged/BandagedPlayActivity.java
index 3e31ee93..c0b01184 100644
--- a/src/main/java/org/distorted/bandaged/BandagedPlayActivity.java
+++ b/src/main/java/org/distorted/bandaged/BandagedPlayActivity.java
@@ -31,11 +31,11 @@ import androidx.appcompat.app.AppCompatActivity;
 
 import com.google.firebase.analytics.FirebaseAnalytics;
 
-import org.distorted.config.ConfigSurfaceView;
 import org.distorted.dialogs.RubikDialogError;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.main.R;
 import org.distorted.objectlib.main.ObjectControl;
+import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
 
@@ -211,11 +211,12 @@ public class BandagedPlayActivity extends AppCompatActivity
       if( object!=null )
         {
         int meshState          = object.getMeshState();
+        int iconMode           = TwistyObject.MODE_NORM;
         InputStream jsonStream = object.getObjectStream(this);
         InputStream meshStream = object.getMeshStream(this);
         String name            = object.getUpperName();
 
-        control.changeIfDifferent(ordinal,name,meshState,jsonStream,meshStream);
+        control.changeIfDifferent(ordinal,name,meshState,iconMode,jsonStream,meshStream);
         }
       }
 
diff --git a/src/main/java/org/distorted/config/ConfigActivity.java b/src/main/java/org/distorted/config/ConfigActivity.java
index b6ea1254..502adb3a 100644
--- a/src/main/java/org/distorted/config/ConfigActivity.java
+++ b/src/main/java/org/distorted/config/ConfigActivity.java
@@ -35,6 +35,7 @@ import org.distorted.library.main.DistortedLibrary;
 import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.main.R;
 import org.distorted.dialogs.RubikDialogError;
+import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
 
@@ -208,11 +209,12 @@ public class ConfigActivity extends AppCompatActivity
       if( object!=null )
         {
         int meshState          = object.getMeshState();
+        int iconMode           = TwistyObject.MODE_NORM;
         InputStream jsonStream = object.getObjectStream(this);
         InputStream meshStream = object.getMeshStream(this);
         String name            = object.getUpperName();
 
-        control.changeIfDifferent(ordinal,name,meshState,jsonStream,meshStream);
+        control.changeIfDifferent(ordinal,name,meshState,iconMode,jsonStream,meshStream);
         }
       }
 
@@ -239,10 +241,11 @@ public class ConfigActivity extends AppCompatActivity
         ObjectControl control = view.getObjectControl();
 
         int meshState          = object.getMeshState();
+        int iconMode           = TwistyObject.MODE_NORM;
         InputStream jsonStream = object.getObjectStream(this);
         InputStream meshStream = object.getMeshStream(this);
 
-        control.changeObject(ordinal,meshState,jsonStream,meshStream);
+        control.changeObject(ordinal,meshState,iconMode,jsonStream,meshStream);
         }
       }
 
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index b7205b32..3a2e92b0 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -490,11 +490,12 @@ public class RubikActivity extends AppCompatActivity
       {
       RubikObject object = RubikObjectList.getObject(ordinal);
       int meshState = object!=null ? object.getMeshState() : MESH_NICE;
+      int iconMode  = TwistyObject.MODE_NORM;
       InputStream jsonStream = object==null ? null : object.getObjectStream(this);
       InputStream meshStream = object==null ? null : object.getMeshStream(this);
       String name = object==null ? "NULL" : object.getUpperName();
 
-      control.changeIfDifferent(ordinal,name,meshState,jsonStream,meshStream);
+      control.changeIfDifferent(ordinal,name,meshState,iconMode,jsonStream,meshStream);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/tutorials/TutorialActivity.java b/src/main/java/org/distorted/tutorials/TutorialActivity.java
index 1c767ef5..636b8ed0 100644
--- a/src/main/java/org/distorted/tutorials/TutorialActivity.java
+++ b/src/main/java/org/distorted/tutorials/TutorialActivity.java
@@ -34,11 +34,10 @@ import androidx.appcompat.app.AppCompatActivity;
 
 import com.google.firebase.analytics.FirebaseAnalytics;
 
-import org.distorted.dmesh.ObjectMesh;
-import org.distorted.jsons.ObjectJson;
 import org.distorted.library.main.DistortedLibrary;
 
 import org.distorted.objectlib.main.ObjectControl;
+import org.distorted.objectlib.main.TwistyObject;
 
 import org.distorted.main.R;
 import org.distorted.dialogs.RubikDialogError;
@@ -232,11 +231,12 @@ public class TutorialActivity extends AppCompatActivity
       {
       RubikObject object = RubikObjectList.getObject(ordinal);
       int meshState = object!=null ? object.getMeshState() : MESH_NICE;
+      int iconMode  = TwistyObject.MODE_NORM;
       InputStream jsonStream = object==null ? null : object.getObjectStream(this);
       InputStream meshStream = object==null ? null : object.getMeshStream(this);
       String name = object==null ? "NULL" : object.getUpperName();
 
-      control.changeIfDifferent(ordinal,name,meshState,jsonStream,meshStream);
+      control.changeIfDifferent(ordinal,name,meshState,iconMode,jsonStream,meshStream);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
