Update to Worldwind release 0.4.1
[worldwind-tracker.git] / gov / nasa / worldwind / render / IconRenderer.java
blobca9f1d7e315d31f2331772f263190e164f0adcc6
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.render;
9 import com.sun.opengl.util.j2d.TextRenderer;
10 import com.sun.opengl.util.texture.*;
11 import gov.nasa.worldwind.Locatable;
12 import gov.nasa.worldwind.exception.WWRuntimeException;
13 import gov.nasa.worldwind.geom.*;
14 import gov.nasa.worldwind.globes.SectorGeometryList;
15 import gov.nasa.worldwind.layers.Layer;
16 import gov.nasa.worldwind.pick.PickSupport;
17 import gov.nasa.worldwind.util.Logging;
19 import javax.media.opengl.GL;
20 import java.awt.*;
21 import java.awt.image.*;
22 import java.util.*;
23 import java.util.logging.Level;
25 /**
26 * @author tag
27 * @version $Id: IconRenderer.java 3631 2007-11-28 00:46:57Z tgaskins $
29 public class IconRenderer
31 private Pedestal pedestal;
32 private PickSupport pickSupport = new PickSupport();
33 private HashMap<Font, ToolTipRenderer> toolTipRenderers = new HashMap<Font, ToolTipRenderer>();
35 public IconRenderer()
39 public Pedestal getPedestal()
41 return pedestal;
44 public void setPedestal(Pedestal pedestal)
46 this.pedestal = pedestal;
49 private static boolean isIconValid(WWIcon icon, boolean checkPosition)
51 if (icon == null || icon.getImageSource() == null)
52 return false;
54 //noinspection RedundantIfStatement
55 if (checkPosition && icon.getPosition() == null)
56 return false;
58 return true;
61 public void pick(DrawContext dc, Iterable<WWIcon> icons, java.awt.Point pickPoint, Layer layer)
63 this.drawMany(dc, icons);
66 public void pick(DrawContext dc, WWIcon icon, Vec4 iconPoint, java.awt.Point pickPoint, Layer layer)
68 if (!isIconValid(icon, false))
69 return;
71 this.drawOne(dc, icon, iconPoint);
74 public void render(DrawContext dc, Iterable<WWIcon> icons)
76 this.drawMany(dc, icons);
79 public void render(DrawContext dc, WWIcon icon, Vec4 iconPoint)
81 if (!isIconValid(icon, false))
82 return;
84 this.drawOne(dc, icon, iconPoint);
87 private void drawMany(DrawContext dc, Iterable<WWIcon> icons)
89 if (dc == null)
91 String msg = Logging.getMessage("nullValue.DrawContextIsNull");
92 Logging.logger().severe(msg);
93 throw new IllegalArgumentException(msg);
96 if (dc.getVisibleSector() == null)
97 return;
99 SectorGeometryList geos = dc.getSurfaceGeometry();
100 //noinspection RedundantIfStatement
101 if (geos == null)
102 return;
104 if (icons == null)
106 String msg = Logging.getMessage("nullValue.IconIterator");
107 Logging.logger().severe(msg);
108 throw new IllegalArgumentException(msg);
111 Iterator<WWIcon> iterator = icons.iterator();
113 if (!iterator.hasNext())
114 return;
116 while (iterator.hasNext())
118 WWIcon icon = iterator.next();
119 if (!isIconValid(icon, true))
120 continue;
122 if (!icon.isVisible())
123 continue;
125 // Determine Cartesian position from the surface geometry if the icon is near the surface,
126 // otherwise draw it from the globe.
127 Position pos = icon.getPosition();
128 Vec4 iconPoint = null;
129 if (pos.getElevation() < dc.getGlobe().getMaxElevation())
130 iconPoint = dc.getSurfaceGeometry().getSurfacePoint(icon.getPosition());
131 if (iconPoint == null)
132 iconPoint = dc.getGlobe().computePointFromPosition(icon.getPosition());
134 // The icons aren't drawn here, but added to the ordered queue to be drawn back-to-front.
135 double eyeDistance = icon.isAlwaysOnTop() ? 0 : dc.getView().getEyePoint().distanceTo3(iconPoint);
136 dc.addOrderedRenderable(new OrderedIcon(icon, iconPoint, eyeDistance));
138 if (icon.isShowToolTip())
139 this.addToolTip(dc, icon, iconPoint);
143 private void drawOne(DrawContext dc, WWIcon icon, Vec4 iconPoint)
145 if (dc == null)
147 String msg = Logging.getMessage("nullValue.DrawContextIsNull");
148 Logging.logger().severe(msg);
149 throw new IllegalArgumentException(msg);
152 if (dc.getVisibleSector() == null)
153 return;
155 SectorGeometryList geos = dc.getSurfaceGeometry();
156 //noinspection RedundantIfStatement
157 if (geos == null)
158 return;
160 if (!icon.isVisible())
161 return;
163 if (iconPoint == null)
165 Angle lat = icon.getPosition().getLatitude();
166 Angle lon = icon.getPosition().getLongitude();
168 if (!dc.getVisibleSector().contains(lat, lon))
169 return;
171 iconPoint = dc.getSurfaceGeometry().getSurfacePoint(lat, lon, icon.getPosition().getElevation());
172 if (iconPoint == null)
173 return;
176 if (!dc.getView().getFrustumInModelCoordinates().contains(iconPoint))
177 return;
179 double horizon = dc.getView().computeHorizonDistance();
180 double eyeDistance = icon.isAlwaysOnTop() ? 0 : dc.getView().getEyePoint().distanceTo3(iconPoint);
181 if (eyeDistance > horizon)
182 return;
184 // The icon isn't drawn here, but added to the ordered queue to be drawn back-to-front.
185 dc.addOrderedRenderable(new OrderedIcon(icon, iconPoint, eyeDistance));
187 if (icon.isShowToolTip())
188 this.addToolTip(dc, icon, iconPoint);
191 private void addToolTip(DrawContext dc, WWIcon icon, Vec4 iconPoint)
193 if (icon.getToolTipFont() == null && icon.getToolTipText() == null)
194 return;
196 final Vec4 screenPoint = dc.getView().project(iconPoint);
197 if (screenPoint == null)
198 return;
200 OrderedText tip = new OrderedText(icon.getToolTipText(), icon.getToolTipFont(), screenPoint,
201 icon.getToolTipTextColor(), 0d);
202 dc.addOrderedRenderable(tip);
205 private class OrderedText implements OrderedRenderable
207 Font font;
208 String text;
209 Vec4 point;
210 double eyeDistance;
211 java.awt.Point pickPoint;
212 Layer layer;
213 java.awt.Color color;
215 OrderedText(String text, Font font, Vec4 point, java.awt.Color color, double eyeDistance)
217 this.text = text;
218 this.font = font;
219 this.point = point;
220 this.eyeDistance = eyeDistance;
221 this.color = color;
224 OrderedText(String text, Font font, Vec4 point, java.awt.Point pickPoint, Layer layer, double eyeDistance)
226 this.text = text;
227 this.font = font;
228 this.point = point;
229 this.eyeDistance = eyeDistance;
230 this.pickPoint = pickPoint;
231 this.layer = layer;
234 public double getDistanceFromEye()
236 return this.eyeDistance;
239 public void render(DrawContext dc)
241 ToolTipRenderer tr = IconRenderer.this.toolTipRenderers.get(this.font);
242 if (tr == null)
244 if (this.font != null)
245 tr = new ToolTipRenderer(new TextRenderer(this.font, true, true));
246 else
247 tr = new ToolTipRenderer();
248 IconRenderer.this.toolTipRenderers.put(this.font, tr);
251 Rectangle vp = dc.getView().getViewport();
252 tr.setForeground(this.color);
253 tr.setUseSystemLookAndFeel(this.color == null);
254 tr.beginRendering(vp.width, vp.height, true);
255 tr.draw(this.text, (int) point.x, (int) point.y);
256 tr.endRendering();
259 public void pick(DrawContext dc, java.awt.Point pickPoint)
264 private class OrderedIcon implements OrderedRenderable, Locatable
266 WWIcon icon;
267 Vec4 point;
268 double eyeDistance;
269 java.awt.Point pickPoint;
270 Layer layer;
272 OrderedIcon(WWIcon icon, Vec4 point, double eyeDistance)
274 this.icon = icon;
275 this.point = point;
276 this.eyeDistance = eyeDistance;
279 OrderedIcon(WWIcon icon, Vec4 point, java.awt.Point pickPoint, Layer layer, double eyeDistance)
281 this.icon = icon;
282 this.point = point;
283 this.eyeDistance = eyeDistance;
284 this.pickPoint = pickPoint;
285 this.layer = layer;
288 public double getDistanceFromEye()
290 return this.eyeDistance;
293 public Position getPosition()
295 return this.icon.getPosition();
298 public void render(DrawContext dc)
300 IconRenderer.this.beginDrawIcons(dc);
304 IconRenderer.this.drawIcon(dc, this);
305 // Draw as many as we can in a batch to save ogl state switching.
306 while (dc.getOrderedRenderables().peek() instanceof OrderedIcon)
308 OrderedIcon oi = (OrderedIcon) dc.getOrderedRenderables().poll();
309 IconRenderer.this.drawIcon(dc, oi);
312 catch (WWRuntimeException e)
314 Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingIcon", e);
316 catch (Exception e)
318 Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingIcon", e);
320 finally
322 IconRenderer.this.endDrawIcons(dc);
326 public void pick(DrawContext dc, java.awt.Point pickPoint)
328 IconRenderer.this.pickSupport.clearPickList();
329 IconRenderer.this.beginDrawIcons(dc);
332 IconRenderer.this.drawIcon(dc, this);
333 // // Draw as many as we can in a batch to save ogl state switching.
334 // while (dc.getOrderedRenderables().peek() instanceof OrderedIcon)
335 // {
336 // IconRenderer.this.drawIcon(dc, (OrderedIcon) dc.getOrderedRenderables().poll());
337 // }
339 catch (WWRuntimeException e)
341 Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingIcon", e);
343 catch (Exception e)
345 Logging.logger().log(Level.SEVERE, "generic.ExceptionWhilePickingIcon", e);
347 finally
349 IconRenderer.this.endDrawIcons(dc);
350 IconRenderer.this.pickSupport.resolvePick(dc, pickPoint, layer);
351 IconRenderer.this.pickSupport.clearPickList(); // to ensure entries can be garbage collected
356 private void beginDrawIcons(DrawContext dc)
358 GL gl = dc.getGL();
360 int attributeMask =
361 GL.GL_DEPTH_BUFFER_BIT // for depth test, depth mask and depth func
362 | GL.GL_TRANSFORM_BIT // for modelview and perspective
363 | GL.GL_VIEWPORT_BIT // for depth range
364 | GL.GL_CURRENT_BIT // for current color
365 | GL.GL_COLOR_BUFFER_BIT // for alpha test func and ref, and blend
366 | GL.GL_TEXTURE_BIT // for texture env
367 | GL.GL_DEPTH_BUFFER_BIT // for depth func
368 | GL.GL_ENABLE_BIT; // for enable/disable changes
369 gl.glPushAttrib(attributeMask);
371 // Apply the depth buffer but don't change it.
372 gl.glEnable(GL.GL_DEPTH_TEST);
373 gl.glDepthMask(false);
375 // Suppress any fully transparent image pixels
376 gl.glEnable(GL.GL_ALPHA_TEST);
377 gl.glAlphaFunc(GL.GL_GREATER, 0.001f);
379 // Load a parallel projection with dimensions (viewportWidth, viewportHeight)
380 int[] viewport = new int[4];
381 gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
382 gl.glMatrixMode(GL.GL_PROJECTION);
383 gl.glPushMatrix();
384 gl.glLoadIdentity();
385 gl.glOrtho(0d, viewport[2], 0d, viewport[3], -1d, 1d);
387 gl.glMatrixMode(GL.GL_MODELVIEW);
388 gl.glPushMatrix();
390 gl.glMatrixMode(GL.GL_TEXTURE);
391 gl.glPushMatrix();
393 if (dc.isPickingMode())
395 this.pickSupport.beginPicking(dc);
397 // Set up to replace the non-transparent texture colors with the single pick color.
398 gl.glEnable(GL.GL_TEXTURE_2D);
399 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_COMBINE);
400 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_SRC0_RGB, GL.GL_PREVIOUS);
401 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_COMBINE_RGB, GL.GL_REPLACE);
403 else
405 gl.glEnable(GL.GL_TEXTURE_2D);
406 gl.glEnable(GL.GL_BLEND);
407 gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);
411 private void endDrawIcons(DrawContext dc)
413 if (dc.isPickingMode())
414 this.pickSupport.endPicking(dc);
416 GL gl = dc.getGL();
417 gl.glMatrixMode(GL.GL_PROJECTION);
418 gl.glPopMatrix();
420 gl.glMatrixMode(GL.GL_MODELVIEW);
421 gl.glPopMatrix();
423 gl.glMatrixMode(GL.GL_TEXTURE);
424 gl.glPopMatrix();
426 gl.glPopAttrib();
429 private Vec4 drawIcon(DrawContext dc, OrderedIcon uIcon)
431 if (uIcon.point == null)
433 String msg = Logging.getMessage("nullValue.PointIsNull");
434 Logging.logger().severe(msg);
435 return null;
438 WWIcon icon = uIcon.icon;
440 final Vec4 screenPoint = dc.getView().project(uIcon.point);
441 if (screenPoint == null)
442 return null;
444 Texture iconTexture = dc.getTextureCache().get(icon.getImageSource());
445 if (iconTexture == null)
446 iconTexture = this.initializeTexture(dc, icon);
448 double pedestalScale;
449 double pedestalSpacing;
450 Texture pedestalTexture = null;
451 if (pedestal != null)
453 pedestalScale = this.pedestal.getScale();
454 pedestalSpacing = pedestal.getSpacingPixels();
456 pedestalTexture = dc.getTextureCache().get(pedestal.getPath());
457 if (pedestalTexture == null)
458 pedestalTexture = this.initializeTexture(dc, pedestal);
460 else
462 pedestalScale = 0d;
463 pedestalSpacing = 0d;
466 javax.media.opengl.GL gl = dc.getGL();
468 this.setDepthFunc(dc, uIcon, screenPoint);
470 gl.glMatrixMode(GL.GL_MODELVIEW);
471 gl.glLoadIdentity();
473 Dimension size = icon.getSize();
474 double width = size != null ? size.getWidth() : iconTexture.getWidth();
475 double height = size != null ? size.getHeight() : iconTexture.getHeight();
476 gl.glTranslated(screenPoint.x - width / 2, screenPoint.y + (pedestalScale * height) + pedestalSpacing, 0d);
478 if (icon.isHighlighted())
480 double heightDelta = this.pedestal != null ? 0 : height / 2; // expand only above the pedestal
481 gl.glTranslated(width / 2, heightDelta, 0);
482 gl.glScaled(icon.getHighlightScale(), icon.getHighlightScale(), icon.getHighlightScale());
483 gl.glTranslated(-width / 2, -heightDelta, 0);
486 if (dc.isPickingMode())
488 java.awt.Color color = dc.getUniquePickColor();
489 int colorCode = color.getRGB();
490 this.pickSupport.addPickableObject(colorCode, icon, uIcon.getPosition(), false);
491 gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue());
494 iconTexture.bind();
495 TextureCoords texCoords = iconTexture.getImageTexCoords();
496 gl.glScaled(width, height, 1d);
497 dc.drawUnitQuad(texCoords);
499 if (pedestalTexture != null)
501 gl.glLoadIdentity();
502 gl.glTranslated(screenPoint.x - (pedestalScale * (width / 2)), screenPoint.y, 0d);
503 gl.glScaled(width * pedestalScale, height * pedestalScale, 1d);
505 pedestalTexture.bind();
506 texCoords = pedestalTexture.getImageTexCoords();
507 dc.drawUnitQuad(texCoords);
510 return screenPoint;
513 private void setDepthFunc(DrawContext dc, OrderedIcon uIcon, Vec4 screenPoint)
515 GL gl = dc.getGL();
517 if (uIcon.icon.isAlwaysOnTop())
519 gl.glDepthFunc(GL.GL_ALWAYS);
520 return;
523 Position eyePos = dc.getView().getEyePosition();
524 if (eyePos == null)
526 gl.glDepthFunc(GL.GL_ALWAYS);
527 return;
530 double altitude = eyePos.getElevation();
531 if (altitude < (dc.getGlobe().getMaxElevation() * dc.getVerticalExaggeration()))
533 double depth = screenPoint.z - (8d * 0.00048875809d);
534 depth = depth < 0d ? 0d : (depth > 1d ? 1d : depth);
535 gl.glDepthFunc(GL.GL_LESS);
536 gl.glDepthRange(depth, depth);
538 else if (screenPoint.z >= 1d)
540 gl.glDepthFunc(GL.GL_EQUAL);
541 gl.glDepthRange(1d, 1d);
543 else
545 gl.glDepthFunc(GL.GL_ALWAYS);
549 private Texture initializeTexture(DrawContext dc, WWIcon icon)
553 Texture iconTexture = null;
555 if (icon.getImageSource() instanceof String)
557 String path = (String) icon.getImageSource();
558 java.io.InputStream iconStream = this.getClass().getResourceAsStream("/" + path);
559 if (iconStream == null)
561 java.io.File iconFile = new java.io.File(path);
562 if (iconFile.exists())
564 iconStream = new java.io.FileInputStream(iconFile);
567 iconTexture = TextureIO.newTexture(iconStream, true, null);
569 else if (icon.getImageSource() instanceof BufferedImage)
571 iconTexture = TextureIO.newTexture((BufferedImage) icon.getImageSource(), true);
573 else
575 // TODO: Log case of unknown image-source type.
578 if (iconTexture == null)
580 // TODO: Log case.
581 return null;
584 // Icons with the same path are assumed to be identical textures, so key the texture id off the path.
585 dc.getTextureCache().put(icon.getImageSource(), iconTexture);
586 iconTexture.bind();
588 GL gl = dc.getGL();
589 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
590 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
591 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
592 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
593 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
595 return iconTexture;
597 catch (java.io.IOException e)
599 String msg = Logging.getMessage("generic.IOExceptionDuringTextureInitialization");
600 Logging.logger().log(Level.SEVERE, msg, e);
601 throw new WWRuntimeException(msg, e);
605 @Override
606 public String toString()
608 return Logging.getMessage("layers.IconLayer.Name");