// Copyright 2009 Google Inc. // // 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 com.google.android.stardroid.renderer; import android.util.Log; import com.google.android.stardroid.renderer.util.ColorBuffer; import com.google.android.stardroid.renderer.util.IndexBuffer; import com.google.android.stardroid.renderer.util.TextureManager; import com.google.android.stardroid.renderer.util.VertexBuffer; import com.google.android.stardroid.units.GeocentricCoordinates; import com.google.android.stardroid.units.Vector3; import com.google.android.stardroid.util.MathUtil; import com.google.android.stardroid.util.VectorUtil; import javax.microedition.khronos.opengles.GL10; public class SkyBox extends RendererObjectManager { public SkyBox(int layer, TextureManager textureManager) { super(layer, textureManager); int numVertices = NUM_VERTEX_BANDS * NUM_STEPS_IN_BAND; int numIndices = (NUM_VERTEX_BANDS-1) * NUM_STEPS_IN_BAND * 6; mVertexBuffer.reset(numVertices); mColorBuffer.reset(numVertices); mIndexBuffer.reset(numIndices); float[] sinAngles = new float[NUM_STEPS_IN_BAND]; float[] cosAngles = new float[NUM_STEPS_IN_BAND]; float angleInBand = 0; float dAngle = MathUtil.TWO_PI / (NUM_STEPS_IN_BAND - 1); for (int i = 0; i < NUM_STEPS_IN_BAND; i++) { sinAngles[i] = MathUtil.sin(angleInBand); cosAngles[i] = MathUtil.cos(angleInBand); angleInBand += dAngle; } float bandStep = 2.0f / (NUM_VERTEX_BANDS-1) + EPSILON; VertexBuffer vb = mVertexBuffer; ColorBuffer cb = mColorBuffer; float bandPos = 1; for (int band = 0; band < NUM_VERTEX_BANDS; band++, bandPos -= bandStep) { int color; if (bandPos > 0) { // TODO(jpowell): This isn't really intensity, name it more appropriately. // I=70 at bandPos = 1, I=50 at bandPos = 0 byte intensity = (byte)(bandPos * 20 + 50); color = (intensity << 16) | 0xff000000; } else { // I=40 at bandPos = -1, I=0 at bandPos = 0 byte intensity = (byte)(bandPos * 40 + 40); color = (intensity << 16) | (intensity << 8) | intensity | 0xff000000; } float sinPhi = bandPos > -1 ? MathUtil.sqrt(1 - bandPos*bandPos) : 0; for (int i = 0; i < NUM_STEPS_IN_BAND; i++) { vb.addPoint(cosAngles[i] * sinPhi, bandPos, sinAngles[i] * sinPhi); cb.addColor(color); } } Log.d("SkyBox", "Vertices: " + vb.size()); IndexBuffer ib = mIndexBuffer; // Set the indices for the first band. short topBandStart = 0; short bottomBandStart = NUM_STEPS_IN_BAND; for (short triangleBand = 0; triangleBand < NUM_VERTEX_BANDS-1; triangleBand++) { for (short offsetFromStart = 0; offsetFromStart < NUM_STEPS_IN_BAND-1; offsetFromStart++) { // Draw one quad as two triangles. short topLeft = (short)(topBandStart + offsetFromStart); short topRight = (short)(topLeft + 1); short bottomLeft = (short)(bottomBandStart + offsetFromStart); short bottomRight = (short)(bottomLeft + 1); // First triangle ib.addIndex(topLeft); ib.addIndex(bottomRight); ib.addIndex(bottomLeft); // Second triangle ib.addIndex(topRight); ib.addIndex(bottomRight); ib.addIndex(topLeft); } // Last quad: connect the end with the beginning. // Top left, bottom right, bottom left ib.addIndex((short)(topBandStart + NUM_STEPS_IN_BAND - 1)); ib.addIndex(bottomBandStart); ib.addIndex((short)(bottomBandStart + NUM_STEPS_IN_BAND - 1)); // Top right, bottom right, top left ib.addIndex(topBandStart); ib.addIndex(bottomBandStart); ib.addIndex((short)(topBandStart + NUM_STEPS_IN_BAND - 1)); topBandStart += NUM_STEPS_IN_BAND; bottomBandStart += NUM_STEPS_IN_BAND; } Log.d("SkyBox", "Indices: " + ib.size()); } @Override public void reload(GL10 gl, boolean fullReload) { mVertexBuffer.reload(); mColorBuffer.reload(); mIndexBuffer.reload(); } public void setSunPosition(GeocentricCoordinates pos) { mSunPos = pos.copy(); //Log.d("SkyBox", "SunPos: " + pos.toString()); } @Override protected void drawInternal(GL10 gl) { if (getRenderState().getNightVisionMode()) { return; } gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glEnable(GL10.GL_CULL_FACE); gl.glFrontFace(GL10.GL_CW); gl.glCullFace(GL10.GL_BACK); gl.glShadeModel(GL10.GL_SMOOTH); gl.glPushMatrix(); // Rotate the sky box to the position of the sun. Vector3 cp = VectorUtil.crossProduct(new Vector3(0, 1, 0), mSunPos); cp = VectorUtil.normalized(cp); float angle = 180.0f / MathUtil.PI * MathUtil.acos(mSunPos.y); gl.glRotatef(angle, cp.x, cp.y, cp.z); mVertexBuffer.set(gl); mColorBuffer.set(gl); mIndexBuffer.draw(gl, GL10.GL_TRIANGLES); gl.glPopMatrix(); } private static final short NUM_VERTEX_BANDS = 8; // This number MUST be even private static final short NUM_STEPS_IN_BAND = 10; // Used to make sure rounding error doesn't make us have off-by-one errors in our iterations. private static final float EPSILON = 1e-3f; VertexBuffer mVertexBuffer = new VertexBuffer(true); ColorBuffer mColorBuffer = new ColorBuffer(true); IndexBuffer mIndexBuffer = new IndexBuffer(true); GeocentricCoordinates mSunPos = new GeocentricCoordinates(0, 1, 0); }