package uk.org.floop.epq3d; import java.awt.event.KeyEvent; public class Player { // working variables private final Matrix rotMatrix = new Matrix(3, 3); public final Matrix camMatrix = new Matrix(4, 3); private final Matrix invRotMatrix = new Matrix(3, 3); private final Matrix invCamMatrix = new Matrix(4, 3); protected Point3D FPWorldPos; protected PointComp[] ScreenCornerPosS = new PointComp[4]; public Vector3D viewVector = new Vector3D(0,0,0); public boolean isSprinting = false; // movement variables private final Point3D position = new Point3D(0,0,1); private final Point3D rotation = new Point3D(0,Math.PI / 2, 4); private final Vector3D direction = new Vector3D(0,0,0); // button variables public int[] keysPressed = new int[6]; // order: fwd, bkwd, left, right, up, down // constants public double mouseSensitivity = 0.005; // radians per pixel (yeah, i know) public Player(drawData drawData) { drawData.FPDis = 1/Math.tan(Math.toRadians(drawData.FOV/2d)); for(int i = 0; i < 6; i+=1){ drawData.frustumPlanes[i] = new Plane(); } drawData.playerPos = position; drawData.camMatrix = camMatrix; double yVal = (double)drawData.scrY / (double)drawData.scrX; // flip x and y because reasons // thank you past me for this incredible description ScreenCornerPosS[0] = new PointComp(-yVal,-1, drawData.FPDis); ScreenCornerPosS[1] = new PointComp(yVal,-1, drawData.FPDis); ScreenCornerPosS[2] = new PointComp(-yVal,1, drawData.FPDis); ScreenCornerPosS[3] = new PointComp(yVal,1, drawData.FPDis); } public void keyPressed(int key, drawData drawData) { // handling direction switch(key){ case KeyEvent.VK_W: keysPressed[0] = 1; break; case KeyEvent.VK_S: keysPressed[1] = 1; break; case KeyEvent.VK_A: keysPressed[2] = 1; break; case KeyEvent.VK_D: keysPressed[3] = 1; break; case KeyEvent.VK_SPACE: keysPressed[4] = 1; break; case KeyEvent.VK_SHIFT: keysPressed[5] = 1; break; case KeyEvent.VK_CONTROL: isSprinting = true; break; case KeyEvent.VK_1: drawData.drawZBuffer = !drawData.drawZBuffer; break; case KeyEvent.VK_2: drawData.drawLines = !drawData.drawLines; break; case KeyEvent.VK_3: drawData.backfaceCullingDisabled = !drawData.backfaceCullingDisabled; break; case KeyEvent.VK_4: drawData.zBufferDisabled = !drawData.zBufferDisabled; break; case KeyEvent.VK_5: drawData.texturesDisabled = !drawData.texturesDisabled; break; case KeyEvent.VK_9: drawData.frustumCullingOverridePercent += 2; break; case KeyEvent.VK_0: drawData.frustumCullingOverridePercent = Math.max(0, drawData.frustumCullingOverridePercent - 2); break; } } public void keyReleased(int key){ switch(key){ case KeyEvent.VK_W: keysPressed[0] = 0; break; case KeyEvent.VK_S: keysPressed[1] = 0; break; case KeyEvent.VK_A: keysPressed[2] = 0; break; case KeyEvent.VK_D: keysPressed[3] = 0; break; case KeyEvent.VK_SPACE: keysPressed[4] = 0; break; case KeyEvent.VK_SHIFT: keysPressed[5] = 0; break; case KeyEvent.VK_CONTROL: isSprinting = false; break; } } public void tick(drawData drawData) { // gets called once every tick, before the repainting process happens. // change rotation depending on mouse movement rotation.z -= drawData.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 += drawData.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; } // calculate the speed multiplier based on playerSpeed and the last frame time. double speedMultiplier = drawData.playerSpeed * drawData.lastFrameTime / 1000; if(isSprinting) speedMultiplier *= drawData.playerSprintModifier; // calculate the direction based on the current keys pressed direction.x = keysPressed[0] - keysPressed[1]; direction.y = keysPressed[2] - keysPressed[3]; direction.z = keysPressed[4] - keysPressed[5]; // 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 * speedMultiplier / (dirLen); dirVec.y = dirVec.y * speedMultiplier / (dirLen); dirVec.z = dirVec.z * speedMultiplier / (dirLen); } 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 xMat.multiplyGetResult(yMat).multiplyTo(zMat, rotMatrix); rotMatrix.multiplyTo(traMat, camMatrix); izMat.multiplyGetResult(iyMat).multiplyTo(ixMat, invRotMatrix); // 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 :( 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, position.y, position.z); for (PointComp point: ScreenCornerPosS) { point.invalidate(); point.setRotatedPoint(invCamMatrix); } // find frustum planes // near plane drawData.frustumPlanes[0].initFromPointAndVector(position, viewVector); // far plane int farPlaneDis = 1000; drawData.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 drawData.frustumPlanes[2].initFrom3Points(FPWorldPos, ScreenCornerPosS[0].getRotatedPoint(), ScreenCornerPosS[1].getRotatedPoint()); // right plane drawData.frustumPlanes[3].initFrom3Points(FPWorldPos, ScreenCornerPosS[3].getRotatedPoint(), ScreenCornerPosS[2].getRotatedPoint()); // top plane drawData.frustumPlanes[4].initFrom3Points(FPWorldPos, ScreenCornerPosS[1].getRotatedPoint(), ScreenCornerPosS[3].getRotatedPoint()); // bottom plane drawData.frustumPlanes[5].initFrom3Points(FPWorldPos, ScreenCornerPosS[2].getRotatedPoint(), ScreenCornerPosS[0].getRotatedPoint()); } public Point3D getPos() { return position; } public Point3D getRot() { return rotation; } }