commit ac940e2408a532c0f01c888c7efe2e93d448c70c
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Sep 28 13:59:20 2020 +0100

    Add a CubitFactory and convert all objects to use it. Biggest change is thr Pyramiinx which now uses a lattice of Octahedrons and Tetrahedrons, just like the Diamond.

diff --git a/src/main/java/org/distorted/objects/CubitFactory.java b/src/main/java/org/distorted/objects/CubitFactory.java
new file mode 100644
index 00000000..ddfcc895
--- /dev/null
+++ b/src/main/java/org/distorted/objects/CubitFactory.java
@@ -0,0 +1,1068 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.objects;
+
+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.mesh.MeshTriangle;
+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 CubitFactory mThis;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private CubitFactory()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static CubitFactory getInstance()
+    {
+    if( mThis==null ) mThis = new CubitFactory();
+
+    return mThis;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// CUBE
+
+  MeshBase createCubeMesh(int index)
+    {
+    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;
+      }
+
+    return createCubeMesh(vertices,bands,extraI,extraV);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private MeshBase createCubeMesh(float[] vertices, float[] bands, int extraI, int extraV)
+    {
+    final int MESHES=6;
+    int association = 1;
+    MeshBase[] meshes = new MeshPolygon[MESHES];
+    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);
+      }
+
+    MeshBase mesh = new MeshJoined(meshes);
+
+    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);
+
+    float d1 = 1.0f;
+    float d2 =-0.05f;
+    float d3 = 0.12f;
+
+    Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
+    Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
+    Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
+    Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
+    Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
+    Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
+    Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
+    Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
+
+    Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
+    Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
+    Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
+    Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
+    Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
+    Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
+    Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
+    Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
+
+    Static4D dReg  = new Static4D(0,0,0,d3);
+    Static1D dRad  = new Static1D(1);
+
+    VertexEffectMove effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
+    VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
+    VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
+    VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
+    VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
+    VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
+
+    VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
+    VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
+    VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
+    VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
+    VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
+    VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
+    VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
+    VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
+
+    effect0.setMeshAssociation(63,-1);  // all 6 sides
+    effect1.setMeshAssociation(32,-1);  // back
+    effect2.setMeshAssociation( 8,-1);  // bottom
+    effect3.setMeshAssociation( 4,-1);  // top
+    effect4.setMeshAssociation( 2,-1);  // left
+    effect5.setMeshAssociation( 1,-1);  // right
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+    mesh.apply(effect10);
+    mesh.apply(effect11);
+    mesh.apply(effect12);
+    mesh.apply(effect13);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// SKEWB
+
+  MeshBase createSkewbCornerMesh()
+    {
+    float D = 0.02f;
+    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 = { 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 };
+
+    MeshBase[] meshes = new MeshBase[6];
+
+    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 = { 1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f };
+
+    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);
+
+    MeshBase mesh = new MeshJoined(meshes);
+
+    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);
+
+    float d0 =-0.04f;
+    float d1 = 0.04f;
+    float r0 = 0.15f;
+    float r1 = 0.10f;
+
+    Static3D vec0   = new Static3D(d0*(+SQ3/3),d0*(+SQ3/3),d0*(+SQ3/3));
+    Static3D vec1   = new Static3D(d1*(+SQ3/3),d1*(-SQ3/3),d1*(-SQ3/3));
+    Static3D vec2   = new Static3D(d1*(-SQ3/3),d1*(+SQ3/3),d1*(-SQ3/3));
+    Static3D vec3   = new Static3D(d1*(-SQ3/3),d1*(-SQ3/3),d1*(+SQ3/3));
+
+    Static1D radius = new Static1D(0.5f);
+
+    Static3D cent0  = new Static3D( 0.0f, 0.0f, 0.0f);
+    Static3D cent1  = new Static3D(-0.5f, 0.0f, 0.0f);
+    Static3D cent2  = new Static3D( 0.0f,-0.5f, 0.0f);
+    Static3D cent3  = new Static3D( 0.0f, 0.0f,-0.5f);
+
+    Static4D reg0   = new Static4D(0,0,0,r0);
+    Static4D reg1   = new Static4D(0,0,0,r1);
+
+    VertexEffectMove   effect0 = new VertexEffectMove(move1);
+    VertexEffectScale effect1 = new VertexEffectScale(new Static3D(1,1,-1));
+    VertexEffectRotate effect2 = new VertexEffectRotate(angle1,axisX,center1);
+    VertexEffectRotate effect3 = new VertexEffectRotate(angle2,axisY,center1);
+    VertexEffectMove   effect4 = new VertexEffectMove(move2);
+    VertexEffectRotate effect5 = new VertexEffectRotate(angle1,axisX,center2);
+    VertexEffectRotate effect6 = new VertexEffectRotate(angle3,axisY,center2);
+    VertexEffectRotate effect7 = new VertexEffectRotate(angle4,axis0,center2);
+    VertexEffectRotate effect8 = new VertexEffectRotate(angle5,axis1,center2);
+    VertexEffectRotate effect9 = new VertexEffectRotate(angle6,axis1,center2);
+
+    VertexEffectDeform effect10= new VertexEffectDeform(vec0,radius,cent0,reg0);
+    VertexEffectDeform effect11= new VertexEffectDeform(vec1,radius,cent1,reg1);
+    VertexEffectDeform effect12= new VertexEffectDeform(vec2,radius,cent2,reg1);
+    VertexEffectDeform effect13= new VertexEffectDeform(vec3,radius,cent3,reg1);
+
+    effect0.setMeshAssociation( 7,-1);  // meshes 0,1,2
+    effect1.setMeshAssociation( 6,-1);  // meshes 1,2
+    effect2.setMeshAssociation( 2,-1);  // mesh 1
+    effect3.setMeshAssociation( 4,-1);  // mesh 2
+    effect4.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect5.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect6.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect7.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect8.setMeshAssociation(16,-1);  // mesh 4
+    effect9.setMeshAssociation(32,-1);  // mesh 5
+
+    effect10.setMeshAssociation(63,-1); // all meshes
+    effect11.setMeshAssociation(63,-1); // all meshes
+    effect12.setMeshAssociation(63,-1); // all meshes
+    effect13.setMeshAssociation(63,-1); // all meshes
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+
+    mesh.apply(effect10);
+    mesh.apply(effect11);
+    mesh.apply(effect12);
+    mesh.apply(effect13);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createSkewbFaceMesh()
+    {
+    int association = 1;
+
+    float D = 0.03f;
+    float E = SQ2/4;
+    float[] vertices0 = { -E,-E, +E,-E, +E,+E, -E,+E };
+
+    float[] bands0 = { 1.0f    , 0,
+                       1.0f-D/2, D*0.30f,
+                       1.0f- D , D*0.50f,
+                       1.0f-2*D, D*0.80f,
+                       0.60f   , D*1.40f,
+                       0.30f   , D*1.60f,
+                       0.0f    , D*1.70f };
+
+    MeshBase[] meshes = new MeshBase[6];
+    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
+    meshes[0].setEffectAssociation(0,association,0);
+
+    association <<= 1;
+
+    float[] vertices1 = { -E,-SQ3*E, +E,-SQ3*E, 0,0 };
+    float[] bands1 = { 1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f };
+
+    meshes[1] = new MeshPolygon(vertices1,bands1,0,0);
+    meshes[1].setEffectAssociation(0,association,0);
+
+    for(int i=2; i<5; i++)
+      {
+      association <<= 1;
+      meshes[i] = meshes[1].copy(true);
+      meshes[i].setEffectAssociation(0,association,0);
+      }
+
+    association <<= 1;
+    meshes[5] = new MeshTriangle(1);                  // empty triangle so that
+    meshes[5].setEffectAssociation(0,association,0);  // all cubits have 6 faces
+
+    MeshBase mesh = new MeshJoined(meshes);
+
+    Static3D center = new Static3D(0,0,0);
+    Static3D axis1   = new Static3D(1,0,0);
+    Static3D axis2   = new Static3D(0,0,1);
+    float angle = -(float)((180.0f/Math.PI)*Math.acos(SQ3/3));
+
+    float f = 0.05f;
+    float r = 0.10f;
+    float d = 0.5f;
+    float e = +D*0.6f;
+    Static3D vector0 = new Static3D(-f, 0, 0);
+    Static3D vector1 = new Static3D( 0,+f, 0);
+    Static3D vector2 = new Static3D(+f, 0, 0);
+    Static3D vector3 = new Static3D( 0,-f, 0);
+    Static1D radius  = new Static1D(1.0f);
+    Static4D region  = new Static4D(0,0,0,r);
+    Static3D center0 = new Static3D(+d, 0, e);
+    Static3D center1 = new Static3D( 0,-d, e);
+    Static3D center2 = new Static3D(-d, 0, e);
+    Static3D center3 = new Static3D( 0,+d, e);
+
+    VertexEffectRotate effect0 = new VertexEffectRotate( new Static1D(angle), axis1, center);
+    VertexEffectRotate effect1 = new VertexEffectRotate( new Static1D(  135), axis2, center);
+    VertexEffectRotate effect2 = new VertexEffectRotate( new Static1D(   45), axis2, center);
+    VertexEffectRotate effect3 = new VertexEffectRotate( new Static1D(  -45), axis2, center);
+    VertexEffectRotate effect4 = new VertexEffectRotate( new Static1D( -135), axis2, center);
+    VertexEffectMove   effect5 = new VertexEffectMove( new Static3D(0,0,-0.5f) );
+    VertexEffectDeform effect6 = new VertexEffectDeform(vector0,radius,center0,region);
+    VertexEffectDeform effect7 = new VertexEffectDeform(vector1,radius,center1,region);
+    VertexEffectDeform effect8 = new VertexEffectDeform(vector2,radius,center2,region);
+    VertexEffectDeform effect9 = new VertexEffectDeform(vector3,radius,center3,region);
+    VertexEffectScale  effect10= new VertexEffectScale(0.01f);
+
+    effect0.setMeshAssociation(30,-1);  // meshes 1,2,3,4
+    effect1.setMeshAssociation( 2,-1);  // mesh 1
+    effect2.setMeshAssociation( 5,-1);  // meshes 0,2
+    effect3.setMeshAssociation( 8,-1);  // mesh 3
+    effect4.setMeshAssociation(16,-1);  // mesh 4
+    effect5.setMeshAssociation(30,-1);  // meshes 1,2,3,4
+    effect6.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
+    effect7.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
+    effect8.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
+    effect9.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
+    effect10.setMeshAssociation(32,-1); // mesh 5
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+    mesh.apply(effect10);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// SKEWB DIAMOND / PYRAMINX
+
+  MeshBase createOctaMesh()
+    {
+    int association = 1;
+
+    float C = 0.06f;
+    float D = 0.02f;
+    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);
+      }
+
+    MeshBase mesh = new MeshJoined(meshes);
+
+    float d1 = SQ2/2;
+    float d2 =-0.06f;
+    float d3 = 0.20f;
+
+    Static3D a0 = new Static3D(     0, 1,     0 );
+    Static3D a1 = new Static3D( SQ2/2, 0, SQ2/2 );
+    Static3D a2 = new Static3D(-SQ2/2, 0, SQ2/2 );
+
+    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);
+
+    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*a0.get0(),-d1*a0.get1(),-d1*a0.get2() );
+    Static3D dCen4 = new Static3D(-d1*a1.get0(),-d1*a2.get1(),-d1*a2.get2() );
+    Static3D dCen5 = new Static3D(-d1*a2.get0(),-d1*a1.get1(),-d1*a1.get2() );
+
+    Static3D dVec0 = new Static3D( d2*a0.get0(), d2*a0.get1(), d2*a0.get2() );
+    Static3D dVec1 = new Static3D( d2*a1.get0(), d2*a1.get1(), d2*a1.get2() );
+    Static3D dVec2 = new Static3D( d2*a2.get0(), d2*a2.get1(), d2*a2.get2() );
+    Static3D dVec3 = new Static3D(-d2*a0.get0(),-d2*a0.get1(),-d2*a0.get2() );
+    Static3D dVec4 = new Static3D(-d2*a1.get0(),-d2*a1.get1(),-d2*a1.get2() );
+    Static3D dVec5 = new Static3D(-d2*a2.get0(),-d2*a2.get1(),-d2*a2.get2() );
+
+    Static4D dReg  = new Static4D(0,0,0,d3);
+    Static1D dRad  = new Static1D(1);
+
+    VertexEffectMove   effect0 = new VertexEffectMove(move1);
+    VertexEffectRotate effect1 = new VertexEffectRotate(alpha , axisX, cent1);
+    VertexEffectRotate effect2 = new VertexEffectRotate(angle1, axisY, cent0);
+    VertexEffectRotate effect3 = new VertexEffectRotate(angle2, axisY, cent0);
+    VertexEffectRotate effect4 = new VertexEffectRotate(angle3, axisY, cent0);
+    VertexEffectScale  effect5 = new VertexEffectScale(flipY);
+
+    VertexEffectDeform  effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
+    VertexEffectDeform  effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
+    VertexEffectDeform  effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
+    VertexEffectDeform  effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
+    VertexEffectDeform  effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
+    VertexEffectDeform  effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
+
+    effect0.setMeshAssociation (255,-1); // apply to all meshes
+    effect1.setMeshAssociation (255,-1); // apply to all meshes
+    effect2.setMeshAssociation ( 34,-1); // apply to meshes 1 & 5
+    effect3.setMeshAssociation ( 68,-1); // apply to meshes 2 & 6
+    effect4.setMeshAssociation (136,-1); // apply to meshes 3 & 7
+    effect5.setMeshAssociation (240,-1); // apply to meshes 4,5,6,7
+    effect6.setMeshAssociation (255,-1); // apply to all meshes
+    effect7.setMeshAssociation (255,-1); // apply to all meshes
+    effect8.setMeshAssociation (255,-1); // apply to all meshes
+    effect9.setMeshAssociation (255,-1); // apply to all meshes
+    effect10.setMeshAssociation(255,-1); // apply to all meshes
+    effect11.setMeshAssociation(255,-1); // apply to all meshes
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+    mesh.apply(effect10);
+    mesh.apply(effect11);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createTetraMesh()
+    {
+    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 };
+
+    MeshBase[] meshes = new MeshBase[8];
+    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);
+      }
+
+    for(int i=4; i<8; i++)
+      {
+      association <<= 1;
+      meshes[i] = new MeshTriangle(1);
+      meshes[i].setEffectAssociation(0,association,0);
+      }
+
+    MeshBase mesh = new MeshJoined(meshes);
+
+    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);
+
+    float d1 =-1.00f;
+    float d2 = 0.10f;
+    float d3 = 0.15f;
+
+    float vx = 0.5f   ;
+    float vy = (SQ2/4);
+
+    Static3D a0 = new Static3D(+vx,-vy, 0 );
+    Static3D a1 = new Static3D(-vx,-vy, 0 );
+    Static3D a2 = new Static3D(  0, vy,-vx);
+    Static3D a3 = new Static3D(  0, vy,+vx);
+
+    Static4D dReg  = new Static4D(0,0,0,d3);
+    Static1D dRad  = new Static1D(1);
+
+    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( d2*a0.get0(), d2*a0.get1(), d2*a0.get2() );
+    Static3D dVec1 = new Static3D( d2*a1.get0(), d2*a1.get1(), d2*a1.get2() );
+    Static3D dVec2 = new Static3D( d2*a2.get0(), d2*a2.get1(), d2*a2.get2() );
+    Static3D dVec3 = new Static3D( d2*a3.get0(), d2*a3.get1(), d2*a3.get2() );
+
+    VertexEffectRotate effect0 = new VertexEffectRotate(angle2, axisZ, cent0);
+    VertexEffectMove   effect1 = new VertexEffectMove(move1);
+    VertexEffectRotate effect2 = new VertexEffectRotate(alpha , axisX, cent1);
+    VertexEffectScale  effect3 = new VertexEffectScale(flipZ);
+    VertexEffectRotate effect4 = new VertexEffectRotate(angle1, axisY, cent0);
+    VertexEffectRotate effect5 = new VertexEffectRotate(angle2, axisZ, cent0);
+
+    VertexEffectDeform  effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
+    VertexEffectDeform  effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
+    VertexEffectDeform  effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
+    VertexEffectDeform  effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
+
+    VertexEffectScale  effect10 = new VertexEffectScale(0.1f);
+
+    effect0.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect1.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect2.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect3.setMeshAssociation(10,-1); // meshes 1 & 3
+    effect4.setMeshAssociation(12,-1); // meshes 2 & 3
+    effect5.setMeshAssociation(12,-1); // meshes 2 & 3
+    effect6.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect7.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect8.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect9.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect10.setMeshAssociation(240,-1); // meshes 4,5,6,7
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+    mesh.apply(effect10);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// DINO
+
+  MeshBase createDinoMesh()
+    {
+    final float ANGLE = (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.10f,
+                       0.0f    , 0.20f };
+
+    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 mesh = 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);
+    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);
+
+    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
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+    mesh.apply(effect10);
+    mesh.apply(effect11);
+    mesh.apply(effect12);
+    mesh.apply(effect13);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Helicopter
+
+  MeshBase createHelicopterCornerMesh()
+    {
+    float D = 0.02f;
+    float E = 0.5f;
+    float F = SQ2/4;
+
+    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
+
+    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 };
+
+    MeshBase[] meshes = new MeshBase[6];
+
+    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,-1.0f/12, +F,-1.0f/12, 0,1.0f/6 };
+    float[] bands1 = { 1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f };
+
+    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);
+
+    MeshBase mesh = new MeshJoined(meshes);
+
+    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);
+
+    float d0 =-0.04f;
+    float d1 = 0.04f;
+    float r0 = 0.15f;
+    float r1 = 0.10f;
+
+    Static3D vec0   = new Static3D(d0*(+SQ3/3),d0*(+SQ3/3),d0*(+SQ3/3));
+    Static3D vec1   = new Static3D(d1*(+SQ3/3),d1*(-SQ3/3),d1*(-SQ3/3));
+    Static3D vec2   = new Static3D(d1*(-SQ3/3),d1*(+SQ3/3),d1*(-SQ3/3));
+    Static3D vec3   = new Static3D(d1*(-SQ3/3),d1*(-SQ3/3),d1*(+SQ3/3));
+
+    Static1D radius = new Static1D(0.5f);
+
+    Static3D cent0  = new Static3D( 0.0f, 0.0f, 0.0f);
+    Static3D cent1  = new Static3D(-0.5f, 0.0f, 0.0f);
+    Static3D cent2  = new Static3D( 0.0f,-0.5f, 0.0f);
+    Static3D cent3  = new Static3D( 0.0f, 0.0f,-0.5f);
+
+    Static4D reg0   = new Static4D(0,0,0,r0);
+    Static4D reg1   = new Static4D(0,0,0,r1);
+
+    VertexEffectMove   effect0 = new VertexEffectMove(move1);
+    VertexEffectScale  effect1 = new VertexEffectScale(new Static3D(1,1,-1));
+    VertexEffectRotate effect2 = new VertexEffectRotate(angle1,axisX,center1);
+    VertexEffectRotate effect3 = new VertexEffectRotate(angle2,axisY,center1);
+    VertexEffectMove   effect4 = new VertexEffectMove(move2);
+    VertexEffectRotate effect5 = new VertexEffectRotate(angle1,axisX,center2);
+    VertexEffectRotate effect6 = new VertexEffectRotate(angle3,axisY,center2);
+    VertexEffectRotate effect7 = new VertexEffectRotate(angle4,axis0,center2);
+    VertexEffectRotate effect8 = new VertexEffectRotate(angle5,axis1,center2);
+    VertexEffectRotate effect9 = new VertexEffectRotate(angle6,axis1,center2);
+
+    VertexEffectDeform effect10= new VertexEffectDeform(vec0,radius,cent0,reg0);
+    VertexEffectDeform effect11= new VertexEffectDeform(vec1,radius,cent1,reg1);
+    VertexEffectDeform effect12= new VertexEffectDeform(vec2,radius,cent2,reg1);
+    VertexEffectDeform effect13= new VertexEffectDeform(vec3,radius,cent3,reg1);
+
+    effect0.setMeshAssociation( 7,-1);  // meshes 0,1,2
+    effect1.setMeshAssociation( 6,-1);  // meshes 1,2
+    effect2.setMeshAssociation( 2,-1);  // mesh 1
+    effect3.setMeshAssociation( 4,-1);  // mesh 2
+    effect4.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect5.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect6.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect7.setMeshAssociation(56,-1);  // meshes 3,4,5
+    effect8.setMeshAssociation(16,-1);  // mesh 4
+    effect9.setMeshAssociation(32,-1);  // mesh 5
+
+    effect10.setMeshAssociation(63,-1); // all meshes
+    effect11.setMeshAssociation(63,-1); // all meshes
+    effect12.setMeshAssociation(63,-1); // all meshes
+    effect13.setMeshAssociation(63,-1); // all meshes
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+
+    mesh.apply(effect10);
+    mesh.apply(effect11);
+    mesh.apply(effect12);
+    mesh.apply(effect13);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createHelicopterFaceMesh()
+    {
+    MeshBase[] meshes = new MeshBase[6];
+
+    float D = 0.02f;
+    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 = { 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 };
+
+    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 = { 1.0f   , 0.0f,
+                       0.5f   , 0.01f,
+                       0.0f   , 0.01f };
+
+    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};
+
+    float[] bands2 = { 1.0f   , 0.0f,
+                       0.5f   , 0.01f,
+                       0.0f   , 0.01f };
+
+    meshes[2] = new MeshPolygon(vertices2, bands2, 1, 3);
+    meshes[2].setEffectAssociation(0,4,0);
+    meshes[3] = meshes[2].copy(true);
+    meshes[3].setEffectAssociation(0,8,0);
+    meshes[4] = new MeshTriangle(1);
+    meshes[4].setEffectAssociation(0,16,0);
+    meshes[5] = new MeshTriangle(1);
+    meshes[5].setEffectAssociation(0,32,0);
+
+    MeshBase mesh = new MeshJoined(meshes);
+
+    Static3D move0  = new Static3D(-1.0f/8, -1.0f/8, 0);
+    Static3D move1  = new Static3D(-(SQ2/24)-1.0f/4, -(SQ2/24)-1.0f/4, 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(-0.25f,-0.25f,0);
+
+    float d0 =-0.03f;
+    float d1 =-0.04f;
+    float r0 = 0.15f;
+    float r1 = 0.10f;
+
+    Static3D vec0   = new Static3D(d0*(+SQ3/3),d0*(+SQ3/3),d0*(+SQ3/3));
+    Static3D vec1   = new Static3D(d1*(-SQ3/3),d1*(+SQ3/3),d1*(+SQ3/3));
+    Static3D vec2   = new Static3D(d1*(+SQ3/3),d1*(-SQ3/3),d1*(+SQ3/3));
+
+    Static1D radius = new Static1D(0.5f);
+
+    Static3D cent0  = new Static3D( 0.0f, 0.0f, 0.0f);
+    Static3D cent1  = new Static3D(-0.5f, 0.0f, 0.0f);
+    Static3D cent2  = new Static3D( 0.0f,-0.5f, 0.0f);
+
+    Static4D reg0   = new Static4D(0,0,0,r0);
+    Static4D reg1   = new Static4D(0,0,0,r1);
+
+    VertexEffectMove   effect0 = new VertexEffectMove(move0);
+    VertexEffectRotate effect1 = new VertexEffectRotate(angle1, axisZ, center);
+    VertexEffectMove   effect2 = new VertexEffectMove(move1);
+    VertexEffectRotate effect3 = new VertexEffectRotate(angle2, axis1, center1);
+    VertexEffectMove   effect4 = new VertexEffectMove(move2);
+    VertexEffectMove   effect5 = new VertexEffectMove(move3);
+    VertexEffectRotate effect6 = new VertexEffectRotate(angle3, axisZ, center);
+    VertexEffectRotate effect7 = new VertexEffectRotate(angle4, axisX, center);
+    VertexEffectRotate effect8 = new VertexEffectRotate(angle1, axisY, center);
+    VertexEffectScale  effect9 = new VertexEffectScale(0);
+    VertexEffectDeform effect10= new VertexEffectDeform(vec0,radius,cent0,reg0);
+    VertexEffectDeform effect11= new VertexEffectDeform(vec1,radius,cent1,reg1);
+    VertexEffectDeform effect12= new VertexEffectDeform(vec2,radius,cent2,reg1);
+    VertexEffectMove   effect13= new VertexEffectMove(move4);
+
+    effect0.setMeshAssociation( 1,-1);  // mesh 0
+    effect1.setMeshAssociation( 2,-1);  // mesh 1
+    effect2.setMeshAssociation( 2,-1);  // mesh 1
+    effect3.setMeshAssociation( 2,-1);  // mesh 1
+    effect4.setMeshAssociation( 4,-1);  // mesh 2
+    effect5.setMeshAssociation( 8,-1);  // mesh 3
+    effect6.setMeshAssociation( 8,-1);  // mesh 3
+    effect7.setMeshAssociation( 4,-1);  // mesh 2
+    effect8.setMeshAssociation( 8,-1);  // mesh 3
+    effect9.setMeshAssociation(48,-1);  // meshes 4,5
+    effect10.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect11.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect12.setMeshAssociation(15,-1); // meshes 0,1,2,3
+    effect13.setMeshAssociation(15,-1); // meshes 0,1,2,3
+
+    mesh.apply(effect0);
+    mesh.apply(effect1);
+    mesh.apply(effect2);
+    mesh.apply(effect3);
+    mesh.apply(effect4);
+    mesh.apply(effect5);
+    mesh.apply(effect6);
+    mesh.apply(effect7);
+    mesh.apply(effect8);
+    mesh.apply(effect9);
+    mesh.apply(effect10);
+    mesh.apply(effect11);
+    mesh.apply(effect12);
+    mesh.apply(effect13);
+
+    mesh.mergeEffComponents();
+
+    return mesh;
+    }
+  }
diff --git a/src/main/java/org/distorted/objects/MovementPyraminx.java b/src/main/java/org/distorted/objects/MovementPyraminx.java
index ae84bfc7..9443a144 100644
--- a/src/main/java/org/distorted/objects/MovementPyraminx.java
+++ b/src/main/java/org/distorted/objects/MovementPyraminx.java
@@ -30,10 +30,10 @@ class MovementPyraminx extends Movement
 
   static final Static3D[] FACE_AXIS = new Static3D[]
          {
-           new Static3D(     0,      -1,       0 ),
-           new Static3D(     0,  1.0f/3,-2*SQ2/3 ),
-           new Static3D( SQ6/3,  1.0f/3,   SQ2/3 ),
-           new Static3D(-SQ6/3,  1.0f/3,   SQ2/3 )
+           new Static3D(     0,+SQ3/3,+SQ6/3),
+           new Static3D(     0,+SQ3/3,-SQ6/3),
+           new Static3D(-SQ6/3,-SQ3/3,     0),
+           new Static3D(+SQ6/3,-SQ3/3,     0),
          };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -61,7 +61,10 @@ class MovementPyraminx extends Movement
 
   boolean isInsideFace(int face, float[] p)
     {
-    return (p[1] >= -DIST2D) && (p[1] <=  DIST2D*(2+6*p[0])) && (p[1] <=  DIST2D*(2-6*p[0]));
+    float y = (face > 1 ? p[1] : -p[1]);
+    float x = p[0];
+
+    return (y >= -DIST2D) && (y <= DIST2D*(2-6*x)) && (y <= DIST2D*(2+6*x));
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/TwistyCube.java b/src/main/java/org/distorted/objects/TwistyCube.java
index af339bac..58b082b5 100644
--- a/src/main/java/org/distorted/objects/TwistyCube.java
+++ b/src/main/java/org/distorted/objects/TwistyCube.java
@@ -23,16 +23,10 @@ import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 
-import org.distorted.library.effect.VertexEffectDeform;
-import org.distorted.library.effect.VertexEffectMove;
-import org.distorted.library.effect.VertexEffectRotate;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.mesh.MeshSquare;
-import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.main.RubikSurfaceView;
@@ -109,6 +103,26 @@ class TwistyCube extends TwistyObject
     super(size, 60, quat, texture, mesh, effects, moves, ObjectList.CUBE, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createCubitMesh(int cubit)
+    {
+    if( mMeshes==null )
+      {
+      mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
+      }
+
+    int ordinal= ObjectList.CUBE.ordinal();
+    int index  = ObjectList.getSizeIndex(ordinal,getSize());
+
+    if( mMeshes[index]==null )
+      {
+      mMeshes[index] = CubitFactory.getInstance().createCubeMesh(index);
+      }
+
+    return mMeshes[index].copy(true);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // paint the square with upper-right corner at (left,top) and side length 'side' with texture
 // for face 'face'.
@@ -200,159 +214,6 @@ class TwistyCube extends TwistyObject
     return belongs ? cubitface : NUM_FACES;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createCubitMesh(int cubit)
-    {
-    int size   = getSize();
-    int ordinal= ObjectList.CUBE.ordinal();
-    int index  = ObjectList.getSizeIndex(ordinal,size);
-    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(size)
-      {
-      case 2 : 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 3 : 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 4 : 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;
-      }
-
-    return createCubitMesh(index,vertices,bands,extraI,extraV);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createCubitMesh(int index, float[] vertices, float[] bands, int extraI, int extraV)
-    {
-    if( mMeshes==null )
-      {
-      mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
-      }
-
-    if( mMeshes[index]==null )
-      {
-      final int MESHES=6;
-      int association = 1;
-      MeshBase[] meshes = new MeshPolygon[MESHES];
-      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);
-        }
-
-      mMeshes[index] = new MeshJoined(meshes);
-
-      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);
-
-      float d1 = 1.0f;
-      float d2 =-0.05f;
-      float d3 = 0.12f;
-
-      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
-      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
-      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
-      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
-      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
-      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
-      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
-      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
-
-      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
-      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
-      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
-      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
-      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
-      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
-      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
-      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
-
-      Static4D dReg  = new Static4D(0,0,0,d3);
-      Static1D dRad  = new Static1D(1);
-
-      VertexEffectMove   effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
-      effect0.setMeshAssociation(63,-1);  // all 6 sides
-      VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
-      effect1.setMeshAssociation(32,-1);  // back
-      VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
-      effect2.setMeshAssociation( 8,-1);  // bottom
-      VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
-      effect3.setMeshAssociation( 4,-1);  // top
-      VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
-      effect4.setMeshAssociation( 2,-1);  // left
-      VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
-      effect5.setMeshAssociation( 1,-1);  // right
-
-      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
-      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
-      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
-      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
-      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
-      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
-      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
-      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
-
-      mMeshes[index].apply(effect0);
-      mMeshes[index].apply(effect1);
-      mMeshes[index].apply(effect2);
-      mMeshes[index].apply(effect3);
-      mMeshes[index].apply(effect4);
-      mMeshes[index].apply(effect5);
-      mMeshes[index].apply(effect6);
-      mMeshes[index].apply(effect7);
-      mMeshes[index].apply(effect8);
-      mMeshes[index].apply(effect9);
-      mMeshes[index].apply(effect10);
-      mMeshes[index].apply(effect11);
-      mMeshes[index].apply(effect12);
-      mMeshes[index].apply(effect13);
-
-      mMeshes[index].mergeEffComponents();
-      }
-
-    return mMeshes[index].copy(true);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float returnMultiplier()
@@ -535,8 +396,7 @@ class TwistyCube extends TwistyObject
     StringBuilder objectString = new StringBuilder();
     int size = getSize();
     int len = size*size;
-    int cubitIndex=-1, row=-1, col=-1;
-    int color=-1, face=-1;
+    int cubitIndex, row, col, color,face;
 
     final int RIGHT= 0;
     final int LEFT = 1;
diff --git a/src/main/java/org/distorted/objects/TwistyDiamond.java b/src/main/java/org/distorted/objects/TwistyDiamond.java
index a4601ee8..66cec4b0 100644
--- a/src/main/java/org/distorted/objects/TwistyDiamond.java
+++ b/src/main/java/org/distorted/objects/TwistyDiamond.java
@@ -24,18 +24,10 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 
 import org.distorted.library.effect.MatrixEffectQuaternion;
-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.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.mesh.MeshSquare;
-import org.distorted.library.mesh.MeshTriangle;
-import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.main.RubikSurfaceView;
@@ -141,242 +133,6 @@ public class TwistyDiamond extends TwistyObject
     super(size, 60, quat, texture, mesh, effects, moves, ObjectList.DIAM, res, scrWidth);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createOctaMesh()
-    {
-    int association = 1;
-
-    float C = 0.06f;
-    float D = 0.02f;
-    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[FACES_PER_CUBIT];
-    meshes[0] = new MeshPolygon(vertices, bands, 2,2);
-    meshes[0].setEffectAssociation(0,association,0);
-
-    for(int i=1; i<FACES_PER_CUBIT; i++)
-      {
-      association <<= 1;
-      meshes[i] = meshes[0].copy(true);
-      meshes[i].setEffectAssociation(0,association,0);
-      }
-
-    mOctaMesh = new MeshJoined(meshes);
-
-    float d1 = SQ2/2;
-    float d2 =-0.06f;
-    float d3 = 0.20f;
-
-    Static3D a0 = new Static3D(     0, 1,     0 );
-    Static3D a1 = new Static3D( SQ2/2, 0, SQ2/2 );
-    Static3D a2 = new Static3D(-SQ2/2, 0, SQ2/2 );
-
-    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);
-
-    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*a0.get0(),-d1*a0.get1(),-d1*a0.get2() );
-    Static3D dCen4 = new Static3D(-d1*a1.get0(),-d1*a2.get1(),-d1*a2.get2() );
-    Static3D dCen5 = new Static3D(-d1*a2.get0(),-d1*a1.get1(),-d1*a1.get2() );
-
-    Static3D dVec0 = new Static3D( d2*a0.get0(), d2*a0.get1(), d2*a0.get2() );
-    Static3D dVec1 = new Static3D( d2*a1.get0(), d2*a1.get1(), d2*a1.get2() );
-    Static3D dVec2 = new Static3D( d2*a2.get0(), d2*a2.get1(), d2*a2.get2() );
-    Static3D dVec3 = new Static3D(-d2*a0.get0(),-d2*a0.get1(),-d2*a0.get2() );
-    Static3D dVec4 = new Static3D(-d2*a1.get0(),-d2*a1.get1(),-d2*a1.get2() );
-    Static3D dVec5 = new Static3D(-d2*a2.get0(),-d2*a2.get1(),-d2*a2.get2() );
-
-    Static4D dReg  = new Static4D(0,0,0,d3);
-    Static1D dRad  = new Static1D(1);
-
-    VertexEffectMove   effect0 = new VertexEffectMove(move1);
-    VertexEffectRotate effect1 = new VertexEffectRotate(alpha , axisX, cent1);
-    VertexEffectRotate effect2 = new VertexEffectRotate(angle1, axisY, cent0);
-    VertexEffectRotate effect3 = new VertexEffectRotate(angle2, axisY, cent0);
-    VertexEffectRotate effect4 = new VertexEffectRotate(angle3, axisY, cent0);
-    VertexEffectScale  effect5 = new VertexEffectScale(flipY);
-
-    VertexEffectDeform  effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
-    VertexEffectDeform  effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
-    VertexEffectDeform  effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
-    VertexEffectDeform  effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
-    VertexEffectDeform  effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
-    VertexEffectDeform  effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
-
-    effect0.setMeshAssociation (255,-1); // apply to all meshes
-    effect1.setMeshAssociation (255,-1); // apply to all meshes
-    effect2.setMeshAssociation ( 34,-1); // apply to meshes 1 & 5
-    effect3.setMeshAssociation ( 68,-1); // apply to meshes 2 & 6
-    effect4.setMeshAssociation (136,-1); // apply to meshes 3 & 7
-    effect5.setMeshAssociation (240,-1); // apply to meshes 4,5,6,7
-    effect6.setMeshAssociation (255,-1); // apply to all meshes
-    effect7.setMeshAssociation (255,-1); // apply to all meshes
-    effect8.setMeshAssociation (255,-1); // apply to all meshes
-    effect9.setMeshAssociation (255,-1); // apply to all meshes
-    effect10.setMeshAssociation(255,-1); // apply to all meshes
-    effect11.setMeshAssociation(255,-1); // apply to all meshes
-
-    mOctaMesh.apply(effect0);
-    mOctaMesh.apply(effect1);
-    mOctaMesh.apply(effect2);
-    mOctaMesh.apply(effect3);
-    mOctaMesh.apply(effect4);
-    mOctaMesh.apply(effect5);
-    mOctaMesh.apply(effect6);
-    mOctaMesh.apply(effect7);
-    mOctaMesh.apply(effect8);
-    mOctaMesh.apply(effect9);
-    mOctaMesh.apply(effect10);
-    mOctaMesh.apply(effect11);
-
-    mOctaMesh.mergeEffComponents();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createTetraMesh()
-    {
-    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 };
-
-    MeshBase[] meshes = new MeshBase[FACES_PER_CUBIT];
-    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);
-      }
-
-    for(int i=4; i<FACES_PER_CUBIT; i++)
-      {
-      association <<= 1;
-      meshes[i] = new MeshTriangle(1);
-      meshes[i].setEffectAssociation(0,association,0);
-      }
-
-    mTetraMesh = new MeshJoined(meshes);
-
-    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);
-
-    float d1 =-1.00f;
-    float d2 = 0.10f;
-    float d3 = 0.15f;
-
-    float vx = 0.5f   ;
-    float vy = (SQ2/4);
-
-    Static3D a0 = new Static3D(+vx,-vy, 0 );
-    Static3D a1 = new Static3D(-vx,-vy, 0 );
-    Static3D a2 = new Static3D(  0, vy,-vx);
-    Static3D a3 = new Static3D(  0, vy,+vx);
-
-    Static4D dReg  = new Static4D(0,0,0,d3);
-    Static1D dRad  = new Static1D(1);
-
-    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( d2*a0.get0(), d2*a0.get1(), d2*a0.get2() );
-    Static3D dVec1 = new Static3D( d2*a1.get0(), d2*a1.get1(), d2*a1.get2() );
-    Static3D dVec2 = new Static3D( d2*a2.get0(), d2*a2.get1(), d2*a2.get2() );
-    Static3D dVec3 = new Static3D( d2*a3.get0(), d2*a3.get1(), d2*a3.get2() );
-
-    VertexEffectRotate effect0 = new VertexEffectRotate(angle2, axisZ, cent0);
-    VertexEffectMove   effect1 = new VertexEffectMove(move1);
-    VertexEffectRotate effect2 = new VertexEffectRotate(alpha , axisX, cent1);
-    VertexEffectScale  effect3 = new VertexEffectScale(flipZ);
-    VertexEffectRotate effect4 = new VertexEffectRotate(angle1, axisY, cent0);
-    VertexEffectRotate effect5 = new VertexEffectRotate(angle2, axisZ, cent0);
-
-    VertexEffectDeform  effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
-    VertexEffectDeform  effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
-    VertexEffectDeform  effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
-    VertexEffectDeform  effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
-
-    VertexEffectScale  effect10 = new VertexEffectScale(0.1f);
-
-    effect0.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect1.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect2.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect3.setMeshAssociation(10,-1); // meshes 1 & 3
-    effect4.setMeshAssociation(12,-1); // meshes 2 & 3
-    effect5.setMeshAssociation(12,-1); // meshes 2 & 3
-    effect6.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect7.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect8.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect9.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect10.setMeshAssociation(240,-1); // meshes 4,5,6,7
-
-    mTetraMesh.apply(effect0);
-    mTetraMesh.apply(effect1);
-    mTetraMesh.apply(effect2);
-    mTetraMesh.apply(effect3);
-    mTetraMesh.apply(effect4);
-    mTetraMesh.apply(effect5);
-    mTetraMesh.apply(effect6);
-    mTetraMesh.apply(effect7);
-    mTetraMesh.apply(effect8);
-    mTetraMesh.apply(effect9);
-    mTetraMesh.apply(effect10);
-
-    mTetraMesh.mergeEffComponents();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -466,12 +222,12 @@ public class TwistyDiamond extends TwistyObject
 
     if( cubit<6 )
       {
-      if( mOctaMesh==null ) createOctaMesh();
+      if( mOctaMesh==null ) mOctaMesh = CubitFactory.getInstance().createOctaMesh();
       mesh = mOctaMesh.copy(true);
       }
     else
       {
-      if( mTetraMesh==null ) createTetraMesh();
+      if( mTetraMesh==null ) mTetraMesh = CubitFactory.getInstance().createTetraMesh();
       mesh = mTetraMesh.copy(true);
       }
 
diff --git a/src/main/java/org/distorted/objects/TwistyDino.java b/src/main/java/org/distorted/objects/TwistyDino.java
index 1d9754e6..5eb09de1 100644
--- a/src/main/java/org/distorted/objects/TwistyDino.java
+++ b/src/main/java/org/distorted/objects/TwistyDino.java
@@ -24,17 +24,10 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 
 import org.distorted.library.effect.MatrixEffectQuaternion;
-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.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.mesh.MeshSquare;
-import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.main.RubikSurfaceView;
@@ -47,7 +40,6 @@ import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
 
 public abstract class TwistyDino extends TwistyObject
 {
-  private static final float SQ2 = (float)Math.sqrt(2);
   private static final float SQ3 = (float)Math.sqrt(3);
 
   // the four rotation axis of a RubikDino. Must be normalized.
@@ -110,134 +102,6 @@ public abstract class TwistyDino extends TwistyObject
     super(size, 60, quat, texture, mesh, effects, moves, obj, res, scrWidth);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBasicMesh()
-    {
-    final float ANGLE = (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.10f,
-                       0.0f    , 0.20f };
-
-    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);
-
-    mMesh = 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);
-    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);
-
-    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
-
-    mMesh.apply(effect0);
-    mMesh.apply(effect1);
-    mMesh.apply(effect2);
-    mMesh.apply(effect3);
-    mMesh.apply(effect4);
-    mMesh.apply(effect5);
-    mMesh.apply(effect6);
-    mMesh.apply(effect7);
-    mMesh.apply(effect8);
-    mMesh.apply(effect9);
-    mMesh.apply(effect10);
-    mMesh.apply(effect11);
-    mMesh.apply(effect12);
-    mMesh.apply(effect13);
-
-    mMesh.mergeEffComponents();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int mulQuat(int q1, int q2)
@@ -331,7 +195,7 @@ public abstract class TwistyDino extends TwistyObject
 
   MeshBase createCubitMesh(int cubit)
     {
-    if( mMesh==null ) createBasicMesh();
+    if( mMesh==null ) mMesh = CubitFactory.getInstance().createDinoMesh();
 
     MeshBase mesh = mMesh.copy(true);
     MatrixEffectQuaternion quat = new MatrixEffectQuaternion( QUATS[cubit], new Static3D(0,0,0) );
diff --git a/src/main/java/org/distorted/objects/TwistyHelicopter.java b/src/main/java/org/distorted/objects/TwistyHelicopter.java
index 132b6f77..89aae476 100644
--- a/src/main/java/org/distorted/objects/TwistyHelicopter.java
+++ b/src/main/java/org/distorted/objects/TwistyHelicopter.java
@@ -24,18 +24,10 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 
 import org.distorted.library.effect.MatrixEffectQuaternion;
-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.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.mesh.MeshSquare;
-import org.distorted.library.mesh.MeshTriangle;
-import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.main.RubikSurfaceView;
@@ -49,7 +41,6 @@ import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
 public class TwistyHelicopter extends TwistyObject
 {
   private static final float SQ2 = (float)Math.sqrt(2);
-  private static final float SQ3 = (float)Math.sqrt(3);
 
   private static final int FACES_PER_CUBIT =6;
 
@@ -205,263 +196,6 @@ public class TwistyHelicopter extends TwistyObject
     super(size, 60, quat, texture, mesh, effects, moves, ObjectList.HELI, res, scrWidth);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createCornerMesh()
-    {
-    float D = 0.02f;
-    float E = 0.5f;
-    float F = SQ2/4;
-
-    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
-
-    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 };
-
-    MeshBase[] meshes = new MeshBase[6];
-
-    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,-1.0f/12, +F,-1.0f/12, 0,1.0f/6 };
-    float[] bands1 = { 1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f };
-
-    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);
-
-    mCornerMesh = new MeshJoined(meshes);
-
-    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);
-
-    float d0 =-0.04f;
-    float d1 = 0.04f;
-    float r0 = 0.15f;
-    float r1 = 0.10f;
-
-    Static3D vec0   = new Static3D(d0*(+SQ3/3),d0*(+SQ3/3),d0*(+SQ3/3));
-    Static3D vec1   = new Static3D(d1*(+SQ3/3),d1*(-SQ3/3),d1*(-SQ3/3));
-    Static3D vec2   = new Static3D(d1*(-SQ3/3),d1*(+SQ3/3),d1*(-SQ3/3));
-    Static3D vec3   = new Static3D(d1*(-SQ3/3),d1*(-SQ3/3),d1*(+SQ3/3));
-
-    Static1D radius = new Static1D(0.5f);
-
-    Static3D cent0  = new Static3D( 0.0f, 0.0f, 0.0f);
-    Static3D cent1  = new Static3D(-0.5f, 0.0f, 0.0f);
-    Static3D cent2  = new Static3D( 0.0f,-0.5f, 0.0f);
-    Static3D cent3  = new Static3D( 0.0f, 0.0f,-0.5f);
-
-    Static4D reg0   = new Static4D(0,0,0,r0);
-    Static4D reg1   = new Static4D(0,0,0,r1);
-
-    VertexEffectMove   effect0 = new VertexEffectMove(move1);
-    VertexEffectScale  effect1 = new VertexEffectScale(new Static3D(1,1,-1));
-    VertexEffectRotate effect2 = new VertexEffectRotate(angle1,axisX,center1);
-    VertexEffectRotate effect3 = new VertexEffectRotate(angle2,axisY,center1);
-    VertexEffectMove   effect4 = new VertexEffectMove(move2);
-    VertexEffectRotate effect5 = new VertexEffectRotate(angle1,axisX,center2);
-    VertexEffectRotate effect6 = new VertexEffectRotate(angle3,axisY,center2);
-    VertexEffectRotate effect7 = new VertexEffectRotate(angle4,axis0,center2);
-    VertexEffectRotate effect8 = new VertexEffectRotate(angle5,axis1,center2);
-    VertexEffectRotate effect9 = new VertexEffectRotate(angle6,axis1,center2);
-
-    VertexEffectDeform effect10= new VertexEffectDeform(vec0,radius,cent0,reg0);
-    VertexEffectDeform effect11= new VertexEffectDeform(vec1,radius,cent1,reg1);
-    VertexEffectDeform effect12= new VertexEffectDeform(vec2,radius,cent2,reg1);
-    VertexEffectDeform effect13= new VertexEffectDeform(vec3,radius,cent3,reg1);
-
-    effect0.setMeshAssociation( 7,-1);  // meshes 0,1,2
-    effect1.setMeshAssociation( 6,-1);  // meshes 1,2
-    effect2.setMeshAssociation( 2,-1);  // mesh 1
-    effect3.setMeshAssociation( 4,-1);  // mesh 2
-    effect4.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect5.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect6.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect7.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect8.setMeshAssociation(16,-1);  // mesh 4
-    effect9.setMeshAssociation(32,-1);  // mesh 5
-
-    effect10.setMeshAssociation(63,-1); // all meshes
-    effect11.setMeshAssociation(63,-1); // all meshes
-    effect12.setMeshAssociation(63,-1); // all meshes
-    effect13.setMeshAssociation(63,-1); // all meshes
-
-    mCornerMesh.apply(effect0);
-    mCornerMesh.apply(effect1);
-    mCornerMesh.apply(effect2);
-    mCornerMesh.apply(effect3);
-    mCornerMesh.apply(effect4);
-    mCornerMesh.apply(effect5);
-    mCornerMesh.apply(effect6);
-    mCornerMesh.apply(effect7);
-    mCornerMesh.apply(effect8);
-    mCornerMesh.apply(effect9);
-
-    mCornerMesh.apply(effect10);
-    mCornerMesh.apply(effect11);
-    mCornerMesh.apply(effect12);
-    mCornerMesh.apply(effect13);
-
-    mCornerMesh.mergeEffComponents();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createFaceMesh()
-    {
-    MeshBase[] meshes = new MeshBase[6];
-
-    float D = 0.02f;
-    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 = { 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 };
-
-    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 = { 1.0f   , 0.0f,
-                       0.5f   , 0.01f,
-                       0.0f   , 0.01f };
-
-    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};
-
-    float[] bands2 = { 1.0f   , 0.0f,
-                       0.5f   , 0.01f,
-                       0.0f   , 0.01f };
-
-    meshes[2] = new MeshPolygon(vertices2, bands2, 1, 3);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = new MeshTriangle(1);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = new MeshTriangle(1);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    mFaceMesh = new MeshJoined(meshes);
-
-    Static3D move0  = new Static3D(-1.0f/8, -1.0f/8, 0);
-    Static3D move1  = new Static3D(-(SQ2/24)-1.0f/4, -(SQ2/24)-1.0f/4, 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(-0.25f,-0.25f,0);
-
-    float d0 =-0.03f;
-    float d1 =-0.04f;
-    float r0 = 0.15f;
-    float r1 = 0.10f;
-
-    Static3D vec0   = new Static3D(d0*(+SQ3/3),d0*(+SQ3/3),d0*(+SQ3/3));
-    Static3D vec1   = new Static3D(d1*(-SQ3/3),d1*(+SQ3/3),d1*(+SQ3/3));
-    Static3D vec2   = new Static3D(d1*(+SQ3/3),d1*(-SQ3/3),d1*(+SQ3/3));
-
-    Static1D radius = new Static1D(0.5f);
-
-    Static3D cent0  = new Static3D( 0.0f, 0.0f, 0.0f);
-    Static3D cent1  = new Static3D(-0.5f, 0.0f, 0.0f);
-    Static3D cent2  = new Static3D( 0.0f,-0.5f, 0.0f);
-
-    Static4D reg0   = new Static4D(0,0,0,r0);
-    Static4D reg1   = new Static4D(0,0,0,r1);
-
-    VertexEffectMove   effect0 = new VertexEffectMove(move0);
-    VertexEffectRotate effect1 = new VertexEffectRotate(angle1, axisZ, center);
-    VertexEffectMove   effect2 = new VertexEffectMove(move1);
-    VertexEffectRotate effect3 = new VertexEffectRotate(angle2, axis1, center1);
-    VertexEffectMove   effect4 = new VertexEffectMove(move2);
-    VertexEffectMove   effect5 = new VertexEffectMove(move3);
-    VertexEffectRotate effect6 = new VertexEffectRotate(angle3, axisZ, center);
-    VertexEffectRotate effect7 = new VertexEffectRotate(angle4, axisX, center);
-    VertexEffectRotate effect8 = new VertexEffectRotate(angle1, axisY, center);
-    VertexEffectScale  effect9 = new VertexEffectScale(0);
-    VertexEffectDeform effect10= new VertexEffectDeform(vec0,radius,cent0,reg0);
-    VertexEffectDeform effect11= new VertexEffectDeform(vec1,radius,cent1,reg1);
-    VertexEffectDeform effect12= new VertexEffectDeform(vec2,radius,cent2,reg1);
-    VertexEffectMove   effect13= new VertexEffectMove(move4);
-
-    effect0.setMeshAssociation( 1,-1);  // mesh 0
-    effect1.setMeshAssociation( 2,-1);  // mesh 1
-    effect2.setMeshAssociation( 2,-1);  // mesh 1
-    effect3.setMeshAssociation( 2,-1);  // mesh 1
-    effect4.setMeshAssociation( 4,-1);  // mesh 2
-    effect5.setMeshAssociation( 8,-1);  // mesh 3
-    effect6.setMeshAssociation( 8,-1);  // mesh 3
-    effect7.setMeshAssociation( 4,-1);  // mesh 2
-    effect8.setMeshAssociation( 8,-1);  // mesh 3
-    effect9.setMeshAssociation(48,-1);  // meshes 4,5
-    effect10.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect11.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect12.setMeshAssociation(15,-1); // meshes 0,1,2,3
-    effect13.setMeshAssociation(15,-1); // meshes 0,1,2,3
-
-    mFaceMesh.apply(effect0);
-    mFaceMesh.apply(effect1);
-    mFaceMesh.apply(effect2);
-    mFaceMesh.apply(effect3);
-    mFaceMesh.apply(effect4);
-    mFaceMesh.apply(effect5);
-    mFaceMesh.apply(effect6);
-    mFaceMesh.apply(effect7);
-    mFaceMesh.apply(effect8);
-    mFaceMesh.apply(effect9);
-    mFaceMesh.apply(effect10);
-    mFaceMesh.apply(effect11);
-    mFaceMesh.apply(effect12);
-    mFaceMesh.apply(effect13);
-
-    mFaceMesh.mergeEffComponents();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -526,12 +260,12 @@ public class TwistyHelicopter extends TwistyObject
 
     if( cubit<8 )
       {
-      if( mCornerMesh==null ) createCornerMesh();
+      if( mCornerMesh==null ) mCornerMesh = CubitFactory.getInstance().createHelicopterCornerMesh();
       mesh = mCornerMesh.copy(true);
       }
     else
       {
-      if( mFaceMesh==null ) createFaceMesh();
+      if( mFaceMesh==null ) mFaceMesh = CubitFactory.getInstance().createHelicopterFaceMesh();
       mesh = mFaceMesh.copy(true);
       }
 
diff --git a/src/main/java/org/distorted/objects/TwistyPyraminx.java b/src/main/java/org/distorted/objects/TwistyPyraminx.java
index 65fd2078..3155ea98 100644
--- a/src/main/java/org/distorted/objects/TwistyPyraminx.java
+++ b/src/main/java/org/distorted/objects/TwistyPyraminx.java
@@ -23,17 +23,10 @@ import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 
-import org.distorted.library.effect.VertexEffectDeform;
-import org.distorted.library.effect.VertexEffectMove;
-import org.distorted.library.effect.VertexEffectRotate;
-import org.distorted.library.effect.VertexEffectSink;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.mesh.MeshSquare;
-import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.main.RubikSurfaceView;
@@ -52,10 +45,10 @@ public class TwistyPyraminx extends TwistyObject
 
   static final Static3D[] ROT_AXIS = new Static3D[]
          {
-           new Static3D(         0,        1,       0 ),
-           new Static3D(         0,  -1.0f/3, 2*SQ2/3 ),
-           new Static3D(-SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 ),
-           new Static3D( SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 )
+           new Static3D(     0,-SQ3/3,-SQ6/3),
+           new Static3D(     0,-SQ3/3,+SQ6/3),
+           new Static3D(+SQ6/3,+SQ3/3,     0),
+           new Static3D(-SQ6/3,+SQ3/3,     0),
          };
 
   private static final int[] FACE_COLORS = new int[]
@@ -68,135 +61,79 @@ public class TwistyPyraminx extends TwistyObject
   private static final Static4D[] QUATS = new Static4D[]
          {
            new Static4D(  0.0f,   0.0f,   0.0f,  1.0f),
-           new Static4D(  0.0f,  SQ3/2,   0.0f,  0.5f),
-           new Static4D( SQ2/2, -SQ3/6, -SQ6/6,  0.5f),
-           new Static4D(-SQ2/2, -SQ3/6, -SQ6/6,  0.5f),
-           new Static4D(  0.0f, -SQ3/6,  SQ6/3,  0.5f),
-           new Static4D(  0.0f,  SQ3/2,   0.0f, -0.5f),
-           new Static4D( SQ2/2, -SQ3/6, -SQ6/6, -0.5f),
-           new Static4D(-SQ2/2, -SQ3/6, -SQ6/6, -0.5f),
-           new Static4D(  0.0f, -SQ3/6,  SQ6/3, -0.5f),
-           new Static4D( SQ2/2, -SQ3/3,  SQ6/6,  0.0f),
-           new Static4D(  0.0f, -SQ3/3, -SQ6/3,  0.0f),
-           new Static4D(-SQ2/2, -SQ3/3,  SQ6/6,  0.0f)
+           new Static4D(  0.0f,   1.0f,   0.0f,  0.0f),
+           new Static4D( SQ2/2,   0.5f,   0.0f,  0.5f),
+           new Static4D(-SQ2/2,   0.5f,   0.0f,  0.5f),
+           new Static4D(  0.0f,  -0.5f, -SQ2/2,  0.5f),
+           new Static4D(  0.0f,  -0.5f,  SQ2/2,  0.5f),
+           new Static4D( SQ2/2,   0.5f,   0.0f, -0.5f),
+           new Static4D(-SQ2/2,   0.5f,   0.0f, -0.5f),
+           new Static4D(  0.0f,  -0.5f, -SQ2/2, -0.5f),
+           new Static4D(  0.0f,  -0.5f,  SQ2/2, -0.5f),
+           new Static4D( SQ2/2,   0.0f,  SQ2/2,  0.0f),
+           new Static4D(-SQ2/2,   0.0f,  SQ2/2,  0.0f)
          };
 
-  private int[] mRotArray;
-  private static VertexEffectRotate[] ROTATION;
-
-  private static MeshBase mMesh =null;
-  private static MeshBase[] mMeshRotated = new MeshBase[ROT_AXIS.length];
-
-  static
-    {
-    Static3D center = new Static3D(0,0,0);
-    Static1D angle  = new Static1D(180.0f);
-
-    ROTATION = new VertexEffectRotate[ROT_AXIS.length];
-
-    for(int i = 0; i< ROT_AXIS.length; i++)
-      {
-      ROTATION[i] = new VertexEffectRotate( angle, ROT_AXIS[i], center);
-      mMeshRotated[i] = null;
-      }
-    }
+  private static MeshBase mOctaMesh, mTetraMesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  TwistyPyraminx(int size, Static4D quat, DistortedTexture texture,
-                 MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
+  TwistyPyraminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
+                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
     {
     super(size, 30, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void emitRow(float x, float y, float z, float dx, float dy, float dz, int n, int rot, Static3D[] array, int index)
-    {
-    for(int i=0; i<n; i++)
-      {
-      mRotArray[i+index] = rot;
-      array[i+index] = new Static3D(x+0.5f,y+SQ2*SQ3/12,z+SQ3/6);
-      x += dx;
-      y += dy;
-      z += dz;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int emitLowermost(float x, float y, float z, int n, Static3D[] array)
+  private void addTetrahedralLattice(int size, int index, Static3D[] pos)
     {
-    int added = 0;
+    final float DX = 1.0f;
+    final float DY = SQ2/2;
+    final float DZ = 1.0f;
 
-    emitRow( x      +0.5f, y+SQ3*SQ2/9, z+ SQ3/18,  1.0f, 0,     0, n-1, 1, array, added);
-    added += (n-1);
-    emitRow( x    +1.0f/3, y+SQ3*SQ2/9, z+2*SQ3/9,  0.5f, 0, SQ3/2, n-1, 3, array, added);
-    added += (n-1);
-    emitRow( x+n-1-1.0f/3, y+SQ3*SQ2/9, z+2*SQ3/9, -0.5f, 0, SQ3/2, n-1, 2, array, added);
-    added += (n-1);
+    float startX = 0.0f;
+    float startY =-DY*(size-1)/2;
+    float startZ = DZ*(size-1)/2;
 
-    for(int i=n; i>=1; i--)
+    for(int layer=0; layer<size; layer++)
       {
-      emitRow(x     , y, z      , 1,0,0, i  , -1, array, added);
-      added += i;
-      emitRow(x+0.5f, y, z+SQ3/6, 1,0,0, i-1,  0, array, added);
-      added += (i-1);
-      x += 0.5f;
-      y += 0.0f;
-      z += SQ3/2;
+      float currX = startX;
+      float currY = startY;
+
+      for(int x=0; x<layer+1; x++)
+        {
+        float currZ = startZ;
+
+        for(int z=0; z<size-layer; z++)
+          {
+          pos[index] = new Static3D(currX,currY,currZ);
+          index++;
+          currZ -= DZ;
+          }
+
+        currX += DX;
+        }
+
+      startX-=DX/2;
+      startY+=DY;
+      startZ-=DZ/2;
       }
-
-    return added;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int emitUpper(float x, float y, float z, int n, Static3D[] array, int index)
-    {
-    if( n>1 )
-      {
-      emitRow( x           , y          , z        ,  1.0f, 0,     0, n-1, -1, array, index);
-      index += (n-1);
-      emitRow( x+0.5f      , y+SQ3*SQ2/9, z+SQ3/18 ,  1.0f, 0,     0, n-1,  1, array, index);
-      index += (n-1);
-      emitRow( x+0.5f      , y          , z+SQ3/2  ,  0.5f, 0, SQ3/2, n-1, -1, array, index);
-      index += (n-1);
-      emitRow( x    +1.0f/3, y+SQ3*SQ2/9, z+2*SQ3/9,  0.5f, 0, SQ3/2, n-1,  3, array, index);
-      index += (n-1);
-      emitRow( x+n-1       , y          , z        , -0.5f, 0, SQ3/2, n-1, -1, array, index);
-      index += (n-1);
-      emitRow( x+n-1-1.0f/3, y+SQ3*SQ2/9, z+2*SQ3/9, -0.5f, 0, SQ3/2, n-1,  2, array, index);
-      index += (n-1);
-      }
-    else
-      {
-      mRotArray[index] = -1;
-      array[index] = new Static3D(x+0.5f,y+SQ2*SQ3/12,z+SQ3/6);
-      index++;
-      }
-
-    return index;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// size^2 + 3*(size-1) in the lowermost layer, then 6*(size-2) in the next, 6*(size-3) in the next,
-// ... 6 in the forelast, 1 in the last = 4size^2 - 6size +4 (if size>1)
+// there are (n^3-n)/6 octahedrons and ((n+1)^3 - (n+1))/6 tetrahedrons
 
   Static3D[] getCubitPositions(int size)
     {
-    int numCubits = size>1 ? 4*size*size - 6*size +4 : 1;
-    Static3D[] tmp = new Static3D[numCubits];
-    mRotArray = new int[numCubits];
+    int numOcta = (size-1)*size*(size+1)/6;
+    int numTetra= size*(size+1)*(size+2)/6;
+    Static3D[] ret = new Static3D[numOcta+numTetra];
 
-    int currentIndex = emitLowermost( -0.5f*size, -(SQ2*SQ3/12)*size, -(SQ3/6)*size, size, tmp);
-
-    for(int i=size-1; i>=1; i--)
-      {
-      currentIndex = emitUpper( -0.5f*i, ((SQ2*SQ3)/12)*(3*size-4*i), -(SQ3/6)*i, i, tmp, currentIndex);
-      }
+    addTetrahedralLattice(size-1,      0,ret);
+    addTetrahedralLattice(size  ,numOcta,ret);
 
-    return tmp;
+    return ret;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -231,7 +168,7 @@ public class TwistyPyraminx extends TwistyObject
 
   int getNumCubitFaces()
     {
-    return FACE_COLORS.length;
+    return 8;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -250,167 +187,48 @@ public class TwistyPyraminx extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getFaceColor(int cubit, int cubitface, int size)
+  private int faceColor(int cubit, int axis)
     {
-    boolean belongs = isOnFace(cubit, cubitface, 0 );
-    return belongs ? cubitface : NUM_FACES;
+    float row = CUBITS[cubit].mRotationRow[axis];
+    return row*row < 0.1f ? axis : NUM_FACES;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private MeshBase createStaticMesh(int cubit)
+  int getFaceColor(int cubit, int cubitface, int size)
     {
-    final float SQ2 = (float)Math.sqrt(2);
-    final float SQ3 = (float)Math.sqrt(3);
-    final float angleFaces = (float)((180/Math.PI)*(2*Math.asin(SQ3/3))); // angle between two faces of a tetrahedron
-    final int MESHES=4;
-
-    int size = getSize();
-    int association = 1;
-
-    float D = 0.0005f;
-    float E = SQ3/2 - 3*D*SQ2;
-    float F = 0.5f - D*SQ2*SQ3;
-    float[] bands;
-    int extraI, extraV;
-
-    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
-
-    switch(size)
-      {
-      case 3 : bands = new float[] { 1.0f    ,-D,
-                                     1.0f  -D,-D*0.80f,
-                                     1.0f-2*D,-D*0.65f,
-                                     1.0f-4*D,+D*0.10f,
-                                     0.50f, 0.035f,
-                                     0.0f, 0.040f };
-                      extraI = 2;
-                      extraV = 2;
-                      break;
-      case 4 : bands = new float[] { 1.0f    ,-D,
-                                     1.0f-D*1.2f,-D*0.70f,
-                                     1.0f-3*D, -D*0.15f,
-                                     0.50f, 0.035f,
-                                     0.0f, 0.040f };
-                      extraI = 2;
-                      extraV = 2;
-                      break;
-      default: bands = new float[] { 1.0f    ,-D,
-                                     1.0f-D*1.2f,-D*0.70f,
-                                     1.0f-3*D, -D*0.15f,
-                                     0.50f, 0.035f,
-                                     0.0f, 0.040f };
-                      extraI = 2;
-                      extraV = 1;
-                      break;
-      }
-
-    MeshBase[] meshes = new MeshPolygon[MESHES];
-    meshes[0] = new MeshPolygon(vertices, bands, extraI,extraV);
-    meshes[0].setEffectAssociation(0,association,0);
-
-    for(int i=1; i<MESHES; i++)
+    if( cubit< (size-1)*size*(size+1)/6 )
       {
-      association <<= 1;
-      meshes[i] = meshes[0].copy(true);
-      meshes[i].setEffectAssociation(0,association,0);
+      switch( cubitface )
+        {
+        case 0: return faceColor(cubit,0);
+        case 2: return faceColor(cubit,1);
+        case 5: return faceColor(cubit,3);
+        case 7: return faceColor(cubit,2);
+        default:return NUM_FACES;
+        }
       }
-
-    MeshBase result = new MeshJoined(meshes);
-
-    Static3D a0 = new Static3D(         0,        1,       0 );
-    Static3D a1 = new Static3D(         0,  -1.0f/3, 2*SQ2/3 );
-    Static3D a2 = new Static3D(-SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 );
-    Static3D a3 = new Static3D( SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 );
-
-    float tetraHeight = SQ2*SQ3/3;
-    float d1 = (0.75f-2*SQ2*D)*tetraHeight;
-    float d2 =-0.06f*tetraHeight;
-    float d3 = 0.05f*tetraHeight;
-    float d4 = 0.70f*tetraHeight;
-    float d5 = 1.2f;
-
-    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( d2*a0.get0(), d2*a0.get1(), d2*a0.get2() );
-    Static3D dVec1 = new Static3D( d2*a1.get0(), d2*a1.get1(), d2*a1.get2() );
-    Static3D dVec2 = new Static3D( d2*a2.get0(), d2*a2.get1(), d2*a2.get2() );
-    Static3D dVec3 = new Static3D( d2*a3.get0(), d2*a3.get1(), d2*a3.get2() );
-
-    Static4D dReg  = new Static4D(0,0,0,d3);
-    Static1D dRad  = new Static1D(1);
-    Static3D center= new Static3D(0,0,0);
-    Static4D sReg  = new Static4D(0,0,0,d4);
-    Static1D sink  = new Static1D(d5);
-
-    Static1D angle  = new Static1D(angleFaces);
-    Static3D axis1  = new Static3D(  -1, 0,      0);
-    Static3D axis2  = new Static3D(0.5f, 0, -SQ3/2);
-    Static3D axis3  = new Static3D(0.5f, 0, +SQ3/2);
-    Static3D center1= new Static3D(0,-SQ3*SQ2/12,-SQ3/6);
-    Static3D center2= new Static3D(0,-SQ3*SQ2/12,+SQ3/3);
-
-    VertexEffectRotate  effect1 = new VertexEffectRotate( new Static1D(90), new Static3D(1,0,0), center );
-    VertexEffectMove    effect2 = new VertexEffectMove  ( new Static3D(0,-SQ3*SQ2/12,0) );
-    VertexEffectRotate  effect3 = new VertexEffectRotate( new Static1D(180), new Static3D(0,0,1), center1 );
-    VertexEffectRotate  effect4 = new VertexEffectRotate( angle, axis1, center1 );
-    VertexEffectRotate  effect5 = new VertexEffectRotate( angle, axis2, center2 );
-    VertexEffectRotate  effect6 = new VertexEffectRotate( angle, axis3, center2 );
-
-    VertexEffectDeform  effect7 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
-    VertexEffectDeform  effect8 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
-    VertexEffectDeform  effect9 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
-    VertexEffectDeform  effect10= new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
-
-    VertexEffectSink effect11= new VertexEffectSink(sink,center, sReg);
-
-    effect3.setMeshAssociation(14,-1);  // apply to mesh[1], [2] and [3]
-    effect4.setMeshAssociation( 2,-1);  // apply only to mesh[1]
-    effect5.setMeshAssociation( 4,-1);  // apply only to mesh[2]
-    effect6.setMeshAssociation( 8,-1);  // apply only to mesh[3]
-
-    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);
-
-    if( mRotArray[cubit]>=0 )
+    else
       {
-      result.apply( ROTATION[mRotArray[cubit]] );
+      return cubitface<NUM_FACES ? faceColor(cubit,cubitface) : NUM_FACES;
       }
-
-    result.mergeEffComponents();
-
-    return result;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   MeshBase createCubitMesh(int cubit)
     {
-    int kind = mRotArray[cubit];
+    int size = getSize();
 
-    if( kind>=0 )
+    if( cubit< (size-1)*size*(size+1)/6 )
       {
-      if( mMeshRotated[kind]==null ) mMeshRotated[kind] = createStaticMesh(cubit);
-      return mMeshRotated[kind].copy(true);
+      if( mOctaMesh==null ) mOctaMesh = CubitFactory.getInstance().createOctaMesh();
+      return mOctaMesh.copy(true);
       }
     else
       {
-      if( mMesh==null ) mMesh = createStaticMesh(cubit);
-      return mMesh.copy(true);
+      if( mTetraMesh==null ) mTetraMesh = CubitFactory.getInstance().createTetraMesh();
+      return mTetraMesh.copy(true);
       }
     }
 
diff --git a/src/main/java/org/distorted/objects/TwistySkewb.java b/src/main/java/org/distorted/objects/TwistySkewb.java
index 7f106487..35c04e06 100644
--- a/src/main/java/org/distorted/objects/TwistySkewb.java
+++ b/src/main/java/org/distorted/objects/TwistySkewb.java
@@ -24,18 +24,10 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 
 import org.distorted.library.effect.MatrixEffectQuaternion;
-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.main.DistortedEffects;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.mesh.MeshSquare;
-import org.distorted.library.mesh.MeshTriangle;
-import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.main.RubikSurfaceView;
@@ -141,233 +133,6 @@ public class TwistySkewb extends TwistyObject
     super(size, 60, quat, texture, mesh, effects, moves, ObjectList.SKEW, res, scrWidth);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createCornerMesh()
-    {
-    float D = 0.02f;
-    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 = { 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 };
-
-    MeshBase[] meshes = new MeshBase[FACES_PER_CUBIT];
-
-    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 = { 1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f };
-
-    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);
-
-    mCornerMesh = new MeshJoined(meshes);
-
-    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);
-
-    float d0 =-0.04f;
-    float d1 = 0.04f;
-    float r0 = 0.15f;
-    float r1 = 0.10f;
-
-    Static3D vec0   = new Static3D(d0*(+SQ3/3),d0*(+SQ3/3),d0*(+SQ3/3));
-    Static3D vec1   = new Static3D(d1*(+SQ3/3),d1*(-SQ3/3),d1*(-SQ3/3));
-    Static3D vec2   = new Static3D(d1*(-SQ3/3),d1*(+SQ3/3),d1*(-SQ3/3));
-    Static3D vec3   = new Static3D(d1*(-SQ3/3),d1*(-SQ3/3),d1*(+SQ3/3));
-
-    Static1D radius = new Static1D(0.5f);
-
-    Static3D cent0  = new Static3D( 0.0f, 0.0f, 0.0f);
-    Static3D cent1  = new Static3D(-0.5f, 0.0f, 0.0f);
-    Static3D cent2  = new Static3D( 0.0f,-0.5f, 0.0f);
-    Static3D cent3  = new Static3D( 0.0f, 0.0f,-0.5f);
-
-    Static4D reg0   = new Static4D(0,0,0,r0);
-    Static4D reg1   = new Static4D(0,0,0,r1);
-
-    VertexEffectMove   effect0 = new VertexEffectMove(move1);
-    VertexEffectScale  effect1 = new VertexEffectScale(new Static3D(1,1,-1));
-    VertexEffectRotate effect2 = new VertexEffectRotate(angle1,axisX,center1);
-    VertexEffectRotate effect3 = new VertexEffectRotate(angle2,axisY,center1);
-    VertexEffectMove   effect4 = new VertexEffectMove(move2);
-    VertexEffectRotate effect5 = new VertexEffectRotate(angle1,axisX,center2);
-    VertexEffectRotate effect6 = new VertexEffectRotate(angle3,axisY,center2);
-    VertexEffectRotate effect7 = new VertexEffectRotate(angle4,axis0,center2);
-    VertexEffectRotate effect8 = new VertexEffectRotate(angle5,axis1,center2);
-    VertexEffectRotate effect9 = new VertexEffectRotate(angle6,axis1,center2);
-
-    VertexEffectDeform effect10= new VertexEffectDeform(vec0,radius,cent0,reg0);
-    VertexEffectDeform effect11= new VertexEffectDeform(vec1,radius,cent1,reg1);
-    VertexEffectDeform effect12= new VertexEffectDeform(vec2,radius,cent2,reg1);
-    VertexEffectDeform effect13= new VertexEffectDeform(vec3,radius,cent3,reg1);
-
-    effect0.setMeshAssociation( 7,-1);  // meshes 0,1,2
-    effect1.setMeshAssociation( 6,-1);  // meshes 1,2
-    effect2.setMeshAssociation( 2,-1);  // mesh 1
-    effect3.setMeshAssociation( 4,-1);  // mesh 2
-    effect4.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect5.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect6.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect7.setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect8.setMeshAssociation(16,-1);  // mesh 4
-    effect9.setMeshAssociation(32,-1);  // mesh 5
-
-    effect10.setMeshAssociation(63,-1); // all meshes
-    effect11.setMeshAssociation(63,-1); // all meshes
-    effect12.setMeshAssociation(63,-1); // all meshes
-    effect13.setMeshAssociation(63,-1); // all meshes
-
-    mCornerMesh.apply(effect0);
-    mCornerMesh.apply(effect1);
-    mCornerMesh.apply(effect2);
-    mCornerMesh.apply(effect3);
-    mCornerMesh.apply(effect4);
-    mCornerMesh.apply(effect5);
-    mCornerMesh.apply(effect6);
-    mCornerMesh.apply(effect7);
-    mCornerMesh.apply(effect8);
-    mCornerMesh.apply(effect9);
-
-    mCornerMesh.apply(effect10);
-    mCornerMesh.apply(effect11);
-    mCornerMesh.apply(effect12);
-    mCornerMesh.apply(effect13);
-
-    mCornerMesh.mergeEffComponents();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createFaceMesh()
-    {
-    int association = 1;
-
-    float D = 0.03f;
-    float E = SQ2/4;
-    float[] vertices0 = { -E,-E, +E,-E, +E,+E, -E,+E };
-
-    float[] bands0 = { 1.0f    , 0,
-                       1.0f-D/2, D*0.30f,
-                       1.0f- D , D*0.50f,
-                       1.0f-2*D, D*0.80f,
-                       0.60f   , D*1.40f,
-                       0.30f   , D*1.60f,
-                       0.0f    , D*1.70f };
-
-    MeshBase[] meshes = new MeshBase[FACES_PER_CUBIT];
-    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
-    meshes[0].setEffectAssociation(0,association,0);
-
-    association <<= 1;
-
-    float[] vertices1 = { -E,-SQ3*E, +E,-SQ3*E, 0,0 };
-    float[] bands1 = { 1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f };
-
-    meshes[1] = new MeshPolygon(vertices1,bands1,0,0);
-    meshes[1].setEffectAssociation(0,association,0);
-
-    for(int i=2; i<FACES_PER_CUBIT-1; i++)
-      {
-      association <<= 1;
-      meshes[i] = meshes[1].copy(true);
-      meshes[i].setEffectAssociation(0,association,0);
-      }
-
-    association <<= 1;
-    meshes[FACES_PER_CUBIT-1] = new MeshTriangle(1);                  // empty triangle so that
-    meshes[FACES_PER_CUBIT-1].setEffectAssociation(0,association,0);  // all cubits have 6 faces
-
-    mFaceMesh = new MeshJoined(meshes);
-
-    Static3D center = new Static3D(0,0,0);
-    Static3D axis1   = new Static3D(1,0,0);
-    Static3D axis2   = new Static3D(0,0,1);
-    float angle = -(float)((180.0f/Math.PI)*Math.acos(SQ3/3));
-
-    float f = 0.05f;
-    float r = 0.10f;
-    float d = 0.5f;
-    float e = +D*0.6f;
-    Static3D vector0 = new Static3D(-f, 0, 0);
-    Static3D vector1 = new Static3D( 0,+f, 0);
-    Static3D vector2 = new Static3D(+f, 0, 0);
-    Static3D vector3 = new Static3D( 0,-f, 0);
-    Static1D radius  = new Static1D(1.0f);
-    Static4D region  = new Static4D(0,0,0,r);
-    Static3D center0 = new Static3D(+d, 0, e);
-    Static3D center1 = new Static3D( 0,-d, e);
-    Static3D center2 = new Static3D(-d, 0, e);
-    Static3D center3 = new Static3D( 0,+d, e);
-
-    VertexEffectRotate effect0 = new VertexEffectRotate( new Static1D(angle), axis1, center);
-    VertexEffectRotate effect1 = new VertexEffectRotate( new Static1D(  135), axis2, center);
-    VertexEffectRotate effect2 = new VertexEffectRotate( new Static1D(   45), axis2, center);
-    VertexEffectRotate effect3 = new VertexEffectRotate( new Static1D(  -45), axis2, center);
-    VertexEffectRotate effect4 = new VertexEffectRotate( new Static1D( -135), axis2, center);
-    VertexEffectMove   effect5 = new VertexEffectMove( new Static3D(0,0,-0.5f) );
-    VertexEffectDeform effect6 = new VertexEffectDeform(vector0,radius,center0,region);
-    VertexEffectDeform effect7 = new VertexEffectDeform(vector1,radius,center1,region);
-    VertexEffectDeform effect8 = new VertexEffectDeform(vector2,radius,center2,region);
-    VertexEffectDeform effect9 = new VertexEffectDeform(vector3,radius,center3,region);
-    VertexEffectScale  effect10= new VertexEffectScale(0.01f);
-
-    effect0.setMeshAssociation(30,-1);  // meshes 1,2,3,4
-    effect1.setMeshAssociation( 2,-1);  // mesh 1
-    effect2.setMeshAssociation( 5,-1);  // meshes 0,2
-    effect3.setMeshAssociation( 8,-1);  // mesh 3
-    effect4.setMeshAssociation(16,-1);  // mesh 4
-    effect5.setMeshAssociation(30,-1);  // meshes 1,2,3,4
-    effect6.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
-    effect7.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
-    effect8.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
-    effect9.setMeshAssociation(31,-1);  // meshes 0,1,2,3,4
-    effect10.setMeshAssociation(32,-1); // mesh 5
-
-    mFaceMesh.apply(effect0);
-    mFaceMesh.apply(effect1);
-    mFaceMesh.apply(effect2);
-    mFaceMesh.apply(effect3);
-    mFaceMesh.apply(effect4);
-    mFaceMesh.apply(effect5);
-    mFaceMesh.apply(effect6);
-    mFaceMesh.apply(effect7);
-    mFaceMesh.apply(effect8);
-    mFaceMesh.apply(effect9);
-    mFaceMesh.apply(effect10);
-
-    mFaceMesh.mergeEffComponents();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
@@ -458,12 +223,12 @@ public class TwistySkewb extends TwistyObject
 
     if( cubit<8 )
       {
-      if( mCornerMesh==null ) createCornerMesh();
+      if( mCornerMesh==null ) mCornerMesh = CubitFactory.getInstance().createSkewbCornerMesh();
       mesh = mCornerMesh.copy(true);
       }
     else
       {
-      if( mFaceMesh==null ) createFaceMesh();
+      if( mFaceMesh==null ) mFaceMesh = CubitFactory.getInstance().createSkewbFaceMesh();
       mesh = mFaceMesh.copy(true);
       }
 
diff --git a/src/main/res/raw/compute_quats.c b/src/main/res/raw/compute_quats.c
index ca64ede1..4e281c38 100644
--- a/src/main/res/raw/compute_quats.c
+++ b/src/main/res/raw/compute_quats.c
@@ -2,7 +2,7 @@
 #include <math.h>
 #include <stdlib.h>
 
-#define DIAM
+#define PYRA
 
 #define SQ2 1.41421356237f
 #define SQ3 1.73205080757f
@@ -13,10 +13,10 @@
 #define NUM_AXIS    4
 #define BASIC_ANGLE 3
 
-float axis[NUM_AXIS][3] = { {         0,       1,       0 } ,
-                            { SQ2*SQ3/3, -1.0f/3,  -SQ2/3 } ,
-                            {-SQ2*SQ3/3, -1.0f/3,  -SQ2/3 } ,
-                            {         0, -1.0f/3, 2*SQ2/3 } };
+float axis[NUM_AXIS][3] = { { SQ2*SQ3/3,   SQ3/3,          0 } ,
+                            {-SQ2*SQ3/3,   SQ3/3,          0 } ,
+                            {         0,  -SQ3/3, -SQ2*SQ3/3 } ,
+                            {         0,  -SQ3/3,  SQ2*SQ3/3 } };
 #endif
 
 #ifdef CUBE
