Project

General

Profile

Download (12.8 KB) Statistics
| Branch: | Revision:

library / src / main / java / org / distorted / library / mesh / MeshSphere.java @ 8c57d77b

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2018 Leszek Koltunski  leszek@koltunski.pl                                          //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// This library is free software; you can redistribute it and/or                                 //
7
// modify it under the terms of the GNU Lesser General Public                                    //
8
// License as published by the Free Software Foundation; either                                  //
9
// version 2.1 of the License, or (at your option) any later version.                            //
10
//                                                                                               //
11
// This library is distributed in the hope that it will be useful,                               //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                             //
14
// Lesser General Public License for more details.                                               //
15
//                                                                                               //
16
// You should have received a copy of the GNU Lesser General Public                              //
17
// License along with this library; if not, write to the Free Software                           //
18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
package org.distorted.library.mesh;
22

    
23
///////////////////////////////////////////////////////////////////////////////////////////////////
24

    
25
import org.distorted.library.main.DistortedLibrary;
26

    
27
/**
28
 * Create a Mesh which approximates a sphere.
29
 * <p>
30
 * Do so by starting off with a 16-faced solid which is basically a regular dodecahedron with each
31
 * of its 8 faces vertically split into 2 triangles, and which each step divide each of its triangular
32
 * faces into smaller and smaller subtriangles and inflate their vertices to lay on the surface or the
33
 * sphere.
34
 */
35
public class MeshSphere extends MeshBase
36
  {
37
  private static final int NUMFACES = 16;
38
  private static final double P = Math.PI;
39

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

    
45
  private static final double[][] FACES =      {
46

    
47
      { 0.00*P, 0.25*P, 0.0, 0.5*P },
48
      { 0.25*P, 0.50*P, 0.0, 0.5*P },
49
      { 0.50*P, 0.75*P, 0.0, 0.5*P },
50
      { 0.75*P, 1.00*P, 0.0, 0.5*P },
51
      { 1.00*P, 1.25*P, 0.0, 0.5*P },
52
      { 1.25*P, 1.50*P, 0.0, 0.5*P },
53
      { 1.50*P, 1.75*P, 0.0, 0.5*P },
54
      { 1.75*P, 0.00*P, 0.0, 0.5*P },
55

    
56
      { 0.00*P, 0.25*P, 0.0,-0.5*P },
57
      { 0.25*P, 0.50*P, 0.0,-0.5*P },
58
      { 0.50*P, 0.75*P, 0.0,-0.5*P },
59
      { 0.75*P, 1.00*P, 0.0,-0.5*P },
60
      { 1.00*P, 1.25*P, 0.0,-0.5*P },
61
      { 1.25*P, 1.50*P, 0.0,-0.5*P },
62
      { 1.50*P, 1.75*P, 0.0,-0.5*P },
63
      { 1.75*P, 0.00*P, 0.0,-0.5*P },
64
                                               };
65
  private int currentVert;
66
  private int numVertices;
67

    
68
///////////////////////////////////////////////////////////////////////////////////////////////////
69
// Each of the 16 faces of the solid requires (level*level + 4*level) vertices for the face
70
// itself and a join to the next face (which requires 2 vertices). We don't need the join in case
71
// of the last, 16th face, thus the -2.
72
// (level*level +4*level) because there are level*level little triangles, each requiring new vertex,
73
// plus 2 extra vertices to start off a row and 2 to move to the next row (or the next face in case
74
// of the last row) and there are 'level' rows.
75

    
76
  private void computeNumberOfVertices(int level)
77
    {
78
    numVertices = NUMFACES*level*(level+4) -2;
79
    currentVert = 0;
80
    }
81

    
82
///////////////////////////////////////////////////////////////////////////////////////////////////
83

    
84
  private void repeatVertex(float[] attribs1, float[] attribs2)
85
    {
86
    if( currentVert>0 )
87
      {
88
      attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currentVert-1) + POS_ATTRIB  ];
89
      attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currentVert-1) + POS_ATTRIB+1];
90
      attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currentVert-1) + POS_ATTRIB+2];
91

    
92
      attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currentVert-1) + NOR_ATTRIB  ];
93
      attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currentVert-1) + NOR_ATTRIB+1];
94
      attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currentVert-1) + NOR_ATTRIB+2];
95

    
96
      attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB  ];
97
      attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB+1];
98

    
99
      currentVert++;
100
      }
101
    }
102

    
103
///////////////////////////////////////////////////////////////////////////////////////////////////
104
// Supposed to return the latitude of the point between two points on the sphere with latitudes
105
// lat1 and lat2, so if for example quot=0.2, then it will return the latitude of something 20%
106
// along the way from lat1 to lat2.
107
//
108
// This is approximation only - in general it is of course not true that the midpoint of two points
109
// on a unit sphere with spherical coords (A1,B1) and (A2,B2) is ( (A1+A2)/2, (B1+B2)/2 ) - take
110
// (0,0) and (PI, epsilon) as a counterexample.
111
//
112
// Here however, the latitudes we are interested at are the latitudes of the vertices of a regular
113
// icosahedron - i.e. +=A and +=PI/2, whose longitudes are close, and we don't really care if the
114
// split into smaller triangles is exact.
115
//
116
// quot better be between 0.0 and 1.0.
117
// this is 'directed' i.e. from lat1 to lat2.
118

    
119
  private double midLatitude(double lat1, double lat2, double quot)
120
    {
121
    return lat1*(1.0-quot)+lat2*quot;
122
    }
123

    
124
///////////////////////////////////////////////////////////////////////////////////////////////////
125
// Same in case of longitude. This is for our needs exact, because we are ever only calling this with
126
// two longitudes of two vertices with the same latitude. Additional problem: things can wrap around
127
// the circle.
128
// this is 'undirected' i.e. we don't assume from lon1 to lon2 - just along the smaller arc joining
129
// lon1 to lon2.
130

    
131
  private double midLongitude(double lon1, double lon2, double quot)
132
    {
133
    double min, max;
134

    
135
    if( lon1<lon2 ) { min=lon1; max=lon2; }
136
    else            { min=lon2; max=lon1; }
137

    
138
    double diff = max-min;
139
    if( diff>P ) { diff=2*P-diff; min=max; }
140

    
141
    double ret = min+quot*diff;
142
    if( ret>=2*P ) ret-=2*P;
143

    
144
    return ret;
145
    }
146

    
147
///////////////////////////////////////////////////////////////////////////////////////////////////
148
// linear map (column,row, level):
149
//
150
// (      0,       0, level) -> (lonV1,latV12)
151
// (      0, level-1, level) -> (lonV3,latV3 )
152
// (level-1,       0, level) -> (lonV2,latV12)
153

    
154
  private void addVertex(float[] attribs1, float[] attribs2, int column, int row, int level,
155
                         double lonV1, double lonV2, double latV12, double latV3)
156
    {
157
    double quotX = (double)column/level;
158
    double quotY = (double)row   /level;
159
    double quotZ;
160

    
161
    if( latV12*latV3 < 0.0 )  // equatorial triangle
162
      {
163
      quotZ = quotX + 0.5*quotY;
164
      }
165
    else                      // polar triangle
166
      {
167
      quotZ = (quotY==1.0 ? 0.5 : quotX / (1.0-quotY));
168
      }
169

    
170
    double longitude = midLongitude(lonV1, lonV2, quotZ );
171
    double latitude  = midLatitude(latV12, latV3, quotY );
172

    
173
    double sinLON = Math.sin(longitude);
174
    double cosLON = Math.cos(longitude);
175
    double sinLAT = Math.sin(latitude);
176
    double cosLAT = Math.cos(latitude);
177

    
178
    float x = (float)(cosLAT*sinLON / 2.0f);
179
    float y = (float)(sinLAT        / 2.0f);
180
    float z = (float)(cosLAT*cosLON / 2.0f);
181

    
182
    double texX = 0.5 + longitude/(2*P);
183
    if( texX>=1.0 ) texX-=1.0;
184

    
185
    double texY = 0.5 + latitude/P;
186

    
187
    attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB  ] = x;  //
188
    attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+1] = y;  //
189
    attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+2] = z;  //
190
                                                             //  In case of this Mesh so it happens that
191
    attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB  ] = 2*x;//  the vertex coords, normal vector, and
192
    attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+1] = 2*y;//  inflate vector have identical (x,y,z).
193
    attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+2] = 2*z;//
194

    
195
    attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB  ] = (float)texX;
196
    attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB+1] = (float)texY;
197

    
198
    currentVert++;
199

    
200
    ////////////////////////////////////////////////////////////////////////////////////////////////
201
    // Problem: on the 'change of date' line in the back of the sphere, some triangles see texX
202
    // coords suddenly jump from 1-epsilon to 0, which looks like a seam with a narrow copy of
203
    // the whole texture there. Solution: remap texX to 1.0.
204
    ////////////////////////////////////////////////////////////////////////////////////////////////
205

    
206
    if( currentVert>=3 && texX==0.0 )
207
      {
208
      double tex1 = attribs2[VERT2_ATTRIBS*(currentVert-2) + TEX_ATTRIB];
209
      double tex2 = attribs2[VERT2_ATTRIBS*(currentVert-3) + TEX_ATTRIB];
210

    
211
      // if the triangle is not degenerate and last vertex was on the western hemisphere
212
      if( tex1!=tex2 && tex1>0.5 )
213
        {
214
        attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
215
        }
216
      }
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220

    
221
  private void buildFace(float[] attribs1, float[] attribs2, int level, int face, double lonV1, double lonV2, double latV12, double latV3)
222
    {
223
    for(int row=0; row<level; row++)
224
      {
225
      for (int column=0; column<level-row; column++)
226
        {
227
        addVertex(attribs1, attribs2, column, row  , level, lonV1, lonV2, latV12, latV3);
228
        if (column==0 && !(face==0 && row==0 ) ) repeatVertex(attribs1, attribs2);
229
        addVertex(attribs1, attribs2, column, row+1, level, lonV1, lonV2, latV12, latV3);
230
        }
231

    
232
      addVertex(attribs1, attribs2, level-row, row , level, lonV1, lonV2, latV12, latV3);
233
      if( row!=level-1 || face!=NUMFACES-1 ) repeatVertex(attribs1, attribs2);
234
      }
235
    }
236

    
237
///////////////////////////////////////////////////////////////////////////////////////////////////
238
// PUBLIC API
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240
  /**
241
   * Creates the underlying grid of vertices with the usual attribs which approximates a sphere.
242
   * <p>
243
   * When level=1, it outputs the 12 vertices of a regular icosahedron.
244
   * When level=N, it divides each of the 20 icosaherdon's triangular faces into N^2 smaller triangles
245
   * (by dividing each side into N equal segments) and 'inflates' the internal vertices so that they
246
   * touch the sphere.
247
   *
248
   * @param level Specifies the approximation level. Valid values: level &ge; 1
249
   */
250
  public MeshSphere(int level)
251
    {
252
    super();
253

    
254
    computeNumberOfVertices(level);
255
    float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
256
    float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
257

    
258
    for(int face=0; face<NUMFACES; face++ )
259
      {
260
      buildFace(attribs1, attribs2, level, face, FACES[face][0], FACES[face][1], FACES[face][2], FACES[face][3]);
261
      }
262

    
263
    if( currentVert!=numVertices )
264
      DistortedLibrary.logMessage("MeshSphere: currentVert= " +currentVert+" numVertices="+numVertices );
265

    
266
    setAttribs(attribs1, attribs2);
267
    }
268

    
269
///////////////////////////////////////////////////////////////////////////////////////////////////
270
/**
271
 * Copy cconstructor.
272
 */
273
  public MeshSphere(MeshSphere mesh, boolean deep)
274
    {
275
    super(mesh,deep);
276
    }
277

    
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279
/**
280
 * Copy the Mesh.
281
 *
282
 * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
283
 *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
284
 *             coordinates and effect associations, is always deep copied)
285
 */
286
  public MeshSphere copy(boolean deep)
287
    {
288
    return new MeshSphere(this,deep);
289
    }
290
  }
(8-8/10)