/******************************************************************************* * Copyright 2014 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.worldwind.common.layers.atmosphere; import gov.nasa.worldwind.View; import gov.nasa.worldwind.geom.Angle; import gov.nasa.worldwind.geom.Line; import gov.nasa.worldwind.geom.Matrix; import gov.nasa.worldwind.geom.Plane; import gov.nasa.worldwind.geom.Vec4; import gov.nasa.worldwind.layers.AbstractLayer; import gov.nasa.worldwind.render.DrawContext; import gov.nasa.worldwind.util.OGLStackHandler; import javax.media.opengl.GL2; import au.gov.ga.earthsci.worldwind.common.sun.SunPositionService; import au.gov.ga.earthsci.worldwind.common.util.GeometryUtil; import au.gov.ga.earthsci.worldwind.common.view.delegate.IDelegateView; /** * Layer that renders atmospheric scattering effects. * <p/> * Based on Sean O'Neil's algorithm described in GPU Gems 2 <a * href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html" * >chapter 16</a>. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public abstract class AbstractAtmosphereLayer extends AbstractLayer { private boolean inited = false; private double[] clippingPlane = new double[4]; @Override protected void doRender(DrawContext dc) { if (!inited) { init(dc); inited = true; } renderAtmosphere(dc); } protected abstract void init(DrawContext dc); protected void renderAtmosphere(DrawContext dc) { GL2 gl = dc.getGL().getGL2(); //setup variables float innerRadius = (float) dc.getGlobe().getRadius(); float outerRadius = innerRadius * Atmosphere.ATMOSPHERE_SCALE; View view = dc.getView(); Vec4 eyePoint = view.getEyePoint(); float eyeMagnitude = (float) eyePoint.getLength3(); Vec4 lightDirection = SunPositionService.INSTANCE.getDirection(dc.getView()); OGLStackHandler ogsh = new OGLStackHandler(); try { ogsh.pushProjection(gl); loadProjection(dc, outerRadius); ogsh.pushAttrib(gl, GL2.GL_TEXTURE_BIT | GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT | GL2.GL_POLYGON_BIT | GL2.GL_TRANSFORM_BIT | GL2.GL_ENABLE_BIT | attribBitsToPush()); gl.glColor4f(1f, 1f, 1f, 1f); setupClippingPlane(dc); renderAtmosphere(dc, lightDirection, eyePoint, eyeMagnitude, innerRadius, outerRadius); } finally { ogsh.pop(gl); } } protected void loadProjection(DrawContext dc, float outerRadius) { IDelegateView view = (IDelegateView) dc.getView(); double far = view.getEyePoint().getLength3() + view.getGlobe().getRadius(); double near = 1000; Matrix projection = view.computeProjection(near, far); if (projection != null) { double[] matrixArray = new double[16]; GL2 gl = dc.getGL().getGL2(); projection.toArray(matrixArray, 0, false); gl.glLoadMatrixd(matrixArray, 0); } } protected void setupClippingPlane(DrawContext dc) { View view = dc.getView(); Vec4 forward = view.getForwardVector(); Vec4 eyeNormalized = view.getEyePoint().normalize3(); Angle pitch = eyeNormalized.angleBetween3(forward); if (pitch.degrees < 90) { //don't clip the sky when looking up at it return; } Vec4 up = view.getUpVector(); Vec4 side = forward.cross3(up); Vec4 origin = Vec4.ZERO; double depth = view.getGlobe().getRadius() * 0.5; Vec4 add = forward.multiply3(depth); Vec4 v1 = origin.add3(add); Vec4 v2 = up.add3(add); Vec4 v3 = side.add3(add); Line l1 = Line.fromSegment(v1, v3); Line l2 = Line.fromSegment(v1, v2); Plane plane = GeometryUtil.createPlaneContainingLines(l1, l2); Vec4 v = plane.getVector(); clippingPlane[0] = v.x; clippingPlane[1] = v.y; clippingPlane[2] = v.z; clippingPlane[3] = v.w; GL2 gl = dc.getGL().getGL2(); gl.glEnable(GL2.GL_CLIP_PLANE5); gl.glClipPlane(GL2.GL_CLIP_PLANE5, clippingPlane, 0); } protected abstract int attribBitsToPush(); protected abstract void renderAtmosphere(DrawContext dc, Vec4 lightDirection, Vec4 eyePoint, float eyeMagnitude, float innerRadius, float outerRadius); }