import java.awt.Font; import java.util.TimerTask; import java.util.Timer; import keel.runtime.Application; import keel.runtime.KeelRuntime; import keel.runtime.event.KeyEvent; import keel.runtime.event.KeyAdapter; import keel.gfx.GLGraphics; import keel.gfx.GLConstants; import keelx.gfx.font.SystemFont; public class LightGoAround implements Application { private int width = 640; private int height = 480; private Cube cube1 = new Cube(); private Cube cube2 = new Cube(); private Material[] materials; private Camera camera; private Light light; private SystemFont font; private int fontSize = Math.min(width, height) / 16; private Timer timer; private Object monitor = new Object(); public LightGoAround() { font = new SystemFont(new Font("System", Font.PLAIN, fontSize)); camera = new Camera(width / (double) height, 10, cube1); light = new Light(); timer = new Timer(true); timer.schedule(new TimerTask() { public void run() { synchronized (monitor) { light.rotate(); } } }, 0, 10); materials = new Material[] { cube1, cube2, }; } public void keelMain(KeelRuntime runtime,String argv[]) { runtime.createWindow("", width, height); runtime.createGraphicsWithDefaultPixelFormat(); runtime.addKeyListener(new Controller()); runtime.run(); } public void initGraphics(GLGraphics g) { g.glViewport(0, 0, width, height); g.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); g.glEnable(GLConstants.GL_BLEND); g.glBlendFunc(GLConstants.GL_SRC_ALPHA, GLConstants.GL_ONE_MINUS_SRC_ALPHA); } public void update(GLGraphics g) { synchronized (monitor) { g.glClear(GLConstants.GL_COLOR_BUFFER_BIT | GLConstants.GL_DEPTH_BUFFER_BIT); g.glPushMatrix(); g.glMatrixMode(GLConstants.GL_PROJECTION); g.glLoadIdentity(); camera.update(g); g.glMatrixMode(GLConstants.GL_MODELVIEW); g.glLoadIdentity(); int[] enables = { GLConstants.GL_DEPTH_TEST, GLConstants.GL_LIGHTING, GLConstants.GL_CULL_FACE, }; for (int i = 0; i < enables.length; i++) { g.glEnable(enables[i]); } g.glPushMatrix(); light.update(g); g.glPopMatrix(); for (int i = 0; i < materials.length; i++) { g.glPushMatrix(); materials[i].update(g); g.glPopMatrix(); } for (int i = enables.length - 1; i >= 0; i--) { g.glDisable(enables[i]); } g.glPopMatrix(); printMessage(g, new String[] { camera.toString(), "Cube1: " + cube1.toString(), "Cube2: " + cube2.toString(), }); } } public void quitRequested() { KeelRuntime.getRuntime().disposeDisplay(); System.exit(0); } private void printMessage(GLGraphics g, String[] messages) { g.glPushMatrix(); g.glMatrixMode(GLConstants.GL_PROJECTION); g.glLoadIdentity(); g.gluOrtho2D(0, width, 0, height); g.glScaled(1, -1, 1); g.glTranslated(0, -height, 0); g.glColor3d(0.0, 0.0, 1.0); int margin = fontSize / 2; int x = margin; int y = height - (margin + fontSize) * (messages.length - 1) - margin; g.glEnable(GLConstants.GL_TEXTURE_2D); for (int i = 0; i < messages.length; i++) { font.draw(messages[i], x, y, g); y += fontSize + margin; } g.glDisable(GLConstants.GL_TEXTURE_2D); g.glPopMatrix(); } class Controller extends KeyAdapter { Material material = cube1; public void keyPressed(KeyEvent ev) { switch (ev.getKeyCode()) { case KeyEvent.VK_LEFT: material.left(); break; case KeyEvent.VK_RIGHT: material.right(); break; case KeyEvent.VK_UP: material.forward(); break; case KeyEvent.VK_DOWN: material.backward(); break; case KeyEvent.VK_PAGE_UP: material.up(); break; case KeyEvent.VK_PAGE_DOWN: material.down(); break; case KeyEvent.VK_SPACE: if (material == camera) { material = cube1; } else { material = camera; } break; case KeyEvent.VK_ADD: camera.narrower(); break; case KeyEvent.VK_SUBTRACT: camera.wider(); break; case KeyEvent.VK_Q: System.exit(0); break; default: break; } } } } class Light extends Material { public Light() { super(10, 10, 0); } private int index; private double radius = 10; private double angle = 0; private float[] ambient = {0.0f, 0.0f, 0.0f, 1.0f}; private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f}; private float[] specular = {1.0f, 1.0f, 1.0f, 1.0f}; public void update(GLGraphics g) { g.glLightModeli(GLConstants.GL_LIGHT_MODEL_TWO_SIDE, 1); int light = GLConstants.GL_LIGHT0; g.glLightfv(light, GLConstants.GL_AMBIENT, ambient); g.glLightfv(light, GLConstants.GL_DIFFUSE, diffuse); g.glLightfv(light, GLConstants.GL_SPECULAR, specular); g.glLightfv(light, GLConstants.GL_POSITION, new float[]{getX(), getY(), getZ(), 1f}); g.glEnable(light); } public void rotate() { angle += getSpeed(); setX((int) (radius * Math.cos(Math.toRadians(angle)))); setZ((int) (radius * Math.sin(Math.toRadians(angle)))); } } class Camera extends Material { public Camera(double anAspectRatio, int aViewAngle, Material aTarget) { super(-20, 100, 100); target = aTarget; aspectRatio = anAspectRatio; viewAngle = aViewAngle; } private Material target; private double aspectRatio; private int viewAngle; public void setTarget(Material aTarget) { target = aTarget; } public void update(GLGraphics g) { g.gluPerspective(viewAngle, aspectRatio, 1.0d, 1600.0d); g.gluLookAt( getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), 0, 0, 1); } public void narrower() { viewAngle--; } public void wider() { viewAngle++; } public String toString() { return "View angle:" + viewAngle + " at: (" + getX() + "," + getY() + "," + getZ() + ") target: (" + target.getX() + "," + target.getY() + "," + target.getZ() + ")"; } } class Cube extends Material { public Cube() { super(0, 0, 0); } private double width = 2d; private double[][] vertexList = { { -1, -1, -1}, { +1, -1, -1}, { +1, +1, -1}, { -1, +1, -1}, { -1, -1, +1}, { +1, -1, +1}, { +1, +1, +1}, { -1, +1, +1}, }; private int[][] faceList = { {0, 3, 2, 1}, {1, 2, 6, 5}, {5, 6, 7, 4}, {4, 7, 3, 0}, {4, 0, 1, 5}, {3, 7, 6, 2}, }; private double[][] normalList = { {0.0, 0.0, -1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 1.0}, {-1.0, 0.0, 0.0}, {0.0, -1.0, 0.0}, {0.0, 1.0, 0.0}, }; float[] color = { 1.0f, 0.1f, 0.1f, 1.0f }; float[] diffuse = { 0.8f, 0.8f, 0.8f, 1.0f }; float[] specular = { 0.0f, 0.0f, 0.0f, 1.0f }; float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; float[] emission = { 0.0f, 0.0f, 0.0f, 1.0f }; float shininess = 0.0f; public void update(GLGraphics g) { g.glTranslated(getX(), getY(), getZ()); g.glScaled(width, width, width); g.glMaterialfv(GLConstants.GL_FRONT, GLConstants.GL_DIFFUSE, color); g.glMaterialfv(GLConstants.GL_FRONT, GLConstants.GL_AMBIENT, color); g.glMaterialfv(GLConstants.GL_FRONT, GLConstants.GL_SPECULAR, specular); g.glMaterialfv(GLConstants.GL_FRONT, GLConstants.GL_EMISSION, emission); g.glMaterialf(GLConstants.GL_FRONT, GLConstants.GL_SHININESS, shininess); g.glBegin(GLConstants.GL_QUADS); for (int i = 0; i < faceList.length; i++) { int[] face = faceList[i]; g.glNormal3dv(normalList[i]); for (int j = 0; j < face.length; j++) { int vertexIndex = face[j]; g.glVertex3dv(vertexList[vertexIndex]); } } g.glEnd(); } public String toString() { return "Cube at: (" + getX() + "," + getY() + "," + getZ() + ") width: " + width; } } class Material { public Material(int aX, int aY, int aZ) { this(aX, aY, aZ, 2); } public Material(int aX, int aY, int aZ, int aSpeed) { x = aX; y = aY; z = aZ; speed = aSpeed; } private int x = 0; private int y = 0; private int z = 0; private int speed = 2; public void setX(int newX) { x = newX; } public void setY(int newY) { y = newY; } public void setZ(int newZ) { z = newZ; } public int getX() { return x; } public int getY() { return y; } public int getZ() { return z; } public int getSpeed() { return speed; } public void right() { x += speed; } public void left() { x -= speed; } public void forward() { y += speed; } public void backward() { y -= speed; } public void up() { z += speed; } public void down() { z -= speed; } public void update(GLGraphics g) { } }