Project

General

Profile

« Previous | Next » 

Revision ebe0476f

Added by Leszek Koltunski over 5 years ago

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.

View differences:

src/main/java/org/distorted/library/mesh/MeshSphere.java
23 23
/**
24 24
 * Create a Mesh which approximates a sphere.
25 25
 * <p>
26
 * Do so by dividing each of the 20 faces of the regular icosahedron into smaller triangles and inflating
27
 * those to lay on the surface of the sphere.
26
 * Do so by starting off with a 16-faced solid which is basically a regular dodecahedron with each
27
 * of its 8 faces vertically split into 2 triangles, and which each step divide each of its triangular
28
 * faces into smaller and smaller subtriangles and inflate their vertices to lay on the surface or the
29
 * sphere.
28 30
 */
29 31
public class MeshSphere extends MeshBase
30 32
  {
31
  private static final int NUMFACES = 20;
33
  private static final int NUMFACES = 16;
32 34
  private static final double sqrt2 = Math.sqrt(2.0);
33 35
  private static final double P = Math.PI;
34
  private static final double A = 0.463647609; // arctan(0.5), +-latitude of the 10 'middle' vertices
35
                                               // https://en.wikipedia.org/wiki/Regular_icosahedron
36 36

  
37
  // An array of 20 entries, each describing a single face of the regular icosahedron in an (admittedly)
38
  // weird fashion.
39
  // Each face of a regular icosahedron is a equilateral triangle, with 2 vertices on the same latitude.
37
  // An array of 16 entries, each describing a single face of the solid in an (admittedly) weird
38
  // fashion. Each face is a triangle, with 2 vertices on the same latitude.
40 39
  // Single row is (longitude of V1, longitude of V2, (common) latitude of V1 and V2, latitude of V3)
41 40
  // longitude of V3 is simply midpoint of V1 and V2 so we don't have to specify it here.
42 41

  
43 42
  private static final double FACES[][] =      {
44
                                                   { 0.0*P, 0.4*P,  A, 0.5*P },
45
                                                   { 0.4*P, 0.8*P,  A, 0.5*P },
46
                                                   { 0.8*P, 1.2*P,  A, 0.5*P },  // 5 'top' faces with
47
                                                   { 1.2*P, 1.6*P,  A, 0.5*P },  // the North Pole
48
                                                   { 1.6*P, 2.0*P,  A, 0.5*P },
49

  
50
                                                   { 0.0*P, 0.4*P,  A,    -A },
51
                                                   { 0.4*P, 0.8*P,  A,    -A },
52
                                                   { 0.8*P, 1.2*P,  A,    -A },  // 5 faces mostly above
53
                                                   { 1.2*P, 1.6*P,  A,    -A },  // the equator
54
                                                   { 1.6*P, 2.0*P,  A,    -A },
55

  
56
                                                   { 0.2*P, 0.6*P, -A,     A },
57
                                                   { 0.6*P, 1.0*P, -A,     A },
58
                                                   { 1.0*P, 1.4*P, -A,     A },  // 5 faces mostly below
59
                                                   { 1.4*P, 1.8*P, -A,     A },  // the equator
60
                                                   { 1.8*P, 0.2*P, -A,     A },
61

  
62
                                                   { 0.2*P, 0.6*P, -A,-0.5*P },
63
                                                   { 0.6*P, 1.0*P, -A,-0.5*P },
64
                                                   { 1.0*P, 1.4*P, -A,-0.5*P },  // 5 'bottom' faces with
65
                                                   { 1.4*P, 1.8*P, -A,-0.5*P },  // the South Pole
66
                                                   { 1.8*P, 0.2*P, -A,-0.5*P },
43

  
44
      { 0.00*P, 0.25*P, 0.0, 0.5*P },
45
      { 0.25*P, 0.50*P, 0.0, 0.5*P },
46
      { 0.50*P, 0.75*P, 0.0, 0.5*P },
47
      { 0.75*P, 1.00*P, 0.0, 0.5*P },
48
      { 1.00*P, 1.25*P, 0.0, 0.5*P },
49
      { 1.25*P, 1.50*P, 0.0, 0.5*P },
50
      { 1.50*P, 1.75*P, 0.0, 0.5*P },
51
      { 1.75*P, 0.00*P, 0.0, 0.5*P },
52

  
53
      { 0.00*P, 0.25*P, 0.0,-0.5*P },
54
      { 0.25*P, 0.50*P, 0.0,-0.5*P },
55
      { 0.50*P, 0.75*P, 0.0,-0.5*P },
56
      { 0.75*P, 1.00*P, 0.0,-0.5*P },
57
      { 1.00*P, 1.25*P, 0.0,-0.5*P },
58
      { 1.25*P, 1.50*P, 0.0,-0.5*P },
59
      { 1.50*P, 1.75*P, 0.0,-0.5*P },
60
      { 1.75*P, 0.00*P, 0.0,-0.5*P },
67 61
                                               };
68 62
  private int currentVert;
69 63
  private int numVertices;
70 64

  
71 65
///////////////////////////////////////////////////////////////////////////////////////////////////
72
// Each of the 20 faces of the icosahedron requires (level*level + 4*level) vertices for the face
66
// Each of the 16 faces of the solid requires (level*level + 4*level) vertices for the face
73 67
// itself and a join to the next face (which requires 2 vertices). We don't need the join in case
74
// of the last, 20th face, thus the -2.
68
// of the last, 16th face, thus the -2.
75 69
// (level*level +4*level) because there are level*level little triangles, each requiring new vertex,
76 70
// plus 2 extra vertices to start off a row and 2 to move to the next row (or the next face in case
77 71
// of the last row) and there are 'level' rows.
78
//
79
// Now to this we need to add 6*(level-1) vertices for the internal seams in the three triangles
80
// in the back ( 2,7,16 in the list above ): 3 triangles, level-1 rows of more than 1 triangle in
81
// each, 2 extra seam vertices in each row.
82 72

  
83 73
  private void computeNumberOfVertices(int level)
84 74
    {
85
    numVertices = NUMFACES*level*(level+4) -2  + 6*(level-1);
75
    numVertices = NUMFACES*level*(level+4) -2;
86 76
    currentVert = 0;
87 77
    }
88 78

  
89 79
///////////////////////////////////////////////////////////////////////////////////////////////////
90 80

  
91
  private int convertAng(double radian)
92
    {
93
    return (int)(radian*180.0/P);
94
    }
95

  
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97

  
98
  private void repeatVertex(float[] attribs, int diff)
81
  private void repeatVertex(float[] attribs)
99 82
    {
100 83
    //android.util.Log.e("sphere", "repeat last!");
101 84

  
102 85
    if( currentVert>0 )
103 86
      {
104
      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + POS_ATTRIB  ];
105
      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + POS_ATTRIB+1];
106
      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-diff) + POS_ATTRIB+2];
87
      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + POS_ATTRIB  ];
88
      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + POS_ATTRIB+1];
89
      attribs[VERT_ATTRIBS*currentVert + POS_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-1) + POS_ATTRIB+2];
107 90

  
108
      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + NOR_ATTRIB  ];
109
      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + NOR_ATTRIB+1];
110
      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-diff) + NOR_ATTRIB+2];
91
      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + NOR_ATTRIB  ];
92
      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + NOR_ATTRIB+1];
93
      attribs[VERT_ATTRIBS*currentVert + NOR_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-1) + NOR_ATTRIB+2];
111 94

  
112
      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + INF_ATTRIB  ];
113
      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + INF_ATTRIB+1];
114
      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-diff) + INF_ATTRIB+2];
95
      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + INF_ATTRIB  ];
96
      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + INF_ATTRIB+1];
97
      attribs[VERT_ATTRIBS*currentVert + INF_ATTRIB+2] = attribs[VERT_ATTRIBS*(currentVert-1) + INF_ATTRIB+2];
115 98

  
116
      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-diff) + TEX_ATTRIB  ];
117
      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-diff) + TEX_ATTRIB+1];
99
      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB  ] = attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB  ];
100
      attribs[VERT_ATTRIBS*currentVert + TEX_ATTRIB+1] = attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB+1];
118 101

  
119 102
      currentVert++;
120 103
      }
......
172 155
// (level-1,       0, level) -> (lonV2,latV12)
173 156

  
174 157
  private void addVertex(float[] attribs, int column, int row, int level,
175
                         double lonV1, double lonV2, double latV12, double latV3, boolean lastInRow)
158
                         double lonV1, double lonV2, double latV12, double latV3)
176 159
    {
177 160
    double quotX = (double)column/level;
178 161
    double quotY = (double)row   /level;
......
227 210

  
228 211
    ////////////////////////////////////////////////////////////////////////////////////////////////
229 212
    // Problem: on the 'change of date' line in the back of the sphere, some triangles see texX
230
    // coords suddenly jump from 1-epsilon to 0+epsilon, which looks like a seam with a narrow copy
231
    // of the whole texture there.
232
    // Solution: each such 'jump' triangle, if it is the last in a row, should have the texX of its
233
    // last (and maybe forelast as well) vertex remapped to 1.0.
234
    // If such triangle is not the last in its row, we need to add two extra vertices between it and
235
    // the next one, remap the old ones' texX to 1.0, and set one of the two new ones' texX to 0.0.
213
    // coords suddenly jump from 1-epsilon to 0, which looks like a seam with a narrow copy of
214
    // the whole texture there. Solution: remap texX to 1.0.
236 215
    ////////////////////////////////////////////////////////////////////////////////////////////////
237 216

  
238
    if( currentVert>=3 )
217
    if( currentVert>=3 && texX==0.0 )
239 218
      {
240 219
      double tex1 = attribs[VERT_ATTRIBS*(currentVert-2) + TEX_ATTRIB];
241 220
      double tex2 = attribs[VERT_ATTRIBS*(currentVert-3) + TEX_ATTRIB];
242
      double y1   = attribs[VERT_ATTRIBS*(currentVert-2) + INF_ATTRIB + 1];
243
      double y2   = attribs[VERT_ATTRIBS*(currentVert-3) + INF_ATTRIB + 1];
244 221

  
245
      if( tex1!=tex2 || y1!=y2 )  // if the triangle is not degenerate
222
      // if the triangle is not degenerate and last vertex was on the western hemisphere
223
      if( tex1!=tex2 && tex1>0.5 )
246 224
        {
247
        double diff1 = Math.abs(texX-tex1);
248
        double diff2 = Math.abs(texX-tex2);
249

  
250
        if( diff1>0.5 || diff2>0.5 )    // a jump in texture coords which can only happen along the
251
          {                             // 'date change' line. Have to correct!
252
          if( lastInRow )
253
            {
254
                            attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
255
            if( tex1<tex2 ) attribs[VERT_ATTRIBS*(currentVert-2) + TEX_ATTRIB] = 1.0f;
256
            }
257
          else
258
            {
259
            if( latitude> -A )  // one of the top two triangles being corrected
260
              {
261
              if (2*column+row-1 == level)  // last such triangle in a row; have to introduce extra 2 vertices.
262
                {
263
                /*
264
                android.util.Log.e("sphere", "LAST max diff exceeded: " + convertAng(longitude) + " " + convertAng(latitude) + " texX=" + texX);
265
                android.util.Log.d("sphere", " tex1=" + tex1 + " tex2=" + tex2 + " y1=" + y1 + " y2=" + y2);
266
                android.util.Log.d("sphere", " latV12=" + latV12 + " latV3=" + latV3);
267
                */
268
                repeatVertex(attribs, 2);
269
                repeatVertex(attribs, 2);
270

  
271
                attribs[VERT_ATTRIBS*(currentVert-2) + TEX_ATTRIB] = 0.0f;
272
                attribs[VERT_ATTRIBS*(currentVert-3) + TEX_ATTRIB] = 1.0f;
273

  
274
                if (tex1 < tex2) attribs[VERT_ATTRIBS*(currentVert-4) + TEX_ATTRIB] = 1.0f;
275
                }
276
              if (2*column+row == level)    // not the last; just remap current vertex' texX to 1.0.
277
                {
278
                /*
279
                android.util.Log.e("sphere", "NOT LAST max diff exceeded: " + convertAng(longitude) + " " + convertAng(latitude) + " texX=" + texX);
280
                android.util.Log.d("sphere", " tex1=" + tex1 + " tex2=" + tex2 + " y1=" + y1 + " y2=" + y2);
281
                android.util.Log.d("sphere", " latV12=" + latV12 + " latV3=" + latV3);
282
                */
283
                attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
284
                }
285
              }
286
            else // bottom triangle
287
              {
288
              /*
289
              android.util.Log.e("sphere", "BOTTOM LAST max diff exceeded: " + convertAng(longitude) + " " + convertAng(latitude) + " texX=" + texX);
290
              android.util.Log.d("sphere", " tex1=" + tex1 + " tex2=" + tex2 + " y1=" + y1 + " y2=" + y2);
291
              android.util.Log.d("sphere", " latV12=" + latV12 + " latV3=" + latV3);
292
              */
293
              repeatVertex(attribs, 2);
294
              repeatVertex(attribs, 2);
295

  
296
              attribs[VERT_ATTRIBS*(currentVert-3) + TEX_ATTRIB] = 1.0f;
297
              }
298
            }
299
          }
225
        attribs[VERT_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
300 226
        }
301 227
      }
302

  
303
    ////////////////////////////////////////////////////////////////////////////////////////////////
304
    // End problem
305
    ////////////////////////////////////////////////////////////////////////////////////////////////
306 228
    }
307 229

  
308 230
///////////////////////////////////////////////////////////////////////////////////////////////////
......
313 235
      {
314 236
      for (int column=0; column<level-row; column++)
315 237
        {
316
        addVertex(attribs, column, row  , level, lonV1, lonV2, latV12, latV3, false);
317
        if (column==0 && !(face==0 && row==0 ) ) repeatVertex(attribs,1);
318
        addVertex(attribs, column, row+1, level, lonV1, lonV2, latV12, latV3, false);
238
        addVertex(attribs, column, row  , level, lonV1, lonV2, latV12, latV3);
239
        if (column==0 && !(face==0 && row==0 ) ) repeatVertex(attribs);
240
        addVertex(attribs, column, row+1, level, lonV1, lonV2, latV12, latV3);
319 241
        }
320 242

  
321
      addVertex(attribs, level-row, row , level, lonV1, lonV2, latV12, latV3, true);
322
      if( row!=level-1 || face!=NUMFACES-1 ) repeatVertex(attribs,1);
243
      addVertex(attribs, level-row, row , level, lonV1, lonV2, latV12, latV3);
244
      if( row!=level-1 || face!=NUMFACES-1 ) repeatVertex(attribs);
323 245
      }
324 246
    }
325 247

  

Also available in: Unified diff