commit ebe0476f1b052746c7616ceed874fe9b6c092c78
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Dec 19 22:53:06 2018 +0000

    Simplify MeshSphere by deriving it not from a regular icosahedron (which has a problem of a wiggly seam line at the 'change of date' in the back of the sphere) but something that at the back has a straight vertical border between triangles: a diamond-like 16-faced solid.

diff --git a/src/main/java/org/distorted/library/mesh/MeshSphere.java b/src/main/java/org/distorted/library/mesh/MeshSphere.java
index 3302419..5a4ac8c 100644
--- a/src/main/java/org/distorted/library/mesh/MeshSphere.java
+++ b/src/main/java/org/distorted/library/mesh/MeshSphere.java
@@ -23,98 +23,81 @@ package org.distorted.library.mesh;
 /**
  * Create a Mesh which approximates a sphere.
  * <p>
- * Do so by dividing each of the 20 faces of the regular icosahedron into smaller triangles and inflating
- * those to lay on the surface of the sphere.
+ * Do so by starting off with a 16-faced solid which is basically a regular dodecahedron with each
+ * of its 8 faces vertically split into 2 triangles, and which each step divide each of its triangular
+ * faces into smaller and smaller subtriangles and inflate their vertices to lay on the surface or the
+ * sphere.
  */
 public class MeshSphere extends MeshBase
   {
-  private static final int NUMFACES = 20;
+  private static final int NUMFACES = 16;
   private static final double sqrt2 = Math.sqrt(2.0);
   private static final double P = Math.PI;
-  private static final double A = 0.463647609; // arctan(0.5), +-latitude of the 10 'middle' vertices
-                                               // https://en.wikipedia.org/wiki/Regular_icosahedron
 
-  // An array of 20 entries, each describing a single face of the regular icosahedron in an (admittedly)
-  // weird fashion.
-  // Each face of a regular icosahedron is a equilateral triangle, with 2 vertices on the same latitude.
+  // An array of 16 entries, each describing a single face of the solid in an (admittedly) weird
+  // fashion. Each face is a triangle, with 2 vertices on the same latitude.
   // Single row is (longitude of V1, longitude of V2, (common) latitude of V1 and V2, latitude of V3)
   // longitude of V3 is simply midpoint of V1 and V2 so we don't have to specify it here.
 
   private static final double FACES[][] =      {
-                                                   { 0.0*P, 0.4*P,  A, 0.5*P },
-                                                   { 0.4*P, 0.8*P,  A, 0.5*P },
-                                                   { 0.8*P, 1.2*P,  A, 0.5*P },  // 5 'top' faces with
-                                                   { 1.2*P, 1.6*P,  A, 0.5*P },  // the North Pole
-                                                   { 1.6*P, 2.0*P,  A, 0.5*P },
-
-                                                   { 0.0*P, 0.4*P,  A,    -A },
-                                                   { 0.4*P, 0.8*P,  A,    -A },
-                                                   { 0.8*P, 1.2*P,  A,    -A },  // 5 faces mostly above
-                                                   { 1.2*P, 1.6*P,  A,    -A },  // the equator
-                                                   { 1.6*P, 2.0*P,  A,    -A },
-
-                                                   { 0.2*P, 0.6*P, -A,     A },
-                                                   { 0.6*P, 1.0*P, -A,     A },
-                                                   { 1.0*P, 1.4*P, -A,     A },  // 5 faces mostly below
-                                                   { 1.4*P, 1.8*P, -A,     A },  // the equator
-                                                   { 1.8*P, 0.2*P, -A,     A },
-
-                                                   { 0.2*P, 0.6*P, -A,-0.5*P },
-                                                   { 0.6*P, 1.0*P, -A,-0.5*P },
-                                                   { 1.0*P, 1.4*P, -A,-0.5*P },  // 5 'bottom' faces with
-                                                   { 1.4*P, 1.8*P, -A,-0.5*P },  // the South Pole
-                                                   { 1.8*P, 0.2*P, -A,-0.5*P },
+
+      { 0.00*P, 0.25*P, 0.0, 0.5*P },
+      { 0.25*P, 0.50*P, 0.0, 0.5*P },
+      { 0.50*P, 0.75*P, 0.0, 0.5*P },
+      { 0.75*P, 1.00*P, 0.0, 0.5*P },
+      { 1.00*P, 1.25*P, 0.0, 0.5*P },
+      { 1.25*P, 1.50*P, 0.0, 0.5*P },
+      { 1.50*P, 1.75*P, 0.0, 0.5*P },
+      { 1.75*P, 0.00*P, 0.0, 0.5*P },
+
+      { 0.00*P, 0.25*P, 0.0,-0.5*P },
+      { 0.25*P, 0.50*P, 0.0,-0.5*P },
+      { 0.50*P, 0.75*P, 0.0,-0.5*P },
+      { 0.75*P, 1.00*P, 0.0,-0.5*P },
+      { 1.00*P, 1.25*P, 0.0,-0.5*P },
+      { 1.25*P, 1.50*P, 0.0,-0.5*P },
+      { 1.50*P, 1.75*P, 0.0,-0.5*P },
+      { 1.75*P, 0.00*P, 0.0,-0.5*P },
                                                };
   private int currentVert;
   private int numVertices;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// Each of the 20 faces of the icosahedron requires (level*level + 4*level) vertices for the face
+// Each of the 16 faces of the solid requires (level*level + 4*level) vertices for the face
 // itself and a join to the next face (which requires 2 vertices). We don't need the join in case
-// of the last, 20th face, thus the -2.
+// of the last, 16th face, thus the -2.
 // (level*level +4*level) because there are level*level little triangles, each requiring new vertex,
 // plus 2 extra vertices to start off a row and 2 to move to the next row (or the next face in case
 // of the last row) and there are 'level' rows.
-//
-// Now to this we need to add 6*(level-1) vertices for the internal seams in the three triangles
-// in the back ( 2,7,16 in the list above ): 3 triangles, level-1 rows of more than 1 triangle in
-// each, 2 extra seam vertices in each row.
 
   private void computeNumberOfVertices(int level)
     {
-    numVertices = NUMFACES*level*(level+4) -2  + 6*(level-1);
+    numVertices = NUMFACES*level*(level+4) -2;
     currentVert = 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int convertAng(double radian)
-    {
-    return (int)(radian*180.0/P);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void repeatVertex(float[] attribs, int diff)
+  private void repeatVertex(float[] attribs)
     {
     //android.util.Log.e("sphere", "repeat last!");
 
     if( currentVert>0 )
       {
-      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + POS_ATTRIB  ];
-      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + POS_ATTRIB+1];
-      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-diff) + POS_ATTRIB+2];
+      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + POS_ATTRIB  ];
+      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + POS_ATTRIB+1];
+      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-1) + POS_ATTRIB+2];
 
-      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + NOR_ATTRIB  ];
-      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + NOR_ATTRIB+1];
-      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-diff) + NOR_ATTRIB+2];
+      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + NOR_ATTRIB  ];
+      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + NOR_ATTRIB+1];
+      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-1) + NOR_ATTRIB+2];
 
-      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + INF_ATTRIB  ];
-      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + INF_ATTRIB+1];
-      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-diff) + INF_ATTRIB+2];
+      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + INF_ATTRIB  ];
+      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + INF_ATTRIB+1];
+      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-1) + INF_ATTRIB+2];
 
-      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + TEX_ATTRIB  ];
-      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + TEX_ATTRIB+1];
+      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB  ];
+      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB+1];
 
       currentVert++;
       }
@@ -172,7 +155,7 @@ public class MeshSphere extends MeshBase
 // (level-1,       0, level) -> (lonV2,latV12)
 
   private void addVertex(float[] attribs, int column, int row, int level,
-                         double lonV1, double lonV2, double latV12, double latV3, boolean lastInRow)
+                         double lonV1, double lonV2, double latV12, double latV3)
     {
     double quotX = (double)column/level;
     double quotY = (double)row   /level;
@@ -227,82 +210,21 @@ public class MeshSphere extends MeshBase
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
     // Problem: on the 'change of date' line in the back of the sphere, some triangles see texX
-    // coords suddenly jump from 1-epsilon to 0+epsilon, which looks like a seam with a narrow copy
-    // of the whole texture there.
-    // Solution: each such 'jump' triangle, if it is the last in a row, should have the texX of its
-    // last (and maybe forelast as well) vertex remapped to 1.0.
-    // If such triangle is not the last in its row, we need to add two extra vertices between it and
-    // the next one, remap the old ones' texX to 1.0, and set one of the two new ones' texX to 0.0.
+    // coords suddenly jump from 1-epsilon to 0, which looks like a seam with a narrow copy of
+    // the whole texture there. Solution: remap texX to 1.0.
     ////////////////////////////////////////////////////////////////////////////////////////////////
 
-    if( currentVert>=3 )
+    if( currentVert>=3 && texX==0.0 )
       {
       double tex1 = attribs[VERT_ATTRIBS*(currentVert-2) + TEX_ATTRIB];
       double tex2 = attribs[VERT_ATTRIBS*(currentVert-3) + TEX_ATTRIB];
-      double y1   = attribs[VERT_ATTRIBS*(currentVert-2) + INF_ATTRIB + 1];
-      double y2   = attribs[VERT_ATTRIBS*(currentVert-3) + INF_ATTRIB + 1];
 
-      if( tex1!=tex2 || y1!=y2 )  // if the triangle is not degenerate
+      // if the triangle is not degenerate and last vertex was on the western hemisphere
+      if( tex1!=tex2 && tex1>0.5 )
         {
-        double diff1 = Math.abs(texX-tex1);
-        double diff2 = Math.abs(texX-tex2);
-
-        if( diff1>0.5 || diff2>0.5 )    // a jump in texture coords which can only happen along the
-          {                             // 'date change' line. Have to correct!
-          if( lastInRow )
-            {
-                            attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
-            if( tex1<tex2 ) attribs[VERT_ATTRIBS*(currentVert-2) + TEX_ATTRIB] = 1.0f;
-            }
-          else
-            {
-            if( latitude> -A )  // one of the top two triangles being corrected
-              {
-              if (2*column+row-1 == level)  // last such triangle in a row; have to introduce extra 2 vertices.
-                {
-                /*
-                android.util.Log.e("sphere", "LAST max diff exceeded: " + convertAng(longitude) + " " + convertAng(latitude) + " texX=" + texX);
-                android.util.Log.d("sphere", " tex1=" + tex1 + " tex2=" + tex2 + " y1=" + y1 + " y2=" + y2);
-                android.util.Log.d("sphere", " latV12=" + latV12 + " latV3=" + latV3);
-                */
-                repeatVertex(attribs, 2);
-                repeatVertex(attribs, 2);
-
-                attribs[VERT_ATTRIBS*(currentVert-2) + TEX_ATTRIB] = 0.0f;
-                attribs[VERT_ATTRIBS*(currentVert-3) + TEX_ATTRIB] = 1.0f;
-
-                if (tex1 < tex2) attribs[VERT_ATTRIBS*(currentVert-4) + TEX_ATTRIB] = 1.0f;
-                }
-              if (2*column+row == level)    // not the last; just remap current vertex' texX to 1.0.
-                {
-                /*
-                android.util.Log.e("sphere", "NOT LAST max diff exceeded: " + convertAng(longitude) + " " + convertAng(latitude) + " texX=" + texX);
-                android.util.Log.d("sphere", " tex1=" + tex1 + " tex2=" + tex2 + " y1=" + y1 + " y2=" + y2);
-                android.util.Log.d("sphere", " latV12=" + latV12 + " latV3=" + latV3);
-                */
-                attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
-                }
-              }
-            else // bottom triangle
-              {
-              /*
-              android.util.Log.e("sphere", "BOTTOM LAST max diff exceeded: " + convertAng(longitude) + " " + convertAng(latitude) + " texX=" + texX);
-              android.util.Log.d("sphere", " tex1=" + tex1 + " tex2=" + tex2 + " y1=" + y1 + " y2=" + y2);
-              android.util.Log.d("sphere", " latV12=" + latV12 + " latV3=" + latV3);
-              */
-              repeatVertex(attribs, 2);
-              repeatVertex(attribs, 2);
-
-              attribs[VERT_ATTRIBS*(currentVert-3) + TEX_ATTRIB] = 1.0f;
-              }
-            }
-          }
+        attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
         }
       }
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // End problem
-    ////////////////////////////////////////////////////////////////////////////////////////////////
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -313,13 +235,13 @@ public class MeshSphere extends MeshBase
       {
       for (int column=0; column<level-row; column++)
         {
-        addVertex(attribs, column, row  , level, lonV1, lonV2, latV12, latV3, false);
-        if (column==0 && !(face==0 && row==0 ) ) repeatVertex(attribs,1);
-        addVertex(attribs, column, row+1, level, lonV1, lonV2, latV12, latV3, false);
+        addVertex(attribs, column, row  , level, lonV1, lonV2, latV12, latV3);
+        if (column==0 && !(face==0 && row==0 ) ) repeatVertex(attribs);
+        addVertex(attribs, column, row+1, level, lonV1, lonV2, latV12, latV3);
         }
 
-      addVertex(attribs, level-row, row , level, lonV1, lonV2, latV12, latV3, true);
-      if( row!=level-1 || face!=NUMFACES-1 ) repeatVertex(attribs,1);
+      addVertex(attribs, level-row, row , level, lonV1, lonV2, latV12, latV3);
+      if( row!=level-1 || face!=NUMFACES-1 ) repeatVertex(attribs);
       }
     }
 
