src.gov.nasa.worldwind.render.airspaces.Polygon Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.render.airspaces;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.cache.Cacheable;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.geom.Box;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.util.*;
import javax.media.opengl.*;
import java.util.*;
/**
* @author tag
* @version $Id: Polygon.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class Polygon extends AbstractAirspace
{
protected static final int DEFAULT_SUBDIVISIONS = 3;
protected static final int MINIMAL_GEOMETRY_SUBDIVISIONS = 2;
private List locations = new ArrayList();
private boolean enableCaps = true;
private int subdivisions = DEFAULT_SUBDIVISIONS;
public Polygon(Iterable locations)
{
this.addLocations(locations);
this.makeDefaultDetailLevels();
}
public Polygon(AirspaceAttributes attributes)
{
super(attributes);
this.makeDefaultDetailLevels();
}
public Polygon()
{
this.makeDefaultDetailLevels();
}
private void makeDefaultDetailLevels()
{
List levels = new ArrayList();
double[] ramp = ScreenSizeDetailLevel.computeDefaultScreenSizeRamp(5);
DetailLevel level;
level = new ScreenSizeDetailLevel(ramp[0], "Detail-Level-0");
level.setValue(SUBDIVISIONS, 4);
level.setValue(DISABLE_TERRAIN_CONFORMANCE, false);
levels.add(level);
level = new ScreenSizeDetailLevel(ramp[1], "Detail-Level-1");
level.setValue(SUBDIVISIONS, 3);
level.setValue(DISABLE_TERRAIN_CONFORMANCE, false);
levels.add(level);
level = new ScreenSizeDetailLevel(ramp[2], "Detail-Level-2");
level.setValue(SUBDIVISIONS, 2);
level.setValue(DISABLE_TERRAIN_CONFORMANCE, false);
levels.add(level);
level = new ScreenSizeDetailLevel(ramp[3], "Detail-Level-3");
level.setValue(SUBDIVISIONS, 1);
level.setValue(DISABLE_TERRAIN_CONFORMANCE, false);
levels.add(level);
level = new ScreenSizeDetailLevel(ramp[4], "Detail-Level-4");
level.setValue(SUBDIVISIONS, 0);
level.setValue(DISABLE_TERRAIN_CONFORMANCE, true);
levels.add(level);
this.setDetailLevels(levels);
}
public List getLocations()
{
return Collections.unmodifiableList(this.locations);
}
public void setLocations(Iterable locations)
{
this.locations.clear();
this.addLocations(locations);
}
protected List getLocationList()
{
return this.locations;
}
protected void addLocations(Iterable newLocations)
{
if (newLocations != null)
for (LatLon ll : newLocations)
{
if (ll != null)
this.locations.add(ll);
}
this.setExtentOutOfDate();
}
public boolean isEnableCaps()
{
return this.enableCaps;
}
public void setEnableCaps(boolean enable)
{
this.enableCaps = enable;
}
public Position getReferencePosition()
{
return this.computeReferencePosition(this.locations, this.getAltitudes());
}
protected Extent computeExtent(Globe globe, double verticalExaggeration)
{
List points = this.computeMinimalGeometry(globe, verticalExaggeration);
if (points == null || points.isEmpty())
return null;
// Add a point at the center of this polygon to the points used to compute its extent. The center point captures
// the curvature of the globe when the polygon's minimal geometry only contain any points near the polygon's
// edges.
Vec4 centerPoint = Vec4.computeAveragePoint(points);
LatLon centerLocation = globe.computePositionFromPoint(centerPoint);
this.makeExtremePoints(globe, verticalExaggeration, Arrays.asList(centerLocation), points);
return Box.computeBoundingBox(points);
}
@Override
protected List computeMinimalGeometry(Globe globe, double verticalExaggeration)
{
if (this.getLocations() == null)
return null;
ArrayList locations = new ArrayList(this.getLocations());
ArrayList tessellatedLocations = new ArrayList();
this.makeTessellatedLocations(globe, MINIMAL_GEOMETRY_SUBDIVISIONS, locations, tessellatedLocations);
ArrayList points = new ArrayList();
this.makeExtremePoints(globe, verticalExaggeration, tessellatedLocations, points);
return points;
}
protected void doMoveTo(Position oldRef, Position newRef)
{
if (oldRef == null)
{
String message = "nullValue.OldRefIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (newRef == null)
{
String message = "nullValue.NewRefIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
super.doMoveTo(oldRef, newRef);
int count = this.locations.size();
LatLon[] newLocations = new LatLon[count];
for (int i = 0; i < count; i++)
{
LatLon ll = this.locations.get(i);
double distance = LatLon.greatCircleDistance(oldRef, ll).radians;
double azimuth = LatLon.greatCircleAzimuth(oldRef, ll).radians;
newLocations[i] = LatLon.greatCircleEndPosition(newRef, azimuth, distance);
}
this.setLocations(Arrays.asList(newLocations));
}
protected int getSubdivisions()
{
return this.subdivisions;
}
protected void setSubdivisions(int subdivisions)
{
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions=" + subdivisions);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.subdivisions = subdivisions;
}
//**************************************************************//
//******************** Geometry Rendering ********************//
//**************************************************************//
protected Vec4 computeReferenceCenter(DrawContext dc)
{
Extent extent = this.getExtent(dc);
return extent != null ? extent.getCenter() : null;
}
protected void doRenderGeometry(DrawContext dc, String drawStyle)
{
this.doRenderGeometry(dc, drawStyle, this.locations, null);
}
protected void doRenderGeometry(DrawContext dc, String drawStyle, List locations, List edgeFlags)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dc.getGL() == null)
{
String message = Logging.getMessage("nullValue.DrawingContextGLIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (locations == null)
{
String message = "nullValue.LocationsIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (locations.size() == 0)
return;
double[] altitudes = this.getAltitudes(dc.getVerticalExaggeration());
boolean[] terrainConformant = this.isTerrainConforming();
boolean enableCaps = this.isEnableCaps();
int subdivisions = this.subdivisions;
if (this.getAltitudeDatum()[0].equals(AVKey.ABOVE_GROUND_REFERENCE)
|| this.getAltitudeDatum()[1].equals(AVKey.ABOVE_GROUND_REFERENCE))
{
this.adjustForGroundReference(dc, terrainConformant, altitudes);
}
if (this.isEnableLevelOfDetail())
{
DetailLevel level = this.computeDetailLevel(dc);
Object o = level.getValue(SUBDIVISIONS);
if (o != null && o instanceof Integer)
subdivisions = (Integer) o;
o = level.getValue(DISABLE_TERRAIN_CONFORMANCE);
if (o != null && o instanceof Boolean && (Boolean) o)
terrainConformant[0] = terrainConformant[1] = false;
}
Vec4 referenceCenter = this.computeReferenceCenter(dc);
this.setExpiryTime(this.nextExpiryTime(dc, terrainConformant));
this.clearElevationMap();
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
OGLStackHandler ogsh = new OGLStackHandler();
try
{
dc.getView().pushReferenceCenter(dc, referenceCenter);
if (Airspace.DRAW_STYLE_FILL.equals(drawStyle))
{
if (enableCaps && !this.isAirspaceCollapsed())
{
ogsh.pushAttrib(gl, GL2.GL_POLYGON_BIT);
gl.glEnable(GL.GL_CULL_FACE);
gl.glFrontFace(GL.GL_CCW);
}
this.drawPolygonFill(dc, locations, edgeFlags, altitudes, terrainConformant, enableCaps, subdivisions,
referenceCenter);
}
else if (Airspace.DRAW_STYLE_OUTLINE.equals(drawStyle))
{
this.drawPolygonOutline(dc, locations, edgeFlags, altitudes, terrainConformant, enableCaps,
subdivisions, referenceCenter);
}
}
finally
{
dc.getView().popReferenceCenter(dc);
ogsh.pop(gl);
}
}
protected void adjustForGroundReference(DrawContext dc, boolean[] terrainConformant, double[] altitudes)
{
LatLon groundRef = this.getGroundReference();
if (groundRef == null && this.getLocationList().size() > 0)
groundRef = this.getLocationList().get(0);
this.adjustForGroundReference(dc, terrainConformant, altitudes, groundRef); // no-op if groudRef is null
}
protected int computeCartesianPolygon(Globe globe, List locations, List edgeFlags,
Vec4[] points, Boolean[] edgeFlagArray, Matrix[] transform)
{
if (globe == null)
{
String message = Logging.getMessage("nullValue.GlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (locations == null)
{
String message = "nullValue.LocationsIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (points == null)
{
String message = "nullValue.LocationsIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (points.length < (1 + locations.size()))
{
String message = Logging.getMessage("generic.ArrayInvalidLength",
"points.length < " + (1 + locations.size()));
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (transform == null)
{
String message = "nullValue.TransformIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (transform.length < 1)
{
String message = Logging.getMessage("generic.ArrayInvalidLength",
"transform.length < 1");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Allocate space to hold the list of locations and location vertices.
int locationCount = locations.size();
// Compute the cartesian points for each location.
for (int i = 0; i < locationCount; i++)
{
LatLon ll = locations.get(i);
points[i] = globe.computePointFromPosition(ll.getLatitude(), ll.getLongitude(), 0.0);
if (edgeFlagArray != null)
edgeFlagArray[i] = (edgeFlags != null) ? edgeFlags.get(i) : true;
}
// Compute the average of the cartesian points.
Vec4 centerPoint = Vec4.computeAveragePoint(Arrays.asList(points));
// Test whether the polygon is closed. If it is not closed, repeat the first vertex.
if (!points[0].equals(points[locationCount - 1]))
{
points[locationCount] = points[0];
if (edgeFlagArray != null)
edgeFlagArray[locationCount] = edgeFlagArray[0];
locationCount++;
}
// Compute a transform that will map the cartesian points to a local coordinate system centered at the average
// of the points and oriented with the globe surface.
Position centerPos = globe.computePositionFromPoint(centerPoint);
Matrix tx = globe.computeSurfaceOrientationAtPosition(centerPos);
Matrix txInv = tx.getInverse();
// Map the cartesian points to a local coordinate space.
for (int i = 0; i < locationCount; i++)
{
points[i] = points[i].transformBy4(txInv);
}
transform[0] = tx;
return locationCount;
}
private void makePolygonVertices(int count, Vec4[] points, float[] vertices)
{
for (int i = 0; i < count; i++)
{
int index = 3 * i;
vertices[index] = (float) points[i].x;
vertices[index + 1] = (float) points[i].y;
vertices[index + 2] = (float) points[i].z;
}
}
//**************************************************************//
//******************** Polygon ******************//
//**************************************************************//
protected static class PolygonGeometry implements Cacheable
{
private Geometry fillIndexGeometry;
private Geometry outlineIndexGeometry;
private Geometry vertexGeometry;
public PolygonGeometry()
{
this.fillIndexGeometry = new Geometry();
this.outlineIndexGeometry = new Geometry();
this.vertexGeometry = new Geometry();
}
public Geometry getFillIndexGeometry()
{
return this.fillIndexGeometry;
}
public Geometry getOutlineIndexGeometry()
{
return this.outlineIndexGeometry;
}
public Geometry getVertexGeometry()
{
return this.vertexGeometry;
}
public long getSizeInBytes()
{
long sizeInBytes = 0L;
sizeInBytes += (this.fillIndexGeometry != null) ? this.fillIndexGeometry.getSizeInBytes() : 0L;
sizeInBytes += (this.outlineIndexGeometry != null) ? this.outlineIndexGeometry.getSizeInBytes() : 0L;
sizeInBytes += (this.vertexGeometry != null) ? this.vertexGeometry.getSizeInBytes() : 0L;
return sizeInBytes;
}
}
private PolygonGeometry getPolygonGeometry(DrawContext dc, List locations, List edgeFlags,
double[] altitudes, boolean[] terrainConformant,
boolean enableCaps, int subdivisions,
Vec4 referenceCenter)
{
Object cacheKey = new Geometry.CacheKey(dc.getGlobe(), this.getClass(), "Polygon",
locations, edgeFlags, altitudes[0], altitudes[1], terrainConformant[0], terrainConformant[1],
enableCaps, subdivisions, referenceCenter);
// Wrap geometry creation in a try/catch block. We do this to catch and handle OutOfMemoryErrors caused during
// tessellation of the polygon vertices. If the polygon cannot be tessellated, we replace the polygon's
// locations with an empty list to prevent subsequent tessellation attempts, and to avoid rendering a misleading
// representation by omitting any part of the geometry.
try
{
PolygonGeometry geom = (PolygonGeometry) this.getGeometryCache().getObject(cacheKey);
if (geom == null || this.isExpired(dc, geom.getVertexGeometry()))
{
if (geom == null)
geom = new PolygonGeometry();
this.makePolygon(dc, locations, edgeFlags, altitudes, terrainConformant, enableCaps, subdivisions,
referenceCenter, geom);
this.updateExpiryCriteria(dc, geom.getVertexGeometry());
this.getGeometryCache().add(cacheKey, geom);
}
return geom;
}
catch (OutOfMemoryError e)
{
String message = Logging.getMessage("generic.ExceptionWhileTessellating", this);
Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
//noinspection ThrowableInstanceNeverThrown
dc.addRenderingException(new WWRuntimeException(message, e));
this.handleUnsuccessfulGeometryCreation();
return null;
}
}
protected void handleUnsuccessfulGeometryCreation()
{
// If creating the polygon geometry was unsuccessful, we modify the polygon to avoid any additional creation
// attempts, and free any resources that the polygon won't use. This is done to gracefully handle
// OutOfMemoryErrors throws while tessellating the polygon geometry.
// Replace the polygon's locations with an empty list. This ensures that any rendering code won't attempt to
// re-create the polygon's geometry.
this.locations = Collections.emptyList();
// Reinitialize the polygon, since we've replaced its locations with an empty list.
this.setExtentOutOfDate();
}
private void drawPolygonFill(DrawContext dc, List locations, List edgeFlags,
double[] altitudes, boolean[] terrainConformant,
boolean enableCaps, int subdivisions,
Vec4 referenceCenter)
{
PolygonGeometry geom = this.getPolygonGeometry(dc, locations, edgeFlags, altitudes, terrainConformant,
enableCaps, subdivisions, referenceCenter);
if (geom != null)
this.getRenderer().drawGeometry(dc, geom.getFillIndexGeometry(), geom.getVertexGeometry());
}
private void drawPolygonOutline(DrawContext dc, List locations, List edgeFlags,
double[] altitudes, boolean[] terrainConformant,
boolean enableCaps, int subdivisions,
Vec4 referenceCenter)
{
PolygonGeometry geom = this.getPolygonGeometry(dc, locations, edgeFlags, altitudes, terrainConformant,
enableCaps, subdivisions, referenceCenter);
if (geom != null)
this.getRenderer().drawGeometry(dc, geom.getOutlineIndexGeometry(), geom.getVertexGeometry());
}
private void makePolygon(DrawContext dc, List locations, List edgeFlags,
double[] altitudes, boolean[] terrainConformant,
boolean enableCaps, int subdivisions,
Vec4 referenceCenter,
PolygonGeometry dest)
{
if (locations.size() == 0)
return;
GeometryBuilder gb = this.getGeometryBuilder();
Vec4[] polyPoints = new Vec4[locations.size() + 1];
Boolean[] polyEdgeFlags = new Boolean[locations.size() + 1];
Matrix[] polyTransform = new Matrix[1];
int polyCount = this.computeCartesianPolygon(dc.getGlobe(), locations, edgeFlags, polyPoints, polyEdgeFlags,
polyTransform);
// Compute the winding order of the planar cartesian points. If the order is not counter-clockwise, then
// reverse the locations and points ordering.
int winding = gb.computePolygonWindingOrder2(0, polyCount, polyPoints);
if (winding != GeometryBuilder.COUNTER_CLOCKWISE)
{
gb.reversePoints(0, polyCount, polyPoints);
gb.reversePoints(0, polyCount, polyEdgeFlags);
}
float[] polyVertices = new float[3 * polyCount];
this.makePolygonVertices(polyCount, polyPoints, polyVertices);
int fillDrawMode = GL.GL_TRIANGLES;
int outlineDrawMode = GL.GL_LINES;
int fillIndexCount = 0;
int outlineIndexCount = 0;
int vertexCount = 0;
GeometryBuilder.IndexedTriangleArray ita = null;
fillIndexCount += this.getEdgeFillIndexCount(polyCount, subdivisions);
outlineIndexCount += this.getEdgeOutlineIndexCount(polyCount, subdivisions, polyEdgeFlags);
vertexCount += this.getEdgeVertexCount(polyCount, subdivisions);
if (enableCaps)
{
ita = gb.tessellatePolygon2(0, polyCount, polyVertices);
for (int i = 0; i < subdivisions; i++)
{
gb.subdivideIndexedTriangleArray(ita);
}
fillIndexCount += ita.getIndexCount();
vertexCount += ita.getVertexCount();
// Bottom cap isn't drawn if airspace is collapsed.
if (!this.isAirspaceCollapsed())
{
fillIndexCount += ita.getIndexCount();
vertexCount += ita.getVertexCount();
}
}
int[] fillIndices = new int[fillIndexCount];
int[] outlineIndices = new int[outlineIndexCount];
float[] vertices = new float[3 * vertexCount];
float[] normals = new float[3 * vertexCount];
int fillIndexPos = 0;
int outlineIndexPos = 0;
int vertexPos = 0;
this.makeEdge(dc, polyCount, polyVertices, polyEdgeFlags, altitudes, terrainConformant, subdivisions,
GeometryBuilder.OUTSIDE, polyTransform[0], referenceCenter,
fillIndexPos, fillIndices, outlineIndexPos, outlineIndices, vertexPos, vertices, normals);
fillIndexPos += this.getEdgeFillIndexCount(polyCount, subdivisions);
outlineIndexPos += this.getEdgeOutlineIndexCount(polyCount, subdivisions, polyEdgeFlags);
vertexPos += this.getEdgeVertexCount(polyCount, subdivisions);
if (enableCaps)
{
this.makeCap(dc, ita, altitudes[1], terrainConformant[1], GeometryBuilder.OUTSIDE, polyTransform[0],
referenceCenter, fillIndexPos, fillIndices, vertexPos, vertices, normals);
fillIndexPos += ita.getIndexCount();
vertexPos += ita.getVertexCount();
// Bottom cap isn't drawn if airspace is collapsed.
if (!this.isAirspaceCollapsed())
{
this.makeCap(dc, ita, altitudes[0], terrainConformant[0], GeometryBuilder.INSIDE, polyTransform[0],
referenceCenter, fillIndexPos, fillIndices, vertexPos, vertices, normals);
fillIndexPos += ita.getIndexCount();
vertexPos += ita.getVertexCount();
}
}
dest.getFillIndexGeometry().setElementData(fillDrawMode, fillIndexCount, fillIndices);
dest.getOutlineIndexGeometry().setElementData(outlineDrawMode, outlineIndexCount, outlineIndices);
dest.getVertexGeometry().setVertexData(vertexCount, vertices);
dest.getVertexGeometry().setNormalData(vertexCount, normals);
}
protected void makeTessellatedLocations(Globe globe, int subdivisions, List locations,
List tessellatedLocations)
{
ArrayList points = new ArrayList();
for (LatLon ll : locations)
{
points.add(globe.computePointFromLocation(ll));
}
//noinspection StringEquality
if (WWMath.computeWindingOrderOfLocations(locations) != AVKey.COUNTER_CLOCKWISE)
Collections.reverse(locations);
Vec4 centerPoint = Vec4.computeAveragePoint(points);
Vec4 surfaceNormal = globe.computeSurfaceNormalAtPoint(centerPoint);
int numPoints = points.size();
float[] coords = new float[3 * numPoints];
for (int i = 0; i < numPoints; i++)
{
points.get(i).toFloatArray(coords, 3 * i, 3);
}
GeometryBuilder gb = new GeometryBuilder();
GeometryBuilder.IndexedTriangleArray tessellatedPoints = gb.tessellatePolygon(0, numPoints, coords,
surfaceNormal);
for (int i = 0; i < subdivisions; i++)
{
gb.subdivideIndexedTriangleArray(tessellatedPoints);
}
for (int i = 0; i < tessellatedPoints.getVertexCount(); i++)
{
Vec4 v = Vec4.fromFloatArray(tessellatedPoints.getVertices(), 3 * i, 3);
tessellatedLocations.add(globe.computePositionFromPoint(v));
}
}
//**************************************************************//
//******************** Polygon Edge ********************//
//**************************************************************//
private int getEdgeFillIndexCount(int count, int subdivisions)
{
return (count - 1) * this.getSectionFillIndexCount(subdivisions);
}
private int getEdgeOutlineIndexCount(int count, int subdivisions, Boolean[] edgeFlags)
{
int sum = 0;
for (int i = 0; i < count - 1; i++)
{
sum += this.getSectionOutlineIndexCount(subdivisions, edgeFlags[i], edgeFlags[i + 1]);
}
return sum;
}
private int getEdgeVertexCount(int count, int subdivisions)
{
return (count - 1) * this.getSectionVertexCount(subdivisions);
}
private int getSectionFillIndexCount(int subdivisions)
{
GeometryBuilder gb = this.getGeometryBuilder();
return 6 * (gb.getSubdivisionPointsVertexCount(subdivisions) - 1);
}
private int getSectionOutlineIndexCount(int subdivisions, boolean beginEdgeFlag, boolean endEdgeFlag)
{
GeometryBuilder gb = this.getGeometryBuilder();
int count = 4 * (gb.getSubdivisionPointsVertexCount(subdivisions) - 1);
if (beginEdgeFlag)
count += 2;
if (endEdgeFlag)
count += 2;
return count;
}
private int getSectionVertexCount(int subdivisions)
{
GeometryBuilder gb = this.getGeometryBuilder();
return 2 * gb.getSubdivisionPointsVertexCount(subdivisions);
}
private void makeEdge(DrawContext dc, int count, float[] locations, Boolean[] edgeFlags,
double[] altitudes, boolean[] terrainConformant,
int subdivisions, int orientation,
Matrix locationTransform,
Vec4 referenceCenter,
int fillIndexPos, int[] fillIndices,
int outlineIndexPos, int[] outlineIndices,
int vertexPos, float[] vertices, float[] normals)
{
GeometryBuilder gb = this.getGeometryBuilder();
gb.setOrientation(orientation);
int sectionFillIndexCount = this.getSectionFillIndexCount(subdivisions);
int sectionVertexCount = this.getSectionVertexCount(subdivisions);
for (int i = 0; i < count - 1; i++)
{
boolean beginEdgeFlag = edgeFlags[i];
boolean endEdgeFlag = edgeFlags[i + 1];
this.makeSectionFillIndices(subdivisions, vertexPos, fillIndexPos, fillIndices);
this.makeSectionOutlineIndices(subdivisions, vertexPos, outlineIndexPos, outlineIndices,
beginEdgeFlag, endEdgeFlag);
this.makeSectionVertices(dc, i, locations, altitudes, terrainConformant, subdivisions,
locationTransform, referenceCenter, vertexPos, vertices);
gb.makeIndexedTriangleArrayNormals(fillIndexPos, sectionFillIndexCount, fillIndices,
vertexPos, sectionVertexCount, vertices, normals);
fillIndexPos += sectionFillIndexCount;
outlineIndexPos += this.getSectionOutlineIndexCount(subdivisions, beginEdgeFlag, endEdgeFlag);
vertexPos += sectionVertexCount;
}
}
private void makeSectionFillIndices(int subdivisions, int vertexPos, int indexPos, int[] indices)
{
GeometryBuilder gb = this.getGeometryBuilder();
int count = gb.getSubdivisionPointsVertexCount(subdivisions);
int index = indexPos;
int pos, nextPos;
for (int i = 0; i < count - 1; i++)
{
pos = vertexPos + 2 * i;
nextPos = vertexPos + 2 * (i + 1);
indices[index++] = pos + 1;
indices[index++] = pos;
indices[index++] = nextPos + 1;
indices[index++] = nextPos + 1;
indices[index++] = pos;
indices[index++] = nextPos;
}
}
private void makeSectionOutlineIndices(int subdivisions, int vertexPos, int indexPos, int[] indices,
boolean beginEdgeFlag, boolean endEdgeFlag)
{
GeometryBuilder gb = this.getGeometryBuilder();
int count = gb.getSubdivisionPointsVertexCount(subdivisions);
int index = indexPos;
int pos, nextPos;
if (beginEdgeFlag)
{
pos = vertexPos;
indices[index++] = pos;
indices[index++] = pos + 1;
}
for (int i = 0; i < count - 1; i++)
{
pos = vertexPos + 2 * i;
nextPos = vertexPos + 2 * (i + 1);
indices[index++] = pos;
indices[index++] = nextPos;
indices[index++] = pos + 1;
indices[index++] = nextPos + 1;
}
if (endEdgeFlag)
{
pos = vertexPos + 2 * (count - 1);
indices[index++] = pos;
indices[index] = pos + 1;
}
}
private void makeSectionVertices(DrawContext dc, int locationPos, float[] locations,
double[] altitude, boolean[] terrainConformant,
int subdivisions,
Matrix locationTransform,
Vec4 referenceCenter,
int vertexPos, float[] vertices)
{
GeometryBuilder gb = this.getGeometryBuilder();
int numPoints = gb.getSubdivisionPointsVertexCount(subdivisions);
Globe globe = dc.getGlobe();
int index1 = 3 * locationPos;
int index2 = 3 * (locationPos + 1);
float[] locationVerts = new float[3 * numPoints];
gb.makeSubdivisionPoints(
locations[index1], locations[index1 + 1], locations[index1 + 2],
locations[index2], locations[index2 + 1], locations[index2 + 2],
subdivisions, locationVerts);
for (int i = 0; i < numPoints; i++)
{
int index = 3 * i;
Vec4 vec = new Vec4(locationVerts[index], locationVerts[index + 1], locationVerts[index + 2]);
vec = vec.transformBy4(locationTransform);
Position pos = globe.computePositionFromPoint(vec);
for (int j = 0; j < 2; j++)
{
vec = this.computePointFromPosition(dc, pos.getLatitude(), pos.getLongitude(), altitude[j],
terrainConformant[j]);
index = 2 * i + j;
index = 3 * (vertexPos + index);
vertices[index] = (float) (vec.x - referenceCenter.x);
vertices[index + 1] = (float) (vec.y - referenceCenter.y);
vertices[index + 2] = (float) (vec.z - referenceCenter.z);
}
}
}
//**************************************************************//
//******************** Polygon Cap ********************//
//**************************************************************//
private void makeCap(DrawContext dc, GeometryBuilder.IndexedTriangleArray ita,
double altitude, boolean terrainConformant,
int orientation,
Matrix locationTransform,
Vec4 referenceCenter,
int indexPos, int[] indices,
int vertexPos, float[] vertices, float[] normals)
{
GeometryBuilder gb = this.getGeometryBuilder();
Globe globe = dc.getGlobe();
int indexCount = ita.getIndexCount();
int vertexCount = ita.getVertexCount();
int[] locationIndices = ita.getIndices();
float[] locationVerts = ita.getVertices();
this.copyIndexArray(indexCount, (orientation == GeometryBuilder.INSIDE), locationIndices,
vertexPos, indexPos, indices);
for (int i = 0; i < vertexCount; i++)
{
int index = 3 * i;
Vec4 vec = new Vec4(locationVerts[index], locationVerts[index + 1], locationVerts[index + 2]);
vec = vec.transformBy4(locationTransform);
Position pos = globe.computePositionFromPoint(vec);
vec = this.computePointFromPosition(dc, pos.getLatitude(), pos.getLongitude(), altitude, terrainConformant);
index = 3 * (vertexPos + i);
vertices[index] = (float) (vec.x - referenceCenter.x);
vertices[index + 1] = (float) (vec.y - referenceCenter.y);
vertices[index + 2] = (float) (vec.z - referenceCenter.z);
}
gb.makeIndexedTriangleArrayNormals(indexPos, indexCount, indices, vertexPos, vertexCount, vertices,
normals);
}
private void copyIndexArray(int indexCount, boolean reverseWinding, int[] indices,
int destVertexPos, int destIndexPos, int[] dest)
{
for (int i = 0; i < indexCount; i += 3)
{
if (reverseWinding)
{
dest[destIndexPos + i] = destVertexPos + indices[i + 2];
dest[destIndexPos + i + 1] = destVertexPos + indices[i + 1];
dest[destIndexPos + i + 2] = destVertexPos + indices[i];
}
else
{
dest[destIndexPos + i] = destVertexPos + indices[i];
dest[destIndexPos + i + 1] = destVertexPos + indices[i + 1];
dest[destIndexPos + i + 2] = destVertexPos + indices[i + 2];
}
}
}
//**************************************************************//
//******************** END Geometry Rendering ****************//
//**************************************************************//
@Override
protected void doGetRestorableState(RestorableSupport rs, RestorableSupport.StateObject context)
{
super.doGetRestorableState(rs, context);
rs.addStateValueAsBoolean(context, "enableCaps", this.enableCaps);
if (this.locations != null)
rs.addStateValueAsLatLonList(context, "locations", this.locations);
}
@Override
protected void doRestoreState(RestorableSupport rs, RestorableSupport.StateObject context)
{
super.doRestoreState(rs, context);
Boolean booleanState = rs.getStateValueAsBoolean(context, "enableCaps");
if (booleanState != null)
this.setEnableCaps(booleanState);
List locations = rs.getStateValueAsLatLonList(context, "locations");
if (locations != null)
this.setLocations(locations);
}
}