Worldwind public release 0.2.1
[worldwind-tracker.git] / gov / nasa / worldwind / PlaceNameRenderer.java
blobc647839eaa95bae1221f9963b4a0004524c261da
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.*;
11 import javax.media.opengl.*;
12 import javax.media.opengl.glu.*;
13 import java.awt.*;
14 import java.awt.geom.*;
15 import java.util.*;
16 import java.util.logging.Level;
18 /**
19 * @author dcollins
20 * @version $Id: PlaceNameRenderer.java 1994 2007-06-11 16:33:33Z dcollins $
22 public class PlaceNameRenderer implements Disposable
24 private static final Font defaultFont = Font.decode("Arial-12-PLAIN");
25 private static final Color defaultColor = Color.white;
26 private final Map<Font, TextRenderer> textRenderers = new HashMap<Font, TextRenderer>();
27 private TextRenderer lastTextRenderer = null;
28 private final GLU glu = new GLU();
30 public PlaceNameRenderer()
34 public void dispose()
36 for (TextRenderer textRenderer : textRenderers.values())
38 if (textRenderer != null)
39 textRenderer.dispose();
43 public void render(DrawContext dc, Iterator<PlaceName> placeNames, boolean enableDepthTest)
45 this.drawMany(dc, placeNames, enableDepthTest);
48 public void render(DrawContext dc, PlaceName placeName, Vec4 placeNamePoint, boolean enableDepthTest)
50 if (!isNameValid(placeName, false))
51 return;
53 this.drawOne(dc, placeName, placeNamePoint, enableDepthTest);
56 private void drawMany(DrawContext dc, Iterator<PlaceName> placeNames, boolean enableDepthTest)
58 if (dc == null)
60 String msg = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
61 WorldWind.logger().log(Level.FINE, msg);
62 throw new IllegalArgumentException(msg);
65 if (dc.getVisibleSector() == null)
66 return;
68 SectorGeometryList geos = dc.getSurfaceGeometry();
69 if (geos == null)
70 return;
72 if (placeNames == null)
74 String msg = WorldWind.retrieveErrMsg("nullValue.Iterator");
75 WorldWind.logger().log(Level.FINE, msg);
76 throw new IllegalArgumentException(msg);
79 if (!placeNames.hasNext())
80 return;
82 Frustum frustumInModelCoords = dc.getView().getFrustumInModelCoordinates();
83 double horizon = dc.getView().computeHorizonDistance();
85 this.beginDrawNames(dc, enableDepthTest);
87 while (placeNames.hasNext())
89 PlaceName placeName = placeNames.next();
90 if (!isNameValid(placeName, true))
91 continue;
93 if (!placeName.isVisible())
94 continue;
96 Angle lat = placeName.getPosition().getLatitude();
97 Angle lon = placeName.getPosition().getLongitude();
99 if (!dc.getVisibleSector().contains(lat, lon))
100 continue;
102 Vec4 namePoint = geos.getSurfacePoint(lat, lon, placeName.getPosition().getElevation());
103 if (namePoint == null)
104 continue;
106 double eyeDistance = dc.getView().getEyePoint().distanceTo3(namePoint);
107 if (eyeDistance > horizon)
108 continue;
110 if (!frustumInModelCoords.contains(namePoint))
111 continue;
113 this.drawName(dc, placeName, namePoint, enableDepthTest);
116 this.endDrawNames(dc);
119 private void drawOne(DrawContext dc, PlaceName placeName, Vec4 namePoint, boolean enableDepthTest)
121 if (dc == null)
123 String msg = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
124 WorldWind.logger().log(Level.FINE, msg);
125 throw new IllegalArgumentException(msg);
127 if (dc.getView() == null)
129 String msg = WorldWind.retrieveErrMsg("nullValue.ViewIsNull");
130 WorldWind.logger().log(Level.FINE, msg);
131 throw new IllegalArgumentException(msg);
134 if (dc.getVisibleSector() == null)
135 return;
137 SectorGeometryList geos = dc.getSurfaceGeometry();
138 if (geos == null)
139 return;
141 if (!placeName.isVisible())
142 return;
144 if (namePoint == null)
146 if (placeName.getPosition() == null)
147 return;
149 Angle lat = placeName.getPosition().getLatitude();
150 Angle lon = placeName.getPosition().getLongitude();
152 if (!dc.getVisibleSector().contains(lat, lon))
153 return;
155 namePoint = geos.getSurfacePoint(lat, lon, placeName.getPosition().getElevation());
156 if (namePoint == null)
157 return;
160 double horizon = dc.getView().computeHorizonDistance();
161 double eyeDistance = dc.getView().getEyePoint().distanceTo3(namePoint);
162 if (eyeDistance > horizon)
163 return;
165 if (!dc.getView().getFrustumInModelCoordinates().contains(namePoint))
166 return;
168 this.beginDrawNames(dc, enableDepthTest);
169 this.drawName(dc, placeName, namePoint, enableDepthTest);
170 this.endDrawNames(dc);
173 private static boolean isNameValid(PlaceName placeName, boolean checkPosition)
175 if (placeName == null || placeName.getText() == null)
176 return false;
178 //noinspection RedundantIfStatement
179 if (checkPosition && placeName.getPosition() == null)
180 return false;
182 return true;
185 private final int[] viewportArray = new int[4];
187 private void beginDrawNames(DrawContext dc, boolean enableDepthTest)
189 GL gl = dc.getGL();
190 int attribBits =
191 GL.GL_ENABLE_BIT
192 | GL.GL_COLOR_BUFFER_BIT // for alpha test func and ref, and blend
193 | GL.GL_CURRENT_BIT // for current color
194 | GL.GL_TRANSFORM_BIT // for modelview and perspective
195 | (enableDepthTest ? GL.GL_VIEWPORT_BIT | GL.GL_DEPTH_BUFFER_BIT : 0); // for depth func, depth range
196 gl.glPushAttrib(attribBits);
198 gl.glGetIntegerv(GL.GL_VIEWPORT, viewportArray, 0);
199 gl.glMatrixMode(GL.GL_PROJECTION);
200 gl.glPushMatrix();
201 gl.glLoadIdentity();
202 glu.gluOrtho2D(0, viewportArray[2], 0, viewportArray[3]);
203 gl.glMatrixMode(GL.GL_MODELVIEW);
204 gl.glPushMatrix();
205 gl.glLoadIdentity();
206 gl.glMatrixMode(GL.GL_TEXTURE);
207 gl.glPushMatrix();
208 gl.glLoadIdentity();
210 // Enable the depth test but don't write to the depth buffer.
211 if (enableDepthTest)
213 gl.glEnable(GL.GL_DEPTH_TEST);
214 gl.glDepthFunc(GL.GL_LESS);
215 gl.glDepthMask(false);
217 else
219 gl.glDisable(GL.GL_DEPTH_TEST);
221 // Suppress polygon culling.
222 gl.glDisable(GL.GL_CULL_FACE);
223 // Suppress any fully transparent image pixels
224 final float ALPHA_EPSILON = 0.001f;
225 gl.glEnable(GL.GL_ALPHA_TEST);
226 gl.glAlphaFunc(GL.GL_GREATER, ALPHA_EPSILON);
229 private void endDrawNames(DrawContext dc)
231 if (this.lastTextRenderer != null)
233 this.lastTextRenderer.end3DRendering();
234 this.lastTextRenderer = null;
237 GL gl = dc.getGL();
239 gl.glMatrixMode(GL.GL_PROJECTION);
240 gl.glPopMatrix();
241 gl.glMatrixMode(GL.GL_MODELVIEW);
242 gl.glPopMatrix();
243 gl.glMatrixMode(GL.GL_TEXTURE);
244 gl.glPopMatrix();
246 gl.glPopAttrib();
249 private Vec4 drawName(DrawContext dc, PlaceName name, Vec4 namePoint, boolean enableDepthTest)
251 if (namePoint == null)
253 String msg = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
254 WorldWind.logger().log(Level.FINE, msg);
255 return null;
258 final String text = name.getText();
259 if (text == null)
260 return null;
262 final Vec4 screenPoint = dc.getView().project(namePoint);
263 if (screenPoint == null)
264 return null;
266 if (enableDepthTest)
267 this.setDepthFunc(dc, screenPoint);
269 Font font = name.getFont();
270 if (font == null)
271 font = defaultFont;
273 TextRenderer textRenderer = this.textRenderers.get(font);
274 if (textRenderer == null)
275 textRenderer = this.initializeTextRenderer(font);
276 if (textRenderer != this.lastTextRenderer)
278 if (this.lastTextRenderer != null)
279 this.lastTextRenderer.end3DRendering();
280 textRenderer.begin3DRendering();
281 this.lastTextRenderer = textRenderer;
284 Rectangle2D nameBound = textRenderer.getBounds(text);
285 int x = (int) (screenPoint.x - nameBound.getWidth() / 2d);
286 int y = (int) screenPoint.y;
288 Color color = name.getColor();
289 if (color == null)
290 color = defaultColor;
292 this.setBackgroundColor(textRenderer, color);
293 textRenderer.draw(text, x + 1, y - 1);
294 textRenderer.setColor(color);
295 textRenderer.draw(text, x, y);
297 return screenPoint;
300 private void setDepthFunc(DrawContext dc, Vec4 screenPoint)
302 double depth = screenPoint.z - (8d * 0.00048875809d);
303 depth = (depth < 0) ? 0 : ((depth > 1) ? 1 : depth);
304 dc.getGL().glDepthRange(depth, depth);
307 private final float[] compArray = new float[4];
309 private void setBackgroundColor(TextRenderer textRenderer, Color color)
311 Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), compArray);
312 if (compArray[2] > 0.5)
313 textRenderer.setColor(0, 0, 0, 0.7f);
314 else
315 textRenderer.setColor(1, 1, 1, 0.7f);
318 private TextRenderer initializeTextRenderer(Font font)
320 TextRenderer textRenderer = new TextRenderer(font, true, true);
321 TextRenderer oldTextRenderer;
322 oldTextRenderer = this.textRenderers.put(font, textRenderer);
323 if (oldTextRenderer != null)
324 oldTextRenderer.dispose();
325 return textRenderer;
328 public String toString()
330 return WorldWind.retrieveErrMsg("layers.PlaceNameLayer.Name");