diff --git a/config.json b/config.json new file mode 100644 index 0000000..ca5dcdd --- /dev/null +++ b/config.json @@ -0,0 +1,14 @@ +{ +"__comment":"Values in pixels", +"screenWidth":900, +"screenHeight":600, +"maxFPS":60, +"FOV":100, +"debug":{ + "__comment":"values are: enabled/disabled", + "masterDebugToggle":"enabled", + "drawDebugHud":"disabled", + "drawLines":"enabled", + "overrideBackFaceCulling":"disabled" + } +} diff --git a/src/main/java/uk/org/floop/epq3d/Face.java b/src/main/java/uk/org/floop/epq3d/Face.java index b376bd3..3ee09fe 100644 --- a/src/main/java/uk/org/floop/epq3d/Face.java +++ b/src/main/java/uk/org/floop/epq3d/Face.java @@ -1,7 +1,5 @@ package uk.org.floop.epq3d; -import java.awt.image.BufferedImage; - public class Face { public PointComp[] points; public Point2D[] UVPoints; @@ -39,7 +37,7 @@ tri.invalidate(); } } - public int draw(int[][] img, int[][] zBuf, BufferedImage debugImg, Matrix camMatrix, double FPDis, int scrX, int scrY) { + public int draw(drawData drawData) { double ang = normal.angleTo(new Vector3D(0.2, 0.5, 1)); if (!isInitialised) { throw new RuntimeException("Face not initialised"); @@ -49,15 +47,15 @@ // initialise points int numberOfPixels = 0; // apply point transforms for all points within the face - boolean valid = applyPointTransforms(camMatrix, FPDis, scrX, scrY); + boolean valid = applyPointTransforms(drawData.camMatrix, drawData.FPDis, drawData.scrX, drawData.scrY); // this function is completed every frame, without checking whether pixels will be drawn first - bakePerspectiveMatrices(img, zBuf, debugImg, camMatrix, FPDis, scrX, scrY); + bakePerspectiveMatrices(drawData); // if all the points are valid (in front of the camera) draw all tris normally. if (valid) { for (Triangle tri : tris) { - tri.draw(img, zBuf, FPDis, scrX, scrY, ang); + tri.draw(drawData, ang); } } else { /*todo check wether there is any slowdown here @@ -68,18 +66,18 @@ // first, count up the number of invalid points in the triangle (points which are behind the camera) int numOfInvalidPoints = 0; for (int i = 0; i < 3; i += 1) { - if (points[trisFaceList[tri_i][i]].getRotatedPoint().z < 0.1) { + if (points[trisFaceList[tri_i][i]].getRotatedPoint().z < 0.001) { numOfInvalidPoints += 1; } } if (numOfInvalidPoints == 0) { // if there are no invalid points, treat the triangle normally // project all points in triangle - tris[tri_i].draw(img, zBuf, FPDis, scrX, scrY, ang); + tris[tri_i].draw(drawData, ang); } else if (numOfInvalidPoints == 1) {// if one point is invalid, interpolate 2 new points and draw 2 new triangles: // find which point is the invalid one TODO could optimise and find this beforehand int invalidIndex = -1; for (int i = 0; i < 3; i += 1) { - if (points[trisFaceList[tri_i][i]].getRotatedPoint().z < 0.1) { + if (points[trisFaceList[tri_i][i]].getRotatedPoint().z < 0.001) { invalidIndex = i; break; } @@ -95,7 +93,7 @@ // assign old points 1 and 2 based on the index of the invalid point { // interpolate between oldPoint1 and invalidPoint - // solving for z = 0.1 for the line between oldPoint1 and invalidPoint, + // solving for z = 0.001 for the line between oldPoint1 and invalidPoint, // separately in the xz and yz planes. double gradX = (oldPoint1.z - invalidPoint.z) / (oldPoint1.x - invalidPoint.x); @@ -103,9 +101,9 @@ (oldPoint1.y - invalidPoint.y); newPoint1 = new Point3D( - (0.1 + gradX * oldPoint1.x - oldPoint1.z) / gradX, - (0.1 + gradY * oldPoint1.y - oldPoint1.z) / gradY, - 0.1); + (0.001 + gradX * oldPoint1.x - oldPoint1.z) / gradX, + (0.001 + gradY * oldPoint1.y - oldPoint1.z) / gradY, + 0.001); if (!Double.isFinite(gradX)) { newPoint1.x = oldPoint1.x; } @@ -117,7 +115,7 @@ // interpolate between oldPoint2 and invalid point { // interpolate between oldPoint2 and invalidPoint - // solving for z = 0.1 for the line between oldPoint2 and invalidPoint, + // solving for z = 0.001 for the line between oldPoint2 and invalidPoint, // separately in the xz and yz planes. double gradX = (oldPoint2.z - invalidPoint.z) / (oldPoint2.x - invalidPoint.x); @@ -125,9 +123,9 @@ (oldPoint2.y - invalidPoint.y); newPoint2 = new Point3D( - (0.1 + gradX * oldPoint2.x - oldPoint2.z) / gradX, - (0.1 + gradY * oldPoint2.y - oldPoint2.z) / gradY, - 0.1); + (0.001 + gradX * oldPoint2.x - oldPoint2.z) / gradX, + (0.001 + gradY * oldPoint2.y - oldPoint2.z) / gradY, + 0.001); if (!Double.isFinite(gradX)) { newPoint2.x = oldPoint2.x; } @@ -137,20 +135,20 @@ } Triangle newTri; - newTri = new Triangle(oldPoint1.project(FPDis, scrX, scrY), newPoint2.project(FPDis, scrX, scrY), newPoint1.project(FPDis, scrX, scrY), + newTri = new Triangle(oldPoint1.project(drawData), newPoint2.project(drawData), newPoint1.project(drawData), new boolean[]{false, false, false}, tris[tri_i].texture, tris[tri_i].perspectiveMappingMatrix); - newTri.draw(img, zBuf, FPDis, scrX, scrY, ang); - newTri = new Triangle(oldPoint1.project(FPDis, scrX, scrY), oldPoint2.project(FPDis, scrX, scrY), newPoint2.project(FPDis, scrX, scrY), + newTri.draw(drawData, ang); + newTri = new Triangle(oldPoint1.project(drawData), oldPoint2.project(drawData), newPoint2.project(drawData), new boolean[]{false, false, false}, tris[tri_i].texture, tris[tri_i].perspectiveMappingMatrix); - newTri.draw(img, zBuf, FPDis, scrX, scrY, ang); + newTri.draw(drawData, ang); } else if (numOfInvalidPoints == 2) { // if two points are invalid, interpolate and draw triangle w/ new points: int validIndex = -1; for (int i = 0; i < 3; i += 1) { - if (points[trisFaceList[tri_i][i]].getRotatedPoint().z > 0.1) { + if (points[trisFaceList[tri_i][i]].getRotatedPoint().z > 0.001) { validIndex = i; break; } @@ -163,7 +161,7 @@ Point3D oldPoint = points[trisFaceList[tri_i][validIndex]].getRotatedPoint(); Point3D newPoint1; Point3D newPoint2; - // interpolate for z = 0.1 between invalid1 and oldPoint + // interpolate for z = 0.001 between invalid1 and oldPoint { double gradX = (oldPoint.z - invalidPoint1.z) / (oldPoint.x - invalidPoint1.x); @@ -171,9 +169,9 @@ (oldPoint.y - invalidPoint1.y); newPoint1 = new Point3D( - (0.1 + gradX * oldPoint.x - oldPoint.z) / gradX, - (0.1 + gradY * oldPoint.y - oldPoint.z) / gradY, - 0.1); + (0.001 + gradX * oldPoint.x - oldPoint.z) / gradX, + (0.001 + gradY * oldPoint.y - oldPoint.z) / gradY, + 0.001); if (!Double.isFinite(gradX)) { newPoint1.x = oldPoint.x; } @@ -181,7 +179,7 @@ newPoint1.y = oldPoint.y; } } - // interpolate for z = 0.1 between invalid2 and oldPoint + // interpolate for z = 0.001 between invalid2 and oldPoint { double gradX = (oldPoint.z - invalidPoint2.z) / (oldPoint.x - invalidPoint2.x); @@ -189,9 +187,9 @@ (oldPoint.y - invalidPoint2.y); newPoint2 = new Point3D( - (0.1 + gradX * oldPoint.x - oldPoint.z) / gradX, - (0.1 + gradY * oldPoint.y - oldPoint.z) / gradY, - 0.1); + (0.001 + gradX * oldPoint.x - oldPoint.z) / gradX, + (0.001 + gradY * oldPoint.y - oldPoint.z) / gradY, + 0.001); if (!Double.isFinite(gradX)) { newPoint2.x = oldPoint.x; } @@ -202,11 +200,11 @@ // create and draw new triangle Triangle newTri; - newTri = new Triangle(newPoint1.project(FPDis, scrX, scrY), newPoint2.project(FPDis, scrX, scrY), oldPoint.project(FPDis, scrX, scrY), + newTri = new Triangle(newPoint1.project(drawData), newPoint2.project(drawData), oldPoint.project(drawData), new boolean[]{false, false, false}, tris[tri_i].texture, tris[tri_i].perspectiveMappingMatrix); - newTri.draw(img, zBuf, FPDis, scrX, scrY, ang); + newTri.draw(drawData, ang); } // if all points are invalid, do nothing } } @@ -292,7 +290,7 @@ points) { point.setRotatedPoint(camMatrix); // if any points are behind the camera, we will need to handle it differently. - if(point.getRotatedPoint().z < 0.1){ + if(point.getRotatedPoint().z < 0.001){ valid = false; } else{ @@ -525,13 +523,13 @@ // should decrease processing time of each pixel by a lot } - private void bakePerspectiveMatrices(int[][] img, int[][] zBuf, BufferedImage debugImg, Matrix camMatrix, double FPDis, int scrX, int scrY){ + private void bakePerspectiveMatrices(drawData drawData){ // calculate matrix which converts 2d points into 3d points { // first, we get the rotated plane points and normal points Point3D planePoint = points[0].getRotatedPoint(); Vector3D rotatedNormalVector = new Vector3D(0,0,0); - camMatrix.multiplyVec3to(normal, rotatedNormalVector); + drawData.camMatrix.multiplyVec3to(normal, rotatedNormalVector); /* next we define matrices according to the simultaneous equations: 1, derived from projection: point.x = scrX - (scrX*0.5*((fpdis*y)/(z) + 1)); @@ -557,8 +555,8 @@ int y = 0; Matrix simulMat = new Matrix(3, 3); simulMat.setItems(new double[][]{ - {0, FPDis, (2*x/(double)scrX)-1}, - {scrX*FPDis, 0, scrY-2*y}, + {0, drawData.FPDis, (2*x/(double)drawData.scrX)-1}, + {drawData.scrX*drawData.FPDis, 0, drawData.scrY-2*y}, {normal.x, normal.y, normal.z}, }); Matrix simulMat2 = new Matrix(1, 3); diff --git a/src/main/java/uk/org/floop/epq3d/Line2d.java b/src/main/java/uk/org/floop/epq3d/Line2d.java index 5054308..8ec6f3d 100644 --- a/src/main/java/uk/org/floop/epq3d/Line2d.java +++ b/src/main/java/uk/org/floop/epq3d/Line2d.java @@ -28,7 +28,6 @@ point2 = _point2; isDrawn = _isDrawn; } - /** * Draws onto a given image * @param img the image to draw onto @@ -41,7 +40,6 @@ if(returnVal[0] >= 0 && returnVal[0] < img.getWidth() && returnVal[1] >= 0 && returnVal[1] < img.getHeight()) { // check if drawing in bounds img.setRGB(returnVal[0], returnVal[1], Color.HSBtoRGB(1, 1, 1)); } - } } else { for (int i = 0; i <= dy; i++){ @@ -134,7 +132,7 @@ return returnVal; } public double getZVal(int x){ - return realPoint1.z + (gradient * (x - realPoint1.x)); + return realPoint1.z + (gradient * (double)(x - realPoint1.x)); } } diff --git a/src/main/java/uk/org/floop/epq3d/Matrix.java b/src/main/java/uk/org/floop/epq3d/Matrix.java index fa9cfd9..5a3c9ff 100644 --- a/src/main/java/uk/org/floop/epq3d/Matrix.java +++ b/src/main/java/uk/org/floop/epq3d/Matrix.java @@ -55,6 +55,25 @@ } setItems(result.getItems()); } + public void multiplyTo(Matrix multiplier, Matrix result) { + if(!(result.x == multiplier.x && result.y == this.y)){ + throw new RuntimeException("wrong dimensions"); + } + double newItem; + if(x== multiplier.y){ + for(int rx = 0; rx< result.x; rx+=1){ + for(int ry = 0; ry< result.y; ry+=1){ + newItem = 0; + for(int i = 0; i= Math.PI/2-0.01 || true){ - numberOfPixels = face.draw(img, zBuf, debugImg, camMatrix, FPDis, scrX, scrY); + if(drawData.backfaceCullingDisabled || face.normal.angleTo(camVec) >= Math.PI/2-0.01){ + numberOfPixels = face.draw(drawData); } - debugImg.getGraphics().drawString(iterator + ": " +numberOfPixels , 10, 20 + 10*iterator); - iterator += 1; + if(drawData.debugHud){ + drawData.debugImg.getGraphics().drawString(iterator + ": " +numberOfPixels , 10, 20 + 10*iterator); + iterator += 1;} } } public void initialise(){ diff --git a/src/main/java/uk/org/floop/epq3d/ObjectCollection.java b/src/main/java/uk/org/floop/epq3d/ObjectCollection.java index 7a7bdb7..0433f08 100644 --- a/src/main/java/uk/org/floop/epq3d/ObjectCollection.java +++ b/src/main/java/uk/org/floop/epq3d/ObjectCollection.java @@ -1,6 +1,5 @@ package uk.org.floop.epq3d; -import java.awt.image.BufferedImage; import java.util.ArrayList; // stores both objects and other object collections @@ -42,21 +41,21 @@ } } - public void draw(int[][] img, int[][] zBuf, BufferedImage debugImg, Matrix camMatrix, Point3D playerPos, Plane[] frustumPlanes, double FPDis, int scrX, int scrY){ + public void draw(drawData drawData){ for (Object3d object: objects) { boolean draw = true; int i = 0; for (Plane plane: - frustumPlanes) { - debugImg.getGraphics().drawString( + drawData.frustumPlanes) { + drawData.debugImg.getGraphics().drawString( "Dis: " + String.format("%.1f", plane.getDistance(object.boundingSphereC)), 500, 10 + 20*i); if(plane.getDistance(object.boundingSphereC) < -object.boundingSphereR){ draw = false; break;} i += 1; } - if(draw){object.draw(img, zBuf, debugImg, camMatrix, FPDis, scrX, scrY, playerPos);} + if(draw){object.draw(drawData);} } // todo check for frustum culling for(ObjectCollection collection: @@ -64,15 +63,15 @@ boolean draw = true; int i = 0; for (Plane plane: - frustumPlanes) { - debugImg.getGraphics().drawString( + drawData.frustumPlanes) { + drawData.debugImg.getGraphics().drawString( "Dis: " + String.format("%.1f", plane.getDistance(collection.boundingSphereC)), 500, 10 + 20*i); if(plane.getDistance(collection.boundingSphereC) < -collection.boundingSphereR){ draw = false; break;} i += 1; } - collection.draw(img, zBuf, debugImg, camMatrix, playerPos, frustumPlanes, FPDis, scrX, scrY); + collection.draw(drawData); } } public void addCollection(){ diff --git a/src/main/java/uk/org/floop/epq3d/Player.java b/src/main/java/uk/org/floop/epq3d/Player.java index 97e4818..eeac9ce 100644 --- a/src/main/java/uk/org/floop/epq3d/Player.java +++ b/src/main/java/uk/org/floop/epq3d/Player.java @@ -1,21 +1,16 @@ 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]; + //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; + 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]; private final Point3D position = new Point3D(0,0,0); @@ -24,49 +19,35 @@ 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); + public Player(drawData drawData) { + drawData.FPDis = 1/Math.tan(Math.toRadians(drawData.FOV)/2d); for(int i = 0; i < 6; i+=1){ - frustumPlanes[i] = new Plane(); + drawData.frustumPlanes[i] = new Plane(); } - double yVal = (double)scrY / (double)scrX; + 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, 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; + 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; + 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; + direction.z -= 1; } } public void keyReleased(int key){ @@ -84,17 +65,17 @@ direction.z -= -1; } } - public void tick(Point2D mouseRel) { + public void tick(drawData drawData) { // gets called once every tick, before the repainting process happens. // change rotation depending on mouse movement - rotation.z -= mouseRel.x * mouseSensitivity; + 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 += mouseRel.y * mouseSensitivity; + rotation.y += drawData.mouseRel.y * mouseSensitivity; // max out y rotation at 0 and -90 degrees if(rotation.y < 0){ rotation.y = 0; @@ -141,15 +122,15 @@ ); // apply izMat to direction vector try { - Vector3D dirvec = new Vector3D(); - izMat.multiplyVec3to(direction, dirvec); - double dirLen = dirvec.getLength(); + 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); + dirVec.x = dirVec.x / (dirLen * 40); + dirVec.y = dirVec.y / (dirLen * 40); + dirVec.z = dirVec.z / (dirLen * 40); } - position.translate(dirvec); + position.translate(dirVec); } catch (Exception e) { throw new RuntimeException(e); @@ -161,12 +142,11 @@ {0, 0, 1, -position.z}} ); // multiply out matrices - rotMatrix = xMat.multiplyGetResult(yMat).multiplyGetResult(zMat); - camMatrix = rotMatrix.multiplyGetResult(traMat); - invRotMatrix = izMat.multiplyGetResult(iyMat).multiplyGetResult(ixMat); + 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 :( - 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)); @@ -182,9 +162,9 @@ // 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); + position.x - viewVector.x*drawData.FPDis, + position.y - viewVector.y*drawData.FPDis, + position.z - viewVector.z* drawData.FPDis); for (PointComp point: ScreenCornerPosS) { point.invalidate(); @@ -192,11 +172,11 @@ } // find frustum planes // near plane - frustumPlanes[0].initFrom3Points(ScreenCornerPosS[0].getRotatedPoint(), + drawData.frustumPlanes[0].initFrom3Points(ScreenCornerPosS[0].getRotatedPoint(), ScreenCornerPosS[1].getRotatedPoint(), ScreenCornerPosS[2].getRotatedPoint()); // far plane int farPlaneDis = 1000; - frustumPlanes[1].initFromPointAndVector( + drawData.frustumPlanes[1].initFromPointAndVector( new Point3D( position.x + viewVector.x*farPlaneDis, position.y + viewVector.y*farPlaneDis, @@ -205,17 +185,19 @@ new Vector3D(-viewVector.x, -viewVector.y, -viewVector.z)); // mid planes // left plane - frustumPlanes[2].initFrom3Points(FPWorldPos, + drawData.frustumPlanes[2].initFrom3Points(FPWorldPos, ScreenCornerPosS[0].getRotatedPoint(), ScreenCornerPosS[1].getRotatedPoint()); // right plane - frustumPlanes[3].initFrom3Points(FPWorldPos, + drawData.frustumPlanes[3].initFrom3Points(FPWorldPos, ScreenCornerPosS[3].getRotatedPoint(), ScreenCornerPosS[2].getRotatedPoint()); // top plane - frustumPlanes[4].initFrom3Points(FPWorldPos, + drawData.frustumPlanes[4].initFrom3Points(FPWorldPos, ScreenCornerPosS[1].getRotatedPoint(), ScreenCornerPosS[3].getRotatedPoint()); // bottom plane - frustumPlanes[5].initFrom3Points(FPWorldPos, + drawData.frustumPlanes[5].initFrom3Points(FPWorldPos, ScreenCornerPosS[2].getRotatedPoint(), ScreenCornerPosS[0].getRotatedPoint()); + + // update drawData } // returnValue Functions diff --git a/src/main/java/uk/org/floop/epq3d/Point2D.java b/src/main/java/uk/org/floop/epq3d/Point2D.java index a18ca1d..46cc457 100644 --- a/src/main/java/uk/org/floop/epq3d/Point2D.java +++ b/src/main/java/uk/org/floop/epq3d/Point2D.java @@ -1,7 +1,5 @@ package uk.org.floop.epq3d; -import java.util.ArrayList; - public class Point2D { public int x; public int y; diff --git a/src/main/java/uk/org/floop/epq3d/Point3D.java b/src/main/java/uk/org/floop/epq3d/Point3D.java index fef2a1d..3d1b2cc 100644 --- a/src/main/java/uk/org/floop/epq3d/Point3D.java +++ b/src/main/java/uk/org/floop/epq3d/Point3D.java @@ -45,6 +45,12 @@ (int)(scrX*0.5*((fpdis*x)/(z) + ((double)scrY/(double)scrX))) ); } + public Point2D project(drawData drawData) { + return new Point2D( + drawData.scrX - (int)(drawData.scrX*0.5*((drawData.FPDis*y)/(z) + 1)), + (int)(drawData.scrX*0.5*((drawData.FPDis*x)/(z) + ((double)drawData.scrY/(double)drawData.scrX))) + ); + } public void projectOn(double fpdis, int scrX, int scrY, Point2D point){ // this is a mess of random fixes because none of my coordinates are correct. :( // sorry @@ -54,4 +60,6 @@ public double[] get(){ return new double[]{x, y, z}; } + + } diff --git a/src/main/java/uk/org/floop/epq3d/Screen.java b/src/main/java/uk/org/floop/epq3d/Screen.java index 67ff83e..4baf557 100644 --- a/src/main/java/uk/org/floop/epq3d/Screen.java +++ b/src/main/java/uk/org/floop/epq3d/Screen.java @@ -15,14 +15,13 @@ private final Point2D mouseRel = new Point2D(0,0); private final Cursor invisibleCursor; + // uber class for storing all draw data which needs to be passed to functions + public final drawData drawData; + // mainCollection stores all the background information in the scene public ObjectCollection mainCollection; // stores the skybox public Object3d skyBox; - // stores the 2d information on the HUD - public BufferedImage debugImg; - public JsonReader json; - // testing\ // private Line2d line = new Line2d( // new Point2D(200, 200), @@ -42,9 +41,6 @@ // __config variables__ // controls the delay between each tick in ms private final int DELAY = 1; - // controls the size of the board - public static final int scrX = 900; - public static final int scrY = 600; // suppress serialization warning private static final long serialVersionUID = 490905409104883233L; @@ -57,8 +53,11 @@ public Screen(ObjectCollection _mainCollection) { mainCollection = _mainCollection; + // initialise drawData + drawData = new drawData("config.json"); + // set the game board size - setPreferredSize(new Dimension(scrX, scrY)); + setPreferredSize(new Dimension(drawData.scrX, drawData.scrY)); // set the game board background color setBackground(new Color(232, 232, 232)); // hide the mouse cursor (https://stackoverflow.com/questions/191592/how-do-i-get-rid-of-the-mouse-cursor-in-full-screen-exclusive-mode) @@ -67,8 +66,9 @@ BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TRANSLUCENT); invisibleCursor = toolkit.createCustomCursor(cursorImage, hotSpot, "InvisibleCursor"); + // initialize the game state - player = new Player(scrX, scrY); + player = new Player(drawData); try { Thread.sleep(1000); } catch (InterruptedException e) { @@ -83,8 +83,10 @@ @Override public void actionPerformed(ActionEvent e) { + // get mouse rel + drawData.mouseRel = get_mouse_rel(); // run the tick of the player before the board is redrawn - player.tick(get_mouse_rel()); + player.tick(drawData); // draw screen repaint(); } @@ -104,11 +106,7 @@ Toolkit.getDefaultToolkit().sync(); } private void drawScreen(Graphics g) { - int[][] img = new int[scrX][scrY]; - //BufferedImage zBuf = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); - int[][] zBuf = new int[scrX][scrY]; - debugImg = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); - debugImg.createGraphics(); + // System.out.println(zBuf.getRGB(0,0)); // zBuf.setRGB(0,0, 100); @@ -149,63 +147,61 @@ ang += 0.02;*/ mainCollection.invalidate(true); - mainCollection.draw(img, zBuf, debugImg, player.camMatrix, player.getPos(), player.frustumPlanes, player.Fpdis, scrX, scrY); - long time = System.currentTimeMillis(); - BufferedImage bufImg = new BufferedImage(scrX, scrY, BufferedImage.TYPE_INT_ARGB); - for(int x = 0; x < scrX; x +=1){ - for(int y = 0; y < scrY; y += 1){ - bufImg.setRGB(x, y, img[x][y]); - } - } - time = (System.currentTimeMillis() - time); - g.drawImage(bufImg, 0, 0, this); - + mainCollection.draw(drawData); // DEBUG DRAWING - boolean debug = false; { - debugImg.getGraphics().setColor(new Color(255,255,255,0)); + drawData.debugImg.getGraphics().setColor(new Color(255,255,255,0)); //debugImg.getGraphics().drawString(Math.round(1000 / (float) (System.currentTimeMillis() - lastTime)) + " fps", 10, 10); - debugImg.getGraphics().drawString("FrameTime: " + (System.currentTimeMillis() - lastTime) + " millis", 10, 10); - debugImg.getGraphics().drawString(time + " millis", 60, 20); - if(debug) { - debugImg.getGraphics().drawString("fpPos: " + + drawData.debugImg.getGraphics().drawString("FrameTime: " + (System.currentTimeMillis() - lastTime) + " millis", 10, 10); + if(drawData.debugHud) { + drawData.debugImg.getGraphics().drawString("fpPos: " + String.format("%.2f", player.FPWorldPos.x) + " " + String.format("%.2f", player.FPWorldPos.y) + " " + String.format("%.2f", player.FPWorldPos.z) + " ", 100, 30); - g.drawImage(debugImg, 0, 0, this); - debugImg.getGraphics().drawString("playerPos: " + + g.drawImage(drawData.debugImg, 0, 0, this); + drawData.debugImg.getGraphics().drawString("playerPos: " + String.format("%.2f", player.getPos().x) + " " + String.format("%.2f", player.getPos().y) + " " + String.format("%.2f", player.getPos().z) + " ", 100, 50); - debugImg.getGraphics().drawString("rotation: " + + drawData.debugImg.getGraphics().drawString("rotation: " + Math.round(Math.toDegrees(player.getRot().x)) + " " + Math.round(Math.toDegrees(player.getRot().y)) + " " + Math.round(Math.toDegrees(player.getRot().z)) + " ", 300, 50); - debugImg.getGraphics().drawString("viewVec: " + + drawData.debugImg.getGraphics().drawString("viewVec: " + String.format("%.2f", player.viewVector.x) + " " + String.format("%.2f", player.viewVector.y) + " " + String.format("%.2f", player.viewVector.z) + " ", 100, 70); for (int i = 0; i < 4; i += 1) { - debugImg.getGraphics().drawString("camPoint " + i + ": " + + drawData.debugImg.getGraphics().drawString("camPoint " + i + ": " + String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().x) + " " + String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().y) + " " + String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().z), 10, 90 + 20 * i); } for (int i = 0; i < 6; i += 1) { - debugImg.getGraphics().drawString("plane " + i + ": " + - String.format("%.2f", player.frustumPlanes[i].mainPoint.x) + " " + - String.format("%.2f", player.frustumPlanes[i].mainPoint.y) + " " + - String.format("%.2f", player.frustumPlanes[i].mainPoint.z) + ", normal: " + - String.format("%.2f", player.frustumPlanes[i].normalVector.x) + " " + - String.format("%.2f", player.frustumPlanes[i].normalVector.y) + " " + - String.format("%.2f", player.frustumPlanes[i].normalVector.z), 10, 170 + 20 * i); + drawData.debugImg.getGraphics().drawString("plane " + i + ": " + + String.format("%.2f", drawData.frustumPlanes[i].mainPoint.x) + " " + + String.format("%.2f", drawData.frustumPlanes[i].mainPoint.y) + " " + + String.format("%.2f", drawData.frustumPlanes[i].mainPoint.z) + ", normal: " + + String.format("%.2f", drawData.frustumPlanes[i].normalVector.x) + " " + + String.format("%.2f", drawData.frustumPlanes[i].normalVector.y) + " " + + String.format("%.2f", drawData.frustumPlanes[i].normalVector.z), 10, 170 + 20 * i); } } // write debug overlay - g.drawImage(debugImg, 0, 0, this); + g.drawImage(drawData.debugImg, 0, 0, this); + BufferedImage bufImg = new BufferedImage(drawData.scrX, drawData.scrY, BufferedImage.TYPE_INT_ARGB); + for(int x = 0; x < drawData.scrX; x +=1){ + for(int y = 0; y < drawData.scrY; y += 1){ + bufImg.setRGB(x, y, drawData.drawImg[x][y]); + drawData.drawImg[x][y] = 0; + drawData.zBuf[x][y] = 0; + drawData.debugImg.setRGB(x, y, 0); + } + } + g.drawImage(bufImg, 0, 0, this); + lastTime = System.currentTimeMillis(); } - // HTTPPost post = new HTTPPost(); // // json WRITING // json = new JsonWriter(); diff --git a/src/main/java/uk/org/floop/epq3d/Triangle.java b/src/main/java/uk/org/floop/epq3d/Triangle.java index a719072..b6cd6c2 100644 --- a/src/main/java/uk/org/floop/epq3d/Triangle.java +++ b/src/main/java/uk/org/floop/epq3d/Triangle.java @@ -36,7 +36,7 @@ perspectiveMappingMatrix = mapMatrix; } // returns int for debug - public int draw(int[][] img, int[][] zBuf, double FPDis, int scrX, int scrY, double ang){ + public int draw(drawData drawData, double ang){ // long lastMillis; @@ -52,8 +52,8 @@ for(int x = min.x+1; x <= max.x; x += 1) { while(x-1 == point1[0]) { if(LineLong.isDrawn && // draw line pixels if needed, and on screen - point1[0] > 0 && point1[1] > 0 && point1[0] < scrX && point1[1] < scrY){ - img[point1[0]][point1[1]] = Color.HSBtoRGB(0, 1, 1); + point1[0] > 0 && point1[1] > 0 && point1[0] < drawData.scrX && point1[1] < drawData.scrY){ + drawData.drawImg[point1[0]][point1[1]] = Color.HSBtoRGB(0, 1, 1); } try { point1 = LineLong.nextPix(); @@ -66,8 +66,8 @@ if (currentLine == 'A') { try{ if(LineA.isDrawn && // draw line pixels if needed, and on screen - point2[0] > 0 && point2[1] > 0 && point2[0] < scrX && point2[1] < scrY){ - img[point2[0]][point2[1]] = Color.HSBtoRGB(0, 1, 1); + point2[0] > 0 && point2[1] > 0 && point2[0] < drawData.scrX && point2[1] < drawData.scrY){ + drawData.drawImg[point2[0]][point2[1]] = Color.HSBtoRGB(0, 1, 1); } point2 = LineA.nextPix(); } @@ -78,25 +78,25 @@ } else { if(LineB.isDrawn && // draw line pixels if needed, and on screen - point2[0] > 0 && point2[1] > 0 && point2[0] < scrX && point2[1] < scrY){ - img[point2[0]][point2[1]] = Color.HSBtoRGB(0, 1, 1); + point2[0] > 0 && point2[1] > 0 && point2[0] < drawData.scrX && point2[1] < drawData.scrY){ + drawData.drawImg[point2[0]][point2[1]] = Color.HSBtoRGB(0, 1, 1); } point2 = LineB.nextPix(); } } // cancel drawing if the x value of the triangle is out of bounds - if (x >= scrX) {break;} + if (x >= drawData.scrX) {break;} if (x > 0) { // check which way to loop // TODO - work out a way of not needing to test for this every time if (point1[1] < point2[1]) { - for (int y = Math.max(point1[1], 0); y <= Math.min(point2[1], scrY - 1); y += 1) { + for (int y = Math.max(point1[1], 0); y <= Math.min(point2[1], drawData.scrY - 1); y += 1) { // function only exists so I don't have to copy paste code everywhere. - drawPix(img, zBuf, FPDis, scrX, scrY, currentLine, x, y, point1[1], point2[1], ang); + drawPix(drawData, currentLine, x, y, point1[1], point2[1], ang); } } else { - for (int y = Math.max(point2[1], 0); y <= Math.min(point1[1], scrY - 1); y += 1) { - drawPix(img, zBuf, FPDis, scrX, scrY, currentLine, x,y, point1[1], point2[1], ang); + for (int y = Math.max(point2[1], 0); y <= Math.min(point1[1], drawData.scrY - 1); y += 1) { + drawPix(drawData, currentLine, x,y, point1[1], point2[1], ang); } } } @@ -149,19 +149,22 @@ // assign points to lines is_initialised = true; } - private void drawPix(int[][] img, int[][] zBuf, double FPDis, int scrX, int scrY, char currentLine, int x, int y, int y1, int y2, double ang){ + private void drawPix(drawData drawData, char currentLine, int x, int y, int y1, int y2, double ang){ // find Z coordinate of pixel double z1 = LineLong.getZVal(x); double z2; - if(currentLine=='A'){z2=LineA.getZVal(x);} else{z2=LineB.getZVal(x);} - double ZVal = z1 + ((z1-z2)/ (y1-y2) * (y - y1)); - + if(currentLine=='A'){ + z2=LineA.getZVal(x); + } else{ + z2=LineB.getZVal(x); + } + double ZVal = z1 + ((z1-z2)/ (y1-y2) * (y-y1)); // a value of 0 represents a value which is on the camera, at z=0. // not the best mapping but it's probably good enough - int newZ = (int)((2147483648L/ZVal)); + int newZ = (int)((2147483648L/(ZVal+1))); // if the new Z value is greater than the existing Z value on the buffer, the new pixel is calculated and drawn - if(zBuf[x][y] == Color.HSBtoRGB(0, 0, 0) || - newZ > zBuf[x][y]){ //newZ > zBuf.getRGB(x, y) || - zBuf[x][y] = newZ; + if(drawData.zBuf[x][y] == 0 || + newZ > drawData.zBuf[x][y]){ //newZ > zBuf.getRGB(x, y) || + drawData.zBuf[x][y] = newZ; result = new Point3D(0,0,0); //perspectiveMappingMatrix.multiplyPoint3to(new Point3D(x, y, 0), result); // project result @@ -171,7 +174,7 @@ } else { throw new RuntimeException("Textures are not supported"); } - img[x][y] = pixColor.getRGB(); + drawData.drawImg[x][y] = pixColor.getRGB(); // result.projectOn(FPDis, scrX, scrY, result2); // int colour = texture.img.getRGB( // Math.floorMod((result2.x), texture.img.getWidth()), diff --git a/src/main/java/uk/org/floop/epq3d/drawData.java b/src/main/java/uk/org/floop/epq3d/drawData.java new file mode 100644 index 0000000..71121d5 --- /dev/null +++ b/src/main/java/uk/org/floop/epq3d/drawData.java @@ -0,0 +1,72 @@ +package uk.org.floop.epq3d; + + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import java.awt.image.BufferedImage; +import java.io.FileReader; +import java.io.IOException; +import java.util.Objects; + +/** + * Stores variables which are required for drawing. Settings, Drawing images etc. + */ +public class drawData { + // config + int scrX; + int scrY; + int delay; + int FOV; + boolean debugHud; + boolean backfaceCullingDisabled; + + // draw onto + public int[][] drawImg; + public int[][] zBuf; + public BufferedImage debugImg; + + // draw data + public Matrix camMatrix; + public double lastFrameTime; // measured in seconds + public Point3D playerPos; + public double FPDis; + public Plane[] frustumPlanes = new Plane[6]; + public Point2D mouseRel; + public drawData(String filePath){ + Object obj; + try { + JSONParser parser = new JSONParser(); + obj = parser.parse(new FileReader(filePath)); + } catch (IOException | ParseException e) { + throw new RuntimeException(e); + } + // save config + JSONObject file = (JSONObject)obj; + scrX = (int) (long) file.get("screenWidth"); + scrY = (int) (long) file.get("screenHeight"); + delay = (int) (1/((double) (long) file.get("maxFPS"))); + FOV = (int) (long) file.get("FOV"); + + + // debug + JSONObject debugFile = (JSONObject) file.get("debug"); + if(toBoolean(debugFile.get("masterDebugToggle"))){ + debugHud = toBoolean(debugFile.get("drawDebugHud")); + backfaceCullingDisabled = toBoolean(debugFile.get("overrideBackFaceCulling")); + } + + drawImg = new int[scrX][scrY]; + zBuf = new int[scrX][scrY]; + debugImg = new BufferedImage(scrX, scrY, BufferedImage.TYPE_INT_ARGB); + debugImg.createGraphics(); + } + private Boolean toBoolean(Object obj){ + if(Objects.equals(obj.toString(), "enabled")){ + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/uk/org/floop/epq3d/simpleGraphics.java b/src/main/java/uk/org/floop/epq3d/simpleGraphics.java new file mode 100644 index 0000000..f6d9ae1 --- /dev/null +++ b/src/main/java/uk/org/floop/epq3d/simpleGraphics.java @@ -0,0 +1,187 @@ +package uk.org.floop.epq3d; + +import java.awt.*; +import java.awt.image.ImageObserver; +import java.text.AttributedCharacterIterator; + +public class simpleGraphics extends Graphics { + @Override + public Graphics create() { + return null; + } + + @Override + public void translate(int x, int y) { + + } + + @Override + public Color getColor() { + return null; + } + + @Override + public void setColor(Color c) { + + } + + @Override + public void setPaintMode() { + + } + + @Override + public void setXORMode(Color c1) { + + } + + @Override + public Font getFont() { + return null; + } + + @Override + public void setFont(Font font) { + + } + + @Override + public FontMetrics getFontMetrics(Font f) { + return null; + } + + @Override + public Rectangle getClipBounds() { + return null; + } + + @Override + public void clipRect(int x, int y, int width, int height) { + + } + + @Override + public void setClip(int x, int y, int width, int height) { + + } + + @Override + public Shape getClip() { + return null; + } + + @Override + public void setClip(Shape clip) { + + } + + @Override + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + + } + + @Override + public void fillRect(int x, int y, int width, int height) { + + } + + @Override + public void clearRect(int x, int y, int width, int height) { + + } + + @Override + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + + } + + @Override + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + + } + + @Override + public void drawOval(int x, int y, int width, int height) { + + } + + @Override + public void fillOval(int x, int y, int width, int height) { + + } + + @Override + public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + + } + + @Override + public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + + } + + @Override + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { + + } + + @Override + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { + + } + + @Override + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { + + } + + @Override + public void drawString(String str, int x, int y) { + + } + + @Override + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + + } + + @Override + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return false; + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { + return false; + } + + @Override + public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { + return false; + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { + return false; + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { + return false; + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { + return false; + } + + @Override + public void dispose() { + + } +} diff --git a/src/main/java/uk/org/floop/epq3d/simpleImage.java b/src/main/java/uk/org/floop/epq3d/simpleImage.java new file mode 100644 index 0000000..ec5d854 --- /dev/null +++ b/src/main/java/uk/org/floop/epq3d/simpleImage.java @@ -0,0 +1,32 @@ +package uk.org.floop.epq3d; + +import java.awt.*; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; + +public class simpleImage extends Image { + @Override + public int getWidth(ImageObserver observer) { + return 0; + } + + @Override + public int getHeight(ImageObserver observer) { + return 0; + } + + @Override + public ImageProducer getSource() { + return null; + } + + @Override + public Graphics getGraphics() { + return null; + } + + @Override + public Object getProperty(String name, ImageObserver observer) { + return null; + } +}