Revision ebe0476f
Added by Leszek Koltunski almost 6 years ago
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
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.