2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
7 package gov
.nasa
.worldwind
.globes
;
9 import com
.sun
.opengl
.util
.*;
10 import gov
.nasa
.worldwind
.*;
11 import gov
.nasa
.worldwind
.geom
.*;
13 import javax
.media
.opengl
.*;
19 * @version $Id: EllipsoidRectangularTessellator.java 1787 2007-05-08 17:11:30Z dcollins $
21 public class EllipsoidRectangularTessellator
extends WWObjectImpl
implements Tessellator
23 // TODO: Make all this configurable
24 private static final int DEFAULT_DENSITY
= 24;
25 private static final double DEFAULT_LOG10_RESOLUTION_TARGET
= 1.3;
26 private static final int DEFAULT_MAX_LEVEL
= 12;
27 private static final int DEFAULT_NUM_LAT_SUBDIVISIONS
= 5;
28 private static final int DEFAULT_NUM_LON_SUBDIVISIONS
= 10;
30 private static class RenderInfo
32 private final int density
;
33 private final Point referenceCenter
;
34 private final DoubleBuffer vertices
;
35 private final DoubleBuffer texCoords
;
36 private final IntBuffer indices
;
37 private final int resolution
;
39 private RenderInfo(int density
, DoubleBuffer vertices
, DoubleBuffer texCoords
, Point refCenter
, int resolution
)
41 this.density
= density
;
42 this.vertices
= vertices
;
43 this.texCoords
= texCoords
;
44 this.referenceCenter
= refCenter
;
45 this.indices
= RectTile
.getIndices(this.density
);
46 this.resolution
= resolution
;
49 private long getSizeInBytes()
51 return 16 + (this.vertices
.limit() + this.texCoords
.limit()) * Double
.SIZE
;
55 private static class CacheKey
57 private final Sector sector
;
58 private int resolution
;
59 private final double verticalExaggeration
;
62 private CacheKey(RectTile tile
, int resolution
, double verticalExaggeration
, int density
)
64 this.sector
= tile
.sector
;
65 this.resolution
= resolution
;
66 this.verticalExaggeration
= verticalExaggeration
;
67 this.density
= density
;
71 public String
toString()
73 return "density " + this.density
+ " ve " + this.verticalExaggeration
+ " resolution " + this.resolution
74 + " sector " + this.sector
;
77 public boolean equals(Object o
)
81 if (o
== null || getClass() != o
.getClass())
84 CacheKey cacheKey
= (CacheKey
) o
;
86 if (density
!= cacheKey
.density
)
88 if (resolution
!= cacheKey
.resolution
)
90 if (Double
.compare(cacheKey
.verticalExaggeration
, verticalExaggeration
) != 0)
92 //noinspection RedundantIfStatement
93 if (sector
!= null ?
!sector
.equals(cacheKey
.sector
) : cacheKey
.sector
!= null)
103 result
= (sector
!= null ? sector
.hashCode() : 0);
104 result
= 31 * result
+ resolution
;
105 temp
= verticalExaggeration
!= +0.0d ? Double
.doubleToLongBits(verticalExaggeration
) : 0L;
106 result
= 31 * result
+ (int) (temp ^
(temp
>>> 32));
107 result
= 31 * result
+ density
;
112 private static class RectTile
implements SectorGeometry
114 private static final HashMap
<Integer
, DoubleBuffer
> parameterizations
= new HashMap
<Integer
, DoubleBuffer
>();
115 private static final HashMap
<Integer
, IntBuffer
> indexLists
= new HashMap
<Integer
, IntBuffer
>();
117 private final Globe globe
;
118 private final int level
;
119 private final Sector sector
;
120 private final Cylinder extent
; // extent of triangle in object coordinates
121 private final int density
;
122 private final double log10CellSize
;
123 private long byteSize
;
124 private RenderInfo ri
;
126 private PickSupport pickSupport
= new PickSupport();
127 private int minColorCode
= 0;
128 private int maxColorCode
= 0;
130 public RectTile(Globe globe
, int level
, int density
, Sector sector
)
134 this.density
= density
;
135 this.sector
= sector
;
136 this.extent
= Sector
.computeBoundingCylinder(globe
, 1d
, this.getSector());
137 double cellSize
= (sector
.getDeltaLatRadians() * globe
.getRadius()) / density
;
138 this.log10CellSize
= Math
.log10(cellSize
);
141 public Sector
getSector()
146 public Extent
getExtent()
151 public long getSizeInBytes()
153 return this.byteSize
;
156 private RectTile
[] split()
158 Sector
[] sectors
= this.sector
.subdivide();
160 RectTile
[] subTiles
= new RectTile
[4];
161 subTiles
[0] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[0]);
162 subTiles
[1] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[1]);
163 subTiles
[2] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[2]);
164 subTiles
[3] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[3]);
169 private void makeVerts(DrawContext dc
)
171 int resolution
= dc
.getGlobe().getElevationModel().getTargetResolution(dc
, this.sector
, this.density
);
173 if (this.ri
!= null && this.ri
.resolution
>= resolution
)
176 CacheKey cacheKey
= new CacheKey(this, resolution
, dc
.getVerticalExaggeration(), this.density
);
177 this.ri
= (RenderInfo
) WorldWind
.memoryCache().getObject(cacheKey
);
181 this.ri
= this.buildVerts(dc
, this.density
, resolution
, true);
182 if (this.ri
!= null && this.ri
.resolution
>= 0)//&& this.ri.elevationsFullyResolved)
183 WorldWind
.memoryCache().add(this.ri
.resolution
, this.ri
, this.byteSize
= this.ri
.getSizeInBytes());
186 private RenderInfo
buildVerts(DrawContext dc
, int density
, int resolution
, boolean makeSkirts
)
188 int numVertices
= (density
+ 3) * (density
+ 3);
189 java
.nio
.DoubleBuffer verts
= BufferUtil
.newDoubleBuffer(numVertices
* 3);
191 Globe globe
= dc
.getGlobe();
192 ElevationModel
.Elevations elevations
= globe
.getElevationModel().getElevations(this.sector
, resolution
);
194 double latMin
= this.sector
.getMinLatitude().radians
;
195 double latMax
= this.sector
.getMaxLatitude().radians
;
196 double dLat
= (latMax
- latMin
) / density
;
198 double lonMin
= this.sector
.getMinLongitude().radians
;
199 double lonMax
= this.sector
.getMaxLongitude().radians
;
200 double dLon
= (lonMax
- lonMin
) / density
;
204 double verticalExaggeration
= dc
.getVerticalExaggeration();
205 double exaggeratedMinElevation
= makeSkirts ? globe
.getMinElevation() * verticalExaggeration
: 0;
206 double equatorialRadius
= globe
.getEquatorialRadius();
207 double eccentricity
= globe
.getEccentricitySquared();
209 LatLon centroid
= sector
.getCentroid();
210 Point refCenter
= globe
.computePointFromPosition(centroid
.getLatitude(), centroid
.getLongitude(), 0d
);
212 for (int j
= 0; j
<= density
+ 2; j
++)
214 double cosLat
= Math
.cos(lat
);
215 double sinLat
= Math
.sin(lat
);
216 double rpm
= equatorialRadius
/ Math
.sqrt(1.0 - eccentricity
* sinLat
* sinLat
);
218 for (int i
= 0; i
<= density
+ 2; i
++)
220 double elevation
= verticalExaggeration
* elevations
.getElevation(lat
, lon
);
221 if (j
== 0 || j
>= density
+ 2 || i
== 0 || i
>= density
+ 2)
222 { // use abs to account for negative elevation.
223 elevation
-= exaggeratedMinElevation
>= 0 ? exaggeratedMinElevation
: -exaggeratedMinElevation
;
226 double x
= ((rpm
+ elevation
) * cosLat
* Math
.sin(lon
)) - refCenter
.getX();
227 double y
= ((rpm
* (1.0 - eccentricity
) + elevation
) * sinLat
) - refCenter
.getY();
228 double z
= ((rpm
+ elevation
) * cosLat
* Math
.cos(lon
)) - refCenter
.getZ();
230 verts
.put(iv
++, x
).put(iv
++, y
).put(iv
++, z
);
243 return new RenderInfo(density
, verts
, getParameterization(density
), refCenter
, elevations
.getResolution());
246 public void renderMultiTexture(DrawContext dc
, int numTextureUnits
)
250 String msg
= WorldWind
.retrieveErrMsg("nullValue.DrawContextIsNull");
251 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
252 throw new IllegalArgumentException(msg
);
255 if (numTextureUnits
< 1)
257 String msg
= WorldWind
.retrieveErrMsg("generic.NumTextureUnitsLessThanOne");
258 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
259 throw new IllegalArgumentException(msg
);
262 this.render(dc
, numTextureUnits
);
265 public void render(DrawContext dc
)
269 String msg
= WorldWind
.retrieveErrMsg("nullValue.DrawContextIsNull");
270 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
271 throw new IllegalArgumentException(msg
);
277 private long render(DrawContext dc
, int numTextureUnits
)
281 String msg
= WorldWind
.retrieveErrMsg("nullValue.RenderInfoIsNull");
282 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
283 throw new IllegalStateException(msg
);
286 dc
.getView().pushReferenceCenter(dc
, ri
.referenceCenter
);
289 gl
.glPushClientAttrib(GL
.GL_CLIENT_VERTEX_ARRAY_BIT
);
290 gl
.glEnableClientState(GL
.GL_VERTEX_ARRAY
);
291 gl
.glVertexPointer(3, GL
.GL_DOUBLE
, 0, this.ri
.vertices
.rewind());
293 for (int i
= 0; i
< numTextureUnits
; i
++)
295 gl
.glClientActiveTexture(GL
.GL_TEXTURE0
+ i
);
296 gl
.glEnableClientState(GL
.GL_TEXTURE_COORD_ARRAY
);
297 gl
.glTexCoordPointer(2, GL
.GL_DOUBLE
, 0, ri
.texCoords
.rewind());
300 gl
.glDrawElements(javax
.media
.opengl
.GL
.GL_TRIANGLE_STRIP
, this.ri
.indices
.limit(),
301 javax
.media
.opengl
.GL
.GL_UNSIGNED_INT
, this.ri
.indices
.rewind());
303 gl
.glPopClientAttrib();
305 dc
.getView().popReferenceCenter(dc
);
307 return this.ri
.indices
.limit() - 2; // return number of triangles rendered
310 public void renderWireframe(DrawContext dc
, boolean showTriangles
, boolean showTileBoundary
)
314 String msg
= WorldWind
.retrieveErrMsg("nullValue.DrawContextIsNull");
315 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
316 throw new IllegalArgumentException(msg
);
321 String msg
= WorldWind
.retrieveErrMsg("nullValue.RenderInfoIsNull");
322 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
323 throw new IllegalStateException(msg
);
326 java
.nio
.IntBuffer indices
= getIndices(this.ri
.density
);
329 dc
.getView().pushReferenceCenter(dc
, this.ri
.referenceCenter
);
331 javax
.media
.opengl
.GL gl
= dc
.getGL();
333 GL
.GL_DEPTH_BUFFER_BIT
| GL
.GL_POLYGON_BIT
| GL
.GL_TEXTURE_BIT
| GL
.GL_ENABLE_BIT
| GL
.GL_CURRENT_BIT
);
334 gl
.glEnable(GL
.GL_BLEND
);
335 gl
.glBlendFunc(GL
.GL_SRC_ALPHA
, GL
.GL_ONE
);
336 gl
.glDisable(javax
.media
.opengl
.GL
.GL_DEPTH_TEST
);
337 gl
.glEnable(javax
.media
.opengl
.GL
.GL_CULL_FACE
);
338 gl
.glCullFace(javax
.media
.opengl
.GL
.GL_BACK
);
339 gl
.glDisable(javax
.media
.opengl
.GL
.GL_TEXTURE_2D
);
340 gl
.glColor4d(1d
, 1d
, 1d
, 0.2);
341 gl
.glPolygonMode(javax
.media
.opengl
.GL
.GL_FRONT
, javax
.media
.opengl
.GL
.GL_LINE
);
345 gl
.glPushClientAttrib(GL
.GL_CLIENT_VERTEX_ARRAY_BIT
);
346 gl
.glEnableClientState(GL
.GL_VERTEX_ARRAY
);
348 gl
.glVertexPointer(3, GL
.GL_DOUBLE
, 0, this.ri
.vertices
);
349 gl
.glDrawElements(javax
.media
.opengl
.GL
.GL_TRIANGLE_STRIP
, indices
.limit(),
350 javax
.media
.opengl
.GL
.GL_UNSIGNED_INT
, indices
);
352 gl
.glPopClientAttrib();
355 dc
.getView().popReferenceCenter(dc
);
357 if (showTileBoundary
)
358 this.renderPatchBoundary(dc
, gl
);
363 private void renderPatchBoundary(DrawContext dc
, GL gl
)
365 // TODO: Currently only works if called from renderWireframe because no state is set here.
366 // TODO: Draw the boundary using the vertices along the boundary rather than just at the corners.
367 gl
.glColor4d(1d
, 0, 0, 1d
);
368 Point
[] corners
= this.sector
.computeCornerPoints(dc
.getGlobe());
370 gl
.glBegin(javax
.media
.opengl
.GL
.GL_QUADS
);
371 gl
.glVertex3d(corners
[0].x(), corners
[0].y(), corners
[0].z());
372 gl
.glVertex3d(corners
[1].x(), corners
[1].y(), corners
[1].z());
373 gl
.glVertex3d(corners
[2].x(), corners
[2].y(), corners
[2].z());
374 gl
.glVertex3d(corners
[3].x(), corners
[3].y(), corners
[3].z());
378 public void renderBoundingVolume(DrawContext dc
)
380 ((Cylinder
) this.getExtent()).render(dc
);
383 public void pick(DrawContext dc
, java
.awt
.Point pickPoint
)
388 renderTrianglesWithUniqueColors(dc
, ri
);
390 int colorCode
= pickSupport
.getTopColor(dc
, pickPoint
);
391 if (colorCode
< minColorCode
|| colorCode
> maxColorCode
)
394 double EPSILON
= (double) 0.00001f
;
396 int triangleIndex
= colorCode
- minColorCode
- 1;
398 if ((null != ri
.indices
) && (triangleIndex
< ri
.indices
.capacity() - 2))
400 double centerX
= ri
.referenceCenter
.getX();
401 double centerY
= ri
.referenceCenter
.getY();
402 double centerZ
= ri
.referenceCenter
.getZ();
404 int vIndex
= 3 * ri
.indices
.get(triangleIndex
);
405 Point v0
= new Point((ri
.vertices
.get(vIndex
++) + centerX
),
406 (ri
.vertices
.get(vIndex
++) + centerY
),
407 (ri
.vertices
.get(vIndex
) + centerZ
));
409 vIndex
= 3 * ri
.indices
.get(triangleIndex
+ 1);
410 Point v1
= new Point((ri
.vertices
.get(vIndex
++) + centerX
),
411 (ri
.vertices
.get(vIndex
++) + centerY
),
412 (ri
.vertices
.get(vIndex
) + centerZ
));
414 vIndex
= 3 * ri
.indices
.get(triangleIndex
+ 2);
415 Point v2
= new Point((ri
.vertices
.get(vIndex
++) + centerX
),
416 (ri
.vertices
.get(vIndex
++) + centerY
),
417 (ri
.vertices
.get(vIndex
) + centerZ
));
419 // get triangle edge vectors and plane normal
420 Point e1
= v1
.subtract(v0
);
421 Point e2
= v2
.subtract(v0
);
422 Point N
= e1
.cross(e2
); // if N is 0, the triangle is degenerate, we are not dealing with it
424 Line ray
= dc
.getView().computeRayFromScreenPoint(pickPoint
.getX(), pickPoint
.getY());
426 Point w0
= ray
.getOrigin().subtract(v0
);
427 double a
= -N
.dot(w0
);
428 double b
= N
.dot(ray
.getDirection());
429 if (java
.lang
.Math
.abs(b
) < EPSILON
) // ray is parallel to triangle plane
430 return; // if a == 0 , ray lies in triangle plane
433 Point intersect
= ray
.getOrigin().add(ray
.getDirection().multiply(r
));
434 Position pp
= dc
.getGlobe().computePositionFromPoint(intersect
);
436 // Draw the elevation from the elevation model, not the geode.
437 double elev
= dc
.getGlobe().getElevation(pp
.getLatitude(), pp
.getLongitude());
438 Position p
= new Position(pp
.getLatitude(), pp
.getLongitude(), elev
);
440 PickedObject po
= new PickedObject(colorCode
, p
, pp
.getLatitude(), pp
.getLongitude(), elev
, true);
441 dc
.addPickedObject(po
);
445 private void renderTrianglesWithUniqueColors(DrawContext dc
, RenderInfo ri
)
449 String message
= WorldWind
.retrieveErrMsg("nullValue.DrawContextIsNull");
450 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, message
);
451 throw new IllegalStateException(message
);
454 if (ri
.vertices
== null)
457 ri
.vertices
.rewind();
460 javax
.media
.opengl
.GL gl
= dc
.getGL();
462 if (null != ri
.referenceCenter
)
463 dc
.getView().pushReferenceCenter(dc
, ri
.referenceCenter
);
465 minColorCode
= dc
.getUniquePickColor().getRGB();
466 int trianglesNum
= ri
.indices
.capacity() - 2;
468 gl
.glBegin(GL
.GL_TRIANGLES
);
469 for (int i
= 0; i
< trianglesNum
; i
++)
471 java
.awt
.Color color
= dc
.getUniquePickColor();
472 gl
.glColor3ub((byte) (color
.getRed() & 0xFF),
473 (byte) (color
.getGreen() & 0xFF),
474 (byte) (color
.getBlue() & 0xFF));
476 int vIndex
= 3 * ri
.indices
.get(i
);
477 gl
.glVertex3d(ri
.vertices
.get(vIndex
), ri
.vertices
.get(vIndex
+ 1), ri
.vertices
.get(
480 vIndex
= 3 * ri
.indices
.get(i
+ 1);
481 gl
.glVertex3d(ri
.vertices
.get(vIndex
), ri
.vertices
.get(vIndex
+ 1), ri
.vertices
.get(
484 vIndex
= 3 * ri
.indices
.get(i
+ 2);
485 gl
.glVertex3d(ri
.vertices
.get(vIndex
), ri
.vertices
.get(vIndex
+ 1), ri
.vertices
.get(
489 maxColorCode
= dc
.getUniquePickColor().getRGB();
491 if (null != ri
.referenceCenter
)
492 dc
.getView().popReferenceCenter(dc
);
495 public Point
getSurfacePoint(Angle latitude
, Angle longitude
, double metersOffset
)
497 if (latitude
== null || longitude
== null)
499 String msg
= WorldWind
.retrieveErrMsg("nullValue.LatLonIsNull");
500 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
501 throw new IllegalArgumentException(msg
);
504 if (!this.sector
.contains(latitude
, longitude
))
506 // not on this geometry
513 double lat
= latitude
.getDegrees();
514 double lon
= longitude
.getDegrees();
516 double bottom
= this.sector
.getMinLatitude().getDegrees();
517 double top
= this.sector
.getMaxLatitude().getDegrees();
518 double left
= this.sector
.getMinLongitude().getDegrees();
519 double right
= this.sector
.getMaxLongitude().getDegrees();
521 double leftDecimal
= (lon
- left
) / (right
- left
);
522 double bottomDecimal
= (lat
- bottom
) / (top
- bottom
);
524 int row
= (int) (bottomDecimal
* (this.density
));
525 int column
= (int) (leftDecimal
* (this.density
));
527 double l
= createPosition(column
, leftDecimal
, ri
.density
);
528 double h
= createPosition(row
, bottomDecimal
, ri
.density
);
530 Point result
= RectTile
.interpolate(row
, column
, l
, h
, ri
);
531 result
= result
.add(ri
.referenceCenter
);
532 if (metersOffset
!= 0)
533 result
= applyOffset(this.globe
, result
, metersOffset
);
539 * Offsets <code>point</code> by <code>metersOffset</code> meters.
541 * @param globe the <code>Globe</code> from which to offset
542 * @param point the <code>Point</code> to offset
543 * @param metersOffset the magnitude of the offset
544 * @return <code>point</code> offset along its surface normal as if it were on <code>globe</code>
546 private static Point
applyOffset(Globe globe
, Point point
, double metersOffset
)
548 Point normal
= globe
.computeSurfaceNormalAtPoint(point
);
549 point
= Point
.fromOriginAndDirection(metersOffset
, normal
, point
);
554 * Computes from a column (or row) number, and a given offset ranged [0,1] corresponding to the distance along
555 * the edge of this sector, where between this column and the next column the corresponding position will fall,
556 * in the range [0,1].
558 * @param start the number of the column or row to the left, below or on this position
559 * @param decimal the distance from the left or bottom of the current sector that this position falls
560 * @param density the number of intervals along the sector's side
561 * @return a decimal ranged [0,1] representing the position between two columns or rows, rather than between two
562 * edges of the sector
564 private static double createPosition(int start
, double decimal
, int density
)
566 double l
= ((double) start
) / (double) density
;
567 double r
= ((double) (start
+ 1)) / (double) density
;
569 return (decimal
- l
) / (r
- l
);
573 * Calculates a <code>Point</code> that sits at <code>xDec</code> offset from <code>column</code> to
574 * <code>column + 1</code> and at <code>yDec</code> offset from <code>row</code> to <code>row + 1</code>.
575 * Accounts for the diagonals.
577 * @param row represents the row which corresponds to a <code>yDec</code> value of 0
578 * @param column represents the column which corresponds to an <code>xDec</code> value of 0
579 * @param xDec constrained to [0,1]
580 * @param yDec constrained to [0,1]
581 * @param ri the render info holding the vertices, etc.
582 * @return a <code>Point</code> geometrically within or on the boundary of the quadrilateral whose bottom left
583 * corner is indexed by (<code>row</code>, <code>column</code>)
585 private static Point
interpolate(int row
, int column
, double xDec
, double yDec
, RenderInfo ri
)
590 int numVerticesPerEdge
= ri
.density
+ 3;
592 int bottomLeft
= row
* numVerticesPerEdge
+ column
;
596 int numVertsTimesThree
= numVerticesPerEdge
* 3;
598 Point bL
= new Point(ri
.vertices
.get(bottomLeft
), ri
.vertices
.get(bottomLeft
+ 1), ri
.vertices
.get(
600 Point bR
= new Point(ri
.vertices
.get(bottomLeft
+ 3), ri
.vertices
.get(bottomLeft
+ 4),
601 ri
.vertices
.get(bottomLeft
+ 5));
603 bottomLeft
+= numVertsTimesThree
;
605 Point tL
= new Point(ri
.vertices
.get(bottomLeft
), ri
.vertices
.get(bottomLeft
+ 1), ri
.vertices
.get(
607 Point tR
= new Point(ri
.vertices
.get(bottomLeft
+ 3), ri
.vertices
.get(bottomLeft
+ 4),
608 ri
.vertices
.get(bottomLeft
+ 5));
610 return interpolate(bL
, bR
, tR
, tL
, xDec
, yDec
);
614 * Calculates the point at (xDec, yDec) in the two triangles defined by {bL, bR, tL} and {bR, tR, tL}. If
615 * thought of as a quadrilateral, the diagonal runs from tL to bR. Of course, this isn't a quad, it's two
618 * @param bL the bottom left corner
619 * @param bR the bottom right corner
620 * @param tR the top right corner
621 * @param tL the top left corner
622 * @param xDec how far along, [0,1] 0 = left edge, 1 = right edge
623 * @param yDec how far along, [0,1] 0 = bottom edge, 1 = top edge
624 * @return the point xDec, yDec in the co-ordinate system defined by bL, bR, tR, tL
626 private static Point
interpolate(Point bL
, Point bR
, Point tR
, Point tL
, double xDec
, double yDec
)
628 double pos
= xDec
+ yDec
;
631 // on the diagonal - what's more, we don't need to do any "oneMinusT" calculation
632 return new Point(tL
.x() * yDec
+ bR
.x() * xDec
, tL
.y() * yDec
+ bR
.y() * xDec
,
633 tL
.z() * yDec
+ bR
.z() * xDec
);
637 // in the "top right" half
639 // vectors pointing from top right towards the point we want (can be thought of as "negative" vectors)
640 Point horizontalVector
= (tL
.subtract(tR
)).multiply(1 - xDec
);
641 Point verticalVector
= (bR
.subtract(tR
)).multiply(1 - yDec
);
643 return tR
.add(horizontalVector
).add(verticalVector
);
647 // pos < 1 - in the "bottom left" half
649 // vectors pointing from the bottom left towards the point we want
650 Point horizontalVector
= (bR
.subtract(bL
)).multiply(xDec
);
651 Point verticalVector
= (tL
.subtract(bL
)).multiply(yDec
);
653 return bL
.add(horizontalVector
).add(verticalVector
);
657 public String
toString()
659 return "level " + this.level
+ ", density " + this.density
+ ", sector " + this.sector
;
662 protected static java
.nio
.DoubleBuffer
getParameterization(int density
)
669 // Approximate 1 to avoid shearing off of right and top skirts in SurfaceTileRenderer.
670 // TODO: dig into this more: why are the skirts being sheared off?
671 final double one
= 0.999999;
673 java
.nio
.DoubleBuffer p
= parameterizations
.get(density
);
677 int coordCount
= (density
+ 3) * (density
+ 3);
678 p
= com
.sun
.opengl
.util
.BufferUtil
.newDoubleBuffer(2 * coordCount
);
679 double delta
= 1d
/ density
;
680 int k
= 2 * (density
+ 3);
681 for (int j
= 0; j
< density
; j
++)
683 double v
= j
* delta
;
685 // skirt column; duplicate first column
690 for (int i
= 0; i
< density
; i
++)
692 p
.put(k
++, i
* delta
); // u
696 // last interior column; force u to 1.
697 p
.put(k
++, one
);//1d);
700 // skirt column; duplicate previous column
701 p
.put(k
++, one
);//1d);
706 //noinspection UnnecessaryLocalVariable
708 p
.put(k
++, 0d
); // skirt column
711 for (int i
= 0; i
< density
; i
++)
713 p
.put(k
++, i
* delta
); // u
716 p
.put(k
++, one
);//1d); // last interior column
719 p
.put(k
++, one
);//1d); // skirt column
723 int kk
= k
- 2 * (density
+ 3);
724 for (int i
= 0; i
< density
+ 3; i
++)
726 p
.put(k
++, p
.get(kk
++));
727 p
.put(k
++, p
.get(kk
++));
732 kk
= 2 * (density
+ 3);
733 for (int i
= 0; i
< density
+ 3; i
++)
735 p
.put(k
++, p
.get(kk
++));
736 p
.put(k
++, p
.get(kk
++));
739 parameterizations
.put(density
, p
);
744 private static java
.nio
.IntBuffer
getIndices(int density
)
749 // return a pre-computed buffer if possible.
750 java
.nio
.IntBuffer buffer
= indexLists
.get(density
);
754 int sideSize
= density
+ 2;
756 int indexCount
= 2 * sideSize
* sideSize
+ 4 * sideSize
- 2;
757 buffer
= com
.sun
.opengl
.util
.BufferUtil
.newIntBuffer(indexCount
);
759 for (int i
= 0; i
< sideSize
; i
++)
768 if (i
% 2 == 0) // even
771 for (int j
= 0; j
< sideSize
; j
++)
781 for (int j
= 0; j
< sideSize
; j
++)
790 indexLists
.put(density
, buffer
);
796 private final java
.util
.ArrayList
<RectTile
> topLevels
;
797 private SectorGeometryList currentTiles
= new SectorGeometryList();
798 private Frustum currentFrustum
;
799 private int currentLevel
;
800 private int maxLevel
= DEFAULT_MAX_LEVEL
;//14; // TODO: Make configurable
801 private Sector sector
; // union of all tiles selected during call to render()
802 private int density
= DEFAULT_DENSITY
; // TODO: make configurable
804 public EllipsoidRectangularTessellator(Globe globe
)
806 this.topLevels
= createTopLevelTiles(globe
, DEFAULT_NUM_LAT_SUBDIVISIONS
, DEFAULT_NUM_LON_SUBDIVISIONS
);
809 public Sector
getSector()
814 private ArrayList
<RectTile
> createTopLevelTiles(Globe globe
, int nRows
, int nCols
)
816 ArrayList
<RectTile
> tops
= new ArrayList
<RectTile
>(nRows
* nCols
);
818 double deltaLat
= 180d
/ nRows
;
819 double deltaLon
= 360d
/ nCols
;
820 Angle lastLat
= Angle
.NEG90
;
822 for (int row
= 0; row
< DEFAULT_NUM_LAT_SUBDIVISIONS
; row
++)
824 Angle lat
= lastLat
.addDegrees(deltaLat
);
825 if (lat
.getDegrees() + 1d
> 90d
)
828 Angle lastLon
= Angle
.NEG180
;
830 for (int col
= 0; col
< DEFAULT_NUM_LON_SUBDIVISIONS
; col
++)
832 Angle lon
= lastLon
.addDegrees(deltaLon
);
833 if (lon
.getDegrees() + 1d
> 180d
)
836 tops
.add(new RectTile(globe
, 0, this.density
, new Sector(lastLat
, lat
, lastLon
, lon
)));
845 public SectorGeometryList
tessellate(DrawContext dc
)
849 String msg
= WorldWind
.retrieveErrMsg("nullValue.DrawContextIsNull");
850 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
851 throw new IllegalArgumentException(msg
);
854 if (dc
.getView() == null)
856 String msg
= WorldWind
.retrieveErrMsg("nullValue.ViewIsNull");
857 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
858 throw new IllegalStateException(msg
);
861 this.currentTiles
.clear();
862 this.currentLevel
= 0;
865 this.currentFrustum
= dc
.getView().getFrustumInModelCoordinates();
866 for (RectTile tile
: topLevels
)
868 this.selectVisibleTiles(dc
, tile
);
871 dc
.setVisibleSector(this.getSector());
873 for (SectorGeometry tile
: this.currentTiles
)
875 ((RectTile
) tile
).makeVerts(dc
);
878 return this.currentTiles
;
881 private void selectVisibleTiles(DrawContext dc
, RectTile tile
)
883 if (!tile
.getExtent().intersects(this.currentFrustum
))
886 if (this.currentLevel
< this.maxLevel
&& needToSplit(dc
, tile
))
889 RectTile
[] subtiles
= tile
.split();
890 for (RectTile child
: subtiles
)
892 this.selectVisibleTiles(dc
, child
);
897 this.sector
= tile
.getSector().union(this.sector
);
898 this.currentTiles
.add(tile
);
901 private static boolean needToSplit(DrawContext dc
, RectTile tile
)
903 Point
[] corners
= tile
.sector
.computeCornerPoints(dc
.getGlobe());
904 Point centerPoint
= tile
.sector
.computeCenterPoint(dc
.getGlobe());
906 View view
= dc
.getView();
907 double d1
= view
.getEyePoint().distanceTo(corners
[0]);
908 double d2
= view
.getEyePoint().distanceTo(corners
[1]);
909 double d3
= view
.getEyePoint().distanceTo(corners
[2]);
910 double d4
= view
.getEyePoint().distanceTo(corners
[3]);
911 double d5
= view
.getEyePoint().distanceTo(centerPoint
);
913 double minDistance
= d1
;
914 if (d2
< minDistance
)
916 if (d3
< minDistance
)
918 if (d4
< minDistance
)
920 if (d5
< minDistance
)
923 double logDist
= Math
.log10(minDistance
);
924 boolean useTile
= tile
.log10CellSize
<= (logDist
- DEFAULT_LOG10_RESOLUTION_TARGET
);
929 public static void main(String
[] args
)
933 DoubleBuffer tcs
= RectTile
.getParameterization(density
);
934 IntBuffer indices
= RectTile
.getIndices(density
);
938 for (int i
= 0; i
< indices
.limit(); i
++)
940 int index
= indices
.get(i
);
941 System
.out
.println(index
+ ": " + tcs
.get(2 * index
) + ", " + tcs
.get(2 * index
+ 1));