src.gov.nasa.worldwind.layers.RenderableLayer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.layers;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.event.*;
import gov.nasa.worldwind.pick.PickSupport;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.Logging;
import javax.media.opengl.GL2;
import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* The RenderableLayer
class manages a collection of {@link gov.nasa.worldwind.render.Renderable} objects
* for rendering, picking, and disposal.
*
* @author tag
* @version $Id: RenderableLayer.java 1171 2013-02-11 21:45:02Z dcollins $
* @see gov.nasa.worldwind.render.Renderable
*/
public class RenderableLayer extends AbstractLayer
{
protected Collection renderables = new ConcurrentLinkedQueue();
protected Iterable renderablesOverride;
protected PickSupport pickSupport = new PickSupport();
/** Creates a new RenderableLayer
with a null delegateOwner
*/
public RenderableLayer()
{
}
/**
* Adds the specified renderable
to this layer's internal collection. If this layer's internal
* collection has been overridden with a call to {@link #setRenderables(Iterable)}, this will throw an exception.
*
* If the renderable
implements {@link gov.nasa.worldwind.avlist.AVList}, the layer forwards its
* property change events to the layer's property change listeners. Any property change listeners the layer attaches
* to the renderable
are removed in {@link #removeRenderable(gov.nasa.worldwind.render.Renderable)},
* {@link #removeAllRenderables()}, or {@link #dispose()}.
*
* @param renderable Renderable to add.
*
* @throws IllegalArgumentException If renderable
is null.
* @throws IllegalStateException If a custom Iterable has been specified by a call to setRenderables
.
*/
public void addRenderable(Renderable renderable)
{
if (renderable == null)
{
String msg = Logging.getMessage("nullValue.RenderableIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (this.renderablesOverride != null)
{
String msg = Logging.getMessage("generic.LayerIsUsingCustomIterable");
Logging.logger().severe(msg);
throw new IllegalStateException(msg);
}
this.renderables.add(renderable);
// Attach the layer as a property change listener of the renderable. This forwards property change events from
// the renderable to the SceneController.
if (renderable instanceof AVList)
((AVList) renderable).addPropertyChangeListener(this);
}
/**
* Adds the contents of the specified renderables
to this layer's internal collection. If this layer's
* internal collection has been overriden with a call to {@link #setRenderables(Iterable)}, this will throw an
* exception.
*
* If any of the renderables
implement {@link gov.nasa.worldwind.avlist.AVList}, the layer forwards
* their property change events to the layer's property change listeners. Any property change listeners the layer
* attaches to the renderable
are removed in {@link #removeRenderable(gov.nasa.worldwind.render.Renderable)},
* {@link #removeAllRenderables()}, or {@link #dispose()}.
*
* @param renderables Renderables to add.
*
* @throws IllegalArgumentException If renderables
is null.
* @throws IllegalStateException If a custom Iterable has been specified by a call to setRenderables
.
*/
public void addRenderables(Iterable renderables)
{
if (renderables == null)
{
String msg = Logging.getMessage("nullValue.IterableIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (this.renderablesOverride != null)
{
String msg = Logging.getMessage("generic.LayerIsUsingCustomIterable");
Logging.logger().severe(msg);
throw new IllegalStateException(msg);
}
for (Renderable renderable : renderables)
{
// Internal list of renderables does not accept null values.
if (renderable != null)
this.renderables.add(renderable);
// Attach the layer as a property change listener of the renderable. This forwards property change events
// from the renderable to the SceneController.
if (renderable instanceof AVList)
((AVList) renderable).addPropertyChangeListener(this);
}
}
/**
* Removes the specified renderable
from this layer's internal collection, if it exists. If this
* layer's internal collection has been overridden with a call to {@link #setRenderables(Iterable)}, this will throw
* an exception.
*
* If the renderable
implements {@link gov.nasa.worldwind.avlist.AVList}, this stops forwarding the its
* property change events to the layer's property change listeners. Any property change listeners the layer attached
* to the renderable
in {@link #addRenderable(gov.nasa.worldwind.render.Renderable)} or {@link
* #addRenderables(Iterable)} are removed.
*
* @param renderable Renderable to remove.
*
* @throws IllegalArgumentException If renderable
is null.
* @throws IllegalStateException If a custom Iterable has been specified by a call to setRenderables
.
*/
public void removeRenderable(Renderable renderable)
{
if (renderable == null)
{
String msg = Logging.getMessage("nullValue.RenderableIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (this.renderablesOverride != null)
{
String msg = Logging.getMessage("generic.LayerIsUsingCustomIterable");
Logging.logger().severe(msg);
throw new IllegalStateException(msg);
}
this.renderables.remove(renderable);
// Remove the layer as a property change listener of the renderable. This prevents the renderable from keeping a
// dangling reference to the layer.
if (renderable instanceof AVList)
((AVList) renderable).removePropertyChangeListener(this);
}
/**
* Clears the contents of this layer's internal Renderable collection. If this layer's internal collection has been
* overriden with a call to {@link #setRenderables(Iterable)}, this will throw an exception.
*
* If any of the renderables
implement {@link gov.nasa.worldwind.avlist.AVList}, this stops forwarding
* their property change events to the layer's property change listeners. Any property change listeners the layer
* attached to the renderables
in {@link #addRenderable(gov.nasa.worldwind.render.Renderable)} or
* {@link #addRenderables(Iterable)} are removed.
*
* @throws IllegalStateException If a custom Iterable has been specified by a call to setRenderables
.
*/
public void removeAllRenderables()
{
if (this.renderablesOverride != null)
{
String msg = Logging.getMessage("generic.LayerIsUsingCustomIterable");
Logging.logger().severe(msg);
throw new IllegalStateException(msg);
}
this.clearRenderables();
}
protected void clearRenderables()
{
if (this.renderables != null && this.renderables.size() > 0)
{
// Remove the layer as property change listener of any renderables. This prevents the renderables from
// keeping a dangling references to the layer.
for (Renderable renderable : this.renderables)
{
if (renderable instanceof AVList)
((AVList) renderable).removePropertyChangeListener(this);
}
this.renderables.clear();
}
}
public int getNumRenderables()
{
if (this.renderablesOverride != null)
{
int size = 0;
//noinspection UnusedDeclaration
for (Renderable r : this.renderablesOverride)
{
++size;
}
return size;
}
else
{
return this.renderables.size();
}
}
/**
* Returns the Iterable of Renderables currently in use by this layer. If the caller has specified a custom Iterable
* via {@link #setRenderables(Iterable)}, this will returns a reference to that Iterable. If the caller passed
* setRenderables
a null parameter, or if setRenderables
has not been called, this returns
* a view of this layer's internal collection of Renderables.
*
* @return Iterable of currently active Renderables.
*/
public Iterable getRenderables()
{
return this.getActiveRenderables();
}
/**
* Returns the Iterable of currently active Renderables. If the caller has specified a custom Iterable via {@link
* #setRenderables(Iterable)}, this will returns a reference to that Iterable. If the caller passed
* setRenderables
a null parameter, or if setRenderables
has not been called, this returns
* a view of this layer's internal collection of Renderables.
*
* @return Iterable of currently active Renderables.
*/
protected Iterable getActiveRenderables()
{
if (this.renderablesOverride != null)
{
return this.renderablesOverride;
}
else
{
// Return an unmodifiable reference to the internal list of renderables.
// This prevents callers from changing this list and invalidating any invariants we have established.
return java.util.Collections.unmodifiableCollection(this.renderables);
}
}
/**
* Overrides the collection of currently active Renderables with the specified renderableIterable
. This
* layer will maintain a reference to renderableIterable
strictly for picking and rendering. This layer
* will not modify the reference, or dispose of its contents. This will also clear and dispose of the internal
* collection of Renderables, and will prevent any modification to its contents via addRenderable,
* addRenderables, removeRenderables, or dispose
.
*
* Unlike {@link #addRenderable(gov.nasa.worldwind.render.Renderable)} or {@link #addRenderables(Iterable)}, this
* does not forward any of the renderable's property change events to the layer's property change listeners. Since
* the layer is not in control of the iIterable's contents, attaching property change listeners to the renderables
* could cause the them to hold dangling references to the layer. If any of the renderables in the Iterable rely on
* forwarding property change events for proper operation - such as {@link gov.nasa.worldwind.render.AbstractBrowserBalloon}
* - use {@link #addRenderables(Iterable)} instead.
*
* If the specified renderableIterable
is null, this layer reverts to maintaining its internal
* collection.
*
* @param renderableIterable Iterable to use instead of this layer's internal collection, or null to use this
* layer's internal collection.
*/
public void setRenderables(Iterable renderableIterable)
{
this.renderablesOverride = renderableIterable;
// Dispose of the internal collection of Renderables.
this.disposeRenderables();
// Clear the internal collection of Renderables.
this.clearRenderables();
}
/**
* Opacity is not applied to layers of this type because each renderable typically has its own opacity control.
*
* @param opacity the current opacity value, which is ignored by this layer.
*/
@Override
public void setOpacity(double opacity)
{
super.setOpacity(opacity);
}
/**
* Returns the layer's opacity value, which is ignored by this layer because each of its renderables typiically has
* its own opacity control.
*
* @return The layer opacity, a value between 0 and 1.
*/
@Override
public double getOpacity()
{
return super.getOpacity();
}
/**
* Disposes the contents of this layer's internal Renderable collection, but does not remove any elements from that
* collection.
*
* If any of layer's internal Renderables implement {@link gov.nasa.worldwind.avlist.AVList}, this stops forwarding
* their property change events to the layer's property change listeners. Any property change listeners the layer
* attached to the renderables
in {@link #addRenderable(gov.nasa.worldwind.render.Renderable)} or
* {@link #addRenderables(Iterable)} are removed.
*
* @throws IllegalStateException If a custom Iterable has been specified by a call to setRenderables
.
*/
public void dispose()
{
if (this.renderablesOverride != null)
{
String msg = Logging.getMessage("generic.LayerIsUsingCustomIterable");
Logging.logger().severe(msg);
throw new IllegalStateException(msg);
}
this.disposeRenderables();
}
protected void disposeRenderables()
{
if (this.renderables != null && this.renderables.size() > 0)
{
for (Renderable renderable : this.renderables)
{
try
{
// Remove the layer as a property change listener of the renderable. This prevents the renderable
// from keeping a dangling reference to the layer.
if (renderable instanceof AVList)
((AVList) renderable).removePropertyChangeListener(this);
if (renderable instanceof Disposable)
((Disposable) renderable).dispose();
}
catch (Exception e)
{
String msg = Logging.getMessage("generic.ExceptionAttemptingToDisposeRenderable");
Logging.logger().severe(msg);
// continue to next renderable
}
}
}
this.renderables.clear();
}
protected void doPreRender(DrawContext dc)
{
this.doPreRender(dc, this.getActiveRenderables());
}
protected void doPick(DrawContext dc, java.awt.Point pickPoint)
{
this.doPick(dc, this.getActiveRenderables(), pickPoint);
}
protected void doRender(DrawContext dc)
{
this.doRender(dc, this.getActiveRenderables());
}
protected void doPreRender(DrawContext dc, Iterable renderables)
{
for (Renderable renderable : renderables)
{
try
{
// If the caller has specified their own Iterable,
// then we cannot make any guarantees about its contents.
if (renderable != null && renderable instanceof PreRenderable)
((PreRenderable) renderable).preRender(dc);
}
catch (Exception e)
{
String msg = Logging.getMessage("generic.ExceptionWhilePrerenderingRenderable");
Logging.logger().severe(msg);
// continue to next renderable
}
}
}
protected void doPick(DrawContext dc, Iterable renderables, java.awt.Point pickPoint)
{
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
this.pickSupport.clearPickList();
this.pickSupport.beginPicking(dc);
try
{
for (Renderable renderable : renderables)
{
// If the caller has specified their own Iterable,
// then we cannot make any guarantees about its contents.
if (renderable != null)
{
// float[] inColor = new float[4];
// gl.glGetFloatv(GL.GL_CURRENT_COLOR, inColor, 0);
java.awt.Color color = dc.getUniquePickColor();
gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue());
try
{
renderable.render(dc);
}
catch (Exception e)
{
String msg = Logging.getMessage("generic.ExceptionWhilePickingRenderable");
Logging.logger().severe(msg);
Logging.logger().log(java.util.logging.Level.FINER, msg, e); // show exception for this level
continue; // go on to next renderable
}
//
// gl.glColor4fv(inColor, 0);
if (renderable instanceof Locatable)
{
this.pickSupport.addPickableObject(color.getRGB(), renderable,
((Locatable) renderable).getPosition(), false);
}
else
{
this.pickSupport.addPickableObject(color.getRGB(), renderable);
}
}
}
this.pickSupport.resolvePick(dc, pickPoint, this);
}
finally
{
this.pickSupport.endPicking(dc);
}
}
protected void doRender(DrawContext dc, Iterable renderables)
{
for (Renderable renderable : renderables)
{
try
{
// If the caller has specified their own Iterable,
// then we cannot make any guarantees about its contents.
if (renderable != null)
renderable.render(dc);
}
catch (Exception e)
{
String msg = Logging.getMessage("generic.ExceptionWhileRenderingRenderable");
Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
// continue to next renderable
}
}
}
@Override
public String toString()
{
return Logging.getMessage("layers.RenderableLayer.Name");
}
/**
* {@inheritDoc}
*
* This implementation forwards the message to each Renderable that implements {@link MessageListener}.
*
* @param message The message that was received.
*/
@Override
public void onMessage(Message message)
{
for (Renderable renderable : this.renderables)
{
try
{
if (renderable instanceof MessageListener)
((MessageListener) renderable).onMessage(message);
}
catch (Exception e)
{
String msg = Logging.getMessage("generic.ExceptionInvokingMessageListener");
Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
// continue to next renderable
}
}
}
}