package uk.org.floop.epq3d; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; public class Player { public Plane[] frustumPlanes = new Plane[6]; // image that represents the player's position on the board // current position of the player on the board grid private Matrix rotMatrix; public Matrix camMatrix; private Matrix invRotMatrix; private Matrix invCamMatrix; public double FOV = 100; protected double Fpdis; protected Point3D FPWorldPos; protected PointComp[] ScreenCornerPosS = new PointComp[4]; private final Point3D position = new Point3D(0,0,0); private final Point3D rotation = new Point3D(0,Math.PI / 2, 0); private final Vector3D direction = new Vector3D(0,0,0); public Vector3D viewVector = new Vector3D(0,0,0); public double mouseSensitivity = 0.005; // radians per pixel // called when the player is initialised public Player(int scrX, int scrY) { Fpdis = 1/Math.tan(Math.toRadians(FOV)/2d); for(int i = 0; i < 6; i+=1){ frustumPlanes[i] = new Plane(); } double yVal = (double)scrY / (double)scrX; // flip x and y because reasons ScreenCornerPosS[0] = new PointComp(-yVal,-1, 0); ScreenCornerPosS[1] = new PointComp(yVal,-1, 0); ScreenCornerPosS[2] = new PointComp(-yVal,1, 0); ScreenCornerPosS[3] = new PointComp(yVal,1, 0); } // unused because the player should not be drawn // I'm leaving it in as a hint to buffered Images public void draw(Graphics g, ImageObserver observer) { int width = 100; int height = 100; BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB ); for ( int py = 0; py < height; py++ ) { for ( int px = 0; px < width; px++ ) { // Set the pixel colour of the image n.b. x = cc, y = rc img.setRGB(px, py, Color.BLACK.getRGB() ); } } g.drawImage(img, 0, 0, observer); } public void keyPressed(int key) { if (key == KeyEvent.VK_W && direction.x < 1) { direction.x += 1; } else if (key == KeyEvent.VK_S && -direction.x < 1) { direction.x += -1; } else if (key == KeyEvent.VK_A && direction.y < 1) { direction.y += 1; } else if (key == KeyEvent.VK_D && -direction.y < 1) { direction.y += -1; } else if (key == KeyEvent.VK_SPACE && direction.z < 1) { direction.z += 1; } else if (key == KeyEvent.VK_SHIFT && -direction.z < 1) { direction.z += -1; } } public void keyReleased(int key){ if (key == KeyEvent.VK_W && direction.x > -1) { direction.x -= 1; } else if (key == KeyEvent.VK_S && -direction.x > -1) { direction.x -= -1; } else if (key == KeyEvent.VK_A && direction.y > -1) { direction.y -= 1; } else if (key == KeyEvent.VK_D && -direction.y > -1) { direction.y -= -1; } else if (key == KeyEvent.VK_SPACE && direction.z > -1) { direction.z -= 1; } else if (key == KeyEvent.VK_SHIFT && -direction.z > -1) { direction.z -= -1; } } public void tick(Point2D mouseRel) { // gets called once every tick, before the repainting process happens. // change rotation depending on mouse movement rotation.z -= mouseRel.x * mouseSensitivity; // force z rotation to wrap around if(rotation.z<-Math.PI){ rotation.z = Math.PI; } else if(rotation.z>Math.PI){ rotation.z = -Math.PI; } rotation.y += mouseRel.y * mouseSensitivity; // max out y rotation at 0 and -90 degrees if(rotation.y < 0){ rotation.y = 0; } else if(rotation.y > Math.PI){ rotation.y = Math.PI; } // define rotation and translation matrices Matrix zMat = new Matrix(3, 3); zMat.setItems(new double[][]{ {Math.cos(rotation.z), Math.sin(rotation.z), 0}, {-Math.sin(rotation.z), Math.cos(rotation.z), 0}, {0, 0, 1}} ); Matrix yMat = new Matrix(3, 3); yMat.setItems(new double[][]{ {Math.cos(rotation.y), 0, -Math.sin(rotation.y)}, {0, 1, 0 }, {Math.sin(rotation.y), 0, Math.cos(rotation.y) }} ); Matrix xMat = new Matrix(3, 3); xMat.setItems(new double[][]{ {1, 0, 0 }, {0, Math.cos(rotation.x), Math.sin(rotation.x)}, {0, -Math.sin(rotation.x), Math.cos(rotation.x)}} ); // calculate inverse matrices Matrix izMat = new Matrix(3, 3); izMat.setItems(new double[][]{ {Math.cos(-rotation.z), Math.sin(-rotation.z), 0}, {-Math.sin(-rotation.z), Math.cos(-rotation.z), 0}, {0, 0, 1}} ); Matrix iyMat = new Matrix(3, 3); iyMat.setItems(new double[][]{ {Math.cos(-rotation.y), 0, -Math.sin(-rotation.y)}, {0, 1, 0 }, {Math.sin(-rotation.y), 0, Math.cos(-rotation.y) }} ); Matrix ixMat = new Matrix(3, 3); ixMat.setItems(new double[][]{ {1, 0, 0 }, {0, Math.cos(-rotation.x), Math.sin(-rotation.x)}, {0, -Math.sin(-rotation.x), Math.cos(-rotation.x)}} ); // apply izMat to direction vector try { Vector3D dirvec = new Vector3D(); izMat.multiplyVec3to(direction, dirvec); double dirLen = dirvec.getLength(); if (dirLen != 0) { dirvec.x = dirvec.x / (dirLen * 40); dirvec.y = dirvec.y / (dirLen * 40); dirvec.z = dirvec.z / (dirLen * 40); } position.translate(dirvec); } catch (Exception e) { throw new RuntimeException(e); } Matrix traMat = new Matrix(4, 3); traMat.setItems(new double[][] {{1, 0, 0, -position.x}, {0, 1, 0, -position.y}, {0, 0, 1, -position.z}} ); // multiply out matrices rotMatrix = xMat.multiplyGetResult(yMat).multiplyGetResult(zMat); camMatrix = rotMatrix.multiplyGetResult(traMat); invRotMatrix = izMat.multiplyGetResult(iyMat).multiplyGetResult(ixMat); // I really don't want to do it like this, but I don't think there's any other way without messing up // all my other matrices :( invCamMatrix = new Matrix(4, 3); for(int x = 0; x<3; x+=1){ for(int y = 0; y<3; y+=1){ invCamMatrix.setItem(x, y, invRotMatrix.getItem(x, y)); }} for(int y = 0; y<3; y+=1){ invCamMatrix.setItem(3, y, -traMat.getItem(3, y)); } // calculate view vector Point3D viewPoint = new Point3D(0,0,0); invRotMatrix.multiplyPoint3to(new Point3D(0,0,1), viewPoint); viewVector.x = viewPoint.x;viewVector.y=viewPoint.y;viewVector.z=viewPoint.z; // calculate camera focal point and edges in world coordinates FPWorldPos = new Point3D( position.x - viewVector.x*Fpdis, position.y - viewVector.y*Fpdis, position.z - viewVector.z*Fpdis); for (PointComp point: ScreenCornerPosS) { point.invalidate(); point.setRotatedPoint(invCamMatrix); } // find frustum planes // near plane frustumPlanes[0].initFrom3Points(ScreenCornerPosS[0].getRotatedPoint(), ScreenCornerPosS[1].getRotatedPoint(), ScreenCornerPosS[2].getRotatedPoint()); // far plane int farPlaneDis = 1000; frustumPlanes[1].initFromPointAndVector( new Point3D( position.x + viewVector.x*farPlaneDis, position.y + viewVector.y*farPlaneDis, position.z + viewVector.z*farPlaneDis), // invert the view vector because the normal needs to point the other way new Vector3D(-viewVector.x, -viewVector.y, -viewVector.z)); // mid planes // left plane frustumPlanes[2].initFrom3Points(FPWorldPos, ScreenCornerPosS[0].getRotatedPoint(), ScreenCornerPosS[1].getRotatedPoint()); // right plane frustumPlanes[3].initFrom3Points(FPWorldPos, ScreenCornerPosS[3].getRotatedPoint(), ScreenCornerPosS[2].getRotatedPoint()); // top plane frustumPlanes[4].initFrom3Points(FPWorldPos, ScreenCornerPosS[1].getRotatedPoint(), ScreenCornerPosS[3].getRotatedPoint()); // bottom plane frustumPlanes[5].initFrom3Points(FPWorldPos, ScreenCornerPosS[2].getRotatedPoint(), ScreenCornerPosS[0].getRotatedPoint()); } // returnValue Functions public Point3D getPos() { return position; } public Point3D getRot() { return rotation; } }