/******************************************************************************* * 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.stars; import gov.nasa.worldwind.geom.Angle; import gov.nasa.worldwind.geom.Matrix; 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 java.awt.image.BufferedImage; import java.io.EOFException; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import javax.media.opengl.GL2; import au.gov.ga.earthsci.worldwind.common.render.fastshape.FloatVBO; import au.gov.ga.earthsci.worldwind.common.view.delegate.IDelegateView; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureData; import com.jogamp.opengl.util.texture.TextureIO; import com.jogamp.opengl.util.texture.awt.AWTTextureIO; /** * Layer that renders stars using a point sprite texture. The stars are rendered * like a skybox, using a unit sphere, so that they appear at an almost infinite * distance; thus the camera cannot move outside the star field sphere. * <p/> * Stars are read from a file created from the Yale Bright Star Catalog, which * contains approximately 9000 stars. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class InfiniteStarsLayer extends AbstractLayer { protected boolean inited = false; protected final FloatVBO vbo = new FloatVBO(4); protected final FloatVBO cbo = new FloatVBO(3); protected Texture starTexture; protected final InfiniteStarsShader shader = new InfiniteStarsShader(); @Override protected void doRender(DrawContext dc) { if (!inited) { init(dc); inited = true; } if (shader.isCreationFailed()) { return; } GL2 gl = dc.getGL().getGL2(); OGLStackHandler ogsh = new OGLStackHandler(); try { ogsh.pushAttrib(gl, GL2.GL_TEXTURE_BIT | GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT | GL2.GL_POINT_BIT); ogsh.pushClientAttrib(gl, GL2.GL_CLIENT_VERTEX_ARRAY_BIT); ogsh.pushProjection(gl); ogsh.pushModelview(gl); IDelegateView view = (IDelegateView) dc.getView(); double near = 0.1; double far = near + 1.0; double normalizedEyeSize = view.getEyePoint().getLength3() / dc.getGlobe().getRadius(); double loglog = Math.log(Math.log(normalizedEyeSize) + 1) + 1; double fovMultiplier = loglog / 4.0; double extraFieldOfView = 20 * fovMultiplier; //as we zoom out/in, change the FOV slightly Angle fieldOfView = Angle.fromDegrees(Math.max(view.getFieldOfView().degrees, 70) + extraFieldOfView); //limit the minimum FOV Matrix projection = view.computeProjection(fieldOfView, near, far); double[] matrixArray = new double[16]; projection.toArray(matrixArray, 0, false); gl.glMatrixMode(GL2.GL_PROJECTION); gl.glLoadMatrixd(matrixArray, 0); Vec4 up = dc.getView().getUpVector(); Vec4 f = dc.getView().getForwardVector(); Vec4 s = f.cross3(up); Vec4 u = s.cross3(f); Matrix rotation = new Matrix( s.x, s.y, s.z, 0.0, u.x, u.y, u.z, 0.0, -f.x, -f.y, -f.z, 0.0, 0.0, 0.0, 0.0, 1.0); rotation.toArray(matrixArray, 0, false); gl.glMatrixMode(GL2.GL_MODELVIEW); gl.glLoadMatrixd(matrixArray, 0); gl.glDepthMask(false); gl.glDisable(GL2.GL_DEPTH_TEST); gl.glEnable(GL2.GL_POINT_SMOOTH); gl.glEnable(GL2.GL_POINT_SPRITE); gl.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); gl.glEnable(GL2.GL_BLEND); gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); gl.glActiveTexture(GL2.GL_TEXTURE0); gl.glEnable(GL2.GL_TEXTURE_2D); gl.glTexEnvi(GL2.GL_POINT_SPRITE, GL2.GL_COORD_REPLACE, GL2.GL_TRUE); starTexture.bind(gl); gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); vbo.bind(gl); gl.glVertexPointer(vbo.getElementStride(), GL2.GL_FLOAT, 0, 0); gl.glEnableClientState(GL2.GL_COLOR_ARRAY); cbo.bind(gl); gl.glColorPointer(cbo.getElementStride(), GL2.GL_FLOAT, 0, 0); useShader(gl); { gl.glDrawArrays(GL2.GL_POINTS, 0, vbo.getBuffer().length / vbo.getElementStride()); } unuseShader(gl); gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); } finally { ogsh.pop(gl); } } protected void useShader(GL2 gl) { shader.use(gl); } protected void unuseShader(GL2 gl) { shader.unuse(gl); } protected void init(DrawContext dc) { GL2 gl = dc.getGL().getGL2(); if (!shader.create(gl)) { return; } try { BufferedImage starImage = ImageIO.read(InfiniteStarsLayer.class.getResourceAsStream("star.png")); //$NON-NLS-1$ TextureData textureData = AWTTextureIO.newTextureData(gl.getGLProfile(), starImage, true); starTexture = TextureIO.newTexture(textureData); } catch (IOException e) { e.printStackTrace(); } try { ObjectInputStream is = new ObjectInputStream(InfiniteStarsLayer.class.getResourceAsStream("stars.dat")); //$NON-NLS-1$ List<Vec4> list = new ArrayList<Vec4>(); List<Integer> colors = new ArrayList<Integer>(); while (true) { try { float rightAscension = is.readFloat(); float declination = is.readFloat(); float magnitude = is.readFloat(); int color = is.readInt(); Vec4 cartesian = sphericalToCartesian(declination, rightAscension, 1); Vec4 withMagnitude = new Vec4(cartesian.x, cartesian.y, cartesian.z, magnitude); list.add(withMagnitude); colors.add(color); } catch (EOFException e) { break; } } float[] buffer = new float[list.size() * 4]; int pos = 0; for (Vec4 v : list) { buffer[pos++] = (float) v.x; buffer[pos++] = (float) v.y; buffer[pos++] = (float) v.z; buffer[pos++] = (float) v.w; } vbo.setBuffer(buffer); buffer = new float[colors.size() * 3]; pos = 0; for (Integer color : colors) { int r = (color >> 16) & 0xff; int g = (color >> 8) & 0xff; int b = (color) & 0xff; buffer[pos++] = r / 255f; buffer[pos++] = g / 255f; buffer[pos++] = b / 255f; } cbo.setBuffer(buffer); } catch (IOException e) { e.printStackTrace(); } } protected static Vec4 sphericalToCartesian(double declination, double rightAscension, double radius) { declination *= Math.PI / 180.0f; rightAscension *= Math.PI / 180.0f; double radCosLat = radius * Math.cos(declination); return new Vec4( radCosLat * Math.sin(rightAscension), radius * Math.sin(declination), radCosLat * Math.cos(rightAscension)); } @Override public String toString() { return "Stars"; } }