2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
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
;
21 import java
.awt
.image
.*;
23 import java
.util
.logging
.Level
;
27 * @version $Id: IconRenderer.java 2471 2007-07-31 21:50: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
>();
39 public Pedestal
getPedestal()
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)
54 //noinspection RedundantIfStatement
55 if (checkPosition
&& icon
.getPosition() == null)
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))
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))
84 this.drawOne(dc
, icon
, iconPoint
);
87 private void drawMany(DrawContext dc
, Iterable
<WWIcon
> icons
)
91 String msg
= Logging
.getMessage("nullValue.DrawContextIsNull");
92 Logging
.logger().severe(msg
);
93 throw new IllegalArgumentException(msg
);
96 if (dc
.getVisibleSector() == null)
99 SectorGeometryList geos
= dc
.getSurfaceGeometry();
100 //noinspection RedundantIfStatement
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())
116 while (iterator
.hasNext())
118 WWIcon icon
= iterator
.next();
119 if (!isIconValid(icon
, true))
122 if (!icon
.isVisible())
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
= 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
)
147 String msg
= Logging
.getMessage("nullValue.DrawContextIsNull");
148 Logging
.logger().severe(msg
);
149 throw new IllegalArgumentException(msg
);
152 if (dc
.getVisibleSector() == null)
155 SectorGeometryList geos
= dc
.getSurfaceGeometry();
156 //noinspection RedundantIfStatement
160 if (!icon
.isVisible())
163 if (iconPoint
== null)
165 Angle lat
= icon
.getPosition().getLatitude();
166 Angle lon
= icon
.getPosition().getLongitude();
168 if (!dc
.getVisibleSector().contains(lat
, lon
))
171 iconPoint
= dc
.getSurfaceGeometry().getSurfacePoint(lat
, lon
, icon
.getPosition().getElevation());
172 if (iconPoint
== null)
176 if (!dc
.getView().getFrustumInModelCoordinates().contains(iconPoint
))
179 double horizon
= dc
.getView().computeHorizonDistance();
180 double eyeDistance
= dc
.getView().getEyePoint().distanceTo3(iconPoint
);
181 if (eyeDistance
> horizon
)
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)
196 final Vec4 screenPoint
= dc
.getView().project(iconPoint
);
197 if (screenPoint
== null)
200 OrderedText tip
= new OrderedText(icon
.getToolTipText(), icon
.getToolTipFont(), screenPoint
,
201 icon
.getToolTipTextColor(), 0d
);
202 dc
.addOrderedRenderable(tip
);
205 private class OrderedText
implements OrderedRenderable
211 java
.awt
.Point pickPoint
;
213 java
.awt
.Color color
;
215 OrderedText(String text
, Font font
, Vec4 point
, java
.awt
.Color color
, double eyeDistance
)
220 this.eyeDistance
= eyeDistance
;
224 OrderedText(String text
, Font font
, Vec4 point
, java
.awt
.Point pickPoint
, Layer layer
, double eyeDistance
)
229 this.eyeDistance
= eyeDistance
;
230 this.pickPoint
= pickPoint
;
234 public double getDistanceFromEye()
236 return this.eyeDistance
;
239 public void render(DrawContext dc
)
241 ToolTipRenderer tr
= IconRenderer
.this.toolTipRenderers
.get(this.font
);
244 if (this.font
!= null)
245 tr
= new ToolTipRenderer(new TextRenderer(this.font
, true, true));
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
);
259 public void pick(DrawContext dc
, java
.awt
.Point pickPoint
)
264 private class OrderedIcon
implements OrderedRenderable
, Locatable
269 java
.awt
.Point pickPoint
;
272 OrderedIcon(WWIcon icon
, Vec4 point
, double eyeDistance
)
276 this.eyeDistance
= eyeDistance
;
279 OrderedIcon(WWIcon icon
, Vec4 point
, java
.awt
.Point pickPoint
, Layer layer
, double eyeDistance
)
283 this.eyeDistance
= eyeDistance
;
284 this.pickPoint
= pickPoint
;
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
);
318 Logging
.logger().log(Level
.SEVERE
, "generic.ExceptionWhileRenderingIcon", e
);
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
)
336 IconRenderer
.this.drawIcon(dc
, (OrderedIcon
) dc
.getOrderedRenderables().poll());
339 catch (WWRuntimeException e
)
341 Logging
.logger().log(Level
.SEVERE
, "generic.ExceptionWhileRenderingIcon", e
);
345 Logging
.logger().log(Level
.SEVERE
, "generic.ExceptionWhilePickingIcon", e
);
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
)
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
);
385 gl
.glOrtho(0d
, viewport
[2], 0d
, viewport
[3], -1d
, 1d
);
387 gl
.glMatrixMode(GL
.GL_MODELVIEW
);
390 gl
.glMatrixMode(GL
.GL_TEXTURE
);
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
);
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
);
417 gl
.glMatrixMode(GL
.GL_PROJECTION
);
420 gl
.glMatrixMode(GL
.GL_MODELVIEW
);
423 gl
.glMatrixMode(GL
.GL_TEXTURE
);
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
);
438 WWIcon icon
= uIcon
.icon
;
440 final Vec4 screenPoint
= dc
.getView().project(uIcon
.point
);
441 if (screenPoint
== 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
);
463 pedestalSpacing
= 0d
;
466 javax
.media
.opengl
.GL gl
= dc
.getGL();
468 this.setDepthFunc(dc
, screenPoint
);
470 gl
.glMatrixMode(GL
.GL_MODELVIEW
);
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());
495 TextureCoords texCoords
= iconTexture
.getImageTexCoords();
496 gl
.glScaled(width
, height
, 1d
);
497 dc
.drawUnitQuad(texCoords
);
499 if (pedestalTexture
!= null)
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
);
513 private void setDepthFunc(DrawContext dc
, Vec4 screenPoint
)
517 Position eyePos
= dc
.getView().getEyePosition();
520 gl
.glDepthFunc(GL
.GL_ALWAYS
);
524 double altitude
= eyePos
.getElevation();
525 if (altitude
< (dc
.getGlobe().getMaxElevation() * dc
.getVerticalExaggeration()))
527 double depth
= screenPoint
.z
- (8d
* 0.00048875809d
);
528 depth
= depth
< 0d ?
0d
: (depth
> 1d ?
1d
: depth
);
529 gl
.glDepthFunc(GL
.GL_LESS
);
530 gl
.glDepthRange(depth
, depth
);
532 else if (screenPoint
.z
>= 1d
)
534 gl
.glDepthFunc(GL
.GL_EQUAL
);
535 gl
.glDepthRange(1d
, 1d
);
539 gl
.glDepthFunc(GL
.GL_ALWAYS
);
543 private Texture
initializeTexture(DrawContext dc
, WWIcon icon
)
547 Texture iconTexture
= null;
549 if (icon
.getImageSource() instanceof String
)
551 String path
= (String
) icon
.getImageSource();
552 java
.io
.InputStream iconStream
= this.getClass().getResourceAsStream("/" + path
);
553 if (iconStream
== null)
555 java
.io
.File iconFile
= new java
.io
.File(path
);
556 if (iconFile
.exists())
558 iconStream
= new java
.io
.FileInputStream(iconFile
);
561 iconTexture
= TextureIO
.newTexture(iconStream
, true, null);
563 else if (icon
.getImageSource() instanceof BufferedImage
)
565 iconTexture
= TextureIO
.newTexture((BufferedImage
) icon
.getImageSource(), true);
569 // TODO: Log case of unknown image-source type.
572 if (iconTexture
== null)
578 // Icons with the same path are assumed to be identical textures, so key the texture id off the path.
579 dc
.getTextureCache().put(icon
.getImageSource(), iconTexture
);
583 gl
.glTexEnvf(GL
.GL_TEXTURE_ENV
, GL
.GL_TEXTURE_ENV_MODE
, GL
.GL_MODULATE
);
584 gl
.glTexParameteri(GL
.GL_TEXTURE_2D
, GL
.GL_TEXTURE_MIN_FILTER
, GL
.GL_LINEAR_MIPMAP_LINEAR
);
585 gl
.glTexParameteri(GL
.GL_TEXTURE_2D
, GL
.GL_TEXTURE_MAG_FILTER
, GL
.GL_LINEAR
);
586 gl
.glTexParameteri(GL
.GL_TEXTURE_2D
, GL
.GL_TEXTURE_WRAP_S
, GL
.GL_CLAMP_TO_EDGE
);
587 gl
.glTexParameteri(GL
.GL_TEXTURE_2D
, GL
.GL_TEXTURE_WRAP_T
, GL
.GL_CLAMP_TO_EDGE
);
591 catch (java
.io
.IOException e
)
593 String msg
= Logging
.getMessage("generic.IOExceptionDuringTextureInitialization");
594 Logging
.logger().log(Level
.SEVERE
, msg
, e
);
595 throw new WWRuntimeException(msg
, e
);
600 public String
toString()
602 return Logging
.getMessage("layers.IconLayer.Name");