commit 7edab735b0162362ac4bdf683fed7420dc69486f
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Oct 13 13:32:13 2020 +0100

    Some improvements to MeshFile.

diff --git a/src/main/java/org/distorted/examples/meshfile/CubitFactory.java b/src/main/java/org/distorted/examples/meshfile/CubitFactory.java
new file mode 100644
index 0000000..94b0666
--- /dev/null
+++ b/src/main/java/org/distorted/examples/meshfile/CubitFactory.java
@@ -0,0 +1,1161 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube 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.                                                           //
+//                                                                                               //
+// Magic Cube 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 Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.meshfile;
+
+import org.distorted.library.effect.VertexEffect;
+import org.distorted.library.effect.VertexEffectDeform;
+import org.distorted.library.effect.VertexEffectMove;
+import org.distorted.library.effect.VertexEffectRotate;
+import org.distorted.library.effect.VertexEffectScale;
+import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.mesh.MeshJoined;
+import org.distorted.library.mesh.MeshPolygon;
+import org.distorted.library.type.Static1D;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class CubitFactory
+  {
+  private static final float SQ2 = (float)Math.sqrt(2);
+  private static final float SQ3 = (float)Math.sqrt(3);
+  private static final float SQ6 = (float)Math.sqrt(6);
+
+  private static final Static1D RADIUS = new Static1D(1);
+
+  private static CubitFactory mThis;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private CubitFactory()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static CubitFactory getInstance()
+    {
+    if( mThis==null ) mThis = new CubitFactory();
+
+    return mThis;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// H - height of the band in the middle
+// alpha - angle of the edge  [0,90]
+// dist - often in a polygon the distance from edge to center is not 1, but something else.
+// This is the distance.
+// K - where to begin the second, much more flat part of the band. [0,1]
+// N - number of bands. N>=3
+//
+// theory: two distinct parts to the band:
+// 1) (0,B) - steep
+// 2) (B,1) - flat
+//
+// In first part, we have y = g(x) ; in second - y = g(f(x)) where
+//
+// g(x) = sqrt( R^2 - (x-D)^2 ) - R*cos(alpha)
+// f(x) = ((D-B)/(1-B)*x + B*(1-D)/(1-B)
+// h(x) = R*(sin(alpha) - sin(x))
+// R = H/(1-cos(alpha))
+// D = H*sin(alpha)
+// B = h(K*alpha)
+//
+// The N points are taken at:
+//
+// 1) in the second part, there are K2 = (N-3)/3 such points
+// 2) in the first - K1 = (N-3) - K2
+// 3) also, the 3 points 0,B,1
+//
+// so we have the sequence A[i] of N points
+//
+// 0
+// h((i+1)*(1-K)*alpha/(K1+1)) (i=0,1,...,K1-1)
+// B
+// (1-B)*(i+1)/(K2+1) + B   (i=0,i,...,K2-1)
+// 1
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float f(float D, float B, float x)
+    {
+    return ((D-B)*x + B*(1-D))/(1-B);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float g(float R, float D, float x, float cosAlpha)
+    {
+    float d = x-D;
+    return (float)(Math.sqrt(R*R-d*d)-R*cosAlpha);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float h(float R, float sinAlpha, float x)
+    {
+    return R*(sinAlpha-(float)Math.sin(x));
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[] computeBands(float H, int alpha, float dist, float K, int N)
+    {
+    float[] bands = new float[2*N];
+
+    bands[0] = 1.0f;
+    bands[1] = 0.0f;
+
+    float beta = (float)Math.atan(dist*Math.tan(Math.PI*alpha/180));
+    float sinBeta = (float)Math.sin(beta);
+    float cosBeta = (float)Math.cos(beta);
+    float R = cosBeta<1.0f ? H/(1.0f-cosBeta) : 0.0f;
+    float D = R*sinBeta;
+    float B = h(R,sinBeta,K*beta);
+
+    if( D>1.0f )
+      {
+      for(int i=1; i<N; i++)
+        {
+        bands[2*i  ] = (float)(N-1-i)/(N-1);
+        bands[2*i+1] = H*(1-bands[2*i]);
+        }
+      }
+    else
+      {
+      int K2 = (N-3)/3;
+      int K1 = (N-3)-K2;
+
+      for(int i=0; i<=K1; i++)
+        {
+        float angle = K*beta + (1-K)*beta*(K1-i)/(K1+1);
+        float x = h(R,sinBeta,angle);
+        bands[2*i+2] = 1.0f - x;
+        bands[2*i+3] = g(R,D,x,cosBeta);
+        }
+
+      for(int i=0; i<=K2; i++)
+        {
+        float x = (1-B)*(i+1)/(K2+1) + B;
+        bands[2*K1+2 + 2*i+2] = 1.0f - x;
+        bands[2*K1+2 + 2*i+3] = g(R,D,f(D,B,x),cosBeta);
+        }
+      }
+
+    return bands;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void roundCorners(MeshBase mesh, Static3D center, Static3D[] vertices, float strength, float regionRadius)
+    {
+    Static4D reg= new Static4D(0,0,0,regionRadius);
+
+    float centX = center.get0();
+    float centY = center.get1();
+    float centZ = center.get2();
+
+    for (Static3D vertex : vertices)
+      {
+      float x = strength*(centX - vertex.get0());
+      float y = strength*(centY - vertex.get1());
+      float z = strength*(centZ - vertex.get2());
+
+      VertexEffect effect = new VertexEffectDeform(new Static3D(x,y,z), RADIUS, vertex, reg);
+      mesh.apply(effect);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesCube(int index)
+    {
+    final int MESHES=6;
+    MeshBase[] meshes = new MeshPolygon[MESHES];
+    int association = 1;
+
+    float[] bands;
+    float D = 0.027f;
+    float E = 0.5f-D;
+    float[] vertices = { -E,-E, +E,-E, +E,+E, -E,+E };
+    int extraI, extraV;
+
+    switch(index)
+      {
+      case 0 : bands = new float[] { 1.0f    ,-D,
+                                     1.0f-D/2,-D*0.55f,
+                                     1.0f-D  ,-D*0.25f,
+                                     1.0f-2*D, 0.0f,
+                                     0.50f   , 0.040f,
+                                     0.0f    , 0.048f };
+               extraI = 2;
+               extraV = 2;
+               break;
+      case 1 : bands = new float[] { 1.0f       ,-D,
+                                     1.0f-D*1.2f,-D*0.55f,
+                                     1.0f-2*D   , 0.0f,
+                                     0.50f      , 0.040f,
+                                     0.0f       , 0.048f };
+               extraI = 2;
+               extraV = 2;
+               break;
+      case 2 : bands = new float[] { 1.0f       ,-D,
+                                     1.0f-D*1.2f,-D*0.55f,
+                                     1.0f-2*D   , 0.0f,
+                                     0.50f      , 0.040f,
+                                     0.0f       , 0.048f };
+               extraI = 1;
+               extraV = 2;
+               break;
+      default: bands = new float[] { 1.0f    ,-D,
+                                     1.0f-2*D, 0.0f,
+                                     0.50f   , 0.025f,
+                                     0.0f    , 0.030f };
+               extraI = 1;
+               extraV = 1;
+               break;
+      }
+
+    meshes[0] = new MeshPolygon(vertices,bands,extraI,extraV);
+    meshes[0].setEffectAssociation(0,association,0);
+
+    for(int i=1; i<MESHES; i++)
+      {
+      association <<=1;
+      meshes[i] = meshes[0].copy(true);
+      meshes[i].setEffectAssociation(0,association,0);
+      }
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesSkewbCorner()
+    {
+    MeshBase[] meshes = new MeshBase[6];
+
+    float E = 0.5f;
+    float F = SQ2/2;
+    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
+    float[] bands0 = computeBands(0.03f,30,E/3,0.5f,7);
+
+    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
+    meshes[0].setEffectAssociation(0,1,0);
+    meshes[1] = meshes[0].copy(true);
+    meshes[1].setEffectAssociation(0,2,0);
+    meshes[2] = meshes[0].copy(true);
+    meshes[2].setEffectAssociation(0,4,0);
+
+    float[] vertices1 = { 0,0, F,0, F/2,(SQ3/2)*F };
+    float[] bands1 = computeBands(0,0,1,0,3);
+
+    meshes[3] = new MeshPolygon(vertices1,bands1,1,5);
+    meshes[3].setEffectAssociation(0,8,0);
+    meshes[4] = meshes[3].copy(true);
+    meshes[4].setEffectAssociation(0,16,0);
+    meshes[5] = meshes[3].copy(true);
+    meshes[5].setEffectAssociation(0,32,0);
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesSkewbFace()
+    {
+    MeshBase[] meshes = new MeshBase[5];
+
+    float E = SQ2/4;
+    float[] vertices0 = { -E,-E, +E,-E, +E,+E, -E,+E };
+    float[] bands0 = computeBands(0.05f,30,E/2,0.5f,7);
+
+    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
+    meshes[0].setEffectAssociation(0,1,0);
+
+    float[] vertices1 = { -E,-SQ3*E, +E,-SQ3*E, 0,0 };
+    float[] bands1 = computeBands(0,0,1,0,3);
+
+    meshes[1] = new MeshPolygon(vertices1,bands1,0,0);
+    meshes[1].setEffectAssociation(0,2,0);
+    meshes[2] = meshes[1].copy(true);
+    meshes[2].setEffectAssociation(0,4,0);
+    meshes[3] = meshes[1].copy(true);
+    meshes[3].setEffectAssociation(0,8,0);
+    meshes[4] = meshes[1].copy(true);
+    meshes[4].setEffectAssociation(0,16,0);
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesOcta()
+    {
+    int association = 1;
+
+    float C = 0.06f;
+    float D = 0.031f;
+    float E = SQ3/2;
+    float F = 0.5f;
+
+    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
+
+    float[] bands = new float[] { 1.0f    , 0,
+                                  1.0f  -C, D*0.55f,
+                                  1.0f-2*C, D*0.85f,
+                                  1.0f-4*C, D*1.20f,
+                                  0.5f    , D*1.40f,
+                                  0.0f    , D*1.50f };
+
+    MeshBase[] meshes = new MeshPolygon[8];
+    meshes[0] = new MeshPolygon(vertices, bands, 2,2);
+    meshes[0].setEffectAssociation(0,association,0);
+
+    for(int i=1; i<8; i++)
+      {
+      association <<= 1;
+      meshes[i] = meshes[0].copy(true);
+      meshes[i].setEffectAssociation(0,association,0);
+      }
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesTetra()
+    {
+    MeshBase[] meshes = new MeshBase[4];
+    int association = 1;
+
+    float C = 0.06f;
+    float D = 0.035f;
+    float E = SQ3/2;
+    float F = 0.5f;
+
+    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
+
+    float[] bands = new float[] { 1.0f    , 0,
+                                  1.0f  -C, D*0.50f,
+                                  1.0f-2*C, D*0.80f,
+                                  1.0f-4*C, D*1.10f,
+                                  0.5f    , D*1.30f,
+                                  0.0f    , D*1.35f };
+
+    meshes[0] = new MeshPolygon(vertices, bands, 2,2);
+    meshes[0].setEffectAssociation(0,association,0);
+
+    for(int i=1; i<4; i++)
+      {
+      association <<= 1;
+      meshes[i] = meshes[0].copy(true);
+      meshes[i].setEffectAssociation(0,association,0);
+      }
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesDino()
+    {
+    final int MESHES=4;
+    MeshBase[] meshes = new MeshPolygon[MESHES];
+
+    float E = 0.5f*SQ2;
+    float F = 0.5f;
+    float[] vertices0 = { -F,F/3, 0,-2*F/3, +F,F/3 };
+    float[] bands0 = computeBands(0.03f,45,F/3,0.2f,7);
+
+    meshes[0] = new MeshPolygon(vertices0, bands0, 2, 5);
+    meshes[0].setEffectAssociation(0,1,0);
+    meshes[1] = meshes[0].copy(true);
+    meshes[1].setEffectAssociation(0,2,0);
+
+    float[] vertices1 = { -E/2,-E*(SQ3/6), E/2,-E*(SQ3/6), 0,E*(SQ3/3) };
+    float[] bands1 = computeBands(0.02f,45,F/3,0.2f,3);
+
+    meshes[2] = new MeshPolygon(vertices1, bands1, 1, 2);
+    meshes[2].setEffectAssociation(0,4,0);
+    meshes[3] = meshes[2].copy(true);
+    meshes[3].setEffectAssociation(0,8,0);
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesHelicopterCorner()
+    {
+    MeshBase[] meshes = new MeshBase[6];
+
+    float E = 0.5f;
+    float F = SQ2/4;
+    float G = 1.0f/12;
+    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
+    float[] bands0 = computeBands(0.03f,45,E/4,0.2f,7);
+
+    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
+    meshes[0].setEffectAssociation(0,1,0);
+    meshes[1] = meshes[0].copy(true);
+    meshes[1].setEffectAssociation(0,2,0);
+    meshes[2] = meshes[0].copy(true);
+    meshes[2].setEffectAssociation(0,4,0);
+
+    float[] vertices1 = { -F,-G, 0,-G, +F,-G, 0,2*G };
+    float[] bands1 = computeBands(0.00f,0,0,0.0f,3);
+    meshes[3] = new MeshPolygon(vertices1,bands1,1,5);
+    meshes[3].setEffectAssociation(0,8,0);
+    meshes[4] = meshes[3].copy(true);
+    meshes[4].setEffectAssociation(0,16,0);
+    meshes[5] = meshes[3].copy(true);
+    meshes[5].setEffectAssociation(0,32,0);
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesHelicopterFace()
+    {
+    MeshBase[] meshes = new MeshBase[4];
+
+    float E = 0.5f;
+    float F = SQ2/4;
+    float G = 1.0f/12;
+    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
+    float[] bands0 = computeBands(0.03f,45,E/4,0.1f,7);
+
+    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
+    meshes[0].setEffectAssociation(0,1,0);
+
+    float[] vertices1 = { -F,-G, +F,-G, 0,2*G};
+    float[] bands1 = computeBands(0.01f,45,F,0.0f,3);
+
+    meshes[1] = new MeshPolygon(vertices1, bands1, 1, 3);
+    meshes[1].setEffectAssociation(0,2,0);
+
+    float[] vertices2 = { -E/2,-F/3, +E/2,-F/3, 0,2*F/3};
+
+    meshes[2] = new MeshPolygon(vertices2, bands1, 1, 3);
+    meshes[2].setEffectAssociation(0,4,0);
+    meshes[3] = meshes[2].copy(true);
+    meshes[3].setEffectAssociation(0,8,0);
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesRediEdge()
+    {
+    final int MESHES=6;
+    float F = 0.25f;
+
+    float[] bands0 = computeBands(0.03f,45,F,0.2f,7);
+    float[] vertices0 = { -F,+F, -F,-F, 0, -2*F, +F,-F, +F,+F };
+
+    MeshBase[] meshes = new MeshPolygon[MESHES];
+    meshes[0] = new MeshPolygon(vertices0, bands0, 2, 2);
+    meshes[0].setEffectAssociation(0,1,0);
+    meshes[1] = meshes[0].copy(true);
+    meshes[1].setEffectAssociation(0,2,0);
+
+    float[] bands1 = computeBands(0.02f,45,F/2,0.2f,3);
+    float[] vertices1 = { -F/2, +F/2, -F/2, -1.5f*F, 1.5f*F, +F/2 };
+
+    meshes[2] = new MeshPolygon(vertices1, bands1, 1, 2);
+    meshes[2].setEffectAssociation(0,4,0);
+    meshes[3] = meshes[2].copy(true);
+    meshes[3].setEffectAssociation(0,8,0);
+
+    float X = 0.25f*SQ2;
+    float Y = SQ6/16;
+    float[] vertices2 = { -X, Y, -1.5f*X, -Y, +1.5f*X, -Y, +X, Y };
+
+    meshes[4] = new MeshPolygon(vertices2, bands1, 1, 1);
+    meshes[4].setEffectAssociation(0,16,0);
+    meshes[5] = meshes[4].copy(true);
+    meshes[5].setEffectAssociation(0,32,0);
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesRediCorner()
+    {
+    final int MESHES=6;
+    MeshBase[] meshes = new MeshBase[MESHES];
+
+    float E = 0.5f;
+    float[] vertices1 = { -E,-E, +E,-E, +E,+E, -E,+E };
+    float[] bands1 = computeBands(0.04f,45,E,0.3f,6);
+
+    meshes[0] = new MeshPolygon(vertices1,bands1,2,2);
+    meshes[0].setEffectAssociation(0,1,0);
+    meshes[1] = meshes[0].copy(true);
+    meshes[1].setEffectAssociation(0,2,0);
+    meshes[2] = meshes[0].copy(true);
+    meshes[2].setEffectAssociation(0,4,0);
+
+    float F = SQ2/2;
+    float X = 0.5f;
+    float G = 0.72f;
+    float[] vertices2 = { -E,+F, -E+X,0, -E,-F, -E*G,-F, +E*G,-F, +E,-F, +E-X,0, +E,+F, +E*G,+F, -E*G,+F };
+    float[] bands2 = computeBands(0.0f,0,1.0f,0.0f,2);
+
+    meshes[3] = new MeshPolygon(vertices2,bands2,0,0);
+    meshes[3].setEffectAssociation(0,8,0);
+    meshes[4] = meshes[3].copy(true);
+    meshes[4].setEffectAssociation(0,16,0);
+    meshes[5] = meshes[3].copy(true);
+    meshes[5].setEffectAssociation(0,32,0);
+
+    return new MeshJoined(meshes);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsCube()
+    {
+    Static3D axisY   = new Static3D(0,1,0);
+    Static3D axisX   = new Static3D(1,0,0);
+    Static3D center  = new Static3D(0,0,0);
+    Static1D angle90 = new Static1D(90);
+    Static1D angle180= new Static1D(180);
+    Static1D angle270= new Static1D(270);
+
+    VertexEffect[] effect = new VertexEffect[6];
+
+    effect[0] = new VertexEffectMove(new Static3D(0,0,+0.5f));
+    effect[1] = new VertexEffectRotate( angle180, axisX, center );
+    effect[2] = new VertexEffectRotate( angle90 , axisX, center );
+    effect[3] = new VertexEffectRotate( angle270, axisX, center );
+    effect[4] = new VertexEffectRotate( angle270, axisY, center );
+    effect[5] = new VertexEffectRotate( angle90 , axisY, center );
+
+    effect[0].setMeshAssociation(63,-1);  // all 6 sides
+    effect[1].setMeshAssociation(32,-1);  // back
+    effect[2].setMeshAssociation( 8,-1);  // bottom
+    effect[3].setMeshAssociation( 4,-1);  // top
+    effect[4].setMeshAssociation( 2,-1);  // left
+    effect[5].setMeshAssociation( 1,-1);  // right
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsSkewbCorner()
+    {
+    float E = 0.5f;
+
+    Static3D axisX  = new Static3D(1,0,0);
+    Static3D axisY  = new Static3D(0,1,0);
+    Static3D axis0  = new Static3D(-SQ2/2,0,SQ2/2);
+    Static3D axis1  = new Static3D(+SQ3/3,+SQ3/3,+SQ3/3);
+    Static1D angle1 = new Static1D(+90);
+    Static1D angle2 = new Static1D(-90);
+    Static1D angle3 = new Static1D(-15);
+    Static1D angle4 = new Static1D((float)((180.0f/Math.PI)*Math.acos(SQ3/3)));
+    Static1D angle5 = new Static1D(120);
+    Static1D angle6 = new Static1D(240);
+    Static3D center1= new Static3D(0,0,0);
+    Static3D center2= new Static3D(-0.5f,-0.5f,-0.5f);
+    Static3D move1  = new Static3D(-E/4,-E/4,0);
+    Static3D move2  = new Static3D(-0.5f,-0.5f,-0.5f);
+
+    VertexEffect[] effect = new VertexEffect[10];
+
+    effect[0] = new VertexEffectMove(move1);
+    effect[1] = new VertexEffectScale(new Static3D(1,1,-1));
+    effect[2] = new VertexEffectRotate(angle1,axisX,center1);
+    effect[3] = new VertexEffectRotate(angle2,axisY,center1);
+    effect[4] = new VertexEffectMove(move2);
+    effect[5] = new VertexEffectRotate(angle1,axisX,center2);
+    effect[6] = new VertexEffectRotate(angle3,axisY,center2);
+    effect[7] = new VertexEffectRotate(angle4,axis0,center2);
+    effect[8] = new VertexEffectRotate(angle5,axis1,center2);
+    effect[9] = new VertexEffectRotate(angle6,axis1,center2);
+
+    effect[0].setMeshAssociation( 7,-1);  // meshes 0,1,2
+    effect[1].setMeshAssociation( 6,-1);  // meshes 1,2
+    effect[2].setMeshAssociation( 2,-1);  // mesh 1
+    effect[3].setMeshAssociation( 4,-1);  // mesh 2
+    effect[4].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[5].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[6].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[7].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[8].setMeshAssociation(16,-1);  // mesh 4
+    effect[9].setMeshAssociation(32,-1);  // mesh 5
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsSkewbFace()
+    {
+    Static3D center = new Static3D(0,0,0);
+    Static3D axisX  = new Static3D(1,0,0);
+    Static3D axisZ  = new Static3D(0,0,1);
+    float angle = -(float)((180.0f/Math.PI)*Math.acos(SQ3/3));
+
+    VertexEffect[] effect = new VertexEffect[6];
+
+    effect[0] = new VertexEffectRotate( new Static1D(angle), axisX, center);
+    effect[1] = new VertexEffectRotate( new Static1D(  135), axisZ, center);
+    effect[2] = new VertexEffectRotate( new Static1D(   45), axisZ, center);
+    effect[3] = new VertexEffectRotate( new Static1D(  -45), axisZ, center);
+    effect[4] = new VertexEffectRotate( new Static1D( -135), axisZ, center);
+    effect[5] = new VertexEffectMove( new Static3D(0,0,-0.5f) );
+
+    effect[0].setMeshAssociation(30,-1);  // meshes 1,2,3,4
+    effect[1].setMeshAssociation( 2,-1);  // mesh 1
+    effect[2].setMeshAssociation( 5,-1);  // meshes 0,2
+    effect[3].setMeshAssociation( 8,-1);  // mesh 3
+    effect[4].setMeshAssociation(16,-1);  // mesh 4
+    effect[5].setMeshAssociation(30,-1);  // meshes 1,2,3,4
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsOcta()
+    {
+    Static1D alpha = new Static1D((float)(-(180/Math.PI)*Math.asin(SQ3/3)));
+    Static1D angle1= new Static1D( 90);
+    Static1D angle2= new Static1D(180);
+    Static1D angle3= new Static1D(270);
+    Static3D move1 = new Static3D(0,SQ2/2-SQ3/3,0);
+    Static3D axisX = new Static3D(1,0,0);
+    Static3D axisY = new Static3D(0,1,0);
+    Static3D cent0 = new Static3D(0,0,0);
+    Static3D cent1 = new Static3D(0,SQ2/2,0);
+    Static3D flipY = new Static3D( 1,-1, 1);
+
+    VertexEffect[] effect = new VertexEffect[6];
+
+    effect[0] = new VertexEffectMove(move1);
+    effect[1] = new VertexEffectRotate(alpha , axisX, cent1);
+    effect[2] = new VertexEffectRotate(angle1, axisY, cent0);
+    effect[3] = new VertexEffectRotate(angle2, axisY, cent0);
+    effect[4] = new VertexEffectRotate(angle3, axisY, cent0);
+    effect[5] = new VertexEffectScale(flipY);
+
+    effect[2].setMeshAssociation ( 34,-1); // apply to meshes 1 & 5
+    effect[3].setMeshAssociation ( 68,-1); // apply to meshes 2 & 6
+    effect[4].setMeshAssociation (136,-1); // apply to meshes 3 & 7
+    effect[5].setMeshAssociation (240,-1); // apply to meshes 4,5,6,7
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsTetra()
+    {
+    Static3D flipZ = new Static3D( 1, 1,-1);
+
+    Static1D alpha = new Static1D((float)(-(180/Math.PI)*Math.asin(SQ3/3)));
+    Static1D angle1= new Static1D( 90);
+    Static1D angle2= new Static1D(180);
+    Static3D move1 = new Static3D(0,SQ2/4-SQ3/6,0);
+
+    Static3D axisX = new Static3D(1,0,0);
+    Static3D axisY = new Static3D(0,1,0);
+    Static3D axisZ = new Static3D(0,0,1);
+
+    Static3D cent0 = new Static3D(0,0,0);
+    Static3D cent1 = new Static3D(0,SQ2/4,0);
+
+    VertexEffect[] effect = new VertexEffect[6];
+
+    effect[0] = new VertexEffectRotate(angle2, axisZ, cent0);
+    effect[1] = new VertexEffectMove(move1);
+    effect[2] = new VertexEffectRotate(alpha , axisX, cent1);
+    effect[3] = new VertexEffectScale(flipZ);
+    effect[4] = new VertexEffectRotate(angle1, axisY, cent0);
+    effect[5] = new VertexEffectRotate(angle2, axisZ, cent0);
+
+    effect[0].setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect[1].setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect[2].setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect[3].setMeshAssociation(10,-1); // meshes 1 & 3
+    effect[4].setMeshAssociation(12,-1); // meshes 2 & 3
+    effect[5].setMeshAssociation(12,-1); // meshes 2 & 3
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsDino()
+    {
+    float E = 0.5f*SQ2;
+    float F = 0.5f;
+    final float ANGLE = (float)((180/Math.PI)*(Math.atan(SQ2)));
+
+    Static1D angle1 = new Static1D(-ANGLE);
+    Static1D angle2 = new Static1D(+ANGLE);
+    Static3D axisX  = new Static3D(1,0,0);
+    Static3D axisY  = new Static3D(0,1,0);
+    Static3D axisZ  = new Static3D(0,-1,1);
+    Static3D center0= new Static3D(0,0,0);
+    Static3D center1= new Static3D(0,-3*F,0);
+
+    VertexEffect[] effect = new VertexEffect[10];
+
+    effect[0] = new VertexEffectScale ( new Static3D(3,3,3) );
+    effect[1] = new VertexEffectMove  ( new Static3D(0,-F,0) );
+    effect[2] = new VertexEffectRotate( new Static1D(90), axisX, center0 );
+    effect[3] = new VertexEffectScale ( new Static3D(1,-1,1) );
+    effect[4] = new VertexEffectMove  ( new Static3D(3*E/2,E*(SQ3/2)-3*F,0) );
+    effect[5] = new VertexEffectRotate( new Static1D(+90), axisY, center1 );
+    effect[6] = new VertexEffectScale ( new Static3D(-1,1,1) );
+    effect[7] = new VertexEffectRotate( new Static1D( 45), axisX, center1 );
+    effect[8] = new VertexEffectRotate( angle1           , axisZ, center1 );
+    effect[9] = new VertexEffectRotate( angle2           , axisZ, center1 );
+
+    effect[0].setMeshAssociation(15,-1);  // apply to meshes 0,1,2,3
+    effect[1].setMeshAssociation( 3,-1);  // apply to meshes 0,1
+    effect[2].setMeshAssociation( 2,-1);  // apply to mesh 1
+    effect[3].setMeshAssociation( 2,-1);  // apply to mesh 0
+    effect[4].setMeshAssociation(12,-1);  // apply to meshes 2,3
+    effect[5].setMeshAssociation(12,-1);  // apply to meshes 2,3
+    effect[6].setMeshAssociation( 8,-1);  // apply to mesh 3
+    effect[7].setMeshAssociation(12,-1);  // apply to meshes 2,3
+    effect[8].setMeshAssociation( 4,-1);  // apply to mesh 2
+    effect[9].setMeshAssociation( 8,-1);  // apply to mesh 3
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsHelicopterCorner()
+    {
+    float E = 0.5f;
+
+    Static3D axisX  = new Static3D(1,0,0);
+    Static3D axisY  = new Static3D(0,1,0);
+    Static3D axis0  = new Static3D(-SQ2/2,0,SQ2/2);
+    Static3D axis1  = new Static3D(+SQ3/3,+SQ3/3,+SQ3/3);
+    Static1D angle1 = new Static1D(+90);
+    Static1D angle2 = new Static1D(-90);
+    Static1D angle3 = new Static1D(-135);
+    Static1D angle4 = new Static1D(90);
+    Static1D angle5 = new Static1D(120);
+    Static1D angle6 = new Static1D(240);
+    Static3D center1= new Static3D(0,0,0);
+    Static3D center2= new Static3D(-0.25f,-0.25f,-0.25f);
+    Static3D move1  = new Static3D(-E/4,-E/4,0);
+    Static3D move2  = new Static3D(-0.25f,(-1.0f/6)-0.25f,-0.25f);
+
+    VertexEffect[] effect = new VertexEffect[10];
+
+    effect[0] = new VertexEffectMove(move1);
+    effect[1] = new VertexEffectScale(new Static3D(1,1,-1));
+    effect[2] = new VertexEffectRotate(angle1,axisX,center1);
+    effect[3] = new VertexEffectRotate(angle2,axisY,center1);
+    effect[4] = new VertexEffectMove(move2);
+    effect[5] = new VertexEffectRotate(angle1,axisX,center2);
+    effect[6] = new VertexEffectRotate(angle3,axisY,center2);
+    effect[7] = new VertexEffectRotate(angle4,axis0,center2);
+    effect[8] = new VertexEffectRotate(angle5,axis1,center2);
+    effect[9] = new VertexEffectRotate(angle6,axis1,center2);
+
+    effect[0].setMeshAssociation( 7,-1);  // meshes 0,1,2
+    effect[1].setMeshAssociation( 6,-1);  // meshes 1,2
+    effect[2].setMeshAssociation( 2,-1);  // mesh 1
+    effect[3].setMeshAssociation( 4,-1);  // mesh 2
+    effect[4].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[5].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[6].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[7].setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect[8].setMeshAssociation(16,-1);  // mesh 4
+    effect[9].setMeshAssociation(32,-1);  // mesh 5
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsHelicopterFace()
+    {
+    float E = 0.5f;
+    float F = SQ2/4;
+
+    Static3D move0  = new Static3D(-E/4, -E/4, 0);
+    Static3D move1  = new Static3D(-(SQ2/24)-E/2, -(SQ2/24)-E/2, 0);
+    Static3D move2  = new Static3D(-E/2, F/3, 0);
+    Static3D move3  = new Static3D(+E/2, F/3, 0);
+    Static3D move4  = new Static3D(+E/3,+E/3, 0);
+    Static1D angle1 = new Static1D(135);
+    Static1D angle2 = new Static1D(90);
+    Static1D angle3 = new Static1D(-90);
+    Static1D angle4 = new Static1D(-135);
+    Static3D axisX  = new Static3D(1,0,0);
+    Static3D axisY  = new Static3D(0,1,0);
+    Static3D axisZ  = new Static3D(0,0,1);
+    Static3D axis1  = new Static3D(1,-1,0);
+    Static3D center = new Static3D(0,0,0);
+    Static3D center1= new Static3D(-E/2,-E/2,0);
+
+    VertexEffect[] effect = new VertexEffect[10];
+
+    effect[0] = new VertexEffectMove(move0);
+    effect[1] = new VertexEffectRotate(angle1, axisZ, center);
+    effect[2] = new VertexEffectMove(move1);
+    effect[3] = new VertexEffectRotate(angle2, axis1, center1);
+    effect[4] = new VertexEffectMove(move2);
+    effect[5] = new VertexEffectMove(move3);
+    effect[6] = new VertexEffectRotate(angle3, axisZ, center);
+    effect[7] = new VertexEffectRotate(angle4, axisX, center);
+    effect[8] = new VertexEffectRotate(angle1, axisY, center);
+    effect[9] = new VertexEffectMove(move4);
+
+    effect[0].setMeshAssociation( 1,-1);  // mesh 0
+    effect[1].setMeshAssociation( 2,-1);  // mesh 1
+    effect[2].setMeshAssociation( 2,-1);  // mesh 1
+    effect[3].setMeshAssociation( 2,-1);  // mesh 1
+    effect[4].setMeshAssociation( 4,-1);  // mesh 2
+    effect[5].setMeshAssociation( 8,-1);  // mesh 3
+    effect[6].setMeshAssociation( 8,-1);  // mesh 3
+    effect[7].setMeshAssociation( 4,-1);  // mesh 2
+    effect[8].setMeshAssociation( 8,-1);  // mesh 3
+    effect[9].setMeshAssociation(15,-1);  // meshes 0,1,2,3
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsRediEdge()
+    {
+    Static3D move0 = new Static3D(0.0f, -0.5f, 0.0f);
+    Static3D move1 = new Static3D(0.25f, -0.25f, 0.0f);
+    Static3D move2 = new Static3D(0.5f, 0.0f, 0.0f);
+    Static3D move3 = new Static3D(0.0f, (SQ3-6)/8, (SQ3-6)/8);
+    Static3D flipZ = new Static3D(1,1,-1);
+    Static3D flipX = new Static3D(-1,1,1);
+    Static3D scale = new Static3D(2,2,2);
+    Static3D cent0 = new Static3D(0,0, 0);
+    Static3D cent1 = new Static3D(0,0, -1.5f);
+    Static3D axisX = new Static3D(1,0, 0);
+    Static3D axisY = new Static3D(0,1, 0);
+    Static3D axis  = new Static3D(0,SQ2/2,-SQ2/2);
+    Static1D angle1= new Static1D(90);
+    Static1D angle2= new Static1D(45);
+    Static1D angle3= new Static1D( (float)(180/Math.PI*Math.acos(SQ3/3)) );
+
+    VertexEffect[] effect = new VertexEffect[12];
+
+    effect[0] = new VertexEffectScale(scale);
+    effect[1] = new VertexEffectMove(move0);
+    effect[2] = new VertexEffectScale(flipZ);
+    effect[3] = new VertexEffectRotate(angle1,axisX,cent0);
+    effect[4] = new VertexEffectMove(move1);
+    effect[5] = new VertexEffectRotate(angle1,axisY,cent0);
+    effect[6] = new VertexEffectMove(move2);
+    effect[7] = new VertexEffectScale(flipX);
+    effect[8] = new VertexEffectRotate(angle2,axisX,cent0);
+    effect[9] = new VertexEffectMove(move3);
+    effect[10]= new VertexEffectRotate(angle3,axis ,cent1);
+    effect[11]= new VertexEffectScale(flipX);
+
+    effect[0].setMeshAssociation(63,-1);  // meshes 0,1,2,3,4,5
+    effect[1].setMeshAssociation( 3,-1);  // meshes 0,1
+    effect[2].setMeshAssociation( 2,-1);  // mesh 1
+    effect[3].setMeshAssociation( 2,-1);  // mesh 1
+    effect[4].setMeshAssociation(12,-1);  // meshes 2,3
+    effect[5].setMeshAssociation(60,-1);  // meshes 2,3,4,5
+    effect[6].setMeshAssociation(12,-1);  // meshes 2,3
+    effect[7].setMeshAssociation( 8,-1);  // mesh 3
+    effect[8].setMeshAssociation(48,-1);  // meshes 4,5
+    effect[9].setMeshAssociation(48,-1);  // meshes 4,5
+    effect[10].setMeshAssociation(48,-1); // meshes 4,5
+    effect[11].setMeshAssociation(32,-1); // mesh 5
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsRediCorner()
+    {
+    Static3D axisY   = new Static3D(0,1,0);
+    Static3D axisX   = new Static3D(1,0,0);
+    Static3D axisZ   = new Static3D(0,0,1);
+    Static3D center  = new Static3D(0,0,0);
+    Static1D angle90 = new Static1D(90);
+    Static1D angle270= new Static1D(270);
+    Static1D angle45 = new Static1D(-45);
+
+    VertexEffect[] effect = new VertexEffect[6];
+
+    effect[0] = new VertexEffectMove(new Static3D(0,0,+0.5f));
+    effect[1] = new VertexEffectRotate( angle270, axisX, center );
+    effect[2] = new VertexEffectRotate( angle90 , axisY, center );
+    effect[3] = new VertexEffectRotate( angle45 , axisX, center );
+    effect[4] = new VertexEffectRotate( angle90 , axisY, center );
+    effect[5] = new VertexEffectRotate( angle270, axisZ, center );
+
+    effect[0].setMeshAssociation( 7,-1);  // 0,1,2
+    effect[1].setMeshAssociation( 2,-1);  // 1
+    effect[2].setMeshAssociation( 4,-1);  // 2
+    effect[3].setMeshAssociation(56,-1);  // 3
+    effect[4].setMeshAssociation(16,-1);  // 4
+    effect[5].setMeshAssociation(32,-1);  // 5
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// OBJECTS
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// CUBE
+
+  MeshBase createCubeMesh(int index)
+    {
+    MeshBase mesh = createFacesCube(index);
+    VertexEffect[] effects = createVertexEffectsCube();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    Static3D roundingCenter  = new Static3D(0,0,0);
+    Static3D[] vertices = new Static3D[8];
+    vertices[0] = new Static3D(+0.5f,+0.5f,+0.5f);
+    vertices[1] = new Static3D(+0.5f,+0.5f,-0.5f);
+    vertices[2] = new Static3D(+0.5f,-0.5f,+0.5f);
+    vertices[3] = new Static3D(+0.5f,-0.5f,-0.5f);
+    vertices[4] = new Static3D(-0.5f,+0.5f,+0.5f);
+    vertices[5] = new Static3D(-0.5f,+0.5f,-0.5f);
+    vertices[6] = new Static3D(-0.5f,-0.5f,+0.5f);
+    vertices[7] = new Static3D(-0.5f,-0.5f,-0.5f);
+    roundCorners(mesh,roundingCenter,vertices,0.06f,0.12f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// SKEWB
+
+  MeshBase createSkewbCornerMesh()
+    {
+    MeshBase mesh = createFacesSkewbCorner();
+    VertexEffect[] effects = createVertexEffectsSkewbCorner();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    float E = 0.5f;
+    Static3D roundingCenter = new Static3D(-E/2,-E/2,-E/2);
+
+    Static3D[] verticesType1 = new Static3D[1];
+    verticesType1[0] = new Static3D(0.0f,0.0f,0.0f);
+    roundCorners(mesh,roundingCenter,verticesType1,0.08f,0.15f);
+
+    Static3D[] verticesType2 = new Static3D[3];
+    verticesType2[0] = new Static3D(-E, 0, 0);
+    verticesType2[1] = new Static3D( 0,-E, 0);
+    verticesType2[2] = new Static3D( 0, 0,-E);
+    roundCorners(mesh,roundingCenter,verticesType2,0.08f,0.20f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createSkewbFaceMesh()
+    {
+    MeshBase mesh = createFacesSkewbFace();
+    VertexEffect[] effects = createVertexEffectsSkewbFace();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    Static3D roundingCenter = new Static3D(0,0,-0.2f);
+    float E = SQ2/4;
+    Static3D[] vertices = new Static3D[4];
+    vertices[0] = new Static3D(-E*SQ2,      0, 0);
+    vertices[1] = new Static3D(+E*SQ2,      0, 0);
+    vertices[2] = new Static3D(     0, -E*SQ2, 0);
+    vertices[3] = new Static3D(     0, +E*SQ2, 0);
+    roundCorners(mesh,roundingCenter,vertices,0.10f,0.10f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// SKEWB DIAMOND / PYRAMINX
+
+  MeshBase createOctaMesh()
+    {
+    MeshBase mesh = createFacesOcta();
+    VertexEffect[] effects = createVertexEffectsOcta();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    Static3D roundingCenter = new Static3D(0,0,0);
+    Static3D[] vertices = new Static3D[6];
+    vertices[0] = new Static3D(    0, SQ2/2,    0 );
+    vertices[1] = new Static3D( 0.5f,     0, 0.5f );
+    vertices[2] = new Static3D(-0.5f,     0, 0.5f );
+    vertices[3] = new Static3D(    0,-SQ2/2,    0 );
+    vertices[4] = new Static3D(-0.5f,     0,-0.5f );
+    vertices[5] = new Static3D( 0.5f,     0,-0.5f );
+    roundCorners(mesh,roundingCenter,vertices,0.06f,0.20f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createTetraMesh()
+    {
+    MeshBase mesh = createFacesTetra();
+    VertexEffect[] effects = createVertexEffectsTetra();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    Static3D roundingCenter = new Static3D(0,0,0);
+    Static3D[] verticesRound = new Static3D[4];
+    verticesRound[0] = new Static3D(-0.5f,+SQ2/4,   0 );
+    verticesRound[1] = new Static3D(+0.5f,+SQ2/4,   0 );
+    verticesRound[2] = new Static3D(    0,-SQ2/4,+0.5f);
+    verticesRound[3] = new Static3D(    0,-SQ2/4,-0.5f);
+    roundCorners(mesh,roundingCenter,verticesRound,0.08f,0.15f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// DINO
+
+  MeshBase createDinoMesh()
+    {
+    MeshBase mesh = createFacesDino();
+    VertexEffect[] effects = createVertexEffectsDino();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    float F = 0.5f;
+    Static3D roundingCenter = new Static3D(0.0f, -1.5f*F, -1.5f*F);
+    Static3D[] verticesRound = new Static3D[4];
+    verticesRound[0] = new Static3D(     0,-3*F,    0 );
+    verticesRound[1] = new Static3D(     0,   0, -3*F );
+    verticesRound[2] = new Static3D(  -3*F,   0,    0 );
+    verticesRound[3] = new Static3D(  +3*F,   0,    0 );
+    roundCorners(mesh,roundingCenter,verticesRound,0.10f,0.40f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Helicopter
+
+  MeshBase createHelicopterCornerMesh()
+    {
+    MeshBase mesh = createFacesHelicopterCorner();
+    VertexEffect[] effects = createVertexEffectsHelicopterCorner();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    float E = 0.5f;
+    Static3D roundingCenter = new Static3D(-E/2,-E/2,-E/2);
+
+    Static3D[] verticesType1 = new Static3D[1];
+    verticesType1[0] = new Static3D(0.0f,0.0f,0.0f);
+    roundCorners(mesh,roundingCenter,verticesType1,0.08f,0.15f);
+
+    Static3D[] verticesType2 = new Static3D[3];
+    verticesType2[0] = new Static3D(-E, 0, 0);
+    verticesType2[1] = new Static3D( 0,-E, 0);
+    verticesType2[2] = new Static3D( 0, 0,-E);
+    roundCorners(mesh,roundingCenter,verticesType2,0.10f,0.20f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createHelicopterFaceMesh()
+    {
+    MeshBase mesh = createFacesHelicopterFace();
+    VertexEffect[] effects = createVertexEffectsHelicopterFace();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    float E = 0.5f;
+    Static3D roundingCenter = new Static3D(-E/2 + E/3,-E/2 + E/3,-E/2);
+
+    Static3D[] verticesType1 = new Static3D[1];
+    verticesType1[0] = new Static3D(E/3,E/3,0.0f);
+    roundCorners(mesh,roundingCenter,verticesType1,0.06f,0.15f);
+
+    Static3D[] verticesType2 = new Static3D[2];
+    verticesType2[0] = new Static3D(-E+E/3, E/3  , 0);
+    verticesType2[1] = new Static3D( E/3  ,-E+E/3, 0);
+    roundCorners(mesh,roundingCenter,verticesType2,0.10f,0.20f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Redi cube
+
+  MeshBase createRediEdgeMesh()
+    {
+    MeshBase mesh = createFacesRediEdge();
+    VertexEffect[] effects = createVertexEffectsRediEdge();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    Static3D center = new Static3D(0.0f,-0.75f,-0.75f);
+    Static3D[] vertices = new Static3D[4];
+    vertices[0] = new Static3D( 0.5f, 0.0f, 0.0f);
+    vertices[1] = new Static3D(-0.5f, 0.0f, 0.0f);
+    vertices[2] = new Static3D(0.0f, 0.0f,-1.5f);
+    vertices[3] = new Static3D(0.0f,-1.5f, 0.0f);
+
+    roundCorners(mesh,center,vertices,0.06f,0.20f);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createRediCornerMesh()
+    {
+    MeshBase mesh = createFacesRediCorner();
+    VertexEffect[] effects = createVertexEffectsRediCorner();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    Static3D center = new Static3D(0,0,0);
+    Static3D[] vertices = new Static3D[8];
+    vertices[0] = new Static3D(+0.5f,+0.5f,+0.5f);
+    vertices[1] = new Static3D(+0.5f,+0.5f,-0.5f);
+    vertices[2] = new Static3D(+0.5f,-0.5f,+0.5f);
+    vertices[3] = new Static3D(+0.5f,-0.5f,-0.5f);
+    vertices[4] = new Static3D(-0.5f,+0.5f,+0.5f);
+    vertices[5] = new Static3D(-0.5f,+0.5f,-0.5f);
+    vertices[6] = new Static3D(-0.5f,-0.5f,+0.5f);
+    vertices[7] = new Static3D(-0.5f,-0.5f,-0.5f);
+
+    roundCorners(mesh,center,vertices,0.06f,0.12f);
+
+    return mesh;
+    }
+  }
diff --git a/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java b/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
index c10dbe5..6a97bcb 100644
--- a/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
+++ b/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
@@ -32,19 +32,14 @@ import org.distorted.library.effect.MatrixEffectQuaternion;
 import org.distorted.library.effect.MatrixEffectScale;
 import org.distorted.library.effect.VertexEffectDeform;
 import org.distorted.library.effect.VertexEffectDisappear;
-import org.distorted.library.effect.VertexEffectMove;
 import org.distorted.library.effect.VertexEffectRotate;
-import org.distorted.library.effect.VertexEffectScale;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.mesh.MeshFile;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.type.DynamicQuat;
-import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
@@ -61,6 +56,8 @@ import static org.distorted.examples.meshfile.MeshFileActivity.PROCEDURAL;
 
 class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.ExceptionListener
 {
+    private static final float SQ2 = (float)Math.sqrt(2);
+    private static final float SQ3 = (float)Math.sqrt(3);
     private final float DEFAULT_SCALE = 0.3f;
 
     private GLSurfaceView mView;
@@ -137,7 +134,7 @@ class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.Excep
       VertexEffectRotate.enable();
       VertexEffectDeform.enable();
 
-      DistortedLibrary.setMax(EffectType.VERTEX, 14);
+      DistortedLibrary.setMax(EffectType.VERTEX, 16);
 
       DistortedLibrary.onCreate(mView.getContext(), this);
       }
@@ -199,293 +196,59 @@ class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.Excep
 
     private Bitmap createTexture(int resourceID)
       {
+      TextureFactory factory = TextureFactory.getInstance();
+
+      float[] vertices;
+      int[] colors;
+      float F = 0.5f;
+      float E = SQ3/2;
+      float G = SQ2/4;
+
       switch(resourceID)
           {
           case  R.raw.deferredjob:
-          case  R.raw.meshjoin   : final int[] TET_COLORS4 = new int[] { 0xffffff00,
-                                                                         0xff00ff00,
-                                                                         0xff0000ff,
-                                                                         0xffff0000 };
-                                   return createTetrahedronTexture(TET_COLORS4);
+          case  R.raw.meshjoin   :
           case  PROCEDURAL       :
-          case  R.raw.predeform  : return createGridTexture(3);
+          case  R.raw.predeform  : return createWhiteTexture();
+
           case  R.raw.cube2      :
           case  R.raw.cube3      :
           case  R.raw.cube4      :
-          case  R.raw.cube5      : final int[] CUBE_COLORS = new int[] { 0xffffff00,
-                                                                         0xffffffff,
-                                                                         0xff0000ff,
-                                                                         0xff00ff00,
-                                                                         0xffff0000,
-                                                                         0xffb5651d };
-                                   return createCubeTexture(CUBE_COLORS);
+          case  R.raw.cube5      : colors = new int[] { 0xffffff00, 0xffffffff, 0xff0000ff, 0xff00ff00, 0xffff0000, 0xffb5651d };
+                                   vertices = new float[] { -F,-F, +F,-F, +F,+F, -F,+F};
+                                   return factory.createTexture(vertices,colors,0.10f, 0.10f, true);
           case  R.raw.pyra3      :
           case  R.raw.pyra4      :
-          case  R.raw.pyra5      : final int[] TET_COLORS5 = new int[] { 0xffffff00,
-                                                                         0xff00ff00,
-                                                                         0xff0000ff,
-                                                                         0xffff0000,
-                                                                         0xff000000 };
-                                   return createTetrahedronTexture(TET_COLORS5);
-          case  R.raw.dino       : final int[] DINO_COLORS = new int[] { 0xffffff00,
-                                                                         0xffffffff,
-                                                                         0xff0000ff,
-                                                                         0xff00ff00,
-                                                                         0xffff0000,
-                                                                         0xffb5651d };
-                                   return createDinoTexture(DINO_COLORS);
-          case R.raw.skewb       : final int[] SKEW_COLORS = new int[] { 0xffffff00,
-                                                                         0xffffffff,
-                                                                         0xff0000ff,
-                                                                         0xff00ff00,
-                                                                         0xffff0000,
-                                                                         0xffb5651d };
-                                   return createSkewbTexture(SKEW_COLORS);
-          }
-
-      return null;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+          case  R.raw.pyra5      : colors = new int[] { 0xffffff00, 0xff00ff00, 0xff0000ff, 0xffff0000 };
+                                   vertices = new float[] { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
+                                   return factory.createTexture(vertices,colors,0.05f, 0.05f, true);
 
-    private Bitmap createCubeTexture(int[] faceColors)
-      {
-      final int FACES=faceColors.length;
-      int SIZE = 200;
-      final float R = SIZE*0.10f;
-      final float M = SIZE*0.05f;
-
-      Bitmap bitmap;
-      Paint paint = new Paint();
-      bitmap = Bitmap.createBitmap( (FACES+1)*SIZE, SIZE, Bitmap.Config.ARGB_8888);
-      Canvas canvas = new Canvas(bitmap);
+          case  R.raw.dino       : colors = new int[] { 0xffffff00, 0xffffffff, 0xff0000ff, 0xff00ff00, 0xffff0000, 0xffb5651d };
+                                   vertices = new float[] { -F,F/3, 0,-2*F/3, +F,F/3 };
+                                   return factory.createTexture(vertices,colors,0.05f, 0.03f, true);
 
-      paint.setStyle(Paint.Style.FILL);
-      paint.setColor(0xff000000);
-      canvas.drawRect(0, 0, (FACES+1)*SIZE, SIZE, paint);
+          case R.raw.skewb       : colors = new int[] { 0xffffff00, 0xffffffff, 0xff0000ff, 0xff00ff00, 0xffff0000, 0xffb5651d };
+                                   //vertices = new float[] { -G,-G, +G,-G, +G,+G, -G,+G };
 
-      for(int face=0; face<FACES; face++)
-        {
-        paint.setColor(faceColors[face]);
-        canvas.drawRoundRect( face*SIZE+M, M, (face+1)*SIZE-M, SIZE-M, R, R, paint);
-        }
+                                   vertices = new float[] { -F+F/4,F/4, F/4,-F+F/4, F/4,F/4};
+                                   return factory.createTexture(vertices,colors,0.05f, 0.08f, true);
+          }
 
-      return bitmap;
+      return null;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private Bitmap createGridTexture(int lines)
+    private Bitmap createWhiteTexture()
       {
-      int SIZE = 200;
+      int SIZE = 1;
       Bitmap bitmap = Bitmap.createBitmap(SIZE,SIZE, Bitmap.Config.ARGB_8888);
       Canvas canvas = new Canvas(bitmap);
 
       Paint paint = new Paint();
-      paint.setColor(0xff008800);
-      paint.setStyle(Paint.Style.FILL);
-      canvas.drawRect(0, 0, SIZE, SIZE, paint);
       paint.setColor(0xffffffff);
-
-      for(int i=0; i<=lines ; i++ )
-        {
-        int x = (SIZE*i)/lines;
-        canvas.drawRect(x-1,   0,  x+1, SIZE, paint);
-        canvas.drawRect(  0, x-1, SIZE,  x+1, paint);
-        }
-
-      return bitmap;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private Bitmap createTetrahedronTexture(int[] faceColors)
-      {
-      final float SQ3 = (float)Math.sqrt(3);
-      final int FACES=faceColors.length;
-      int SIZE = 256;
-      float STROKE = 0.05f*SIZE;
-      float OFF = STROKE/2 -1;
-      float OFF2 = 0.5f*SIZE + OFF;
-      float HEIGHT = SIZE - OFF;
-      float RADIUS = SIZE/12.0f;
-      float ARC1_H = 0.2f*SIZE;
-      float ARC1_W = SIZE*0.5f;
-      float ARC2_W = 0.153f*SIZE;
-      float ARC2_H = 0.905f*SIZE;
-      float ARC3_W = SIZE-ARC2_W;
-
-      float M = SQ3/2;
-      float D = (M/2 - 0.51f)*SIZE;
-
-      Bitmap result = Bitmap.createBitmap(FACES*SIZE,SIZE, Bitmap.Config.ARGB_8888);
-      Canvas canvas = new Canvas(result);
-      Paint paint = new Paint();
-      paint.setAntiAlias(true);
-      paint.setStrokeWidth(STROKE);
-
-      for(int i=0; i<FACES; i++)
-        {
-        paint.setColor(faceColors[i]);
-        paint.setStyle(Paint.Style.FILL);
-
-        canvas.drawRect(i*SIZE,0,(i+1)*SIZE,SIZE,paint);
-
-        paint.setColor(0xff000000);
-        paint.setStyle(Paint.Style.STROKE);
-
-        canvas.drawLine(           i*SIZE, M*HEIGHT+D,  SIZE       +i*SIZE, M*HEIGHT+D, paint);
-        canvas.drawLine(      OFF +i*SIZE, M*SIZE  +D,       OFF2  +i*SIZE,          D, paint);
-        canvas.drawLine((SIZE-OFF)+i*SIZE, M*SIZE  +D, (SIZE-OFF2) +i*SIZE,          D, paint);
-
-        canvas.drawArc( ARC1_W-RADIUS+i*SIZE, M*(ARC1_H-RADIUS)+D, ARC1_W+RADIUS+i*SIZE, M*(ARC1_H+RADIUS)+D, 225, 90, false, paint);
-        canvas.drawArc( ARC2_W-RADIUS+i*SIZE, M*(ARC2_H-RADIUS)+D, ARC2_W+RADIUS+i*SIZE, M*(ARC2_H+RADIUS)+D, 105, 90, false, paint);
-        canvas.drawArc( ARC3_W-RADIUS+i*SIZE, M*(ARC2_H-RADIUS)+D, ARC3_W+RADIUS+i*SIZE, M*(ARC2_H+RADIUS)+D, 345, 90, false, paint);
-        }
-
-      return result;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void createSkewbFaceTexture(Canvas canvas, Paint paint, int[] faceColors, int face, int left, int top, int side)
-    {
-    final int COLORS = 6;
-    final float SQ2 = (float)Math.sqrt(2);
-
-    if( face<COLORS )
-      {
-      float STROKE = 0.035f*side;
-      float L= left+0.125f*side;
-      float H= 0.375f*side;
-      float LEN = 0.5f*side;
-
-      paint.setAntiAlias(true);
-      paint.setStrokeWidth(STROKE);
-      paint.setColor(faceColors[face]);
       paint.setStyle(Paint.Style.FILL);
-
-      canvas.drawRect(left,top,left+side,top+side,paint);
-
-      paint.setColor(0xff000000);
-      paint.setStyle(Paint.Style.STROKE);
-
-      canvas.drawLine( L    , H,  L+LEN, H    , paint);
-      canvas.drawLine( L    , H,  L+LEN, H+LEN, paint);
-      canvas.drawLine( L+LEN, H,  L+LEN, H+LEN, paint);
-
-      float S1 = 0.125f*side;
-      float S2 = 0.070f*side;
-      float X  = 0.7f*S2;
-
-      float LA = left+0.625f*side;
-      float RA = left+0.125f*side;
-      float TA = 0.375f*side;
-      float BA = 0.875f*side;
-
-      canvas.drawArc( LA-S1, TA     , LA     , TA+S1, 270, 90, false, paint);
-      canvas.drawArc( RA+X , TA     , RA+X+S2, TA+S2, 135,135, false, paint);
-      canvas.drawArc( LA-S2, BA-X-S2, LA     , BA-X ,   0,135, false, paint);
-      }
-    else
-      {
-      final float R = (SQ2/2)*side*0.10f;
-      final float M = side*(0.5f-SQ2/4+0.018f);
-
-      paint.setColor(faceColors[face-COLORS]);
-      paint.setStyle(Paint.Style.FILL);
-      canvas.drawRoundRect( left+M, top+M, left+side-M, top+side-M, R, R, paint);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private Bitmap createSkewbTexture(int[] faceColors)
-      {
-      final int TEXTURE_HEIGHT = 256;
-      final int NUM_TEXTURES = 2*6;
-      Bitmap bitmap;
-
-      Paint paint = new Paint();
-      bitmap = Bitmap.createBitmap( (NUM_TEXTURES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
-      Canvas canvas = new Canvas(bitmap);
-
-      paint.setAntiAlias(true);
-      paint.setTextAlign(Paint.Align.CENTER);
-      paint.setStyle(Paint.Style.FILL);
-
-      paint.setColor(0xff000000);
-      canvas.drawRect(0, 0, (NUM_TEXTURES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
-
-      for(int i=0; i<NUM_TEXTURES; i++)
-        {
-        createSkewbFaceTexture(canvas, paint, faceColors, i, i*TEXTURE_HEIGHT, 0, TEXTURE_HEIGHT);
-        }
-
-      return bitmap;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void createDinoFaceTexture(Canvas canvas, Paint paint, int[] faceColors, int face, int left, int top, int side)
-    {
-    float STROKE = 0.04f*side;
-    float L= left;
-    float H= 0.333f*side;
-    float LEN = 0.5f*side;
-
-    paint.setAntiAlias(true);
-    paint.setStrokeWidth(STROKE);
-    paint.setColor(faceColors[face]);
-    paint.setStyle(Paint.Style.FILL);
-
-    canvas.drawRect(left,top,left+side,top+side,paint);
-
-    paint.setColor(0xff000000);
-    paint.setStyle(Paint.Style.STROKE);
-
-    canvas.drawLine( L      , H,  L+2*LEN, H    , paint);
-    canvas.drawLine( L      , H,  L+  LEN, H+LEN, paint);
-    canvas.drawLine( L+2*LEN, H,  L+  LEN, H+LEN, paint);
-
-    float S1 = 0.150f*side;
-    float S2 = 0.090f*side;
-    float X  = 0.7f*S2;
-    float Y  = 0.2f*S1;
-
-    float LA = left+0.500f*side;
-    float RA = left;
-    float TA = 0.333f*side;
-    float BA = 0.833f*side;
-
-    canvas.drawArc( RA+X        , TA     , RA+X+S2  , TA+S2, 135,135, false, paint);
-    canvas.drawArc( RA+side-S2-X, TA     , RA+side-X, TA+S2, 270,135, false, paint);
-    canvas.drawArc( LA-S1/2     , BA-S1-Y, LA+S1/2  , BA-Y ,  45, 90, false, paint);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private Bitmap createDinoTexture(int[] faceColors)
-      {
-      final int TEXTURE_HEIGHT = 256;
-      final int NUM_TEXTURES = 6;
-      Bitmap bitmap;
-
-      Paint paint = new Paint();
-      bitmap = Bitmap.createBitmap( (NUM_TEXTURES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
-      Canvas canvas = new Canvas(bitmap);
-
-      paint.setAntiAlias(true);
-      paint.setTextAlign(Paint.Align.CENTER);
-      paint.setStyle(Paint.Style.FILL);
-
-      paint.setColor(0xff000000);
-      canvas.drawRect(0, 0, (NUM_TEXTURES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
-
-      for(int i=0; i<NUM_TEXTURES; i++)
-        {
-        createDinoFaceTexture(canvas, paint, faceColors, i, i*TEXTURE_HEIGHT, 0, TEXTURE_HEIGHT);
-        }
+      canvas.drawRect(0, 0, SIZE, SIZE, paint);
 
       return bitmap;
       }
@@ -494,130 +257,8 @@ class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.Excep
 
   private MeshBase createStaticMesh()
     {
-    final float SQ2 = (float)Math.sqrt(2);
-    final float SQ3 = (float)Math.sqrt(3);
-    final float ANGLE_FACES = (float)((180/Math.PI)*(Math.atan(SQ2)));
-
-    final int MESHES=4;
-
-    float D = 0.02f;
-    float E = 0.5f*SQ2;
-    float F = 0.5f;
-
-    float[] bands0 = { 1.0f    , 0,
-                       1.0f-2*D, D*0.25f,
-                       1.0f-4*D, D*0.35f,
-                       1.0f-8*D, D*0.6f,
-                       0.60f   , D*1.0f,
-                       0.30f   , D*1.375f,
-                       0.0f    , D*1.4f };
-
-    float[] vertices0 = { -F,F/3, 0,-2*F/3, +F,F/3 };
-
-    MeshBase[] meshes = new MeshPolygon[MESHES];
-    meshes[0] = new MeshPolygon(vertices0, bands0, 2, 5);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0,2,0);
-
-    float[] bands1 = { 1.0f    , 0,
-                       0.50f   , 0,
-                       0.0f    , 0 };
-
-    float[] vertices1 = { -E/2,-E*(SQ3/6), E/2,-E*(SQ3/6), 0,E*(SQ3/3) };
-
-    meshes[2] = new MeshPolygon(vertices1, bands1, 1, 2);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
-
-    MeshBase result = new MeshJoined(meshes);
-
-    Static3D a0 = new Static3D(     0,-3*F,    0 );
-    Static3D a1 = new Static3D(     0,   0, -3*F );
-    Static3D a2 = new Static3D(  -3*F,   0,    0 );
-    Static3D a3 = new Static3D(  +3*F,   0,    0 );
-
-    Static3D v0 = new Static3D(     0,-3*F/2, 3*F/2 );
-    Static3D v1 = new Static3D(     0, 3*F/2,-3*F/2 );
-    Static3D v2 = new Static3D(  -3*F, 3*F/2, 3*F/2 );
-    Static3D v3 = new Static3D(  +3*F, 3*F/2, 3*F/2 );
-
-    float d1 = 1.0f;
-    float d2 =-0.10f;
-    float d3 =-0.10f;
-    float d4 = 0.40f;
-
-    Static3D dCen0 = new Static3D( d1*a0.get0(), d1*a0.get1(), d1*a0.get2() );
-    Static3D dCen1 = new Static3D( d1*a1.get0(), d1*a1.get1(), d1*a1.get2() );
-    Static3D dCen2 = new Static3D( d1*a2.get0(), d1*a2.get1(), d1*a2.get2() );
-    Static3D dCen3 = new Static3D( d1*a3.get0(), d1*a3.get1(), d1*a3.get2() );
-
-    Static3D dVec0 = new Static3D( d3*v0.get0(), d3*v0.get1(), d3*v0.get2() );
-    Static3D dVec1 = new Static3D( d3*v1.get0(), d3*v1.get1(), d3*v1.get2() );
-    Static3D dVec2 = new Static3D( d2*v2.get0(), d2*v2.get1(), d2*v2.get2() );
-    Static3D dVec3 = new Static3D( d2*v3.get0(), d2*v3.get1(), d2*v3.get2() );
-
-    Static4D dReg  = new Static4D(0,0,0,d4);
-    Static1D dRad  = new Static1D(1);
-
-    Static1D angle1 = new Static1D(+ANGLE_FACES);
-    Static1D angle2 = new Static1D(-ANGLE_FACES);
-
-    Static3D axisX  = new Static3D(1,0,0);
-    Static3D axisY  = new Static3D(0,1,0);
-    Static3D axisZ  = new Static3D(0,-1,1);
-
-    Static3D center0= new Static3D(0,0,0);
-    Static3D center1= new Static3D(0,-3*F,0);
-
-    VertexEffectScale   effect0 = new VertexEffectScale ( new Static3D(3,3,3) );
-    VertexEffectMove    effect1 = new VertexEffectMove  ( new Static3D(0,-F,0) );
-    VertexEffectRotate  effect2 = new VertexEffectRotate( new Static1D(90), axisX, center0 );
-    VertexEffectScale   effect3 = new VertexEffectScale ( new Static3D(1,-1,1) );
-    VertexEffectMove    effect4 = new VertexEffectMove  ( new Static3D(3*E/2,E*(SQ3/2)-3*F,0) );
-    VertexEffectRotate  effect5 = new VertexEffectRotate( new Static1D(+90), axisY, center1 );
-    VertexEffectScale   effect6 = new VertexEffectScale ( new Static3D(-1,1,1) );
-    VertexEffectRotate  effect7 = new VertexEffectRotate( new Static1D( 45), axisX, center1 );
-    VertexEffectRotate  effect8 = new VertexEffectRotate( angle1           , axisZ, center1 );
-    VertexEffectRotate  effect9 = new VertexEffectRotate( angle2           , axisZ, center1 );
-
-    VertexEffectDeform  effect10= new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
-    VertexEffectDeform  effect11= new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
-    VertexEffectDeform  effect12= new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
-    VertexEffectDeform  effect13= new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
-
-    effect0.setMeshAssociation(15,-1);  // apply to meshes 0,1,2,3
-    effect1.setMeshAssociation( 3,-1);  // apply to meshes 0,1
-    effect2.setMeshAssociation( 2,-1);  // apply to mesh 1
-    effect3.setMeshAssociation( 2,-1);  // apply to mesh 0
-    effect4.setMeshAssociation(12,-1);  // apply to meshes 2,3
-    effect5.setMeshAssociation(12,-1);  // apply to meshes 2,3
-    effect6.setMeshAssociation( 8,-1);  // apply to mesh 3
-    effect7.setMeshAssociation(12,-1);  // apply to meshes 2,3
-    effect8.setMeshAssociation( 4,-1);  // apply to mesh 2
-    effect9.setMeshAssociation( 8,-1);  // apply to mesh 3
-    effect10.setMeshAssociation(15,-1); // apply to meshes 0,1,2,3
-    effect11.setMeshAssociation(15,-1); // apply to meshes 0,1,2,3
-    effect12.setMeshAssociation(15,-1); // apply to meshes 0,1,2,3
-    effect13.setMeshAssociation(15,-1); // apply to meshes 0,1,2,3
-
-    result.apply(effect0);
-    result.apply(effect1);
-    result.apply(effect2);
-    result.apply(effect3);
-    result.apply(effect4);
-    result.apply(effect5);
-    result.apply(effect6);
-    result.apply(effect7);
-    result.apply(effect8);
-    result.apply(effect9);
-    result.apply(effect10);
-    result.apply(effect11);
-    result.apply(effect12);
-    result.apply(effect13);
-
-    return result;
+    CubitFactory factory = CubitFactory.getInstance();
+    return factory.createSkewbFaceMesh();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/examples/meshfile/TextureFactory.java b/src/main/java/org/distorted/examples/meshfile/TextureFactory.java
new file mode 100644
index 0000000..f612c88
--- /dev/null
+++ b/src/main/java/org/distorted/examples/meshfile/TextureFactory.java
@@ -0,0 +1,187 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube 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.                                                           //
+//                                                                                               //
+// Magic Cube 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 Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.meshfile;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class TextureFactory
+  {
+  static final int COLOR_BLACK  = 0xff000000;
+  static final int TEXTURE_HEIGHT = 256;
+
+  private static TextureFactory mThis;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private TextureFactory()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static TextureFactory getInstance()
+    {
+    if( mThis==null ) mThis = new TextureFactory();
+
+    return mThis;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float computeAngle(float dx, float dy)
+    {
+    float PI = (float)Math.PI;
+    double angle = Math.atan2(dy,dx);
+    float ret = (float)(3*PI/2-angle);
+
+    if( ret>2*PI ) ret-= 2*PI;
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void drawCurrVertex(Canvas canvas, Paint paint, float left, float r, float stroke, float pX, float pY, float cX, float cY, float nX, float nY)
+    {
+    pX = (0.5f+pX)*TEXTURE_HEIGHT;
+    pY = (0.5f-pY)*TEXTURE_HEIGHT;
+    cX = (0.5f+cX)*TEXTURE_HEIGHT;
+    cY = (0.5f-cY)*TEXTURE_HEIGHT;
+    nX = (0.5f+nX)*TEXTURE_HEIGHT;
+    nY = (0.5f-nY)*TEXTURE_HEIGHT;
+
+    canvas.drawLine(left+pX,pY,left+cX,cY,paint);
+
+    float aX = pX-cX;
+    float aY = pY-cY;
+    float bX = cX-nX;
+    float bY = cY-nY;
+
+    float aLen = (float)Math.sqrt(aX*aX+aY*aY);
+    float bLen = (float)Math.sqrt(bX*bX+bY*bY);
+
+    aX /= aLen;
+    aY /= aLen;
+    bX /= bLen;
+    bY /= bLen;
+
+    float sX = (aX-bX)/2;
+    float sY = (aY-bY)/2;
+    float sLen = (float)Math.sqrt(sX*sX+sY*sY);
+    sX /= sLen;
+    sY /= sLen;
+
+    float startAngle = computeAngle(bX,-bY);
+    float endAngle   = computeAngle(aX,-aY);
+    float sweepAngle = endAngle-startAngle;
+    if( sweepAngle<0 ) sweepAngle += 2*Math.PI;
+
+    float R = r*TEXTURE_HEIGHT+stroke/2;
+
+    float A = (float)(R/(Math.cos(sweepAngle/2)));
+
+    float rX = cX + A*sX;
+    float rY = cY + A*sY;
+
+    startAngle *= 180/(Math.PI);
+    sweepAngle *= 180/(Math.PI);
+
+    canvas.drawArc( left+rX-R, rY-R, left+rX+R, rY+R, startAngle, sweepAngle, false, paint);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void drawRoundedPolygon(Canvas canvas, Paint paint, int left, float[] vertices, float stroke, int color, float r)
+    {
+    stroke *= TEXTURE_HEIGHT;
+
+    paint.setAntiAlias(true);
+    paint.setStrokeWidth(stroke);
+    paint.setColor(color);
+    paint.setStyle(Paint.Style.FILL);
+
+    canvas.drawRect(left,0,left+TEXTURE_HEIGHT,TEXTURE_HEIGHT,paint);
+
+    paint.setColor(COLOR_BLACK);
+    paint.setStyle(Paint.Style.STROKE);
+
+    int length = vertices.length;
+    int numVertices = length/2;
+
+    float prevX = vertices[length-2];
+    float prevY = vertices[length-1];
+    float currX = vertices[0];
+    float currY = vertices[1];
+    float nextX = vertices[2];
+    float nextY = vertices[3];
+
+    for(int vert=0; vert<numVertices; vert++)
+      {
+      drawCurrVertex(canvas, paint, left, r, stroke, prevX,prevY,currX,currY,nextX,nextY);
+
+      prevX = currX;
+      prevY = currY;
+      currX = nextX;
+      currY = nextY;
+
+      if( 2*(vert+2)+1 < length )
+        {
+        nextX = vertices[2*(vert+2)  ];
+        nextY = vertices[2*(vert+2)+1];
+        }
+      else
+        {
+        nextX = vertices[0];
+        nextY = vertices[1];
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Bitmap createTexture(float[] vertices, int[] colors, float S, float R, boolean addFinalBlack)
+    {
+    int extra = addFinalBlack ? 1:0;
+    int numColors = colors.length;
+
+    Paint paint = new Paint();
+    Bitmap bitmap = Bitmap.createBitmap( (numColors+extra)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
+    Canvas canvas = new Canvas(bitmap);
+
+    paint.setAntiAlias(true);
+    paint.setTextAlign(Paint.Align.CENTER);
+    paint.setStyle(Paint.Style.FILL);
+
+    paint.setColor(COLOR_BLACK);
+    canvas.drawRect(0, 0, (numColors+extra)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
+
+    for(int face=0; face<numColors; face++)
+      {
+      drawRoundedPolygon(canvas, paint, face*TEXTURE_HEIGHT, vertices, S, colors[face], R);
+      }
+
+    return bitmap;
+    }
+  }
diff --git a/src/main/res/raw/cube2.dmesh b/src/main/res/raw/cube2.dmesh
index 5d11426..cef99d3 100644
Binary files a/src/main/res/raw/cube2.dmesh and b/src/main/res/raw/cube2.dmesh differ
diff --git a/src/main/res/raw/cube3.dmesh b/src/main/res/raw/cube3.dmesh
index 0f0fc52..668ee90 100644
Binary files a/src/main/res/raw/cube3.dmesh and b/src/main/res/raw/cube3.dmesh differ
diff --git a/src/main/res/raw/cube4.dmesh b/src/main/res/raw/cube4.dmesh
index 36ad3a5..feacc0b 100644
Binary files a/src/main/res/raw/cube4.dmesh and b/src/main/res/raw/cube4.dmesh differ
diff --git a/src/main/res/raw/cube5.dmesh b/src/main/res/raw/cube5.dmesh
index 0b5cacd..18430d5 100644
Binary files a/src/main/res/raw/cube5.dmesh and b/src/main/res/raw/cube5.dmesh differ
diff --git a/src/main/res/raw/diamond.dmesh b/src/main/res/raw/diamond.dmesh
new file mode 100644
index 0000000..bce44f5
Binary files /dev/null and b/src/main/res/raw/diamond.dmesh differ
diff --git a/src/main/res/raw/dino.dmesh b/src/main/res/raw/dino.dmesh
index b14b436..181ae57 100644
Binary files a/src/main/res/raw/dino.dmesh and b/src/main/res/raw/dino.dmesh differ
diff --git a/src/main/res/raw/heli.dmesh b/src/main/res/raw/heli.dmesh
index 5701624..ab9cdbf 100644
Binary files a/src/main/res/raw/heli.dmesh and b/src/main/res/raw/heli.dmesh differ
diff --git a/src/main/res/raw/pyra3.dmesh b/src/main/res/raw/pyra3.dmesh
index 1c7c312..f493950 100644
Binary files a/src/main/res/raw/pyra3.dmesh and b/src/main/res/raw/pyra3.dmesh differ
diff --git a/src/main/res/raw/pyra4.dmesh b/src/main/res/raw/pyra4.dmesh
index 4902fe0..77b3562 100644
Binary files a/src/main/res/raw/pyra4.dmesh and b/src/main/res/raw/pyra4.dmesh differ
diff --git a/src/main/res/raw/pyra5.dmesh b/src/main/res/raw/pyra5.dmesh
index 7e5389f..4cada7e 100644
Binary files a/src/main/res/raw/pyra5.dmesh and b/src/main/res/raw/pyra5.dmesh differ
diff --git a/src/main/res/raw/redi.dmesh b/src/main/res/raw/redi.dmesh
new file mode 100644
index 0000000..fc8112f
Binary files /dev/null and b/src/main/res/raw/redi.dmesh differ
diff --git a/src/main/res/raw/skewb.dmesh b/src/main/res/raw/skewb.dmesh
index 752ba10..c3bf60b 100644
Binary files a/src/main/res/raw/skewb.dmesh and b/src/main/res/raw/skewb.dmesh differ
