package gl; import gl.textures.TextureManager; import java.nio.FloatBuffer; import java.util.ArrayList; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import util.Log; import util.Vec; import android.opengl.GLU; import android.os.SystemClock; /** * This is the OpenGL renderer used for the {@link CustomGLSurfaceView} * * @author Spobo * */ public class GL1Renderer extends GLRenderer { private static final String LOG_TAG = "GLRenderer"; private boolean useLightning = false; private boolean switchLightning = false; public void setUseLightning(boolean useLightning) { this.switchLightning = true; this.useLightning = useLightning; } /** * the light list should be contained in the {@link GL1Renderer} because * there is a global maximum of 8 lights in the complete OpenGL ES * environment and not only per world */ private ArrayList<LightSource> myLights; public ArrayList<LightSource> getMyLights() { if (myLights == null) { myLights = new ArrayList<LightSource>(); } return myLights; } /** * TODO move to extra object! And: * * Fog isn't fully supported yet because the color picking mechanism wont * work with fog enabled. fog should be disabled for the picking frames. * this has to be implemented first */ private static final boolean USE_FOG = false; private static final float FOG_END_DISTANCE = 25.0f; private static final float FOG_START_DISTANCE = 2.0f; private static final FloatBuffer FOG_COLOR = new Color(0, 0, 0, 0) .toFloatBuffer(); private static final boolean FLASH_SCREEN = false; private final ArrayList<Renderable> elementsToRender = new ArrayList<Renderable>(); private boolean readyToPickPixel; @Override public void onDrawFrame(GL10 gl) { if (pauseRenderer) { startPauseLoop(); } final long currentTime = SystemClock.uptimeMillis(); // if the lightning was recently enabled/disabled if (switchLightning) { switchLightning = false; if (useLightning) { gl.glEnable(GL10.GL_LIGHTING); } else { gl.glDisable(GL10.GL_LIGHTING); } } if (ObjectPicker.readyToDrawWithColor) { readyToPickPixel = true; if (useLightning) { /* * before the picking is executed lightning has to be disabled * for the picking frame because it affects the colors of the * objects and picking would not be possible with lightning * enabled */ gl.glDisable(GL10.GL_LIGHTING); } } // first check if there are new textures to load into openGL: TextureManager.getInstance().updateTextures(gl); // TODO optimize? check // boolean boolean repeat; do { // Clears the screen and depth buffer. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); for (int i = 0; i < elementsToRender.size(); i++) { // Reset the modelview matrix gl.glLoadIdentity(); elementsToRender.get(i).render(gl, null); } repeat = false; if (readyToPickPixel) { ObjectPicker.getInstance().pickObject(gl); readyToPickPixel = false; // first time in life i would like to have a goto in Java;) if (!FLASH_SCREEN) { repeat = true; } // switch lights back on if lightning is used: if (useLightning) { gl.glEnable(GL10.GL_LIGHTING); } } } while (repeat); final float delta = (currentTime - lastTimeInMs); lastTimeInMs = currentTime; if (delta > 0 && 1000 / delta > MAX_FPS) { // System.out.println("delta=" + delta); // System.out.println("FPS=" + 1000 / delta); // System.out.println("1000/MAX_FPS-delta=" + (long) (1000 / MAX_FPS // - delta)); try { Thread.sleep((long) (1000 / MAX_FPS - delta)); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * do not kill the rendering thread, instead pause it this way because * otherwise the opengl resources would be released and the thread cant be * resatarted! */ private void startPauseLoop() { Log.d("OpenGL", "Renderer paused"); while (pauseRenderer) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } Log.d("OpenGL", "Renderer woken up"); } /** * This method will switch on all the defined light sources * * @param gl */ public void enableLights(GL10 gl) { if (myLights.size() > 0) { gl.glEnable(GL10.GL_LIGHTING); for (int i = 0; i < myLights.size(); i++) { myLights.get(i).switchOn(gl); } } } public void disableLights(GL10 gl) { gl.glDisable(GL10.GL_LIGHTING); for (int i = 0; i < myLights.size(); i++) { myLights.get(i).switchOff(gl); } } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { Log.d("Activity", "GLSurfaceView.onSurfaceChanged"); // Sets the current view port to the new size. gl.glViewport(0, 0, width, height); /* * Select the projection matrix which transforms the point from view * space to homogeneous clipping space. Clip space is a right-handed * coordinate system (+Z into the screen) contained within a canonical * clipping volume extending from (-1,-1,-1) to (+1,+1,+1): */ gl.glMatrixMode(GL10.GL_PROJECTION); // Reset the projection matrix gl.glLoadIdentity(); /* * GLU.gluPerspective parameters (see * http://www.zeuscmd.com/tutorials/opengles/12-Perspective.php): * * fovy - This specifies the field of view. A 90 degree angle means that * you can see everything directly to the left right around to the right * of you. This is not how humans see things. 45 degrees is a good value * to start. * * aspect - This specifies that aspect ratio that you desire. This is * usually specified as the width divided by the height of the window. * * zNear and zFar - This specifies the near and far clipping planes as * normal. */ GL1Renderer.halfWidth = width / 2; GL1Renderer.halfHeight = height / 2; GL1Renderer.height = height; GL1Renderer.nearHeight = minViewDistance * (float) Math.tan((GL1Renderer.LENSE_ANGLE * Vec.deg2rad) / 2); GL1Renderer.aspectRatio = (float) width / (float) height; GLU.gluPerspective(gl, LENSE_ANGLE, aspectRatio, minViewDistance, maxViewDistance); // TODO what is a good value?? /* * Select the modelview matrix which transforms a point from model space * to view space, using a right-handed coordinate system with +Y up, +X * to the right, and -Z into the screen: */ gl.glMatrixMode(GL10.GL_MODELVIEW); if (useLightning) { enableLights(gl); } if (USE_FOG) { addFog(gl); } /* * update this here to get a goot init value for lastTimeInMs */ lastTimeInMs = SystemClock.uptimeMillis(); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { Log.d("Activity", "GLSurfaceView.onSurfaceCreated"); // Set the background color to black (and alpha to 0) ( rgba ). gl.glClearColor(0, 0, 0, 0); /* * To enable flat shading use gl.glShadeModel(GL10.GL_FLAT); default is * GL_SMOOTH and GL_FLAT renders faces always with the same color, * shading... so its a little cheaper then GL_SMOOTH but the polygons * wont look realistic! */ // Depth buffer setup. gl.glClearDepthf(1.0f); // Enables depth testing. gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDisable(GL10.GL_DITHER); // The type of depth testing to do. gl.glDepthFunc(GL10.GL_LEQUAL); // Really nice perspective calculations. // gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); /* * Transparancy * * "The only sure way to achieve visually correct results is to sort and * render your primitives from back to front." * * http://www.opengl.org/sdk/docs/man/xhtml/glBlendFunc.xml */ gl.glEnable(GL10.GL_BLEND); // gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_DST_ALPHA); gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); // Enable smooth shading for nice light effects gl.glShadeModel(GL10.GL_SMOOTH); } private void addFog(GL10 gl) { // TODO extract constants gl.glFogf(GL10.GL_FOG_MODE, GL10.GL_LINEAR); gl.glFogf(GL10.GL_FOG_START, FOG_START_DISTANCE); gl.glFogf(GL10.GL_FOG_END, FOG_END_DISTANCE); gl.glHint(GL10.GL_FOG_HINT, GL10.GL_NICEST); gl.glFogfv(GL10.GL_FOG_COLOR, FOG_COLOR); gl.glEnable(GL10.GL_FOG); } public void addRenderElement(Renderable elementToRender) { if (elementToRender == null) { Log.e(LOG_TAG, "Added element was NULL, cant be added!"); } elementsToRender.add(elementToRender); } public boolean removeRenderElement(Renderable elementToRemove) { return elementsToRender.remove(elementToRemove); } }