All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.render.SurfacePolylines Maven / Gradle / Ivy

The newest version!
/*
 * 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.render;

import gov.nasa.worldwind.cache.GpuResourceCache;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.util.*;

import com.jogamp.opengl.*;
import java.util.*;

/**
 * This class renders fast multiple surface polylines in one pass. It relies on a {@link CompoundVecBuffer}.
 *
 * @author Dave Collins
 * @author Patrick Murris
 * @version $Id: SurfacePolylines.java 2406 2014-10-29 23:39:29Z dcollins $
 */
public class SurfacePolylines extends AbstractSurfaceShape
{
    protected List sectors;
    protected CompoundVecBuffer buffer;
    protected boolean needsOutlineTessellation = true;
    protected boolean crossesDateLine = false;
    protected Object outlineDisplayListCacheKey = new Object();

    public SurfacePolylines(CompoundVecBuffer buffer)
    {
        if (buffer == null)
        {
            String message = Logging.getMessage("nullValue.BufferIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.buffer = buffer;
    }

    public SurfacePolylines(Sector sector, CompoundVecBuffer buffer)
    {
        if (sector == null)
        {
            String message = Logging.getMessage("nullValue.SectorIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }
        if (buffer == null)
        {
            String message = Logging.getMessage("nullValue.BufferIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.sectors = Arrays.asList(sector);
        this.buffer = buffer;
    }

    /**
     * Get the underlying {@link CompoundVecBuffer} describing the geometry.
     *
     * @return the underlying {@link CompoundVecBuffer}.
     */
    public CompoundVecBuffer getBuffer()
    {
        return this.buffer;
    }

    @Override
    public List getSectors(DrawContext dc)
    {
        if (dc == null)
        {
            String message = Logging.getMessage("nullValue.DrawContextIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        // SurfacePolylines does not interpolate between caller specified positions, therefore it has no path type.
        if (this.sectors == null)
            this.sectors = this.computeSectors(dc);

        return this.sectors;
    }

    public Iterable getLocations(Globe globe)
    {
        if (globe == null)
        {
            String message = Logging.getMessage("nullValue.GlobeIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        return this.getLocations();
    }

    protected List> createGeometry(Globe globe, SurfaceTileDrawContext sdc)
    {
        // SurfacePolylines does not invoke this method, so return null indicating this method is not supported.
        // We avoid invoking computeGeometry by overriding determineActiveGeometry below.
        return null;
    }

    protected List> createGeometry(Globe globe, double edgeIntervalsPerDegree)
    {
        return null;
    }

    public Iterable getLocations()
    {
        return this.buffer.getLocations();
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public void setLocations(Iterable iterable)
    {
        throw new UnsupportedOperationException();
    }

    public Position getReferencePosition()
    {
        Iterator iterator = this.getLocations().iterator();
        if (iterator.hasNext())
            return new Position(iterator.next(), 0);

        return null;
    }

    /** {@inheritDoc} Overridden to treat the polylines as open paths rather than closed polygons. */
    @Override
    protected boolean canContainPole()
    {
        return false;
    }

    protected void doMoveTo(Position oldReferencePosition, Position newReferencePosition)
    {
        for (int i = 0; i < this.buffer.size(); i++)
        {
            VecBuffer vb = this.buffer.subBuffer(i);

            for (int pos = 0; pos < vb.getSize(); pos++)
            {
                LatLon ll = vb.getLocation(pos);
                Angle heading = LatLon.greatCircleAzimuth(oldReferencePosition, ll);
                Angle pathLength = LatLon.greatCircleDistance(oldReferencePosition, ll);
                vb.putLocation(pos, LatLon.greatCircleEndPosition(newReferencePosition, heading, pathLength));
            }
        }

        this.onGeometryChanged();
    }

    protected void doMoveTo(Globe globe, Position oldReferencePosition, Position newReferencePosition)
    {
        for (int i = 0; i < this.buffer.size(); i++)
        {
            VecBuffer vb = this.buffer.subBuffer(i);

            List newLocations = LatLon.computeShiftedLocations(globe, oldReferencePosition,
                newReferencePosition, vb.getLocations());

            for (int pos = 0; pos < vb.getSize(); pos++)
            {
                vb.putLocation(pos, newLocations.get(i));
            }
        }

        this.onGeometryChanged();
    }

    protected void onGeometryChanged()
    {
        this.sectors = null;
        this.needsOutlineTessellation = true;
        super.onShapeChanged();
    }

    protected void determineActiveGeometry(DrawContext dc, SurfaceTileDrawContext sdc)
    {
        // Intentionally left blank in order to override the superclass behavior with nothing.
    }

    protected void drawInterior(DrawContext dc, SurfaceTileDrawContext sdc)
    {
        // Intentionally left blank; SurfacePolylines does not render an interior.
    }

    protected void drawOutline(DrawContext dc, SurfaceTileDrawContext sdc)
    {
        // Exit immediately if the Polyline has no coordinate data.
        if (this.buffer.size() == 0)
            return;

        Position referencePos = this.getReferencePosition();
        if (referencePos == null)
            return;

        int hemisphereSign = (int) Math.signum(sdc.getSector().getCentroid().getLongitude().degrees);

        // Attempt to tessellate the Polyline's outline if the Polyline's outline display list is uninitialized, or if
        // the Polyline is marked as needing tessellation.
        int[] dlResource = (int[]) dc.getGpuResourceCache().get(this.outlineDisplayListCacheKey);
        if (dlResource == null || this.needsOutlineTessellation)
            dlResource = this.tessellateOutline(dc, referencePos);

        // Exit immediately if the Polyline's interior failed to tessellate. The cause has already been logged by
        // tessellateInterior.
        if (dlResource == null)
            return;

        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
        this.applyOutlineState(dc, this.getActiveAttributes());
        gl.glCallList(dlResource[0]);

        if (this.crossesDateLine)
        {
            gl.glPushMatrix();
            try
            {
                // Apply hemisphere offset and draw again
                gl.glTranslated(360 * hemisphereSign, 0, 0);
                gl.glCallList(dlResource[0]);
            }
            finally
            {
                gl.glPopMatrix();
            }
        }
    }

    protected int[] tessellateOutline(DrawContext dc, LatLon referenceLocation)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
        this.crossesDateLine = false;

        int[] dlResource = new int[] {gl.glGenLists(1), 1};

        gl.glNewList(dlResource[0], GL2.GL_COMPILE);
        try
        {
            // Tessellate each part, note if crossing date line
            for (int i = 0; i < this.buffer.size(); i++)
            {
                VecBuffer subBuffer = this.buffer.subBuffer(i);
                if (this.tessellatePart(gl, subBuffer, referenceLocation))
                    this.crossesDateLine = true;
            }
        }
        finally
        {
            gl.glEndList();
        }

        this.needsOutlineTessellation = false;

        int numBytes = this.buffer.size() * 3 * 4; // 3 float coords
        dc.getGpuResourceCache().put(this.outlineDisplayListCacheKey, dlResource, GpuResourceCache.DISPLAY_LISTS,
            numBytes);

        return dlResource;
    }

    protected boolean tessellatePart(GL2 gl, VecBuffer vecBuffer, LatLon referenceLocation)
    {
        Iterable iterable = vecBuffer.getCoords(3);
        boolean dateLineCrossed = false;

        gl.glBegin(GL2.GL_LINE_STRIP);
        try
        {
            int sign = 0; // hemisphere offset direction
            double previousLongitude = 0;

            for (double[] coords : iterable)
            {
                if (Math.abs(previousLongitude - coords[0]) > 180)
                {
                    // Crossing date line, sum departure point longitude sign for hemisphere offset
                    sign += (int) Math.signum(previousLongitude);
                    dateLineCrossed = true;
                }

                previousLongitude = coords[0];

                double lonDegrees = coords[0] - referenceLocation.getLongitude().degrees;
                double latDegrees = coords[1] - referenceLocation.getLatitude().degrees;
                lonDegrees += sign * 360; // apply hemisphere offset
                gl.glVertex3f((float) lonDegrees, (float) latDegrees, 0);
            }
        }
        finally
        {
            gl.glEnd();
        }

        return dateLineCrossed;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy