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

src.gov.nasa.worldwind.render.GeographicTextRenderer Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show 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 com.jogamp.opengl.util.awt.TextRenderer;
import gov.nasa.worldwind.View;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.terrain.SectorGeometryList;
import gov.nasa.worldwind.util.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.gl2.GLUgl2;
import java.awt.*;
import java.awt.geom.*;
import java.io.IOException;
import java.util.*;

/**
 * @author dcollins
 * @version $Id: GeographicTextRenderer.java 1181 2013-02-15 22:27:10Z dcollins $
 */
public class GeographicTextRenderer
{
    private TextRenderer lastTextRenderer = null;
    private final GLU glu = new GLUgl2();

    private static final Font DEFAULT_FONT = Font.decode("Arial-PLAIN-12");
    private static final Color DEFAULT_COLOR = Color.white;
    private boolean cullText = false;
    private int cullTextMargin = 0;
    private String effect = AVKey.TEXT_EFFECT_SHADOW;

    // Distance scaling and fading
    private double distanceMinScale = 1d;
    private double distanceMaxScale = 1d;
    private double distanceMinOpacity = 1d;
    private boolean isDistanceScaling = false;
    private double lookAtDistance = 0;

    private boolean hasJOGLv111Bug = false;

    public GeographicTextRenderer()
    {
    }

    /**
     * Determines whether overlapping text are culled. If true text items are sorted front to back
     * according to their respective priority rather then back to front, and will be drawn only if they do not overlap
     * an already drawn text. If false all text will be drawn back to front whether they overlap or not.
     *
     * @return true if overlapping text are culled.
     */
    public boolean isCullTextEnabled()
    {
        return cullText;
    }

    /**
     * Set whether overlapping text should be culled. If true text items will be sorted front to back
     * according to their respective priority rather then back to front, and will be drawn only if they do not overlap
     * an already drawn text. If false all text will be drawn back to front whether they overlap or not.
     *
     * @param cullText true if overlapping text should be culled.
     */
    public void setCullTextEnabled(boolean cullText)
    {
        this.cullText = cullText;
    }

    /**
     * Get the empty margin that surrounds a text item when considering it's bounding rectangle during text culling -
     * see {@link #setCullTextEnabled(boolean)}. The smaller the margin, the closer text items can get before being
     * considered as overlapping.
     *
     * @return the empty margin that surrounds a text item - in pixels.
     */
    public int getCullTextMargin()
    {
        return this.cullTextMargin;
    }

    /**
     * Set the empty margin that surrounds a text item when considering it's bounding rectangle during text culling -
     * see {@link #setCullTextEnabled(boolean)}. The smaller the margin, the closer text items can get before being
     * considered as overlapping.
     *
     * @param margin the empty margin that surrounds a text item - in pixels.
     */
    public void setCullTextMargin(int margin)
    {
        this.cullTextMargin = margin;
    }

    /**
     * Get the effect used to decorate the text. Can be one of {@link AVKey#TEXT_EFFECT_SHADOW} (default), {@link
     * AVKey#TEXT_EFFECT_OUTLINE} or {@link AVKey#TEXT_EFFECT_NONE}.
     *
     * @return the effect used for text rendering.
     */
    public String getEffect()
    {
        return this.effect;
    }

    /**
     * Set the effect used to decorate the text. Can be one of {@link AVKey#TEXT_EFFECT_SHADOW} (default), {@link
     * AVKey#TEXT_EFFECT_OUTLINE} or {@link AVKey#TEXT_EFFECT_NONE}.
     *
     * @param effect the effect to use for text rendering.
     */
    public void setEffect(String effect)
    {
        if (effect == null)
        {
            String msg = Logging.getMessage("nullValue.StringIsNull");
            Logging.logger().fine(msg);
            throw new IllegalArgumentException(msg);
        }

        this.effect = effect;
    }

    /**
     * Get the minimum scale that can be applied to a text item when it gets farther away from the eye than the view
     * lookat point - screen center.
     *
     * @return the minimum scale that can be applied to a text item when it gets away from the eye.
     */
    public double getDistanceMinScale()
    {
        return this.distanceMinScale;
    }

    /**
     * Set the minimum scale that can be applied to a text item when it gets farther away from the eye than the view
     * lookat point - screen center. Use a value less then one to have the text 'fade' away. A value of one will have no
     * effect.
     *
     * @param scale the minimum scale that can be applied to a text item when it gets away from the eye.
     */
    public void setDistanceMinScale(double scale)
    {
        this.distanceMinScale = scale;
    }

    /**
     * Get the maximum scale that can be applied to a text item when it gets closer to the eye than the view lookat
     * point - screen center.
     *
     * @return the maximum scale that can be applied to a text item when it closer to the eye.
     */
    public double getDistanceMaxScale()
    {
        return this.distanceMaxScale;
    }

    /**
     * Set the maximum scale that can be applied to a text item when it gets closer to the eye than the view lookat
     * point - screen center. Use a value greater then one to have the text magnified in the foreground. A value of one
     * will have no effect.
     *
     * @param scale the maximum scale that can be applied to a text item when it closer to the eye.
     */
    public void setDistanceMaxScale(double scale)
    {
        this.distanceMaxScale = scale;
    }

    /**
     * Get the minimum opacity that can be applied to a text item when it gets farther away from the eye than the view
     * lookat point - screen center.
     *
     * @return the minimum opacity that can be applied to a text item when it gets away from the eye.
     */
    public double getDistanceMinOpacity()
    {
        return this.distanceMinOpacity;
    }

    /**
     * Set the minimum opacity that can be applied to a text item when it gets farther away from the eye than the view
     * lookat point - screen center. Use a value less then one to have the text 'fade' away. A value of one will have no
     * effect.
     *
     * @param opacity the minimum opacity that can be applied to a text item when it gets away from the eye.
     */
    public void setDistanceMinOpacity(double opacity)
    {
        this.distanceMinOpacity = opacity;
    }

    public void render(DrawContext dc, Iterable text)
    {
        this.drawMany(dc, text);
    }

    public void render(DrawContext dc, GeographicText text, Vec4 textPoint)
    {
        if (!isTextValid(text, false))
            return;

        this.drawOne(dc, text, textPoint);
    }

    private void drawMany(DrawContext dc, Iterable textIterable)
    {
        if (dc == null)
        {
            String msg = Logging.getMessage("nullValue.DrawContextIsNull");
            Logging.logger().fine(msg);
            throw new IllegalArgumentException(msg);
        }
        if (textIterable == null)
        {
            String msg = Logging.getMessage("nullValue.IterableIsNull");
            Logging.logger().fine(msg);
            throw new IllegalArgumentException(msg);
        }

        if (dc.getVisibleSector() == null)
            return;

        SectorGeometryList geos = dc.getSurfaceGeometry();
        if (geos == null)
            return;

        Iterator iterator = textIterable.iterator();
        if (!iterator.hasNext())
            return;

        Frustum frustumInModelCoords = dc.getView().getFrustumInModelCoordinates();
        double horizon = dc.getView().getHorizonDistance();

        while (iterator.hasNext())
        {
            GeographicText text = iterator.next();
            if (!isTextValid(text, true))
                continue;

            if (!text.isVisible())
                continue;

            Angle lat = text.getPosition().getLatitude();
            Angle lon = text.getPosition().getLongitude();

            if (!dc.getVisibleSector().contains(lat, lon))
                continue;

            Vec4 textPoint = geos.getSurfacePoint(lat, lon,
                text.getPosition().getElevation() * dc.getVerticalExaggeration());
            if (textPoint == null)
                continue;

            double eyeDistance = dc.getView().getEyePoint().distanceTo3(textPoint);
            if (eyeDistance > horizon)
                continue;

            if (!frustumInModelCoords.contains(textPoint))
                continue;

            dc.addOrderedRenderable(new OrderedText(text, textPoint, eyeDistance));
        }
    }

    private void drawOne(DrawContext dc, GeographicText text, Vec4 textPoint)
    {
        if (dc == null)
        {
            String msg = Logging.getMessage("nullValue.DrawContextIsNull");
            Logging.logger().fine(msg);
            throw new IllegalArgumentException(msg);
        }
        if (dc.getView() == null)
        {
            String msg = Logging.getMessage("nullValue.ViewIsNull");
            Logging.logger().fine(msg);
            throw new IllegalArgumentException(msg);
        }

        if (dc.getVisibleSector() == null)
            return;

        SectorGeometryList geos = dc.getSurfaceGeometry();
        if (geos == null)
            return;

        if (!text.isVisible())
            return;

        if (textPoint == null)
        {
            if (text.getPosition() == null)
                return;

            Angle lat = text.getPosition().getLatitude();
            Angle lon = text.getPosition().getLongitude();

            if (!dc.getVisibleSector().contains(lat, lon))
                return;

            textPoint = geos.getSurfacePoint(lat, lon,
                text.getPosition().getElevation() * dc.getVerticalExaggeration());
            if (textPoint == null)
                return;
        }

        double horizon = dc.getView().getHorizonDistance();
        double eyeDistance = dc.getView().getEyePoint().distanceTo3(textPoint);
        if (eyeDistance > horizon)
            return;

        if (!dc.getView().getFrustumInModelCoordinates().contains(textPoint))
            return;

        dc.addOrderedRenderable(new OrderedText(text, textPoint, eyeDistance));
    }

    protected static boolean isTextValid(GeographicText text, boolean checkPosition)
    {
        if (text == null || text.getText() == null)
            return false;

        //noinspection RedundantIfStatement
        if (checkPosition && text.getPosition() == null)
            return false;

        return true;
    }

    protected class OrderedText implements OrderedRenderable, Comparable
    {
        GeographicText text;
        Vec4 point;
        double eyeDistance;

        OrderedText(GeographicText text, Vec4 point, double eyeDistance)
        {
            this.text = text;
            this.point = point;
            this.eyeDistance = eyeDistance;
        }

        // When overlapping text are culled we want to sort them front to back by priority.
        public int compareTo(OrderedText t)
        {
            if (t.text.getPriority() - this.text.getPriority() == 0)
            {
                return (int) (this.eyeDistance - t.eyeDistance);
            }
            else
                return (int) (t.text.getPriority() - this.text.getPriority());
        }

        public double getDistanceFromEye()
        {
            return this.eyeDistance;
        }

        private GeographicTextRenderer getRenderer()
        {
            return GeographicTextRenderer.this;
        }

        public void render(DrawContext dc)
        {
            GeographicTextRenderer.this.beginRendering(dc);
            try
            {
                if (cullText)
                {
                    ArrayList textList = new ArrayList();
                    textList.add(this);

                    // Draw as many as we can in a batch to save ogl state switching.
                    Object nextItem = dc.peekOrderedRenderables();
                    while (nextItem != null && nextItem instanceof OrderedText)
                    {
                        OrderedText ot = (OrderedText) nextItem;
                        if (ot.getRenderer() != GeographicTextRenderer.this)
                            break;

                        textList.add(ot);
                        dc.pollOrderedRenderables(); // take it off the queue
                        nextItem = dc.peekOrderedRenderables();
                    }

                    Collections.sort(textList); // sort for rendering priority then front to back

                    ArrayList textBounds = new ArrayList();
                    for (OrderedText ot : textList)
                    {
                        double[] scaleAndOpacity = GeographicTextRenderer.this.computeDistanceScaleAndOpacity(dc, ot);
                        Rectangle2D newBounds = GeographicTextRenderer.this.computeTextBounds(dc, ot,
                            scaleAndOpacity[0]);
                        if (newBounds == null)
                            continue;

                        boolean overlap = false;
                        newBounds = GeographicTextRenderer.this.computeExpandedBounds(newBounds, cullTextMargin);
                        for (Rectangle2D rect : textBounds)
                        {
                            if (rect.intersects(newBounds))
                                overlap = true;
                        }

                        if (!overlap)
                        {
                            textBounds.add(newBounds);
                            GeographicTextRenderer.this.drawText(dc, ot, scaleAndOpacity[0], scaleAndOpacity[1]);
                        }
                    }
                }
                else //just draw each label
                {
                    double[] scaleAndOpacity = GeographicTextRenderer.this.computeDistanceScaleAndOpacity(dc, this);
                    GeographicTextRenderer.this.drawText(dc, this, scaleAndOpacity[0], scaleAndOpacity[1]);
                    // Draw as many as we can in a batch to save ogl state switching.
                    Object nextItem = dc.peekOrderedRenderables();
                    while (nextItem != null && nextItem instanceof OrderedText)
                    {
                        OrderedText ot = (OrderedText) nextItem;
                        if (ot.getRenderer() != GeographicTextRenderer.this)
                            break;

                        scaleAndOpacity = GeographicTextRenderer.this.computeDistanceScaleAndOpacity(dc, ot);
                        GeographicTextRenderer.this.drawText(dc, ot, scaleAndOpacity[0], scaleAndOpacity[1]);
                        dc.pollOrderedRenderables(); // take it off the queue
                        nextItem = dc.peekOrderedRenderables();
                    }
                }
            }
            catch (WWRuntimeException e)
            {
                Logging.logger().log(java.util.logging.Level.SEVERE, "generic.ExceptionWhileRenderingText", e);
            }
            catch (Exception e)
            {
                Logging.logger().log(java.util.logging.Level.SEVERE, "generic.ExceptionWhileRenderingText", e);
            }
            finally
            {
                GeographicTextRenderer.this.endRendering(dc);
            }
        }

        public void pick(DrawContext dc, java.awt.Point pickPoint)
        {
        }
    }

    protected Rectangle2D computeTextBounds(DrawContext dc, OrderedText uText, double scale) throws Exception
    {
        GeographicText geographicText = uText.text;

        final CharSequence charSequence = geographicText.getText();
        if (charSequence == null)
            return null;

        final Vec4 screenPoint = dc.getView().project(uText.point);
        if (screenPoint == null)
            return null;

        Font font = geographicText.getFont();
        if (font == null)
            font = DEFAULT_FONT;

        try
        {
            TextRenderer textRenderer = OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), font);
            if (textRenderer != this.lastTextRenderer)
            {
                if (this.lastTextRenderer != null)
                    this.lastTextRenderer.end3DRendering();
                textRenderer.begin3DRendering();
                this.lastTextRenderer = textRenderer;
            }

            Rectangle2D textBound = textRenderer.getBounds(charSequence);
            double x = screenPoint.x - textBound.getWidth() / 2d;
            Rectangle2D bounds = new Rectangle2D.Float();
            bounds.setRect(x, screenPoint.y, textBound.getWidth(), textBound.getHeight());

            return computeScaledBounds(bounds, scale);
        }
        catch (Exception e)
        {
            handleTextRendererExceptions(e);
            return null;
        }
    }

    protected Rectangle2D computeScaledBounds(Rectangle2D bounds, double scale)
    {
        if (scale == 1)
            return bounds;

        // Scale rectangle from bottom center
        double halfWidth = bounds.getWidth() / 2;
        bounds.setRect(bounds.getX() + halfWidth - halfWidth * scale, bounds.getY(),
            bounds.getWidth() * scale, bounds.getHeight() * scale);
        return bounds;
    }

    protected Rectangle2D computeExpandedBounds(Rectangle2D bounds, int margin)
    {
        if (margin == 0)
            return bounds;

        // Add margin around rectangle
        bounds.setRect(bounds.getX() - margin, bounds.getY() - margin,
            bounds.getWidth() + margin * 2, bounds.getHeight() + margin * 2);
        return bounds;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    protected double[] computeDistanceScaleAndOpacity(DrawContext dc, OrderedText ot)
    {
        if (!this.isDistanceScaling)
            return new double[] {1, 1};

        // Determine scale and opacity factors based on distance from eye vs the distance to the look at point.
        double lookAtDistance = this.lookAtDistance;
        double eyeDistance = ot.getDistanceFromEye();
        double distanceFactor = Math.sqrt(lookAtDistance / eyeDistance);
        double scale = WWMath.clamp(distanceFactor,
            this.getDistanceMinScale(), this.getDistanceMaxScale());
        double opacity = WWMath.clamp(distanceFactor,
            this.getDistanceMinOpacity(), 1);

        return new double[] {scale, opacity};
    }

    protected double computeLookAtDistance(DrawContext dc)
    {
        View view = dc.getView();

        // Get point in the middle of the screen
        // TODO: Get a point on the surface rather then the geoid
        Position groundPos = view.computePositionFromScreenPoint(
            view.getViewport().getCenterX(), view.getViewport().getCenterY());

        // Update look at distance if center point found
        if (groundPos != null)
        {
            // Compute distance from eye to the position in the middle of the screen
            this.lookAtDistance = view.getEyePoint().distanceTo3(dc.getGlobe().computePointFromPosition(groundPos));
        }

        return this.lookAtDistance;
    }

    protected void beginRendering(DrawContext dc)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
        int attribBits =
            GL2.GL_ENABLE_BIT // for enable/disable changes
                | GL2.GL_COLOR_BUFFER_BIT // for alpha test func and ref, and blend
                | GL2.GL_CURRENT_BIT      // for current color
                | GL2.GL_DEPTH_BUFFER_BIT // for depth test, depth func, and depth mask
                | GL2.GL_TRANSFORM_BIT    // for modelview and perspective
                | GL2.GL_VIEWPORT_BIT;    // for depth range
        gl.glPushAttrib(attribBits);

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glPushMatrix();
        gl.glLoadIdentity();
        glu.gluOrtho2D(0, dc.getView().getViewport().width, 0, dc.getView().getViewport().height);
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glPushMatrix();
        gl.glLoadIdentity();
        gl.glMatrixMode(GL2.GL_TEXTURE);
        gl.glPushMatrix();
        gl.glLoadIdentity();
        // Set model view as current matrix mode
        gl.glMatrixMode(GL2.GL_MODELVIEW);

        // Enable the depth test but don't write to the depth buffer.
        gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glDepthMask(false);

        // Suppress polygon culling.
        gl.glDisable(GL.GL_CULL_FACE);

        // Suppress any fully transparent image pixels
        gl.glEnable(GL2.GL_ALPHA_TEST);
        gl.glAlphaFunc(GL2.GL_GREATER, 0.001f);

        // Cache distance scaling values
        this.isDistanceScaling = this.getDistanceMinScale() != 1 || this.getDistanceMaxScale() != 1
            || this.distanceMinOpacity != 1;
        this.computeLookAtDistance(dc);
    }

    protected void endRendering(DrawContext dc)
    {
        if (this.lastTextRenderer != null)
        {
            this.lastTextRenderer.end3DRendering();
            this.lastTextRenderer = null;
        }

        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glPopMatrix();
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glPopMatrix();
        gl.glMatrixMode(GL2.GL_TEXTURE);
        gl.glPopMatrix();

        gl.glPopAttrib();
    }

    protected Vec4 drawText(DrawContext dc, OrderedText uText, double scale, double opacity) throws Exception
    {
        if (uText.point == null)
        {
            String msg = Logging.getMessage("nullValue.PointIsNull");
            Logging.logger().fine(msg);
            return null;
        }

        GeographicText geographicText = uText.text;
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

        final CharSequence charSequence = geographicText.getText();
        if (charSequence == null)
            return null;

        final Vec4 screenPoint = dc.getView().project(uText.point);
        if (screenPoint == null)
            return null;

        Font font = geographicText.getFont();
        if (font == null)
            font = DEFAULT_FONT;

        try
        {
            TextRenderer textRenderer = OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), font);
            if (textRenderer != this.lastTextRenderer)
            {
                if (this.lastTextRenderer != null)
                    this.lastTextRenderer.end3DRendering();
                textRenderer.begin3DRendering();
                this.lastTextRenderer = textRenderer;
            }

            this.setDepthFunc(dc, uText, screenPoint);

            Rectangle2D textBounds = textRenderer.getBounds(
                charSequence);//note:may already be calculated during culling
            textBounds = this.computeScaledBounds(textBounds, scale);
            Point.Float drawPoint = computeDrawPoint(dc, textBounds, screenPoint);

            if (drawPoint != null)
            {
                if (scale != 1d)
                {
                    gl.glScaled(scale, scale, 1d);
                    drawPoint.setLocation(drawPoint.x / (float) scale, drawPoint.y / (float) scale);
                }

                Color color = geographicText.getColor();
                if (color == null)
                    color = DEFAULT_COLOR;
                color = this.applyOpacity(color, opacity);

                Color background = geographicText.getBackgroundColor();
                if (background != null)
                {
                    background = this.applyOpacity(background, opacity);
                    textRenderer.setColor(background);
                    if (this.effect.equals(AVKey.TEXT_EFFECT_SHADOW))
                    {
                        textRenderer.draw3D(charSequence, drawPoint.x + 1, drawPoint.y - 1, 0, 1);
                    }
                    else if (this.effect.equals(AVKey.TEXT_EFFECT_OUTLINE))
                    {
                        textRenderer.draw3D(charSequence, drawPoint.x + 1, drawPoint.y - 1, 0, 1);
                        textRenderer.draw3D(charSequence, drawPoint.x + 1, drawPoint.y + 1, 0, 1);
                        textRenderer.draw3D(charSequence, drawPoint.x - 1, drawPoint.y - 1, 0, 1);
                        textRenderer.draw3D(charSequence, drawPoint.x - 1, drawPoint.y + 1, 0, 1);
                    }
                }

                textRenderer.setColor(color);
                textRenderer.draw3D(charSequence, drawPoint.x, drawPoint.y, 0, 1);
                textRenderer.flush();

                if (scale != 1d)
                    gl.glLoadIdentity();
            }
        }
        catch (Exception e)
        {
            handleTextRendererExceptions(e);
        }

        return screenPoint;
    }

    protected Color applyOpacity(Color color, double opacity)
    {
        if (opacity >= 1)
            return color;

        float[] compArray = color.getRGBComponents(null);
        return new Color(compArray[0], compArray[1], compArray[2], compArray[3] * (float) opacity);
    }

    private void handleTextRendererExceptions(Exception e) throws Exception
    {
        if (e instanceof IOException)
        {
            if (!this.hasJOGLv111Bug)
            {
                // This is likely a known JOGL 1.1.1 bug - see AMZN-287 or 343
                // Log once and then ignore.
                Logging.logger().log(java.util.logging.Level.SEVERE, "generic.ExceptionWhileRenderingText", e);
                this.hasJOGLv111Bug = true;
            }
        }
        else
        {
            throw e;
        }
    }

    /**
     * Computes the final draw point for the given rectangle lower left corner and target screen point. If the returned
     * point is null the text will not be drawn.
     *
     * @param dc          the current {@link DrawContext}
     * @param rect        the text rectangle to draw.
     * @param screenPoint the projected screen point the text relates to.
     *
     * @return the final draw point for the given rectangle lower left corner or null.
     */
    @SuppressWarnings({"UnusedDeclaration"})
    protected Point.Float computeDrawPoint(DrawContext dc, Rectangle2D rect, Vec4 screenPoint)
    {
        return new Point.Float((float) (screenPoint.x - rect.getWidth() / 2d), (float) (screenPoint.y));
    }

    @SuppressWarnings({"UnusedDeclaration"})
    protected void setDepthFunc(DrawContext dc, OrderedText uText, Vec4 screenPoint)
    {
        GL gl = dc.getGL();

        //if (uText.text.isAlwaysOnTop())
        //{
        //    gl.glDepthFunc(GL.GL_ALWAYS);
        //    return;
        //}

        Position eyePos = dc.getView().getEyePosition();
        if (eyePos == null)
        {
            gl.glDepthFunc(GL.GL_ALWAYS);
            return;
        }

        double altitude = eyePos.getElevation();
        if (altitude < (dc.getGlobe().getMaxElevation() * dc.getVerticalExaggeration()))
        {
            double depth = screenPoint.z - (8d * 0.00048875809d);
            depth = depth < 0d ? 0d : (depth > 1d ? 1d : depth);
            gl.glDepthFunc(GL.GL_LESS);
            gl.glDepthRange(depth, depth);
        }
        //else if (screenPoint.z >= 1d)
        //{
        //    gl.glDepthFunc(GL.GL_EQUAL);
        //    gl.glDepthRange(1d, 1d);
        //}
        else
        {
            gl.glDepthFunc(GL.GL_ALWAYS);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy