gov.nasa.worldwind.render.Wedge Maven / Gradle / Ivy
The newest version!
/*
* 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;
import com.jogamp.common.nio.Buffers;
import gov.nasa.worldwind.Exportable;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.render.airspaces.Geometry;
import gov.nasa.worldwind.terrain.Terrain;
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.*;
import javax.xml.stream.*;
import java.io.IOException;
import java.nio.*;
import java.util.List;
/**
* A general cylinder volume defined by a center position, height and radius, or alternatively, by three axis radii.
*
* @author ccrick
* @version $Id: Wedge.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class Wedge extends RigidShape
{
protected static final int DEFAULT_SUBDIVISIONS = 2;
protected Angle wedgeAngle = Angle.fromDegrees(220); // default value for angle consumed by the wedge
// Geometry.
@SuppressWarnings({"FieldCanBeLocal"})
protected int faceCount = 5; // number of separate Geometry pieces that comprise this Wedge
// The faces are numbered as follows:
// face 0: Wedge top
// face 1: Wedge bottom
// face 2: rounded Wedge wall
// face 3: left rectangular Wedge side
// face 4: right rectangular Wedge side
protected int subdivisions = DEFAULT_SUBDIVISIONS;
/** Construct a wedge with default parameters */
public Wedge()
{
this.setUpGeometryCache();
}
/**
* Constructs a Wedge from a specified center position, height, radius and angle.
*
* @param centerPosition the Wedge's center position.
* @param height the Wedge's height, in meters.
* @param radius the radius of the Wedge's base, in meters.
* @param angle the angle covered by the wedge
*
* @throws IllegalArgumentException if the center position is null, if the wedgeAngle is null or not in [0, 2*PI],
* or if any of the radii are not greater than 0.
*/
public Wedge(Position centerPosition, Angle angle, double height, double radius)
{
if (centerPosition == null)
{
String message = Logging.getMessage("nullValue.PositionIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (radius <= 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius <= 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (height <= 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "height <= 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle.getRadians() < 0 || angle.getRadians() > 2 * Math.PI)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "angle < 0 or angle > 2 PI");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.centerPosition = centerPosition;
this.wedgeAngle = angle;
this.northSouthRadius = radius;
this.verticalRadius = height / 2;
this.eastWestRadius = radius;
this.setUpGeometryCache();
}
/**
* Constructs a wedge from a specified center position and axes lengths.
*
* @param centerPosition the wedge's center position.
* @param angle the angle covered by the wedge
* @param northSouthRadius the wedge's north-south radius, in meters.
* @param verticalRadius the wedge's vertical radius, in meters.
* @param eastWestRadius the wedge's east-west radius, in meters.
*
* @throws IllegalArgumentException if the center position is null, if the wedgeAngle is null or not in [0, 2*PI],
* or if any of the radii are not greater than 0.
*/
public Wedge(Position centerPosition, Angle angle, double northSouthRadius, double verticalRadius,
double eastWestRadius)
{
if (centerPosition == null)
{
String message = Logging.getMessage("nullValue.PositionIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (northSouthRadius <= 0 || eastWestRadius <= 0 || verticalRadius <= 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius <= 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle.getRadians() < 0 || angle.getRadians() > 2 * Math.PI)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "angle < 0 or angle > 2 PI");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.centerPosition = centerPosition;
this.wedgeAngle = angle;
this.northSouthRadius = northSouthRadius;
this.verticalRadius = verticalRadius;
this.eastWestRadius = eastWestRadius;
this.setUpGeometryCache();
}
/**
* Constructs a wedge from a specified center position, axes lengths and rotation angles. All angles are specified
* in degrees and positive angles are counter-clockwise.
*
* @param centerPosition the wedge's center position.
* @param angle the angle covered by the wedge
* @param northSouthRadius the wedge's north-south radius, in meters.
* @param verticalRadius the wedge's vertical radius, in meters.
* @param eastWestRadius the wedge's east-west radius, in meters.
* @param heading the wedge's azimuth, its rotation about its vertical axis.
* @param tilt the wedge pitch, its rotation about its east-west axis.
* @param roll the wedge's roll, its rotation about its north-south axis.
*
* @throws IllegalArgumentException the centerPosition is null, or if the wedgeAngle is null or not in [0, 2*PI], or
* if any of the radii are not greater than 0.
*/
public Wedge(Position centerPosition, Angle angle, double northSouthRadius, double verticalRadius,
double eastWestRadius, Angle heading, Angle tilt, Angle roll)
{
if (centerPosition == null)
{
String message = Logging.getMessage("nullValue.PositionIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle.getRadians() < 0 || angle.getRadians() > 2 * Math.PI)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "angle < 0 or angle > 2 PI");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (northSouthRadius <= 0 || eastWestRadius <= 0 || verticalRadius <= 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius <= 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.centerPosition = centerPosition;
this.wedgeAngle = angle;
this.northSouthRadius = northSouthRadius;
this.verticalRadius = verticalRadius;
this.eastWestRadius = eastWestRadius;
this.heading = heading;
this.tilt = tilt;
this.roll = roll;
this.setUpGeometryCache();
}
@Override
protected void initialize()
{
// Nothing to override
}
/**
* Returns the angle covered by this wedge.
*
* @return the angle covered by the wedge.
*/
public Angle getWedgeAngle()
{
return wedgeAngle;
}
/**
* Specifies the angle covered by the wedge. This angle must fall in [0, 2*PI].
*
* @param angle the angle covered by the wedge. Must be in [0, 2*PI].
*
* @throws IllegalArgumentException if the wedgeAngle is null, or is not in [0, 2*PI].
*/
public void setWedgeAngle(Angle angle)
{
if (angle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle.getRadians() < 0 || angle.getRadians() > 2 * Math.PI)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "wedgeAngle < 0 or wedgeAngle > 2 PI");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.wedgeAngle = angle;
reset();
}
@Override
public int getFaceCount()
{
return this.faceCount;
}
public int getSubdivisions()
{
return this.subdivisions;
}
/**
* Computes a threshold value, based on the current detailHint, for use in the sufficientDetail() calculation.
*
* @return the detailThreshold
*/
protected double computeDetailThreshold()
{
// these values must be calibrated on a shape-by-shape basis
double detailThreshold = 8;
double rangeDetailThreshold = 25;
detailThreshold += this.getDetailHint() * rangeDetailThreshold;
return detailThreshold;
}
/**
* Computes the number of subdivisions necessary to achieve the expected Level of Detail given the shape's
* relationship to the viewer.
*
* @param dc the current drawContext.
* @param shapeData the current globe-specific shape data
*/
protected void computeSubdivisions(DrawContext dc, ShapeData shapeData)
{
// test again possible subdivision values
int minDivisions = 0;
int maxDivisions = 6;
if (shapeData.getExtent() != null)
{
for (int divisions = minDivisions; divisions <= maxDivisions; divisions++)
{
this.subdivisions = divisions;
if (this.sufficientDetail(dc, divisions, shapeData))
break;
}
}
}
protected boolean sufficientDetail(DrawContext dc, int subdivisions, ShapeData shapeData)
{
if (dc.getView() == null)
{
String message = "nullValue.DrawingContextViewIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (shapeData == null)
return false;
Extent extent = shapeData.getExtent();
if (extent == null)
return true;
double thresholdDensity = this.computeDetailThreshold();
double d = dc.getView().getEyePoint().distanceTo3(extent.getCenter());
double shapeScreenSize = extent.getDiameter() / d;
// formula for this object's current vertex density
double vertexDensity = Math.pow(subdivisions, 3) / shapeScreenSize;
return vertexDensity > thresholdDensity;
}
protected boolean mustRegenerateGeometry(DrawContext dc)
{
// check if current LOD is sufficient
int oldDivisions = this.subdivisions;
computeSubdivisions(dc, this.getCurrentShapeData());
if (oldDivisions != this.subdivisions)
return true;
return super.mustRegenerateGeometry(dc);
}
//**************************************************************//
//******************** Geometry Rendering ********************//
//**************************************************************//
/**
* Sets the Geometry mesh for this wedge, either by pulling it from the geometryCache, or by creating it anew if the
* appropriate geometry does not yet exist in the cache.
*
* @param shapeData this shape's current shape data.
*
* @throws IllegalArgumentException if the wedgeAngle is null
*/
protected void makeGeometry(ShapeData shapeData)
{
if (this.wedgeAngle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// attempt to retrieve a cached unit wedge with the same angle and number of subdivisions
Object cacheKey = new Geometry.CacheKey(this.getClass(), "Wedge0-" + this.wedgeAngle.toString(),
this.subdivisions);
Geometry geom = (Geometry) this.getGeometryCache().getObject(cacheKey);
if (geom == null)
{
// if none exists, create a new one
makeUnitWedge(this.subdivisions, shapeData.getMeshes());
for (int piece = 0; piece < getFaceCount(); piece++)
{
if (offsets.get(piece) == null) // if texture offsets don't exist, set default values to 0
offsets.put(piece, new OffsetsList());
// add the new mesh pieces to the cache
cacheKey = new Geometry.CacheKey(this.getClass(), "Wedge" + piece + "-" + this.wedgeAngle.toString(),
this.subdivisions);
this.getGeometryCache().add(cacheKey, shapeData.getMesh(piece));
}
}
else
{
// otherwise, just use the one from the cache
for (int piece = 0; piece < getFaceCount(); piece++)
{
if (offsets.get(piece) == null) // if texture offsets don't exist, set default values to 0
offsets.put(piece, new OffsetsList());
cacheKey = new Geometry.CacheKey(this.getClass(), "Wedge" + piece + "-" + this.wedgeAngle.toString(),
this.subdivisions);
geom = (Geometry) this.getGeometryCache().getObject(cacheKey);
shapeData.addMesh(piece, geom);
}
}
}
/**
* Generates a unit wedge geometry, including the vertices, indices, normals and texture coordinates, tessellated
* with the specified number of divisions.
*
* @param subdivisions the number of times to subdivide the unit wedge geometry
* @param dest the Geometry container to hold the computed points, etc.
*
* @throws IllegalArgumentException if the wedgeAngle is null
*/
/*
protected void makeUnitWedge(int subdivisions, Geometry dest)
{
if (this.wedgeAngle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float radius = 1.0f;
GeometryBuilder gb = this.getGeometryBuilder();
gb.setOrientation(GeometryBuilder.OUTSIDE);
// create wedge in model space
GeometryBuilder.IndexedTriangleBuffer itb =
gb.tessellateWedgeBuffer(radius, subdivisions, this.wedgeAngle);
FloatBuffer normalBuffer = Buffers.newDirectFloatBuffer(3 * itb.getVertexCount());
gb.makeIndexedTriangleBufferNormals(itb, normalBuffer);
FloatBuffer textureCoordBuffer = Buffers.newDirectFloatBuffer(2 * itb.getVertexCount());
gb.makeWedgeTextureCoordinates(textureCoordBuffer, subdivisions, this.wedgeAngle);
dest.setElementData(GL.GL_TRIANGLES, itb.getIndexCount(), itb.getIndices());
dest.setVertexData(itb.getVertexCount(), itb.getVertices());
dest.setNormalData(normalBuffer.limit(), normalBuffer);
dest.setTextureCoordData(textureCoordBuffer.limit(), textureCoordBuffer);
}
*/
/**
* Generates a unit wedge geometry, including the vertices, indices, normals and texture coordinates, tessellated
* with the specified number of divisions.
*
* @param subdivisions the number of times to subdivide the unit wedge geometry
* @param meshes the Geometry list to hold the computed points, etc. for all wedge Geometries
*
* @throws IllegalArgumentException if the wedgeAngle is null
*/
protected void makeUnitWedge(int subdivisions, List meshes)
{
if (this.wedgeAngle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float radius = 1.0f;
Geometry dest;
GeometryBuilder gb = this.getGeometryBuilder();
gb.setOrientation(GeometryBuilder.OUTSIDE);
for (int index = 0; index < getFaceCount(); index++)
{
// create wedge in model space
GeometryBuilder.IndexedTriangleBuffer itb =
gb.tessellateWedgeBuffer(index, radius, subdivisions, this.wedgeAngle);
FloatBuffer normalBuffer = Buffers.newDirectFloatBuffer(3 * itb.getVertexCount());
gb.makeIndexedTriangleBufferNormals(itb, normalBuffer);
FloatBuffer textureCoordBuffer = Buffers.newDirectFloatBuffer(2 * itb.getVertexCount());
gb.makeUnitWedgeTextureCoordinates(index, textureCoordBuffer, subdivisions, this.wedgeAngle);
dest = new Geometry();
dest.setElementData(GL.GL_TRIANGLES, itb.getIndexCount(), itb.getIndices());
dest.setVertexData(itb.getVertexCount(), itb.getVertices());
dest.setNormalData(normalBuffer.limit(), normalBuffer);
dest.setTextureCoordData(textureCoordBuffer.limit(), textureCoordBuffer);
meshes.add(index, dest);
}
}
/**
* Renders the wedge, using data from the provided buffer and the given parameters.
*
* @param dc the current draw context
* @param mode the render mode
* @param count the number of elements to be drawn
* @param type the data type of the elements to be drawn
* @param elementBuffer the buffer containing the list of elements to be drawn
* @param shapeData this shape's current globe-specific shape data
*/
protected void drawGeometry(DrawContext dc, int mode, int count, int type, Buffer elementBuffer,
ShapeData shapeData, int face)
{
if (elementBuffer == null)
{
String message = "nullValue.ElementBufferIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Geometry mesh = shapeData.getMesh(face);
if (mesh.getBuffer(Geometry.VERTEX) == null)
{
String message = "nullValue.VertexBufferIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
int size, glType, stride;
Buffer vertexBuffer, normalBuffer;
size = mesh.getSize(Geometry.VERTEX);
glType = mesh.getGLType(Geometry.VERTEX);
stride = mesh.getStride(Geometry.VERTEX);
vertexBuffer = mesh.getBuffer(Geometry.VERTEX);
normalBuffer = null;
if (!dc.isPickingMode())
{
if (mustApplyLighting(dc, null))
{
normalBuffer = mesh.getBuffer(Geometry.NORMAL);
if (normalBuffer == null)
{
gl.glDisableClientState(GL2.GL_NORMAL_ARRAY);
}
else
{
glType = mesh.getGLType(Geometry.NORMAL);
stride = mesh.getStride(Geometry.NORMAL);
gl.glNormalPointer(glType, stride, normalBuffer);
}
}
}
// cull the back face
//gl.glEnable(GL.GL_CULL_FACE);
//gl.glFrontFace(GL.GL_CCW);
// disable VBO's because they are not equipped to handle the arbitrary wedge angle
boolean vboState = dc.getGLRuntimeCapabilities().isVertexBufferObjectEnabled();
dc.getGLRuntimeCapabilities().setVertexBufferObjectEnabled(false);
// decide whether to draw with VBO's or VA's
if (this.shouldUseVBOs(dc) && (this.getVboIds(getSubdivisions(), dc)) != null)
{
// render using VBO's
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, getVboIds(getSubdivisions(), dc)[2 * face]);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, this.getVboIds(getSubdivisions(), dc)[2 * face + 1]);
gl.glVertexPointer(size, glType, stride, 0);
gl.glDrawElements(mode, count, type, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
}
else
{
// render using vertex arrays
gl.glVertexPointer(size, glType, stride, vertexBuffer.rewind());
gl.glDrawElements(mode, count, type, elementBuffer);
}
// turn off normals rescaling, which was turned on because shape had to be scaled
gl.glDisable(GL2.GL_RESCALE_NORMAL);
// disable back face culling
// gl.glDisable(GL.GL_CULL_FACE);
dc.getGLRuntimeCapabilities().setVertexBufferObjectEnabled(vboState);
if (!dc.isPickingMode())
{
if (mustApplyLighting(dc, null))
{
// re-enable normals if we temporarily turned them off earlier
if (normalBuffer == null)
gl.glEnableClientState(GL2.GL_NORMAL_ARRAY);
}
// this.logGeometryStatistics(dc, geom);
}
}
protected ShapeData createIntersectionGeometry(Terrain terrain)
{
ShapeData shapeData = new ShapeData(null, this);
shapeData.setGlobeStateKey(terrain.getGlobe().getGlobeStateKey());
Geometry mesh;
makeUnitWedge(6, shapeData.getMeshes()); // use maximum subdivisions for good intersection accuracy
// transform the vertices from local to world coords
Matrix matrix = computeRenderMatrix(terrain.getGlobe(), terrain.getVerticalExaggeration());
for (int i = 0; i < getFaceCount(); i++)
{
mesh = shapeData.getMesh(i);
// transform the vertices from local to world coords
FloatBuffer newVertices = computeTransformedVertices((FloatBuffer) mesh.getBuffer(Geometry.VERTEX),
mesh.getCount(Geometry.VERTEX), matrix);
mesh.setVertexData(mesh.getCount(Geometry.VERTEX), newVertices);
}
shapeData.setReferencePoint(this.computeReferencePoint(terrain.getGlobe(),
terrain.getVerticalExaggeration()));
shapeData.setExtent(getExtent(terrain.getGlobe(), terrain.getVerticalExaggeration()));
return shapeData;
}
/** No export formats supported. */
@Override
public String isExportFormatSupported(String mimeType)
{
// Overridden because this shape does not support export to KML.
return Exportable.FORMAT_NOT_SUPPORTED;
}
@Override
protected void doExportAsKML(XMLStreamWriter xmlWriter) throws IOException, XMLStreamException
{
String message = Logging.getMessage("generic.UnsupportedOperation", "doExportAsKML");
Logging.logger().severe(message);
throw new UnsupportedOperationException(message);
}
@Override
protected void doGetRestorableState(RestorableSupport rs, RestorableSupport.StateObject context)
{
super.doGetRestorableState(rs, context);
rs.addStateValueAsDouble(context, "wedgeAngle", this.getWedgeAngle().degrees);
}
@Override
protected void doRestoreState(RestorableSupport rs, RestorableSupport.StateObject context)
{
super.doRestoreState(rs, context);
Double doubleState = rs.getStateValueAsDouble(context, "wedgeAngle");
if (doubleState != null)
this.setWedgeAngle(Angle.fromDegrees(doubleState));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy