⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 atmospherelayer.java

📁 world wind java sdk 源码
💻 JAVA
字号:
package gov.nasa.worldwind.examples.sunlight;

import gov.nasa.worldwind.View;
import gov.nasa.worldwind.layers.AbstractLayer;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.util.*;

import javax.media.opengl.*;
import java.awt.*;

/**
 * Renders an atmosphere around the globe and a sky dome at low altitude.
 * Uses atmospheric scattering as color source.
 * <p>
 * Issue : Ellipsoidal globe doesnt match the spherical atmosphere everywhere.
 * <p>
 * TODO: Find a way to get a blue sky at ground level
 * TODO: Increase dome geometry resolution and implement partial sphere
 *
 * @author Patrick Murris
 * @version $Id: AtmosphereLayer.java 10406 2009-04-22 18:28:45Z patrickmurris $
 */
public class AtmosphereLayer extends AbstractLayer
{
    protected final static int STACKS = 24;
    protected final static int SLICES = 64;

    protected int glListId = -1;        // GL list id
    protected double thickness = 60e3; // Atmosphere thickness
    protected double lastRebuildHorizon = 0;

    protected AtmosphericScatteringComputer asc;
    protected Vec4 sunDirection;
    protected boolean update = true;

    /**
     * Renders an atmosphere around the globe
     */
    public AtmosphereLayer() {
    }

    /**
     * Get the atmosphere thickness in meter
     * @return the atmosphere thickness in meter
     */
    public double getAtmosphereThickness()
    {
        return this.thickness;
    }

    /**
     * Set the atmosphere thickness in meter
     * @param thickness the atmosphere thickness in meter
     */
    public void setAtmosphereThickness(double thickness)
    {
        if (thickness < 0)
        {
            String msg = Logging.getMessage("generic.ArgumentOutOfRange");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }
        this.thickness = thickness;
        this.asc = null; // invalidate atmospheric scattering computer
        this.update = true;
    }

    public Vec4 getSunDirection()
    {
        return this.sunDirection;
    }

    public void setSunDirection(Vec4 direction)
    {
        this.sunDirection = direction;
        this.update = true;
    }

    @Override
    public void doRender(DrawContext dc)
    {
        GL gl = dc.getGL();
        boolean attribsPushed = false;
        boolean modelviewPushed = false;
        boolean projectionPushed = false;

        try {
            View view = dc.getView();
            Position camPos = dc.getGlobe().computePositionFromPoint(view.getEyePoint());
            double worldRadius = dc.getGlobe().getRadiusAt(camPos);
            double distToCenterOfPlanet = view.getEyePoint().getLength3();
            double camAlt = camPos.getElevation();
            double tangentalDistance = view.computeHorizonDistance();
            // Dome radius
            double domeRadius = tangentalDistance;

            // horizon latitude degrees
            double horizonLat = (-Math.PI / 2 + Math.acos(tangentalDistance / distToCenterOfPlanet))
                    * 180 / Math.PI;
            // zenith latitude degrees
            double zenithLat = 90;
            if (camAlt >= thickness) {
                double tangentalDistanceZenith = Math.sqrt(distToCenterOfPlanet * distToCenterOfPlanet
                        - (worldRadius + thickness) * (worldRadius + thickness));
                zenithLat = (-Math.PI / 2 + Math.acos(tangentalDistanceZenith / distToCenterOfPlanet)) * 180 / Math.PI;
            }
            if (camAlt < thickness && camAlt > thickness * 0.7) {
                zenithLat = (thickness - camAlt) / (thickness - thickness * 0.7) * 90;
            }

            // Build or rebuild sky dome if horizon distance changed more then 100m
             if (this.update || this.glListId == -1 || Math.abs(this.lastRebuildHorizon - tangentalDistance) > 100)
             {
                 if (this.glListId != -1)
                     gl.glDeleteLists(this.glListId, 1);

                 this.makeSkyDome(dc, (float) (domeRadius), horizonLat, zenithLat, SLICES, STACKS);
                 this.lastRebuildHorizon = tangentalDistance;
                 this.update = false;
             }

            // GL set up
            gl.glPushAttrib(GL.GL_POLYGON_BIT); // Temporary hack around aliased sky.
            gl.glPopAttrib();

            gl.glPushAttrib(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_TRANSFORM_BIT
                    | GL.GL_POLYGON_BIT | GL.GL_TEXTURE_BIT | GL.GL_ENABLE_BIT
                    | GL.GL_CURRENT_BIT);
            attribsPushed = true;
            gl.glDisable(GL.GL_TEXTURE_2D);        // no textures
            gl.glDisable(GL.GL_DEPTH_TEST);
            gl.glDepthMask(false);

            Matrix projection = Matrix.fromPerspective(view.getFieldOfView(),
                            view.getViewport().getWidth(), view.getViewport().getHeight(),
                            10e3, 2 * distToCenterOfPlanet + 10e3);
            double[] matrixArray = new double[16];
            projection.toArray(matrixArray, 0, false);
            gl.glMatrixMode(GL.GL_PROJECTION);
            gl.glPushMatrix();
            projectionPushed = true;
            gl.glLoadMatrixd(matrixArray, 0);

            gl.glMatrixMode(GL.GL_MODELVIEW);
            gl.glPushMatrix();
            modelviewPushed = true;
            // Sky transform
            Matrix skyTransform = computeSkyTransform(dc);
            Matrix modelView = view.getModelviewMatrix().multiply(skyTransform);
            modelView.toArray(matrixArray, 0, false);
            gl.glLoadMatrixd(matrixArray, 0);
             // Draw sky
             if (this.glListId != -1)
                 gl.glCallList(this.glListId);

        }
        finally {
            // Restore GL state
            if (modelviewPushed)
            {
                gl.glMatrixMode(GL.GL_MODELVIEW);
                gl.glPopMatrix();
            }
            if (projectionPushed)
            {
                gl.glMatrixMode(GL.GL_PROJECTION);
                gl.glPopMatrix();
            }
            if (attribsPushed)
                gl.glPopAttrib();
        }
    }

    /**
     * Build sky dome and draw into a glList
     *
     * @param dc the current DrawContext
     * @param radius the sky dome radius in meters.
     * @param startLat the horizon latitude in decimal degrees.
     * @param endLat the zenith latitude in decimal degrees.
     * @param slices the number of longitude divisions used for the dome geometry.
     * @param stacks the number of latitude divisions used for the dome geometry.
     */
    protected void makeSkyDome(DrawContext dc, float radius, double startLat, double endLat,
                                int slices, int stacks)
    {
        if (this.sunDirection == null)
            return;
        
        GL gl = dc.getGL();
        this.glListId = gl.glGenLists(1);
        gl.glNewList(this.glListId, GL.GL_COMPILE);
        this.drawSkyGradient(dc, radius, startLat, endLat, slices, stacks);
        gl.glEndList();
    }

    /**
     * Draws the sky dome
     *
     * @param dc       the current DrawContext
     * @param radius   the sky dome radius
     * @param startLat the horizon latitude
     * @param endLat   the zenith latitude
     * @param slices   the number of slices - vertical divisions
     * @param stacks   the nuber os stacks - horizontal divisions
     */
    protected void drawSkyGradient(DrawContext dc, float radius, double startLat, double endLat,
                                int slices, int stacks)
    {
        // Init atmospheric scattering computer
        if (this.asc == null)
            this.asc = new AtmosphericScatteringComputer(dc.getGlobe().getRadius(), this.thickness);

        // Get sky dome transform
        Matrix skyTransform = computeSkyTransform(dc);

        // GL setup
        GL gl = dc.getGL();
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        gl.glEnable(GL.GL_BLEND);
        gl.glDisable(GL.GL_TEXTURE_2D);
        //gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);    // wireframe

        double latitude, longitude, latitudeTop = endLat;
        double linear, linearTop, k, kTop;
        Color color;
        Color[] stackColors = new Color[slices + 1];
        Vec4 eyePoint = dc.getView().getEyePoint();

        // bottom fade
        latitude = startLat - Math.max((endLat - startLat) / 4, 2);
        gl.glBegin(GL.GL_QUAD_STRIP);
        for (int slice = 0; slice <= slices; slice++)
        {
            longitude = 180 - ((float) slice / slices * (float) 360);
            Vec4 v1 = SphericalToCartesian(latitude, longitude, radius);
            Vec4 v2 = SphericalToCartesian(startLat, longitude, radius);
            color = this.asc.getAtmosphereColor(v2.transformBy4(skyTransform), eyePoint, this.sunDirection);
            gl.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 0f);
            gl.glVertex3d(v1.getX(), v1.getY(), v1.getZ());
            gl.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
            gl.glVertex3d(v2.getX(), v2.getY(), v2.getZ());
            stackColors[slice] = color;
        }
        gl.glEnd();

        // stacks and slices
        for (int stack = 1; stack < stacks - 1; stack++)
        {
            // bottom vertex
            linear = (float) (stack - 1) / (stacks - 1f);
            k = 1 - Math.cos(linear * Math.PI / 2);
            latitude = startLat + Math.pow(k, 3) * (endLat - startLat);
            // top vertex
            linearTop = (float) (stack) / (stacks - 1f);
            kTop = 1 - Math.cos(linearTop * Math.PI / 2);
            latitudeTop = startLat + Math.pow(kTop, 3) * (endLat - startLat);
            // Draw stack
            gl.glBegin(GL.GL_QUAD_STRIP);
            for (int slice = 0; slice <= slices; slice++)
            {
                longitude = 180 - ((float) slice / slices * (float) 360);
                Vec4 v = SphericalToCartesian(latitude, longitude, radius);
                color = stackColors[slice];
                gl.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
                gl.glVertex3d(v.getX(), v.getY(), v.getZ());
                v = SphericalToCartesian(latitudeTop, longitude, radius);
                color = this.asc.getAtmosphereColor(v.transformBy4(skyTransform), eyePoint, this.sunDirection);
                gl.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
                gl.glVertex3d(v.getX(), v.getY(), v.getZ());
                stackColors[slice] = color;
            }
            gl.glEnd();
        }

        // Top fade
        if (endLat < 90)
        {
            gl.glBegin(GL.GL_QUAD_STRIP);
            for (int slice = 0; slice <= slices; slice++) {
                longitude = 180 - ((float) slice / slices * (float) 360);
                Vec4 v = SphericalToCartesian(latitudeTop, longitude, radius);
                color = stackColors[slice];
                gl.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
                gl.glVertex3d(v.getX(), v.getY(), v.getZ());
                v = SphericalToCartesian(endLat, longitude, radius);
                gl.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 0);
                gl.glVertex3d(v.getX(), v.getY(), v.getZ());
            }
            gl.glEnd();
        }

        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glDisable(GL.GL_BLEND);
    }

    protected Matrix computeSkyTransform(DrawContext dc)
    {
        Matrix transform = Matrix.IDENTITY;
        transform = transform.multiply(dc.getGlobe().computeTransformToPosition(dc.getView().getEyePosition()));
        transform = transform.multiply(Matrix.fromRotationX(Angle.POS90));
        return transform;
    }

    /**
     * Converts position in spherical coordinates (lat/lon/altitude)
     * to cartesian (XYZ) coordinates.
     *
     * @param latitude  Latitude in decimal degrees
     * @param longitude Longitude in decimal degrees
     * @param radius    Radius
     * @return the corresponding Point
     */
    protected static Vec4 SphericalToCartesian(double latitude, double longitude, double radius) {
        latitude *= Math.PI / 180.0f;
        longitude *= Math.PI / 180.0f;

        double radCosLat = radius * Math.cos(latitude);

        return new Vec4(
                radCosLat * Math.sin(longitude),
                radius * Math.sin(latitude),
                radCosLat * Math.cos(longitude));
    }

    public void dispose()
    {
        if (this.glListId < 0)
            return;

        GLContext glc = GLContext.getCurrent();
        if (glc == null)
            return;

        glc.getGL().glDeleteLists(this.glListId, 1);
        this.glListId = -1;
    }

    @Override
    public String toString() {
        return Logging.getMessage("layers.Earth.SkyGradientLayer.Name");
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -