Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / PlaceNameRenderer.java
blobab50781013fe5061d6297f9714cae6b71e77ae52
1 /*
2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
4 All Rights Reserved.
5 */
6 package gov.nasa.worldwind;
8 import com.sun.opengl.util.j2d.*;
9 import gov.nasa.worldwind.geom.*;
10 import gov.nasa.worldwind.geom.Point;
12 import javax.media.opengl.*;
13 import javax.media.opengl.glu.*;
14 import java.awt.*;
15 import java.awt.geom.*;
16 import java.util.*;
17 import java.util.logging.Level;
19 /**
20 * @author dcollins
21 * @version $Id: PlaceNameRenderer.java 1774 2007-05-08 01:03:37Z dcollins $
23 public class PlaceNameRenderer implements Disposable
25 private static final Font defaultFont = Font.decode("Arial-12-PLAIN");
26 private static final Color defaultColor = Color.white;
27 private final Map<Font, TextRenderer> textRenderers = new HashMap<Font, TextRenderer>();
28 private TextRenderer lastTextRenderer = null;
29 private final GLU glu = new GLU();
31 public PlaceNameRenderer()
35 public void dispose()
37 for (TextRenderer textRenderer : textRenderers.values())
39 if (textRenderer != null)
40 textRenderer.dispose();
44 public void render(DrawContext dc, Iterator<PlaceName> placeNames, boolean enableDepthTest)
46 this.drawMany(dc, placeNames, enableDepthTest);
49 public void render(DrawContext dc, PlaceName placeName, Point placeNamePoint, boolean enableDepthTest)
51 if (!isNameValid(placeName, false))
52 return;
54 this.drawOne(dc, placeName, placeNamePoint, enableDepthTest);
57 private void drawMany(DrawContext dc, Iterator<PlaceName> placeNames, boolean enableDepthTest)
59 if (dc == null)
61 String msg = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
62 WorldWind.logger().log(Level.FINE, msg);
63 throw new IllegalArgumentException(msg);
66 if (dc.getVisibleSector() == null)
67 return;
69 SectorGeometryList geos = dc.getSurfaceGeometry();
70 if (geos == null)
71 return;
73 if (placeNames == null)
75 String msg = WorldWind.retrieveErrMsg("nullValue.Iterator");
76 WorldWind.logger().log(Level.FINE, msg);
77 throw new IllegalArgumentException(msg);
80 if (!placeNames.hasNext())
81 return;
83 Frustum frustumInModelCoords = dc.getView().getFrustumInModelCoordinates();
84 double horizon = dc.getView().computeHorizonDistance();
86 this.beginDrawNames(dc, enableDepthTest);
88 while (placeNames.hasNext())
90 PlaceName placeName = placeNames.next();
91 if (!isNameValid(placeName, true))
92 continue;
94 if (!placeName.isVisible())
95 continue;
97 Angle lat = placeName.getPosition().getLatitude();
98 Angle lon = placeName.getPosition().getLongitude();
100 if (!dc.getVisibleSector().contains(lat, lon))
101 continue;
103 Point namePoint = geos.getSurfacePoint(lat, lon, placeName.getPosition().getElevation());
104 if (namePoint == null)
105 continue;
107 double eyeDistance = dc.getView().getEyePoint().distanceTo(namePoint);
108 if (eyeDistance > horizon)
109 continue;
111 if (!frustumInModelCoords.contains(namePoint))
112 continue;
114 this.drawName(dc, placeName, namePoint, enableDepthTest);
117 this.endDrawNames(dc);
120 private void drawOne(DrawContext dc, PlaceName placeName, Point namePoint, boolean enableDepthTest)
122 if (dc == null)
124 String msg = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
125 WorldWind.logger().log(Level.FINE, msg);
126 throw new IllegalArgumentException(msg);
128 if (dc.getView() == null)
130 String msg = WorldWind.retrieveErrMsg("nullValue.ViewIsNull");
131 WorldWind.logger().log(Level.FINE, msg);
132 throw new IllegalArgumentException(msg);
135 if (dc.getVisibleSector() == null)
136 return;
138 SectorGeometryList geos = dc.getSurfaceGeometry();
139 if (geos == null)
140 return;
142 if (!placeName.isVisible())
143 return;
145 if (namePoint == null)
147 if (placeName.getPosition() == null)
148 return;
150 Angle lat = placeName.getPosition().getLatitude();
151 Angle lon = placeName.getPosition().getLongitude();
153 if (!dc.getVisibleSector().contains(lat, lon))
154 return;
156 namePoint = geos.getSurfacePoint(lat, lon, placeName.getPosition().getElevation());
157 if (namePoint == null)
158 return;
161 double horizon = dc.getView().computeHorizonDistance();
162 double eyeDistance = dc.getView().getEyePoint().distanceTo(namePoint);
163 if (eyeDistance > horizon)
164 return;
166 if (!dc.getView().getFrustumInModelCoordinates().contains(namePoint))
167 return;
169 this.beginDrawNames(dc, enableDepthTest);
170 this.drawName(dc, placeName, namePoint, enableDepthTest);
171 this.endDrawNames(dc);
174 private static boolean isNameValid(PlaceName placeName, boolean checkPosition)
176 if (placeName == null || placeName.getText() == null)
177 return false;
179 //noinspection RedundantIfStatement
180 if (checkPosition && placeName.getPosition() == null)
181 return false;
183 return true;
186 private final int[] viewportArray = new int[4];
188 private void beginDrawNames(DrawContext dc, boolean enableDepthTest)
190 GL gl = dc.getGL();
191 int attribBits =
192 GL.GL_ENABLE_BIT
193 | GL.GL_COLOR_BUFFER_BIT // for alpha test func and ref, and blend
194 | GL.GL_CURRENT_BIT // for current color
195 | GL.GL_TRANSFORM_BIT // for modelview and perspective
196 | (enableDepthTest ? GL.GL_VIEWPORT_BIT | GL.GL_DEPTH_BUFFER_BIT : 0); // for depth func, depth range
197 gl.glPushAttrib(attribBits);
199 gl.glGetIntegerv(GL.GL_VIEWPORT, viewportArray, 0);
200 gl.glMatrixMode(GL.GL_PROJECTION);
201 gl.glPushMatrix();
202 gl.glLoadIdentity();
203 glu.gluOrtho2D(0, viewportArray[2], 0, viewportArray[3]);
204 gl.glMatrixMode(GL.GL_MODELVIEW);
205 gl.glPushMatrix();
206 gl.glLoadIdentity();
207 gl.glMatrixMode(GL.GL_TEXTURE);
208 gl.glPushMatrix();
209 gl.glLoadIdentity();
211 // Enable the depth test but don't write to the depth buffer.
212 if (enableDepthTest)
214 gl.glEnable(GL.GL_DEPTH_TEST);
215 gl.glDepthFunc(GL.GL_LESS);
216 gl.glDepthMask(false);
218 else
220 gl.glDisable(GL.GL_DEPTH_TEST);
222 // Suppress polygon culling.
223 gl.glDisable(GL.GL_CULL_FACE);
224 // Suppress any fully transparent image pixels
225 final float ALPHA_EPSILON = 0.001f;
226 gl.glEnable(GL.GL_ALPHA_TEST);
227 gl.glAlphaFunc(GL.GL_GREATER, ALPHA_EPSILON);
230 private void endDrawNames(DrawContext dc)
232 if (this.lastTextRenderer != null)
234 this.lastTextRenderer.end3DRendering();
235 this.lastTextRenderer = null;
238 GL gl = dc.getGL();
240 gl.glMatrixMode(GL.GL_PROJECTION);
241 gl.glPopMatrix();
242 gl.glMatrixMode(GL.GL_MODELVIEW);
243 gl.glPopMatrix();
244 gl.glMatrixMode(GL.GL_TEXTURE);
245 gl.glPopMatrix();
247 gl.glPopAttrib();
250 private Point drawName(DrawContext dc, PlaceName name, Point namePoint, boolean enableDepthTest)
252 if (namePoint == null)
254 String msg = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
255 WorldWind.logger().log(Level.FINE, msg);
256 return null;
259 final String text = name.getText();
260 if (text == null)
261 return null;
263 final Point screenPoint = dc.getView().project(namePoint);
264 if (screenPoint == null)
265 return null;
267 if (enableDepthTest)
268 this.setDepthFunc(dc, screenPoint);
270 Font font = name.getFont();
271 if (font == null)
272 font = defaultFont;
274 TextRenderer textRenderer = this.textRenderers.get(font);
275 if (textRenderer == null)
276 textRenderer = this.initializeTextRenderer(font);
277 if (textRenderer != this.lastTextRenderer)
279 if (this.lastTextRenderer != null)
280 this.lastTextRenderer.end3DRendering();
281 textRenderer.begin3DRendering();
282 this.lastTextRenderer = textRenderer;
285 Rectangle2D nameBound = textRenderer.getBounds(text);
286 int x = (int) (screenPoint.x() - nameBound.getWidth() / 2d);
287 int y = (int) screenPoint.y();
289 Color color = name.getColor();
290 if (color == null)
291 color = defaultColor;
293 this.setBackgroundColor(textRenderer, color);
294 textRenderer.draw(text, x + 1, y - 1);
295 textRenderer.setColor(color);
296 textRenderer.draw(text, x, y);
298 return screenPoint;
301 private void setDepthFunc(DrawContext dc, Point screenPoint)
303 double depth = screenPoint.z() - 8d * 0.00048875809d;
304 depth = (depth < 0) ? 0 : ((depth > 1) ? 1 : depth);
305 dc.getGL().glDepthRange(depth, depth);
308 private final float[] compArray = new float[4];
310 private void setBackgroundColor(TextRenderer textRenderer, Color color)
312 Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), compArray);
313 if (compArray[2] > 0.5)
314 textRenderer.setColor(0, 0, 0, 0.7f);
315 else
316 textRenderer.setColor(1, 1, 1, 0.7f);
319 private TextRenderer initializeTextRenderer(Font font)
321 TextRenderer textRenderer = new TextRenderer(font, true, true);
322 TextRenderer oldTextRenderer;
323 oldTextRenderer = this.textRenderers.put(font, textRenderer);
324 if (oldTextRenderer != null)
325 oldTextRenderer.dispose();
326 return textRenderer;
329 public String toString()
331 return WorldWind.retrieveErrMsg("layers.PlaceNameLayer.Name");