diff --git a/gluegen-rt-natives-linux-amd64.jar b/gluegen-rt-natives-linux-amd64.jar index 599866fee1..625b8479ed 100644 Binary files a/gluegen-rt-natives-linux-amd64.jar and b/gluegen-rt-natives-linux-amd64.jar differ diff --git a/gluegen-rt-natives-macosx-universal.jar b/gluegen-rt-natives-macosx-universal.jar index ca3fcae955..0e791ff2ce 100644 Binary files a/gluegen-rt-natives-macosx-universal.jar and b/gluegen-rt-natives-macosx-universal.jar differ diff --git a/gluegen-rt-natives-windows-amd64.jar b/gluegen-rt-natives-windows-amd64.jar index 0101c6ca94..2ae552be1e 100644 Binary files a/gluegen-rt-natives-windows-amd64.jar and b/gluegen-rt-natives-windows-amd64.jar differ diff --git a/gluegen-rt.jar b/gluegen-rt.jar index 49c0099c62..51cd1a8826 100644 Binary files a/gluegen-rt.jar and b/gluegen-rt.jar differ diff --git a/gluegen.jar b/gluegen.jar index 54baa8538d..3c1d257c7e 100644 Binary files a/gluegen.jar and b/gluegen.jar differ diff --git a/jogl-all-natives-linux-amd64.jar b/jogl-all-natives-linux-amd64.jar index e79f8eb078..fa74de8f96 100644 Binary files a/jogl-all-natives-linux-amd64.jar and b/jogl-all-natives-linux-amd64.jar differ diff --git a/jogl-all-natives-macosx-universal.jar b/jogl-all-natives-macosx-universal.jar index b111e2df73..23d78f5370 100644 Binary files a/jogl-all-natives-macosx-universal.jar and b/jogl-all-natives-macosx-universal.jar differ diff --git a/jogl-all-natives-windows-amd64.jar b/jogl-all-natives-windows-amd64.jar index e05aecfd08..7b8dcff736 100644 Binary files a/jogl-all-natives-windows-amd64.jar and b/jogl-all-natives-windows-amd64.jar differ diff --git a/jogl-all.jar b/jogl-all.jar index 8011e7f085..ab93476acc 100644 Binary files a/jogl-all.jar and b/jogl-all.jar differ diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml index 506f468c86..c02064ba01 100644 --- a/nbproject/build-impl.xml +++ b/nbproject/build-impl.xml @@ -119,43 +119,7 @@ is divided into following sections: - - - - - - - - - - - - - - - - - - - - - - - - - - Must set platform.home - Must set platform.bootcp - Must set platform.java - Must set platform.javac - - The J2SE Platform is not correctly set up. - Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. - Either open the project in the IDE and setup the Platform with the same name or add it manually. - For example like this: - ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) - or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) - + @@ -278,6 +242,20 @@ is divided into following sections: + + + + + + + + + + + + + + @@ -365,7 +343,7 @@ is divided into following sections: - + @@ -416,7 +394,7 @@ is divided into following sections: - + @@ -458,7 +436,7 @@ is divided into following sections: - + @@ -537,7 +515,7 @@ is divided into following sections: - + @@ -565,7 +543,7 @@ is divided into following sections: - + @@ -641,7 +619,7 @@ is divided into following sections: - + @@ -872,9 +850,6 @@ is divided into following sections: - - - @@ -924,7 +899,7 @@ is divided into following sections: - + @@ -958,7 +933,7 @@ is divided into following sections: - + @@ -990,7 +965,7 @@ is divided into following sections: - + @@ -1224,7 +1199,7 @@ is divided into following sections: To run this application from the command line without Ant, try: - ${platform.java} -jar "${dist.jar.resolved}" + java -jar "${dist.jar.resolved}" @@ -1326,8 +1301,8 @@ is divided into following sections: - - + + @@ -1520,19 +1495,16 @@ is divided into following sections: - - - - + - + - + diff --git a/nbproject/configs/AnnotationControls.properties b/nbproject/configs/AnnotationControls.properties new file mode 100644 index 0000000000..ef944d1651 --- /dev/null +++ b/nbproject/configs/AnnotationControls.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.AnnotationControls diff --git a/nbproject/configs/Cones.properties b/nbproject/configs/Cones.properties new file mode 100644 index 0000000000..9832191404 --- /dev/null +++ b/nbproject/configs/Cones.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.Cones diff --git a/nbproject/configs/EGM2008Offsets.properties b/nbproject/configs/EGM2008Offsets.properties new file mode 100644 index 0000000000..646d1236d5 --- /dev/null +++ b/nbproject/configs/EGM2008Offsets.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.EGM2008Offsets diff --git a/nbproject/configs/EGM96Offsets.properties b/nbproject/configs/EGM96Offsets.properties new file mode 100644 index 0000000000..55026b5809 --- /dev/null +++ b/nbproject/configs/EGM96Offsets.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.EGM96Offsets diff --git a/nbproject/configs/ExportImageOrElevations.properties b/nbproject/configs/ExportImageOrElevations.properties new file mode 100644 index 0000000000..301d17b18c --- /dev/null +++ b/nbproject/configs/ExportImageOrElevations.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.ExportImageOrElevations diff --git a/nbproject/configs/Placemarks.properties b/nbproject/configs/Placemarks.properties new file mode 100644 index 0000000000..b2f33a4ace --- /dev/null +++ b/nbproject/configs/Placemarks.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.Placemarks diff --git a/nbproject/configs/ScreenImageDragging.properties b/nbproject/configs/ScreenImageDragging.properties new file mode 100644 index 0000000000..a033a21e1d --- /dev/null +++ b/nbproject/configs/ScreenImageDragging.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.ScreenImageDragging diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index 6a15e776fa..f7555ac948 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=ed839dc9 +build.xml.data.CRC32=6106e3d1 build.xml.script.CRC32=b2ee8dee -build.xml.stylesheet.CRC32=f85dc8f2@1.102.0.48 +build.xml.stylesheet.CRC32=f85dc8f2@1.108.0.48 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=ed839dc9 -nbproject/build-impl.xml.script.CRC32=2e7bce4f -nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.102.0.48 +nbproject/build-impl.xml.data.CRC32=6106e3d1 +nbproject/build-impl.xml.script.CRC32=43da2f27 +nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.108.0.48 diff --git a/nbproject/project.properties b/nbproject/project.properties index 1adab5d73f..fab181db51 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -104,7 +104,7 @@ manifest.custom.permissions= manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF mkdist.disabled=false -platform.active=JDK_11 +platform.active=default_platform run.classpath=\ ${javac.classpath}:\ ${build.classes.dir} diff --git a/nbproject/project.xml b/nbproject/project.xml index cba6c32199..e27d90a9ba 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -4,7 +4,6 @@ WorldWindJava - diff --git a/src/gov/nasa/worldwind/awt/AWTInputHandler.java b/src/gov/nasa/worldwind/awt/AWTInputHandler.java index 564ed3fcb3..4915cd6d77 100644 --- a/src/gov/nasa/worldwind/awt/AWTInputHandler.java +++ b/src/gov/nasa/worldwind/awt/AWTInputHandler.java @@ -404,24 +404,26 @@ public void mouseClicked(final MouseEvent awtMouseEvent) if (pickedObjects != null && pickedObjects.getTopPickedObject() != null && !pickedObjects.getTopPickedObject().isTerrain()) { - // Something is under the cursor, so it's deemed "selected". + Point awtPt = awtMouseEvent.getPoint(); // AWT screen coordinates + + // Something is under the cursor, so it's deemed "selected". if (MouseEvent.BUTTON1 == mouseEvent.getButton()) { if (mouseEvent.getClickCount() <= 1) { this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.LEFT_CLICK, - mouseEvent, pickedObjects)); + awtPt, mouseEvent, pickedObjects)); } else { this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.LEFT_DOUBLE_CLICK, - mouseEvent, pickedObjects)); + awtPt, mouseEvent, pickedObjects)); } } else if (MouseEvent.BUTTON3 == mouseEvent.getButton()) { this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.RIGHT_CLICK, - mouseEvent, pickedObjects)); + awtPt, mouseEvent, pickedObjects)); } this.wwd.getView().firePropertyChange(AVKey.VIEW, null, this.wwd.getView()); @@ -472,16 +474,18 @@ public void mousePressed(MouseEvent awtMouseEvent) if (this.objectsAtButtonPress != null && objectsAtButtonPress.getTopPickedObject() != null && !this.objectsAtButtonPress.getTopPickedObject().isTerrain()) { + Point awtPt = awtMouseEvent.getPoint(); // AWT screen coordinates + // Something is under the cursor, so it's deemed "selected". if (MouseEvent.BUTTON1 == mouseEvent.getButton()) { this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.LEFT_PRESS, - mouseEvent, this.objectsAtButtonPress)); + awtPt, mouseEvent, this.objectsAtButtonPress)); } else if (MouseEvent.BUTTON3 == mouseEvent.getButton()) { this.callSelectListeners(new SelectEvent(this.wwd, SelectEvent.RIGHT_PRESS, - mouseEvent, this.objectsAtButtonPress)); + awtPt, mouseEvent, this.objectsAtButtonPress)); } // Initiate a repaint. @@ -600,8 +604,9 @@ public void mouseDragged(MouseEvent awtMouseEvent) && !pickedObjects.getTopPickedObject().isTerrain())) { this.isDragging = true; - DragSelectEvent selectEvent = new DragSelectEvent(this.wwd, SelectEvent.DRAG, mouseEvent, pickedObjects, - prevMousePoint); + DragSelectEvent selectEvent = new DragSelectEvent(this.wwd, SelectEvent.DRAG, + awtMouseEvent.getPoint(), mouseEvent, + pickedObjects, prevMousePoint); this.callSelectListeners(selectEvent); // If no listener consumed the event, then cancel the drag. @@ -790,7 +795,7 @@ protected void cancelDrag() if (this.isDragging) { this.callSelectListeners(new DragSelectEvent(this.wwd, SelectEvent.DRAG_END, null, - this.objectsAtButtonPress, this.mousePoint)); + null, this.objectsAtButtonPress, this.mousePoint)); } this.isDragging = false; diff --git a/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java b/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java index b0783cd1f4..012457796b 100644 --- a/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java +++ b/src/gov/nasa/worldwind/awt/AbstractViewInputHandler.java @@ -954,7 +954,7 @@ public double computeDragSlope(Point point1, Point point2, Vec4 vec1, Vec4 vec2) return slope - 1.0; } - protected static Point constrainToSourceBounds(Point point, Object source) + protected static Point constrainToSourceBounds(Point point, WorldWindow source) { if (point == null) return null; @@ -962,19 +962,24 @@ protected static Point constrainToSourceBounds(Point point, Object source) if (!(source instanceof Component)) return point; - Component c = (Component) source; + // source.getHeight(), source.getWidth() are AWT coords height, + // but the 'point' is MouseEvent GL surface coords. + // Clamp to GL viewport size. + int glWidth = source.getView().getViewport().width; + int glHeight = source.getView().getViewport().height; int x = (int) point.getX(); if (x < 0) x = 0; - if (x > c.getWidth()) - x = c.getWidth(); + if (x >= glWidth) + x = glWidth - 1; int y = (int) point.getY(); if (y < 0) y = 0; - if (y > c.getHeight()) - y = c.getHeight(); + + if (y >= glHeight) + y = glHeight - 1; return new Point(x, y); } diff --git a/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java b/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java index e7af6f0ca6..2008517ca6 100644 --- a/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java +++ b/src/gov/nasa/worldwind/awt/BasicViewInputHandler.java @@ -219,10 +219,10 @@ public boolean inputActionPerformed(KeyEventState keys, String target, Point movement = ViewUtil.subtract(point, lastPoint); int headingInput = movement.x; - int pitchInput = movement.y; + int pitchInput = -movement.y; Point totalMovement = ViewUtil.subtract(point, mouseDownPoint); int totalHeadingInput = totalMovement.x; - int totalPitchInput = totalMovement.y; + int totalPitchInput = -totalMovement.y; ViewInputAttributes.DeviceAttributes deviceAttributes = getAttributes().getDeviceAttributes(ViewInputAttributes.DEVICE_MOUSE); @@ -258,16 +258,16 @@ public boolean inputActionPerformed(AbstractViewInputHandler inputHandler, return false; } + // 'mouseEvent' is in GL surface coords, (0,0) in lower left of canvas + // Make down mouse movement increase the pitch. Point movement = ViewUtil.subtract(point, lastPoint); int headingInput = movement.x; - int pitchInput = movement.y; + int pitchInput = -movement.y; if (mouseDownPoint == null) mouseDownPoint = lastPoint; Point totalMovement = ViewUtil.subtract(point, mouseDownPoint); int totalHeadingInput = totalMovement.x; - int totalPitchInput = totalMovement.y; - - + int totalPitchInput = -totalMovement.y; ViewInputAttributes.DeviceAttributes deviceAttributes = getAttributes().getDeviceAttributes(ViewInputAttributes.DEVICE_MOUSE); diff --git a/src/gov/nasa/worldwind/drag/DragContext.java b/src/gov/nasa/worldwind/drag/DragContext.java index 6623902e88..935f2c31b4 100644 --- a/src/gov/nasa/worldwind/drag/DragContext.java +++ b/src/gov/nasa/worldwind/drag/DragContext.java @@ -41,16 +41,16 @@ public class DragContext { /** - * In accordance with the AWT screen coordinates the top left point of the window is the origin. + * In accordance with the GL surface coordinates the top left point of the window is the origin. */ protected Point point; /** - * In accordance with the AWT screen coordinates the top left point of the window is the origin. This point is the + * In accordance with the GL surface coordinates the top left point of the window is the origin. This point is the * previous screen point. */ protected Point previousPoint; /** - * In accordance with the AWT screen coordinates the top left point of the window is the origin. This point refers + * In accordance with the GL surface coordinates the top left point of the window is the origin. This point refers * to the initial point of the drag event. */ protected Point initialPoint; @@ -81,9 +81,9 @@ public DragContext() } /** - * Returns the current screen point with the origin at the top left corner of the window. + * Returns the current GL surface point with the origin at the top left corner of the window. * - * @return the current screen point. + * @return the current GL surface point. */ public Point getPoint() { @@ -91,9 +91,9 @@ public Point getPoint() } /** - * Set the {@link DragContext} current screen point. + * Set the {@link DragContext} current GL surface point. * - * @param point the point to assign to the current screen point. + * @param point the point to assign to the current GL surface point. * * @throws IllegalArgumentException if the point is null. */ @@ -110,7 +110,7 @@ public void setPoint(Point point) } /** - * Returns the previous screen point with the origin at the top left corner of the window. + * Returns the previous GL surface point with the origin at the top left corner of the window. * * @return the previous point. */ @@ -120,9 +120,9 @@ public Point getPreviousPoint() } /** - * Set the {@link DragContext} previous screen point. + * Set the {@link DragContext} previous GL surface point. * - * @param previousPoint the screen point to assign to the previous screen point. + * @param previousPoint the GL surface point to assign to the previous screen point. * * @throws IllegalArgumentException if the previousPoint is null. */ @@ -139,10 +139,10 @@ public void setPreviousPoint(Point previousPoint) } /** - * Returns the initial screen point with the origin at the top left corner of the window. The initial point is the - * screen point at the initiation of the drag event. + * Returns the initial GL surface point with the origin at the top left corner of the window. The initial point is the + * GL surface point at the initiation of the drag event. * - * @return the initial screen point. + * @return the initial GL surface point. */ public Point getInitialPoint() { @@ -150,9 +150,9 @@ public Point getInitialPoint() } /** - * Set the {@link DragContext} initial screen point. + * Set the {@link DragContext} initial GL surface point. * - * @param initialPoint the screen point to assign to the initial screen point. + * @param initialPoint the GL surface point to assign to the initial screen point. * * @throws IllegalArgumentException if the initialPoint is null. */ diff --git a/src/gov/nasa/worldwind/drag/DraggableSupport.java b/src/gov/nasa/worldwind/drag/DraggableSupport.java index 3a5e7b52a5..7af85f8eb6 100644 --- a/src/gov/nasa/worldwind/drag/DraggableSupport.java +++ b/src/gov/nasa/worldwind/drag/DraggableSupport.java @@ -428,9 +428,7 @@ protected Vec4 computeScreenOffsetFromReferencePosition(Position dragObjectRefer Vec4 screenPointOffset = new Vec4( dragContext.getInitialPoint().getX() - dragObjectScreenPoint.getX(), - dragContext.getInitialPoint().getY() - ( - dragContext.getView().getViewport().getHeight() - - dragObjectScreenPoint.getY() - 1.0) + dragContext.getInitialPoint().getY() - dragObjectScreenPoint.getY() ); return screenPointOffset; diff --git a/src/gov/nasa/worldwind/event/DragSelectEvent.java b/src/gov/nasa/worldwind/event/DragSelectEvent.java index fcd36fe33a..c694b58f1b 100644 --- a/src/gov/nasa/worldwind/event/DragSelectEvent.java +++ b/src/gov/nasa/worldwind/event/DragSelectEvent.java @@ -42,10 +42,11 @@ public class DragSelectEvent extends SelectEvent { private final java.awt.Point previousPickPoint; - public DragSelectEvent(Object source, String eventAction, MouseEvent mouseEvent, PickedObjectList pickedObjects, - java.awt.Point previousPickPoint) + public DragSelectEvent(Object source, String eventAction, java.awt.Point awtPt, MouseEvent mouseEvent, + PickedObjectList pickedObjects, + java.awt.Point previousPickPoint) { - super(source, eventAction, mouseEvent, pickedObjects); + super(source, eventAction, awtPt, mouseEvent, pickedObjects); this.previousPickPoint = previousPickPoint; } diff --git a/src/gov/nasa/worldwind/event/SelectEvent.java b/src/gov/nasa/worldwind/event/SelectEvent.java index 0100deae12..1804356111 100644 --- a/src/gov/nasa/worldwind/event/SelectEvent.java +++ b/src/gov/nasa/worldwind/event/SelectEvent.java @@ -104,15 +104,17 @@ public class SelectEvent extends WWEvent private final Point pickPoint; // GL surface coordinates private final Rectangle pickRect; // GL surface coordinates private final MouseEvent mouseEvent; // GL surface coordinates + private final Point awtMousePt; // AWT screen coordinates private final PickedObjectList pickedObjects; - public SelectEvent(Object source, String eventAction, MouseEvent mouseEvent, PickedObjectList pickedObjects) + public SelectEvent(Object source, String eventAction, Point awtPt, MouseEvent mouseEvent, PickedObjectList pickedObjects) { super(source); this.eventAction = eventAction; this.pickPoint = mouseEvent != null ? mouseEvent.getPoint() : null; this.pickRect = null; this.mouseEvent = mouseEvent; + this.awtMousePt = awtPt; this.pickedObjects = pickedObjects; } @@ -123,6 +125,7 @@ public SelectEvent(Object source, String eventAction, Point pickPoint, PickedObj this.pickPoint = pickPoint; this.pickRect = null; this.mouseEvent = null; + this.awtMousePt = null; this.pickedObjects = pickedObjects; } @@ -133,6 +136,7 @@ public SelectEvent(Object source, String eventAction, Rectangle pickRectangle, P this.pickPoint = null; this.pickRect = pickRectangle; this.mouseEvent = null; + this.awtMousePt = null; this.pickedObjects = pickedObjects; } @@ -150,6 +154,10 @@ public String getEventAction() return this.eventAction != null ? this.eventAction : "gov.nasa.worldwind.SelectEvent.UnknownEventAction"; } + public Point getAwtMousePt() { + return awtMousePt; + } + public Point getPickPoint() { return this.pickPoint; diff --git a/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java b/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java index 479cf7fbe6..d5ccf17d09 100644 --- a/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java +++ b/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java @@ -2,25 +2,25 @@ * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. - * + * * The NASA World Wind Java (WWJ) platform is licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. - * + * * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source * software: - * + * * Jackson Parser – Licensed under Apache 2.0 * GDAL – Licensed under MIT * JOGL – Licensed under Berkeley Software Distribution (BSD) * Gluegen – Licensed under Berkeley Software Distribution (BSD) - * + * * A complete listing of 3rd Party software notices and licenses included in * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party * notices and licenses PDF found in code directory. @@ -132,7 +132,10 @@ public Globe getGlobe() return this.globe; } - @SuppressWarnings({"RedundantIfStatement"}) + @SuppressWarnings( + { + "RedundantIfStatement" + }) @Override public boolean equals(Object o) { @@ -145,8 +148,8 @@ public boolean equals(Object o) if (Double.compare(stateKey.verticalExaggeration, verticalExaggeration) != 0) return false; - if (elevationModel != null ? !elevationModel.equals(stateKey.elevationModel) : - stateKey.elevationModel != null) + if (elevationModel != null ? !elevationModel.equals(stateKey.elevationModel) + : stateKey.elevationModel != null) return false; if (globe != null ? !globe.equals(stateKey.globe) : stateKey.globe != null) return false; @@ -238,7 +241,6 @@ public double getRadiusAt(Angle latitude, Angle longitude) // observing that the length of the ellipsoidal point at the specified latitude and longitude indicates the // radius at that location. The formula for the length of the ellipsoidal point was then converted into the // simplified form below. - double sinLat = Math.sin(latitude.radians); double rpm = this.equatorialRadius / Math.sqrt(1.0 - this.es * sinLat * sinLat); @@ -294,7 +296,10 @@ public double[] getMinAndMaxElevations(Angle latitude, Angle longitude) } return this.elevationModel != null ? this.elevationModel.getExtremeElevations(latitude, longitude) - : new double[] {0, 0}; + : new double[] + { + 0, 0 + }; } public double[] getMinAndMaxElevations(Sector sector) @@ -306,7 +311,10 @@ public double[] getMinAndMaxElevations(Sector sector) throw new IllegalArgumentException(message); } - return this.elevationModel != null ? this.elevationModel.getExtremeElevations(sector) : new double[] {0, 0}; + return this.elevationModel != null ? this.elevationModel.getExtremeElevations(sector) : new double[] + { + 0, 0 + }; } public Extent getExtent() @@ -347,7 +355,6 @@ protected Intersection[] intersect(Line line, double equRadius, double polRadius return null; // Taken from Lengyel, 2Ed., Section 5.2.3, page 148. - double m = equRadius / polRadius; // "ratio of the x semi-axis length to the y semi-axis length" double n = 1d; // "ratio of the x semi-axis length to the z semi-axis length" double m2 = m * m; @@ -373,16 +380,25 @@ protected Intersection[] intersect(Line line, double equRadius, double polRadius if (discriminant == 0) { Vec4 p = line.getPointAt((-b - discriminantRoot) / (2 * a)); - return new Intersection[] {new Intersection(p, true)}; + return new Intersection[] + { + new Intersection(p, true) + }; } else // (discriminant > 0) { Vec4 near = line.getPointAt((-b - discriminantRoot) / (2 * a)); Vec4 far = line.getPointAt((-b + discriminantRoot) / (2 * a)); if (c >= 0) // Line originates outside the Globe. - return new Intersection[] {new Intersection(near, false), new Intersection(far, false)}; + return new Intersection[] + { + new Intersection(near, false), new Intersection(far, false) + }; else // Line originates inside the Globe. - return new Intersection[] {new Intersection(far, false)}; + return new Intersection[] + { + new Intersection(far, false) + }; } } @@ -492,7 +508,10 @@ public double[] getElevations(Sector sector, List latLons, dou double[] elevations) { if (this.elevationModel == null) - return new double[] {0}; + return new double[] + { + 0 + }; double[] resolution = this.elevationModel.getElevations(sector, latLons, targetResolution, elevations); @@ -508,6 +527,16 @@ public double[] getElevations(Sector sector, List latLons, dou return resolution; } + /** + * Get the EGM96 offset class for this globe. + * + * @return The EGM96 offset class for this globe. + */ + public EGM96 getEGM96() + { + return this.egm96; + } + public double getElevation(Angle latitude, Angle longitude) { if (latitude == null || longitude == null) @@ -909,7 +938,8 @@ protected Vec4 geodeticToEllipsoidal(Angle latitude, Angle longitude, double met double cosLon = Math.cos(longitude.radians); double sinLon = Math.sin(longitude.radians); - double rpm = // getRadius (in meters) of vertical in prime meridian + double rpm + = // getRadius (in meters) of vertical in prime meridian this.equatorialRadius / Math.sqrt(1.0 - this.es * sinLat * sinLat); double x = (rpm + metersElevation) * cosLat * sinLon; @@ -1035,7 +1065,6 @@ protected void geodeticToCartesian(Sector sector, int numLat, int numLon, double // // return Position.fromRadians(lat, lon, elevation); // } - /** * Compute the geographic position to corresponds to a Cartesian point. * @@ -1059,7 +1088,10 @@ protected Position cartesianToGeodetic(Vec4 cart) * * @see #geodeticToEllipsoidal(gov.nasa.worldwind.geom.Angle, gov.nasa.worldwind.geom.Angle, double) */ - @SuppressWarnings({"SuspiciousNameCombination"}) + @SuppressWarnings( + { + "SuspiciousNameCombination" + }) protected Position ellipsoidalToGeodetic(Vec4 cart) { // Contributed by Nathan Kronenfeld. Integrated 1/24/2011. Brings this calculation in line with Vermeille's @@ -1370,4 +1402,4 @@ public static ElevationModel makeElevationModel(String key, String defaultValue) Object configSource = Configuration.getStringValue(key, defaultValue); return (ElevationModel) BasicFactory.create(AVKey.ELEVATION_MODEL_FACTORY, configSource); } -} \ No newline at end of file +} diff --git a/src/gov/nasa/worldwind/layers/ScalebarLayer.java b/src/gov/nasa/worldwind/layers/ScalebarLayer.java index 03f495b886..6cfd8af978 100644 --- a/src/gov/nasa/worldwind/layers/ScalebarLayer.java +++ b/src/gov/nasa/worldwind/layers/ScalebarLayer.java @@ -34,6 +34,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; diff --git a/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java b/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java index 8f1303ff6e..149ab8c2eb 100644 --- a/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java +++ b/src/gov/nasa/worldwind/layers/TerrainProfileLayer.java @@ -37,6 +37,8 @@ import gov.nasa.worldwind.view.orbit.OrbitView; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; import java.beans.PropertyChangeEvent; diff --git a/src/gov/nasa/worldwind/layers/TiledImageLayer.java b/src/gov/nasa/worldwind/layers/TiledImageLayer.java index 67fba48604..1a19ce23e7 100644 --- a/src/gov/nasa/worldwind/layers/TiledImageLayer.java +++ b/src/gov/nasa/worldwind/layers/TiledImageLayer.java @@ -39,6 +39,8 @@ import javax.imageio.ImageIO; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import javax.xml.xpath.XPath; import java.awt.image.*; import java.io.*; diff --git a/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java b/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java index 9ba7c0ff76..9d6f527329 100644 --- a/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java +++ b/src/gov/nasa/worldwind/layers/mercator/MercatorTiledImageLayer.java @@ -38,6 +38,8 @@ import javax.imageio.ImageIO; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; import java.awt.image.*; diff --git a/src/gov/nasa/worldwind/render/AbstractAnnotation.java b/src/gov/nasa/worldwind/render/AbstractAnnotation.java index 82cbad8bff..72d1571bfc 100644 --- a/src/gov/nasa/worldwind/render/AbstractAnnotation.java +++ b/src/gov/nasa/worldwind/render/AbstractAnnotation.java @@ -34,6 +34,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; /** diff --git a/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java b/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java index 81faf5ce86..394e81e879 100644 --- a/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java +++ b/src/gov/nasa/worldwind/render/DeclutteringTextRenderer.java @@ -36,6 +36,8 @@ import com.jogamp.opengl.*; import com.jogamp.opengl.glu.GLU; import com.jogamp.opengl.glu.gl2.GLUgl2; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; import java.io.IOException; diff --git a/src/gov/nasa/worldwind/render/DrawContext.java b/src/gov/nasa/worldwind/render/DrawContext.java index ffc935a719..c6e8567a5e 100644 --- a/src/gov/nasa/worldwind/render/DrawContext.java +++ b/src/gov/nasa/worldwind/render/DrawContext.java @@ -1051,4 +1051,11 @@ public interface DrawContext extends WWObject, Disposable * Convert AWT effective screen location to GL surface location using DPI scaling. */ int [] awtPointToGLpoint(Point pt); + + /** + * Convert GL surface coordinate point to AWT device point using DPI scaling. + * @param glPoint + * @return + */ + public Point glPointToAwtPoint(Point glPoint); } diff --git a/src/gov/nasa/worldwind/render/DrawContextImpl.java b/src/gov/nasa/worldwind/render/DrawContextImpl.java index eaed5cc1c7..c24100b162 100644 --- a/src/gov/nasa/worldwind/render/DrawContextImpl.java +++ b/src/gov/nasa/worldwind/render/DrawContextImpl.java @@ -1752,7 +1752,17 @@ else if (altitudeMode == WorldWind.RELATIVE_TO_GROUND) // Convert to GL surface coordinates int [] glSurfacePt = drawable.getNativeSurface().convertToPixelUnits(awtPt); int glSurfaceHeight = drawable.getSurfaceHeight(); - glSurfacePt[1] = glSurfaceHeight - glSurfacePt[1] - 1; + glSurfacePt[1] = glSurfaceHeight-1 - glSurfacePt[1]; return glSurfacePt; } + + public Point glPointToAwtPoint(Point glPoint) { + GLDrawable drawable = glContext.getGLDrawable(); + if (drawable == null) return glPoint; + + final int viewportHeight = getView().getViewport().height; + int [] glPt = { glPoint.x, viewportHeight-1 - glPoint.y }; + getGLDrawable().getNativeSurface().convertToWindowUnits(glPt); + return new Point(glPt[0], glPt[1]); + } } diff --git a/src/gov/nasa/worldwind/render/GeographicTextRenderer.java b/src/gov/nasa/worldwind/render/GeographicTextRenderer.java index a362b387dc..d3aa21fa5d 100644 --- a/src/gov/nasa/worldwind/render/GeographicTextRenderer.java +++ b/src/gov/nasa/worldwind/render/GeographicTextRenderer.java @@ -38,6 +38,8 @@ import com.jogamp.opengl.*; import com.jogamp.opengl.glu.GLU; import com.jogamp.opengl.glu.gl2.GLUgl2; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; import java.io.IOException; diff --git a/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java b/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java index d8677398b8..201b35eb64 100644 --- a/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java +++ b/src/gov/nasa/worldwind/render/MultiLineTextRenderer.java @@ -33,6 +33,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; import java.util.*; diff --git a/src/gov/nasa/worldwind/render/PointPlacemark.java b/src/gov/nasa/worldwind/render/PointPlacemark.java index 93c6e2d5c8..9ac04e0b57 100644 --- a/src/gov/nasa/worldwind/render/PointPlacemark.java +++ b/src/gov/nasa/worldwind/render/PointPlacemark.java @@ -40,6 +40,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import javax.xml.stream.*; import java.awt.*; import java.awt.geom.*; diff --git a/src/gov/nasa/worldwind/render/ScreenImage.java b/src/gov/nasa/worldwind/render/ScreenImage.java index 69c33b5cb7..488d283002 100644 --- a/src/gov/nasa/worldwind/render/ScreenImage.java +++ b/src/gov/nasa/worldwind/render/ScreenImage.java @@ -78,13 +78,6 @@ public class ScreenImage extends WWObjectImpl implements Renderable, Exportable * is computed in computeOffsets and used in draw Initially null. */ protected Point screenLocation; - /** - * Indicates the location of this screen image in the viewport (on the screen) in AWT coordinates. This property is - * assigned in setScreenLocation and computeOffsets. In computeOffsets, this - * is computed by converting the screenLocation from OpenGL coordinates to AWT coordinates. Initially - * null. - */ - protected Point awtScreenLocation; protected double dx; protected double dy; protected Layer pickLayer; @@ -122,7 +115,7 @@ public void render(DrawContext dc) */ public Point getScreenLocation() { - return this.awtScreenLocation; + return this.screenLocation; } /** @@ -136,7 +129,7 @@ public Point getScreenLocation() public Point getScreenLocation(DrawContext dc) { this.computeOffsets(dc); - return this.awtScreenLocation; + return this.screenLocation; } /** @@ -151,17 +144,16 @@ public Point getScreenLocation(DrawContext dc) */ public void setScreenLocation(Point screenLocation) { - // Use units PIXELS for the X screen offset, and and INSET_PIXELS for the Y screen offset. The Offset is in - // OpenGL coordinates with the origin in the lower-left corner, but the Point is in AWT coordinates with the - // origin in the upper-left corner. This offset translates the origin from the lower-left to the upper-left - // corner. - this.screenOffset = new Offset(screenLocation.getX(), screenLocation.getY(), AVKey.PIXELS, AVKey.INSET_PIXELS); + // Use units PIXELS for the X screen offset, and and PIXELS for the Y screen offset. The Offset is in + // OpenGL coordinates with the origin in the lower-left corner, as is the Point. This offset + // translates the origin from the lower-left to the upper-left corner. + this.screenOffset = new Offset(screenLocation.getX(), screenLocation.getY(), AVKey.PIXELS, AVKey.PIXELS); this.imageOffset = new Offset(0.5, 0.5, AVKey.FRACTION, AVKey.FRACTION); // Set cached screen location to the initial screen location so that it can be retrieved if getScreenLocation() // is called before the image is rendered. This maintains backward compatibility with the previous behavior of // ScreenImage. - this.awtScreenLocation = new Point(screenLocation); + this.screenLocation = new Point(screenLocation); } /** @@ -544,12 +536,7 @@ else if (this.getImageSource() == null) // If no image source is set, draw a rec { this.screenLocation = new Point(viewportWidth / 2, viewportHeight / 2); } - - // Convert the screen location from OpenGL to AWT coordinates and store the result in awtScreenLocation. The - // awtScreenLocation property is used in getScreenLocation to indicate the screen location in AWT - // coordinates. - this.awtScreenLocation = new Point(this.screenLocation.x, viewportHeight - this.screenLocation.y); - + Point.Double overlayPoint; if (this.imageOffset != null) overlayPoint = this.imageOffset.computeOffset(this.width, this.height, null, null); diff --git a/src/gov/nasa/worldwind/render/SurfaceText.java b/src/gov/nasa/worldwind/render/SurfaceText.java index bdb5490b3a..74386e9aae 100644 --- a/src/gov/nasa/worldwind/render/SurfaceText.java +++ b/src/gov/nasa/worldwind/render/SurfaceText.java @@ -36,6 +36,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.GL2; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; import java.util.Arrays; diff --git a/src/gov/nasa/worldwind/render/TextRenderer.java b/src/gov/nasa/worldwind/render/TextRenderer.java deleted file mode 100644 index d25c14f4fc..0000000000 --- a/src/gov/nasa/worldwind/render/TextRenderer.java +++ /dev/null @@ -1,2108 +0,0 @@ -/* - * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the - * Administrator of the National Aeronautics and Space Administration. - * All rights reserved. - * - * The NASA World Wind Java (WWJ) platform is licensed under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source - * software: - * - * Jackson Parser – Licensed under Apache 2.0 - * GDAL – Licensed under MIT - * JOGL – Licensed under Berkeley Software Distribution (BSD) - * Gluegen – Licensed under Berkeley Software Distribution (BSD) - * - * A complete listing of 3rd Party software notices and licenses included in - * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party - * notices and licenses PDF found in code directory. - */ -/* - * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ -/** - * @version $Id: TextRenderer.java 2387 2014-10-15 20:25:02Z tgaskins $ - */ - -package gov.nasa.worldwind.render; - -import com.jogamp.common.nio.Buffers; -import com.jogamp.nativewindow.NativeSurface; -import com.jogamp.nativewindow.ScalableSurface; -import com.jogamp.opengl.GLExtensions; -import com.jogamp.opengl.util.FPSAnimator; -import com.jogamp.opengl.util.awt.TextureRenderer; -import com.jogamp.opengl.util.packrect.*; -import com.jogamp.opengl.util.texture.TextureCoords; -import jogamp.opengl.Debug; - -import com.jogamp.opengl.*; -import com.jogamp.opengl.awt.GLCanvas; -import com.jogamp.opengl.glu.GLU; -import java.awt.*; -import java.awt.event.*; -import java.awt.font.*; -import java.awt.geom.*; -import java.nio.*; -import java.text.CharacterIterator; -import java.util.*; -import java.util.List; - -// For debugging purposes - - -/** Renders bitmapped Java 2D text into an OpenGL window with high - performance, full Unicode support, and a simple API. Performs - appropriate caching of text rendering results in an OpenGL texture - internally to avoid repeated font rasterization. The caching is - completely automatic, does not require any user intervention, and - has no visible controls in the public API.

- - Using the {@link TextRenderer TextRenderer} is simple. Add a - "TextRenderer renderer;" field to your {@link - com.jogamp.opengl.GLEventListener GLEventListener}. In your {@link - com.jogamp.opengl.GLEventListener#init init} method, add: - -

-    renderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 36));
-    
- -

In the {@link com.jogamp.opengl.GLEventListener#display display} method of your - {@link com.jogamp.opengl.GLEventListener GLEventListener}, add: -

-    renderer.beginRendering(drawable.getWidth(), drawable.getHeight());
-    // optionally set the color
-    renderer.setColor(1.0f, 0.2f, 0.2f, 0.8f);
-    renderer.draw("Text to draw", xPosition, yPosition);
-    // ... more draw commands, color changes, etc.
-    renderer.endRendering();
-    
- - Unless you are sharing textures and display lists between OpenGL - contexts, you do not need to call the {@link #dispose dispose} - method of the TextRenderer; the OpenGL resources it uses - internally will be cleaned up automatically when the OpenGL - context is destroyed.

- - Note that the TextRenderer may cause the vertex and texture - coordinate array buffer bindings to change, or to be unbound. This - is important to note if you are using Vertex Buffer Objects (VBOs) - in your application.

- - Internally, the renderer uses a rectangle packing algorithm to - pack both glyphs and full Strings' rendering results (which are - variable size) onto a larger OpenGL texture. The internal backing - store is maintained using a {@link - com.jogamp.opengl.util.awt.TextureRenderer TextureRenderer}. A least - recently used (LRU) algorithm is used to discard previously - rendered strings; the specific algorithm is undefined, but is - currently implemented by flushing unused Strings' rendering - results every few hundred rendering cycles, where a rendering - cycle is defined as a pair of calls to {@link #beginRendering - beginRendering} / {@link #endRendering endRendering}. - - @author John Burkey - @author Kenneth Russell -*/ -public class TextRenderer { - private static final boolean DEBUG; - - static { - Debug.initSingleton(); - DEBUG = Debug.isPropertyDefined("jogl.debug.TextRenderer", true); - } - - // These are occasionally useful for more in-depth debugging - private static boolean DISABLE_GLYPH_CACHE = false; - private static final boolean DRAW_BBOXES = false; - static - { - String arg = System.getProperty("gov.nasa.worldwind.textrender.useglyphcache"); - if (arg != null && arg.toLowerCase().startsWith("f")) - DISABLE_GLYPH_CACHE = true; - } - - static final int kSize = 256; - - // Every certain number of render cycles, flush the strings which - // haven't been used recently - private static final int CYCLES_PER_FLUSH = 100; - - // The amount of vertical dead space on the backing store before we - // force a compaction - private static final float MAX_VERTICAL_FRAGMENTATION = 0.7f; - static final int kQuadsPerBuffer = 100; - static final int kCoordsPerVertVerts = 3; - static final int kCoordsPerVertTex = 2; - static final int kVertsPerQuad = 4; - static final int kTotalBufferSizeVerts = kQuadsPerBuffer * kVertsPerQuad; - static final int kTotalBufferSizeCoordsVerts = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertVerts; - static final int kTotalBufferSizeCoordsTex = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertTex; - static final int kTotalBufferSizeBytesVerts = kTotalBufferSizeCoordsVerts * 4; - static final int kTotalBufferSizeBytesTex = kTotalBufferSizeCoordsTex * 4; - static final int kSizeInBytes_OneVertices_VertexData = kCoordsPerVertVerts * 4; - static final int kSizeInBytes_OneVertices_TexData = kCoordsPerVertTex * 4; - private final Font font; - private final boolean antialiased; - private final boolean useFractionalMetrics; - - // Whether we're attempting to use automatic mipmap generation support - private boolean mipmap; - private RectanglePacker packer; - private boolean haveMaxSize; - private final RenderDelegate renderDelegate; - private TextureRenderer cachedBackingStore; - private Graphics2D cachedGraphics; - private FontRenderContext cachedFontRenderContext; - private final Map stringLocations = new HashMap(); - private final GlyphProducer mGlyphProducer; - - private int numRenderCycles; - - // Need to keep track of whether we're in a beginRendering() / - // endRendering() cycle so we can re-enter the exact same state if - // we have to reallocate the backing store - private boolean inBeginEndPair; - private boolean isOrthoMode; - private int beginRenderingWidth; - private int beginRenderingHeight; - private boolean beginRenderingDepthTestDisabled; - - // For resetting the color after disposal of the old backing store - private boolean haveCachedColor; - private float cachedR; - private float cachedG; - private float cachedB; - private float cachedA; - private Color cachedColor; - private boolean needToResetColor; - - // For debugging only - private Frame dbgFrame; - - // Debugging purposes only - private boolean debugged; - Pipelined_QuadRenderer mPipelinedQuadRenderer; - - //emzic: added boolean flag - private boolean useVertexArrays = true; - - //emzic: added boolean flag - private boolean isExtensionAvailable_GL_VERSION_1_5; - private boolean checkFor_isExtensionAvailable_GL_VERSION_1_5; - - // Whether GL_LINEAR filtering is enabled for the backing store - private boolean smoothing = true; - - /** Creates a new TextRenderer with the given font, using no - antialiasing or fractional metrics, and the default - RenderDelegate. Equivalent to TextRenderer(font, false, - false). - - @param font the font to render with - */ - public TextRenderer(Font font) { - this(font, false, false, null, false); - } - - /** Creates a new TextRenderer with the given font, using no - antialiasing or fractional metrics, and the default - RenderDelegate. If mipmap is true, attempts to use - OpenGL's automatic mipmap generation for better smoothing when - rendering the TextureRenderer's contents at a distance. - Equivalent to TextRenderer(font, false, false). - - @param font the font to render with - @param mipmap whether to attempt use of automatic mipmap generation - */ - public TextRenderer(Font font, boolean mipmap) { - this(font, false, false, null, mipmap); - } - - /** Creates a new TextRenderer with the given Font, specified font - properties, and default RenderDelegate. The - antialiased and useFractionalMetrics - flags provide control over the same properties at the Java 2D - level. No mipmap support is requested. Equivalent to - TextRenderer(font, antialiased, useFractionalMetrics, - null). - - @param font the font to render with - @param antialiased whether to use antialiased fonts - @param useFractionalMetrics whether to use fractional font - metrics at the Java 2D level - */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics) { - this(font, antialiased, useFractionalMetrics, null, false); - } - - /** Creates a new TextRenderer with the given Font, specified font - properties, and given RenderDelegate. The - antialiased and useFractionalMetrics - flags provide control over the same properties at the Java 2D - level. The renderDelegate provides more control - over the text rendered. No mipmap support is requested. - - @param font the font to render with - @param antialiased whether to use antialiased fonts - @param useFractionalMetrics whether to use fractional font - metrics at the Java 2D level - @param renderDelegate the render delegate to use to draw the - text's bitmap, or null to use the default one - */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics, RenderDelegate renderDelegate) { - this(font, antialiased, useFractionalMetrics, renderDelegate, false); - } - - /** Creates a new TextRenderer with the given Font, specified font - properties, and given RenderDelegate. The - antialiased and useFractionalMetrics - flags provide control over the same properties at the Java 2D - level. The renderDelegate provides more control - over the text rendered. If mipmap is true, attempts - to use OpenGL's automatic mipmap generation for better smoothing - when rendering the TextureRenderer's contents at a distance. - - @param font the font to render with - @param antialiased whether to use antialiased fonts - @param useFractionalMetrics whether to use fractional font - metrics at the Java 2D level - @param renderDelegate the render delegate to use to draw the - text's bitmap, or null to use the default one - @param mipmap whether to attempt use of automatic mipmap generation - */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics, RenderDelegate renderDelegate, - boolean mipmap) { - - NativeSurface surface = GLContext.getCurrent().getGLDrawable().getNativeSurface(); - if (surface instanceof ScalableSurface) { - // DPI scaling for surface - float[] surfaceScale = new float[2]; - ((ScalableSurface) surface).getCurrentSurfaceScale(surfaceScale); - float newSize = font.getSize() * surfaceScale[0]; - this.font = font.deriveFont(newSize); - } else { - this.font = font; - } - - this.antialiased = antialiased; - this.useFractionalMetrics = useFractionalMetrics; - this.mipmap = mipmap; - - // FIXME: consider adjusting the size based on font size - // (it will already automatically resize if necessary) - packer = new RectanglePacker(new Manager(), kSize, kSize); - - if (renderDelegate == null) { - renderDelegate = new DefaultRenderDelegate(); - } - - this.renderDelegate = renderDelegate; - - mGlyphProducer = new GlyphProducer(font.getNumGlyphs()); - } - - /** - * Returns the bounding rectangle of the given String, assuming it was rendered at the origin.See - * {@link #getBounds(CharSequence) getBounds(CharSequence)}. - * - * @param str The string. - * @return The bounding rectangle for str. - */ - public Rectangle2D getBounds(String str) { - return getBounds((CharSequence) str); - } - - /** - * Returns the bounding rectangle of the given CharSequence, assuming it was rendered at the origin.The coordinate - * system of the returned rectangle is Java 2D's, with increasing Y coordinates in the downward direction.The - * relative coordinate (0, 0) in the returned rectangle corresponds to the baseline of the leftmost character of the - * rendered string, in similar fashion to the results returned by, for example, - * {@link java.awt.font.GlyphVector#getVisualBounds}. Most applications will use only the width and height of the - * returned Rectangle for the purposes of centering or justifying the String. It is not specified which Java 2D - * bounds ({@link - * java.awt.font.GlyphVector#getVisualBounds getVisualBounds}, - * {@link java.awt.font.GlyphVector#getPixelBounds getPixelBounds}, etc.) the returned bounds correspond to, - * although every effort is made to ensure an accurate bound. - * - * @param str The string. - * @return The bounds of the string. - */ - public Rectangle2D getBounds(CharSequence str) { - // FIXME: this should be more optimized and use the glyph cache - Rect r = stringLocations.get(str); - - if (r != null) { - TextData data = (TextData) r.getUserData(); - - // Reconstitute the Java 2D results based on the cached values - return new Rectangle2D.Double(-data.origin().x, -data.origin().y, - r.w(), r.h()); - } - - // Must return a Rectangle compatible with the layout algorithm -- - // must be idempotent - return normalize(renderDelegate.getBounds(str, font, - getFontRenderContext())); - } - - /** - * Returns the Font this renderer is using. - * - * @return The Font. - */ - public Font getFont() { - return font; - } - - /** - * * Returns a FontRenderContext which can be used for external text-related size computations.This object should be - * considered transient and may become invalidated between - * {@link #beginRendering beginRendering} / {@link #endRendering endRendering} pairs. - * - * @return A FontRenderContext. - */ - public FontRenderContext getFontRenderContext() { - if (cachedFontRenderContext == null) { - cachedFontRenderContext = getGraphics2D().getFontRenderContext(); - } - - return cachedFontRenderContext; - } - - /** Begins rendering with this {@link TextRenderer TextRenderer} - into the current OpenGL drawable, pushing the projection and - modelview matrices and some state bits and setting up a - two-dimensional orthographic projection with (0, 0) as the - lower-left coordinate and (width, height) as the upper-right - coordinate. Binds and enables the internal OpenGL texture - object, sets the texture environment mode to GL_MODULATE, and - changes the current color to the last color set with this - TextRenderer via {@link #setColor setColor}. This method - disables the depth test and is equivalent to - beginRendering(width, height, true). - - @param width the width of the current on-screen OpenGL drawable - @param height the height of the current on-screen OpenGL drawable - @throws com.jogamp.opengl.GLException If an OpenGL context is not current when this method is called - */ - public void beginRendering(int width, int height) throws GLException { - beginRendering(width, height, true); - } - - /** Begins rendering with this {@link TextRenderer TextRenderer} - into the current OpenGL drawable, pushing the projection and - modelview matrices and some state bits and setting up a - two-dimensional orthographic projection with (0, 0) as the - lower-left coordinate and (width, height) as the upper-right - coordinate. Binds and enables the internal OpenGL texture - object, sets the texture environment mode to GL_MODULATE, and - changes the current color to the last color set with this - TextRenderer via {@link #setColor setColor}. Disables the depth - test if the disableDepthTest argument is true. - - @param width the width of the current on-screen OpenGL drawable - @param height the height of the current on-screen OpenGL drawable - @param disableDepthTest whether to disable the depth test - @throws GLException If an OpenGL context is not current when this method is called - */ - public void beginRendering(int width, int height, boolean disableDepthTest) - throws GLException { - beginRendering(true, width, height, disableDepthTest); - } - - /** Begins rendering of 2D text in 3D with this {@link TextRenderer - TextRenderer} into the current OpenGL drawable. Assumes the end - user is responsible for setting up the modelview and projection - matrices, and will render text using the {@link #draw3D draw3D} - method. This method pushes some OpenGL state bits, binds and - enables the internal OpenGL texture object, sets the texture - environment mode to GL_MODULATE, and changes the current color - to the last color set with this TextRenderer via {@link - #setColor setColor}. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void begin3DRendering() throws GLException { - beginRendering(false, 0, 0, false); - } - - /** Changes the current color of this TextRenderer to the supplied - one. The default color is opaque white. - - @param color the new color to use for rendering text - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setColor(Color color) throws GLException { - boolean noNeedForFlush = (haveCachedColor && (cachedColor != null) && - color.equals(cachedColor)); - - if (!noNeedForFlush) { - flushGlyphPipeline(); - } - - getBackingStore().setColor(color); - haveCachedColor = true; - cachedColor = color; - } - - /** Changes the current color of this TextRenderer to the supplied - one, where each component ranges from 0.0f - 1.0f. The alpha - component, if used, does not need to be premultiplied into the - color channels as described in the documentation for {@link - com.jogamp.opengl.util.texture.Texture Texture}, although - premultiplied colors are used internally. The default color is - opaque white. - - @param r the red component of the new color - @param g the green component of the new color - @param b the blue component of the new color - @param a the alpha component of the new color, 0.0f = completely - transparent, 1.0f = completely opaque - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setColor(float r, float g, float b, float a) - throws GLException { - boolean noNeedForFlush = (haveCachedColor && (cachedColor == null) && - (r == cachedR) && (g == cachedG) && (b == cachedB) && - (a == cachedA)); - - if (!noNeedForFlush) { - flushGlyphPipeline(); - } - - getBackingStore().setColor(r, g, b, a); - haveCachedColor = true; - cachedR = r; - cachedG = g; - cachedB = b; - cachedA = a; - cachedColor = null; - } - - /** Draws the supplied CharSequence at the desired location using - the renderer's current color. The baseline of the leftmost - character is at position (x, y) specified in OpenGL coordinates, - where the origin is at the lower-left of the drawable and the Y - coordinate increases in the upward direction. - - @param str the string to draw - @param x the x coordinate at which to draw - @param y the y coordinate at which to draw - @throws GLException If an OpenGL context is not current when this method is called - */ - public void draw(CharSequence str, int x, int y) throws GLException { - draw3D(str, x, y, 0, 1); - } - - /** - * * Draws the supplied String at the desired location using the renderer's current color.See - * {@link #draw(CharSequence, int, int) draw(CharSequence, int, int)}. - * - * @param str The string to draw. - * @param x The desired x location. - * @param y The desired y location. - */ - public void draw(String str, int x, int y) throws GLException { - draw3D(str, x, y, 0, 1); - } - - /** Draws the supplied CharSequence at the desired 3D location using - the renderer's current color. The baseline of the leftmost - character is placed at position (x, y, z) in the current - coordinate system. - - @param str the string to draw - @param x the x coordinate at which to draw - @param y the y coordinate at which to draw - @param z the z coordinate at which to draw - @param scaleFactor a uniform scale factor applied to the width and height of the drawn rectangle - @throws GLException If an OpenGL context is not current when this method is called - */ - public void draw3D(CharSequence str, float x, float y, float z, - float scaleFactor) { - internal_draw3D(str, x, y, z, scaleFactor); - } - - /** - * * Draws the supplied String at the desired 3D location using the renderer's current color.See {@link #draw3D(CharSequence, float, float, float, float) draw3D(CharSequence, float, float, - * float, float)}. - * - * @param str The string to draw. - * @param x The x location. - * @param y The y location. - * @param z The z location. - * @param scaleFactor The scale factor. - */ - public void draw3D(String str, float x, float y, float z, float scaleFactor) { - internal_draw3D(str, x, y, z, scaleFactor); - } - - /** - * Returns the pixel width of the given character. - * - * @param inChar The character to measure. - * @return The pixel width. - */ - public float getCharWidth(char inChar) { - return mGlyphProducer.getGlyphPixelWidth(inChar); - } - - /** Causes the TextRenderer to flush any internal caches it may be - maintaining and draw its rendering results to the screen. This - should be called after each call to draw() if you are setting - OpenGL state such as the modelview matrix between calls to - draw(). */ - public void flush() { - flushGlyphPipeline(); - } - - /** Ends a render cycle with this {@link TextRenderer TextRenderer}. - Restores the projection and modelview matrices as well as - several OpenGL state bits. Should be paired with {@link - #beginRendering beginRendering}. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void endRendering() throws GLException { - endRendering(true); - } - - /** Ends a 3D render cycle with this {@link TextRenderer TextRenderer}. - Restores several OpenGL state bits. Should be paired with {@link - #begin3DRendering begin3DRendering}. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void end3DRendering() throws GLException { - endRendering(false); - } - - /** Disposes of all resources this TextRenderer is using. It is not - valid to use the TextRenderer after this method is called. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void dispose() throws GLException { - packer.dispose(); - packer = null; - cachedBackingStore = null; - cachedGraphics = null; - cachedFontRenderContext = null; - - if (dbgFrame != null) { - dbgFrame.dispose(); - } - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private static Rectangle2D preNormalize(Rectangle2D src) { - // Need to round to integer coordinates - // Also give ourselves a little slop around the reported - // bounds of glyphs because it looks like neither the visual - // nor the pixel bounds works perfectly well - int minX = (int) Math.floor(src.getMinX()) - 1; - int minY = (int) Math.floor(src.getMinY()) - 1; - int maxX = (int) Math.ceil(src.getMaxX()) + 1; - int maxY = (int) Math.ceil(src.getMaxY()) + 1; - return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); - } - - - private Rectangle2D normalize(Rectangle2D src) { - // Give ourselves a boundary around each entity on the backing - // store in order to prevent bleeding of nearby Strings due to - // the fact that we use linear filtering - - // NOTE that this boundary is quite heuristic and is related - // to how far away in 3D we may view the text -- - // heuristically, 1.5% of the font's height - int boundary = (int) Math.max(1, 0.015 * font.getSize()); - - return new Rectangle2D.Double((int) Math.floor(src.getMinX() - boundary), - (int) Math.floor(src.getMinY() - boundary), - (int) Math.ceil(src.getWidth() + 2 * boundary), - (int) Math.ceil(src.getHeight()) + 2 * boundary); - } - - private TextureRenderer getBackingStore() { - TextureRenderer renderer = (TextureRenderer) packer.getBackingStore(); - - if (renderer != cachedBackingStore) { - // Backing store changed since last time; discard any cached Graphics2D - if (cachedGraphics != null) { - cachedGraphics.dispose(); - cachedGraphics = null; - cachedFontRenderContext = null; - } - - cachedBackingStore = renderer; - } - - return cachedBackingStore; - } - - private Graphics2D getGraphics2D() { - TextureRenderer renderer = getBackingStore(); - - if (cachedGraphics == null) { - cachedGraphics = renderer.createGraphics(); - - // Set up composite, font and rendering hints - cachedGraphics.setComposite(AlphaComposite.Src); - cachedGraphics.setColor(Color.WHITE); - cachedGraphics.setFont(font); - cachedGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - (antialiased ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON - : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)); - cachedGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - (useFractionalMetrics - ? RenderingHints.VALUE_FRACTIONALMETRICS_ON - : RenderingHints.VALUE_FRACTIONALMETRICS_OFF)); - } - - return cachedGraphics; - } - - private void beginRendering(boolean ortho, int width, int height, - boolean disableDepthTestForOrtho) { - GL2 gl = GLContext.getCurrentGL().getGL2(); - - if (DEBUG && !debugged) { - debug(gl); - } - - inBeginEndPair = true; - isOrthoMode = ortho; - beginRenderingWidth = width; - beginRenderingHeight = height; - beginRenderingDepthTestDisabled = disableDepthTestForOrtho; - - if (ortho) { - getBackingStore().beginOrthoRendering(width, height, - disableDepthTestForOrtho); - } else { - getBackingStore().begin3DRendering(); - } - - // Push client attrib bits used by the pipelined quad renderer - gl.glPushClientAttrib((int) GL2.GL_ALL_CLIENT_ATTRIB_BITS); - - if (!haveMaxSize) { - // Query OpenGL for the maximum texture size and set it in the - // RectanglePacker to keep it from expanding too large - int[] sz = new int[1]; - gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_SIZE, sz, 0); - packer.setMaxSize(sz[0], sz[0]); - haveMaxSize = true; - } - - if (needToResetColor && haveCachedColor) { - if (cachedColor == null) { - getBackingStore().setColor(cachedR, cachedG, cachedB, cachedA); - } else { - getBackingStore().setColor(cachedColor); - } - - needToResetColor = false; - } - - // Disable future attempts to use mipmapping if TextureRenderer - // doesn't support it - if (mipmap && !getBackingStore().isUsingAutoMipmapGeneration()) { - if (DEBUG) { - System.err.println("Disabled mipmapping in TextRenderer"); - } - - mipmap = false; - } - } - - /** - * emzic: here the call to glBindBuffer crashes on certain graphicscard/driver combinations - * this is why the ugly try-catch block has been added, which falls back to the old textrenderer - * - * @param ortho - * @throws GLException - */ - private void endRendering(boolean ortho) throws GLException { - flushGlyphPipeline(); - - inBeginEndPair = false; - - GL2 gl = GLContext.getCurrentGL().getGL2(); - - // Pop client attrib bits used by the pipelined quad renderer - gl.glPopClientAttrib(); - - // The OpenGL spec is unclear about whether this changes the - // buffer bindings, so preemptively zero out the GL_ARRAY_BUFFER - // binding - if (getUseVertexArrays() && is15Available(gl)) { - try { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); - } catch (Exception e) { - isExtensionAvailable_GL_VERSION_1_5 = false; - } - } - - if (ortho) { - getBackingStore().endOrthoRendering(); - } else { - getBackingStore().end3DRendering(); - } - - if (++numRenderCycles >= CYCLES_PER_FLUSH) { - numRenderCycles = 0; - - if (DEBUG) { - System.err.println("Clearing unused entries in endRendering()"); - } - - clearUnusedEntries(); - } - } - - private void clearUnusedEntries() { - final java.util.List deadRects = new ArrayList(); - - // Iterate through the contents of the backing store, removing - // text strings that haven't been used recently - packer.visit(new RectVisitor() { - @Override - public void visit(Rect rect) { - TextData data = (TextData) rect.getUserData(); - - if (data.used()) { - data.clearUsed(); - } else { - deadRects.add(rect); - } - } - }); - - for (Rect r : deadRects) { - packer.remove(r); - stringLocations.remove(((TextData) r.getUserData()).string()); - - int unicodeToClearFromCache = ((TextData) r.getUserData()).unicodeID; - - if (unicodeToClearFromCache > 0) { - mGlyphProducer.clearCacheEntry(unicodeToClearFromCache); - } - - // if (DEBUG) { - // Graphics2D g = getGraphics2D(); - // g.setComposite(AlphaComposite.Clear); - // g.fillRect(r.x(), r.y(), r.w(), r.h()); - // g.setComposite(AlphaComposite.Src); - // } - } - - // If we removed dead rectangles this cycle, try to do a compaction - float frag = packer.verticalFragmentationRatio(); - - if (!deadRects.isEmpty() && (frag > MAX_VERTICAL_FRAGMENTATION)) { - if (DEBUG) { - System.err.println( - "Compacting TextRenderer backing store due to vertical fragmentation " + - frag); - } - - packer.compact(); - } - - if (DEBUG) { - getBackingStore().markDirty(0, 0, getBackingStore().getWidth(), - getBackingStore().getHeight()); - } - } - - private void internal_draw3D(CharSequence str, float x, float y, float z, - float scaleFactor) { - for (Glyph glyph : mGlyphProducer.getGlyphs(str)) { - float advance = glyph.draw3D(x, y, z, scaleFactor); - x += advance * scaleFactor; - } - } - - private void flushGlyphPipeline() { - if (mPipelinedQuadRenderer != null) { - mPipelinedQuadRenderer.draw(); - } - } - - private void draw3D_ROBUST(CharSequence str, float x, float y, float z, - float scaleFactor) { - String curStr; - if (str instanceof String) { - curStr = (String) str; - } else { - curStr = str.toString(); - } - - // Look up the string on the backing store - Rect rect = stringLocations.get(curStr); - - if (rect == null) { - // Rasterize this string and place it on the backing store - Graphics2D g = getGraphics2D(); - Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(curStr, font, getFontRenderContext())); - Rectangle2D bbox = normalize(origBBox); - Point origin = new Point((int) -bbox.getMinX(), - (int) -bbox.getMinY()); - rect = new Rect(0, 0, (int) bbox.getWidth(), - (int) bbox.getHeight(), - new TextData(curStr, origin, origBBox, -1)); - - packer.add(rect); - stringLocations.put(curStr, rect); - - // Re-fetch the Graphics2D in case the addition of the rectangle - // caused the old backing store to be thrown away - g = getGraphics2D(); - - // OK, should now have an (x, y) for this rectangle; rasterize - // the String - int strx = rect.x() + origin.x; - int stry = rect.y() + origin.y; - - // Clear out the area we're going to draw into - g.setComposite(AlphaComposite.Clear); - g.fillRect(rect.x(), rect.y(), rect.w(), rect.h()); - g.setComposite(AlphaComposite.Src); - - // Draw the string - renderDelegate.draw(g, curStr, strx, stry); - - if (DRAW_BBOXES) { - TextData data = (TextData) rect.getUserData(); - // Draw a bounding box on the backing store - g.drawRect(strx - data.origOriginX(), - stry - data.origOriginY(), - (int) data.origRect().getWidth(), - (int) data.origRect().getHeight()); - g.drawRect(strx - data.origin().x, - stry - data.origin().y, - rect.w(), - rect.h()); - } - - // Mark this region of the TextureRenderer as dirty - getBackingStore().markDirty(rect.x(), rect.y(), rect.w(), - rect.h()); - } - - // OK, now draw the portion of the backing store to the screen - TextureRenderer renderer = getBackingStore(); - - // NOTE that the rectangles managed by the packer have their - // origin at the upper-left but the TextureRenderer's origin is - // at its lower left!!! - TextData data = (TextData) rect.getUserData(); - data.markUsed(); - - Rectangle2D origRect = data.origRect(); - - // Align the leftmost point of the baseline to the (x, y, z) coordinate requested - renderer.draw3DRect(x - (scaleFactor * data.origOriginX()), - y - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())), z, - rect.x() + (data.origin().x - data.origOriginX()), - renderer.getHeight() - rect.y() - (int) origRect.getHeight() - - (data.origin().y - data.origOriginY()), - (int) origRect.getWidth(), (int) origRect.getHeight(), scaleFactor); - } - - //---------------------------------------------------------------------- - // Debugging functionality - // - private void debug(GL gl) { - dbgFrame = new Frame("TextRenderer Debug Output"); - - GLCanvas dbgCanvas = new GLCanvas(new GLCapabilities(gl.getGLProfile())); - dbgCanvas.setSharedContext(GLContext.getCurrent()); - dbgCanvas.addGLEventListener(new DebugListener(gl, dbgFrame)); - dbgFrame.add(dbgCanvas); - - final FPSAnimator anim = new FPSAnimator(dbgCanvas, 10); - dbgFrame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - // Run this on another thread than the AWT event queue to - // make sure the call to Animator.stop() completes before - // exiting - new Thread(new Runnable() { - @Override - public void run() { - anim.stop(); - } - }).start(); - } - }); - dbgFrame.setSize(kSize, kSize); - dbgFrame.setVisible(true); - anim.start(); - debugged = true; - } - - /** Class supporting more full control over the process of rendering - the bitmapped text. Allows customization of whether the backing - store text bitmap is full-color or intensity only, the size of - each individual rendered text rectangle, and the contents of - each individual rendered text string. The default implementation - of this interface uses an intensity-only texture, a - closely-cropped rectangle around the text, and renders text - using the color white, which is modulated by the set color - during the rendering process. */ - public static interface RenderDelegate { - /** - * Indicates whether the backing store of this TextRenderer should be intensity-only (the default) or - * full-color. - * - * @return whether or not intensity only is on. - */ - public boolean intensityOnly(); - - /** - * Computes the bounds of the given String relative to the origin. - * - * @param str The string to process. - * @param font The font to use. - * @param frc The render context to use. - * @return The bounds given the parameters. - */ - public Rectangle2D getBounds(String str, Font font, - FontRenderContext frc); - - /** - * Computes the bounds of the given character sequence relative to the origin. - * - * @param str The string to process. - * @param font The font to use. - * @param frc The render context to use. - * @return The bounds given the parameters. - */ - public Rectangle2D getBounds(CharSequence str, Font font, - FontRenderContext frc); - - /** - * Computes the bounds of the given GlyphVector, already assumed to have been created for a particular Font, - * relative to the origin. - * - * @param gv The string to process. - * @param frc The render context to use. - * @return The bounds given the parameters. - */ - public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc); - - /** - * * Render the passed character sequence at the designated location using the supplied Graphics2D instance.The - * surrounding region will already have been cleared to the RGB color (0, 0, 0) with zero alpha.The initial - * drawing context of the passed Graphics2D will be set to use AlphaComposite.Src, the color white, the Font - * specified in the TextRenderer's constructor, and the rendering hints specified in the TextRenderer - * constructor.Changes made by the end user may be visible in successive calls to this method, but are not - * guaranteed to be preserved.Implementors of this method should reset the Graphics2D's state to that desired - * each time this method is called, in particular those states which are not the defaults. - * - * @param graphics The canvas to draw on. - * @param str The string to draw. - * @param x The x location. - * @param y The y location. - */ - public void draw(Graphics2D graphics, String str, int x, int y); - - /** - * * Render the passed GlyphVector at the designated location using the supplied Graphics2D instance.The - * surrounding region will already have been cleared to the RGB color (0, 0, 0) with zero alpha.The initial - * drawing context of the passed Graphics2D will be set to use AlphaComposite.Src, the color white, the Font - * specified in the TextRenderer's constructor, and the rendering hints specified in the TextRenderer - * constructor.Changes made by the end user may be visible in successive calls to this method, but are not - * guaranteed to be preserved.Implementors of this method should reset the Graphics2D's state to that desired - * each time this method is called, in particular those states which are not the defaults. - * - * @param graphics The canvas to draw on. - * @param str The string to draw. - * @param x The x location. - * @param y The y location. - */ - public void drawGlyphVector(Graphics2D graphics, GlyphVector str, - int x, int y); - } - - private static class CharSequenceIterator implements CharacterIterator { - CharSequence mSequence; - int mLength; - int mCurrentIndex; - - CharSequenceIterator() { - } - - CharSequenceIterator(CharSequence sequence) { - initFromCharSequence(sequence); - } - - public void initFromCharSequence(CharSequence sequence) { - mSequence = sequence; - mLength = mSequence.length(); - mCurrentIndex = 0; - } - - @Override - public char last() { - mCurrentIndex = Math.max(0, mLength - 1); - - return current(); - } - - @Override - public char current() { - if ((mLength == 0) || (mCurrentIndex >= mLength)) { - return CharacterIterator.DONE; - } - - return mSequence.charAt(mCurrentIndex); - } - - @Override - public char next() { - mCurrentIndex++; - - return current(); - } - - @Override - public char previous() { - mCurrentIndex = Math.max(mCurrentIndex - 1, 0); - - return current(); - } - - @Override - public char setIndex(int position) { - mCurrentIndex = position; - - return current(); - } - - @Override - public int getBeginIndex() { - return 0; - } - - @Override - public int getEndIndex() { - return mLength; - } - - @Override - public int getIndex() { - return mCurrentIndex; - } - - @Override - public Object clone() { - CharSequenceIterator iter = new CharSequenceIterator(mSequence); - iter.mCurrentIndex = mCurrentIndex; - - return iter; - } - - @Override - public char first() { - if (mLength == 0) { - return CharacterIterator.DONE; - } - - mCurrentIndex = 0; - - return current(); - } - } - - // Data associated with each rectangle of text - static class TextData { - // Back-pointer to String this TextData describes, if it - // represents a String rather than a single glyph - private final String str; - - // If this TextData represents a single glyph, this is its - // unicode ID - int unicodeID; - - // The following must be defined and used VERY precisely. This is - // the offset from the upper-left corner of this rectangle (Java - // 2D coordinate system) at which the string must be rasterized in - // order to fit within the rectangle -- the leftmost point of the - // baseline. - private final Point origin; - - // This represents the pre-normalized rectangle, which fits - // within the rectangle on the backing store. We keep a - // one-pixel border around entries on the backing store to - // prevent bleeding of adjacent letters when using GL_LINEAR - // filtering for rendering. The origin of this rectangle is - // equivalent to the origin above. - private final Rectangle2D origRect; - - private boolean used; // Whether this text was used recently - - TextData(String str, Point origin, Rectangle2D origRect, int unicodeID) { - this.str = str; - this.origin = origin; - this.origRect = origRect; - this.unicodeID = unicodeID; - } - - String string() { - return str; - } - - Point origin() { - return origin; - } - - // The following three methods are used to locate the glyph - // within the expanded rectangle coming from normalize() - int origOriginX() { - return (int) -origRect.getMinX(); - } - - int origOriginY() { - return (int) -origRect.getMinY(); - } - - Rectangle2D origRect() { - return origRect; - } - - boolean used() { - return used; - } - - void markUsed() { - used = true; - } - - void clearUsed() { - used = false; - } - } - - class Manager implements BackingStoreManager { - private Graphics2D g; - - @Override - public Object allocateBackingStore(int w, int h) { - // FIXME: should consider checking Font's attributes to see - // whether we're likely to need to support a full RGBA backing - // store (i.e., non-default Paint, foreground color, etc.), but - // for now, let's just be more efficient - TextureRenderer renderer; - - if (renderDelegate.intensityOnly()) { - renderer = TextureRenderer.createAlphaOnlyRenderer(w, h, mipmap); - } else { - renderer = new TextureRenderer(w, h, true, mipmap); - } - renderer.setSmoothing(smoothing); - - if (DEBUG) { - System.err.println(" TextRenderer allocating backing store " + - w + " x " + h); - } - - return renderer; - } - - @Override - public void deleteBackingStore(Object backingStore) { - ((TextureRenderer) backingStore).dispose(); - } - - @Override - public boolean preExpand(Rect cause, int attemptNumber) { - // Only try this one time; clear out potentially obsolete entries - // NOTE: this heuristic and the fact that it clears the used bit - // of all entries seems to cause cycling of entries in some - // situations, where the backing store becomes small compared to - // the amount of text on the screen (see the TextFlow demo) and - // the entries continually cycle in and out of the backing - // store, decreasing performance. If we added a little age - // information to the entries, and only cleared out entries - // above a certain age, this behavior would be eliminated. - // However, it seems the system usually stabilizes itself, so - // for now we'll just keep things simple. Note that if we don't - // clear the used bit here, the backing store tends to increase - // very quickly to its maximum size, at least with the TextFlow - // demo when the text is being continually re-laid out. - if (attemptNumber == 0) { - if (DEBUG) { - System.err.println( - "Clearing unused entries in preExpand(): attempt number " + - attemptNumber); - } - - if (inBeginEndPair) { - // Draw any outstanding glyphs - flush(); - } - - clearUnusedEntries(); - - return true; - } - - return false; - } - - @Override - public boolean additionFailed(Rect cause, int attemptNumber) { - // Heavy hammer -- might consider doing something different - packer.clear(); - stringLocations.clear(); - mGlyphProducer.clearAllCacheEntries(); - - if (DEBUG) { - System.err.println( - " *** Cleared all text because addition failed ***"); - } - - if (attemptNumber == 0) { - return true; - } - - return false; - } - - @Override - public boolean canCompact() { - return true; - } - - @Override - public void beginMovement(Object oldBackingStore, Object newBackingStore) { - // Exit the begin / end pair if necessary - if (inBeginEndPair) { - // Draw any outstanding glyphs - flush(); - - GL2 gl = GLContext.getCurrentGL().getGL2(); - - // Pop client attrib bits used by the pipelined quad renderer - gl.glPopClientAttrib(); - - // The OpenGL spec is unclear about whether this changes the - // buffer bindings, so preemptively zero out the GL_ARRAY_BUFFER - // binding - if (getUseVertexArrays() && is15Available(gl)) { - try { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); - } catch (Exception e) { - isExtensionAvailable_GL_VERSION_1_5 = false; - } - } - - if (isOrthoMode) { - ((TextureRenderer) oldBackingStore).endOrthoRendering(); - } else { - ((TextureRenderer) oldBackingStore).end3DRendering(); - } - } - - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; - g = newRenderer.createGraphics(); - } - - @Override - public void move(Object oldBackingStore, Rect oldLocation, - Object newBackingStore, Rect newLocation) { - TextureRenderer oldRenderer = (TextureRenderer) oldBackingStore; - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; - - if (oldRenderer == newRenderer) { - // Movement on the same backing store -- easy case - g.copyArea(oldLocation.x(), oldLocation.y(), oldLocation.w(), - oldLocation.h(), newLocation.x() - oldLocation.x(), - newLocation.y() - oldLocation.y()); - } else { - // Need to draw from the old renderer's image into the new one - Image img = oldRenderer.getImage(); - g.drawImage(img, newLocation.x(), newLocation.y(), - newLocation.x() + newLocation.w(), - newLocation.y() + newLocation.h(), oldLocation.x(), - oldLocation.y(), oldLocation.x() + oldLocation.w(), - oldLocation.y() + oldLocation.h(), null); - } - } - - @Override - public void endMovement(Object oldBackingStore, Object newBackingStore) { - g.dispose(); - - // Sync the whole surface - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; - newRenderer.markDirty(0, 0, newRenderer.getWidth(), - newRenderer.getHeight()); - - // Re-enter the begin / end pair if necessary - if (inBeginEndPair) { - if (isOrthoMode) { - ((TextureRenderer) newBackingStore).beginOrthoRendering(beginRenderingWidth, - beginRenderingHeight, beginRenderingDepthTestDisabled); - } else { - ((TextureRenderer) newBackingStore).begin3DRendering(); - } - - // Push client attrib bits used by the pipelined quad renderer - GL2 gl = GLContext.getCurrentGL().getGL2(); - gl.glPushClientAttrib((int) GL2.GL_ALL_CLIENT_ATTRIB_BITS); - - if (haveCachedColor) { - if (cachedColor == null) { - ((TextureRenderer) newBackingStore).setColor(cachedR, - cachedG, cachedB, cachedA); - } else { - ((TextureRenderer) newBackingStore).setColor(cachedColor); - } - } - } else { - needToResetColor = true; - } - } - } - - public static class DefaultRenderDelegate implements RenderDelegate { - @Override - public boolean intensityOnly() { - return true; - } - - @Override - public Rectangle2D getBounds(CharSequence str, Font font, - FontRenderContext frc) { - return getBounds(font.createGlyphVector(frc, - new CharSequenceIterator(str)), - frc); - } - - @Override - public Rectangle2D getBounds(String str, Font font, - FontRenderContext frc) { - return getBounds(font.createGlyphVector(frc, str), frc); - } - - @Override - public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc) { - return gv.getVisualBounds(); - } - - @Override - public void drawGlyphVector(Graphics2D graphics, GlyphVector str, - int x, int y) { - graphics.drawGlyphVector(str, x, y); - } - - @Override - public void draw(Graphics2D graphics, String str, int x, int y) { - graphics.drawString(str, x, y); - } - } - - //---------------------------------------------------------------------- - // Glyph-by-glyph rendering support - // - - // A temporary to prevent excessive garbage creation - private final char[] singleUnicode = new char[1]; - - /** A Glyph represents either a single unicode glyph or a - substring of characters to be drawn. The reason for the dual - behavior is so that we can take in a sequence of unicode - characters and partition them into runs of individual glyphs, - but if we encounter complex text and/or unicode sequences we - don't understand, we can render them using the - string-by-string method.

- - Glyphs need to be able to re-upload themselves to the backing - store on demand as we go along in the render sequence. - */ - - class Glyph { - // If this Glyph represents an individual unicode glyph, this - // is its unicode ID. If it represents a String, this is -1. - private int unicodeID; - // If the above field isn't -1, then these fields are used. - // The glyph code in the font - private int glyphCode; - // The GlyphProducer which created us - private GlyphProducer producer; - // The advance of this glyph - private float advance; - // The GlyphVector for this single character; this is passed - // in during construction but cleared during the upload - // process - private GlyphVector singleUnicodeGlyphVector; - // The rectangle of this glyph on the backing store, or null - // if it has been cleared due to space pressure - private Rect glyphRectForTextureMapping; - // If this Glyph represents a String, this is the sequence of - // characters - private String str; - // Whether we need a valid advance when rendering this string - // (i.e., whether it has other single glyphs coming after it) - private boolean needAdvance; - - // Creates a Glyph representing an individual Unicode character - public Glyph(int unicodeID, - int glyphCode, - float advance, - GlyphVector singleUnicodeGlyphVector, - GlyphProducer producer) { - this.unicodeID = unicodeID; - this.glyphCode = glyphCode; - this.advance = advance; - this.singleUnicodeGlyphVector = singleUnicodeGlyphVector; - this.producer = producer; - } - - // Creates a Glyph representing a sequence of characters, with - // an indication of whether additional single glyphs are being - // rendered after it - public Glyph(String str, boolean needAdvance) { - this.str = str; - this.needAdvance = needAdvance; - } - - /** Returns this glyph's unicode ID */ - public int getUnicodeID() { - return unicodeID; - } - - /** Returns this glyph's (font-specific) glyph code */ - public int getGlyphCode() { - return glyphCode; - } - - /** Returns the advance for this glyph */ - public float getAdvance() { - return advance; - } - - /** Draws this glyph and returns the (x) advance for this glyph */ - public float draw3D(float inX, float inY, float z, float scaleFactor) { - if (str != null) { - draw3D_ROBUST(str, inX, inY, z, scaleFactor); - if (!needAdvance) { - return 0; - } - // Compute and return the advance for this string - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), str); - float totalAdvance = 0; - for (int i = 0; i < gv.getNumGlyphs(); i++) { - totalAdvance += gv.getGlyphMetrics(i).getAdvance(); - } - return totalAdvance; - } - - // This is the code path taken for individual glyphs - if (glyphRectForTextureMapping == null) { - upload(); - } - - try { - if (mPipelinedQuadRenderer == null) { - mPipelinedQuadRenderer = new Pipelined_QuadRenderer(); - } - - TextureRenderer renderer = getBackingStore(); - // Handles case where NPOT texture is used for backing store - TextureCoords wholeImageTexCoords = renderer.getTexture().getImageTexCoords(); - float xScale = wholeImageTexCoords.right(); - float yScale = wholeImageTexCoords.bottom(); - - Rect rect = glyphRectForTextureMapping; - TextData data = (TextData) rect.getUserData(); - data.markUsed(); - - Rectangle2D origRect = data.origRect(); - - float x = inX - (scaleFactor * data.origOriginX()); - float y = inY - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())); - - int texturex = rect.x() + (data.origin().x - data.origOriginX()); - int texturey = renderer.getHeight() - rect.y() - (int) origRect.getHeight() - - (data.origin().y - data.origOriginY()); - int width = (int) origRect.getWidth(); - int height = (int) origRect.getHeight(); - - float tx1 = xScale * texturex / renderer.getWidth(); - float ty1 = yScale * (1.0f - - ((float) texturey / (float) renderer.getHeight())); - float tx2 = xScale * (texturex + width) / renderer.getWidth(); - float ty2 = yScale * (1.0f - - ((float) (texturey + height) / (float) renderer.getHeight())); - - mPipelinedQuadRenderer.glTexCoord2f(tx1, ty1); - mPipelinedQuadRenderer.glVertex3f(x, y, z); - mPipelinedQuadRenderer.glTexCoord2f(tx2, ty1); - mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor), y, - z); - mPipelinedQuadRenderer.glTexCoord2f(tx2, ty2); - mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor), - y + (height * scaleFactor), z); - mPipelinedQuadRenderer.glTexCoord2f(tx1, ty2); - mPipelinedQuadRenderer.glVertex3f(x, - y + (height * scaleFactor), z); - } catch (Exception e) { - e.printStackTrace(); - } - return advance; - } - - /** Notifies this glyph that it's been cleared out of the cache */ - public void clear() { - glyphRectForTextureMapping = null; - } - - private void upload() { - GlyphVector gv = getGlyphVector(); - Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(gv, getFontRenderContext())); - Rectangle2D bbox = normalize(origBBox); - Point origin = new Point((int) -bbox.getMinX(), - (int) -bbox.getMinY()); - Rect rect = new Rect(0, 0, (int) bbox.getWidth(), - (int) bbox.getHeight(), - new TextData(null, origin, origBBox, unicodeID)); - packer.add(rect); - glyphRectForTextureMapping = rect; - Graphics2D g = getGraphics2D(); - // OK, should now have an (x, y) for this rectangle; rasterize - // the glyph - int strx = rect.x() + origin.x; - int stry = rect.y() + origin.y; - - // Clear out the area we're going to draw into - g.setComposite(AlphaComposite.Clear); - g.fillRect(rect.x(), rect.y(), rect.w(), rect.h()); - g.setComposite(AlphaComposite.Src); - - // Draw the string - renderDelegate.drawGlyphVector(g, gv, strx, stry); - - if (DRAW_BBOXES) { - TextData data = (TextData) rect.getUserData(); - // Draw a bounding box on the backing store - g.drawRect(strx - data.origOriginX(), - stry - data.origOriginY(), - (int) data.origRect().getWidth(), - (int) data.origRect().getHeight()); - g.drawRect(strx - data.origin().x, - stry - data.origin().y, - rect.w(), - rect.h()); - } - - // Mark this region of the TextureRenderer as dirty - getBackingStore().markDirty(rect.x(), rect.y(), rect.w(), - rect.h()); - // Re-register ourselves with our producer - producer.register(this); - } - - private GlyphVector getGlyphVector() { - GlyphVector gv = singleUnicodeGlyphVector; - if (gv != null) { - singleUnicodeGlyphVector = null; // Don't need this anymore - return gv; - } - singleUnicode[0] = (char) unicodeID; - return font.createGlyphVector(getFontRenderContext(), singleUnicode); - } - } - - class GlyphProducer { - final int undefined = -2; - FontRenderContext fontRenderContext; - List glyphsOutput = new ArrayList(); - HashMap fullGlyphVectorCache = new HashMap(); - HashMap glyphMetricsCache = new HashMap(); - // The mapping from unicode character to font-specific glyph ID - int[] unicodes2Glyphs; - // The mapping from glyph ID to Glyph - Glyph[] glyphCache; - // We re-use this for each incoming string - CharSequenceIterator iter = new CharSequenceIterator(); - - GlyphProducer(int fontLengthInGlyphs) { - unicodes2Glyphs = new int[512]; - glyphCache = new Glyph[fontLengthInGlyphs]; - clearAllCacheEntries(); - } - - public List getGlyphs(CharSequence inString) { - glyphsOutput.clear(); - GlyphVector fullRunGlyphVector; - fullRunGlyphVector = fullGlyphVectorCache.get(inString.toString()); - if (fullRunGlyphVector == null) { - iter.initFromCharSequence(inString); - fullRunGlyphVector = font.createGlyphVector(getFontRenderContext(), iter); - fullGlyphVectorCache.put(inString.toString(), fullRunGlyphVector); - } - boolean complex = (fullRunGlyphVector.getLayoutFlags() != 0); - if (complex || DISABLE_GLYPH_CACHE) { - // Punt to the robust version of the renderer - glyphsOutput.add(new Glyph(inString.toString(), false)); - return glyphsOutput; - } - - int lengthInGlyphs = fullRunGlyphVector.getNumGlyphs(); - int i = 0; - while (i < lengthInGlyphs) { - Character letter = CharacterCache.valueOf(inString.charAt(i)); - GlyphMetrics metrics = glyphMetricsCache.get(letter); - if (metrics == null) { - metrics = fullRunGlyphVector.getGlyphMetrics(i); - glyphMetricsCache.put(letter, metrics); - } - Glyph glyph = getGlyph(inString, metrics, i); - if (glyph != null) { - glyphsOutput.add(glyph); - i++; - } else { - // Assemble a run of characters that don't fit in - // the cache - StringBuilder buf = new StringBuilder(); - while (i < lengthInGlyphs && - getGlyph(inString, fullRunGlyphVector.getGlyphMetrics(i), i) == null) { - buf.append(inString.charAt(i++)); - } - glyphsOutput.add(new Glyph(buf.toString(), - // Any more glyphs after this run? - i < lengthInGlyphs)); - } - } - return glyphsOutput; - } - - public void clearCacheEntry(int unicodeID) { - int glyphID = unicodes2Glyphs[unicodeID]; - if (glyphID != undefined) { - Glyph glyph = glyphCache[glyphID]; - if (glyph != null) { - glyph.clear(); - } - glyphCache[glyphID] = null; - } - unicodes2Glyphs[unicodeID] = undefined; - } - - public void clearAllCacheEntries() { - for (int i = 0; i < unicodes2Glyphs.length; i++) { - clearCacheEntry(i); - } - } - - public void register(Glyph glyph) { - unicodes2Glyphs[glyph.getUnicodeID()] = glyph.getGlyphCode(); - glyphCache[glyph.getGlyphCode()] = glyph; - } - - public float getGlyphPixelWidth(char unicodeID) { - Glyph glyph = getGlyph(unicodeID); - if (glyph != null) { - return glyph.getAdvance(); - } - - // Have to do this the hard / uncached way - singleUnicode[0] = unicodeID; - GlyphVector gv = font.createGlyphVector(fontRenderContext, - singleUnicode); - return gv.getGlyphMetrics(0).getAdvance(); - } - - // Returns a glyph object for this single glyph. Returns null - // if the unicode or glyph ID would be out of bounds of the - // glyph cache. - private Glyph getGlyph(CharSequence inString, - GlyphMetrics glyphMetrics, - int index) { - char unicodeID = inString.charAt(index); - - if (unicodeID >= unicodes2Glyphs.length) { - return null; - } - - int glyphID = unicodes2Glyphs[unicodeID]; - if (glyphID != undefined) { - return glyphCache[glyphID]; - } - - // Must fabricate the glyph - singleUnicode[0] = unicodeID; - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); - return getGlyph(unicodeID, gv, glyphMetrics); - } - - // It's unclear whether this variant might produce less - // optimal results than if we can see the entire GlyphVector - // for the incoming string - private Glyph getGlyph(int unicodeID) { - if (unicodeID >= unicodes2Glyphs.length) { - return null; - } - - int glyphID = unicodes2Glyphs[unicodeID]; - if (glyphID != undefined) { - return glyphCache[glyphID]; - } - singleUnicode[0] = (char) unicodeID; - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); - return getGlyph(unicodeID, gv, gv.getGlyphMetrics(0)); - } - - private Glyph getGlyph(int unicodeID, - GlyphVector singleUnicodeGlyphVector, - GlyphMetrics metrics) { - int glyphCode = singleUnicodeGlyphVector.getGlyphCode(0); - // Have seen huge glyph codes (65536) coming out of some fonts in some Unicode situations - if (glyphCode >= glyphCache.length) { - return null; - } - Glyph glyph = new Glyph(unicodeID, - glyphCode, - metrics.getAdvance(), - singleUnicodeGlyphVector, - this); - register(glyph); - return glyph; - } - } - - private static class CharacterCache { - private CharacterCache() { - } - - static final Character CACHE[] = new Character[127 + 1]; - - static { - for (int i = 0; i < CACHE.length; i++) { - CACHE[i] = (char) i; - } - } - - public static Character valueOf(char c) { - if (c <= 127) { // must cache - return CharacterCache.CACHE[c]; - } - return c; - } - } - - class Pipelined_QuadRenderer { - int mOutstandingGlyphsVerticesPipeline = 0; - FloatBuffer mTexCoords; - FloatBuffer mVertCoords; - boolean usingVBOs; - int mVBO_For_ResuableTileVertices; - int mVBO_For_ResuableTileTexCoords; - - Pipelined_QuadRenderer() { - GL2 gl = GLContext.getCurrentGL().getGL2(); - mVertCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsVerts); - mTexCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsTex); - - usingVBOs = getUseVertexArrays() && is15Available(gl); - - if (usingVBOs) { - try { - int[] vbos = new int[2]; - gl.glGenBuffers(2, IntBuffer.wrap(vbos)); - - mVBO_For_ResuableTileVertices = vbos[0]; - mVBO_For_ResuableTileTexCoords = vbos[1]; - - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileVertices); - gl.glBufferData(GL2.GL_ARRAY_BUFFER, kTotalBufferSizeBytesVerts, - null, GL2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline - - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileTexCoords); - gl.glBufferData(GL2.GL_ARRAY_BUFFER, kTotalBufferSizeBytesTex, - null, GL2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline - } catch (Exception e) { - isExtensionAvailable_GL_VERSION_1_5 = false; - usingVBOs = false; - } - } - } - - public void glTexCoord2f(float v, float v1) { - mTexCoords.put(v); - mTexCoords.put(v1); - } - - public void glVertex3f(float inX, float inY, float inZ) { - mVertCoords.put(inX); - mVertCoords.put(inY); - mVertCoords.put(inZ); - - mOutstandingGlyphsVerticesPipeline++; - - if (mOutstandingGlyphsVerticesPipeline >= kTotalBufferSizeVerts) { - this.draw(); - } - } - - private void draw() { - if (useVertexArrays) { - drawVertexArrays(); - } else { - drawIMMEDIATE(); - } - } - - private void drawVertexArrays() { - if (mOutstandingGlyphsVerticesPipeline > 0) { - GL2 gl = GLContext.getCurrentGL().getGL2(); - - TextureRenderer renderer = getBackingStore(); - renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? - - mVertCoords.rewind(); - mTexCoords.rewind(); - - gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); - - if (usingVBOs) { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileVertices); - gl.glBufferSubData(GL2.GL_ARRAY_BUFFER, 0, - mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_VertexData, - mVertCoords); // upload only the new stuff - gl.glVertexPointer(3, GL2.GL_FLOAT, 0, 0); - } else { - gl.glVertexPointer(3, GL2.GL_FLOAT, 0, mVertCoords); - } - - gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); - - if (usingVBOs) { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileTexCoords); - gl.glBufferSubData(GL2.GL_ARRAY_BUFFER, 0, - mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_TexData, - mTexCoords); // upload only the new stuff - gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, 0); - } else { - gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, mTexCoords); - } - - gl.glDrawArrays(GL2.GL_QUADS, 0, - mOutstandingGlyphsVerticesPipeline); - - mVertCoords.rewind(); - mTexCoords.rewind(); - mOutstandingGlyphsVerticesPipeline = 0; - } - } - - private void drawIMMEDIATE() { - if (mOutstandingGlyphsVerticesPipeline > 0) { - TextureRenderer renderer = getBackingStore(); - renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? - - GL2 gl = GLContext.getCurrentGL().getGL2(); - gl.glBegin(GL2.GL_QUADS); - - try { - int numberOfQuads = mOutstandingGlyphsVerticesPipeline / 4; - mVertCoords.rewind(); - mTexCoords.rewind(); - - for (int i = 0; i < numberOfQuads; i++) { - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - gl.glEnd(); - mVertCoords.rewind(); - mTexCoords.rewind(); - mOutstandingGlyphsVerticesPipeline = 0; - } - } - } - } - - class DebugListener implements GLEventListener { - private GLU glu; - private Frame frame; - - DebugListener(GL gl, Frame frame) { - this.glu = GLU.createGLU(gl); - this.frame = frame; - } - - @Override - public void display(GLAutoDrawable drawable) { - GL2 gl = GLContext.getCurrentGL().getGL2(); - gl.glClear(GL2.GL_DEPTH_BUFFER_BIT | GL2.GL_COLOR_BUFFER_BIT); - - if (packer == null) { - return; - } - - TextureRenderer rend = getBackingStore(); - final int w = rend.getWidth(); - final int h = rend.getHeight(); - rend.beginOrthoRendering(w, h); - rend.drawOrthoRect(0, 0); - rend.endOrthoRendering(); - - if ((frame.getWidth() != w) || (frame.getHeight() != h)) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - frame.setSize(w, h); - } - }); - } - } - - @Override - public void dispose(GLAutoDrawable drawable) { - glu=null; - frame=null; - } - - // Unused methods - @Override - public void init(GLAutoDrawable drawable) { - } - - @Override - public void reshape(GLAutoDrawable drawable, int x, int y, int width, - int height) { - } - - public void displayChanged(GLAutoDrawable drawable, - boolean modeChanged, boolean deviceChanged) { - } - } - - /** - * Sets whether vertex arrays are being used internally for rendering, or whether text is rendered using the OpenGL - * immediate mode commands.This is provided as a concession for certain graphics cards which have poor vertex array - * performance. Defaults to true. - * - * @param useVertexArrays The desired value. - */ - public void setUseVertexArrays(boolean useVertexArrays) { - this.useVertexArrays = useVertexArrays; - } - - /** - * Indicates whether vertex arrays are being used internally for - * rendering, or whether text is rendered using the OpenGL - * immediate mode commands.Defaults to true. - * @return whether userVertexArrays is on or off. - */ - public final boolean getUseVertexArrays() { - return useVertexArrays; - } - - /** - * Sets whether smoothing (i.e., GL_LINEAR filtering) is enabled in the backing TextureRenderer of this - * TextRenderer.A few graphics cards do not behave well when this is enabled, resulting in fuzzy text. Defaults to - * true. - * - * @param smoothing The new smoothing setting. - */ - public void setSmoothing(boolean smoothing) { - this.smoothing = smoothing; - getBackingStore().setSmoothing(smoothing); - } - - /** - * Indicates whether smoothing is enabled in the backing TextureRenderer of this TextRenderer.A few graphics cards - * do not behave well when this is enabled, resulting in fuzzy text. Defaults to true. - * - * @return The current smoothing setting. - */ - public boolean getSmoothing() { - return smoothing; - } - - private boolean is15Available(GL gl) { - if (!checkFor_isExtensionAvailable_GL_VERSION_1_5) { - isExtensionAvailable_GL_VERSION_1_5 = gl.isExtensionAvailable(GLExtensions.VERSION_1_5); - checkFor_isExtensionAvailable_GL_VERSION_1_5 = true; - } - return isExtensionAvailable_GL_VERSION_1_5; - } -} diff --git a/src/gov/nasa/worldwind/render/TextRendererCache.java b/src/gov/nasa/worldwind/render/TextRendererCache.java index 5f670f784e..82a710bbe6 100644 --- a/src/gov/nasa/worldwind/render/TextRendererCache.java +++ b/src/gov/nasa/worldwind/render/TextRendererCache.java @@ -27,6 +27,8 @@ */ package gov.nasa.worldwind.render; +import com.jogamp.opengl.util.awt.TextRenderer; + import gov.nasa.worldwind.Disposable; import gov.nasa.worldwind.util.Logging; diff --git a/src/gov/nasa/worldwind/render/ToolTipRenderer.java b/src/gov/nasa/worldwind/render/ToolTipRenderer.java index b9e4b7af0f..13fb44fb69 100644 --- a/src/gov/nasa/worldwind/render/ToolTipRenderer.java +++ b/src/gov/nasa/worldwind/render/ToolTipRenderer.java @@ -30,6 +30,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import javax.swing.*; import javax.swing.border.*; import java.awt.*; diff --git a/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java b/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java index af167bd955..7c410ec413 100644 --- a/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java +++ b/src/gov/nasa/worldwind/symbology/AbstractTacticalSymbol.java @@ -28,6 +28,7 @@ package gov.nasa.worldwind.symbology; +import com.jogamp.opengl.util.awt.TextRenderer; import com.jogamp.opengl.util.texture.*; import com.jogamp.opengl.util.texture.awt.AWTTextureIO; import gov.nasa.worldwind.*; diff --git a/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java b/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java index d24b0dcfb9..0837534eaa 100644 --- a/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java +++ b/src/gov/nasa/worldwind/symbology/TacticalGraphicLabel.java @@ -37,6 +37,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; diff --git a/src/gov/nasa/worldwind/terrain/RectangularTessellator.java b/src/gov/nasa/worldwind/terrain/RectangularTessellator.java index 869e99424a..07ea6ba500 100644 --- a/src/gov/nasa/worldwind/terrain/RectangularTessellator.java +++ b/src/gov/nasa/worldwind/terrain/RectangularTessellator.java @@ -39,6 +39,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.nio.*; import java.util.*; diff --git a/src/gov/nasa/worldwind/util/EGM2008.java b/src/gov/nasa/worldwind/util/EGM2008.java new file mode 100644 index 0000000000..f8e64d9900 --- /dev/null +++ b/src/gov/nasa/worldwind/util/EGM2008.java @@ -0,0 +1,255 @@ +/* + * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The NASA World Wind Java (WWJ) platform is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source + * software: + * + * Jackson Parser – Licensed under Apache 2.0 + * GDAL – Licensed under MIT + * JOGL – Licensed under Berkeley Software Distribution (BSD) + * Gluegen – Licensed under Berkeley Software Distribution (BSD) + * + * A complete listing of 3rd Party software notices and licenses included in + * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party + * notices and licenses PDF found in code directory. + */ + +package gov.nasa.worldwind.util; + +import gov.nasa.worldwind.cache.BasicMemoryCache; +import gov.nasa.worldwind.geom.Angle; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +public class EGM2008 +{ + public static final int N_ROW_MARKERS = 2; // The beginning and end of each row of latitude data is a flag of some sort + public static final int N_LONGITUDE_COLS = 8640 + N_ROW_MARKERS; // Number of float32s in a row of data in the data file. + public static final int N_LATITUDE_ROWS = 4321; // Number of rows. + public static final double GRID_RESOLUTION = 2.5d / 60d; // 2.5 minute grid + public static final double CELL_AREA = GRID_RESOLUTION * GRID_RESOLUTION; + protected static final long CACHE_SIZE = EGM2008.N_LONGITUDE_COLS * 4 * 45 * 15; // Cache 15 degrees worth of offsets. + public static final int N_LAT_ROW_BYTES = N_LONGITUDE_COLS * 4; // Offsets are float32 + + protected String offsetsFilePath; + protected BufferWrapper deltas; + protected final BasicMemoryCache offsetCache; + + protected class GridCell + { + public double x1; + public double y1; + public double x2; + public double y2; + + public GridCell(double x1, double y1) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x1 + EGM2008.GRID_RESOLUTION; + this.y2 = y1 + EGM2008.GRID_RESOLUTION; + } + + public GridCell() + { + this(0, 0); + } + + public GridCell intersect(GridCell that) + { + GridCell intersection = new GridCell(); + intersection.x1 = Math.max(this.x1, that.x1); + intersection.x2 = Math.min(this.x2, that.x2); + intersection.y1 = Math.max(this.y1, that.y1); + intersection.y2 = Math.min(this.y2, that.y2); + return intersection; + } + + @Override + public String toString() + { + return String.format("%5.2f,%5.2f,%5.2f,%5.2f", x1, y1, x2, y2); + } + + public double area() + { + return (this.x2 - this.x1) * (this.y2 - this.y1); + } + } + + /** + * Allows the retrieval of geoid offsets from the EGM2008 2.5 Minute Interpolation Grid sourced from the + * National Geospatial-Intelligence Agency Office of Geomatics (https://earth-info.nga.mil/). + * + * The EGM2008 data path. This data file is not included in the SDK due to its size. The data may be downloaded here: + * https://builds.worldwind.arc.nasa.gov/artifactory/EGM2008-Data/egm2008_25.dat + * + * @param offsetsFilePath a path pointing to a file with the geoid offsets. + */ + public EGM2008(String offsetsFilePath) + { + if (offsetsFilePath == null) + { + String msg = Logging.getMessage("nullValue.PathIsNull"); + Logging.logger().severe(msg); + throw new IllegalArgumentException(msg); + } + File test = new File(offsetsFilePath); + if (test.exists()) + { + this.offsetsFilePath = offsetsFilePath; + } + else + { + Class c = EGM2008.class; + URL url = c.getResource("/" + offsetsFilePath); + if (url != null) + { + test = WWIO.getFileForLocalAddress(url); + this.offsetsFilePath = test.getAbsolutePath(); + } + else + { + this.offsetsFilePath = null; + } + } + this.offsetCache = new BasicMemoryCache((EGM2008.CACHE_SIZE * 8) / 10, EGM2008.CACHE_SIZE); + this.offsetCache.setName(EGM2008.class.getName()); + } + + public float getOffset(Angle lat, Angle lon) throws IOException + { + return this.getOffset((float) lat.degrees, (float) lon.degrees); + } + + public int getLatRow(double lat) + { + // Compute the row in the data file corresponding to a given latitude. + // Latitude row zero in the data corresponds to 90 degrees latitude (north pole) and increases southward + // Longitude column zero in the data corresponds to 0 degrees of longitude and increases eastward + float lat180 = 90f - (float) lat; + return (int) Math.floor(lat180 / EGM2008.GRID_RESOLUTION); + } + + public int getLonCol(double lon) + { + // Compute the column in the data file corresponding to a given latitude and longitude. + // Latitude row zero in the data corresponds to 90 degrees latitude (north pole) and increases southward + // Longitude column zero in the data corresponds to 0 degrees of longitude and increases eastward + float lon360 = (float) lon; + if (lon < 0) + { + lon360 = lon360 + 360; + } + return (int) Math.floor(lon360 / EGM2008.GRID_RESOLUTION); + } + + public float[][] getLatRows(int latRow) throws IOException + { + int[] interpRowIndices = + { + latRow, latRow + 1 + }; + float[][] latDataArray = new float[2][]; + boolean retrievalRequired = false; + for (int i = 0; i < interpRowIndices.length; i++) + { + if (interpRowIndices[i] < EGM2008.N_LATITUDE_ROWS) + { + float[] latData = (float[]) this.offsetCache.getObject(interpRowIndices[i]); + latDataArray[i] = latData; + if (latData == null) + { + retrievalRequired = true; + } + } + } + if (retrievalRequired) + { + try (RandomAccessFile offsetFile = new RandomAccessFile(this.offsetsFilePath, "r")) + { + for (int i = 0; i < interpRowIndices.length; i++) + { + if (interpRowIndices[i] < EGM2008.N_LATITUDE_ROWS && latDataArray[i] == null) + { + offsetFile.seek(interpRowIndices[i] * EGM2008.N_LAT_ROW_BYTES); + byte[] latByteData = new byte[EGM2008.N_LAT_ROW_BYTES]; + offsetFile.read(latByteData); + ByteBuffer latByteBuffer = ByteBuffer.wrap(latByteData).order(ByteOrder.LITTLE_ENDIAN); + FloatBuffer latFloatBuffer = latByteBuffer.asFloatBuffer(); + float[] latData = new float[EGM2008.N_LONGITUDE_COLS]; + latFloatBuffer.get(latData); + this.offsetCache.add(interpRowIndices[i], latData, EGM2008.N_LAT_ROW_BYTES); + latDataArray[i] = latData; + } + } + } + } + return latDataArray; + } + + public float getOffset(double lat, double lon) throws IOException + { + if (this.offsetsFilePath == null) + { + return 0f; + } + int latRow = this.getLatRow(lat); + int lonCol = this.getLonCol(lon); + + float[][] latDataArray = getLatRows(latRow); + + float baseOffset = latDataArray[0][lonCol + EGM2008.N_ROW_MARKERS / 2]; + if (latDataArray[1] == null) + { + return baseOffset; + } + + // Interpolate with surrounding offset cells + float lat180 = 90f - (float) lat; + float lon360 = (float) lon; + if (lon < 0) + { + lon360 = lon360 + 360; + } + GridCell offsetCell = new GridCell(lon360, lat180); + double baseLat = ((double) latRow) * EGM2008.GRID_RESOLUTION; + double baseLon = ((double) lonCol) * EGM2008.GRID_RESOLUTION; + float interpOffset = 0; + for (int x = 0; x < 2; x++) + { + double cellLon = baseLon + ((double) x) * EGM2008.GRID_RESOLUTION; + for (int y = 0; y < 2; y++) + { + float cellOffset = latDataArray[y][lonCol + EGM2008.N_ROW_MARKERS / 2 + x]; + double cellLat = baseLat + ((double) y) * EGM2008.GRID_RESOLUTION; + GridCell interpCell = new GridCell(cellLon, cellLat); + GridCell intersection = offsetCell.intersect(interpCell); + interpOffset += cellOffset * (intersection.area() / EGM2008.CELL_AREA); + } + } + return interpOffset; + } + + public boolean isEGMDataAvailable() + { + return this.offsetsFilePath != null; + } +} diff --git a/src/gov/nasa/worldwind/util/OGLTextRenderer.java b/src/gov/nasa/worldwind/util/OGLTextRenderer.java index 271568cc94..a5b77a291d 100644 --- a/src/gov/nasa/worldwind/util/OGLTextRenderer.java +++ b/src/gov/nasa/worldwind/util/OGLTextRenderer.java @@ -27,6 +27,8 @@ */ package gov.nasa.worldwind.util; +import com.jogamp.opengl.util.awt.TextRenderer; + import gov.nasa.worldwind.render.*; /** diff --git a/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java b/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java index e41b6a8100..0d785e5e3f 100644 --- a/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java +++ b/src/gov/nasa/worldwind/util/PlacemarkClutterFilter.java @@ -34,6 +34,8 @@ import gov.nasa.worldwind.render.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.awt.geom.*; import java.util.*; diff --git a/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java b/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java index f87da8f212..d9325e46f8 100644 --- a/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java +++ b/src/gov/nasa/worldwind/util/tree/BasicTreeLayout.java @@ -28,6 +28,7 @@ package gov.nasa.worldwind.util.tree; +import com.jogamp.opengl.util.awt.TextRenderer; import com.jogamp.opengl.util.texture.TextureCoords; import gov.nasa.worldwind.WWObjectImpl; import gov.nasa.worldwind.avlist.AVKey; diff --git a/src/gov/nasa/worldwind/util/tree/ScrollFrame.java b/src/gov/nasa/worldwind/util/tree/ScrollFrame.java index 6111066332..057fd378fa 100644 --- a/src/gov/nasa/worldwind/util/tree/ScrollFrame.java +++ b/src/gov/nasa/worldwind/util/tree/ScrollFrame.java @@ -28,6 +28,7 @@ package gov.nasa.worldwind.util.tree; +import com.jogamp.opengl.util.awt.TextRenderer; import com.jogamp.opengl.util.texture.*; import gov.nasa.worldwind.*; import gov.nasa.worldwind.avlist.AVKey; diff --git a/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java b/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java index de6ad819d8..23dc2412e0 100644 --- a/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java +++ b/src/gov/nasa/worldwindx/applications/antenna/AntennaAxes.java @@ -37,6 +37,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import javax.xml.stream.*; import java.awt.*; import java.io.IOException; diff --git a/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java b/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java index 5455ebed9f..3503bd4c49 100644 --- a/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java +++ b/src/gov/nasa/worldwindx/applications/sar/render/TrackSegmentInfo.java @@ -34,6 +34,8 @@ import gov.nasa.worldwindx.applications.sar.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; /** diff --git a/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java b/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java index 9444f7228d..b99607e25a 100644 --- a/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java +++ b/src/gov/nasa/worldwindx/applications/sar/segmentplane/SegmentPlaneRenderer.java @@ -40,6 +40,8 @@ import gov.nasa.worldwind.util.*; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import java.awt.*; import java.nio.*; import java.util.*; diff --git a/src/gov/nasa/worldwindx/examples/Annotations.java b/src/gov/nasa/worldwindx/examples/Annotations.java index cd0e8972dc..866ba403c6 100644 --- a/src/gov/nasa/worldwindx/examples/Annotations.java +++ b/src/gov/nasa/worldwindx/examples/Annotations.java @@ -38,6 +38,8 @@ import gov.nasa.worldwindx.examples.util.PowerOfTwoPaddedImage; import com.jogamp.opengl.*; +import com.jogamp.opengl.util.awt.TextRenderer; + import javax.swing.*; import javax.swing.Box; import javax.swing.border.*; diff --git a/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java b/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java index c432b47db4..9ace62fed7 100644 --- a/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java +++ b/src/gov/nasa/worldwindx/examples/ContextMenusOnShapes.java @@ -72,7 +72,6 @@ public void selected(SelectEvent event) { } } - @SuppressWarnings({"UnusedDeclaration"}) protected void highlight(SelectEvent event, Object o) { if (this.lastPickedPlacemark == o) { return; // same thing selected @@ -110,7 +109,7 @@ protected void showContextMenu(SelectEvent event) { } ContextMenu menu = new ContextMenu((Component) event.getSource(), menuInfo); - menu.show(event.getMouseEvent()); + menu.show(event.getAwtMousePt()); } } } @@ -145,7 +144,7 @@ protected void makeMenuItems() { } } - public void show(final MouseEvent event) { + public void show(final Point screenPt) { JPopupMenu popup = new JPopupMenu(); popup.add(this.menuTitleItem); @@ -156,7 +155,7 @@ public void show(final MouseEvent event) { popup.add(subMenu); } - popup.show(sourceComponent, event.getX(), event.getY()); + popup.show(sourceComponent, (int)screenPt.getX(), (int)screenPt.getY()); } } diff --git a/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java b/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java new file mode 100644 index 0000000000..f2b403ca0d --- /dev/null +++ b/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java @@ -0,0 +1,237 @@ +/* + * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The NASA World Wind Java (WWJ) platform is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source + * software: + * + * Jackson Parser – Licensed under Apache 2.0 + * GDAL – Licensed under MIT + * JOGL – Licensed under Berkeley Software Distribution (BSD) + * Gluegen – Licensed under Berkeley Software Distribution (BSD) + * + * A complete listing of 3rd Party software notices and licenses included in + * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party + * notices and licenses PDF found in code directory. + */ +package gov.nasa.worldwindx.examples; + +import gov.nasa.worldwind.Model; +import gov.nasa.worldwind.avlist.AVKey; +import gov.nasa.worldwind.geom.LatLon; +import gov.nasa.worldwind.geom.Position; +import gov.nasa.worldwind.geom.Sector; +import gov.nasa.worldwind.globes.Earth; +import gov.nasa.worldwind.globes.Globe; +import gov.nasa.worldwind.layers.RenderableLayer; +import gov.nasa.worldwind.render.PointPlacemark; +import gov.nasa.worldwind.util.EGM2008; +import java.awt.Dimension; + +import java.io.IOException; +import java.util.ArrayList; +import javax.swing.SwingUtilities; + +/** + * Shows how to apply EGM2008 offsets to Earth elevations. + * + * This EGM2008 data file is not included in the SDK due to its size. The data may be downloaded here: + * https://builds.worldwind.arc.nasa.gov/artifactory/EGM2008-Data/egm2008_25.dat + * + * This example looks for the EGM2008 data in the WorldWind src/config folder by default. + */ +public class EGM2008Offsets extends ApplicationTemplate +{ + public static class AppFrame extends ApplicationTemplate.AppFrame + { + /** + * Attempt to retrieve the best elevations for a specified list of locations. The elevations returned are the best currently + * available for the data set and the area bounding the locations. Since the necessary elevation data might not + * be in memory at the time of the call, this method iterates until the necessary elevation data is in memory + * and can be used to determine the locations elevations. + * + * @param locations a list of locations to determine elevations for + */ + public void loadBestElevations(ArrayList locations) + { + Globe globe = this.getWwd().getModel().getGlobe(); + ArrayList sectors = new ArrayList<>(); + ArrayList> locationsList = new ArrayList<>(); + double delta = 0.0001; + for (LatLon ll : locations) + { + double lat = ll.latitude.degrees; + double lon = ll.longitude.degrees; + sectors.add(Sector.fromDegrees(lat, lat + delta, lon, lon + delta)); + ArrayList sectorLocations = new ArrayList<>(); + sectorLocations.add(ll); + sectorLocations.add(LatLon.fromDegrees(lat + delta, lon + delta)); + locationsList.add(sectorLocations); + } + + double[] targetResolutions = new double[sectors.size()]; + double[] actualResolutions = new double[sectors.size()]; + for (int i = 0, len = sectors.size(); i < len; i++) + { + targetResolutions[i] = globe.getElevationModel().getBestResolution(sectors.get(i)); + } + boolean resolutionsAchieved = false; + double[] elevations = new double[2]; + while (!resolutionsAchieved) + { + for (int i = 0, len = sectors.size(); i < len; i++) + { + actualResolutions[i] = globe.getElevations(sectors.get(i), locationsList.get(i), targetResolutions[i], elevations); + } + + resolutionsAchieved = true; + for (int i = 0, len = actualResolutions.length; i < len && resolutionsAchieved; i++) + { + resolutionsAchieved = actualResolutions[i] <= targetResolutions[i]; + } + if (!resolutionsAchieved) + { + try + { + Thread.sleep(200); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + } + + public AppFrame() + { + Model m = this.wwjPanel.getWwd().getModel(); + Earth earth = (Earth) m.getGlobe(); + final RenderableLayer layer = new RenderableLayer(); + double[] locations = new double[] + { + 37.0, -119.0, + 36.0, -117.016667, + 89.0, 0.0, + -80.0, 0.0, + -90.0, 0.0 + }; + EGM2008 egm2008Offsets = new EGM2008("config/egm2008_25.dat"); + boolean egmAvailable = egm2008Offsets.isEGMDataAvailable(); + if (!egmAvailable) + { + System.out.println("*** EGM 2008 data not available."); + } + + // Run the elevation query in a separate thread to avoid locking up the user interface + Thread t = new Thread(() -> + { + ArrayList elevLocations = new ArrayList<>(); + for (int i = 0; i < locations.length; i += 2) + { + elevLocations.add(LatLon.fromDegrees(locations[i], locations[i + 1])); + } + + loadBestElevations(elevLocations); + + try + { + for (int i = 0; i < locations.length; i += 2) + { + Position pos = Position.fromDegrees(locations[i], locations[i + 1], 0); + PointPlacemark placemark = new PointPlacemark(pos); + String label = String.format("lat: %7.4f, lon: %7.4f", locations[i], locations[i + 1]); + float egmOffset = egm2008Offsets.getOffset(pos.latitude, pos.longitude); + double elevation = earth.getElevation(pos.latitude, pos.longitude); + if (egmAvailable) + { + placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM2008 Offset: %7.4f\nEllipsoid elevation:%7.4f\nEGM2008 Adjusted elevation: %7.4f", + egmOffset, elevation, elevation - egmOffset)); + } + else + { + placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM2008 Offset: N/A\nEllipsoid elevation:%7.4f\nEGM2008 Adjusted elevation: N/A", + elevation)); + } + placemark.setLabelText(label); + layer.addRenderable(placemark); + } + } + catch (IOException iex) + { + iex.printStackTrace(); + } + + SwingUtilities.invokeLater(() -> + { + System.out.println("Elevations retrieved"); + getWwd().redraw(); + }); + }); + t.start(); + + try + { + // Test offsets for some coordinates + float lat = 47; + float lon = -94; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + lat = 37; + lon = -119; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + // Try previous coordinates to verify caching + lat = 47; + lon = -94; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + lat = 37; + lon = -119; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + lat = 47.02f; + lon = -94.02f; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + float gridResolution = (float) EGM2008.GRID_RESOLUTION; + lat = 47 + gridResolution; + lon = -94 - gridResolution; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + lat = 36.0f; + lon = -117.0f; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + lat = 36.0f; + lon = -117.041666666667f; + System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon)); + + System.out.println(); + } + catch (IOException iex) + { + iex.printStackTrace(); + } + + this.wwjPanel.toolTipController.setAnnotationSize(new Dimension(500, 0)); + insertBeforeCompass(getWwd(), layer); + } + } + + public static void main(String[] args) + { + ApplicationTemplate.start("WorldWind EGM2008 Offsets", AppFrame.class); + } +} diff --git a/src/gov/nasa/worldwindx/examples/EGM96Offsets.java b/src/gov/nasa/worldwindx/examples/EGM96Offsets.java index a64d11b1bc..39b7adf6b0 100644 --- a/src/gov/nasa/worldwindx/examples/EGM96Offsets.java +++ b/src/gov/nasa/worldwindx/examples/EGM96Offsets.java @@ -2,39 +2,50 @@ * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. - * + * * The NASA World Wind Java (WWJ) platform is licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. - * + * * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source * software: - * + * * Jackson Parser – Licensed under Apache 2.0 * GDAL – Licensed under MIT * JOGL – Licensed under Berkeley Software Distribution (BSD) * Gluegen – Licensed under Berkeley Software Distribution (BSD) - * + * * A complete listing of 3rd Party software notices and licenses included in * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party * notices and licenses PDF found in code directory. */ - package gov.nasa.worldwindx.examples; import gov.nasa.worldwind.Model; +import gov.nasa.worldwind.avlist.AVKey; +import gov.nasa.worldwind.geom.LatLon; +import gov.nasa.worldwind.geom.Position; +import gov.nasa.worldwind.geom.Sector; import gov.nasa.worldwind.globes.Earth; +import gov.nasa.worldwind.globes.Globe; +import gov.nasa.worldwind.layers.RenderableLayer; +import gov.nasa.worldwind.render.PointPlacemark; +import gov.nasa.worldwind.util.EGM96; +import java.awt.Dimension; import java.io.IOException; +import java.util.ArrayList; +import javax.swing.SwingUtilities; /** * Shows how to apply EGM96 offsets to the Earth. + * * @author tag * @version $Id: EGM96Offsets.java 1501 2013-07-11 15:59:11Z tgaskins $ */ @@ -42,17 +53,118 @@ public class EGM96Offsets extends ApplicationTemplate { public static class AppFrame extends ApplicationTemplate.AppFrame { + /** + * Attempt to retrieve the best elevations for a specified list of locations. The elevations returned are the best currently + * available for the data set and the area bounding the locations. Since the necessary elevation data might not + * be in memory at the time of the call, this method iterates until the necessary elevation data is in memory + * and can be used to determine the locations elevations. + * + * @param locations a list of locations to determine elevations for + */ + public void loadBestElevations(ArrayList locations) + { + Globe globe = this.getWwd().getModel().getGlobe(); + ArrayList sectors = new ArrayList<>(); + ArrayList> locationsList = new ArrayList<>(); + double delta = 0.0001; + for (LatLon ll : locations) + { + double lat = ll.latitude.degrees; + double lon = ll.longitude.degrees; + sectors.add(Sector.fromDegrees(lat, lat + delta, lon, lon + delta)); + ArrayList sectorLocations = new ArrayList<>(); + sectorLocations.add(ll); + sectorLocations.add(LatLon.fromDegrees(lat + delta, lon + delta)); + locationsList.add(sectorLocations); + } + + double[] targetResolutions = new double[sectors.size()]; + double[] actualResolutions = new double[sectors.size()]; + for (int i = 0, len = sectors.size(); i < len; i++) + { + targetResolutions[i] = globe.getElevationModel().getBestResolution(sectors.get(i)); + } + boolean resolutionsAchieved = false; + double[] elevations = new double[2]; + while (!resolutionsAchieved) + { + for (int i = 0, len = sectors.size(); i < len; i++) + { + actualResolutions[i] = globe.getElevations(sectors.get(i), locationsList.get(i), targetResolutions[i], elevations); + } + + resolutionsAchieved = true; + for (int i = 0, len = actualResolutions.length; i < len && resolutionsAchieved; i++) + { + resolutionsAchieved = actualResolutions[i] <= targetResolutions[i]; + } + if (!resolutionsAchieved) + { + try + { + Thread.sleep(200); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + } + public AppFrame() { Model m = this.wwjPanel.getWwd().getModel(); + Earth earth = (Earth) m.getGlobe(); try { - ((Earth) m.getGlobe()).applyEGMA96Offsets("config/EGM96.dat"); + earth.applyEGMA96Offsets("config/EGM96.dat"); } catch (IOException e) { e.printStackTrace(); } + final RenderableLayer layer = new RenderableLayer(); + double[] locations = new double[] + { + 37.0, -119.0, + 36.0, -117.016667, + 89.0, 0.0, + -80.0, 0.0, + -90.0, 0.0 + }; + Thread t = new Thread(() -> + { + ArrayList elevLocations = new ArrayList<>(); + for (int i = 0; i < locations.length; i += 2) + { + elevLocations.add(LatLon.fromDegrees(locations[i], locations[i + 1])); + } + + loadBestElevations(elevLocations); + + EGM96 egm96Offsets = earth.getEGM96(); + for (int i = 0; i < locations.length; i += 2) + { + Position pos = Position.fromDegrees(locations[i], locations[i + 1], 0); + PointPlacemark placemark = new PointPlacemark(pos); + String label = String.format("lat: %7.4f, lon: %7.4f", locations[i], locations[i + 1]); + placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM96 Offset: %7.4f\nEGM96 Adjusted elevation: %7.4f", + egm96Offsets.getOffset(pos.latitude, pos.longitude), + earth.getElevation(pos.latitude, pos.longitude))); + placemark.setLabelText(label); + layer.addRenderable(placemark); + } + SwingUtilities.invokeLater(() -> + { + System.out.println("Elevations retrieved"); + getWwd().redraw(); + }); + }); + t.start(); + + this.wwjPanel.toolTipController.setAnnotationSize(new Dimension(500, 0)); + insertBeforeCompass(getWwd(), layer); } } diff --git a/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java b/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java index 1daa69884b..69b42dfc81 100644 --- a/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java +++ b/src/gov/nasa/worldwindx/examples/analytics/AnalyticSurfaceLegend.java @@ -37,6 +37,8 @@ import java.text.Format; import java.util.ArrayList; +import com.jogamp.opengl.util.awt.TextRenderer; + /** * @author dcollins * @version $Id: AnalyticSurfaceLegend.java 2053 2014-06-10 20:16:57Z tgaskins $ diff --git a/src/gov/nasa/worldwindx/examples/util/StatusLayer.java b/src/gov/nasa/worldwindx/examples/util/StatusLayer.java index 9544a97cee..76f9d4c6e5 100644 --- a/src/gov/nasa/worldwindx/examples/util/StatusLayer.java +++ b/src/gov/nasa/worldwindx/examples/util/StatusLayer.java @@ -27,6 +27,7 @@ */ package gov.nasa.worldwindx.examples.util; +import com.jogamp.opengl.util.awt.TextRenderer; import com.jogamp.opengl.util.texture.*; import gov.nasa.worldwind.*; import gov.nasa.worldwind.event.*; diff --git a/src/gov/nasa/worldwindx/examples/util/ToolTipController.java b/src/gov/nasa/worldwindx/examples/util/ToolTipController.java index 0cadd531d9..05e58a7e63 100644 --- a/src/gov/nasa/worldwindx/examples/util/ToolTipController.java +++ b/src/gov/nasa/worldwindx/examples/util/ToolTipController.java @@ -2,25 +2,25 @@ * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. - * + * * The NASA World Wind Java (WWJ) platform is licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. - * + * * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source * software: - * + * * Jackson Parser – Licensed under Apache 2.0 * GDAL – Licensed under MIT * JOGL – Licensed under Berkeley Software Distribution (BSD) * Gluegen – Licensed under Berkeley Software Distribution (BSD) - * + * * A complete listing of 3rd Party software notices and licenses included in * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party * notices and licenses PDF found in code directory. @@ -34,6 +34,7 @@ import gov.nasa.worldwindx.examples.ApplicationTemplate; import gov.nasa.worldwind.layers.*; import gov.nasa.worldwind.util.*; +import java.awt.Dimension; /** * Controls display of tool tips on picked objects. Any shape implementing {@link AVList} can participate. Shapes @@ -52,6 +53,7 @@ public class ToolTipController implements SelectListener, Disposable protected Object lastHoverObject; protected AnnotationLayer layer; protected ToolTipAnnotation annotation; + protected Dimension annotationSize = null; /** * Create a controller for a specified {@link WorldWindow} that displays tool tips on hover and/or rollover. @@ -84,6 +86,16 @@ public ToolTipController(WorldWindow wwd) this.wwd.addSelectListener(this); } + public Dimension getAnnotationSize() + { + return this.annotationSize; + } + + public void setAnnotationSize(Dimension value) + { + this.annotationSize = value; + } + public void dispose() { this.wwd.removeSelectListener(this); @@ -91,14 +103,14 @@ public void dispose() protected String getHoverText(SelectEvent event) { - return event.getTopObject() != null && event.getTopObject() instanceof AVList ? - ((AVList) event.getTopObject()).getStringValue(this.hoverKey) : null; + return event.getTopObject() != null && event.getTopObject() instanceof AVList + ? ((AVList) event.getTopObject()).getStringValue(this.hoverKey) : null; } protected String getRolloverText(SelectEvent event) { - return event.getTopObject() != null && event.getTopObject() instanceof AVList ? - ((AVList) event.getTopObject()).getStringValue(this.rolloverKey) : null; + return event.getTopObject() != null && event.getTopObject() instanceof AVList + ? ((AVList) event.getTopObject()).getStringValue(this.rolloverKey) : null; } public void selected(SelectEvent event) @@ -167,6 +179,7 @@ protected void showToolTip(SelectEvent event, String text) else { annotation = new ToolTipAnnotation(text); + annotation.getAttributes().setSize(this.annotationSize); } if (layer == null)