// Life Tower // Adam Parrish // NYU ITP Spring 2007 // import java.nio.*; import org.lwjgl.opengl.*; import org.lwjgl.opengl.glu.GLU; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; public class LifeTower { private final String windowTitle = "Life Tower"; private final int gridSize = 48; private final int layers = 100; private int layerListBase = -1; private int atomList = -1; private int curLayer = 0; private DisplayMode displayMode; private float rotationY = 0; private boolean done; private boolean runLife = false; private boolean life[][][] = new boolean[gridSize][gridSize][2]; float sceneAmbientLight[] = { 0.3f, 0.3f, 0.3f, 1f }; float lightDiffuse[] = { 0.9f, 0.9f, 0.9f, 1f }; float lightSpecular[] = { 0.9f, 0.9f, 0.9f, 1f }; float lightAmbient[] = { 0.1f, 0.1f, 0.1f, 1f }; float lightPosition[] = { 60f, -50f, -100f, 1f }; GLMaterial mat = new GLMaterial(); private float ypos = 75f; private float yLookAt = -5f; public static float[][] modelViewMatrix = new float[4][4]; public static float[][] projectionMatrix = new float[4][4]; public static int[] viewport = new int[4]; public static void main(String args[]) { LifeTower app = new LifeTower(); app.run(); } public void run() { try { init(); while (!done) { mainloop(); render(); Display.update(); } cleanup(); } catch (Exception e) { e.printStackTrace(); System.exit(0); } } private void init() throws Exception { initDisplay(); initGL(); atomList = GL11.glGenLists(1); GL11.glNewList(atomList, GL11.GL_COMPILE); drawAtom(); GL11.glEndList(); initLife(); } private void initDisplay() throws Exception { Display.setFullscreen(false); DisplayMode d[] = Display.getAvailableDisplayModes(); // find a resolution we like for (int i = 0; i < d.length; i++) { if (d[i].getWidth() == 800 && d[i].getHeight() == 600 && d[i].getBitsPerPixel() == 32) { displayMode = d[i]; break; } } Display.setDisplayMode(displayMode); Display.setTitle(windowTitle); Display.create(); } private void initGL() { GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glLoadIdentity(); GLU.gluPerspective(30.0f, (float)displayMode.getWidth() / (float)displayMode.getHeight(), 0.9f, 250f); GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glEnable(GL11.GL_CULL_FACE); GL11.glEnable(GL11.GL_LIGHTING); GL11.glShadeModel(GL11.GL_SMOOTH); // blending GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); // Force normals to length 1 GL11.glEnable(GL11.GL_NORMALIZE); // separate specular color GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR ); mat = new GLMaterial(); mat.setSurfaceColorLit(new float[] { 0.8f, 0.8f, 0.8f, 0.9f }); mat.setSurfaceColorShadow(new float[] { 0.55f, 0.55f, 0.55f, 0.8f }); mat.setReflectionColor(new float[] { 0.85f, 0.85f, 0.85f, 1f }); mat.setShininess(75f); } private void mainloop() { if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) { done = true; } if (Display.isCloseRequested()) { done = true; } while (Keyboard.next()) { if (Keyboard.getEventKeyState()) { keyDown(Keyboard.getEventKey()); } else { keyUp(Keyboard.getEventKey()); } } if (Mouse.isButtonDown(0)) { int mouseDX = Mouse.getDX(); int mouseDY = Mouse.getDY(); rotationY -= mouseDX; ypos = (ypos * 0.25f) + ((ypos + mouseDY) * 0.75f); yLookAt = (yLookAt * 0.25f) + ((yLookAt + mouseDY * 0.2f) * 0.75f); } } private void keyDown(int keycode) { if (keycode == Keyboard.KEY_SPACE) { runLife = !runLife; } else if (keycode == Keyboard.KEY_R) { initLife(); runLife = false; } else if (keycode == Keyboard.KEY_S) { runLife = false; updateLife(); } else if (keycode == Keyboard.KEY_P) { atomList = GL11.glGenLists(1); GL11.glNewList(atomList, GL11.GL_COMPILE); drawPoint(); GL11.glEndList(); } else if (keycode == Keyboard.KEY_C) { atomList = GL11.glGenLists(1); GL11.glNewList(atomList, GL11.GL_COMPILE); drawAtom(); GL11.glEndList(); } } private void keyUp(int keycode) { } private void render() { if (runLife) { updateLife(); } GL11.glClearColor(0.25f, 0.15f, 0.55f, 1f); GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); // clear to black, draw texture-sized scene GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glLoadIdentity(); setLight(GL11.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition); setAmbientLight(sceneAmbientLight); GLU.gluLookAt( 55f, ypos, 55f, // position 0f, yLookAt , 0f, // target 0f, 1f, 0f); // orientation // draw all previous states GL11.glPushMatrix(); GL11.glRotatef(rotationY, 0, 1f, 0); for (int i = 0; i < layers; i++) { int offset = (curLayer - 1) - i; if (offset < 0) { offset = layers + offset; } GL11.glPushMatrix(); GL11.glTranslatef(0, (i * -1.2f), 0); GL11.glCallList(layerListBase + offset); GL11.glPopMatrix(); } GL11.glPopMatrix(); } private void updateLife() { for (int i = 0; i < life.length; i++) { for (int j = 0; j < life[i].length; j++) { int s = sumNeighbors(life, i, j); if ( life[i][j][0] && (s < 2)) { life[i][j][1] = false; } else if ( life[i][j][0] && (s > 3)) { life[i][j][1] = false; } else if (!life[i][j][0] && (s == 3)) { life[i][j][1] = true; } else { life[i][j][1] = life[i][j][0]; } } } for (int i = 0; i < life.length; i++) { for (int j = 0; j < life.length; j++) { life[i][j][0] = life[i][j][1]; } } // draw current state into new list GL11.glNewList(layerListBase + curLayer, GL11.GL_COMPILE); GL11.glEnable(GL11.GL_DEPTH_TEST); for (int i = 0; i < life.length; i++) { for (int j = 0; j < life[i].length; j++) { if (life[i][j][0]) { GL11.glPushMatrix(); GL11.glTranslatef(gridSize * -0.5f, 0, gridSize * -0.5f); GL11.glRotatef(Math.abs(life.length/2 - i) * 0.15f, 0, 0, 1f); GL11.glTranslatef(i, 0, j); GL11.glCallList(atomList); GL11.glPopMatrix(); } } } GL11.glEndList(); // increment list to draw to curLayer++; if (curLayer >= layers) { //layerListBase = GL11.glGenLists(layers); curLayer = 0; } } private void drawPoint() { mat.apply(); GL11.glBegin(GL11.GL_QUADS); // top face GL11.glNormal3f(0f, -1f, 0f); GL11.glVertex3f(0f, 0, 0.5f); // H GL11.glVertex3f(0.5f, 0, 0.5f); // G GL11.glVertex3f(0.5f, 0, 0f); // C GL11.glVertex3f(0f, 0, 0f); // D // bottom face GL11.glNormal3f(0f, 1f, 0f); GL11.glVertex3f(0.5f, 0f, 0f); // B GL11.glVertex3f(0.5f, 0f, 0.5f); // F GL11.glVertex3f(0f, 0f, 0.5f); // E GL11.glVertex3f(0f, 0f, 0f); // A GL11.glEnd(); } private void drawAtom() { GL11.glPushMatrix(); GL11.glScalef(1f, 1f, 1f); mat.apply(); GL11.glBegin(GL11.GL_QUADS); // front face GL11.glNormal3f(0f, 0f, 1f); GL11.glVertex3f(0f, 1f, 0f); // D GL11.glVertex3f(1f, 1f, 0f); // C GL11.glVertex3f(1f, 0f, 0f); // B GL11.glVertex3f(0f, 0f, 0f); // A // back face GL11.glNormal3f(0f, 0f, -1f); GL11.glVertex3f(1f, 0f, 1f); // F GL11.glVertex3f(1f, 1f, 1f); // G GL11.glVertex3f(0f, 1f, 1f); // H GL11.glVertex3f(0f, 0f, 1f); // E // top face GL11.glNormal3f(0f, -1f, 0f); GL11.glVertex3f(0f, 1f, 1f); // H GL11.glVertex3f(1f, 1f, 1f); // G GL11.glVertex3f(1f, 1f, 0f); // C GL11.glVertex3f(0f, 1f, 0f); // D // bottom face GL11.glNormal3f(0f, 1f, 0f); GL11.glVertex3f(1f, 0f, 0f); // B GL11.glVertex3f(1f, 0f, 1f); // F GL11.glVertex3f(0f, 0f, 1f); // E GL11.glVertex3f(0f, 0f, 0f); // A // left face GL11.glNormal3f(1f, 0f, 0f); GL11.glVertex3f(0f, 0f, 1f); // E GL11.glVertex3f(0f, 1f, 1f); // H GL11.glVertex3f(0f, 1f, 0f); // D GL11.glVertex3f(0f, 0f, 0f); // A // right face GL11.glNormal3f(-1f, 0f, 0f); GL11.glVertex3f(1f, 1f, 0f); // C GL11.glVertex3f(1f, 1f, 1f); // G GL11.glVertex3f(1f, 0f, 1f); // F GL11.glVertex3f(1f, 0f, 0f); // B GL11.glEnd(); GL11.glPopMatrix(); } private void initLife() { for (int i = 0; i < life.length; i++) { for (int j = 0; j < life[i].length; j++) { if (Math.random() > 0.85) { life[i][j][0] = true; } else { life[i][j][0] = false; } } } layerListBase = GL11.glGenLists(layers); curLayer = 0; updateLife(); } int sumNeighbors(boolean[][][] l, int s, int t) { int total = 0; for (int i = s - 1; i <= s + 1; i++) { for (int j = t - 1; j <= t + 1; j++) { total += (l[wrap(i, l.length - 1)][wrap(j, l.length - 1)][0] ? 1 : 0); } } if (l[s][t][0]) { total--; } return total; } int wrap(int index, int maxIndex) { if (index < 0) { return maxIndex + index; } else if (index > maxIndex) { return index - maxIndex; } else { return index; } } private void cleanup() { Display.destroy(); } public static void setLight( int GLLightHandle, float[] diffuseLightColor, float[] ambientLightColor, float[] specularLightColor, float[] position ) { FloatBuffer ltDiffuse = allocFloats(diffuseLightColor); FloatBuffer ltAmbient = allocFloats(ambientLightColor); FloatBuffer ltSpecular = allocFloats(specularLightColor); FloatBuffer ltPosition = allocFloats(position); GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, ltDiffuse); // color of the direct illumination GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ltAmbient); // color of the reflected light GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, ltSpecular); // color of the highlight (same as direct light) GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition); GL11.glEnable(GLLightHandle); // Enable the light (GL_LIGHT1 - 7) //GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .005F); // how light beam drops off } public static void setAmbientLight(float[] ambientLightColor) { FloatBuffer ltAmbient = allocFloats(ambientLightColor); GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, ltAmbient); } public static final int SIZE_FLOAT = 4; public static FloatBuffer allocFloats(int howmany) { return ByteBuffer.allocateDirect(howmany * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer(); } public static FloatBuffer allocFloats(float[] floatarray) { FloatBuffer fb = ByteBuffer.allocateDirect(floatarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer(); fb.put(floatarray).flip(); return fb; } public static final int SIZE_INT = 4; public static IntBuffer allocInts(int howmany) { return ByteBuffer.allocateDirect(howmany * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer(); } }