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
.BufferUtil
;
10 import gov
.nasa
.worldwind
.*;
11 import gov
.nasa
.worldwind
.avlist
.AVKey
;
12 import gov
.nasa
.worldwind
.cache
.*;
13 import gov
.nasa
.worldwind
.geom
.*;
14 import gov
.nasa
.worldwind
.pick
.*;
15 import gov
.nasa
.worldwind
.render
.DrawContext
;
16 import gov
.nasa
.worldwind
.util
.Logging
;
17 import gov
.nasa
.worldwind
.view
.View
;
19 import javax
.media
.opengl
.GL
;
25 * @version $Id: EllipsoidRectangularTessellator.java 3210 2007-10-06 22:14:23Z tgaskins $
27 public class EllipsoidRectangularTessellator
extends WWObjectImpl
implements Tessellator
29 // TODO: Make all this configurable
30 private static final int DEFAULT_DENSITY
= 24;
31 private static final double DEFAULT_LOG10_RESOLUTION_TARGET
= 1.3;
32 private static final int DEFAULT_MAX_LEVEL
= 12;
33 private static final int DEFAULT_NUM_LAT_SUBDIVISIONS
= 5;
34 private static final int DEFAULT_NUM_LON_SUBDIVISIONS
= 10;
36 private static class RenderInfo
38 private final int density
;
39 private final Vec4 referenceCenter
;
40 private final DoubleBuffer vertices
;
41 private final DoubleBuffer texCoords
;
42 private final IntBuffer indices
;
43 private final int resolution
;
45 private RenderInfo(int density
, DoubleBuffer vertices
, DoubleBuffer texCoords
, Vec4 refCenter
, int resolution
)
47 this.density
= density
;
48 this.vertices
= vertices
;
49 this.texCoords
= texCoords
;
50 this.referenceCenter
= refCenter
;
51 this.indices
= RectTile
.getIndices(this.density
);
52 this.resolution
= resolution
;
55 private long getSizeInBytes()
57 // Texture coordinates are shared among all tiles of the same density, so do not count towards size.
58 // 8 references, doubles in buffer.
59 return 8 * 4 + (this.vertices
.limit()) * Double
.SIZE
;
63 private static class CacheKey
65 private final Sector sector
;
66 private int resolution
;
67 private final double verticalExaggeration
;
70 private CacheKey(RectTile tile
, int resolution
, double verticalExaggeration
, int density
)
72 this.sector
= tile
.sector
;
73 this.resolution
= resolution
;
74 this.verticalExaggeration
= verticalExaggeration
;
75 this.density
= density
;
79 public String
toString()
81 return "density " + this.density
+ " ve " + this.verticalExaggeration
+ " resolution " + this.resolution
82 + " sector " + this.sector
;
85 public boolean equals(Object o
)
89 if (o
== null || getClass() != o
.getClass())
92 CacheKey cacheKey
= (CacheKey
) o
;
94 if (density
!= cacheKey
.density
)
96 if (resolution
!= cacheKey
.resolution
)
98 if (Double
.compare(cacheKey
.verticalExaggeration
, verticalExaggeration
) != 0)
100 //noinspection RedundantIfStatement
101 if (sector
!= null ?
!sector
.equals(cacheKey
.sector
) : cacheKey
.sector
!= null)
107 public int hashCode()
111 result
= (sector
!= null ? sector
.hashCode() : 0);
112 result
= 31 * result
+ resolution
;
113 temp
= verticalExaggeration
!= +0.0d ? Double
.doubleToLongBits(verticalExaggeration
) : 0L;
114 result
= 31 * result
+ (int) (temp ^
(temp
>>> 32));
115 result
= 31 * result
+ density
;
120 private static class RectTile
implements SectorGeometry
122 private static final HashMap
<Integer
, DoubleBuffer
> parameterizations
= new HashMap
<Integer
, DoubleBuffer
>();
123 private static final HashMap
<Integer
, IntBuffer
> indexLists
= new HashMap
<Integer
, IntBuffer
>();
125 private final Globe globe
;
126 private final int level
;
127 private final Sector sector
;
128 private final Cylinder extent
; // extent of triangle in object coordinates
129 private final int density
;
130 private final double log10CellSize
;
131 private long byteSize
;
132 private RenderInfo ri
;
134 private PickSupport pickSupport
= new PickSupport();
135 private int minColorCode
= 0;
136 private int maxColorCode
= 0;
138 public RectTile(Globe globe
, int level
, int density
, Sector sector
)
142 this.density
= density
;
143 this.sector
= sector
;
144 this.extent
= Sector
.computeBoundingCylinder(globe
, 1d
, this.getSector());
145 double cellSize
= (sector
.getDeltaLatRadians() * globe
.getRadius()) / density
;
146 this.log10CellSize
= Math
.log10(cellSize
);
149 public Sector
getSector()
154 public Extent
getExtent()
159 public long getSizeInBytes()
161 return this.byteSize
;
164 private RectTile
[] split()
166 Sector
[] sectors
= this.sector
.subdivide();
168 RectTile
[] subTiles
= new RectTile
[4];
169 subTiles
[0] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[0]);
170 subTiles
[1] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[1]);
171 subTiles
[2] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[2]);
172 subTiles
[3] = new RectTile(this.globe
, this.level
+ 1, this.density
, sectors
[3]);
177 private void makeVerts(DrawContext dc
)
179 int resolution
= dc
.getGlobe().getElevationModel().getTargetResolution(dc
, this.sector
, this.density
);
181 if (this.ri
!= null && this.ri
.resolution
>= resolution
)
184 MemoryCache cache
= WorldWind
.getMemoryCache(RectTile
.class.getName());
185 CacheKey cacheKey
= new CacheKey(this, resolution
, dc
.getVerticalExaggeration(), this.density
);
186 this.ri
= (RenderInfo
) cache
.getObject(cacheKey
);
190 this.ri
= this.buildVerts(dc
, this.density
, resolution
, true);
191 if (this.ri
!= null && this.ri
.resolution
>= 0)
193 cacheKey
= new CacheKey(this, this.ri
.resolution
, dc
.getVerticalExaggeration(), this.density
);
194 cache
.add(cacheKey
, this.ri
, this.byteSize
= this.ri
.getSizeInBytes());
198 private RenderInfo
buildVerts(DrawContext dc
, int density
, int resolution
, boolean makeSkirts
)
200 int numVertices
= (density
+ 3) * (density
+ 3);
201 java
.nio
.DoubleBuffer verts
= BufferUtil
.newDoubleBuffer(numVertices
* 3);
203 Globe globe
= dc
.getGlobe();
204 ElevationModel
.Elevations elevations
= globe
.getElevationModel().getElevations(this.sector
, resolution
);
206 double latMin
= this.sector
.getMinLatitude().radians
;
207 double latMax
= this.sector
.getMaxLatitude().radians
;
208 double dLat
= (latMax
- latMin
) / density
;
210 double lonMin
= this.sector
.getMinLongitude().radians
;
211 double lonMax
= this.sector
.getMaxLongitude().radians
;
212 double dLon
= (lonMax
- lonMin
) / density
;
216 double verticalExaggeration
= dc
.getVerticalExaggeration();
217 double exaggeratedMinElevation
= makeSkirts ? globe
.getMinElevation() * verticalExaggeration
: 0;
218 double equatorialRadius
= globe
.getEquatorialRadius();
219 double eccentricity
= globe
.getEccentricitySquared();
221 LatLon centroid
= this.sector
.getCentroid();
222 Vec4 refCenter
= globe
.computePointFromPosition(centroid
.getLatitude(), centroid
.getLongitude(), 0d
);
224 for (int j
= 0; j
<= density
+ 2; j
++)
226 double cosLat
= Math
.cos(lat
);
227 double sinLat
= Math
.sin(lat
);
228 double rpm
= equatorialRadius
/ Math
.sqrt(1.0 - eccentricity
* sinLat
* sinLat
);
230 for (int i
= 0; i
<= density
+ 2; i
++)
232 double elevation
= verticalExaggeration
* elevations
.getElevation(lat
, lon
);
233 if (j
== 0 || j
>= density
+ 2 || i
== 0 || i
>= density
+ 2)
234 { // use abs to account for negative elevation.
235 elevation
-= exaggeratedMinElevation
>= 0 ? exaggeratedMinElevation
: -exaggeratedMinElevation
;
238 double x
= ((rpm
+ elevation
) * cosLat
* Math
.sin(lon
)) - refCenter
.x
;
239 double y
= ((rpm
* (1.0 - eccentricity
) + elevation
) * sinLat
) - refCenter
.y
;
240 double z
= ((rpm
+ elevation
) * cosLat
* Math
.cos(lon
)) - refCenter
.z
;
242 verts
.put(iv
++, x
).put(iv
++, y
).put(iv
++, z
);
255 return new RenderInfo(density
, verts
, getGeographicTextureCoordinates(density
), refCenter
,
256 elevations
.getResolution());
259 public void renderMultiTexture(DrawContext dc
, int numTextureUnits
)
263 String msg
= Logging
.getMessage("nullValue.DrawContextIsNull");
264 Logging
.logger().severe(msg
);
265 throw new IllegalArgumentException(msg
);
268 if (numTextureUnits
< 1)
270 String msg
= Logging
.getMessage("generic.NumTextureUnitsLessThanOne");
271 Logging
.logger().severe(msg
);
272 throw new IllegalArgumentException(msg
);
275 this.render(dc
, numTextureUnits
);
278 public void render(DrawContext dc
)
282 String msg
= Logging
.getMessage("nullValue.DrawContextIsNull");
283 Logging
.logger().severe(msg
);
284 throw new IllegalArgumentException(msg
);
290 private long render(DrawContext dc
, int numTextureUnits
)
294 String msg
= Logging
.getMessage("nullValue.RenderInfoIsNull");
295 Logging
.logger().severe(msg
);
296 throw new IllegalStateException(msg
);
299 dc
.getView().pushReferenceCenter(dc
, ri
.referenceCenter
);
302 gl
.glPushClientAttrib(GL
.GL_CLIENT_VERTEX_ARRAY_BIT
);
303 gl
.glEnableClientState(GL
.GL_VERTEX_ARRAY
);
304 gl
.glVertexPointer(3, GL
.GL_DOUBLE
, 0, this.ri
.vertices
.rewind());
306 for (int i
= 0; i
< numTextureUnits
; i
++)
308 gl
.glClientActiveTexture(GL
.GL_TEXTURE0
+ i
);
309 gl
.glEnableClientState(GL
.GL_TEXTURE_COORD_ARRAY
);
310 gl
.glTexCoordPointer(2, GL
.GL_DOUBLE
, 0, ri
.texCoords
.rewind());
313 gl
.glDrawElements(javax
.media
.opengl
.GL
.GL_TRIANGLE_STRIP
, this.ri
.indices
.limit(),
314 javax
.media
.opengl
.GL
.GL_UNSIGNED_INT
, this.ri
.indices
.rewind());
316 gl
.glPopClientAttrib();
318 dc
.getView().popReferenceCenter(dc
);
320 return this.ri
.indices
.limit() - 2; // return number of triangles rendered
323 public void renderWireframe(DrawContext dc
, boolean showTriangles
, boolean showTileBoundary
)
327 String msg
= Logging
.getMessage("nullValue.DrawContextIsNull");
328 Logging
.logger().severe(msg
);
329 throw new IllegalArgumentException(msg
);
334 String msg
= Logging
.getMessage("nullValue.RenderInfoIsNull");
335 Logging
.logger().severe(msg
);
336 throw new IllegalStateException(msg
);
339 java
.nio
.IntBuffer indices
= getIndices(this.ri
.density
);
342 dc
.getView().pushReferenceCenter(dc
, this.ri
.referenceCenter
);
344 javax
.media
.opengl
.GL gl
= dc
.getGL();
346 GL
.GL_DEPTH_BUFFER_BIT
| GL
.GL_POLYGON_BIT
| GL
.GL_TEXTURE_BIT
| GL
.GL_ENABLE_BIT
| GL
.GL_CURRENT_BIT
);
347 gl
.glEnable(GL
.GL_BLEND
);
348 gl
.glBlendFunc(GL
.GL_SRC_ALPHA
, GL
.GL_ONE
);
349 gl
.glDisable(javax
.media
.opengl
.GL
.GL_DEPTH_TEST
);
350 gl
.glEnable(javax
.media
.opengl
.GL
.GL_CULL_FACE
);
351 gl
.glCullFace(javax
.media
.opengl
.GL
.GL_BACK
);
352 gl
.glDisable(javax
.media
.opengl
.GL
.GL_TEXTURE_2D
);
353 gl
.glColor4d(1d
, 1d
, 1d
, 0.2);
354 gl
.glPolygonMode(javax
.media
.opengl
.GL
.GL_FRONT
, javax
.media
.opengl
.GL
.GL_LINE
);
358 gl
.glPushClientAttrib(GL
.GL_CLIENT_VERTEX_ARRAY_BIT
);
359 gl
.glEnableClientState(GL
.GL_VERTEX_ARRAY
);
361 gl
.glVertexPointer(3, GL
.GL_DOUBLE
, 0, this.ri
.vertices
);
362 gl
.glDrawElements(javax
.media
.opengl
.GL
.GL_TRIANGLE_STRIP
, indices
.limit(),
363 javax
.media
.opengl
.GL
.GL_UNSIGNED_INT
, indices
);
365 gl
.glPopClientAttrib();
368 dc
.getView().popReferenceCenter(dc
);
370 if (showTileBoundary
)
371 this.renderPatchBoundary(dc
, gl
);
376 private void renderPatchBoundary(DrawContext dc
, GL gl
)
378 // TODO: Currently only works if called from renderWireframe because no state is set here.
379 // TODO: Draw the boundary using the vertices along the boundary rather than just at the corners.
380 gl
.glColor4d(1d
, 0, 0, 1d
);
381 Vec4
[] corners
= this.sector
.computeCornerPoints(dc
.getGlobe());
383 gl
.glBegin(javax
.media
.opengl
.GL
.GL_QUADS
);
384 gl
.glVertex3d(corners
[0].x
, corners
[0].y
, corners
[0].z
);
385 gl
.glVertex3d(corners
[1].x
, corners
[1].y
, corners
[1].z
);
386 gl
.glVertex3d(corners
[2].x
, corners
[2].y
, corners
[2].z
);
387 gl
.glVertex3d(corners
[3].x
, corners
[3].y
, corners
[3].z
);
391 public void renderBoundingVolume(DrawContext dc
)
393 ((Cylinder
) this.getExtent()).render(dc
);
396 public void pick(DrawContext dc
, java
.awt
.Point pickPoint
)
401 renderTrianglesWithUniqueColors(dc
, ri
);
403 int colorCode
= pickSupport
.getTopColor(dc
, pickPoint
);
404 if (colorCode
< minColorCode
|| colorCode
> maxColorCode
)
407 double EPSILON
= (double) 0.00001f
;
409 int triangleIndex
= colorCode
- minColorCode
- 1;
411 if ((null != ri
.indices
) && (triangleIndex
< ri
.indices
.capacity() - 2))
413 double centerX
= ri
.referenceCenter
.x
;
414 double centerY
= ri
.referenceCenter
.y
;
415 double centerZ
= ri
.referenceCenter
.z
;
417 int vIndex
= 3 * ri
.indices
.get(triangleIndex
);
418 Vec4 v0
= new Vec4((ri
.vertices
.get(vIndex
++) + centerX
),
419 (ri
.vertices
.get(vIndex
++) + centerY
),
420 (ri
.vertices
.get(vIndex
) + centerZ
));
422 vIndex
= 3 * ri
.indices
.get(triangleIndex
+ 1);
423 Vec4 v1
= new Vec4((ri
.vertices
.get(vIndex
++) + centerX
),
424 (ri
.vertices
.get(vIndex
++) + centerY
),
425 (ri
.vertices
.get(vIndex
) + centerZ
));
427 vIndex
= 3 * ri
.indices
.get(triangleIndex
+ 2);
428 Vec4 v2
= new Vec4((ri
.vertices
.get(vIndex
++) + centerX
),
429 (ri
.vertices
.get(vIndex
++) + centerY
),
430 (ri
.vertices
.get(vIndex
) + centerZ
));
432 // get triangle edge vectors and plane normal
433 Vec4 e1
= v1
.subtract3(v0
);
434 Vec4 e2
= v2
.subtract3(v0
);
435 Vec4 N
= e1
.cross3(e2
); // if N is 0, the triangle is degenerate, we are not dealing with it
437 Line ray
= dc
.getView().computeRayFromScreenPoint(pickPoint
.getX(), pickPoint
.getY());
439 Vec4 w0
= ray
.getOrigin().subtract3(v0
);
440 double a
= -N
.dot3(w0
);
441 double b
= N
.dot3(ray
.getDirection());
442 if (java
.lang
.Math
.abs(b
) < EPSILON
) // ray is parallel to triangle plane
443 return; // if a == 0 , ray lies in triangle plane
446 Vec4 intersect
= ray
.getOrigin().add3(ray
.getDirection().multiply3(r
));
447 Position pp
= dc
.getGlobe().computePositionFromPoint(intersect
);
449 // Draw the elevation from the elevation model, not the geode.
450 double elev
= dc
.getGlobe().getElevation(pp
.getLatitude(), pp
.getLongitude());
451 Position p
= new Position(pp
.getLatitude(), pp
.getLongitude(), elev
);
453 PickedObject po
= new PickedObject(colorCode
, p
, pp
.getLatitude(), pp
.getLongitude(), elev
, true);
454 dc
.addPickedObject(po
);
458 private void renderTrianglesWithUniqueColors(DrawContext dc
, RenderInfo ri
)
462 String message
= Logging
.getMessage("nullValue.DrawContextIsNull");
463 Logging
.logger().severe(message
);
464 throw new IllegalStateException(message
);
467 if (ri
.vertices
== null)
470 ri
.vertices
.rewind();
473 javax
.media
.opengl
.GL gl
= dc
.getGL();
475 if (null != ri
.referenceCenter
)
476 dc
.getView().pushReferenceCenter(dc
, ri
.referenceCenter
);
478 minColorCode
= dc
.getUniquePickColor().getRGB();
479 int trianglesNum
= ri
.indices
.capacity() - 2;
481 gl
.glBegin(GL
.GL_TRIANGLES
);
482 for (int i
= 0; i
< trianglesNum
; i
++)
484 java
.awt
.Color color
= dc
.getUniquePickColor();
485 gl
.glColor3ub((byte) (color
.getRed() & 0xFF),
486 (byte) (color
.getGreen() & 0xFF),
487 (byte) (color
.getBlue() & 0xFF));
489 int vIndex
= 3 * ri
.indices
.get(i
);
490 gl
.glVertex3d(ri
.vertices
.get(vIndex
), ri
.vertices
.get(vIndex
+ 1), ri
.vertices
.get(
493 vIndex
= 3 * ri
.indices
.get(i
+ 1);
494 gl
.glVertex3d(ri
.vertices
.get(vIndex
), ri
.vertices
.get(vIndex
+ 1), ri
.vertices
.get(
497 vIndex
= 3 * ri
.indices
.get(i
+ 2);
498 gl
.glVertex3d(ri
.vertices
.get(vIndex
), ri
.vertices
.get(vIndex
+ 1), ri
.vertices
.get(
502 maxColorCode
= dc
.getUniquePickColor().getRGB();
504 if (null != ri
.referenceCenter
)
505 dc
.getView().popReferenceCenter(dc
);
508 public Vec4
getSurfacePoint(Angle latitude
, Angle longitude
, double metersOffset
)
510 if (latitude
== null || longitude
== null)
512 String msg
= Logging
.getMessage("nullValue.LatLonIsNull");
513 Logging
.logger().severe(msg
);
514 throw new IllegalArgumentException(msg
);
517 if (!this.sector
.contains(latitude
, longitude
))
519 // not on this geometry
526 double lat
= latitude
.getDegrees();
527 double lon
= longitude
.getDegrees();
529 double bottom
= this.sector
.getMinLatitude().getDegrees();
530 double top
= this.sector
.getMaxLatitude().getDegrees();
531 double left
= this.sector
.getMinLongitude().getDegrees();
532 double right
= this.sector
.getMaxLongitude().getDegrees();
534 double leftDecimal
= (lon
- left
) / (right
- left
);
535 double bottomDecimal
= (lat
- bottom
) / (top
- bottom
);
537 int row
= (int) (bottomDecimal
* (this.density
));
538 int column
= (int) (leftDecimal
* (this.density
));
540 double l
= createPosition(column
, leftDecimal
, ri
.density
);
541 double h
= createPosition(row
, bottomDecimal
, ri
.density
);
543 Vec4 result
= RectTile
.interpolate(row
, column
, l
, h
, ri
);
544 result
= result
.add3(ri
.referenceCenter
);
545 if (metersOffset
!= 0)
546 result
= applyOffset(this.globe
, result
, metersOffset
);
552 * Offsets <code>point</code> by <code>metersOffset</code> meters.
554 * @param globe the <code>Globe</code> from which to offset
555 * @param point the <code>Vec4</code> to offset
556 * @param metersOffset the magnitude of the offset
557 * @return <code>point</code> offset along its surface normal as if it were on <code>globe</code>
559 private static Vec4
applyOffset(Globe globe
, Vec4 point
, double metersOffset
)
561 Vec4 normal
= globe
.computeSurfaceNormalAtPoint(point
);
562 point
= Vec4
.fromLine3(point
, metersOffset
, normal
);
567 * Computes from a column (or row) number, and a given offset ranged [0,1] corresponding to the distance along
568 * the edge of this sector, where between this column and the next column the corresponding position will fall,
569 * in the range [0,1].
571 * @param start the number of the column or row to the left, below or on this position
572 * @param decimal the distance from the left or bottom of the current sector that this position falls
573 * @param density the number of intervals along the sector's side
574 * @return a decimal ranged [0,1] representing the position between two columns or rows, rather than between two
575 * edges of the sector
577 private static double createPosition(int start
, double decimal
, int density
)
579 double l
= ((double) start
) / (double) density
;
580 double r
= ((double) (start
+ 1)) / (double) density
;
582 return (decimal
- l
) / (r
- l
);
586 * Calculates a <code>Point</code> that sits at <code>xDec</code> offset from <code>column</code> to
587 * <code>column + 1</code> and at <code>yDec</code> offset from <code>row</code> to <code>row + 1</code>.
588 * Accounts for the diagonals.
590 * @param row represents the row which corresponds to a <code>yDec</code> value of 0
591 * @param column represents the column which corresponds to an <code>xDec</code> value of 0
592 * @param xDec constrained to [0,1]
593 * @param yDec constrained to [0,1]
594 * @param ri the render info holding the vertices, etc.
595 * @return a <code>Point</code> geometrically within or on the boundary of the quadrilateral whose bottom left
596 * corner is indexed by (<code>row</code>, <code>column</code>)
598 private static Vec4
interpolate(int row
, int column
, double xDec
, double yDec
, RenderInfo ri
)
603 int numVerticesPerEdge
= ri
.density
+ 3;
605 int bottomLeft
= row
* numVerticesPerEdge
+ column
;
609 int numVertsTimesThree
= numVerticesPerEdge
* 3;
611 Vec4 bL
= new Vec4(ri
.vertices
.get(bottomLeft
), ri
.vertices
.get(bottomLeft
+ 1), ri
.vertices
.get(
613 Vec4 bR
= new Vec4(ri
.vertices
.get(bottomLeft
+ 3), ri
.vertices
.get(bottomLeft
+ 4),
614 ri
.vertices
.get(bottomLeft
+ 5));
616 bottomLeft
+= numVertsTimesThree
;
618 Vec4 tL
= new Vec4(ri
.vertices
.get(bottomLeft
), ri
.vertices
.get(bottomLeft
+ 1), ri
.vertices
.get(
620 Vec4 tR
= new Vec4(ri
.vertices
.get(bottomLeft
+ 3), ri
.vertices
.get(bottomLeft
+ 4),
621 ri
.vertices
.get(bottomLeft
+ 5));
623 return interpolate(bL
, bR
, tR
, tL
, xDec
, yDec
);
627 * Calculates the point at (xDec, yDec) in the two triangles defined by {bL, bR, tL} and {bR, tR, tL}. If
628 * thought of as a quadrilateral, the diagonal runs from tL to bR. Of course, this isn't a quad, it's two
631 * @param bL the bottom left corner
632 * @param bR the bottom right corner
633 * @param tR the top right corner
634 * @param tL the top left corner
635 * @param xDec how far along, [0,1] 0 = left edge, 1 = right edge
636 * @param yDec how far along, [0,1] 0 = bottom edge, 1 = top edge
637 * @return the point xDec, yDec in the co-ordinate system defined by bL, bR, tR, tL
639 private static Vec4
interpolate(Vec4 bL
, Vec4 bR
, Vec4 tR
, Vec4 tL
, double xDec
, double yDec
)
641 double pos
= xDec
+ yDec
;
644 // on the diagonal - what's more, we don't need to do any "oneMinusT" calculation
646 tL
.x
* yDec
+ bR
.x
* xDec
,
647 tL
.y
* yDec
+ bR
.y
* xDec
,
648 tL
.z
* yDec
+ bR
.z
* xDec
);
652 // in the "top right" half
654 // vectors pointing from top right towards the point we want (can be thought of as "negative" vectors)
655 Vec4 horizontalVector
= (tL
.subtract3(tR
)).multiply3(1 - xDec
);
656 Vec4 verticalVector
= (bR
.subtract3(tR
)).multiply3(1 - yDec
);
658 return tR
.add3(horizontalVector
).add3(verticalVector
);
662 // pos < 1 - in the "bottom left" half
664 // vectors pointing from the bottom left towards the point we want
665 Vec4 horizontalVector
= (bR
.subtract3(bL
)).multiply3(xDec
);
666 Vec4 verticalVector
= (tL
.subtract3(bL
)).multiply3(yDec
);
668 return bL
.add3(horizontalVector
).add3(verticalVector
);
672 public String
toString()
674 return "level " + this.level
+ ", density " + this.density
+ ", sector " + this.sector
;
677 protected static java
.nio
.DoubleBuffer
getGeographicTextureCoordinates(int density
)
684 // Approximate 1 to avoid shearing off of right and top skirts in SurfaceTileRenderer.
685 // TODO: dig into this more: why are the skirts being sheared off?
686 final double one
= 0.999999;
688 java
.nio
.DoubleBuffer p
= parameterizations
.get(density
);
692 int coordCount
= (density
+ 3) * (density
+ 3);
693 p
= com
.sun
.opengl
.util
.BufferUtil
.newDoubleBuffer(2 * coordCount
);
694 double delta
= 1d
/ density
;
695 int k
= 2 * (density
+ 3);
696 for (int j
= 0; j
< density
; j
++)
698 double v
= j
* delta
;
700 // skirt column; duplicate first column
705 for (int i
= 0; i
< density
; i
++)
707 p
.put(k
++, i
* delta
); // u
711 // last interior column; force u to 1.
712 p
.put(k
++, one
);//1d);
715 // skirt column; duplicate previous column
716 p
.put(k
++, one
);//1d);
721 //noinspection UnnecessaryLocalVariable
723 p
.put(k
++, 0d
); // skirt column
726 for (int i
= 0; i
< density
; i
++)
728 p
.put(k
++, i
* delta
); // u
731 p
.put(k
++, one
);//1d); // last interior column
734 p
.put(k
++, one
);//1d); // skirt column
738 int kk
= k
- 2 * (density
+ 3);
739 for (int i
= 0; i
< density
+ 3; i
++)
741 p
.put(k
++, p
.get(kk
++));
742 p
.put(k
++, p
.get(kk
++));
747 kk
= 2 * (density
+ 3);
748 for (int i
= 0; i
< density
+ 3; i
++)
750 p
.put(k
++, p
.get(kk
++));
751 p
.put(k
++, p
.get(kk
++));
754 parameterizations
.put(density
, p
);
759 // protected static DoubleBuffer getGeometricTextureCoordinates(Sector sector, double radius, int density)
764 // // Approximate 1 to avoid shearing off of right and top skirts in SurfaceTileRenderer.
765 // // TODO: dig into this more: why are the skirts being sheared off?
766 // final double one = 0.999999;
769 // int coordCount = (density + 3) * (density + 3);
770 // p = BufferUtil.newDoubleBuffer(2 * coordCount);
772 // LatLon sw = new LatLon(sector.getMinLatitude(), sector.getMinLongitude());
773 // LatLon nw = new LatLon(sector.getMaxLatitude(), sector.getMinLongitude());
774 // LatLon se = new LatLon(sector.getMinLatitude(), sector.getMaxLongitude());
775 // LatLon ne = new LatLon(sector.getMaxLatitude(), sector.getMaxLongitude());
777 // double swse = SphericalGeometry.unitDistance(sw, se);
778 // double nwne = SphericalGeometry.unitDistance(nw, ne);
779 // double swnw = SphericalGeometry.unitDistance(sw, nw);
780 // double sene = SphericalGeometry.unitDistance(se, ne);
781 // double s3 = 0.5 * (swse - nwne) / swse;
782 // double s4 = s3 + nwne / swse;
784 // double dt = 1d / density;
785 // int k = 2 * (density + 3);
786 // for (int j = 0; j < density; j++)
788 // double t = j * dt;
789 // double s3p = t * s3;
790 // double s4p = 1 - s3p;//1 + t * (s4 - 1);
791 // double ds = (s4p - s3p) / density;
794 // // skirt column; duplicate first column
798 // // interior columns
799 // for (int i = 0; i <= density; i++)
801 // p.put(k++, s + i * ds); // u
805 // // skirt column; duplicate previous column
806 // p.put(k++, s + ds * density);
810 // // Last interior row
811 // //noinspection UnnecessaryLocalVariable
813 // double ds = (s4 - s3) / density;
814 // //noinspection UnnecessaryLocalVariable
817 // p.put(k++, s); // skirt column
820 // for (int i = 0; i <= density; i++)
822 // p.put(k++, s + i * ds); // u
826 // p.put(k++, s + ds * density);//1d); // skirt column
830 // int kk = k - 2 * (density + 3);
831 // for (int i = 0; i < density + 3; i++)
833 // p.put(k++, p.get(kk++));
834 // p.put(k++, p.get(kk++));
837 // // first skirt row
839 // kk = 2 * (density + 3);
840 // for (int i = 0; i < density + 3; i++)
842 // p.put(k++, p.get(kk++));
843 // p.put(k++, p.get(kk++));
849 private static java
.nio
.IntBuffer
getIndices(int density
)
854 // return a pre-computed buffer if possible.
855 java
.nio
.IntBuffer buffer
= indexLists
.get(density
);
859 int sideSize
= density
+ 2;
861 int indexCount
= 2 * sideSize
* sideSize
+ 4 * sideSize
- 2;
862 buffer
= com
.sun
.opengl
.util
.BufferUtil
.newIntBuffer(indexCount
);
864 for (int i
= 0; i
< sideSize
; i
++)
873 if (i
% 2 == 0) // even
876 for (int j
= 0; j
< sideSize
; j
++)
886 for (int j
= 0; j
< sideSize
; j
++)
895 indexLists
.put(density
, buffer
);
901 private java
.util
.ArrayList
<RectTile
> topLevels
;
902 private SectorGeometryList currentTiles
= new SectorGeometryList();
903 private Frustum currentFrustum
;
904 private int currentLevel
;
905 private int maxLevel
= DEFAULT_MAX_LEVEL
;//14; // TODO: Make configurable
906 private Sector sector
; // union of all tiles selected during call to render()
907 private int density
= DEFAULT_DENSITY
; // TODO: make configurable
909 public EllipsoidRectangularTessellator()
911 // this.topLevels = createTopLevelTiles(globe, DEFAULT_NUM_LAT_SUBDIVISIONS, DEFAULT_NUM_LON_SUBDIVISIONS);
914 public void setGlobe(Globe globe
)
918 String msg
= Logging
.getMessage("nullValue.GlobeIsNull");
919 Logging
.logger().severe(msg
);
920 throw new IllegalArgumentException(msg
);
923 this.topLevels
= createTopLevelTiles(globe
, DEFAULT_NUM_LAT_SUBDIVISIONS
, DEFAULT_NUM_LON_SUBDIVISIONS
);
926 public Sector
getSector()
931 private ArrayList
<RectTile
> createTopLevelTiles(Globe globe
, int nRows
, int nCols
)
933 ArrayList
<RectTile
> tops
= new ArrayList
<RectTile
>(nRows
* nCols
);
935 double deltaLat
= 180d
/ nRows
;
936 double deltaLon
= 360d
/ nCols
;
937 Angle lastLat
= Angle
.NEG90
;
939 for (int row
= 0; row
< DEFAULT_NUM_LAT_SUBDIVISIONS
; row
++)
941 Angle lat
= lastLat
.addDegrees(deltaLat
);
942 if (lat
.getDegrees() + 1d
> 90d
)
945 Angle lastLon
= Angle
.NEG180
;
947 for (int col
= 0; col
< DEFAULT_NUM_LON_SUBDIVISIONS
; col
++)
949 Angle lon
= lastLon
.addDegrees(deltaLon
);
950 if (lon
.getDegrees() + 1d
> 180d
)
953 tops
.add(new RectTile(globe
, 0, this.density
, new Sector(lastLat
, lat
, lastLon
, lon
)));
962 public SectorGeometryList
tessellate(DrawContext dc
)
966 String msg
= Logging
.getMessage("nullValue.DrawContextIsNull");
967 Logging
.logger().severe(msg
);
968 throw new IllegalArgumentException(msg
);
971 if (dc
.getView() == null)
973 String msg
= Logging
.getMessage("nullValue.ViewIsNull");
974 Logging
.logger().severe(msg
);
975 throw new IllegalStateException(msg
);
978 String cacheName
= RectTile
.class.getName();
979 if (!WorldWind
.getMemoryCacheSet().containsCache(cacheName
))
981 long size
= Configuration
.getLongValue(AVKey
.SECTOR_GEOMETRY_CACHE_SIZE
, 20000000L);
982 MemoryCache cache
= new BasicMemoryCache((long) (0.85 * size
), size
);
983 cache
.setName("Terrain");
984 WorldWind
.getMemoryCacheSet().addCache(cacheName
, cache
);
987 this.currentTiles
.clear();
988 this.currentLevel
= 0;
991 this.currentFrustum
= dc
.getView().getFrustumInModelCoordinates();
992 for (RectTile tile
: topLevels
)
994 this.selectVisibleTiles(dc
, tile
);
997 dc
.setVisibleSector(this.getSector());
999 for (SectorGeometry tile
: this.currentTiles
)
1001 ((RectTile
) tile
).makeVerts(dc
);
1004 return this.currentTiles
;
1007 private void selectVisibleTiles(DrawContext dc
, RectTile tile
)
1009 if (!tile
.getExtent().intersects(this.currentFrustum
))
1012 if (this.currentLevel
< this.maxLevel
&& needToSplit(dc
, tile
))
1014 ++this.currentLevel
;
1015 RectTile
[] subtiles
= tile
.split();
1016 for (RectTile child
: subtiles
)
1018 this.selectVisibleTiles(dc
, child
);
1020 --this.currentLevel
;
1023 this.sector
= tile
.getSector().union(this.sector
);
1024 this.currentTiles
.add(tile
);
1027 private static boolean needToSplit(DrawContext dc
, RectTile tile
)
1029 Vec4
[] corners
= tile
.sector
.computeCornerPoints(dc
.getGlobe());
1030 Vec4 centerPoint
= tile
.sector
.computeCenterPoint(dc
.getGlobe());
1032 View view
= dc
.getView();
1033 double d1
= view
.getEyePoint().distanceTo3(corners
[0]);
1034 double d2
= view
.getEyePoint().distanceTo3(corners
[1]);
1035 double d3
= view
.getEyePoint().distanceTo3(corners
[2]);
1036 double d4
= view
.getEyePoint().distanceTo3(corners
[3]);
1037 double d5
= view
.getEyePoint().distanceTo3(centerPoint
);
1039 double minDistance
= d1
;
1040 if (d2
< minDistance
)
1042 if (d3
< minDistance
)
1044 if (d4
< minDistance
)
1046 if (d5
< minDistance
)
1049 double logDist
= Math
.log10(minDistance
);
1050 boolean useTile
= tile
.log10CellSize
<= (logDist
- DEFAULT_LOG10_RESOLUTION_TARGET
);
1055 // private static void printTextureCoords(DoubleBuffer tcs, int density)
1058 // for (int j = density + 2; j >= 0; j--)
1060 // System.out.printf("%2d: ", j * (density + 3));
1061 // for (int i = 0; i <= 2 * (density + 2); i += 2)
1063 // System.out.printf("(%6.3f, %6.3f) ",
1064 // tcs.get(2 * j * (density + 3) + i), tcs.get(2 * j * (density + 3) + i + 1));
1066 // System.out.println();