Update to Worldwind release 0.4.0
[worldwind-tracker.git] / gov / nasa / worldwind / globes / EllipsoidRectangularTessellator.java
blobe4dcc83e93b4a9fd343c0c1aba88ee428af38a02
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
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;
20 import java.nio.*;
21 import java.util.*;
23 /**
24 * @author tag
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;
68 private int density;
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;
78 @Override
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)
87 if (this == o)
88 return true;
89 if (o == null || getClass() != o.getClass())
90 return false;
92 CacheKey cacheKey = (CacheKey) o;
94 if (density != cacheKey.density)
95 return false;
96 if (resolution != cacheKey.resolution)
97 return false;
98 if (Double.compare(cacheKey.verticalExaggeration, verticalExaggeration) != 0)
99 return false;
100 //noinspection RedundantIfStatement
101 if (sector != null ? !sector.equals(cacheKey.sector) : cacheKey.sector != null)
102 return false;
104 return true;
107 public int hashCode()
109 int result;
110 long temp;
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;
116 return result;
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)
140 this.globe = globe;
141 this.level = level;
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()
151 return this.sector;
154 public Extent getExtent()
156 return this.extent;
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]);
174 return subTiles;
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)
182 return;
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);
187 if (this.ri != null)
188 return;
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;
214 int iv = 0;
215 double lat = latMin;
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);
229 double lon = lonMin;
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);
244 if (i > density)
245 lon = lonMax;
246 else if (i != 0)
247 lon += dLon;
249 if (j > density)
250 lat = latMax;
251 else if (j != 0)
252 lat += dLat;
255 return new RenderInfo(density, verts, getGeographicTextureCoordinates(density), refCenter,
256 elevations.getResolution());
259 public void renderMultiTexture(DrawContext dc, int numTextureUnits)
261 if (dc == null)
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)
280 if (dc == null)
282 String msg = Logging.getMessage("nullValue.DrawContextIsNull");
283 Logging.logger().severe(msg);
284 throw new IllegalArgumentException(msg);
287 this.render(dc, 1);
290 private long render(DrawContext dc, int numTextureUnits)
292 if (this.ri == null)
294 String msg = Logging.getMessage("nullValue.RenderInfoIsNull");
295 Logging.logger().severe(msg);
296 throw new IllegalStateException(msg);
299 dc.getView().pushReferenceCenter(dc, ri.referenceCenter);
301 GL gl = dc.getGL();
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)
325 if (dc == null)
327 String msg = Logging.getMessage("nullValue.DrawContextIsNull");
328 Logging.logger().severe(msg);
329 throw new IllegalArgumentException(msg);
332 if (this.ri == null)
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);
340 indices.rewind();
342 dc.getView().pushReferenceCenter(dc, this.ri.referenceCenter);
344 javax.media.opengl.GL gl = dc.getGL();
345 gl.glPushAttrib(
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);
356 if (showTriangles)
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);
373 gl.glPopAttrib();
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);
388 gl.glEnd();
391 public void renderBoundingVolume(DrawContext dc)
393 ((Cylinder) this.getExtent()).render(dc);
396 public void pick(DrawContext dc, java.awt.Point pickPoint)
398 if (this.ri == null)
399 return;
401 renderTrianglesWithUniqueColors(dc, ri);
403 int colorCode = pickSupport.getTopColor(dc, pickPoint);
404 if (colorCode < minColorCode || colorCode > maxColorCode)
405 return;
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
444 double r = a / b;
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)
460 if (dc == null)
462 String message = Logging.getMessage("nullValue.DrawContextIsNull");
463 Logging.logger().severe(message);
464 throw new IllegalStateException(message);
467 if (ri.vertices == null)
468 return;
470 ri.vertices.rewind();
471 ri.indices.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(
491 vIndex + 2));
493 vIndex = 3 * ri.indices.get(i + 1);
494 gl.glVertex3d(ri.vertices.get(vIndex), ri.vertices.get(vIndex + 1), ri.vertices.get(
495 vIndex + 2));
497 vIndex = 3 * ri.indices.get(i + 2);
498 gl.glVertex3d(ri.vertices.get(vIndex), ri.vertices.get(vIndex + 1), ri.vertices.get(
499 vIndex + 2));
501 gl.glEnd();
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
520 return null;
523 if (this.ri == null)
524 return null;
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);
548 return result;
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);
563 return point;
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)
600 row++;
601 column++;
603 int numVerticesPerEdge = ri.density + 3;
605 int bottomLeft = row * numVerticesPerEdge + column;
607 bottomLeft *= 3;
609 int numVertsTimesThree = numVerticesPerEdge * 3;
611 Vec4 bL = new Vec4(ri.vertices.get(bottomLeft), ri.vertices.get(bottomLeft + 1), ri.vertices.get(
612 bottomLeft + 2));
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(
619 bottomLeft + 2));
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
629 * triangles.
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;
642 if (pos == 1)
644 // on the diagonal - what's more, we don't need to do any "oneMinusT" calculation
645 return new Vec4(
646 tL.x * yDec + bR.x * xDec,
647 tL.y * yDec + bR.y * xDec,
648 tL.z * yDec + bR.z * xDec);
650 else if (pos > 1)
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);
660 else
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)
679 if (density < 1)
681 density = 1;
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);
689 if (p != null)
690 return p;
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
701 p.put(k++, 0d);
702 p.put(k++, v);
704 // interior columns
705 for (int i = 0; i < density; i++)
707 p.put(k++, i * delta); // u
708 p.put(k++, v);
711 // last interior column; force u to 1.
712 p.put(k++, one);//1d);
713 p.put(k++, v);
715 // skirt column; duplicate previous column
716 p.put(k++, one);//1d);
717 p.put(k++, v);
720 // Last interior row
721 //noinspection UnnecessaryLocalVariable
722 double v = one;//1d;
723 p.put(k++, 0d); // skirt column
724 p.put(k++, v);
726 for (int i = 0; i < density; i++)
728 p.put(k++, i * delta); // u
729 p.put(k++, v);
731 p.put(k++, one);//1d); // last interior column
732 p.put(k++, v);
734 p.put(k++, one);//1d); // skirt column
735 p.put(k++, v);
737 // last skirt row
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++));
745 // first skirt row
746 k = 0;
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);
756 return p;
759 // protected static DoubleBuffer getGeometricTextureCoordinates(Sector sector, double radius, int density)
760 // {
761 // if (density < 1)
762 // density = 1;
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;
768 // DoubleBuffer p;
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++)
787 // {
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;
792 // double s = s3p;
794 // // skirt column; duplicate first column
795 // p.put(k++, s);
796 // p.put(k++, t);
798 // // interior columns
799 // for (int i = 0; i <= density; i++)
800 // {
801 // p.put(k++, s + i * ds); // u
802 // p.put(k++, t);
803 // }
805 // // skirt column; duplicate previous column
806 // p.put(k++, s + ds * density);
807 // p.put(k++, t);
808 // }
810 // // Last interior row
811 // //noinspection UnnecessaryLocalVariable
812 // double t = one;
813 // double ds = (s4 - s3) / density;
814 // //noinspection UnnecessaryLocalVariable
815 // double s = s3;
817 // p.put(k++, s); // skirt column
818 // p.put(k++, t);
820 // for (int i = 0; i <= density; i++)
821 // {
822 // p.put(k++, s + i * ds); // u
823 // p.put(k++, t);
824 // }
826 // p.put(k++, s + ds * density);//1d); // skirt column
827 // p.put(k++, t);
829 // // last skirt row
830 // int kk = k - 2 * (density + 3);
831 // for (int i = 0; i < density + 3; i++)
832 // {
833 // p.put(k++, p.get(kk++));
834 // p.put(k++, p.get(kk++));
835 // }
837 // // first skirt row
838 // k = 0;
839 // kk = 2 * (density + 3);
840 // for (int i = 0; i < density + 3; i++)
841 // {
842 // p.put(k++, p.get(kk++));
843 // p.put(k++, p.get(kk++));
844 // }
846 // return p;
847 // }
849 private static java.nio.IntBuffer getIndices(int density)
851 if (density < 1)
852 density = 1;
854 // return a pre-computed buffer if possible.
855 java.nio.IntBuffer buffer = indexLists.get(density);
856 if (buffer != null)
857 return buffer;
859 int sideSize = density + 2;
861 int indexCount = 2 * sideSize * sideSize + 4 * sideSize - 2;
862 buffer = com.sun.opengl.util.BufferUtil.newIntBuffer(indexCount);
863 int k = 0;
864 for (int i = 0; i < sideSize; i++)
866 buffer.put(k);
867 if (i > 0)
869 buffer.put(++k);
870 buffer.put(k);
873 if (i % 2 == 0) // even
875 buffer.put(++k);
876 for (int j = 0; j < sideSize; j++)
878 k += sideSize;
879 buffer.put(k);
880 buffer.put(++k);
883 else // odd
885 buffer.put(--k);
886 for (int j = 0; j < sideSize; j++)
888 k -= sideSize;
889 buffer.put(k);
890 buffer.put(--k);
895 indexLists.put(density, buffer);
897 return 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)
916 if (globe == null)
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()
928 return this.sector;
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)
943 lat = Angle.POS90;
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)
951 lon = Angle.POS180;
953 tops.add(new RectTile(globe, 0, this.density, new Sector(lastLat, lat, lastLon, lon)));
954 lastLon = lon;
956 lastLat = lat;
959 return tops;
962 public SectorGeometryList tessellate(DrawContext dc)
964 if (dc == null)
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;
989 this.sector = null;
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))
1010 return;
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;
1021 return;
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)
1041 minDistance = d2;
1042 if (d3 < minDistance)
1043 minDistance = d3;
1044 if (d4 < minDistance)
1045 minDistance = d4;
1046 if (d5 < minDistance)
1047 minDistance = d5;
1049 double logDist = Math.log10(minDistance);
1050 boolean useTile = tile.log10CellSize <= (logDist - DEFAULT_LOG10_RESOLUTION_TARGET);
1052 return !useTile;
1055 // private static void printTextureCoords(DoubleBuffer tcs, int density)
1056 // {
1057 // tcs.rewind();
1058 // for (int j = density + 2; j >= 0; j--)
1059 // {
1060 // System.out.printf("%2d: ", j * (density + 3));
1061 // for (int i = 0; i <= 2 * (density + 2); i += 2)
1062 // {
1063 // System.out.printf("(%6.3f, %6.3f) ",
1064 // tcs.get(2 * j * (density + 3) + i), tcs.get(2 * j * (density + 3) + i + 1));
1065 // }
1066 // System.out.println();
1067 // }
1068 // }